使用Numpy数组可以使你利用简单的数组表达式完成多种数据操作任务,而无须写些大量循环。这种利用数组表达式来替代显式循环的方法,称为向量化。通常,向量化的数组操作会比纯Python的等价实现在速度上快一到两个数量级(甚至更多),这对所有种类的数值计算产生了最大的影响。
作一个简单的示例,假设我们想要对一些网格数据来计算函数sqrt(x^2 + y^2)的值。np.meshgrid函数接收两个一维数组,并根据两个数组的所有(x,y)对生成一个二维矩阵:
import numpy as np
points = np.arange(-5,5,0.01)
xs,ys = np.meshgrid(points,points)
ys
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]])
xs
array([[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
...,
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99]])
现在你可以用和两个坐标值相同的表达式来使用函数:
z = np.sqrt(xs ** 2 + ys ** 2)
z
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]])
一、将逻辑条件作为数组操作
numpy.where函数上三元表达式 x if condition else y 的向量化版本。假设我们有一个布尔值数组和两个数值数组:
xarr = np.array([1.1,1.2,1.3,1.4,1.5])
yarr = np.array([2.1,2.2,2.3,2.4,2.5])
cond = np.array([True,False,True,True,False])
假设cond中的元素为True时,我们取xarr中的对应元素值,否则取yarr中的元素。我们可以通过列表推导式来完成,像代码这样:
result = [(x if c else y) for x,y,c in zip(xarr,yarr,cond)]
result
[1.1, 2.2, 1.3, 1.4, 2.5]
这样会产生多个问题。首先,如果数组很大的话,速度会很慢。其次,当数组上多维时,就无法奏效了。而使用np.where时,就可以非常简单地完成:
result = np.where(cond,xarr,yarr)
result
array([1.1, 2.2, 1.3, 1.4, 2.5])
np.where的第二个和第三个参数并不需要数组,它们可以是标量。where在数据分析中的一个典型用法上根据一个数组来生成一个新的数组,假设你有一个随机生成的矩阵数据,并且你想将其中的正值都替换为2,将所有的负值替换为-2,使用np.where会很容易实现:
arr = np.random.randn(4,4)
arr
array([[-0.15384072, -0.52666358, 0.79846853, 1.65839579],
[ 0.76100703, -0.40294247, -0.50833117, -1.37770235],
[-1.0287086 , 0.2443933 , -0.79481477, 0.41286556],
[-0.22541371, 0.07460424, 0.75058749, 0.92358304]])
arr > 0
array([[False, False, True, True],
[ True, False, False, False],
[False, True, False, True],
[False, True, True, True]])
np.where(arr >0,2,-2)
array([[-2, -2, 2, 2],
[ 2, -2, -2, -2],
[-2, 2, -2, 2],
[-2, 2, 2, 2]])
你可以使用np.where将标量和数组联合,例如,像我一样将arr中所有正值替换为常数2:
np.where(arr>0,2,arr) #仅将正值设为2
array([[-0.15384072, -0.52666358, 2. , 2. ],
[ 2. , -0.40294247, -0.50833117, -1.37770235],
[-1.0287086 , 2. , -0.79481477, 2. ],
[-0.22541371, 2. , 2. , 2. ]])
传递给np.where的数组既可以上同等大小的数组,也可以是标量。