1、numpy简介
numpy 是Python中科学计算的核心库。它提供一个高性能多维数据对象,以及操作这个对象的工具。如果你已经熟悉了MATLAB,你会发现本教程对于numpy起步很有用。numpy数组是一个多维矩阵,所有类型都是一样的,是一个被索引的非负实数的元组。数组的维度大小是数组的rank,数组的shape是一个整型的元组,包含元组的大小和有几个这样的元组。
NumPy的主要对象是同种元素的多维数组。这是一个所有的元素都是一种类型、通过一个正整数元组索引的元素表格(通常是元素是数字)。在NumPy中维度(dimensions)叫做轴(axes),轴的个数叫做秩(rank)。秩大小即为维度大小。
例如,在3D空间一个点的坐标 [1, 2, 3] 是一个秩为1的数组,因为它只有一个轴。那个轴长度为3.又例如,在以下例子中,数组的秩为2(它有两个维度).第一个维度长度为2,第二个维度长度为3.
[[ 1., 0., 0.], [ 0., 1., 2.]]
2、数组的属性
NumPy的数组类被称作 ndarray 。通常被称作数组。注意numpy.array和标准Python库类array.array并不相同,后者只处理一维数组和提供少量功能。更多重要ndarray对象属性有:
1)ndarray.ndim: 数组轴的个数,在python的世界中,轴的个数被称作秩;
2)ndarray.shape: 数组的维度。这是一个指示数组在每个维度上大小的整数元组。例如一个2排3列的矩阵,它的shape属性将是(2,3),这个元组的长度显然是秩,即维度或者ndim属性;
3)ndarray.size: 数组元素的总个数,等于shape属性中元组元素的乘积,上例的size为2X3=6;
4)ndarray.dtype: 一个用来描述数组中元素类型的对象,可以通过创造或指定dtype使用标准Python类型。另外NumPy提供它自己的数据类型;
5)ndarray.itemsize: 数组中每个元素的字节大小。例如,一个元素类型为float64的数组itemsiz属性值为8(=64/8),又如,一个元素类型为complex32的数组item属性为4(=32/8);
6)ndarray.data: 包含实际数组元素的缓冲区,通常我们不需要使用这个属性,因为我们总是通过索引来使用数组中的元素;
3、创建数组
1)你可以使用array函数从常规的Python列表和元组创造数组,所创建的数组类型由原序列中的元素类型推导而来。array和asarray都可以将结构数据转化为ndarray,但是主要区别就是当数据源是ndarray时,array仍然会copy出一个副本,占用新的内存,但asarray不会。
>>> from numpy import *
>>> a = array( [2,3,4] )
* 一个常见的错误包括用多个数值参数调用`array`而不是提供一个由数值组成的列表作为一个参数。
>>> a = array(1,2,3,4) # WRONG
>>> a = array([1,2,3,4]) # RIGHT
* 数组将序列包含序列转化成二维的数组,序列包含序列包含序列转化成三维数组等等。
>>> b = array( [ (1.5,2,3), (4,5,6) ] )
* 数组类型可以在创建时显示指定
>>>c = array( [ [1,2], [3,4] ], dtype=complex )
>>> c
array([[ 1.+0.j, 2.+0.j],
[ 3.+0.j, 4.+0.j]])
* array和asarray的区别:
>>> import numpy as np
>>> a1=np.ones((3,3))
>>> a2=np.array(a1)
>>> a3=np.asarray(a1)
>>> a1[1]=2
>>> a1
array([[ 1., 1., 1.],
[ 2., 2., 2.],
[ 1., 1., 1.]])
>>> a2
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> a3
array([[ 1., 1., 1.],
[ 2., 2., 2.],
[ 1., 1., 1.]])
2)函数zeros创建一个全是0的数组,函数ones创建一个全1的数组,函数 empty 创建一个内容随机并且依赖与内存状态的数组。默认创建的数组类型(dtype)都是float64。
empty( (2,3) )
array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],
[ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])
>>> zeros( (3,4) )
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> ones( (2,3,4), dtype=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)
3)为了创建一个数列,NumPy提供一个类似arange的函数返回数组而不是列表:
>>> arange( 10, 30, 5 ) #参数分别代表起始位置,终点位置,步长
array([10, 15, 20, 25])
>>> b = arange( 4 )
>>> b
array([0, 1, 2, 3])
4)使用random函数
>>> from numpy import random
>>> a = random.random((2,3)) # 产生的每一个元素在0-1之间
>>> a
array([[ 0.6903007 , 0.39168346, 0.16524769],
[ 0.48819875, 0.77188505, 0.94792155]])
5)使用linspace函数
>>> b = linspace(0,pi,3) #参数分别代表起始位置,终点位置,步长
array([ 0. , 1.57079633, 3.14159265])
其它函数array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, rand, randn, fromfunction, fromfile
参考:NumPy示例
4、打印数组
当你打印一个数组,NumPy以类似嵌套列表的形式显示它,但是呈以下布局:
- 最后的轴从左到右打印
- 次后的轴从顶向下打印
- 剩下的轴从顶向下打印,每个切片通过一个空行与下一个隔开
1)一维数组被打印成行,二维数组成矩阵,三维数组成矩阵列表。
>>> a = arange(6) # 1d array
>>> print a
[0 1 2 3 4 5]
>>> b = arange(12).reshape(4,3) # 2d array
>>> print b
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
>>> c = 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]]]
查看形状操作一节获得有关reshape的更多细节
2)如果一个数组用来打印太大了,NumPy自动省略中间部分而只打印角落:
>>> print arange(10000)
[ 0 1 2 ..., 9997 9998 9999]
>>> print 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的这种行为并强制打印整个数组,你可以设置printoptions参数来更改打印选项。
>>> set_printoptions(threshold='nan')
5、基本运算
1)数组的算术运算是按元素的。新的数组被创建并且被结果填充。
array([20, 29, 38, 47])
>>> b**2
array([ 9.12945251, -9.88031624, 7.4511316 , -2.62374854])
>>> a<35
array([True, True, False, False], dtype=bool)
2)不像许多矩阵语言,NumPy中的乘法运算符 * 指示按元素计算,矩阵乘法可以使用 dot 函数实现
>>> A = array( [[1,1],
[0,1]])
>>> B = array( [[2,0],
[3,4]])
>>> A*B # elementwise product
array([[2, 0],
[0, 4]])
>>> dot(A,B) # matrix product
array([[5, 4],
[3, 4]])
3)有些操作符像 *= 被用来更改已存在数组而不创建一个新的数组。
>>> a = ones((2,3), dtype=int)
>>> a *= 3
>>> a
array([[3, 3, 3],
[3, 3, 3]])
4)当运算的是不同类型的数组时,结果数组转换为更精确的的类型
>>> a = ones(3, dtype=int32)
>>> b = linspace(0,pi,3)
>>> b.dtype.name
'float64'
>>> c = a+b
>>> c
array([ 1. , 2.57079633, 4.14159265])
>>> c.dtype.name
'float64'
>>> from numpy import random
>>> a = random.random((2,3))
>>> a
array([[ 0.6903007 , 0.39168346, 0.16524769],
[ 0.48819875, 0.77188505, 0.94792155]])
>>> a.sum()
3.4552372100521485
>>> a.min()
0.16524768654743593
>>> a.max()
0.9479215542670073
5)指定 axis 参数你可以吧运算应用到数组指定的轴上:
>>> b = 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]])
6)通用函数
NumPy提供常见的数学函数如sin,cos和exp。在NumPy中,这些叫作“通用函数”(ufunc)。在NumPy里这些函数作用按数组的元素运算,产生一个数组作为输出。
>>> B = arange(3)
>>> B
array([0, 1, 2])
>>> exp(B)
array([ 1 , 2.71828183, 7.3890561 ])
>>> sqrt(B)
array([ 0., 1., 1.41421356])
>>> C = array([2., -1., 4.])
>>> add(B, C)
array([ 2., 0., 6.])。
>>> numpy.tile([0,0],5)#在列方向上重复[0,0]5次,默认行1次
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
>>> numpy.tile([0,0],(1,1))#在列方向上重复[0,0]1次,行1次
array([[0, 0]])
>>> numpy.tile([0,0],(2,1))#在列方向上重复[0,0]1次,行2次
array([[0, 0],
[0, 0]])
>>> numpy.tile([0,0],(3,1))
array([[0, 0],
[0, 0],
[0, 0]])
>>> numpy.tile([0,0],(1,3)) #在列方向上重复[0,0]3次,行1次
array([[0, 0, 0, 0, 0, 0]])
>>> numpy.tile([0,0],(2,3)) #在列方向上重复[0,0]3次,行2次
array([[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
>>>numpy.tile([1,2],(2,2,3)) #在列方向重复三次,行2次,行和列再两次
array([[[1,2,1,2,1,2],[1,2,1,2,1,2]] , [[1,2,1,2,1,2],[1,2,1,2,1,2]]])
>>> np.sum([0.5, 1.5])
2.0
>>> np.sum([0.5, 0.7, 0.2, 1.5], dtype=np.int32)
1
>>> np.sum([[0, 1], [0, 5]])
6
>>> np.sum([[0, 1], [0, 5]], axis=0) # 每列元素求和
array([0, 6])
>>> np.sum([[0, 1], [0, 5]], axis=1) # 每行元素求和
array([1, 5])
nonzeros(a)返回数组a中值不为零的元素的下标,它的返回值是一个长度为a.ndim(数组a的轴数)的元组,元组的每个元素都是一个整数数组,其值为非零元素的下标在对应轴上的值。例如对于一维布尔数组b1,nonzero(b1)所得到的是一个长度为1的元组,它表示b1[0]和b1[2]的值不为0(False)。
>>> b1 = np.array([True, False, True, False])
>>> np.nonzero(b1)
(array([0, 2]),)
对于二维数组b2,nonzero(b2)所得到的是一个长度为2的元组。它的第0个元素是数组a中值不为0的元素的第0轴的下标,第1个元素则是第1轴的下标,因此从下面的结果可知b2[0,0]、b2[0,2]和b2[1,0]的值不为0:
>>> b2 = np.array([[True, False, True], [True, False, False]])
>>> np.nonzero(b2)
(array([0, 0, 1]), array([0, 2, 0]))
6、索引,切片和迭代
1)一维数组可以被索引、切片和迭代,就像列表和其它Python序列。
>>> a = arange(10)**3
>>> a
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
>>> 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])
2)多维数组可以每个轴有一个索引。这些索引由一个逗号分割的元组给出。
>>> from numpy import *
>>> def f(x,y):
return 10*x+y
...
>>> b = 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[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]])
3)当少于轴数的索引被提供时,缺失的索引被认为是整个切片:
>>> b[-1] # the last row. Equivalent to b[-1,:]
array([40, 41, 42, 43])
点 (…)代表许多产生一个完整的索引元组必要的分号。如果x是秩为5的数组(即它有5个轴),那么:
x[1,2,…] 等同于 x[1,2,:,:,:],
x[…,3] 等同于 x[:,:,:,:,3]
x[4,…,5,:] 等同 x[4,:,:,5,:].
4)然而,如果一个人想对每个数组中元素进行运算,我们可以使用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
更多[], …, newaxis, ndenumerate, indices, index exp 参考NumPy示例
7、形状操作
1)数组形状修改
>>> a = floor(10*random.random((3,4)))
>>> a
array([[ 7., 5., 9., 3.],
[ 7., 2., 7., 8.],
[ 6., 8., 3., 2.]])
>>> a.shape
(3, 4)
一个数组的形状可以被多种命令修改:
>>> a.ravel() # 展平一个矩阵返回,并不改变原来的矩阵
array([ 7., 5., 9., 3., 7., 2., 7., 8., 6., 8., 3., 2.])
>>> a.reshape(6,2) # 不更改矩阵自身,只是变换形状后返回
array([[ 7., 4.],
[ 3., 3.],
[ 4., 8.],
[ 4., 3.],
[ 0., 2.],
[ 0., 6.]])
>>> a.shape = (6, 2) # 改变数组自身形状
>>> a.transpose() # 矩阵转置并返回,并不改变数组自身
array([[ 7., 9., 7., 7., 6., 3.],
[ 5., 3., 2., 8., 8., 2.]])
>>> a.resize((6,2)) # resize函数改变数组自身
>>> a
array([[ 7., 6.],
[ 1., 5.],
[ 9., 7.],
[ 7., 7.],
[ 9., 8.],
[ 5., 8.]])
更多shape, reshape, resize, ravel 参考NumPy示例
2)组合(stack)不同的数组
>>> a = floor(10*random.random((2,2)))
>>> a
array([[ 1., 1.],
[ 5., 8.]])
>>> b = floor(10*random.random((2,2)))
>>> b
array([[ 3., 3.],
[ 6., 0.]])
>>> vstack((a,b))
array([[ 1., 1.],
[ 5., 8.],
[ 3., 3.],
[ 6., 0.]])
>>> hstack((a,b))
array([[ 1., 1., 3., 3.],
[ 5., 8., 6., 0.]])
>>> column_stack((a,b)) # With 2D arrays
array([[ 1., 1., 3., 3.],
[ 5., 8., 6., 0.]])
>>> a=array([4.,2.])
>>> b=array([2.,8.])
>>> a[:,newaxis] # This allows to have a 2D columns vector
array([[ 4.],
[ 2.]])
>>> column_stack((a[:,newaxis],b[:,newaxis]))
array([[ 4., 2.],
[ 2., 8.]])
>>> vstack((a[:,newaxis],b[:,newaxis])) # The behavior of vstack is different
array([[ 4.],
[ 2.],
[ 2.],
[ 8.]])
3)数组分割(split)成几个小数组:
使用hsplit你能将数组沿着它的水平轴分割,或者指定返回相同形状数组的个数,或者指定在哪些列后发生分割,vsplit沿着纵向的轴分割。
>>> a = floor(10*random.random((2,12)))
>>> a
array([[ 8., 8., 3., 9., 0., 4., 3., 0., 0., 6., 4., 4.],
[ 0., 3., 2., 9., 6., 0., 4., 5., 7., 5., 1., 4.]])
>>> hsplit(a,3) # 分为三个形状相同的数组
[array([[ 8., 8., 3., 9.],
[ 0., 3., 2., 9.]]),
array([[ 0., 4., 3., 0.],
[ 6., 0., 4., 5.]]),
array([[ 0., 6., 4., 4.],
[ 7., 5., 1., 4.]])]
>>> hsplit(a,(3,4)) # Split a after the third and the fourth column
[ array([[ 8., 8., 3.],
[ 0., 3., 2.]]),
array([[ 9.],[ 9.]]),
array([[ 0., 4., 3., 0., 0., 6., 4., 4.],
[ 6., 0., 4., 5., 7., 5., 1., 4.]])]
8、复制和视图
1)完全不拷贝,简单的赋值不拷贝数组对象或它们的数据。
>>> a = 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
2)视图(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) #c的形状改变后不影响a的形状
>>> a.shape
(3,4)
>>> c[0,4] = 1234 # c的数据改变后会影响a的形状,因为c和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]])
3)深复制,这个复制方法完全复制数组和它的数据。
>>> 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]])
本文转载自http://reverland.org/python/2012/08/22/numpy/#fn:3