在读本教程之前,你最好懂一点儿Python
。如果你想唤起对Python
的记忆,可以看这里Python官方教程。
NumPy
的主要对象是同类型的多维数组。这种数组里面包含了一个表格的元素(通常是数字),所有的元素为同一个类型。由一个正整数组成的元组来索引。在NumPy
中维度被称为axes
(轴),轴的数目被称为rank
。
举个例子:在三维空间里面有一个点[1,2,1]
就是一个维度(或者轴)为1的数组。这个维度的长度为3 。下面给出的数组维度是2,第一维长度是2,第二维长度为3.
[[1.,0.,0.],[0.,1.,2.]]
在NumPy
中表示数组的类为ndarray
,别名就是array
,注意,这里的numpy.array
不同于Python
标准库中的类array.array
(这个数组只维护了一维的数组,仅仅提供了很少的函数方法)。下面是ndarray
的重要属性:
ndarray.ndim
在ndarray
数组中轴(维度)的数量。在Python
的世界里,维度的数量又叫做rank
。
ndarray.shape
在ndarray
中数组的形状,这是一个由正整数组成的元组来代表数组中每一个维度的大小。对于一个n*m
的矩阵,shape
就是(n,m)
,因此,shape
元组的长度代表的就是维度也就是ndim
。
ndarray.size
在ndarray
数组中所有元素的数量,这个等于shape
元组中相乘的数量。
ndarray.dtype
描述数组中的元素的类型的对象。人们可以创建类型或者使用python
标准库中的类型来指定dtype
。另外,NumPy
也提供了它自己的类型,比如numpy.int32
,numpy.int16
,numpy.float64
等。
ndarray.itemsize
描述数组中元素所占内存bytes
的大小。举个例子,元素的类型为float64
,那么itemsize
就是8。这个值相当于ndarray.dtype.itemsize
。
ndarray.data
含有数组中实际元素的缓冲器(the buffer containing the actual elements of the array)。通常情况下,我们并不需要使用这个属性,因为我们将使用索引访问数组中的元素。
一个例子:
>>> 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‘>
运行截图:
有很多方式可以来创建数组。
举个例子,你可以从一个正规的Python List
或者tuple
来创建数组,用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) # 错误
>>> 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
在创建数组的时候提供了一些函数来用占位符初始化内容。这些函数减少了数组增长的必要性,这是一个很昂贵的操作。(These minimize the necessity of growing arrays, an expensive operation.)
函数zeros
用来创建一个元素全部是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
函数的函数,叫做arange
函数,生成一个数组而不是列表。例如:
>>> 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
去接收我们想要的元素个数来代替用range
来指定步长:
>>> from numpy import pi
>>> np.linspace( 0, 2, 9 ) # 从0到2之间取9个数
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])
>>> x = np.linspace( 0, 2*pi, 100 ) # 多用在许多点的评价函数
>>> f = np.sin(x)
其他的一些函数:
array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.rand, numpy.random.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]]]
如果一个数组由于太大不能打印,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
方式强制NumPy
打印全部的数组内容。
>>> np.set_printoptions(threshold=‘nan‘)
在数组中的算术操作都应用了elementwise
(翻译不好这个),创建一个数组然后充满它:
>>> 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
的数组中,操作符*
的操作结果是elementwise
(矩阵中元素对应相乘),矩阵相乘可以使用dot
函数和方法:
>>> A = np.array( [[1,1],
... [0,1]] )
>>> B = np.array( [[2,0],
... [3,4]] )
>>> A*B # 元素相乘
array([[2, 0],
[0, 4]])
>>> A.dot(B) # 矩阵相乘
array([[5, 4],
[3, 4]])
>>> np.dot(A, B) # 另外一种矩阵相乘方式
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 不能自动转换类型为integer
Traceback (most recent call last):
...
TypeError: Cannot cast ufunc add output from dtype(‘float64‘) to dtype(‘int64‘) with casting rule ‘same_kind‘
Traceback (most recent call last):
...
TypeError: Cannot cast ufunc add output from dtype(‘float64‘) to dtype(‘int64‘) with casting rule ‘same_kind‘
当两种数组操作类型不同的时候,结果数组的类型对应于更通用或者更精确的那个数组(这种行为被称作upcasting
)。
>>> 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 function
,在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.])
更多函数:
all, alltrue, any, apply along axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, conjugate, corrcoef, cov, cross, cumprod, cumsum, diff, dot, floor, inner, inv, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round, sometrue, sort, std, sum, trace, transpose, var, vdot, vectorize, where
一维数组就跟Python
中的list
和其他序列一样,可以被索引,切片和迭代。
>>> 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
>>> 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.))
...
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] # 每行的第二列的数字
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
后面跟着一系列的:
,就像b[i,:,:]
,代表剩下的轴,NumPy
也允许你使用像...
的符号代表一系列的:
。点 (…)
代表许多产生一个完整的索引元组必要的分号。如果x是秩为5的数组(即它有5个轴),那么:
x[1,2,...]
等同于x[1,2,:,:,:]
x[...,3]
等同于x[:,:,:,:,3]
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
更多可以查看的:
Indexing, Indexing (reference), newaxis, ndenumerate, indices
一个数组的形状可以由它每个轴上的元素个数给出:
>>> 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() # 使得数组平滑
array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.])
>>> a.shape = (6, 2)
>>> a.T
array([[ 2., 0., 4., 1., 8., 3.],
[ 8., 6., 5., 1., 9., 6.]])
由函数ravel()
展开的数组里面的元素顺序正常是C
风格的,就是说,最右边的索引“变化得最快”,所以在元素a[0,0]
后面的元素是a[0,1]
,如果数组被改变成为其他的形状,数组仍然是C
风格的。NumPy
通常创建一个以这个顺序保存的数组,所以ravel()
函数不需要拷贝它的参数,但是如果数组是通过切片其它数组或有不同寻常的选项时,它可能需要被复制。函数 reshape()
和 ravel()
还可以被通过一些可选参数构建成FORTRAN
风格的数组,即最左边的索引变化最快。
函数reshape
改变参数数组的形状并且返回它,而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.]])
如果在改变形状操作中一个维度被给做-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
以列将一维数组合成二维数组,它等同于 对一维数组vstack
。
>>> from numpy import newaxis
>>> np.column_stack((a,b)) # With 2D arrays
array([[ 8., 8., 1., 8.],
[ 0., 0., 0., 4.]])
>>> a = np.array([4.,2.])
>>> b = np.array([2.,8.])
>>> a[:,newaxis] # This allows to have a 2D columns vector
array([[ 4.],
[ 2.]])
>>> np.column_stack((a[:,newaxis],b[:,newaxis]))
array([[ 4., 2.],
[ 2., 8.]])
>>> np.vstack((a[:,newaxis],b[:,newaxis])) # The behavior of vstack is different
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
的默认行为其实是很像的,但是允许可选的参数给出组合所沿着的轴的代号。
更多
hstack, vstack, column_stack, concatenate, c_, r_
使用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
可以指定一个轴进行分割。
当对数组进行操作的时候,有时候需要拷贝数组中的值到一个新的数组,有时不需要,这通常让新手感到困惑。这里介绍三种情况:
简单的分配不需要拷贝数组或者数组中的值。
>>> a = np.arange(12)
>>> b = a # 没有新的对象生成
>>> b is a # a和b是同一个对象的不同名字
True
>>> b.shape = 3,4 # 改变a的形状
>>> a.shape
(3, 4)
Python
传递不定对象作为引用,所以不拷贝数组。
>>> def f(x):
... print(id(x))
...
>>> id(a) # id是对象的唯一标识
148293216
>>> f(a)
148293216
不同的数组对象可以共享相同的数据,view
函数可以创建一个新的数组指向相同的数据。
>>> c = a.view()
>>> c is a
False
>>> c.base is a # c是a的数据的视图
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # a的形状没有发生改变
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # a的数据改变了
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])
通过切片一个数组返回一个视图:
>>> s = a[ : , 1:3] # spaces added for clarity; could also be written "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]])
到此为止,NumPy
的简单操作部分就全部结束了。以下是方法总览。
下面是一些有用的NumPy
的方法列表,进行了不同的分类,如果想看全部的方法,可以看Routines
。
创建数组
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
原文:http://blog.csdn.net/jie310300215/article/details/50956754