【翻译】NumPy快速开始教程

原文: https://docs.scipy.org/doc/numpy-dev/user/quickstart.html

基本


NumPy的主要对象是多维数组。它是一组相同类型的元素(通常是数字)组成的表,由一组正整数做索引。在NumPy中,把维度(dimensions)称作轴(axes),维度的数量称作秩(rank)。

例如,三维空间中的一个坐标[1, 2, 1]是一个秩为1的数组,因为它有1个轴.这个轴的长度为3。在下面的例子中,这个数组的秩为2(它是2维的)。第一个维度(轴)长度为2,第二个维度长度为3.

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

NumPy的数组类被称作ndarray,别名array。注意numpy.array与Python标准库中的类array.array不同,后者只能处理一维数组,功能更少。ndarray对象的更多属性有:

ndarray.ndim
数组的轴的数量(维度)。在Python世界,维度被称为秩(rank)。

ndarray.shap
数组的维(dimensions)。这是一个整数元组,指示数组在每一维度的大小。对于一个n行m列的矩阵,shape(n,m)shape元组的长度就是秩,或者说维度,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
包含实际数组元素的缓冲区。通常我们不会用到这个属性,因为我们可以通过数组下标访问元素。

一个例子

>>> 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)
<type 'numpy.ndarray'>
>>> b = np.array([6, 7, 8])
>>> b
array([6, 7, 8])
>>> type(b)
<type 'numpy.ndarray'>
数组的创建

创建数组有多种方法。

例如,你可以使用array函数从一个常规的Python列表或元组创建一个数组。得到的数组的类型由序列中的元素类型推断。

>>> 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)    # 错误
>>> a = np.array([1,2,3,4])  # 正确

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提供了几个函数用来创建带初始占位内容的数组。这减少了扩大数组的需求,扩大数组是一个开销很大的操作。
函数zeres创建一个全为0的数组,函数ones创建一个全为1的数组,函数empty创建一个初始内容随机,与内存状态有关的数组。默认情况下,创建的数组的dtype是float64

>>> 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 )                # dtype可以指定
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],
       [  5.30498948e-313,   3.14673309e-307,   1.00000000e+000]])

创建数字序列,NumPy提供了一个与range相似的函数,返回数组而不是列表。

>>> np.arange( 10, 30, 5 )
array([10, 15, 20, 25])
>>> np.arange( 0, 2, 0.3 )                 # 接受浮点参数
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)
打印数组

当你打印一个数组,NumPy用一种类似嵌套列表的方式显示它,但是遵循下面的布局:

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

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

>>> a = np.arange(6)                         # 1维数组
>>> print(a)
[0 1 2 3 4 5]
>>>
>>> b = np.arange(12).reshape(4,3)           # 2维数组
>>> print(b)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
>>>
>>> c = np.arange(24).reshape(2,3,4)         # 3维数组
>>> 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]]]

如果数组太大,难以完全打印,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]]

如果想避免这种行为,强制打印完整数组,你可以使用set_printoptions改变打印选项。

>>> np.set_printoptions(threshold='nan')
基本操作

算术操作符在数组中是作用在元素对元素的运算。结果为一个新创建的数组。

>>> 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], dtype=bool)

和多数矩阵语言不同,NumPy中的乘法运算符*是作用在对应元素上的。矩阵乘法可以使用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.dot(B)                    # matrix product
array([[5, 4],
       [3, 4]])
>>> np.dot(A, B)                # another matrix product
array([[5, 4],
       [3, 4]])

一些运算符,比如+=*=,将会改变现存数组,而不是创建一个新数组

>>> a = np.ones((2,3), dtype=int)
>>> b = np.random.random((2,3))
>>> a *= 3
>>> a
array([[3, 3, 3],
       [3, 3, 3]])
>>> b += a
>>> b
array([[ 3.417022  ,  3.72032449,  3.00011437],
       [ 3.30233257,  3.14675589,  3.09233859]])
>>> a += b                  # b is not automatically converted to integer type
Traceback (most recent call last):
  ...
TypeError: 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 = np.random.random((2,3))
>>> a
array([[ 0.18626021,  0.34556073,  0.39676747],
       [ 0.53881673,  0.41919451,  0.6852195 ]])
>>> a.sum()
2.5718191614547998
>>> a.min()
0.1862602113776709
>>> a.max()
0.6852195003967595

默认情况下,这些运算符把数组当作一个数字列表来运算,忽略它的形状。然而,通过指定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)                            # 每一列的和
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1)                            # 每一行的最小值
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1)                         # 沿每一行的累积和
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]])
通用函数

NumPy提供了常见的数学函数,比如sin, cos, exp。在NumPy中,这些函数被称为通用函数 "universal functions" (ufinc)。在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.])
索引,切片,迭代

一维数组可以被索引,分片和迭代,与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])
>>> a[:6:2] = -1000    #等价于 a[0:6:2] = -1000; 从起始到第6的位置, 将每一个偶数位置的元素设置为 -1000
>>> a
array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,   729])
>>> a[ : :-1]                                 # 将 a 倒置
array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1, -1000])
>>> for i in a:
...     print(i**(1/3.))
...
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0

多维数组可以每个轴有一个下标。这些下标以逗号分隔的列表形式给出:

>>> 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]                       # b的每一行的第二列
array([ 1, 11, 21, 31, 41])
>>> b[ : ,1]                        # 同上
array([ 1, 11, 21, 31, 41])
>>> b[1:3, : ]                      # 第二、三行的每一列
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

当提供的下标数小于坐标轴数量时,丢失的坐标被当作完全切片:

>>> b[-1]                                  # 最后一行,等价于 b[-1,:]
array([40, 41, 42, 43])

表达式b[i]中的括号里的部分,被看作i后面接了多个:来表示剩下的坐标轴。NumPy也允许你用点来表示b[i,...]
点(...)代表所需的任意多列的全切片。例如,如果x是一个秩为5的数组(有5个坐标轴),那么:

  • x[1,2,...] 等价于 x[1,2,:,:,:]
  • x[...,3] 等价于 x[:,:,:,:]
  • x[4,...,5,:] 等价于 x[4,:,:,5,:]
>>> c = np.array( [[[  0,  1,  2],               #三维数组(两个叠起来的二维数组)
...                 [ 10, 12, 13]],
...                [[100,101,102],
...                 [110,112,113]]])
>>> c.shape
(2, 2, 3)
>>> c[1,...]                                   # 与 c[1,:,:] 或 c[1] 相同
array([[100, 101, 102],
       [110, 112, 113]])
>>> c[...,2]                                   # 与 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属性,它是一个作用在数组中所有元素的迭代器:

>>> 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

形状控制


改变数组的形状

一个数组的形状由每个坐标轴的元素数量确定:

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

数组形状可以通过很多命令改变。接下来的三个命令都返回一个改变的数组,但是不改变源数组:

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

ravel()返回的数组元素的顺序通常是C风格,也就是说,最右边的下标变得最快,所以a[0,0]之后是a[0,1],如果数组被重塑成娶她形状,仍然被当作 C风格。NumPy通常创建的数组为C风格,所以ravel()通常不需要复制它的参数,但是如果数组是通过其他数组的切片产生,或用不平常的参数创建,就需要复制。ravel()和reshape()函数也可以通过可选参数指定为FORTRAN风格的数组,即最左边的下标变化最快。
reshape函数将参数改变形状后返回,而ndarray.resize方法改变数组本身:

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

在reshape时,如果某一维参数给成-1,那么这一维的大小将被自动计算:

>>> a.reshape(3,-1)
array([[ 2.,  8.,  0.,  6.],
       [ 4.,  5.,  1.,  1.],
       [ 8.,  9.,  3.,  6.]])
堆叠不同数组

几个数组可以沿着某一坐标轴堆叠在一起:

>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[ 8.,  8.],
       [ 0.,  0.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1.,  8.],
       [ 0.,  4.]])
>>> np.vstack((a,b))
array([[ 8.,  8.],
       [ 0.,  0.],
       [ 1.,  8.],
       [ 0.,  4.]])
>>> np.hstack((a,b))
array([[ 8.,  8.,  1.,  8.],
       [ 0.,  0.,  0.,  4.]])

column_stack函数将一维数组作为列堆叠成二维数组。只有在1维数组中,它才等同于vstack:

>>> from numpy import newaxis
>>> np.column_stack((a,b))   # 输入二维数组
array([[ 8.,  8.,  1.,  8.],
       [ 0.,  0.,  0.,  4.]])
>>> a = np.array([4.,2.])
>>> b = np.array([2.,8.])
>>> a[:,newaxis]  # 这样允许有二维列向量
array([[ 4.],
       [ 2.]])
>>> np.column_stack((a[:,newaxis],b[:,newaxis]))
array([[ 4.,  2.],
       [ 2.,  8.]])
>>> np.vstack((a[:,newaxis],b[:,newaxis])) # vstack的行为不同
array([[ 4.],
       [ 2.],
       [ 2.],
       [ 8.]])

对于超过二维的数组,hstack沿着第二个坐标轴堆叠,vstack沿着第一个坐标轴堆叠,concatenate沿着一个指定坐标轴进行。
注意
在复杂情况下,r_和c_可以沿着一个坐标轴堆叠创建数组。他们允许使用符号(":")

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

当使用数组作为参数时,r_和c_的默认行为与vstack和hstack相似,但是允许一个可选参数给出连结的坐标轴号。

一个数组分成多个小数组

使用hsplit,你可以把一个数组沿着水平方向分裂,指定分成相同形状数组的数量,或指定在哪几列之后分裂:

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

vsplit沿着垂直坐标轴划分,array_split允许指定坐标轴划分。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容