Numpy
Numpy基于python的科学计算的基础包,包含几个部分:
- 强大的N维数组对象
- 复杂的(broadcasting)广播函数
- 结合了c/c++和Fortran 代码工具
- 提供了有用的线性代数,傅里叶变换,生成随机数的功能
除了上面特定功能外,Numpy还可以作为
- 通用数据有效的多维容器
- 任意的数据类型都可以在Numpy中定义
这使NumPy能够无缝快速地与各种数据库集成。
安装Numpy
sudo apt-get install python-numpy python-scipy python-matplotlib ipython ipython-notebook python-pandas python-sympy python-nose
- 如果你使用python3
sudo apt install python3-pip
sudo pip3 install numpy
简明教程
Numpy主要面向同构多维数组(homogeneous multidimensional array),它是一个元素表(通常是数字),所有类型都相同,由正整数元组(tuple)索引。在Numpy中维度被称作轴。
比如,三维空间中一个点[1, 2, 1]
有一个轴, 它有3个元素,因此我们也说它的长度是3。在下面的例子中,该数组有2个轴,第一个轴的长度是2,第二个轴的长度是3.
[[1., 0., 0.],
[0., 1., 2.]]
Numpy的array类被称作ndarray
,也被叫做array
。值得一提的是,numpy.array
与标准库中的array.array
不同,标准库仅处理一维数组且提供很少的函数,ndarray
对象有更多的重要属性:
ndarray.ndim
数组轴的数量.
ndarray.shape
数组的维数,使用整型元组(tuple)表示每一维的大小。例如,对于一个n行,m列的矩阵,
shape
用(n, m)
表示。因此,shape
的元组(tuple)长度是轴(ndim
)的数量.ndarray.size
数组元素的总数量,也等于
shape
元素的乘积(n*m
).ndarray.dtype
描述数组元素对象的的类型,可以使用标准的python中提供的dtype的类型,也可以使用Numpy提供的类,如
numpy.int32
,numpy.int16
,numpy.float64
等.ndarry.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
'int32'
>>> a.itemsize
4
>>> 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列表和元组创建array
数组,生成的数组类型根据列表或者元组的类型自动推导出来。
>>> import numpy as np
>>> a = np.array([2, 3, 4])
>>> a
array([2, 3, 4])
>>> a.dtype
dtype('int32')
>>> b = np.array([1.2, 3.5, 5.1])
>>> b.dtype
dtype('float64')
一个常见的错误在于调用具有多个数字参数的数组,而不是提供单个数字列表作为参数。
>>> a = np.array(1,2,3,4) # WRONG
>>> 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
函数创建全部为0的数组ones
函数创建全部为1的数组empty
函数创建依赖内存状态的一组随机内容的数组,默认创建数组元素的类型为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([[ 0., 0., 0.],
[ 0., 0., 0.]])
为了创建数字序列,Numpy提供了一个类似python中列表的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
作为参数接收我们想要的元素数:
>>> from numpy import pi
>>> np.linspace(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)
Printing Array
当打印数组时,NumPy用与嵌套列表类似的方式显示它,但具有以下布局:
- 最后一个轴从左到右打印
- 倒数第二个轴从上到下打印
- 其余部分也从上到下打印,每个切片用空行分隔
>>> a = np.arange(6)
>>> print(a)
[0 1 2 3 4 5]
>>> a
array([0, 1, 2, 3, 4, 5])
>>> b = np.arange(12).reshape(4, 3)
>>> b
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
>>> print(b)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
>>> c = np.arange(24).reshape(2, 3, 4)
>>> c
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]]])
>>> 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]]]
如果数组太大,NupPy自动忽略打印数组中间的部分,仅仅打印一部分:
>>> 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=np.nan)
基本运算
>>> a = np.array([20, 30, 40, 50])
>>> b = np.arange(4)
>>> print(a)
[20 30 40 50]
>>> print(b)
[0 1 2 3]
>>> c = a - b
>>> print(b)
[0 1 2 3]
>>> print(c)
[20 29 38 47]
>>> print(b)
[0 1 2 3]
>>> b**2
array([0, 1, 4, 9])
>>> 10*np.sin(a)
array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854])
>>> print(a)
[20 30 40 50]
>>> a < 35
array([ True, True, False, False], dtype=bool)
不像一般意义上的矩阵运算,在Numpy中操作符*
应用到数组的每个元素。矩阵运算使用操作符@
(python>=3.5)或者使用dot函数:
>>> A = np.array([[1, 1], [0, 1]])
>>> B = np.array([[2, 0], [3, 4]])
>>>
>>> print(A)
[[1 1]
[0 1]]
>>> print(B)
[[2 0]
[3 4]]
>>> print(A*B) # elementwise product
[[2 0]
[0 4]]
>>>
>>> A @ B # python >=3.5 support
File "<stdin>", line 1
A @ B
^
SyntaxError: invalid syntax
>>> A.dot(B) # matrix product
array([[5, 4],
[3, 4]])
某些操作(例如+ =和* =)用于修改现有阵列而不是创建新阵列:
>>> a = np.ones((2, 3), dtype=int)
>>> b = np.random.random((2, 3))
>>> print (a)
[[1 1 1]
[1 1 1]]
>>> print(b)
[[ 0.8830467 0.68710312 0.29412506]
[ 0.13154102 0.23519967 0.02945235]]
>>> b += a
>>> print(b)
[[ 1.8830467 1.68710312 1.29412506]
[ 1.13154102 1.23519967 1.02945235]]
>>> a += b # b is not automatically converted to integer type
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int32') 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.29200798, 0.62564339, 0.19456579],
[ 0.50341921, 0.91176034, 0.64409186]])
>>> a.sum()
3.1714885595659452
>>> a.min()
0.19456578860982421
>>> a.max()
0.91176034087922453
默认情况下,这些操作应用到数组的所有元素,不管数组是什么shape
,然而,可以规定一个轴参数,可以将运算应用到数组的特定轴上:
>>> 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) # sum 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中又叫universal function
(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])
>>> C
array([ 2., -1., 4.])
>>> np.add(B, c)
array([ 1. , 3.57079633, 6.14159265])
索引,切片,迭代
一维数组可以被索引,切片和迭代,就像列表和其他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.))
...
__main__:2: RuntimeWarning: invalid value encountered in power
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0
>>> print(a)
[-1000 1 -1000 27 -1000 125 216 343 512 729]
多维数组每个轴可以有一个索引。 这些索引以逗号分隔的元组给出:
>>> 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.ndim
2
>>> b.shape
(5, 4)
>>> 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[-2]
array([30, 31, 32, 33])
>>> b[-3]
array([20, 21, 22, 23])
>>> b[0]
array([0, 1, 2, 3])
>>> b[1]
array([10, 11, 12, 13])
b[i]
中括号内的表达式被视为i
,后跟多个实例:根据需要表示剩余的轴。 NumPy还允许你这样写b[i, ...]
。...
表示生成完整索引元组所需的:
。 例如,如果x是一个包含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, ...]
array([[100, 101, 102],
[110, 112, 113]])
>>> c[..., 2]
array([[ 2, 13],
[102, 113]])
对多维数组进行迭代是针对第一个轴完成的:
array([[ 0, 1, 2, 3],
[10, 11, 12, 13],
[20, 21, 22, 23],
[30, 31, 32, 33],
[40, 41, 42, 43]])
>>> 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
属性:
>>> b
array([[ 0, 1, 2, 3],
[10, 11, 12, 13],
[20, 21, 22, 23],
[30, 31, 32, 33],
[40, 41, 42, 43]])
>>> for elem in b.flat:
... print (elem)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43
Shape Manipulation
数组的形状由沿每个轴的元素数量给出:
>>> a = np.floor(10*np.random.random((3, 4)))
>>> a
array([[ 0., 7., 9., 2.],
[ 5., 2., 8., 2.],
[ 6., 5., 3., 8.]])
>>> a.shape
(3, 4)
可以使用各种命令更改阵列的形状。 请注意,以下三个命令都返回已修改的数组,但不更改原始数组:
>>> a
array([[ 0., 7., 9., 2.],
[ 5., 2., 8., 2.],
[ 6., 5., 3., 8.]])
>>> a.ravel() #return the array, flattened
array([ 0., 7., 9., 2., 5., 2., 8., 2., 6., 5., 3., 8.])
>>> a
array([[ 0., 7., 9., 2.],
[ 5., 2., 8., 2.],
[ 6., 5., 3., 8.]])
>>> a.reshape(6, 2) #return the array with a modified shape
array([[ 0., 7.],
[ 9., 2.],
[ 5., 2.],
[ 8., 2.],
[ 6., 5.],
[ 3., 8.]])
>>> a
array([[ 0., 7., 9., 2.],
[ 5., 2., 8., 2.],
[ 6., 5., 3., 8.]])
>>> a.T #return the array, transposed
array([[ 0., 5., 6.],
[ 7., 2., 5.],
[ 9., 8., 3.],
[ 2., 2., 8.]])
>>> a
array([[ 0., 7., 9., 2.],
[ 5., 2., 8., 2.],
[ 6., 5., 3., 8.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4)
由ravel()
产生的数组中元素的顺序通常是“C风格”,也就是说,最右边的索引“变化最快”,因此[0, 0]
之后的元素是[0,1]
。如果数组被reshape
到其他的shape
,该数组被当做c-style
对待。NumPy通常会创建按此顺序存储的数组,因此ravel()
通常不需要复制其参数,但如果数组是通过获取另一个数组的切片或使用unusual options
创建的,则可能需要复制它。函数ravel()
和reshape()
也可以使用可选参数指示使用FORTRAN样式的数组,其中最左边的索引变化最快。
reshape
函数返回其参数的修改形状,而ndarray.resize方法修改数组本身:
>>> a
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
>>> a.shape
(3, 4)
>>> a.resize((2, 6))
>>> a.shape
(2, 6)
>>> a
array([[ 2., 8., 0., 6., 4., 5.],
[ 1., 1., 8., 9., 3., 6.]])
如果shape()
函数中有一个参数为-1
,则另外的参数被自动计算:
>>> a
array([[ 2., 8., 0., 6., 4., 5.],
[ 1., 1., 8., 9., 3., 6.]])
>>> a.reshape(3, -1)
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
>>> a.reshape(-1, 6)
array([[ 2., 8., 0., 6., 4., 5.],
[ 1., 1., 8., 9., 3., 6.]])
堆叠在一起的不同数组
一些数组可以沿着不同的轴堆叠在一起
>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[ 5., 7.],
[ 5., 8.]])
>>> b = np.floor(10*np.random.random((2, 2)))
>>> b
array([[ 2., 5.],
[ 7., 2.]])
>>> np.vstack((a, b))
array([[ 5., 7.],
[ 5., 8.],
[ 2., 5.],
[ 7., 2.]])
>>> np.hstack((a, b))
array([[ 5., 7., 2., 5.],
[ 5., 8., 7., 2.]])
函数column_stack
将1D数组作为列堆叠到2D数组中。 它相当于仅针对2D数组的hstack
:
>>> from numpy import newaxis
>>> np.column_stack((a, b))
array([[ 5., 7., 2., 5.],
[ 5., 8., 7., 2.]])
>>> a = np.array([4., 2.])
>>> b = np.array([3., 8.])
>>> np.column_stack((a, b))
array([[ 4., 3.],
[ 2., 8.]])
>>> np.hstack((a, b))
array([ 4., 2., 3., 8.])
>>> np.vstack((a, b))
array([[ 4., 2.],
[ 3., 8.]])
>>> a[:,newaxis] # this allows to have a 2D columns vector
array([[ 4.],
[ 2.]])
>>> a
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
堆栈沿着它们的第一个轴,并且连接允许可选参数给出连接应该发生的轴的数量。
在复杂情况下,r_
和c_
对于通过沿一个轴堆叠数字来创建数组非常有用。 它们允许使用范围文字(:
)
>>> np.r_[1:4, 0, 4]
array([1, 2, 3, 0, 4])
将一个数组拆分成几个较小的数组
使用hsplit
,您可以沿着水平轴分割数组,方法是指定要返回的同形数组的数量,或者通过指定应该进行分割(division)的列:
>>> a = np.floor(10*np.random.random((2,12)))
>>> a
array([[ 6., 3., 3., 5., 2., 6., 9., 6., 5., 5., 0., 8.],
[ 5., 2., 7., 0., 1., 0., 4., 6., 9., 0., 4., 1.]])
>>> np.hsplit(a,3) # Split a into 3
[array([[ 6., 3., 3., 5.],
[ 5., 2., 7., 0.]]), array([[ 2., 6., 9., 6.],
[ 1., 0., 4., 6.]]), array([[ 5., 5., 0., 8.],
[ 9., 0., 4., 1.]])]
>>> np.hsplit(a,(3,4)) # Split a after the third and the fourth column
[array([[ 6., 3., 3.],
[ 5., 2., 7.]]), array([[ 5.],
[ 0.]]), array([[ 2., 6., 9., 6., 5., 5., 0., 8.],
[ 1., 0., 4., 6., 9., 0., 4., 1.]])]
vsplit
沿垂直轴分割,array_split
允许指定要分割的轴。
拷贝和视图
在运行和操作数组时,有时会将数据复制到新数组中,有时则不会。 这通常是初学者混淆的根源。 有三种情况:
数组赋值
简单赋值不会复制数组对象或其数据。
>>> a = np.arange(12)
>>> b = a # no new object is created
>>> b = a
>>>
>>>
>>>
>>> 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
>>> a.shape
(3, 4)
Python将可变对象作为引用传递,因此函数调用不会复制。
>>> def f(x):
... print(id(x))
...
>>> id(a)
3080244904L
>>> f(a)
3080244904
视图和浅拷贝
不同的数组对象可以共享相同的数据,
view
函数创建一个新的对象来(视图)查看共享的数据.>>> a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> c = a.view() >>> c is a False >>> c.base is a True >>> c.flags.owndata False >>> >>> c.shape = 2, 6 >>> c array([[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]]) >>> a.shape (3, 4) >>> c[0, 4] = 1234 >>> a array([[ 0, 1, 2, 3], [1234, 5, 6, 7], [ 8, 9, 10, 11]])
切片一个数组并返回它的一个视图:
>>> 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
array([[ 1, 2],
[ 5, 6],
[ 9, 10]])
>>> s[:] = 10 # s[:] is a view of s. Note the difference between s=10 and s[:]=10
>>> s
array([[10, 10],
[10, 10],
[10, 10]])
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
深拷贝
copy
方法是数组数据完全拷贝:>>> a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]]) >>> d = a.copy() >>> d is a False >>> d.base is a False >>> d[0,0] = 9999 >>> a array([[ 0, 10, 10, 3], [1234, 10, 10, 7], [ 8, 10, 10, 11]])
Reference here