目录
基础知识
NumPy的主要对象是同构多维数组。它是一个元素表(通常是数字),都是相同的类型,由正整数元组索引。在NumPy维度中称为轴。
例如,3D空间中的点的坐标[1, 2, 1]
具有一个轴。该轴有3个元素,所以我们说它的长度为3.在下图所示的例子中,数组有2个轴。第一轴的长度为2,第二轴的长度为3。
[[ 1., 0., 0.],
[ 0., 1., 2.]]
NumPy的数组类被调用ndarray(是n
dimensionality array 即n维数组的缩写
)
。它也被别名所知 array
。请注意,numpy.array
这与标准Python库类不同array.array
,后者仅处理一维数组并提供较少的功能。ndarray
对象更重要的属性是:
解释之前先对比一下Python的list、numpy的ndarray、numpy的matrix的关系和不同点:
Python列表和Numpy数组的区别:
Numpy使用ndarray对象来处理多维数组,该对象是一个快速而灵活的大数据容器。使用Python列表可以存储一维数组,通过列表的嵌套可以实现多维数组,那么为什么还需要使用Numpy呢?Numpy是专门针对数组的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于Python中的嵌套列表,数组越大,Numpy的优势就越明显。通常Numpy数组中的所有元素的类型都是相同的,而Python列表中的元素类型是任意的,所以在通用性能方面Numpy数组不及Python列表,但在科学计算中,可以省掉很多循环语句,代码使用方面比Python列表简单的多。
Numpy数组和矩阵的区别:
1. Numpy matrices必须是2维的,但是 numpy arrays (ndarrays) 可以是多维的(1D,2D,3D····ND). Matrix是Array的一个小的分支,包含于Array。所以matrix 拥有array的所有特性。
2. 在numpy中matrix的主要优势是:相对简单的乘法运算符号。例如,a和b是两个matrices,那么a*b,就是矩阵积。
3. matrix 和 array 都可以通过objects后面加.T 得到其转置。但是 matrix objects 还可以在后面加 .H f得到共轭矩阵, 加 .I 得到逆矩阵。
4. 相反的是在numpy里面arrays遵从逐个元素的运算,所以array:c 和d的c*d运算相当于matlab里面的c.*d运算。而矩阵相乘,则需要numpy里面的dot命令 。
ndarray
对象更重要的属性是:下面给出属性
ndarray.ndim
阵列的轴数(尺寸)。
ndarray.shape
数组的大小。这是一个整数元组,表示每个维度中数组的大小。对于具有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提供了几个函数来创建具有初始占位符内容的数组。这些最小化了增长阵列的必要性,这是一项昂贵的操作
该函数zeros
创建一个充满零的数组,该函数 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 can also be specified
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) ) # uninitialized, output may vary
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 ) # 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)
打印数组
当您打印数组时,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]]
要禁用此行为并强制NumPy打印整个阵列,可以使用更改打印选项set_printoptions
。
>>> np.set_printoptions(threshold=np.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])
与许多矩阵语言不同,运算符*
在NumPy数组中以元素方式运行。矩阵乘积可以使用@
运算符(在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]])
某些操作(例如+=
和)*=
可以修改现有阵列而不是创建新阵列。
>>> 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) # 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]])
索引,切片和迭代
一维数组可以被索引,切片和迭代,就像 列表 和其他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 # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -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] # 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]
被视为一个i
后跟尽可能多的实例:
来表示剩余的轴。NumPy还允许你用点来写这个 b[i,...]
。
的点(...
根据需要,以产生一个完整的索引元组)表示为许多冒号。例如,if 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
属性 作为数组的所有元素的 迭代器:
>>>
>>> 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
堆叠不同的数组
几个阵列可以沿不同的轴堆叠在一起:
>>>
>>> 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
1D数组作为列堆叠到2D数组中。它仅相当于hstack
2D数组:
>>>
>>> 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([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] # this allows to have a 2D columns 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
于任何输入数组。通常,对于具有两个以上维度的数组, hstack
沿其第二轴vstack
堆叠,沿其第一轴堆叠,并concatenate
允许可选参数给出连接应发生的轴的数量。
注意
在复杂的情况下,r_
并且 c_
是用于通过沿一个轴堆叠号码创建阵列有用的。它们允许使用范围文字(“:”)
>>>
>>> np.r_[1:4,0,4]
array([1, 2, 3, 0, 4])
当与数组作为参数使用的, r_
并且 c_
是类似于 vstack
和 hstack
在它们的默认行为,但允许一个可选参数给轴沿其来连接的数量。
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风格”,也就是说,最右边的索引“变化最快”,因此[0,0]之后的元素是[0,1] 。如果将数组重新整形为其他形状,则该数组将被视为“C风格”。NumPy通常会创建按此顺序存储的数组,因此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.]])
如果在重新整形操作中将尺寸指定为-1,则会自动计算其他尺寸:
>>>
>>> a.reshape(3,-1)
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
将一个数组拆分成几个较小的数组
使用时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 # no new object is created
>>> b is a # a and b are two names for the same ndarray object
True
>>> b.shape = 3,4 # changes the shape of a
>>> a.shape
(3, 4)
Python将可变对象作为引用传递,因此函数调用不会复制。
>>>
>>> def f(x):
... print(id(x))
...
>>> id(a) # id is a unique identifier of an object
148293216
>>> f(a)
148293216
查看或浅拷贝
不同的数组对象可以共享相同的数据。该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.shape = 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] # 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]])
技巧和提示
这里我们列出一些简短有用的提示。
“自动”整形
要更改数组的尺寸,您可以省略其中一个尺寸,然后自动推导出尺寸:
>>>
>>> a = np.arange(30)
>>> a.shape = 2,-1,3 # -1 means "whatever is needed"
>>> a.shape
(2, 5, 3)
>>> a
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这是很简单:如果x
和y
你只需要做两个相同长度的向量m=[x;y]
。在此NumPy的通过功能的工作原理column_stack
,dstack
,hstack
和vstack
,视维在堆叠是必须要做的。例如:
x = np.arange(0,10,2) # x=([0,2,4,6,8])
y = np.arange(5) # y=([0,1,2,3,4])
m = np.vstack([x,y]) # m=([[0,2,4,6,8],
# [0,1,2,3,4]])
xy = np.hstack([x,y]) # xy =([0,2,4,6,8,0,1,2,3,4])
这些函数背后的逻辑在两个以上的维度上可能很奇怪。
简单操作到这里就结束了,需要深入学的直接看官网教程即可