快速入门教程

先决条件

在阅读本教程之前,你应该了解一些Python的基础知识。如果你想复习一下,请回去看看Python教程

如果您希望使用本教程中的示例,则还必须在计算机上安装某些软件。有关说明,请参阅https://scipy.org/install.html

基础知识

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

例如,3D空间中的点的坐标[1, 2, 1]具有一个轴。该轴有3个元素,所以我们说它的长度为3.在下图所示的例子中,数组有2个轴。第一轴的长度为2,第二轴的长度为3。

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

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

  • ndarray.ndim - 数组的轴(维度)的个数。在Python世界中,维度的数量被称为rank。
  • ndarray.shape - 数组的维度。这是一个整数的元组,表示每个维度中数组的大小。对于有 n 行和 m 列的矩阵,shape 将是 (n,m)。因此,shape 元组的长度就是rank或维度的个数 ndim
  • ndarray.size - 数组元素的总数。这等于 shape 的元素的乘积。
  • ndarray.dtype - 一个描述数组中元素类型的对象。可以使用标准的Python类型创建或指定dtype。另外NumPy提供它自己的类型。例如numpy.int32、numpy.int16和numpy.float64。
  • ndarray.itemsize - 数组中每个元素的字节大小。例如,元素为 float64 类型的数组的 itemsize 为8(=64/8),而 complex32 类型的数组的 itemsize 为4(=32/8)。它等于 ndarray.dtype.itemsize
  • ndarray.data - 该缓冲区包含数组的实际元素。通常,我们不需要使用此属性,因为我们将使用索引访问数组中的元素。

一个例子

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

数组创建

有几种方法可以创建数组。

例如,你可以使用array函数从常规Python列表或元组中创建数组。得到的数组的类型是从Python列表中元素的类型推导出来的。

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

一个常见的错误,就是调用array的时候传入多个数字参数,而不是提供单个数字的列表类型作为参数。

  1. >>> a = np.array(1,2,3,4) # WRONG
  2. >>> a = np.array([1,2,3,4]) # RIGHT

array 还可以将序列的序列转换成二维数组,将序列的序列的序列转换成三维数组,等等。

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

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

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

通常,数组的元素最初是未知的,但它的大小是已知的。因此,NumPy提供了几个函数来创建具有初始占位符内容的数组。这就减少了数组增长的必要,因为数组增长的操作花费很大。

函数zeros创建一个由0组成的数组,函数 ones创建一个完整的数组,函数empty 创建一个数组,其初始内容是随机的,取决于内存的状态。默认情况下,创建的数组的dtype是 float64 类型的。

  1. >>> np.zeros( (3,4) )
  2. array([[ 0., 0., 0., 0.],
  3. [ 0., 0., 0., 0.],
  4. [ 0., 0., 0., 0.]])
  5. >>> np.ones( (2,3,4), dtype=np.int16 ) # dtype can also be specified
  6. array([[[ 1, 1, 1, 1],
  7. [ 1, 1, 1, 1],
  8. [ 1, 1, 1, 1]],
  9. [[ 1, 1, 1, 1],
  10. [ 1, 1, 1, 1],
  11. [ 1, 1, 1, 1]]], dtype=int16)
  12. >>> np.empty( (2,3) ) # uninitialized, output may vary
  13. array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],
  14. [ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])

为了创建数字组成的数组,NumPy提供了一个类似于range的函数,该函数返回数组而不是列表。

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

arange与浮点参数一起使用时,由于有限的浮点精度,通常不可能预测所获得的元素的数量。出于这个原因,通常最好使用linspace函数来接收我们想要的元素数量的函数,而不是步长(step):

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

::: tip 另见这些API

arrayzeroszeros_likeonesones_likeemptyempty_likearangelinspacenumpy.random.mtrand.RandomState.randnumpy.random.mtrand.RandomState.randnfromfunctionfromfile

:::

打印数组

当您打印数组时,NumPy以与嵌套列表类似的方式显示它,但具有以下布局:

  • 最后一个轴从左到右打印,
  • 倒数第二个从上到下打印,
  • 其余部分也从上到下打印,每个切片用空行分隔。

然后将一维数组打印为行,将二维数据打印为矩阵,将三维数据打印为矩数组表。

  1. >>> a = np.arange(6) # 1d array
  2. >>> print(a)
  3. [0 1 2 3 4 5]
  4. >>>
  5. >>> b = np.arange(12).reshape(4,3) # 2d array
  6. >>> print(b)
  7. [[ 0 1 2]
  8. [ 3 4 5]
  9. [ 6 7 8]
  10. [ 9 10 11]]
  11. >>>
  12. >>> c = np.arange(24).reshape(2,3,4) # 3d array
  13. >>> print(c)
  14. [[[ 0 1 2 3]
  15. [ 4 5 6 7]
  16. [ 8 9 10 11]]
  17. [[12 13 14 15]
  18. [16 17 18 19]
  19. [20 21 22 23]]]

有关 reshape 的详情,请参阅下文。

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

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

要禁用此行为并强制NumPy打印整个数组,可以使用更改打印选项set_printoptions

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

基本操作

数组上的算术运算符会应用到 元素 级别。下面是创建一个新数组并填充结果的示例:

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

与许多矩阵语言不同,乘积运算符*在NumPy数组中按元素进行运算。矩阵乘积可以使用@运算符(在python> = 3.5中)或dot函数或方法执行:

  1. >>> A = np.array( [[1,1],
  2. ... [0,1]] )
  3. >>> B = np.array( [[2,0],
  4. ... [3,4]] )
  5. >>> A * B # elementwise product
  6. array([[2, 0],
  7. [0, 4]])
  8. >>> A @ B # matrix product
  9. array([[5, 4],
  10. [3, 4]])
  11. >>> A.dot(B) # another matrix product
  12. array([[5, 4],
  13. [3, 4]])

某些操作(例如+=*=)会更直接更改被操作的矩阵数组而不会创建新矩阵数组。

  1. >>> a = np.ones((2,3), dtype=int)
  2. >>> b = np.random.random((2,3))
  3. >>> a *= 3
  4. >>> a
  5. array([[3, 3, 3],
  6. [3, 3, 3]])
  7. >>> b += a
  8. >>> b
  9. array([[ 3.417022 , 3.72032449, 3.00011437],
  10. [ 3.30233257, 3.14675589, 3.09233859]])
  11. >>> a += b # b is not automatically converted to integer type
  12. Traceback (most recent call last):
  13. ...
  14. TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int64') with casting rule 'same_kind'

当使用不同类型的数组进行操作时,结果数组的类型对应于更一般或更精确的数组(称为向上转换的行为)。

  1. >>> a = np.ones(3, dtype=np.int32)
  2. >>> b = np.linspace(0,pi,3)
  3. >>> b.dtype.name
  4. 'float64'
  5. >>> c = a+b
  6. >>> c
  7. array([ 1. , 2.57079633, 4.14159265])
  8. >>> c.dtype.name
  9. 'float64'
  10. >>> d = np.exp(c*1j)
  11. >>> d
  12. array([ 0.54030231+0.84147098j, -0.84147098+0.54030231j,
  13. -0.54030231-0.84147098j])
  14. >>> d.dtype.name
  15. 'complex128'

许多一元操作,例如计算数组中所有元素的总和,都是作为ndarray类的方法实现的。

  1. >>> a = np.random.random((2,3))
  2. >>> a
  3. array([[ 0.18626021, 0.34556073, 0.39676747],
  4. [ 0.53881673, 0.41919451, 0.6852195 ]])
  5. >>> a.sum()
  6. 2.5718191614547998
  7. >>> a.min()
  8. 0.1862602113776709
  9. >>> a.max()
  10. 0.6852195003967595

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

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

通函数

NumPy提供熟悉的数学函数,例如sin,cos和exp。在NumPy中,这些被称为“通函数”(ufunc)。在NumPy中,这些函数在数组上按元素进行运算,产生一个数组作为输出。

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

::: tip 另见这些通函数

allanyapply_along_axisargmaxargminargsortaveragebincountceilclipconjcorrcoefcovcrosscumprodcumsumdiffdotfloorinnerINVlexsortmaxmaximummeanmedianminminimumnonzeroouterprodreroundsortstdsumtracetransposevarvdotvectorizewhere

:::

索引、切片和迭代

一维的数组可以进行索引、切片和迭代操作的,就像 列表 和其他Python序列类型一样。

  1. >>> a = np.arange(10)**3
  2. >>> a
  3. array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
  4. >>> a[2]
  5. 8
  6. >>> a[2:5]
  7. array([ 8, 27, 64])
  8. >>> a[:6:2] = -1000 # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
  9. >>> a
  10. array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])
  11. >>> a[ : :-1] # reversed a
  12. array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])
  13. >>> for i in a:
  14. ... print(i**(1/3.))
  15. ...
  16. nan
  17. 1.0
  18. nan
  19. 3.0
  20. nan
  21. 5.0
  22. 6.0
  23. 7.0
  24. 8.0
  25. 9.0

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

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

当提供的索引少于轴的数量时,缺失的索引被认为是完整的切片:

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

b[i] 方括号中的表达式 i 被视为后面紧跟着 : 的多个实例,用于表示剩余轴。NumPy也允许你使用三个点写为 b[i,...]

三个点( ... )表示产生完整索引元组所需的冒号。例如,如果 x 是rank为5的数组(即,它具有5个轴),则:

  • x[1,2,...] 相当于 x[1,2,:,:,:]
  • x[...,3] 等效于 x[:,:,:,:,3]
  • x[4,...,5,:] 等效于 x[4,:,:,5,:]
  1. >>> c = np.array( [[[ 0, 1, 2], # a 3D array (two stacked 2D arrays)
  2. ... [ 10, 12, 13]],
  3. ... [[100,101,102],
  4. ... [110,112,113]]])
  5. >>> c.shape
  6. (2, 2, 3)
  7. >>> c[1,...] # same as c[1,:,:] or c[1]
  8. array([[100, 101, 102],
  9. [110, 112, 113]])
  10. >>> c[...,2] # same as c[:,:,2]
  11. array([[ 2, 13],
  12. [102, 113]])

对多维数组进行 迭代(Iterating) 是相对于第一个轴完成的:

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

但是,如果想要对数组中的每个元素执行操作,可以使用flat属性,该属性是数组的所有元素的迭代器

  1. >>> for element in b.flat:
  2. ... print(element)
  3. ...
  4. 0
  5. 1
  6. 2
  7. 3
  8. 10
  9. 11
  10. 12
  11. 13
  12. 20
  13. 21
  14. 22
  15. 23
  16. 30
  17. 31
  18. 32
  19. 33
  20. 40
  21. 41
  22. 42
  23. 43

::: tip 另见

Indexing, Indexing (reference), newaxis, ndenumerate, indices

:::

形状操纵

改变数组的形状

一个数组的形状是由每个轴的元素数量决定的:

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

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

  1. >>> a.ravel() # returns the array, flattened
  2. array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.])
  3. >>> a.reshape(6,2) # returns the array with a modified shape
  4. array([[ 2., 8.],
  5. [ 0., 6.],
  6. [ 4., 5.],
  7. [ 1., 1.],
  8. [ 8., 9.],
  9. [ 3., 6.]])
  10. >>> a.T # returns the array, transposed
  11. array([[ 2., 4., 8.],
  12. [ 8., 5., 9.],
  13. [ 0., 1., 3.],
  14. [ 6., 1., 6.]])
  15. >>> a.T.shape
  16. (4, 3)
  17. >>> a.shape
  18. (3, 4)

由 ravel() 产生的数组中元素的顺序通常是“C风格”,也就是说,最右边的索引“变化最快”,因此[0,0]之后的元素是[0,1] 。如果将数组重新整形为其他形状,则该数组将被视为“C风格”。NumPy通常创建按此顺序存储的数组,因此 ravel() 通常不需要复制其参数,但如果数组是通过获取另一个数组的切片或使用不常见的选项创建的,则可能需要复制它。还可以使用可选参数指示函数 ravel() 和 reshape(),以使用FORTRAN样式的数组,其中最左边的索引变化最快。

reshape函数返回带有修改形状的参数,而该 ndarray.resize方法会修改数组本身:

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

如果在 reshape 操作中将 size 指定为-1,则会自动计算其他的 size 大小:

  1. >>> a.reshape(3,-1)
  2. array([[ 2., 8., 0., 6.],
  3. [ 4., 5., 1., 1.],
  4. [ 8., 9., 3., 6.]])

::: tip 另见

ndarray.shapereshaperesizeravel

:::

将不同数组堆叠在一起

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

  1. >>> a = np.floor(10*np.random.random((2,2)))
  2. >>> a
  3. array([[ 8., 8.],
  4. [ 0., 0.]])
  5. >>> b = np.floor(10*np.random.random((2,2)))
  6. >>> b
  7. array([[ 1., 8.],
  8. [ 0., 4.]])
  9. >>> np.vstack((a,b))
  10. array([[ 8., 8.],
  11. [ 0., 0.],
  12. [ 1., 8.],
  13. [ 0., 4.]])
  14. >>> np.hstack((a,b))
  15. array([[ 8., 8., 1., 8.],
  16. [ 0., 0., 0., 4.]])

该函数将column_stack 1D数组作为列堆叠到2D数组中。它仅相当于 hstack2D数组:

  1. >>> from numpy import newaxis
  2. >>> np.column_stack((a,b)) # with 2D arrays
  3. array([[ 8., 8., 1., 8.],
  4. [ 0., 0., 0., 4.]])
  5. >>> a = np.array([4.,2.])
  6. >>> b = np.array([3.,8.])
  7. >>> np.column_stack((a,b)) # returns a 2D array
  8. array([[ 4., 3.],
  9. [ 2., 8.]])
  10. >>> np.hstack((a,b)) # the result is different
  11. array([ 4., 2., 3., 8.])
  12. >>> a[:,newaxis] # this allows to have a 2D columns vector
  13. array([[ 4.],
  14. [ 2.]])
  15. >>> np.column_stack((a[:,newaxis],b[:,newaxis]))
  16. array([[ 4., 3.],
  17. [ 2., 8.]])
  18. >>> np.hstack((a[:,newaxis],b[:,newaxis])) # the result is the same
  19. array([[ 4., 3.],
  20. [ 2., 8.]])

另一方面,该函数ma.row_stack等效vstack 于任何输入数组。通常,对于具有两个以上维度的数组, hstack沿其第二轴vstack堆叠,沿其第一轴堆叠,并concatenate 允许可选参数给出连接应发生的轴的编号。

注意

在复杂的情况下,r_和c c_于通过沿一个轴堆叠数字来创建数组很有用。它们允许使用范围操作符(“:”)。

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

与数组一起用作参数时, r_c_ 在默认行为上类似于 vstackhstack ,但允许使用可选参数给出要连接的轴的编号。

::: tip 另见

hstackvstackcolumn_stackconcatenatec_r_

:::

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

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

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

vsplit沿垂直轴分割,并array_split允许指定要分割的轴。

拷贝和视图

当计算和操作数组时,有时会将数据复制到新数组中,有时则不会。这通常是初学者混淆的根源。有三种情况:

完全不复制

简单分配不会复制数组对象或其数据。

  1. >>> a = np.arange(12)
  2. >>> b = a # no new object is created
  3. >>> b is a # a and b are two names for the same ndarray object
  4. True
  5. >>> b.shape = 3,4 # changes the shape of a
  6. >>> a.shape
  7. (3, 4)

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

  1. >>> def f(x):
  2. ... print(id(x))
  3. ...
  4. >>> id(a) # id is a unique identifier of an object
  5. 148293216
  6. >>> f(a)
  7. 148293216

视图或浅拷贝

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

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

切片数组会返回一个视图:

  1. >>> s = a[ : , 1:3] # spaces added for clarity; could also be written "s = a[:,1:3]"
  2. >>> s[:] = 10 # s[:] is a view of s. Note the difference between s=10 and s[:]=10
  3. >>> a
  4. array([[ 0, 10, 10, 3],
  5. [1234, 10, 10, 7],
  6. [ 8, 10, 10, 11]])

深拷贝

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

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

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

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

如果改为使用 b = a[:100],则 ab 引用,并且即使执行 del a 也会在内存中持久存在。

功能和方法概述

以下是按类别排序的一些有用的NumPy函数和方法名称的列表。有关完整列表,请参阅参考手册里的常用API

Less 基础

广播(Broadcasting)规则

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

广播的第一个规则是,如果所有输入数组不具有相同数量的维度,则将“1”重复地预先添加到较小数组的形状,直到所有数组具有相同数量的维度。

广播的第二个规则确保沿特定维度的大小为1的数组表现为具有沿该维度具有最大形状的数组的大小。假定数组元素的值沿着“广播”数组的那个维度是相同的。

应用广播规则后,所有数组的大小必须匹配。更多细节可以在广播中找到。

花式索引和索引技巧

NumPy提供比常规Python序列更多的索引功能。除了通过整数和切片进行索引之外,正如我们之前看到的,数组可以由整数数组和布尔数组索引。

使用索引数组进行索引

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

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

  1. >>> palette = np.array( [ [0,0,0], # black
  2. ... [255,0,0], # red
  3. ... [0,255,0], # green
  4. ... [0,0,255], # blue
  5. ... [255,255,255] ] ) # white
  6. >>> image = np.array( [ [ 0, 1, 2, 0 ], # each value corresponds to a color in the palette
  7. ... [ 0, 3, 4, 0 ] ] )
  8. >>> palette[image] # the (2,4,3) color image
  9. array([[[ 0, 0, 0],
  10. [255, 0, 0],
  11. [ 0, 255, 0],
  12. [ 0, 0, 0]],
  13. [[ 0, 0, 0],
  14. [ 0, 0, 255],
  15. [255, 255, 255],
  16. [ 0, 0, 0]]])

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

  1. >>> a = np.arange(12).reshape(3,4)
  2. >>> a
  3. array([[ 0, 1, 2, 3],
  4. [ 4, 5, 6, 7],
  5. [ 8, 9, 10, 11]])
  6. >>> i = np.array( [ [0,1], # indices for the first dim of a
  7. ... [1,2] ] )
  8. >>> j = np.array( [ [2,1], # indices for the second dim
  9. ... [3,3] ] )
  10. >>>
  11. >>> a[i,j] # i and j must have equal shape
  12. array([[ 2, 5],
  13. [ 7, 11]])
  14. >>>
  15. >>> a[i,2]
  16. array([[ 2, 6],
  17. [ 6, 10]])
  18. >>>
  19. >>> a[:,j] # i.e., a[ : , j]
  20. array([[[ 2, 1],
  21. [ 3, 3]],
  22. [[ 6, 5],
  23. [ 7, 7]],
  24. [[10, 9],
  25. [11, 11]]])

当然,我们可以按顺序(比如列表)放入ij然后使用列表进行索引。

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

但是,我们不能通过放入ij放入数组来实现这一点,因为这个数组将被解释为索引a的第一个维度。

  1. >>> s = np.array( [i,j] )
  2. >>> a[s] # not what we want
  3. Traceback (most recent call last):
  4. File "<stdin>", line 1, in ?
  5. IndexError: index (3) out of range (0<=index<=2) in dimension 0
  6. >>>
  7. >>> a[tuple(s)] # same as a[i,j]
  8. array([[ 2, 5],
  9. [ 7, 11]])

使用数组索引的另一个常见用法是搜索与时间相关的系列的最大值:

  1. >>> time = np.linspace(20, 145, 5) # time scale
  2. >>> data = np.sin(np.arange(20)).reshape(5,4) # 4 time-dependent series
  3. >>> time
  4. array([ 20. , 51.25, 82.5 , 113.75, 145. ])
  5. >>> data
  6. array([[ 0. , 0.84147098, 0.90929743, 0.14112001],
  7. [-0.7568025 , -0.95892427, -0.2794155 , 0.6569866 ],
  8. [ 0.98935825, 0.41211849, -0.54402111, -0.99999021],
  9. [-0.53657292, 0.42016704, 0.99060736, 0.65028784],
  10. [-0.28790332, -0.96139749, -0.75098725, 0.14987721]])
  11. >>>
  12. >>> ind = data.argmax(axis=0) # index of the maxima for each series
  13. >>> ind
  14. array([2, 0, 3, 1])
  15. >>>
  16. >>> time_max = time[ind] # times corresponding to the maxima
  17. >>>
  18. >>> data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
  19. >>>
  20. >>> time_max
  21. array([ 82.5 , 20. , 113.75, 51.25])
  22. >>> data_max
  23. array([ 0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
  24. >>>
  25. >>> np.all(data_max == data.max(axis=0))
  26. True

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

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

但是,当索引列表包含重复时,分配会多次完成,留下最后一个值:

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

这是合理的,但请注意是否要使用Python的 +=构造,因为它可能不会按预期执行:

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

即使0在索引列表中出现两次,第0个元素也只增加一次。这是因为Python要求“a + = 1”等同于“a = a + 1”。

使用布尔数组进行索引

当我们使用(整数)索引数组索引数组时,我们提供了要选择的索引列表。使用布尔索引,方法是不同的; 我们明确地选择我们想要的数组中的哪些项目以及我们不需要的项目。

人们可以想到的最自然的布尔索引方法是使用与原始数组具有 相同形状的 布尔数组:

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

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

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

您可以查看以下示例,了解如何使用布尔索引生成Mandelbrot集的图像:

  1. >>> import numpy as np
  2. >>> import matplotlib.pyplot as plt
  3. >>> def mandelbrot( h,w, maxit=20 ):
  4. ... """Returns an image of the Mandelbrot fractal of size (h,w)."""
  5. ... y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ]
  6. ... c = x+y*1j
  7. ... z = c
  8. ... divtime = maxit + np.zeros(z.shape, dtype=int)
  9. ...
  10. ... for i in range(maxit):
  11. ... z = z**2 + c
  12. ... diverge = z*np.conj(z) > 2**2 # who is diverging
  13. ... div_now = diverge & (divtime==maxit) # who is diverging now
  14. ... divtime[div_now] = i # note when
  15. ... z[diverge] = 2 # avoid diverging too much
  16. ...
  17. ... return divtime
  18. >>> plt.imshow(mandelbrot(400,400))
  19. >>> plt.show()

quickstart-1

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

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

请注意,1D布尔数组的长度必须与要切片的尺寸(或轴)的长度一致。在前面的例子中,b1具有长度为3(的数目 的行a),和 b2(长度4)适合于索引的第二轴线(列) a

ix_()函数

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

  1. >>> a = np.array([2,3,4,5])
  2. >>> b = np.array([8,5,4])
  3. >>> c = np.array([5,4,6,8,3])
  4. >>> ax,bx,cx = np.ix_(a,b,c)
  5. >>> ax
  6. array([[[2]],
  7. [[3]],
  8. [[4]],
  9. [[5]]])
  10. >>> bx
  11. array([[[8],
  12. [5],
  13. [4]]])
  14. >>> cx
  15. array([[[5, 4, 6, 8, 3]]])
  16. >>> ax.shape, bx.shape, cx.shape
  17. ((4, 1, 1), (1, 3, 1), (1, 1, 5))
  18. >>> result = ax+bx*cx
  19. >>> result
  20. array([[[42, 34, 50, 66, 26],
  21. [27, 22, 32, 42, 17],
  22. [22, 18, 26, 34, 14]],
  23. [[43, 35, 51, 67, 27],
  24. [28, 23, 33, 43, 18],
  25. [23, 19, 27, 35, 15]],
  26. [[44, 36, 52, 68, 28],
  27. [29, 24, 34, 44, 19],
  28. [24, 20, 28, 36, 16]],
  29. [[45, 37, 53, 69, 29],
  30. [30, 25, 35, 45, 20],
  31. [25, 21, 29, 37, 17]]])
  32. >>> result[3,2,4]
  33. 17
  34. >>> a[3]+b[2]*c[4]
  35. 17

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

  1. >>> def ufunc_reduce(ufct, *vectors):
  2. ... vs = np.ix_(*vectors)
  3. ... r = ufct.identity
  4. ... for v in vs:
  5. ... r = ufct(r,v)
  6. ... return r

然后将其用作:

  1. >>> ufunc_reduce(np.add,a,b,c)
  2. array([[[15, 14, 16, 18, 13],
  3. [12, 11, 13, 15, 10],
  4. [11, 10, 12, 14, 9]],
  5. [[16, 15, 17, 19, 14],
  6. [13, 12, 14, 16, 11],
  7. [12, 11, 13, 15, 10]],
  8. [[17, 16, 18, 20, 15],
  9. [14, 13, 15, 17, 12],
  10. [13, 12, 14, 16, 11]],
  11. [[18, 17, 19, 21, 16],
  12. [15, 14, 16, 18, 13],
  13. [14, 13, 15, 17, 12]]])

与普通的ufunc.reduce相比,这个版本的reduce的优点是它利用了广播规则 ,以避免创建一个参数数组,输出的大小乘以向量的数量。

使用字符串建立索引

请参见结构化数组

线性代数

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

简单数组操作

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

  1. >>> import numpy as np
  2. >>> a = np.array([[1.0, 2.0], [3.0, 4.0]])
  3. >>> print(a)
  4. [[ 1. 2.]
  5. [ 3. 4.]]
  6. >>> a.transpose()
  7. array([[ 1., 3.],
  8. [ 2., 4.]])
  9. >>> np.linalg.inv(a)
  10. array([[-2. , 1. ],
  11. [ 1.5, -0.5]])
  12. >>> u = np.eye(2) # unit 2x2 matrix; "eye" represents "I"
  13. >>> u
  14. array([[ 1., 0.],
  15. [ 0., 1.]])
  16. >>> j = np.array([[0.0, -1.0], [1.0, 0.0]])
  17. >>> j @ j # matrix product
  18. array([[-1., 0.],
  19. [ 0., -1.]])
  20. >>> np.trace(u) # trace
  21. 2.0
  22. >>> y = np.array([[5.], [7.]])
  23. >>> np.linalg.solve(a, y)
  24. array([[-3.],
  25. [ 4.]])
  26. >>> np.linalg.eig(j)
  27. (array([ 0.+1.j, 0.-1.j]), array([[ 0.70710678+0.j , 0.70710678-0.j ],
  28. [ 0.00000000-0.70710678j, 0.00000000+0.70710678j]]))
  1. Parameters:
  2. square matrix
  3. Returns
  4. The eigenvalues, each repeated according to its multiplicity.
  5. The normalized (unit "length") eigenvectors, such that the
  6. column ``v[:,i]`` is the eigenvector corresponding to the
  7. eigenvalue ``w[i]`` .

技巧和提示

这里我们列出一些简短有用的提示。

“自动”整形

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

  1. >>> a = np.arange(30)
  2. >>> a.shape = 2,-1,3 # -1 means "whatever is needed"
  3. >>> a.shape
  4. (2, 5, 3)
  5. >>> a
  6. array([[[ 0, 1, 2],
  7. [ 3, 4, 5],
  8. [ 6, 7, 8],
  9. [ 9, 10, 11],
  10. [12, 13, 14]],
  11. [[15, 16, 17],
  12. [18, 19, 20],
  13. [21, 22, 23],
  14. [24, 25, 26],
  15. [27, 28, 29]]])

矢量堆叠

我们如何从同等大小的行向量列表中构造一个二维数组?在MATLAB这是很简单:如果xy你只需要做两个相同长度的向量m=[x;y]。在此NumPy的通过功能的工作原理column_stackdstackhstackvstack,视维在堆叠是必须要做的。例如:

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

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

::: tip 另见

与 Matlab 比较

:::

直方图

histogram应用于数组的NumPy 函数返回一对向量:数组的直方图和bin的向量。注意: matplotlib还有一个构建直方图的功能(hist在Matlab中称为),与NumPy中的直方图不同。主要区别在于pylab.hist自动绘制直方图,而 numpy.histogram只生成数据。

  1. >>> import numpy as np
  2. >>> import matplotlib.pyplot as plt
  3. >>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
  4. >>> mu, sigma = 2, 0.5
  5. >>> v = np.random.normal(mu,sigma,10000)
  6. >>> # Plot a normalized histogram with 50 bins
  7. >>> plt.hist(v, bins=50, density=1) # matplotlib version (plot)
  8. >>> plt.show()

quickstart-2_00_00

  1. >>> # Compute the histogram with numpy and then plot it
  2. >>> (n, bins) = np.histogram(v, bins=50, density=True) # NumPy version (no plot)
  3. >>> plt.plot(.5*(bins[1:]+bins[:-1]), n)
  4. >>> plt.show()

quickstart-2_01_00

进一步阅读