数组很重要,因为它使你不用编写循环即可对数据执行批量运算。这通常就叫做矢量化(vectorization)。
In [81]: arr=np.array([[1,2],[3,4.]])
In [82]: arr*arr
Out[82]:
array([[ 1., 4.],
[ 9., 16.]])
In [83]: arr-arr
Out[83]:
array([[ 0., 0.],
[ 0., 0.]])
In [84]: 1/arr
Out[84]:
array([[ 1. , 0.5 ],
[ 0.33333333, 0.25 ]])
In [85]: arr**0.5
Out[85]:
array([[ 1. , 1.41421356],
[ 1.73205081, 2. ]])
广播的的原则:
如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为它们是兼容广播的。广播会在缺失和(或)长度为1的维度上进行。
通俗点说就是较小数组的“广播维”必须为1.
我们先来看下在二维数组上的广播。arr的shape为(4,3),arr.mean(0)(ar.mean(index)可以简单理解为对arr进行扁平化处理,如shape为(m,n,l,...)index为1处理完后shape为(m,l,...))的shape为(3,),符合广播原则,运行如下:
>>> import numpy as np
>>> arr=np.arange(12).reshape((4,3))
>>> arr
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
>>> arr.mean(0)
array([4.5, 5.5, 6.5])
>>> arr.mean(0).shape
(3,)
>>> demeaned = arr-arr.mean(0)
>>> demeaned
array([[-4.5, -4.5, -4.5],
[-1.5, -1.5, -1.5],
[ 1.5, 1.5, 1.5],
[ 4.5, 4.5, 4.5]])
还是二维,接上面的例子,我们对1轴进行降维,得到shape为(4,)的数组,后缘维度不为3且不为1,故不符合广播规则,你将会得到如下错误:
>>> arr.mean(1)
array([ 1., 4., 7., 10.])
>>> arr.mean(1).shape
(4,)
>>> arr-arr.mean(1)
Traceback (most recent call last):
File "", line 1, in
ValueError: operands could not be broadcast together with shapes (4,3) (4,)
>>>
shape(4,)转成(4,1)就可以广播了,如下:
>>> arr-arr.mean(1).reshape((4,1))
array([[-1., 0., 1.],
[-1., 0., 1.],
[-1., 0., 1.],
[-1., 0., 1.]])
多维数组跟这类似,以三维举例。对于shape为(2,3,4)和shape为(2,4)的两个数组,我们需要把shape(2,4)转成shape(2,1,4)才符合广播规则,这里给出两种方法来转换,一是用reshape方法,二是用通过特殊的np.newaxis属性:
>>> arr= np.arange(24).reshape((2,3,4))
>>> arr
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
>>> arr.mean(1)
array([[ 4., 5., 6., 7.],
[16., 17., 18., 19.]])
>>> arr1= arr.mean(1).reshape((2,1,4))
>>> arr2= arr.mean(1)[:,np.newaxis,:]
>>> arr-arr1
array([[[-4., -4., -4., -4.],
[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.]],
[[-4., -4., -4., -4.],
[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.]]])
>>> arr-arr2
array([[[-4., -4., -4., -4.],
[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.]],
[[-4., -4., -4., -4.],
[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.]]])
对于多维数组来讲,有没有一种既通用又不牺牲性能的方法呢?实际上是有的,但是需要一些索引的技巧,这里用到了slice类。slice接受三个参数,分别是开始索引,结束索引,和步长。
>>> def demeaned_axis(arr,axis=0):
... means=arr.mean(axis)
... indexer = [slice(None)]*arr.ndim
... indexer[axis] =np.newaxis
... return arr-means[indexer]
...
>>> demeaned_axis(arr,1)
array([[[-4., -4., -4., -4.],
[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.]],
[[-4., -4., -4., -4.],
[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.]]])
广播同样适用于设置数组值的操作。arr还是之前的三维数组:
>>> arr[:1] = np.array([0,0,0])[:,np.newaxis]
>>> arr
array([[[ 0, 0, 0, 0],
[ 0, 0, 0, 0],
[ 0, 0, 0, 0]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])