- 使用NumPy数组可以使你利用简单的数组表达式完成多种数据操作任务,而无须写些大量循环。这种利用数组表达式来代替显示循环的方法,成为向量化。通常,向量化的数组操作会比纯Python的等价实现在速度上快一到两个量级(甚至更多),这对所有种类的数值计算产生了最大的影响。
- 作为一个简单的示例,假设我们想要对一些网格数据来计算函数sqrt(x ^2 + y ^2)的值。np.meshgrid函数接收两个一维数组,并根据两个数组的所有(x,y)对生成一个二维矩阵:
>>> 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]])
- 现在,你可以用和两个坐标值同样的表达式来使用函数:
>>> 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 d 的向量化版本。假设我们有一个布尔值数组和两个数值数组:
>>> 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]
- 这样会产生多个问题。首先,如果数组很大的话,速度会很慢(因为所有的工作都是通过解释器解释Python代码完成)。其次,当数组是多维时,就无法奏效了。而是用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([[-2.3624117 , -1.96653044, -0.89714196, -0.68149659],
[ 0.70245615, 1.14386905, 1.12898706, -1.23815808],
[-0.56145414, 0.58495615, 1.95468701, 0.4702848 ],
[-1.37148413, -0.00377742, 1.80542236, -1.41635326]])
>>> arr > 0
array([[False, False, False, False],
[ True, True, True, False],
[False, True, True, True],
[False, False, True, False]])
>>> 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)
array([[-2.3624117 , -1.96653044, -0.89714196, -0.68149659],
[ 2. , 2. , 2. , -1.23815808],
[-0.56145414, 2. , 2. , 2. ],
[-1.37148413, -0.00377742, 2. , -1.41635326]])
- 传递给np.where的数组既可以是同等大小的数组,也可以是标量。
数学和统计方法
- 许多关于计算整个数组统计值或关于轴向数据的数学函数,可以作为数组类型的方法被调用。你可以使用聚合函数(通常也叫缩减函数),比如sum、mean和std(标准差),既可以直接调用数组实例的方法,也可以使用顶层的NumPy函数。
- 此处我生成了一些正态分布的随机数,并计算了部分聚合统计数据:
>>> arr = np.random.randn(5,4)
>>> arr
array([[-1.80797791, 1.33606592, 0.46795816, 1.53387629],
[-0.25954537, 0.4898996 , -2.4211862 , 0.01197818],
[ 0.34866204, 0.24130813, 0.67829309, -0.30678474],
[ 0.24134156, -0.14722192, -0.16460745, -0.04512704],
[-0.39224254, -0.51723372, 0.71783118, -1.55365385]])
>>> arr.mean()
-0.0774183287914044
>>> np.mean(arr)
-0.0774183287914044
>>> arr.sum()
-1.548366575828088
- 像mean、sum等函数可以接收一个可选参数axis,这个参数可以用于计算给定轴上的统计值,形成一个下降一维度的数组:
>>> arr.mean(axis=1)
array([ 0.38248062, -0.54471345, 0.24036963, -0.02890371, -0.43632473])
>>> arr.sum(axis=0)
array([-1.86976222, 1.40281802, -0.72171122, -0.35971116])
- arr.mean(1)表示“计算每一列的平均值”,而arr.sum(0)表示“计算行轴向的类和”。
- 其他的方法,例如cumsum和cumprod并不会聚合,它们会产生一个中间结果:
>>> arr = np.array([0,1,2,3,4,5,6,7])
>>> arr.cumsum()
array([ 0, 1, 3, 6, 10, 15, 21, 28], dtype=int32)
- 在多维数组中,像cumsum这样的累计函数返回相同长度的数组,但是可能在指定轴向上根据较低维度的切片进行部分聚合:
>>> arr = np.array([[0,1,2],[3,4,5],[6,7,8]])
>>> arr
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> arr.cumsum(axis=0)
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)
>>> arr.cumprod(axis=1)
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)
- 下表是统计方法的列表,我们将会在后面的章节中看到这些方法的大量示例。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2033b5ea4935add59aa72a6464c19374.png)
布尔值数组的方法
- 在前面介绍的方法,布尔值会被强制为1(True)和0(False)。因此,sum通常可以用于计算布尔值数组中的True的个数:
>>> arr = np.random.randn(100)
>>> (arr > 0).sum()
53
- 对于布尔值数组,有两个非常有用的方法any和all。any检查数组中是否至少有一个True,而all检查是否每个值都是True:
>>> bools = np.array([False,False,True,False])
>>> bools.any()
True
>>> bools.all()
False
- 这些方法也可适用于非布尔值数组,所有的非0元素都会按True处理。
排序
- 和Python的内建列表类型相似,NumPy数组可以使用sort方法按位置排序:
>>> arr = np.random.randn(6)
>>> arr
array([ 0.11418273, -1.41277321, 1.69985834, -0.03271853, -2.3744435 ,
2.20253339])
>>> arr.sort()
>>> arr
array([-2.3744435 , -1.41277321, -0.03271853, 0.11418273, 1.69985834,
2.20253339])
- 你可以在多维数组中根据传递的axis值,沿着轴向对每个一维数组段进行排序:
>>> arr = np.random.randn(5,3)
>>> arr
array([[ 0.15801241, -1.52346998, -0.51402114],
[ 0.79141373, -1.02397976, -0.11795965],
[ 0.27084299, -0.20892054, -0.63789455],
[ 0.50918216, 0.19572398, 0.42971156],
[-0.57140424, 0.23773035, 0.54311631]])
>>> arr.sort(1)
>>> arr
array([[-1.52346998, -0.51402114, 0.15801241],
[-1.02397976, -0.11795965, 0.79141373],
[-0.63789455, -0.20892054, 0.27084299],
[ 0.19572398, 0.42971156, 0.50918216],
[-0.57140424, 0.23773035, 0.54311631]])
- 顶层的np.sort方法返回的是已经排序好的数组拷贝,而不是对原数组按位置排序。下面的例子计算的是一个数组的分位数,并选出分位数所对应的值,这是一种应急的方式:
>>> large_arr = np.random.randn(1000)
>>> large_arr.sort()
>>> large_arr[int(0.05 * len(large_arr))]
-1.8030598888891862
唯一值与其他集合逻辑
- NumPy包含一些针对一维ndarray的基础集合操作。常用的一个方法是np.unique,返回的是数组中唯一值排序后形成的数组:
>>> names = np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
>>> np.unique(names)
array(['Bob', 'Joe', 'Will'], dtype='<U4')
>>> ints = np.array([3,3,3,2,2,1,1,4,4])
>>> np.unique(ints)
array([1, 2, 3, 4])
>>> sorted(set(names))
['Bob', 'Joe', 'Will']
- 另一个函数,np.in1d,可以检查一个数组中的值是否在另外一个数组中,并返回一个布尔值数组:
>>> values = np.array([6,0,0,3,2,5,6])
>>> np.in1d(values,[2,3,6])
array([ True, False, False, True, True, False, True])
- 下表是NumPy中集合函数的列表。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3fbff8e86923d384bc4e53da634cd24a.png)