1. 聚合:最小值、最大值和其他值
当面对大量数据时,第一个步骤通常是计算相关数据的概括统计值,最常用的概括统计值可能是均值和标准差,这两个值都能让你分别概括数据集中的“经典”值,但是其他一些形式的聚合也是很有用的(如求和,乘积,中位数,最大值和最小值,分位数等)
numpy有非常快速的内置聚合函数可用于数组
1.1 数组值求和
计算一个数组所有元素的和,可以使用Python本身内置的sum函数来实现:
1 import numpy as np
2
3 l = np.random.random(100)
4
5 sum(l)
6 Out[7]: 52.340268364356575
它的语法与Numpy的sum函数非常相似
1 np.sum(l)
2 Out[8]: 52.34026836435658
但是,因为Numpy中的sum函数是在编译码中执行操作,所以操作计算更快一些
1 big_array = np.random.rand(1000000)
2
3 %timeit sum(big_array)
4 70.5 ms ± 424 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
5
6 %timeit np.sum(big_array)
7 1.01 ms ± 2.02 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.2 最大值和最小值
python中也有内置的min和max函数,分别用于获取给定数组的最小值和最大值:
1 min(big_array)
2 Out[13]: 2.426756139373154e-07
3
4 max(big_array)
5 Out[14]: 0.9999978808646249
numpy对应的函数也有类似的用法:
1 np.min(big_array)
2 Out[15]: 2.426756139373154e-07
3
4 np.max(big_array)
5 Out[16]: 0.9999978808646249
对于sum,min,max和其他Numpy聚合,,一种更加简单的语法方式是数组对象直接调用这些方法:
1 print(big_array.sum(), big_array.min(), big_array.max())
2 500338.00061074767 2.426756139373154e-07 0.9999978808646249
1.多维度聚合
一种常用的聚合操作是向一行或一列聚合
1 M = np.random.random((3,4))
2
3 print(M)
4 [[0.92333809 0.47906549 0.63142885 0.52152519]
5 [0.66829419 0.52895095 0.95324477 0.7010968 ]
6 [0.92015305 0.69294617 0.50466039 0.50205913]]
默认情况下,每一个Numpy聚合函数将会返回对整个数组的聚合结果:
1 M.sum()
2 Out[20]: 8.026763061739384
聚合函数还有一个参数,用于指定沿着哪个轴的方向进行聚合。例如,可以通过指定的axis=0找到每一列的最小值
1 M.min(axis=0)
2 Out[21]: array([0.66829419, 0.47906549, 0.50466039, 0.50205913])
函数返回四个值,对应四列数字的计算值
同样可以找到每一行的最大值:
1 M.max(axis=1)
2 Out[22]: array([0.92333809, 0.95324477, 0.92015305])
axis关键字指定的是数组将会被折叠的维度,而不是将要返回的维度,因此指定axis=0意味着第一个轴将要被折叠-对于二维数组,这意味着每一列的值都将被聚合
2.其他聚合函数
大多数的聚合都对NaN值的安全处理策略(NaN-Safe), 即计算忽略所有的缺失值,这些缺失值即特殊的IEEE浮点型NaN值
Numpy中可用的聚合函数
2. 数组的计算:广播
前面介绍了Numpy如何通过通用函数的向量化操作来减少缓慢的python循环操作,另一种向量化的操作时利用Numpy的广播功能
广播功能可以理解为用于不同大小数组的二元通用函数(加,减,乘)的一组规则
2.1 广播的介绍
对于同样大小的数组,二元操作符是对相应的元素逐个计算:
1 import numpy as np
2
3 a = np.array([1, 4, 5])
4
5 b = np.array([2, 3, 6])
6
7 a + b
8 Out[26]: array([ 3, 7, 11])
广播允许这些二元运算符可以用于不同大小的数组
例如,可以简单的将一个标量(可以认为是一个零维的数组)和一个数组相加
1 a + 5
2 Out[27]: array([ 6, 9, 10])
我们可以认为这个操作是将数值5扩展或重复至数组[5, 5, 5]然后执行加法操作。
同样可以将这个原理扩展到更高维度的数组
观察以下将一个一维数组和一个二维数组相加的结果:
1 M = np.ones((3, 3))
2
3 M
4 Out[29]:
5 array([[1., 1., 1.],
6 [1., 1., 1.],
7 [1., 1., 1.]])
8
9 M + a
10 Out[30]:
11 array([[2., 5., 6.],
12 [2., 5., 6.],
13 [2., 5., 6.]])
这里这个数组就被扩展或者广播了,他沿着第二个维度扩展,扩展到匹配M数组的形状
涉及到两个数组同时广播
1 a = np.arange(3)
2
3 b = np.arange(3)[:,np.newaxis]
4
5 print(a)
6 [0 1 2]
7
8 print(b)
9 [[0]
10 [1]
11 [2]]
12
13 a + b
14 Out[35]:
15 array([[0, 1, 2],
16 [1, 2, 3],
17 [2, 3, 4]])
这里的a和b都进行了扩展来匹配一个公共的形状,最终的结果是一个二维数组
注意的是,这个额外的内存并没有在实际操作中进行分配,只是方便理解而已
2.2 广播的规则
Numpy的广播遵循一组严格的规则,设定该组规则是为决定两个数组间的操作
1. 规则1:如果两个数组维度数不相同,那么小维度数组的形状将会在最左边补1
2. 规则2:如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度为1的维度扩展以匹配另外数组的形状
3. 规则3:如果两个数组的形状在任何一个维度上都不匹配并且没有任何维度等于1,那么会引发异常
① 广播示例1
将一个二维数组与一维数组相加:
1 M = np.ones((2,3))
2
3 a = np.arange(3)
4
5 M.shape = (2, 3)
6
7 a.shape = (3,)
可以看到,根据规则1,数组a的维度数更小,所以在其左边补1:
1 M.shape -> (2, 3)
2 a.shape -> (1, 3)
根据规则2,第一个维度不匹配,因此扩展这个维度已匹配是数组
1 M.shape -> (2, 3)
2 a.shape -> (2, 3)
现在两个数组的形状匹配了,可以看到最终的形状都为(2, 3)
1 M + a
2 Out[43]:
3 array([[1., 2., 3.],
4 [1., 2., 3.]])
② 广播示例2
看下两个数组均需要广播的情况:
1 a = np.arange(3).reshape((3,1))
2
3 b = np.arange(3)
同样首先写出两个数组的形状
1 a.shape -> (3, 1)
2 b.shape -> (3,)
规则1,补全b:
1 a.shape -> (3, 1)
2 b.shape -> (1, 3)
规则2,需要更新两个数组的维度来相互匹配
1 a.shape -> (3, 3)
2 b.shape -> (3, 3)
结果同样是匹配的,所以两个形状是兼容的
1 a + b
2 Out[46]:
3 array([[0, 1, 2],
4 [1, 2, 3],
5 [2, 3, 4]])
③ 广播示例3
两个不兼容的数组示例:
1 M = np.ones((3, 2))
2
3 a = np.arange(3)
两个数组的形状:
1 M.shape -> (3, 2)
2 a.shape -> (3,)
规则1,补全a:
1 M.shape -> (3, 2)
2 a.shape -> (1, 3)
规则2,a数组的第一个维度扩展一匹配M的维度:
1 M.shape -> (3, 2)
2 a.shape -> (3, 3)
最终还是不匹配,因此两个数组不兼容:
1 M = np.ones((3, 2))
2
3 a = np.arange(3)
4
5 M + a
6 Traceback (most recent call last):
7
8 File "<ipython-input-49-8cac1d547906>", line 1, in <module>
9 M + a
10
11 ValueError: operands could not be broadcast together with shapes (3,2) (3,)
1 M = np.ones((3, 2))
2
3 a = np.arange(3)
4
5 M + a
6 Traceback (most recent call last):
7
8 File "<ipython-input-49-8cac1d547906>", line 1, in <module>
9 M + a
10
11 ValueError: operands could not be broadcast together with shapes (3,2) (3,)
如果希望右边补全,可以通过变型数组来实现(用到np.newaxis关键字)
1 a[:, np.newaxis].shape
2 Out[50]: (3, 1)
3
4 M + a[:, np.newaxis]
5 Out[51]:
6 array([[1., 1.],
7 [2., 2.],
8 [3., 3.]])
2.3 广播的实际应用
1.数组的归一化
假设有一个10个观察值的数组,每个观察值包含3个数值,可以用10×3的数组存放数据:
计算每个特征的均值,计算方法是利用mean函数沿着第一个维度聚合:
1 X = np.random.random((10, 3))
2
3 Xmean = X.mean(0)
4
5 Xmean
6 Out[54]: array([0.41290359, 0.44184376, 0.52243085])
现在通过从X数组的元素中减去这个均值实现归一化(该操作时一个广播操作)
1 X_centered = X -Xmean
2
3 X_centered
4 Out[56]:
5 array([[ 0.01632031, -0.04991934, 0.02390285],
6 [-0.22869882, -0.14248755, 0.21596561],
7 [ 0.27042931, 0.47324529, 0.05049647],
8 [ 0.05333787, -0.25397372, -0.38220317],
9 [-0.31278918, 0.12058912, -0.48048683],
10 [-0.06980194, -0.0271224 , 0.40289488],
11 [ 0.58452572, 0.03485366, 0.31782146],
12 [-0.37699945, -0.42393852, 0.46615793],
13 [ 0.15895104, 0.43469103, -0.32263444],
14 [-0.09527485, -0.16593756, -0.29191475]])
15
16 X_centered.mean(0)
17 Out[57]: array([4.99600361e-17, 2.22044605e-17, 4.44089210e-17])
为了核对处理是否正确,查看归一化的数组的均值是否接近于0:
1 X_centered.mean(0)
2 Out[57]: array([4.99600361e-17, 2.22044605e-17, 4.44089210e-17])
在机器精度范围内,该均值为0
2.画一个二维函数
广播还能基于二维函数显示图像
先定义一个函数z = f(x, y),可以广播沿着数值区间计算该函数:
利用Matplotib画出这个二维数组组:
1 import numpy as np
2
3 # x和y表示0-5区间50个步长的序列
4 x = np.linspace(0, 5, 50)
5 y = np.linspace(0, 5, 50)[:, np.newaxis]
6
7 z = np.sin(x) ** 10 +np.cos(10 + y * x) * np.cos(x)
8
9
10 import matplotlib.pyplot as plt
11
12 plt.imshow(z, origin='lower', extent=[0, 5, 0, 5], cmap='viridis')
13 plt.colorbar()
可视化图像: