Numpy数组使你可以将许多种数据处理任务表述为简洁的数组表达式。利用数组表达式代替循环的做法,通常被称为矢量化。一般来说,矢量化数组运算要比等价的纯Python方式快上一两个数量级(甚至更多)尤其是各种数值计算。
假如我们想要在一组值上计算函数sqrt(x^2+y^2)。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵:
In [4]: points=np.arange(-5,5,0.01) In [5]: xs,ys=np.meshgrid(points,points) #产生1000个间隔相等的点 In [6]: ys Out[6]: 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 [8]: import matplotlib.pyplot as plt In [9]: z=np.sqrt(xs ** 2 + ys ** 2) In [10]: z Out[10]: 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,
Out[20]: Text(0.5,1,'Image plot of for a grid of values')
Out[21]: <matplotlib.image.AxesImage at 0x1b669f2ab38>
Out[22]: <matplotlib.colorbar.Colorbar at 0x1b669f77e80>
Out[23]: Text(0.5,1,'Image plot of for a grid of values')
效果如下:
函数值得图形结果如上所示,使用的是matlpltlib的imshow函数创建的,colorbar显示右侧的条形栏,title显示标题。
将条件逻辑表述为数组运算
numpy.where函数是三元表达式x if condition else y的矢量化版本。假设我们有一个布尔数组和两个值数组:
In [24]: xarr=np.array([1.1,1.2,1.3,1.4,1.5,]) In [25]: yarr=np.array([2.1,2.2,2.3,2.4,2.5]) In [26]: cond=np.array([True,False,True,True,False])
假设我们想要根据cond的值选取xarr和yarr的值:当cond种的值为True时,选取xarr的值,否则选取yarr值。那么:
In [27]: result=[(x if c else y) ...: for x,y,c in zip(xarr,yarr,cond)] ...: In [28]: result Out[28]: [1.1, 2.2, 1.3, 1.4, 2.5]
这有几个问题。第一,它对大多数数组的处理速度不是很快(因为所有操作都是纯Python完成的)。第二,无法用于多维数组。若使用np.where,则该功能写的非常简洁:
In [30]: result=np.where(cond,xarr,yarr) In [31]: result Out[31]: array([1.1, 2.2, 1.3, 1.4, 2.5])
np.where的第二个和第三个参数不必是数组,它们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新数组。假设有一个随机数数据组成的矩阵,你希望将所有正值替换成2,将所有负值替换成-2,。若利用np.where,则会非常简单:
In [32]: arr=randn(4,4) In [33]: arr Out[33]: array([[-0.86420168, 0.46737262, 1.52870151, 0.87138646], [ 1.25042219, -1.26081875, 1.13138761, 0.30288777], [-0.17935329, 0.6377377 , 2.94953378, -1.16395691], [ 0.71109135, -0.88708025, 2.25487302, 0.67770479]]) In [34]: np.where(arr>0,2,-2) Out[34]: array([[-2, 2, 2, 2], [ 2, -2, 2, 2], [-2, 2, 2, -2], [ 2, -2, 2, 2]])
In [36]: np.where(arr>0,2,arr) #只将正值设置为2
Out[36]:
array([[-0.86420168, 2. , 2. , 2. ],
[ 2. , -1.26081875, 2. , 2. ],
[-0.17935329, 2. , 2. , -1.16395691],
[ 2. , -0.88708025, 2. , 2. ]])
传递给where的数组大小可以不相等,甚至可以是标量值。
假如我们有两个布尔型数组cond1和cond2。希望根据4种不同的布尔值组合实现不同的赋值操作:
result=[] for i in range(n): ...: if cond1[i] and cond2[i]: ...: result.append(0) ...: elif cond[1]: ...: result.append(1) ...: elif cond2[i]: ...: result.append(2) ...: else: ...: result.append(3)
这个for循环可以改写成一个嵌套的where表达式:
np.where(cond1 & cond2,0,np.where(cond1,1,np.where(cond2,2,3)))
在这个特殊的例子里,我们还可以利用“布尔值在计算过程中可以被当做0或1处理”这个事实,所以还能将其写成这样的算术运算:
result= 1 * (cond1-cond2) +2 * (cond2 & -cond1) + 3 * -(cond1 | cond2)

In [40]: cond1=True In [41]: cond2=False In [42]: result= 1 * (cond1-cond2) +2 * (cond2 & -cond1) + 3 * -(cond1 | cond2) In [43]: result Out[43]: -2
数学和统计方法
可以通过数组上一组数学函数对整个数组或某个轴向的数据进行统计运算。sum、mean以及标准差std等聚合运算。既可以当做数组的实例方法调用,也可以当做顶级numpy函数使用:
In [44]: arr=np.random.randn(5,4) #正态分布 In [45]: arr Out[45]: array([[-0.66406273, 0.8416933 , 0.50576114, 0.94731857], [ 1.02715787, -0.90753908, -0.98615632, 0.41013117], [ 0.77767026, 0.93195883, -0.43795365, -0.07269931], [-0.06836068, -0.36374319, 1.03753398, -0.47534392], [-2.86457512, 0.03055321, 0.20997436, 0.4156038 ]]) In [46]: np.mean(arr) Out[46]: 0.014746125565483863 In [47]: arr.sum() Out[47]: 0.2949225113096773
mean和sum这类的函数,可以接受一个axis参数(用户计算该轴向上的统计值),最终结果是一个少一维的数组:
In [48]: arr.mean() Out[48]: 0.014746125565483863 In [49]: arr.mean(axis=1) Out[49]: array([ 0.40767757, -0.11410159, 0.29974403, 0.03252155, -0.55211094])
其他如cumsum和cumprod之类的方法则不聚合,而是产生一个由中间结果组成的数组:
In [50]: arr=np.array([[0,1,2],[3,4,5],[6,7,8]]) In [51]: arr.cumsum(0) Out[51]: array([[ 0, 1, 2], [ 3, 5, 7], [ 9, 12, 15]], dtype=int32) In [52]: arr.cumprod(1) Out[52]: array([[ 0, 0, 0], [ 3, 12, 60], [ 6, 42, 336]], dtype=int32)
cumsum:所有元素的累积和,里面可以不带参数,如果带参数只能是0或1
In [60]: arr
Out[60]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
In [57]: arr.cumsum() Out[57]: array([ 0, 1, 3, 6, 10, 15, 21, 28, 36], dtype=int32) In [58]: arr.cumsum(0) Out[58]: array([[ 0, 1, 2], [ 3, 5, 7], [ 9, 12, 15]], dtype=int32) In [59]: arr.cumsum(1) Out[59]: array([[ 0, 1, 3], [ 3, 7, 12], [ 6, 13, 21]], dtype=int32)
从上可以看出,如果不带参数,结果为一个少一维的数组,值为前面元素累积和;如果带参数为0,表述纵向累积和,如果带参数为1,则为横向累积和(等同于axis=1)
cumprod类似,不过它是计算累积积
基本数组统计方法:
sum: 对数组中全部或某轴向的元素求和。零长度的数组sum为0
mean:算术平均差。零长度的数组mean为NaN。
std、var:分别为标准差和方差,自由度可调(默认为n)
min、max:最小值和最大值
argmin、argmax:分别为最小和最大元素的索引
cumsum、cumprod:所有元素的累积和、累积积
用于布尔型数组的方法
从上面我们知道,布尔值会被强制转换成1(True)和0(False)。因此,sum经常用来对布尔型数组中的True值计数:
In [64]: arr=randn(100) In [65]: (arr>0).sum() Out[65]: 45
另外还有2个方法any和all,它们对布尔型数组非常有用。any用于测试数组中是否存在一个或多个True,而all则检查数组中所有值是否都是True:
In [67]: bools=np.array([False,False,True,False]) In [68]: bools.any() Out[68]: True In [69]: bools.all() Out[69]: False