NumPy快速启动

先决条件

你需要懂一点Python。如需复习,请参阅 Python tutorial .

要使用这些示例,您需要 matplotlib 安装在NumPy之外。

学习者简介

这是NumPy中代数和数组的快速概述。它演示了n维 (n>=2 )数组被表示并且可以被操纵。特别是,如果您不知道如何将公共函数应用于n维数组(不使用for循环),或者您想了解n维数组的轴和形状属性,本文可能会有所帮助。

学习目标

阅读后,你应该能够:

  • 了解NumPy中一维、二维和n维数组的区别;

  • 了解如何在不使用for循环的情况下对n维数组应用一些线性代数运算;

  • 了解n维数组的形状属性。

基础知识

NumPy的主要对象是同构多维数组。它是一个元素表(通常是数字),所有元素都是相同类型的,由一个非负整数元组索引。在NumPy中,维度被称为 axes .

例如,三维空间中的点坐标 [1, 2, 1] 有一个轴。这个轴有3个元素,所以我们说它有3个长度。在下面的示例中,数组有两个轴。第一轴的长度为2,第二轴的长度为3。

[[1., 0., 0.],
 [0., 1., 2.]]

调用numpy的数组类 ndarray . 别名也知道 array . 注意 numpy.array 与标准的python库类不同 array.array 它只处理一维数组并提供较少的功能。更重要的属性 ndarray 对象是:

ndarray.ndim

数组的轴(维度)数。

ndarray.shape

数组的维度。这是一个整数元组,指示数组在每个维度中的大小。对于矩阵 n 行和 m 柱, shape(n,m) . 的长度 shape 因此tuple是轴的数目, ndim .

ndarray.size

数组的元素总数。这等于 shape .

ndarray.dtype

描述数组中元素类型的对象。可以使用标准的python类型创建或指定dtype。此外,numpy还提供自己的类型。numpy.int32、numpy.int16和numpy.float64是一些示例。

ndarray.itemsize

数组中每个元素的字节大小。例如,类型的元素数组 float64itemsize 8(=64/8),而类型之一 complex32itemsize 4(=32/8)。它相当于 ndarray.dtype.itemsize .

ndarray.data

包含数组的实际元素的缓冲区。通常,我们不需要使用这个属性,因为我们将使用索引工具访问数组中的元素。

一个例子

>>> import numpy as np
>>> a = np.arange(15).reshape(3, 5)
>>> a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>> a.shape
(3, 5)
>>> a.ndim
2
>>> a.dtype.name
'int64'
>>> a.itemsize
8
>>> a.size
15
>>> type(a)
<class 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<class 'numpy.ndarray'>

数组创建

创建数组有几种方法。

例如,可以使用 array 功能。结果数组的类型是根据序列中元素的类型推导出来的。

>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int64')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')

一个常见的错误是调用 array 使用多个参数,而不是提供单个序列作为参数。

>>> a = np.array(1, 2, 3, 4)    # WRONG
Traceback (most recent call last):
  ...
TypeError: array() takes from 1 to 2 positional arguments but 4 were given
>>> a = np.array([1, 2, 3, 4])  # RIGHT

array 将序列转换为二维数组、序列转换为三维数组等等。

>>> b = np.array([(1.5, 2, 3), (4, 5, 6)])
>>> b
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

数组的类型也可以在创建时显式指定:

>>> c = np.array([[1, 2], [3, 4]], dtype=complex)
>>> c
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])

通常,数组的元素最初是未知的,但其大小是已知的。因此,numpy提供了几个函数来创建带有初始占位符内容的数组。这些都将增加阵列的必要性降到最低,这是一项昂贵的操作。

函数 zeros 创建一个满是零的数组,函数 ones 创建一个满是1的数组,函数 empty 创建初始内容是随机的且取决于内存状态的数组。默认情况下,创建的数组的数据类型为 float64 ,但可以通过关键字参数指定 dtype .

>>> np.zeros((3, 4))
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
>>> np.ones((2, 3, 4), dtype=np.int16)
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int16)
>>> np.empty((2, 3))
array([[3.73603959e-262, 6.02658058e-154, 6.55490914e-260],  # may vary
       [5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])

要创建数字序列,NumPy提供 arange 类似于Python内置的 range ,但返回一个数组。

>>> np.arange(10, 30, 5)
array([10, 15, 20, 25])
>>> np.arange(0, 2, 0.3)  # it accepts float arguments
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

什么时候? arange 与浮点参数一起使用时,由于浮点精度有限,通常无法预测获得的元素数。因此,通常最好使用函数 linspace 作为参数接收所需元素的数量,而不是步骤:

>>> from numpy import pi
>>> np.linspace(0, 2, 9)                   # 9 numbers from 0 to 2
array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])
>>> x = np.linspace(0, 2 * pi, 100)        # useful to evaluate function at lots of points
>>> f = np.sin(x)

参见

array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.Generator.rand, numpy.random.Generator.randn, fromfunction, fromfile

列印阵列

打印数组时,numpy以类似于嵌套列表的方式显示数组,但使用以下布局:

  • 最后一个轴从左到右打印,

  • “倒数第二个”从上到下打印,

  • 其余的部分也从上到下打印,每个部分与下一个部分之间用空行分隔。

然后一维数组被打印成行,二维数组被打印成矩阵,三维数组被打印成矩阵列表。

>>> a = np.arange(6)                    # 1d array
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4, 3)     # 2d array
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2, 3, 4)  # 3d array
>>> print(c)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

below 获取更多详细信息 reshape .

如果数组太大而无法打印,numpy会自动跳过数组的中心部分,只打印角:

>>> print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
>>>
>>> print(np.arange(10000).reshape(100, 100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]

要禁用此行为并强制numpy打印整个数组,可以使用 set_printoptions .

>>> np.set_printoptions(threshold=sys.maxsize)  # sys module should be imported

基本操作

应用数组上的算术运算符 元素的 . 将创建一个新数组并用结果填充。

>>> a = np.array([20, 30, 40, 50])
>>> b = np.arange(4)
>>> b
array([0, 1, 2, 3])
>>> c = a - b
>>> c
array([20, 29, 38, 47])
>>> b**2
array([0, 1, 4, 9])
>>> 10 * np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
>>> a < 35
array([ True,  True, False, False])

与许多矩阵语言不同,产品运算符 * 在numpy数组中操作elementwise。矩阵产品可以使用 @ 运算符(在python中>=3.5)或 dot 函数或方法:

>>> A = np.array([[1, 1],
...               [0, 1]])
>>> B = np.array([[2, 0],
...               [3, 4]])
>>> A * B     # elementwise product
array([[2, 0],
       [0, 4]])
>>> A @ B     # matrix product
array([[5, 4],
       [3, 4]])
>>> A.dot(B)  # another matrix product
array([[5, 4],
       [3, 4]])

一些操作,例如 +=*= ,就地修改现有数组,而不是创建新数组。

>>> rg = np.random.default_rng(1)  # create instance of default random number generator
>>> a = np.ones((2, 3), dtype=int)
>>> b = rg.random((2, 3))
>>> a *= 3
>>> a
array([[3, 3, 3],
       [3, 3, 3]])
>>> b += a
>>> b
array([[3.51182162, 3.9504637 , 3.14415961],
       [3.94864945, 3.31183145, 3.42332645]])
>>> a += b  # b is not automatically converted to integer type
Traceback (most recent call last):
    ...
numpy.core._exceptions._UFuncOutputCastingError: Cannot cast ufunc 'add' output from dtype('float64') to dtype('int64') with casting rule 'same_kind'

当对不同类型的数组进行操作时,得到的数组的类型对应于更一般或更精确的数组(一种称为上溯的行为)。

>>> a = np.ones(3, dtype=np.int32)
>>> b = np.linspace(0, pi, 3)
>>> b.dtype.name
'float64'
>>> c = a + b
>>> c
array([1.        , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> d = np.exp(c * 1j)
>>> d
array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
       -0.54030231-0.84147098j])
>>> d.dtype.name
'complex128'

许多一元操作,例如计算数组中所有元素的总和,都是作为 ndarray 班级。

>>> a = rg.random((2, 3))
>>> a
array([[0.82770259, 0.40919914, 0.54959369],
       [0.02755911, 0.75351311, 0.53814331]])
>>> a.sum()
3.1057109529998157
>>> a.min()
0.027559113243068367
>>> a.max()
0.8277025938204418

默认情况下,这些操作应用于数组,就好像它是一个数字列表,而不管其形状如何。但是,通过指定 axis 参数可以沿数组的指定轴应用操作:

>>> b = np.arange(12).reshape(3, 4)
>>> b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> b.sum(axis=0)     # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)     # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)  # cumulative sum along each row
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]])

通用函数

NumPy提供了常见的数学函数,如sin、cos和exp。在NumPy中,这些函数被称为“通用函数”(ufunc )在numpy中,这些函数对数组进行元素操作,生成一个数组作为输出。

>>> B = np.arange(3)
>>> B
array([0, 1, 2])
>>> np.exp(B)
array([1.        , 2.71828183, 7.3890561 ])
>>> np.sqrt(B)
array([0.        , 1.        , 1.41421356])
>>> C = np.array([2., -1., 4.])
>>> np.add(B, C)
array([2., 0., 6.])

索引、切片和迭代

One-dimensional 数组可以被索引、切片和迭代,就像 lists 以及其他的python序列。

>>> a = np.arange(10)**3
>>> a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> # equivalent to a[0:6:2] = 1000;
>>> # from start to position 6, exclusive, set every 2nd element to 1000
>>> a[:6:2] = 1000
>>> a
array([1000,    1, 1000,   27, 1000,  125,  216,  343,  512,  729])
>>> a[::-1]  # reversed a
array([ 729,  512,  343,  216,  125, 1000,   27, 1000,    1, 1000])
>>> for i in a:
...     print(i**(1 / 3.))
...
9.999999999999998
1.0
9.999999999999998
3.0
9.999999999999998
4.999999999999999
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998

多维的 数组每个轴可以有一个索引。这些索引以一个用逗号分隔的元组给出:

>>> def f(x, y):
...     return 10 * x + y
...
>>> b = np.fromfunction(f, (5, 4), dtype=int)
>>> b
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33],
       [40, 41, 42, 43]])
>>> b[2, 3]
23
>>> b[0:5, 1]  # each row in the second column of b
array([ 1, 11, 21, 31, 41])
>>> b[:, 1]    # equivalent to the previous example
array([ 1, 11, 21, 31, 41])
>>> b[1:3, :]  # each column in the second and third row of b
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

当提供的索引少于轴的数目时,丢失的索引被视为完整的切片。:

>>> b[-1]   # the last row. Equivalent to b[-1, :]
array([40, 41, 42, 43])

中括号内的表达式 b[i] is treated as an i followed by as many instances of : as needed to represent the remaining axes. NumPy also allows you to write this using dots as `` 乙 [i、 是的。。。] ''.

这个 dots (... )根据需要表示尽可能多的冒号,以生成完整的索引元组。例如,如果 x 是一个有5个轴的数组,然后

  • x[1, 2, ...] 等于 x[1, 2, :, :, :]

  • x[..., 3]x[:, :, :, :, 3]

  • x[4, ..., 5, :]x[4, :, :, 5, :] .

>>> c = np.array([[[  0,  1,  2],  # a 3D array (two stacked 2D arrays)
...                [ 10, 12, 13]],
...               [[100, 101, 102],
...                [110, 112, 113]]])
>>> c.shape
(2, 2, 3)
>>> c[1, ...]  # same as c[1, :, :] or c[1]
array([[100, 101, 102],
       [110, 112, 113]])
>>> c[..., 2]  # same as c[:, :, 2]
array([[  2,  13],
       [102, 113]])

迭代 在多维数组上,相对于第一个轴:

>>> for row in b:
...     print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

但是,如果要对数组中的每个元素执行操作,可以使用 flat attribute which is an iterator 在数组的所有元素上::

>>> for element in b.flat:
...     print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43

参见

索引, 索引 (reference), newaxis, ndenumerate, indices

形状操纵

更改数组的形状

数组的形状由沿每个轴的元素数给定:

>>> a = np.floor(10 * rg.random((3, 4)))
>>> a
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])
>>> a.shape
(3, 4)

数组的形状可以通过各种命令更改。请注意,以下三个命令都返回一个已修改的数组,但不更改原始数组::

>>> a.ravel()  # returns the array, flattened
array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
>>> a.reshape(6, 2)  # returns the array with a modified shape
array([[3., 7.],
       [3., 4.],
       [1., 4.],
       [2., 2.],
       [7., 2.],
       [4., 9.]])
>>> a.T  # returns the array, transposed
array([[3., 1., 7.],
       [7., 4., 2.],
       [3., 2., 4.],
       [4., 2., 9.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4)

数组中元素的顺序 ravel 通常是“C风格”,即最右边的索引“变化最快”,所以后面的元素 a[0, 0]a[0, 1] . 如果数组被重塑为其他形状,那么数组同样被视为“C样式”。NumPy通常创建按此顺序存储的数组,因此 ravel 通常不需要复制它的参数,但是如果数组是由另一个数组的切片或使用不寻常的选项创建的,则可能需要复制它。功能 ravelreshape 也可以使用可选参数指示使用FORTRAN样式的数组,其中最左边的索引更改最快。

这个 reshape 函数返回其具有已修改形状的参数,而 ndarray.resize 方法修改数组本身::

>>> a
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])
>>> a.resize((2, 6))
>>> a
array([[3., 7., 3., 4., 1., 4.],
       [2., 2., 7., 2., 4., 9.]])

如果尺寸给定为 -1 在整形操作中,其他尺寸将自动计算:

>>> a.reshape(3, -1)
array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])

将不同的阵列堆叠在一起

几个数组可以沿不同的轴堆叠在一起:

>>> a = np.floor(10 * rg.random((2, 2)))
>>> a
array([[9., 7.],
       [5., 2.]])
>>> b = np.floor(10 * rg.random((2, 2)))
>>> b
array([[1., 9.],
       [5., 1.]])
>>> np.vstack((a, b))
array([[9., 7.],
       [5., 2.],
       [1., 9.],
       [5., 1.]])
>>> np.hstack((a, b))
array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])

函数 column_stack 将一维数组作为列堆叠到二维数组中。它相当于 hstack 仅适用于二维阵列:

>>> from numpy import newaxis
>>> np.column_stack((a, b))  # with 2D arrays
array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])
>>> a = np.array([4., 2.])
>>> b = np.array([3., 8.])
>>> np.column_stack((a, b))  # returns a 2D array
array([[4., 3.],
       [2., 8.]])
>>> np.hstack((a, b))        # the result is different
array([4., 2., 3., 8.])
>>> a[:, newaxis]  # view `a` as a 2D column vector
array([[4.],
       [2.]])
>>> np.column_stack((a[:, newaxis], b[:, newaxis]))
array([[4., 3.],
       [2., 8.]])
>>> np.hstack((a[:, newaxis], b[:, newaxis]))  # the result is the same
array([[4., 3.],
       [2., 8.]])

另一方面,功能 row_stack 等于 vstack 对于任何输入数组。事实上, row_stack 是一个别名 vstack ::

>>> np.column_stack is np.hstack
False
>>> np.row_stack is np.vstack
True

一般来说,对于二维以上的数组, hstack 沿着第二个轴堆叠, vstack 沿着第一个轴堆叠,以及 concatenate 允许一个可选参数,给出发生连接的轴的编号。

Note

在复杂情况下, r_c_ 用于通过沿一个轴堆叠数字来创建阵列。它们允许使用范围文字 : . ::

>>> np.r_[1:4, 0, 4]
array([1, 2, 3, 0, 4])

将数组用作参数时, r_c_ 类似于 vstackhstack 在它们的默认行为中,但是允许使用一个可选参数,给出要连接的轴的编号。

将一个数组拆分为几个较小的数组

使用 hsplit ,可以沿水平轴拆分数组,方法是指定要返回的等形状数组的数目,或者指定在其后面进行拆分的列:

>>> a = np.floor(10 * rg.random((2, 12)))
>>> a
array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
       [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])
>>> # Split `a` into 3
>>> np.hsplit(a, 3)
[array([[6., 7., 6., 9.],
       [8., 5., 5., 7.]]), array([[0., 5., 4., 0.],
       [1., 8., 6., 7.]]), array([[6., 8., 5., 2.],
       [1., 8., 1., 0.]])]
>>> # Split `a` after the third and the fourth column
>>> np.hsplit(a, (3, 4))
[array([[6., 7., 6.],
       [8., 5., 5.]]), array([[9.],
       [7.]]), array([[0., 5., 4., 0., 6., 8., 5., 2.],
       [1., 8., 6., 7., 1., 8., 1., 0.]])]

vsplit 沿垂直轴拆分,以及 array_split 允许指定沿哪个轴拆分。

副本和视图

在操作和操作数组时,它们的数据有时会被复制到新的数组中,有时则不会。对于初学者来说,这常常是一个困惑的根源。有三种情况:

根本没有复制品

简单的赋值不会复制对象或其数据。

>>> a = np.array([[ 0,  1,  2,  3],
...               [ 4,  5,  6,  7],
...               [ 8,  9, 10, 11]])
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True

python将可变对象作为引用传递,因此函数调用不进行复制。

>>> def f(x):
...     print(id(x))
...
>>> id(a)  # id is a unique identifier of an object
148293216  # may vary
>>> f(a)
148293216  # may vary

查看或浅复制

不同的数组对象可以共享相同的数据。这个 view 方法创建一个新的数组对象,该对象查看相同的数据。

>>> c = a.view()
>>> c is a
False
>>> c.base is a            # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c = c.reshape((2, 6))  # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0, 4] = 1234         # a's data changes
>>> a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])

切片数组返回其视图::

>>> s = a[:, 1:3]
>>> s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

深拷贝

这个 copy 方法生成数组及其数据的完整副本。

>>> d = a.copy()  # a new array object with new data is created
>>> d is a
False
>>> d.base is a  # d doesn't share anything with a
False
>>> d[0, 0] = 9999
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

有时 copy 如果不再需要原始数组,则应在切片后调用。例如,假设 a 是一个巨大的中间结果和最终结果 b 只包含一小部分 a ,在构造时应进行深度复制 b 切片:

>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a  # the memory of ``a`` can be released.

如果 b = a[:100] 而是使用, a 被引用 b 即使 del a 执行。

功能和方法概述

下面是按类别排列的一些有用的numpy函数和方法名称的列表。见 日常工作 完整的列表。

数组创建

arange, array, copy, empty, empty_like, eye, fromfile, fromfunction, identity, linspace, logspace, mgrid, ogrid, ones, ones_like, r_, zeros, zeros_like

转换

ndarray.astype, atleast_1d, atleast_2d, atleast_3d, mat

手法

array_split, column_stack, concatenate, diagonal, dsplit, dstack, hsplit, hstack, ndarray.item, newaxis, ravel, repeat, reshape, resize, squeeze, swapaxes, take, transpose, vsplit, vstack

问题

all, any, nonzero, where

排序

argmax, argmin, argsort, max, min, ptp, searchsorted, sort

操作

choose, compress, cumprod, cumsum, inner, ndarray.fill, imag, prod, put, putmask, real, sum

基础统计学

cov, mean, std, var

基本线性代数

cross, dot, outer, linalg.svd, vdot

较少基础

广播规则

广播允许通用功能以有意义的方式处理形状不完全相同的输入。

广播的第一条规则是,如果所有输入数组的维数都不相同,则会重复在较小数组的形状前面加上“1”,直到所有数组的维数都相同。

第二个广播规则确保沿特定维度的大小为1的数组的大小与沿该维度的最大形状的数组的大小相同。假定数组元素的值在“广播”数组的该维度上相同。

应用广播规则后,所有阵列的大小必须匹配。有关更多详细信息,请参见 广播 .

高级索引和索引技巧

numpy提供了比常规的python序列更多的索引工具。正如我们前面看到的,除了按整数和片索引之外,还可以按整数数组和布尔数组索引数组。

用索引数组索引

>>> a = np.arange(12)**2  # the first 12 square numbers
>>> i = np.array([1, 1, 3, 8, 5])  # an array of indices
>>> a[i]  # the elements of `a` at the positions `i`
array([ 1,  1,  9, 64, 25])
>>>
>>> j = np.array([[3, 4], [9, 7]])  # a bidimensional array of indices
>>> a[j]  # the same shape as `j`
array([[ 9, 16],
       [81, 49]])

当索引数组 a 是多维的,单个索引数组引用 a . 下面的示例通过使用调色板将标签图像转换为彩色图像来显示此行为。

>>> palette = np.array([[0, 0, 0],         # black
...                     [255, 0, 0],       # red
...                     [0, 255, 0],       # green
...                     [0, 0, 255],       # blue
...                     [255, 255, 255]])  # white
>>> image = np.array([[0, 1, 2, 0],  # each value corresponds to a color in the palette
...                   [0, 3, 4, 0]])
>>> palette[image]  # the (2, 4, 3) color image
array([[[  0,   0,   0],
        [255,   0,   0],
        [  0, 255,   0],
        [  0,   0,   0]],

       [[  0,   0,   0],
        [  0,   0, 255],
        [255, 255, 255],
        [  0,   0,   0]]])

我们还可以为多个维度提供索引。每个维度的索引数组必须具有相同的形状。

>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> i = np.array([[0, 1],  # indices for the first dim of `a`
...               [1, 2]])
>>> j = np.array([[2, 1],  # indices for the second dim
...               [3, 3]])
>>>
>>> a[i, j]  # i and j must have equal shape
array([[ 2,  5],
       [ 7, 11]])
>>>
>>> a[i, 2]
array([[ 2,  6],
       [ 6, 10]])
>>>
>>> a[:, j]
array([[[ 2,  1],
        [ 3,  3]],

       [[ 6,  5],
        [ 7,  7]],

       [[10,  9],
        [11, 11]]])

在 Python 中, arr[i, j] 与…完全相同 arr[(i, j)] ---所以我们可以 ij 在一个 tuple 然后用这个做索引。

>>> l = (i, j)
>>> # equivalent to a[i, j]
>>> a[l]
array([[ 2,  5],
       [ 7, 11]])

但是,我们不能通过 ij 因为这个数组将被解释为索引 a .

>>> s = np.array([i, j])
>>> # not what we want
>>> a[s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 3
>>> # same as `a[i, j]`
>>> a[tuple(s)]
array([[ 2,  5],
       [ 7, 11]])

对数组进行索引的另一个常见用途是搜索时间相关序列的最大值:

>>> time = np.linspace(20, 145, 5)  # time scale
>>> data = np.sin(np.arange(20)).reshape(5, 4)  # 4 time-dependent series
>>> time
array([ 20.  ,  51.25,  82.5 , 113.75, 145.  ])
>>> data
array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],
       [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
       [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
       [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
       [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
>>> # index of the maxima for each series
>>> ind = data.argmax(axis=0)
>>> ind
array([2, 0, 3, 1])
>>> # times corresponding to the maxima
>>> time_max = time[ind]
>>>
>>> data_max = data[ind, range(data.shape[1])]  # => data[ind[0], 0], data[ind[1], 1]...
>>> time_max
array([ 82.5 ,  20.  , 113.75,  51.25])
>>> data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>> np.all(data_max == data.max(axis=0))
True

还可以使用数组索引作为要分配给的目标::

>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1, 3, 4]] = 0
>>> a
array([0, 0, 2, 0, 0])

但是,如果索引列表包含重复项,则会多次执行该分配,留下最后一个值:

>>> a = np.arange(5)
>>> a[[0, 0, 2]] = [1, 2, 3]
>>> a
array([2, 1, 3, 3, 4])

这是合理的,但是如果您想使用python的 += 构造,因为它可能不符合您的期望:

>>> a = np.arange(5)
>>> a[[0, 0, 2]] += 1
>>> a
array([1, 1, 3, 3, 4])

即使0在索引列表中出现两次,第0个元素也只增加一次。这是因为Python需要 a += 1 相当于 a = a + 1 .

使用布尔数组编制索引

当我们用(整数)索引数组对数组进行索引时,我们将提供要选择的索引列表。对于布尔索引,方法是不同的;我们显式地选择数组中需要哪些项,不需要哪些项。

对于布尔索引来说,最自然的方法是使用具有 相同的形状 作为原始数组:

>>> a = np.arange(12).reshape(3, 4)
>>> b = a > 4
>>> b  # `b` is a boolean with `a`'s shape
array([[False, False, False, False],
       [False,  True,  True,  True],
       [ True,  True,  True,  True]])
>>> a[b]  # 1d array with the selected elements
array([ 5,  6,  7,  8,  9, 10, 11])

此属性在工作分配中非常有用:

>>> a[b] = 0  # All elements of `a` higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
       [4, 0, 0, 0],
       [0, 0, 0, 0]])

您可以查看以下示例,了解如何使用布尔索引生成 Mandelbrot set

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot(h, w, maxit=20):
...     """Returns an image of the Mandelbrot fractal of size (h,w)."""
...     y, x = np.ogrid[-1.4:1.4:h*1j, -2:0.8:w*1j]
...     c = x + y * 1j
...     z = c
...     divtime = maxit + np.zeros(z.shape, dtype=int)
...
...     for i in range(maxit):
...         z = z**2 + c
...         diverge = z * np.conj(z) > 2**2       # who is diverging
...         div_now = diverge & (divtime == maxit)  # who is diverging now
...         divtime[div_now] = i                    # note when
...         z[diverge] = 2                          # avoid diverging too much
...
...     return divtime
>>> plt.imshow(mandelbrot(400, 400))
../_images/quickstart-1.png

使用布尔型进行索引的第二种方法更类似于整数索引;对于数组的每个维度,我们给出一个1d布尔型数组,选择所需的切片:

>>> a = np.arange(12).reshape(3, 4)
>>> b1 = np.array([False, True, True])         # first dim selection
>>> b2 = np.array([True, False, True, False])  # second dim selection
>>>
>>> a[b1, :]                                   # selecting rows
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> a[b1]                                      # same thing
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>
>>> a[:, b2]                                   # selecting columns
array([[ 0,  2],
       [ 4,  6],
       [ 8, 10]])
>>>
>>> a[b1, b2]                                  # a weird thing to do
array([ 4, 10])

请注意,一维布尔数组的长度必须与要切片的维度(或轴)的长度一致。在前面的示例中, b1 长度为3 rows 在里面 ab2 (长度为4)适用于索引 a .

ix_()函数

这个 ix_ 函数可以用来组合不同的向量,从而得到每个n-对的结果。例如,如果要计算所有a+b * c对于从每个向量a、b和c中选取的所有三元组:

>>> a = np.array([2, 3, 4, 5])
>>> b = np.array([8, 5, 4])
>>> c = np.array([5, 4, 6, 8, 3])
>>> ax, bx, cx = np.ix_(a, b, c)
>>> ax
array([[[2]],

       [[3]],

       [[4]],

       [[5]]])
>>> bx
array([[[8],
        [5],
        [4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax + bx * cx
>>> result
array([[[42, 34, 50, 66, 26],
        [27, 22, 32, 42, 17],
        [22, 18, 26, 34, 14]],

       [[43, 35, 51, 67, 27],
        [28, 23, 33, 43, 18],
        [23, 19, 27, 35, 15]],

       [[44, 36, 52, 68, 28],
        [29, 24, 34, 44, 19],
        [24, 20, 28, 36, 16]],

       [[45, 37, 53, 69, 29],
        [30, 25, 35, 45, 20],
        [25, 21, 29, 37, 17]]])
>>> result[3, 2, 4]
17
>>> a[3] + b[2] * c[4]
17

您还可以按以下方式实现reduce:

>>> def ufunc_reduce(ufct, *vectors):
...    vs = np.ix_(*vectors)
...    r = ufct.identity
...    for v in vs:
...        r = ufct(r, v)
...    return r

然后将其用作:

>>> ufunc_reduce(np.add, a, b, c)
array([[[15, 14, 16, 18, 13],
        [12, 11, 13, 15, 10],
        [11, 10, 12, 14,  9]],

       [[16, 15, 17, 19, 14],
        [13, 12, 14, 16, 11],
        [12, 11, 13, 15, 10]],

       [[17, 16, 18, 20, 15],
        [14, 13, 15, 17, 12],
        [13, 12, 14, 16, 11]],

       [[18, 17, 19, 21, 16],
        [15, 14, 16, 18, 13],
        [14, 13, 15, 17, 12]]])

与普通的ufunc.reduce相比,此版本的reduce的优势在于它使用了 broadcasting rules 为了避免创建参数数组,输出的大小乘以向量的数目。

用字符串索引

结构化数组 .

线性代数

正在进行中。这里包括基本的线性代数。

简单的数组操作

有关更多信息,请参见numpy文件夹中的linalg.py。

>>> import numpy as np
>>> a = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> print(a)
[[1. 2.]
 [3. 4.]]
>>> a.transpose()
array([[1., 3.],
       [2., 4.]])
>>> np.linalg.inv(a)
array([[-2. ,  1. ],
       [ 1.5, -0.5]])
>>> u = np.eye(2)  # unit 2x2 matrix; "eye" represents "I"
>>> u
array([[1., 0.],
       [0., 1.]])
>>> j = np.array([[0.0, -1.0], [1.0, 0.0]])
>>> j @ j  # matrix product
array([[-1.,  0.],
       [ 0., -1.]])
>>> np.trace(u)  # trace
2.0
>>> y = np.array([[5.], [7.]])
>>> np.linalg.solve(a, y)
array([[-3.],
       [ 4.]])
>>> np.linalg.eig(j)
(array([0.+1.j, 0.-1.j]), array([[0.70710678+0.j        , 0.70710678-0.j        ],
       [0.        -0.70710678j, 0.        +0.70710678j]]))
Parameters:
    square matrix
Returns
    The eigenvalues, each repeated according to its multiplicity.
    The normalized (unit "length") eigenvectors, such that the
    column ``v[:, i]`` is the eigenvector corresponding to the
    eigenvalue ``w[i]`` .

诀窍与窍门

这里我们给出一个简短而有用的提示列表。

“自动”整形

若要更改数组的尺寸,可以省略其中一个尺寸,然后自动推导:

>>> a = np.arange(30)
>>> b = a.reshape((2, -1, 3))  # -1 means "whatever is needed"
>>> b.shape
(2, 5, 3)
>>> b
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11],
        [12, 13, 14]],

       [[15, 16, 17],
        [18, 19, 20],
        [21, 22, 23],
        [24, 25, 26],
        [27, 28, 29]]])

矢量叠加

如何从大小相等的行向量列表中构造二维数组?在matlab中,这很容易:如果 xy 你只需要两个相同长度的向量吗? m=[x;y] . 在numpy中,这通过函数工作 column_stackdstackhstackvstack ,取决于要进行堆叠的尺寸。例如::

>>> x = np.arange(0, 10, 2)
>>> y = np.arange(5)
>>> m = np.vstack([x, y])
>>> m
array([[0, 2, 4, 6, 8],
       [0, 1, 2, 3, 4]])
>>> xy = np.hstack([x, y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4])

在两个以上维度中,这些函数背后的逻辑可能很奇怪。

直方图

NumPy histogram 应用于数组的函数返回一对向量:数组的直方图和bin边的向量。当心: matplotlib 还具有一个函数来构建柱状图(称为 hist 与numpy中的不同。主要区别在于 pylab.hist 自动绘制柱状图,同时 numpy.histogram 只生成数据。

>>> import numpy as np
>>> rg = np.random.default_rng(1)
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
>>> mu, sigma = 2, 0.5
>>> v = rg.normal(mu, sigma, 10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, density=1)       # matplotlib version (plot)
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, density=True)  # NumPy version (no plot)
>>> plt.plot(.5 * (bins[1:] + bins[:-1]), n)
../_images/quickstart-2.png