向量化的数组运算比纯python
同等程度的运算要快很多。
一个简单的例子,假设我们想要评价函数sqrt(x^2 + y^2)
。np.meshgrid
函数取两个1维的数组,产生一个2位的矩阵,对应于所有两个数组中(x, y)的组合:
import numpy as np
在进行书中的内容之前,先举个例子说明meshgrid
的效果。meshgrid
函数用两个坐标轴上的点在平面上画网格。用法:
[X,Y]=meshgrid(x,y)
[X,Y]=meshgrid(x)
与[X,Y]=meshgrid(x,x)
是等同的
[X,Y,Z]=meshgrid(x,y,z)
生成三维数组,可用来计算三变量的函数和绘制三维立体图
这里,主要以[X,Y]=meshgrid(x,y)
为例,来对该函数进行介绍。
[X,Y] = meshgrid(x,y)
将向量x和y定义的区域转换成矩阵X和Y,其中矩阵X的行向量是向量x的简单复制,而矩阵Y的列向量是向量y的简单复制(注:下面代码中X和Y均是数组,在文中统一称为矩阵了)。
假设x是长度为m的向量,y是长度为n的向量,则最终生成的矩阵X和Y的维度都是 nm (注意不是mn)。
m, n = (5, 3)
x = np.linspace(0, 1, m)
y = np.linspace(0, 1, n)
X, Y = np.meshgrid(x, y)
x
array([ 0. , 0.25, 0.5 , 0.75, 1. ])
y
array([ 0. , 0.5, 1. ])
X
array([[ 0. , 0.25, 0.5 , 0.75, 1. ],
[ 0. , 0.25, 0.5 , 0.75, 1. ],
[ 0. , 0.25, 0.5 , 0.75, 1. ]])
Y
array([[ 0. , 0. , 0. , 0. , 0. ],
[ 0.5, 0.5, 0.5, 0.5, 0.5],
[ 1. , 1. , 1. , 1. , 1. ]])
可以看到X和Y的shape
都是3x5
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
plt.plot(X, Y, marker='.', color='blue', linestyle='none')
[,
,
,
,
]
可以用zip得到网格平面上坐标点的数据:
z = [i for i in zip(X.flat, Y.flat)]
z
[(0.0, 0.0),
(0.25, 0.0),
(0.5, 0.0),
(0.75, 0.0),
(1.0, 0.0),
(0.0, 0.5),
(0.25, 0.5),
(0.5, 0.5),
(0.75, 0.5),
(1.0, 0.5),
(0.0, 1.0),
(0.25, 1.0),
(0.5, 1.0),
(0.75, 1.0),
(1.0, 1.0)]
好了,下面继续进入书中的内容
points = np.arange(-5, 5, 0.01) # 1000 equally spaced points
xs, ys = np.meshgrid(points, points) # xs和ys是一样的
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]])
这里我们用matplotlib
把图画出来:
plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
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.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 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。用where
的话会非常简单:
arr = np.random.randn(4, 4)
arr
array([[ 2.18194474, 0.15001978, -0.77191684, 0.18716397],
[ 1.2083149 , -0.22911585, 1.30880201, 0.14197253],
[ 0.65639111, -1.28394185, 0.65706167, 1.14277598],
[-0.32639966, -0.26880881, -0.10225964, 0.4739671 ]])
arr > 0
array([[ True, True, False, True],
[ True, False, True, True],
[ True, False, True, True],
[False, False, False, True]], dtype=bool)
np.where(arr > 0, 2, -2)
array([[ 2, 2, -2, 2],
[ 2, -2, 2, 2],
[ 2, -2, 2, 2],
[-2, -2, -2, 2]])
我们可以结合标量和数组。比如只把整数变为2,其他仍未原来的数字:
np.where(arr > 0, 2, arr) # set only positive value to 2
array([[ 2. , 2. , -0.77191684, 2. ],
[ 2. , -0.22911585, 2. , 2. ],
[ 2. , -1.28394185, 2. , 2. ],
[-0.32639966, -0.26880881, -0.10225964, 2. ]])
一些能计算统计值的数学函数能基于整个数组,或者沿着一个axis
(轴)。可以使用aggregations
(often called reductions
,汇总,或被叫做降维),比如sum, mean, and std
(标准差).
下面是一些aggregate statistics
(汇总统计):
arr = np.random.randn(5, 4)
arr
array([[-1.53575656, -1.39268394, -1.02284353, -1.03165049],
[ 0.53301867, 0.50258973, -0.49389656, 0.24610963],
[ 0.95377174, -1.57268184, 0.42969986, 1.22912566],
[ 0.73686692, -2.82328155, 0.48018497, -1.38046692],
[ 0.94164808, 0.19599722, -0.88779738, -0.87556277]])
arr.mean()
-0.33838045197794597
np.mean(arr)
-0.33838045197794597
arr.sum()
-6.767609039558919
mean, sum
这样的函数能接受axis
作为参数来计算统计数字,返回的结果维度更少:
arr.mean(axis=1)
array([-1.24573363, 0.19695537, 0.25997886, -0.74667415, -0.15642871])
arr.sum(axis=0)
array([ 1.62954886, -5.09006038, -1.49465263, -1.81244489])
这里arr.mean(1)
表示,compute mean acros the columns
(计算各列之间的平均值)。arr.sum(0)
表示,compute sum down the rows
(计算各行总和)。
其他一些方法,像cumsum
和cumprod
不做汇总,而是产生一个中间结果的数组:
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
arr.cumsum()
array([ 0, 1, 3, 6, 10, 15, 21, 28])
上面的计算是一个累加的结果,0+1=1,1+2=3,3+3=6
以此类推。
np.cumsum?
对于多维数组,accumulation functions
(累积函数)比如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]])
arr.cumprod(axis=1) # 沿着列乘法
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]])
sum
是用来计算布尔数组中有多少个true
的:
arr = np.random.randn(100)
(arr > 0).sum() # Number of positive values
46
有两个其他方法,any
和all
,对于布尔数组特别有用。any
检测数组中只要有一个ture
返回就是true
,而all
检测数组中都是true
才会返回true
。
bools = np.array([False, False, True, False])
bools.any()
True
bools.all()
False
numpy
中也有sort
方法:
np.random.randn?
# 返回符合正态分布的数值
arr = np.random.randn(6)
arr
array([ 1.93663555, -1.29810982, 0.83366006, 0.51674613, 2.32879117,
1.07342758])
arr.sort()
arr
array([-1.29810982, 0.51674613, 0.83366006, 1.07342758, 1.93663555,
2.32879117])
如果是多维数组,还可以按axis
来排序:
arr = np.random.randn(5, 3)
arr
array([[-0.76658562, -1.00222899, 0.39039437],
[ 0.23100317, -1.0581081 , 1.69177329],
[ 1.0239365 , 0.84698669, -0.97911915],
[ 0.76255951, 0.27828523, 0.41807172],
[ 0.40792019, -1.19514714, -1.41666804]])
arr.sort(1)
arr
array([[-1.00222899, -0.76658562, 0.39039437],
[-1.0581081 , 0.23100317, 1.69177329],
[-0.97911915, 0.84698669, 1.0239365 ],
[ 0.27828523, 0.41807172, 0.76255951],
[-1.41666804, -1.19514714, 0.40792019]])
上面是直接调用数组的sort
方法,会改变原有数组的顺序。但如果使用np.sort()
函数的话,会生成一个新的排序后的结果。
一个计算分位数的快捷方法是先给数组排序,然后选择某个排名的值:
large_arr = np.random.randn(1000)
large_arr.sort()
large_arr[int(0.05 * len(large_arr))] # 5% quantile
-1.6908607973872243
Numpy
也有一些基本的集合操作用于一维数组。np.unique
,能返回排好序且不重复的值:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names)
array(['Bob', 'Joe', 'Will'],
dtype='
ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
np.unique(ints)
array([1, 2, 3, 4])
如果用纯python
代码来实现的话,要这么写:
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], dtype=bool)