Numpy的数组类叫做ndarray。也被称做轴数组array。值得注意的是numpy.array并不像标准Python库类array.array一样,只能处理一维数组及提供少量功能。ndarray对象更重要的属性如下:
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) # 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创建一个初始内容随机并且依赖于内存状态的数组。默认情况下,创建的数组的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)
See also
array, zeros, zeros_like, ones, ones_like, empty, empty_like, arange, linspace, numpy.random.rand,numpy.random.randn, fromfunction, fromfile
打印数组
当你打印一个数组时,Numpy通过类似的嵌套列表进行显示,但是要按照下面的布局:
- 最后的一个轴(axis)从左向右打印
- 第二个到最后一个轴(axis)从上到下打印
- 剩下的同样从上向下打印,其中第一个分片与下一个分片通过一个空行进行分隔
一维的数组打印成行,二维数组打印成矩阵形式,三维的成为矩阵列表形式。
>>> 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='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'
当操作不同类型的数组时,结果的类型倾向于更常见的类型或者更精确的类型(这一行为被称为向上范型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
默认情况下,这些操作应用在数组上就像它是一个数字列表一样,忽略了它的形状shape。但是,通过指定axis参数,你可以将操作应用到一个数组的指定轴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]])
通用函数
Numpy提供了一些我们所熟悉的数学操作,像sin,cos和ext.在Numpy中,这些叫做通用函数(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.])
>>> np.add(B, C)
array([ 2., 0., 6.])
See also
all, any, apply_along_axis, argmax, argmin, argsort, average, bincount, ceil, clip, conj, corrcoef, cov, cross, cumprod,cumsum, diff, dot, floor, inner, inv, lexsort, max, maximum, mean, median, min, minimum, nonzero, outer, prod, re, round,sort, std, sum, trace, transpose, var, vdot, vectorize, where
索引、分片和迭代
一个维度的数组可以被索引、分片和迭代,这很像列表和其他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
多个维度的数组每一个轴axis有一个索引。这些索引通过一个由逗号分隔的元组给出:
>>> 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]])
当提供的索引少于轴axes的数量时,丢失的索引被认为是完整的分片:
>>> b[-1] # the last row. Equivalent to b[-1,:]
array([40, 41, 42, 43])
带有中括号的表达式b[i]被认为是i后面根据需要可以跟任意多的 : 来表示剩余的轴(axes)。Numpy同样支持带有点的写法b[i,...]
这些
点(...)代表尽可能多的冒号来产生一个完整的索引元组。例如,如果x是排序5的数组(也就是它有5个轴),那么
-
x[1,2,...] is equivalent to x[1,2,:,:,:],
-
x[...,3] to x[:,:,:,:,3] and
-
x[4,...,5,:] to 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
(Shape)形状操作
改变一个数组的形状
一个数组具有由各个轴元素个数给出的形状:
numpy.floor()向下取整函数
>>> 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() # flatten the array
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()通常不用拷贝自己的参数,但是一个数组是通过其他数组的分片或者用不寻常的选项来创建的,那么它或许需要拷贝元素。函数ravel()和reshape()也可以被通过使用一个
选项参数来指定使用FORTRAN形式的数组,这种情况下,最左侧的索引变化最快。
reshape函数返回其带有修改形状的参数,鉴于ndarray.siza方法修改了数组自己:
>>> 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.]])
如果在一个reshaping操作中一个维给定为-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维数组作为列堆叠到2维数组中去。它和1维数组的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.]])
对于超过2维的数组,hstack沿着第二个轴堆叠,vstack沿着第一个轴,concatenate允许一个可选的参数来指定具体的轴来进行联接操作。
注意
在更复杂的情况下,r_和c_在通过沿着一个轴堆叠数字创建数组时是有用的。他们允许使用范围(range)字面量(“:”):
>>> 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允许指定沿着哪个轴进行分割。
拷贝和观察
当操作数组时,它们的数据有时需要拷贝到一个新数组中有时不需要。这对于新手常常是一个困惑。有三种情况:
完全不拷贝
简单任务不需要拷贝数组对象及它们的数据。
>>> 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]])
对一个数组进行分片,返回它的一个视图(view):
对s进行的操作,实际上改变的是a.
>>> 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中比较有用的函数和方法名字,按类型进行排列。
Array Creation
arange,
array,
copy,
empty,
empty_like,
eye,
fromfile,
fromfunction,
identity,
linspace,
logspace,
mgrid,
ogrid,
ones,
ones_like,
r,
zeros,
zeros_like
Conversions
ndarray.astype,
atleast_1d,
atleast_2d,
atleast_3d,
mat
Manipulations
array_split,
column_stack,
concatenate,
diagonal,
dsplit,
dstack,
hsplit,
hstack,
ndarray.item,
newaxis,
ravel,
repeat,
reshape,
resize,
squeeze,
swapaxes,
take,
transpose,
vsplit,
vstack
Questions
all,
any,
nonzero,
where
Ordering
argmax,
argmin,
argsort,
max,
min,
ptp,
searchsorted,
sort
Operations
choose,
compress,
cumprod,
cumsum,
inner,
ndarray.fill,
imag,
prod,
put,
putmask,
real,
sum
Basic Statistics
cov,
mean,
std,
var
Basic Linear Algebra
cross,
dot,
outer,
linalg.svd,
vdot
非常见基础
广播规则
广播允许通用函数以一种更有意义的方式处理那些具有不完全相同形状的输入。
广播的第一条规则是:如果所有输入数组不全具有相同的维数,“1”将被重复添加到小的数组上直到所有的数组都具有相同的维数为止。
广播的第二条规则:确保沿着特定维度大小为1的数组就好像它们具有沿着这个维度最大形状的数组的大小一样。数组元素的值假定和“广播”数组维度的值一样。
应用广播规则后,所有数组的大小必须匹配。
花哨索引和索引技巧
Numpy提供了比普通Python序列更多的索引工具,除了通过整数和分片索引外,像我们之前看到的一样,数组可以被整数数组和布尔数组索引。
使用索引数组索引
>>> a = np.arange(12)**2 # the first 12 square numbers
>>> i = np.array( [ 1,1,3,8,5 ] ) # an array of indices
>>> a[i] # the elements of a at the positions i
array([ 1, 1, 9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # a bidimensional array of indices
>>> a[j] # the same shape as j
array([[ 9, 16],
[81, 49]])
当已索引的数组a是多维的时候,索引的一个单独数组是指a的第一维。下面的例子通过使用palette将一个标签图像转换为一个彩色图像展示了这一行为。
>>> palette = np.array( [ [0,0,0], # black
... [255,0,0], # red
... [0,255,0], # green
... [0,0,255], # blue
... [255,255,255] ] ) # white
>>> image = np.array( [ [ 0, 1, 2, 0 ], # each value corresponds to a color in the palette
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # the (2,4,3) color image
array([[[ 0, 0, 0],
[255, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 0, 0, 255],
[255, 255, 255],
[ 0, 0, 0]]])
我们同样可以给出多于一维的索引值。每一维的索引数组必须具有相同的形状。
a[i]取的是第一维的,a[i,j]中的j是取的第二维的,本例子中是取第二维(即a[i])中的第一行第二个元素,第二行第一个元素,第三行第三个元素,第四行第三个元素。
>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> i = np.array( [ [0,1], # indices for the first dim of a
... [1,2] ] )
>>> j = np.array( [ [2,1], # indices for the second dim
... [3,3] ] )
>>>
>>> a[i,j] # i and j must have equal shape
array([[ 2, 5],
[ 7, 11]])
>>>
>>> a[i,2]
array([[ 2, 6],
[ 6, 10]])
>>>
>>> a[:,j] # i.e., a[ : , j]
array([[[ 2, 1],
[ 3, 3]],
[[ 6, 5],
[ 7, 7]],
[[10, 9],
[11, 11]]])
自然地,我们可以将i和j放在一个序列中(比如一个列表),然后用这个列表来进行索引。
>>> l = [i,j]
>>> a[l] # equivalent to a[i,j]
array([[ 2, 5],
[ 7, 11]])
但是,我们不能将i和j放入数组中执行此操作,因此这个数组会被认为是索引a的第一维。
>>> s = np.array( [i,j] )
>>> a[s] # not what we want
Traceback (most recent call last):
File "<stdin>", line 1, in ?
IndexError: index (3) out of range (0<=index<=2) in dimension 0
>>>
>>> a[tuple(s)] # same as a[i,j]
array([[ 2, 5],
[ 7, 11]])
另一个索引数组的常用情况是搜索时间序列的最大值:
>>> time = np.linspace(20, 145, 5) # time scale
>>> data = np.sin(np.arange(20)).reshape(5,4) # 4 time-dependent series
>>> time
array([ 20. , 51.25, 82.5 , 113.75, 145. ])
>>> data
array([[ 0. , 0.84147098, 0.90929743, 0.14112001],
[-0.7568025 , -0.95892427, -0.2794155 , 0.6569866 ],
[ 0.98935825, 0.41211849, -0.54402111, -0.99999021],
[-0.53657292, 0.42016704, 0.99060736, 0.65028784],
[-0.28790332, -0.96139749, -0.75098725, 0.14987721]])
>>>
>>> ind = data.argmax(axis=0) # index of the maxima for each series
>>> ind
array([2, 0, 3, 1])
>>>
>>> time_max = time[ ind] # times corresponding to the maxima
>>>
>>> data_max = data[ind, xrange(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
>>>
>>> time_max
array([ 82.5 , 20. , 113.75, 51.25])
>>> data_max
array([ 0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>>
>>> np.all(data_max == data.max(axis=0))
True
你也可以使用数组索引来作为分配的目标:
>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1,3,4]] = 0
>>> a
array([0, 0, 2, 0, 0])
However, when the list of indices contains repetitions, the assignment is done several times, leaving behind the last value:
>>> a = np.arange(5)
>>> a[[0,0,2]]=[1,2,3]
>>> a
array([2, 1, 3, 3, 4])
这是合理的,但是请注意如果你想使用Python的+=结构,将不会产生你所期待的结果:
>>> a = np.arange(5)
>>> a[[0,0,2]]+=1
>>> a
array([1, 1, 3, 3, 4])
虽然在索引列表中0出现了两次,第0维只是增加一次。这是因为Python需要‘a+=1’和‘a=a+1’相等。
使用布尔数组索引
当我们使用(整数)索引数组对数组进行索引时我们提供可挑选索引的列表。对于布尔型索引这个方法就不同了;我们必须明确指出数组中我们需要哪个条目不需要哪个。
最自然的方式是一个人可以认为对于布尔索引使用布尔数组的情况与原始数组具有同样大小的形状。
>> a = np.arange(12).reshape(3,4)
>>> b = a > 4
>>> b # b is a boolean with a's shape
array([[False, False, False, False],
[False, True, True, True],
[ True, True, True, True]], dtype=bool)
>>> a[b] # 1d array with the selected elements
array([ 5, 6, 7, 8, 9, 10, 11])
这一特性在分配时将十分有用:
>>> a[b] = 0 # All elements of 'a' higher than 4 become 0
>>> a
array([[0, 1, 2, 3],
[4, 0, 0, 0],
[0, 0, 0, 0]])
你可以看下面的例子是如何使用布尔索引来生成Mandelbrot集图像的:
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> def mandelbrot( h,w, maxit=20 ):
... """Returns an image of the Mandelbrot fractal of size (h,w)."""
... y,x = np.ogrid[ -1.4:1.4:h*1j, -2:0.8:w*1j ]
... c = x+y*1j
... z = c
... divtime = maxit + np.zeros(z.shape, dtype=int)
...
... for i in range(maxit):
... z = z**2 + c
... diverge = z*np.conj(z) > 2**2 # who is diverging
... div_now = diverge & (divtime==maxit) # who is diverging now
... divtime[div_now] = i # note when
... z[diverge] = 2 # avoid diverging too much
...
... return divtime
>>> plt.imshow(mandelbrot(400,400))
>>> plt.show()
(Source code, png, pdf)
使用布尔型索引的第二种方式与整数索引非常相似;对于这个数组的每一维度我们给定一个1维布尔数组选择我们需要的分片。
>>> a = np.arange(12).reshape(3,4)
>>> b1 = np.array([False,True,True]) # first dim selection
>>> b2 = np.array([True,False,True,False]) # second dim selection
>>>
>>> a[b1,:] # selecting rows
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> a[b1] # same thing
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> a[:,b2] # selecting columns
array([[ 0, 2],
[ 4, 6],
[ 8, 10]])
>>>
>>> a[b1,b2] # a weird thing to do
array([ 4, 10])
a[b1,b2] 一个奇怪的事情
注意1维布尔数组的长度必须与你想分片的维(或者轴)的长度一致,在前面的例子中,b1是一个排序1长度3(a中的行数)的数组,b2(长度4)用于a的第二个排序(列)。
ix_()函数
ix_函数可以用来结合不同的向量,这是为了获取每一个多元组(n-uplet)的结果。例如:如果你想为所有取自向量a,b,c的三元组计算a+b*c
>>> a = np.array([2,3,4,5])
>>> b = np.array([8,5,4])
>>> c = np.array([5,4,6,8,3])
>>> ax,bx,cx = np.ix_(a,b,c)
>>> ax
array([[[2]],
[[3]],
[[4]],
[[5]]])
>>> bx
array([[[8],
[5],
[4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> result = ax+bx*cx
>>> result
array([[[42, 34, 50, 66, 26],
[27, 22, 32, 42, 17],
[22, 18, 26, 34, 14]],
[[43, 35, 51, 67, 27],
[28, 23, 33, 43, 18],
[23, 19, 27, 35, 15]],
[[44, 36, 52, 68, 28],
[29, 24, 34, 44, 19],
[24, 20, 28, 36, 16]],
[[45, 37, 53, 69, 29],
[30, 25, 35, 45, 20],
[25, 21, 29, 37, 17]]])
>>> result[3,2,4]
17
>>> a[3]+b[2]*c[4]
17
你也可以按下面的方法实现缩减:
>>> def ufunc_reduce(ufct, *vectors):
... vs = np.ix_(*vectors)
... r = ufct.identity
... for v in vs:
... r = ufct(r,v)
... return r
然后这样使用它:
>>> ufunc_reduce(np.add,a,b,c)
array([[[15, 14, 16, 18, 13],
[12, 11, 13, 15, 10],
[11, 10, 12, 14, 9]],
[[16, 15, 17, 19, 14],
[13, 12, 14, 16, 11],
[12, 11, 13, 15, 10]],
[[17, 16, 18, 20, 15],
[14, 13, 15, 17, 12],
[13, 12, 14, 16, 11]],
[[18, 17, 19, 21, 16],
[15, 14, 16, 18, 13],
[14, 13, 15, 17, 12]]])
这个版本的缩减(reduce)与正常的ufunc.reduce相比优势是:为了避免创建一个输出大小乘以向量数量的参数数组而使用广播规则。(
it makes use of the
Broadcasting Rules
in order to avoid creating an argument array the size of the output times the number of vectors)
线性代数
工作当中基础的线性代数包含在这里。
简单操作
在numpy文件夹中中的linalg.py文件获取更多内容。
>>> import numpy as np
>>> a = np.array([[1.0, 2.0], [3.0, 4.0]])
>>> print(a)
[[ 1. 2.]
[ 3. 4.]]
>>>
>>> a.transpose()
array([[ 1., 3.],
[ 2., 4.]])
>>>
>>> np.linalg.inv(a)
array([[-2. , 1. ],
[ 1.5, -0.5]])
>>>
>>> u = np.eye(2) # unit 2x2 matrix; "eye" represents "I"
>>> u
array([[ 1., 0.],
[ 0., 1.]])
>>> j = np.array([[0.0, -1.0], [1.0, 0.0]])
>>>
>>> np.dot (j, j) # matrix product
array([[-1., 0.],
[ 0., -1.]])
>>>
>>> np.trace(u) # trace
2.0
>>>
>>> y = np.array([[5.], [7.]])
>>> np.linalg.solve(a, y)
array([[-3.],
[ 4.]])
>>>
>>> np.linalg.eig(j)
(array([ 0.+1.j, 0.-1.j]), array([[ 0.70710678+0.j , 0.70710678-0.j ],
[ 0.00000000-0.70710678j, 0.00000000+0.70710678j]]))
Parameters:
square matrix
Returns
The eigenvalues, each repeated according to its multiplicity.
The normalized (unit "length") eigenvectors, such that the
column ``v[:,i]`` is the eigenvector corresponding to the
eigenvalue ``w[i]`` .
技巧和提示
这里给出一些短的但有用的技巧列表。
“自动”整形
为了改变数组的维度,你可以忽略一个后续可以自动推断出来的大小:
>>> 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]]])
向量堆叠
我们如何从一系列相同大小的元始向量构建一个2维数组呢?在MATLAB中这相当简单:如果x和y是两个长度相同的向量,你只需要做m=[x:y]。在Numpy中这个工作通过column_stack,dstack和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])
这些函数背后的逻辑在超过2维时是奇怪的。
柱状图
Numpy的histogram函数应用到一个数组上返回一对向量:数组的直方图和箱子的向量。注意:matplotlib中也有一个创建直方图的函数(和Matlab中一样,叫做hist)和Numpy中的这个不一样。主要的不同点是pylab.hist自动画出直方图,而numpy.histogram仅仅是产生数据。
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> # Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
>>> mu, sigma = 2, 0.5
>>> v = np.random.normal(mu,sigma,10000)
>>> # Plot a normalized histogram with 50 bins
>>> plt.hist(v, bins=50, normed=1) # matplotlib version (plot)
>>> plt.show()
(Source code, png, pdf)
>>>
>>> # Compute the histogram with numpy and then plot it
>>> (n, bins) = np.histogram(v, bins=50, normed=True) # NumPy version (no plot)
>>> plt.plot(.5*(bins[1:]+bins[:-1]), n)
>>> plt.show()
(png, pdf)