快速的元素级数组函数
通用函数(即ufunc) 是一种对ndarray中的数据执行元素级运算的函数。 你可以将其看做简单函数(接受一个或多个标量值, 并产生一个或多个标量值) 的矢量化包装器
In [1]: import numpy as np
In [2]: arr = np.arange(10)
In [3]: arr
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [4]: np.sqrt(arr)
Out[4]:
array([0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
In [5]: np.exp(arr)
Out[5]:
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
2.98095799e+03, 8.10308393e+03])
这些都是一元(unary)ufunc。另外一些(如add或maximum)接受2个数组(因此也叫二元(binary) ufunc) ,并返回一个结果数组
In [6]: x = np.random.randn(8)
In [7]: y = np.random.randn(8)
In [8]: x
Out[8]:
array([ 0.10716392, 0.49217214, -1.14228221, 0.31049503, -1.11340346,
-1.21463633, 0.11805104, -0.30401207])
In [9]: y
Out[9]:
array([ 0.70061311, -0.48270951, -1.62563645, -0.02473194, 0.08231935,
-1.8701379 , 0.86725936, 0.49598605])
In [10]: np.maximum(x, y)
Out[10]:
array([ 0.70061311, 0.49217214, -1.14228221, 0.31049503, 0.08231935,
-1.21463633, 0.86725936, 0.49598605])
这里,numpy.maximum计算了x和y中元素级别最大的元素
虽然并不常见, 但有些ufunc的确可以返回多个数组。modf就是一个例子,它是Python内置函数divmod的矢量化版本,它会返回浮点数数组的小数和整数部分
In [11]: arr = np.random.randn(7) * 5
In [12]: arr
Out[12]:
array([ 1.96753342, 5.72566158, 5.33468918, -9.30095939, -5.27622152,
-4.50344508, -3.2361097 ])
In [13]: remainder, whole_part = np.modf(arr)
In [14]: remainder
Out[14]:
array([ 0.96753342, 0.72566158, 0.33468918, -0.30095939, -0.27622152,
-0.50344508, -0.2361097 ])
In [15]: whole_part
Out[15]: array([ 1., 5., 5., -9., -5., -4., -3.])
Ufuncs可以接受一个out可选参数, 这样就能在数组原地进行操作
In [16]: arr
Out[16]:
array([ 1.96753342, 5.72566158, 5.33468918, -9.30095939, -5.27622152,
-4.50344508, -3.2361097 ])
In [17]: np.sqrt(arr)
E:\Environment\anaconda\Scripts\ipython:1: RuntimeWarning: invalid value encountered in sqrt
Out[17]:
array([1.40268793, 2.39283547, 2.30969461, nan, nan,
nan, nan])
In [18]: np.sqrt(arr, arr)
E:\Environment\anaconda\Scripts\ipython:1: RuntimeWarning: invalid value encountered in sqrt
Out[18]:
array([1.40268793, 2.39283547, 2.30969461, nan, nan,
nan, nan])
In [19]: arr
Out[19]:
array([1.40268793, 2.39283547, 2.30969461, nan, nan,
nan, nan])
- 一元ufunc
- 二元ufunc
利用数组进行数据处理
作为简单的例子,假设我们想要在一组值(网格型)上计算函数 sqrt(x^2+y^2)
。 np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x,y)对)
In [20]: points = np.arange(-5, 5, 0.01) # 1000 equally spaced points
In [21]: xs, ys = np.meshgrid(points, points)
In [22]: ys
Out[22]:
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
[-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
[-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
...,
[ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
[ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
[ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])
现在,对该函数的求值运算就好办了,把这两个数组当做两个浮点数那样编写表达式即可
In [23]: z = np.sqrt(xs ** 2 + ys ** 2)
In [24]: z
Out[24]:
array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,
7.06400028],
[7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
7.05692568],
[7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
7.04985815],
...,
[7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,
7.04279774],
[7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
7.04985815],
[7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
7.05692568]])
我用matplotlib创建了这个二维数组的可视化
In [27]: plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
Out[27]: <matplotlib.colorbar.Colorbar at 0x1afa7b44548>
In [28]: plt.title('Image plot of $\sqrt{x^2 + y^2}$ for a grid values')
Out[28]: Text(0.5, 1.0, 'Image plot of $\\sqrt{x^2 + y^2}$ for a grid values')
使用where函数将条件逻辑表述为数组运算
numpy.where函数是三元表达式x if condition else y的矢量化版本。假设我们有一个布尔数组和两个值数组
In [29]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
In [30]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
In [31]: cond = np.array([True, False, True, True, False])
假设我们想要根据cond中的值选取xarr和yarr的值:当cond中的值为True时,选取xarr的值,否则从yarr中选取。 列表推导式的写法应该如下所示
In [32]: result = [(x if c else y) for x,y,c in zip(xarr, yarr, cond)]
In [33]: result
Out[33]: [1.1, 2.2, 1.3, 1.4, 2.5]
这有几个问题。第一,它对大数组的处理速度不是很快(因为所有工作都是由纯Python完成的)。第二,无法用于多维数组。若使用np.where,则可以将该功能写得非常简洁
In [2]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
In [3]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
In [4]: cond = np.array([True, False, True, True, True])
In [5]: result = np.where(cond, xarr, yarr)
In [6]: result
Out[6]: array([1.1, 2.2, 1.3, 1.4, 1.5])
np.where的第二个和第三个参数不必是数组,它们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。假设有一个由随机数据组成的矩阵,你希望将所有正值替换为2,将所有负值替换为-2。若利用np.where,则会非常简单
In [8]: arr = np.random.randn(4, 4)
In [9]: arr
Out[9]:
array([[ 0.94949802, 0.41144197, -1.91940976, 1.67166374],
[ 1.32145048, 1.71122163, 0.15182974, -0.65659749],
[-0.2575805 , -0.10543429, 1.24716095, 0.44008596],
[ 0.01099706, 1.46653594, -0.41596372, 1.1810662 ]])
In [11]: arr > 0
Out[11]:
array([[ True, True, False, True],
[ True, True, True, False],
[False, False, True, True],
[ True, True, False, True]])
In [12]: np.where(arr>0, 2, -2)
Out[12]:
array([[ 2, 2, -2, 2],
[ 2, 2, 2, -2],
[-2, -2, 2, 2],
[ 2, 2, -2, 2]])
使用np.where,可以将标量和数组结合起来,例如,用常数2替换arr中所有正的值
In [13]: np.where(arr > 0, 2, arr)
Out[13]:
array([[ 2. , 2. , -1.91940976, 2. ],
[ 2. , 2. , 2. , -0.65659749],
[-0.2575805 , -0.10543429, 2. , 2. ],
[ 2. , 2. , -0.41596372, 2. ]])
数学和统计方法
可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算。sum、mean以及标准差std等聚合计算(aggregation,通常叫做约简(reduction))既可以当做数组的实例方法调用,也可以当做顶级NumPy函数使用
In [14]: arr = np.random.randn(5, 4)
In [15]: arr
Out[15]:
array([[-0.18450665, 1.09160022, -0.45164509, -1.29567266],
[-0.36163517, 0.37244986, 0.98034514, 0.82316027],
[ 1.13255244, -0.75213107, 0.85103452, -0.15437491],
[ 0.18715067, 1.75264532, 0.38247457, 1.18892021],
[-0.34493636, 1.16156775, 1.92229378, -0.15901044]])
In [16]: arr.mean()
Out[16]: 0.40711411999618424
In [17]: np.mean(arr)
Out[17]: 0.40711411999618424
In [18]: arr.sum()
Out[18]: 8.142282399923685
mean和sum这类的函数可以接受一个axis选项参数,用于沿着该轴计算的统计值,最终结果是一个少一维的数组
In [19]: arr.mean(axis=0)
Out[19]: array([0.08572498, 0.72522642, 0.73690058, 0.08060449])
In [20]: arr.mean(axis=1)
Out[20]: array([-0.21005604, 0.45358002, 0.26927025, 0.87779769, 0.64497868])
这里, arr.mean(1)
是“计算行的平均值”,arr.mean(0)
是“计算每列的和”
其他如cumsum和cumprod之类的方法则不聚合, 而是产生一个由中间结果组成的数组
In [21]: arr = np.array([0,1,2,3,4,5,6,7])
In [22]: arr.cumsum()
Out[22]: array([ 0, 1, 3, 6, 10, 15, 21, 28], dtype=int32)
在多维数组中,累加函数(如cumsum)返回的是同样大小的数组,但是会根据每个低维的切片沿着标记轴计算部分聚类
In [23]: arr = np.array([[0,1,2],[3,4,5], [6,7,8]])
In [24]: arr
Out[24]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
In [25]: arr.cumsum(axis=0)
Out[25]:
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)
In [26]: arr.cumsum(axis=1)
Out[26]:
array([[ 0, 1, 3],
[ 3, 7, 12],
[ 6, 13, 21]], dtype=int32)
In [27]: arr.cumprod(axis=1)
Out[27]:
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)
排序
跟Python内置的列表类型一样,NumPy数组也可以通过sort方法就地排序
In [28]: arr = np.random.randn(6)
In [29]: arr
Out[29]:
array([ 0.70636751, -0.73598582, 1.16913476, -1.37940337, -0.51591008,
-0.42780537])
In [30]: arr.sort()
In [31]: arr
Out[31]:
array([-1.37940337, -0.73598582, -0.51591008, -0.42780537, 0.70636751,
1.16913476])
多维数组可以在任何一个轴向上进行排序, 只需将轴编号传给sort即可
In [32]: arr = np.random.randn(5, 3)
In [33]: arr
Out[33]:
array([[-1.53935156, 1.50062807, -0.21328874],
[ 0.92822483, 1.77466032, 0.43758902],
[-0.9286603 , 0.63807763, -1.77895267],
[ 1.24869571, 0.17529268, 0.46659006],
[ 0.88561678, 1.36707667, 0.40294837]])
In [34]: arr.sort(axis=1)
In [35]: arr
Out[35]:
array([[-1.53935156, -0.21328874, 1.50062807],
[ 0.43758902, 0.92822483, 1.77466032],
[-1.77895267, -0.9286603 , 0.63807763],
[ 0.17529268, 0.46659006, 1.24869571],
[ 0.40294837, 0.88561678, 1.36707667]])
顶级方法np.sort返回的是数组的已排序副本,而就地排序则会修改数组本身。计算数组分位数最简单的办法是对其进行排序,然后选取特定位置的值
In [37]: large_arr = np.random.randn(1000)
In [38]: large_arr.sort()
In [39]: large_arr[int(0.05 * len(large_arr))] # 选择第%5的元素值
Out[39]: -1.6077327462775528
唯一化以及其它的集合逻辑
NumPy提供了一些针对一维ndarray的基本集合运算。最常用的可能要数np.unique了,它用于找出数组中的唯一值并返回已排序的结果
In [41]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Joe', 'Joe'])
In [42]: np.unique(names)
Out[42]: array(['Bob', 'Joe', 'Will'], dtype='<U4')
In [43]: ints = np.array([3,3,3,2,2,1,1,4,4])
In [44]: np.unique(ints)
Out[44]: array([1, 2, 3, 4])
拿跟np.unique等价的纯Python代码来对比一下
In [45]: sorted(set(names))
Out[45]: ['Bob', 'Joe', 'Will']
另一个函数np.in1d用于测试一个数组中的值在另一个数组中的成员资格, 返回一个布尔型数组
In [46]: values = np.array([6,0,0,3,2,5,6])
In [47]: np.in1d(values, [2,3,6])
Out[47]: array([ True, False, False, True, True, False, True])
NumPy中的集合函数如下表