【Task 1】 Python基础与Numpy基础

 Task 1 思维导图

一、python基础

1、列表推导式和条件赋值

列表推导式:在一定的Python语法基础上实现语句的简化操作,可以利用列表推导式进行写法上的简化: [* for i in *] 。其中,第一个 * 为映射函数,其输入为后面 i 指代的内容,第二个 * 表示迭代的对象。

def my_func(x):
    return x ^ 2

# 结果如下
[0, 1, 4, 9, 16]

## 列表推导式
[myfunc(i) for i in range(5)]

# 结果如下
[0, 1, 4, 9, 16]

列表表达式也支持多层嵌套。

条件赋值:其形式为 value = a if condition else b。

value = 2 if 1 > 0 else 3

## value的结果如下
2

条件赋值也可结合列表使用。

2、匿名函数与map方法

匿名函数:使用lambda关键字来实现其作用。

[(lambda x : x * x)(i) for i in range(5)]

## 结果如下
[0, 1, 4, 9, 16]

map方法:对于上述的这种列表推导式的匿名函数映射, Python 中提供了 map 函数来完成,它返回的是一个 map 对象,需要通过 list 转为列表。

list(map(lambda x: 2*x, range(5)))

## 结果
[0, 2, 4, 6, 8]

3、zip对象与enumerate方法

zip对象:zip函数能够把多个可迭代对象打包成一个元组构成的可迭代对象,它返回了一个 zip 对象,通过 tuple, list 可以得到相应的打包结果。

L1, L2, L3 = list('abc'), list('def'), list('hij')

list(zip(L1, L2, L3))

## 结果
[('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]



tuple(zip(L1, L2, L3))

## 结果
(('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j'))

经常在循环迭代的时候使用到 zip 函数:

for i, j, k in zip(L1, L2, L3):
        print(i, j, k)


## 结果
a d h
b e i
c f j

对两个列表建立字典映射时,也可以利用 zip 对象:

dict(zip(L1, L2))

## 结果
{'a': 'd', 'b': 'e', 'c': 'f'}

有了压缩函数,那么 Python 也提供了 * 操作符和 zip 联合使用来进行解压操作:

zipped = list(zip(L1, L2, L3))

zipped

## 结果
[('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]



list(zip(*zipped))   # 三个元组分别对应原来的列表

## 结果
[('a', 'b', 'c'), ('d', 'e', 'f'), ('h', 'i', 'j')]

enumerate: 是一种特殊的打包,它可以在迭代时绑定迭代元素的遍历序号:

L = list('abcd')

for index, value in enumerate(L):
     print(index, value)


## 结果 
0 a
1 b
2 c
3 d

二、Numpy基础

1.np数组的构造

最常使用的方法是通过rarray来构造。

np.array([1,2,3])
## 结果
array([1, 2, 3])

下面是一些特殊数组的生成方式:

1)等差序列: np.linspace, np.arange

np.linspace(1,5,11) # 参数意义: 起始、终止(包含)、样本个数

## 结果
array([1. , 1.4, 1.8, 2.2, 2.6, 3. , 3.4, 3.8, 4.2, 4.6, 5. ])
 

np.arange(1,5,2) # 参数意义: 起始、终止(不包含)、步长

## 结果
array([1, 3])

2)特殊矩阵: zeros, eye, full

  • np.full(矩阵维度,填充数值):构造一个指定维度的矩阵,使用指定数值进行填充,也可传入列表填充每列的值。
np.zeros((2,3)) # 传入元组表示各维度大小
## 结果 
array([[0., 0., 0.],
       [0., 0., 0.]])


np.eye(3)  # 3*3的单位矩阵
## 结果
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

np.eye(3, k=1) # 偏移主对角线1个单位的伪单位矩阵
## 结果
array([[0., 1., 0.],
       [0., 0., 1.],
       [0., 0., 0.]])

np.full((2,3), 10) # 元组传入大小,10表示填充数值
## 结果 
array([[10, 10, 10],
       [10, 10, 10]])

np.full((2,3), [1,2,3]) # 通过传入列表填充每列的值
## 结果
array([[1, 2, 3],
       [1, 2, 3]])

3)随机矩阵: np.random

最常用的随机生成函数为 rand, randn, randint, choice。

  • rand:表示0-1均匀分布的随机数组
  • randn标准正态的随机数组
  • randint随机整数组
  • choice随机列表抽样(参数:replace = True 为可放回抽样,False 为不可放回抽样)

例子如下:

np.random.rand(3) # 生成服从0-1均匀分布的三个随机数
## 结果
array([0.33475955, 0.95078732, 0.05285509])

np.random.rand(3, 3) # 注意这里传入的不是元组,每个维度大小分开输入
## 结果 
array([[0.1188322 , 0.51993935, 0.73054809],
       [0.97169376, 0.72724319, 0.84687781],
       [0.18001319, 0.8011098 , 0.05113275]])


# 对于服从区间 a 到 b 上的均匀分布
a, b = 5, 15
(b - a) * np.random.rand(3) + a
## 结果
array([ 9.67438882, 12.49445466,  6.51381903])


# randn 生成了 N(0,I) 的标准正态分布
np.random.randn(3)
## 结果
array([ 0.91321097, -0.02203455,  0.44235296])

np.random.randn(2, 2)
## 结果 
array([[ 0.49897634, -1.57842429],
       [-0.09213398,  0.00613158]])


# 对于服从方差为 σ2 均值为 μ 的一元正态分布
sigma, mu = 2.5, 3
mu + np.random.randn(3) * sigma
## 结果
array([5.89540275, 2.56563403, 1.56208693])

 choice 可以从给定的列表中,以一定概率和方式抽取结果,当不指定概率时为均匀采样,默认抽取方式为有放回抽样,即参数:replace = True 为可放回抽样,False 为不可放回抽样。

当返回的元素个数与原列表相同时,等价于使用 permutation 函数,即打散原列表

np.random.permutation(my_list)

## 结果
array(['d', 'c', 'a', 'b'], dtype='<U1')

4)随机种子:它能够固定随机数的输出结果。

np.random.seed(0)

np.random.rand()
## 结果
0.5488135039273248

np.random.seed(0)
np.random.rand()
## 结果
0.5488135039273248

2.np数组的变形与合并

1)转置:T

2)合并操作: r_, c_

  • 对于二维数组而言, r_ 和 c_ 分别表示上下合并和左右合并。
  • 在一维数组和二维数组进行合并的情况下,应当把其视作列向量,在长度匹配的情况下只能够使用左右合并的 c_ 操作。举例如下:
np.r_[np.array([0,0]),np.zeros(2)]   
## 结果
array([0., 0., 0., 0.])

np.c_[np.array([0,0]),np.zeros((2,3))]  # 将array([0,0])看作列向量,再进行左合并
## 结果 
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.]])

3)维度变换: reshape

reshape 能够帮助用户把原数组按照新的维度重新排列。有如下两种模式:

  • C 模式,以逐的顺序进行填充读取。
  •  F 模式,以逐的顺序进行填充读取。
target = np.arange(8).reshape(2,4)
target
## 结果 
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

target.reshape((4,2), order='C') # 按照行读取和填充
## 结果 
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7]])

target.reshape((4,2), order='F') # 按照列读取和填充
## 结果
array([[0, 2],
       [4, 6],
       [1, 3],
       [5, 7]])

特别地,由于被调用数组的大小是确定的, reshape 允许有一个维度存在空缺,此时只需填充-1即可。也就是说,数组的大小是确定的,知道行数,就能够确定列数;反之,知道列数,也能确定行数。

3. np数组的切片与索引

数组的切片模式支持使用 slice 类型的 start:end:step 切片(参数意义即 开始位置:结束位置:步长),还可以直接传入列表指定某个维度的索引进行切片。

target = np.arange(9).reshape(3,3)

target
## 结果 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

target[:-1, [0,2]]  # 逗号用来分开维度, :-1 指从第一个维度的第一个数到倒数第二个数
## 结果             # [0,2] 表示第二个维度的第一列和第三列                  
array([[0, 2],
       [3, 5]])

4.常用函数

下面列举一些常用的函数

  • where:一种条件函数,可以指定满足条件与不满足条件位置对应的填充值。

                  np.where(a>0, a, 5) ,对应位置为True时填充a对应元素,否则填充5

  • nonzero:返回非零数的索引

  • argmax:返回最大的索引

  • argmin:返回最小数的索引

  • any:指当序列至少 存在一个 True 或非零元素时返回 True ,否则返回 False

  • all:指当序列元素 全为 True 或非零元素时返回 True ,否则返回 False

  • cumprod:表示累乘函数,返回同长度的数组

  • cumsum:表示累加函数,返回同长度的数组

  • diff:表示和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1

还有一些常用的统计函数

  • max:最大值
  • min:最小值
  • mean:均值
  • median:中位数
  • std:标准差
  • var:方差
  • sum:求和
  • quantile:四分之几分位数
  • cov:协方差
  • corrcoef:相关系数

5.广播机制

1)广播机制用于处理两个不同维度数组之间的操作

2)当一个标量和数组进行运算时,标量会自动把大小扩充为数组大小,之后进行逐元素操作

3)当二维数组ab进行操作时,两个数组维度完全一致时,对应元素进行相关操作,维度不一致时报错。但是,当其中每个数组是m×1 或者 1×n时,把1这个维度扩充到另一个数组对应维度大小后进行计算。

res = np.ones((3,2))
res
## 结果 
array([[1., 1.],
       [1., 1.],
       [1., 1.]])


res * np.array([[2,3]]) # 扩充第一维度为3
## 结果 
array([[2., 3.],
       [2., 3.],
       [2., 3.]])


res * np.array([[2],[3],[4]]) # 扩充第二维度为2
## 结果
array([[2., 2.],
       [3., 3.],
       [4., 4.]])

res * np.array([[2]]) # 等价于两次扩充
## 结果
array([[2., 2.],
       [2., 2.],
       [2., 2.]])

6.向量与矩阵的计算

  • 向量内积: dot
  • 向量范数和矩阵范数: np.linalg.norm
  • 矩阵乘法: @ 

 

三、练习

# 最外层循环在最右边
# 两个矩阵相乘 【2,3】* 【3,4】 = 【2,4】  类似于这样的【【 , , ,】,【 , , , 】】
# 外面两层循环表示的是二维矩阵的行标和列标进行对应法则的相乘,
# 最里面一层循环表示的是对相乘矩阵再进行求和。
# 只不过 最里面的一层循环是放在 sum() 函数里面进行的

res = [[sum([M1[i][k] * M2[k][j] for k in range(M1.shape[1])]) for j in range(M2.shape[1])] for i in range(M1.shape[0])]
res


[[1.1650640931571854,
  1.2547181565812449,
  0.9590145951545355,
  0.9488091282003532],
 [1.2725522615844083,
  1.409747599639991,
  1.0768864923004082,
  1.1334836896947502]]


(np.abs((M1 @ M2 - res) < 1e-15)).all()  # 排除数值误差

## 答案
True

 

A = np.arange(1,10).reshape(3,3)
A
## 结果
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

1/A
## 结果
array([[1.        , 0.5       , 0.33333333],
       [0.25      , 0.2       , 0.16666667],
       [0.14285714, 0.125     , 0.11111111]])

(1/A).sum(1)  # 参数:axis = 0 对列求和,axis = 1 对行求和
## 结果
array([1.83333333, 0.61666667, 0.37896825])

(1/A).sum(1).reshape(-1, 1)  # 对行的求和 进行广播机制(和转置的效果一样) 成 三行一列,再与矩阵A相乘,可得出结果
## 结果
array([[1.83333333],
       [0.61666667],
       [0.37896825]])

A * (1/A).sum(1).reshape(-1, 1)
## 结果
array([[1.83333333, 3.66666667, 5.5       ],
       [2.46666667, 3.08333333, 3.7       ],
       [2.65277778, 3.03174603, 3.41071429]])


37/12
## 结果
3.0833333333333335

np.random.seed(0)
A = np.random.randint(10, 20, (8, 5))
A
## 结果
array([[15, 10, 13, 13, 17],
       [19, 13, 15, 12, 14],
       [17, 16, 18, 18, 11],
       [16, 17, 17, 18, 11],
       [15, 19, 18, 19, 14],
       [13, 10, 13, 15, 10],
       [12, 13, 18, 11, 13],
       [13, 13, 17, 10, 11]])

A.sum(0)  # 列求和
## 结果
array([120, 111, 129, 116, 101])

A.sum(1)  # 行求和
## 结果
array([68, 73, 80, 79, 85, 61, 67, 64])

A.sum(0) * (A.sum(1).reshape(-1,1))  # 广播转换
## 保留原来的行和列 [8, 5]
## 结果
array([[ 8160,  7548,  8772,  7888,  6868],
       [ 8760,  8103,  9417,  8468,  7373],
       [ 9600,  8880, 10320,  9280,  8080],
       [ 9480,  8769, 10191,  9164,  7979],
       [10200,  9435, 10965,  9860,  8585],
       [ 7320,  6771,  7869,  7076,  6161],
       [ 8040,  7437,  8643,  7772,  6767],
       [ 7680,  7104,  8256,  7424,  6464]])

B = (A.sum(0) * (A.sum(1).reshape(-1,1)))/A.sum()   # 即求出B[i][j]
B
## 结果
array([[14.14211438, 13.08145581, 15.20277296, 13.67071057, 11.90294627],
       [15.18197574, 14.04332756, 16.32062392, 14.67590988, 12.77816291],
       [16.63778163, 15.38994801, 17.88561525, 16.08318891, 14.0034662 ],
       [16.42980936, 15.19757366, 17.66204506, 15.88214905, 13.82842288],
       [17.67764298, 16.35181976, 19.0034662 , 17.08838821, 14.87868284],
       [12.68630849, 11.73483536, 13.63778163, 12.26343154, 10.67764298],
       [13.93414211, 12.88908146, 14.97920277, 13.46967071, 11.72790295],
       [13.3102253 , 12.31195841, 14.3084922 , 12.86655113, 11.20277296]])

((A - B) ** 2 / B).sum()  # 最后求和
## 结果

11.842696601945802




 分析公式有:

# 运行函数
def solution(B=B, U=U, Z=Z):
     L_res = []
     for i in range(m):
         for j in range(n):
             norm_value = ((B[i]-U[:,j])**2).sum()
             L_res.append(norm_value*Z[i][j])
     return sum(L_res)
solution(B, U, Z)
## 结果
199919



(B ** 2).sum(1)
## 结果
array([30, 26, 26, 19, 23, 28, 32, 28, 27, 23, 21, 27, 25, 28, 26, 22, 27,
       27, 24, 15, 21, 25, 24, 26, 22, 29, 27, 31, 30, 25, 27, 25, 27, 24,
       25, 24, 21, 28, 25, 29, 24, 22, 24, 25, 25, 25, 33, 27, 25, 25, 29,
       21, 28, 31, 24, 26, 26, 24, 32, 28, 22, 27, 31, 27, 24, 20, 23, 25,
       25, 29, 20, 22, 24, 29, 27, 23, 30, 20, 25, 24, 26, 26, 26, 15, 29,
       30, 31, 30, 28, 30, 24, 16, 25, 20, 26, 28, 20, 23, 28, 23],
      dtype=int32)


# B U Z 的行列分别是: B:(100, 50)  U:(50, 80)  Z:(100, 80)

((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2*B@U)*Z  # (B**2).sum(1) :(100,1)
                                                         # (U**2).sum(0): (80,1)
                                                         # 广播之后 变成(100, 80)正好与 Z(100,80) 相符合,可做乘法
## 结果
array([[25, 22, 31, ..., 58,  0,  0],
       [ 0, 26, 42, ...,  0,  0, 24],
       [54,  0, 42, ..., 23,  0,  0],
       ...,
       [24,  0, 36, ...,  0, 26,  0],
       [50, 26,  0, ...,  0, 23, 24],
       [52, 54,  0, ..., 72, 44,  0]])

(((B**2).sum(1).reshape(-1,1) + (U**2).sum(0) - 2*B@U)*Z).sum()
## 结果
199919

两者方法的时间性能比较:

%timeit -n 30 solution(B, U, Z)
## 结果
38.6 ms ± 887 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)


%timeit -n 30 ((np.ones((m,n))*(B**2).sum(1).reshape(-1,1) + np.ones((m,n))*(U**2).sum(0) - 2*B@U)*Z).sum()
## 结果
533 µs ± 34.4 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)

在时间上区别还是比较明显的。

用匿名函数来表达:


 ​​​总结:

   学习不容易啊,尤其是练习题,看不太懂,都是看着视频的讲解还是很勉强理解一点点。加油加油加油!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值