pythonnumpy多维数组的理解(用Python做科学计算工具篇)
pythonnumpy多维数组的理解(用Python做科学计算工具篇)>>> a = np.array([1 2 3 4]) >>> b = np.array([4 2 2 4]) >>> c = np.array([1 2 3 4]) >>> np.array_equal(a b) False >>> np.array_equal(a c) True 逻辑运算:>>> a = np.array([1 2 3 4]) >>> b = np.array([4 2 2 4]) >>> a == b array([False True False True]) >>> a > b array([False False True False]) 数组
目录
- 元素操作
- Basic reductions
- Broadcasting
- 阵列形状操作
- 排序数据
- 总结
使用标量:
>>> a = np.array([1 2 3 4])
>>> a 1
array([2 3 4 5])
>>> 2**a
array([ 2 4 8 16])
所有算术都按元素进行操作:
>>> b = np.ones(4) 1
>>> a - b
array([-1. 0. 1. 2.])
>>> a * b
array([2. 4. 6. 8.])
>>> j = np.arange(5)
>>> 2**(j 1) - j
array([ 2 3 6 13 28])
这些操作当然比在纯 python 中执行要快得多:
>>> a = np.arange(10000)
>>> %timeit a 1
10000 loops best of 3: 24.3 us per loop
>>> l = range(10000)
>>> %timeit [i 1 for i in l]
1000 loops best of 3: 861 us per loop
数组乘法不是矩阵乘法:
>>> c = np.ones((3 3))
>>> c * c # 不是矩阵乘法!
array([[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]])
矩阵乘法:
>>> c.dot(c)
array([[3. 3. 3.]
[3. 3. 3.]
[3. 3. 3.]])
其他操作
比较:
>>> a = np.array([1 2 3 4])
>>> b = np.array([4 2 2 4])
>>> a == b
array([False True False True])
>>> a > b
array([False False True False])
数组比较:
>>> a = np.array([1 2 3 4])
>>> b = np.array([4 2 2 4])
>>> c = np.array([1 2 3 4])
>>> np.array_equal(a b)
False
>>> np.array_equal(a c)
True
逻辑运算:
>>>
>>> a = np.array([1 1 0 0] dtype=bool)
>>> b = np.array([1 0 1 0] dtype=bool)
>>> np.logical_or(a b)
array([ True True True False])
>>> np.logical_and(a b)
array([ True False False False])
超越函数:
>>>
>>> a = np.arange(5)
>>> np.sin(a)
array([ 0. 0.84147098 0.90929743 0.14112001 -0.7568025 ])
>>> np.log(a)
array([ -inf 0. 0.69314718 1.09861229 1.38629436])
>>> np.exp(a)
array([ 1. 2.71828183 7.3890561 20.08553692 54.59815003])
形状不匹配
>>>
>>> a = np.arange(5)
>>> a np.array([1 2])
ValueError: operands could not be broadcast together with shapes (5 ) (2 )
换位:
>>>
>>> a = np.triu(np.ones((3 3)) 1) # help(np.triu)
>>> a
array([[0. 1. 1.]
[0. 0. 1.]
[0. 0. 0.]])
# triu(m k),保留上三角,左下部分调为0. k=0 表示主对角线。
>>> a.T # 转置
array([[0. 0. 0.]
[1. 0. 0.]
[1. 1. 0.]])
np.reshape
>>>
>>> a = np.arange(9).reshape(3 3)
>>> a.T[0 2] = 999
>>> a.T
array([[ 0 3 999]
[ 1 4 7]
[ 2 5 8]])
>>> a
array([[ 0 1 2]
[ 3 4 5]
[999 7 8]])
线性代数
该子模块NumPy.linalg实现了基本的线性代数,例如求解线性系统、奇异值分解等。但是,它并不一定总是高效,因此我们一般使用scipy.linalg
1.2.2 Basic reductions计算总和>>>
>>> x = np.array([1 2 3 4])
>>> np.sum(x)
10
>>> x.sum()
10
按行和按列求和:
>>>
>>> x = np.array([[1 1] [2 2]])
>>> x
array([[1 1]
[2 2]])
>>> x.sum(axis=0) # columns (first dimension)
array([3 3])
>>> x[: 0].sum() x[: 1].sum()
(3 3)
>>> x.sum(axis=1) # rows (second dimension)
array([2 4])
>>> x[0 :].sum() x[1 :].sum()
(2 4)
更高维度:
>>>
x = np.random.rand(2 2 2)
x
Out[14]:
array([[[0.31174025 0.11658995]
[0.27243086 0.87529974]]
[[0.7719098 0.30237664]
[0.45840615 0.05789042]]])
x.sum(axis=2)
Out[15]:
array([[0.4283302 1.14773061]
[1.07428645 0.51629657]])
x.sum(axis=2)[0 1]
Out[16]: 1.147730606111291
x[0 1 :].sum()
Out[17]: 1.147730606111291
其它
极值:
>>>
x = np.arange(1 17).reshape(4 4)
x
Out[23]:
array([[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
[13 14 15 16]])
x.min()
Out[24]: 1
x.max()
Out[25]: 16
x.argmin() # index of minimum
Out[26]: 0
x.argmax() # index of maximum
Out[27]: 15
逻辑运算:
>>>
>>> np.all([True True False])
False
>>> np.any([True True False])
True
可用于数组比较:
>>>
>>> a = np.zeros((100 100))
>>> np.any(a != 0)
False
>>> np.all(a == a)
True
>>> a = np.array([1 2 3 2])
>>> b = np.array([2 2 3 2])
>>> c = np.array([6 4 4 5])
>>> ((a <= b) & (b <= c)).all()
True
统计数据:
>>>
>>> x = np.array([1 2 3 1])
>>> y = np.array([[1 2 3] [5 6 1]])
>>> x.mean()
1.75
>>> np.median(x)
1.5
>>> np.median(y axis=-1) # last axis
array([2. 5.])
>>> x.std() # full population standard dev.
0.82915619758884995
工作示例:使用随机游走算法
让我们考虑一个简单的一维随机游走过程:在每个时间步,步行者以相等的概率向右或向左跳跃。
我们有兴趣在 t 左跳或右跳后找到与随机步行者原点的距离?我们将模拟许多“步行者”来找到这个定律,我们将使用数组计算技巧来做到这一点:我们将创建一个二维数组,
>>>
>>> n_stories = 1000 # number of walkers
>>> t_max = 200 # 时间
我们随机选择步行的所有步骤 1 或 -1:
>>>
>>> t = np.arange(t_max)
>>> steps = 2 * np.random.randint(0 1 1 (n_stories t_max)) - 1 # 1 because the high value is exclusive
>>> np.unique(steps) # 验证,所有步为1或-1
array([-1 1])
我们通过对时间的步求和来得到距离:
>>>
>>> positions = np.cumsum(steps axis=1) # axis = 1: dimension of time
>>> sq_distance = positions**2
我们得到故事轴的平均值:
>>>
>>> mean_sq_distance = np.mean(sq_distance axis=0)
结果:
我们得到的结论:RMS 距离随着时间的平方根而增长!
1.2.3 Broadcasting- numpy数组的基本操作(加法等)是元素级的
- 这适用于相同大小的数组。尽管如此,也可以对不同大小的数组进行操作,如果NumPy可以转换这些数组,以便它们大小相同:这种转换称为Broadcasting。
下图给出了一个Broadcasting的例子:
让我们验证一下:
>>>
>>> a = np.tile(np.arange(0 40 10) (3 1)).T
>>> a
array([[ 0 0 0]
[10 10 10]
[20 20 20]
[30 30 30]])
>>> b = np.array([0 1 2])
>>> a b
array([[ 0 1 2]
[10 11 12]
[20 21 22]
[30 31 32]])
我们已经在不知不觉中使用了Broadcasting!:
>>>
>>> a = np.ones((4 5))
>>> a[0] = 2 # 我们将维数为0的数组赋值给维数为1的数组
>>> a
array([[2. 2. 2. 2. 2.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]])
一个有用的技巧:
>>>
>>> a = np.arange(0 40 10)
>>> a.shape
(4 )
>>> a = a[: np.newaxis] # adds a new axis -> 2D array
>>> a.shape
(4 1)
>>> a
array([[ 0]
[10]
[20]
[30]])
>>> a b
array([[ 0 1 2]
[10 11 12]
[20 21 22]
[30 31 32]])
Broadcasting 看起来有点神奇,但当我们要解决输出数据是比输入数据多维的数组的问题时,使用它实际上是很自然的。
许多基于网格或基于网络的问题也可以使用Broadcasting。例如,如果我们想计算 5x5 网格上点到原点的距离,我们可以这样做
>>>
>>> x y = np.arange(5) np.arange(5)[: np.newaxis]
>>> distance = np.sqrt(x ** 2 y ** 2)
>>> distance
array([[0. 1. 2. 3. 4. ]
[1. 1.41421356 2.23606798 3.16227766 4.12310563]
[2. 2.23606798 2.82842712 3.60555128 4.47213595]
[3. 3.16227766 3.60555128 4.24264069 5. ]
[4. 4.12310563 4.47213595 5. 5.65685425]])
或颜色:
>>>
>>> plt.pcolor(distance)
>>> plt.colorbar()
备注:该numpy.ogrid()函数允许直接创建上向量 x 和 y,具有两个“重要维度”:
>>>
>>> x y = np.ogrid[0:5 0:5]
>>> x y
(array([[0]
[1]
[2]
[3]
[4]]) array([[0 1 2 3 4]]))
>>> x.shape y.shape
((5 1) (1 5))
>>> distance = np.sqrt(x ** 2 y ** 2)
因此一旦我们需要在网格上处理计算,np.ogrid,用起来非常方便。另一方面,np.mgrid直接为我们不能(或不想)从 Broadcasting 中受益的情况提供索引的矩阵:
>>>
>>> x y = np.mgrid[0:4 0:4]
>>> x
array([[0 0 0 0]
[1 1 1 1]
[2 2 2 2]
[3 3 3 3]])
>>> y
array([[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]])
1.2.4 阵列形状操作展平
>>>
>>> a = np.array([[1 2 3] [4 5 6]])
>>> a.ravel()
array([1 2 3 4 5 6])
>>> a.T
array([[1 4]
[2 5]
[3 6]])
>>> a.T.ravel()
array([1 4 2 5 3 6])
更高的维度:最后的维度是“第一”。
重塑展平的逆操作:
>>>
>>> a.shape
(2 3)
>>> b = a.ravel()
>>> b = b.reshape((2 3))
>>> b
array([[1 2 3]
[4 5 6]])
或者,
>>>
>>> a.reshape((2 -1)) # unspecified (-1) value is inferred
array([[1 2 3]
[4 5 6]])
>>> b[0 0] = 99
>>> a
array([[99 2 3]
[ 4 5 6]])
当心:reshape 也可能返回一个copy!:
>>>
>>> a = np.zeros((3 2))
>>> b = a.T.reshape(3*2)
>>> b[0] = 9
>>> a
array([[0. 0.]
[0. 0.]
[0. 0.]])
要理解这一点,需要了解有关 numpy 数组的内存布局的更多信息。
添加维度使用np.newaxis对象,允许我们向数组添加轴:
>>>
>>> z = np.array([1 2 3])
>>> z
array([1 2 3])
>>> z[: np.newaxis]
array([[1]
[2]
[3]])
>>> z[np.newaxis :]
array([[1 2 3]])
维度洗牌
>>>
>>> a = np.arange(4*3*2).reshape(4 3 2)
>>> a.shape
(4 3 2)
>>> a[0 2 1]
5
>>> b = a.transpose(1 2 0)
>>> b.shape
(3 2 4)
>>> b[2 1 0]
5
还创建了一个view:
>>>
>>> b[2 1 0] = -1
>>> a[0 2 1]
-1
调整大小
可以通过以下方式更改数组的大小ndarray.resize:
>>>
>>> a = np.arange(4)
>>> a.resize((8 ))
>>> a
array([0 1 2 3 0 0 0 0])
但是,不得在其它地方提及它:
>>>
>>> b = a
>>> a.resize((4 ))
ValueError: cannot resize an array that has been referenced or is
referencing another array in this way. Use the resize function
1.2.5。排序数据
沿轴排序:
>>>
>>> a = np.array([[4 3 5] [1 2 1]])
>>> b = np.sort(a axis=1)
>>> b
array([[3 4 5]
[1 1 2]])
分别对每一行进行排序!
就地排序:
>>>
>>> a.sort(axis=1)
>>> a
array([[3 4 5]
[1 1 2]])
用花哨的索引排序:
>>>
>>> a = np.array([4 3 1 2])
>>> j = np.argsort(a)
>>> j
array([2 3 1 0])
>>> a[j]
array([1 2 3 4])
寻找最小值和最大值:
>>>
>>> a = np.array([4 3 1 2])
>>> j_max = np.argmax(a)
>>> j_min = np.argmin(a)
>>> j_max j_min
(0 2)
1.2.6 总结
你需要知道哪些才能开始?
- 知道如何创建数组 : array arange ones zeros.
- 用array.shape 查看数组形状,然后使用切片来获得数组的不同view等。用 reshape调整数组的形状或ravel将其展平。
- 获取数组元素的子集和/或使用掩码修改它们的值>>> a[a < 0] = 0
- 了解对数组的各种操作,例如求均值或最大值 ( array.max() array.mean())。
- 高级用途:掌握整数数组的索引以及Broadcasting。了解更多处理各种数组操作的 NumPy 函数。