numpy经验及技巧

numpy 的江湖地位和作用就不多说了,现在很多深度学习框架的 python 接口都有点 numpy 风格, 它们的大部分思想可以共用,本文主要记录我的 numpy 使用的一些经验,以及讲述一些 numpy 中比较难理解的部分,如:轴 axis 的概念, transpose() 函数的原理,还会分享一些 numpy 的使用技巧。

本文主要分两大部分:

  • numpy 中比较难掌握的概念
  • numpy 的使用技巧

numpy 中比较难掌握的概念

axis

轴的概念有点抽象,特别是对于高维空间。大家熟悉的二维、三维空间中的坐标轴就是这里所说的轴。

在 numpy 中,维度(dimensions)是通过轴(axes)来扩展的,轴的个数被称作 rank 。这里的 rank 不是线性代数中的 rank(秩),它指代的是维数(number of dimensions)

The number of axes (dimensions) of the array. In the Python world, the number of dimensions is referred to as rank.

举例子前先说明,下文的代码直接使用 np 来代替 numpy 就不每次引入了

1
import numpy as np

维数可通过 ndim() 来获得

1
2
x = np.arange(9).reshape((3, 3))
print(np.ndim(x)) # 2

这里得到的 rank 是 2 也就是维数是 2,也就是说轴有两个(axis 0、axis 1),轴是从 0 开始表示的,如 axis 0, axis 1

通过 np.shape() 或者直接 x.shape 可以获得形状也就是各维度的大小

1
2
x = np.array([[1,2,3],[2,3,4],[3,4,9]])
print(x.shape) # (3, 3)

axis 的计数方式

axis 是从外到内计数的,很多函数都可以指定轴,如 np.sum() , np.mean()

假设我们有一个矩阵 x

\[\begin{bmatrix} 0 & 1 \\\\ 2 & 3 \end{bmatrix}\]

在 numpy 中的表示为:

1
2
x = [[0, 1],
[2, 3]]

对应的轴为:

1
2
3
4
5
6
7
axis = 0
|
| axis = 1
| |
↓ ↓
[ [0, 1],
[2, 3]]

对于在指定轴上求和,

对于 np.sum(x, axis=0)

\[\left [ \ [0\ \ 1] + [2\ \ 3]\ \right ] = [2\ \ 4]\]

对于 np.sum(x, axis=1)

\[\begin{bmatrix} [0] \\\\ [2] \end{bmatrix} + \begin{bmatrix} [1] \\\\ [3] \end{bmatrix} = \begin{bmatrix} 1 \\\\ 5 \end{bmatrix}\]

观察可发现 numpy 会默认压缩维度,np.sum(x, axis=0) 会把 0 轴压缩得到 (, 2)np.sum(x, axis=1) 会把 1 轴压缩得到 (2, ),那如何控制不压缩维度呢?可通过 keepdims 参数来控制,这个参数默认值是 False

np.sum(x, axis=0, keepdims=True)

\[\left [ \ [0\ \ 1] + [2\ \ 3]\ \right ] = [[2\ \ 4]]\]

此时 shape 为 (1, 2)

np.sum(x, axis=1, keepdims=True)

\[\begin{bmatrix} [0] \\\\ [2] \end{bmatrix} + \begin{bmatrix} [1] \\\\ [3] \end{bmatrix} = \begin{bmatrix} [1] \\\\ [5] \end{bmatrix}\]

此时 shape 为 (2, 1)

再举一个三维的例子

\[x = \begin{bmatrix} \begin{bmatrix} [0, 1] \\\\ [2, 3] \end{bmatrix}, \\\\ \begin{bmatrix} [4, 5] \\\\ [6, 7] \end{bmatrix} \end{bmatrix}\]

对于 np.sum(x, axis=0)

\[\begin{bmatrix} \begin{bmatrix} 0 & 1\\\\ 2 & 3 \end{bmatrix} + \begin{bmatrix} 4 & 5\\\\ 6 & 7 \end{bmatrix} \end{bmatrix} = \begin{bmatrix} \begin{bmatrix} 4 & 6\\ 8 & 11 \end{bmatrix} \end{bmatrix}\]

对于 np.sum(x, axis=1)

\[\begin{bmatrix} \begin{bmatrix} [0\ 1] + [2\ 3] \end{bmatrix}\\\\ \begin{bmatrix} [4\ 5] + [6\ 7] \end{bmatrix} \end{bmatrix} = \begin{bmatrix} \begin{bmatrix} 2 & 4 \end{bmatrix}\\\\ \begin{bmatrix} 10 & 20 \end{bmatrix} \end{bmatrix}\]

对于 np.sum(x, axis=2)

\[\begin{bmatrix} \begin{bmatrix} [0] \\\\ [2] \end{bmatrix} + \begin{bmatrix} [1] \\\\ [3] \end{bmatrix}\\\\ \begin{bmatrix} [4] \\\\ [6] \end{bmatrix} + \begin{bmatrix} [5] \\\\ [7] \end{bmatrix} \end{bmatrix} = \begin{bmatrix} \begin{bmatrix} 1 & 5 \end{bmatrix}\\\\ \begin{bmatrix} 9 & 13 \end{bmatrix} \end{bmatrix}\]

transpose

简单的理解 transpose 可以改变 shape 中各个数的顺序

如:

1
2
3
x = np.arange(24).reshape((2, 3, 4))

x.transpose((1, 0, 2)).shape) # => (3, 2, 4)

其中

\[x = \begin{bmatrix} \begin{bmatrix} [0 & 1 & 2 & 3]\\\\ [4 & 5 & 6 & 7]\\\\ [8 & 9 & 10 & 11] \end{bmatrix}\\\\ \begin{bmatrix} [12 & 13 & 14 & 15]\\\\ [16 & 17 & 18 & 19]\\\\ [20 & 21 & 22 & 23] \end{bmatrix} \end{bmatrix}\]

有:

  • \(axis=0\) 有 2 个元素
  • \(axis=1\) 有 3 个元素
  • \(axis=2\) 有 4 个元素

\(axis=0\) 为例,整个数组有 24 个数,\(axis=0\) 有 2 个元素,所以每隔 \(\frac{24}{2} = 12\) 就跳一下,也就是说 \(axis=0\) 中各元素对应位置值的差为 \(12\),如此时有: \(x[1][0][0] - x[0][0][0] = 12\)\(x[0][1][0] - x[0][0][0] = 4\)

同理对于 \(axis=1\) 的每个元素,每隔 \(\frac{12}{3}=4\) 就跳一下

对于 \(axis=2\) 的每个元素,每隔 \(\frac{4}{4} = 1\) 就跳一下

我们定义一个跳数记录元组 strides ,则上面的 strides 为 \((12, 4, 1)\)

transpose的本质 就是对 strides 中各个数的顺序进行调换

如:

1
x.transpose((1, 0, 2))

输出为

1
2
3
4
5
6
7
8
array([[[ 0,  1,  2,  3],
[12, 13, 14, 15]],

[[ 4, 5, 6, 7],
[16, 17, 18, 19]],

[[ 8, 9, 10, 11],
[20, 21, 22, 23]]])

可以看到上述的跳数元组变为了 \((4, 12, 1)\),此时 \(x[1][0][0] - x[0][0][0] = 4\), \(x[0][1][0] - x[0][0][0] = 14\)

numpy 常用技巧

这部分内容主要来自:100 numpy exercises

下面会以 Q&A 的形式进行

Q Reverse a vector (first element becomes last) (★☆☆)

1
2
Z = np.arange(50)
Z = Z[::-1]

这个其实用到了 python 中的切片

1
<object_name>[<start_index>, <stop_index>, <step>]

object is going to slice every “step” index from the given start index, till the stop index (excluding the stop index) and return it to you.

Q Create a 3x3 matrix with values ranging from 0 to 8 (★☆☆)

1
Z = np.arange(9).reshape((3, 3))

Q Find indices of non-zero elements from [1,2,0,0,4,0] (★☆☆)

1
2
Z = np.nonzero([1, 2, 0, 0, 4, 0])
# Z 会得到非 0 值的下标

Q Create a 3x3 identity matrix (★☆☆)

1
2
3
4
5
6
7
Z = np.eye(3)

"""
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
"""

Q Create a 10x10 array with random values and find the minimum and maximum values (★☆☆)

1
2
Z = np.random.random((10,10))
Zmin, Zmax = Z.min(), Z.max()

Q Create a 2d array with 1 on the border and 0 inside (★☆☆)

1
2
3
4
5
6
7
8
9
Z = np.ones((5, 5))
Z[1:-1, 1:-1] = 0
"""
array([[1., 1., 1., 1., 1.],
[1., 0., 0., 0., 1.],
[1., 0., 0., 0., 1.],
[1., 0., 0., 0., 1.],
[1., 1., 1., 1., 1.]])
"""

Q How to add a border (filled with 0’s) around an existing array? (★☆☆)

1
2
3
4
5
6
7
8
9
10
Z = np.ones((4, 4))
Z = np.pad(Z, pad_width=1, mode="constant", constant_values=0)
"""
array([[0., 0., 0., 0., 0., 0.],
[0., 1., 1., 1., 1., 0.],
[0., 1., 1., 1., 1., 0.],
[0., 1., 1., 1., 1., 0.],
[0., 1., 1., 1., 1., 0.],
[0., 0., 0., 0., 0., 0.]])
"""

Q What is the result of the following expression? (★☆☆)

1
2
3
4
5
6
print(0 * np.nan)              # nan
print(np.nan == np.nan) # False
print(np.inf > np.nan) # False
print(np.nan - np.nan) # nan
print(np.nan in set([np.nan])) # True
print(0.3 == 3 * 0.1) # False

Q Create a 5x5 matrix with values 1,2,3,4 just below the diagonal (★☆☆)

1
2
3
4
5
6
7
8
9
Z = np.diag(1+np.arange(4),k=-1)

"""
array([[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 4, 0]])
"""

Q Consider a (6,7,8) shape array, what is the index (x,y,z) of the 100th element?

1
2
3
4
5
6
7
np.unravel_index(99,(6,7,8))

"""
(1, 5, 3)

1 * (7 * 8) + 5 * (8) + 3 = 99
"""

Q Normalize a 5x5 random matrix (★☆☆)

1
2
Z = np.random.random((5,5))
Z = (Z - np.mean (Z)) / (np.std (Z))

Q Given a 1D array, negate all elements which are between 3 and 8, in place. (★☆☆)

1
2
Z = np.arrange(11)
Z[(3 < Z) & (Z <= 8)] *= -1

Q Create random vector of size 10 and replace the maximum value by 0 (★★☆)

1
2
Z = np.random.random(10)
Z[Z.argmax()] = 0

Q How to find the closest value (to a given scalar) in a vector? (★★☆)

1
2
3
4
Z = np.arange(100)
v = np.random.uniform(0,100)
index = (np.abs(Z-v)).argmin()
v = Z[index]

Q What is the equivalent of enumerate for numpy arrays? (★★☆)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Z = np.arange(9).reshape(3, 3)
for idx, val in np.ndenumerate(Z):
print(idx, val)

"""
(0, 0) 0
(0, 1) 1
(0, 2) 2
(1, 0) 3
(1, 1) 4
(1, 2) 5
(2, 0) 6
(2, 1) 7
(2, 2) 8
"""

Q How to swap two rows of an array? (★★★)

1
2
Z = np.arange(25).reshape(5, 5)
Z[[0, 1]] = Z[[1, 0]] # 交换第 0 行和第 1 行

Q Compute a matrix rank (★★★)

1
2
3
4
5
6
# Author: Stefan van der Walt


Z = np.random.uniform(0,1,(10,10))
U, S, V = np.linalg.svd(Z) # Singular Value Decomposition
rank = np.sum(S > 1e-10)

refrence

sean lee wechat
欢迎关注我的公众号!
感谢支持!