一、复制
当操作和操作数组时,它们的数据有时被复制到一个新的数组中,有时候不会。 这往往是初学者混乱的根源。 有三种情况:
1、简单的赋值
简单的赋值不会使数组对象或其数据复制。
>>> a = np.arange(12)
>>> b = a # 并没有创建新的对象
>>> b is a # a、b是数组的两个名字(或称为两个引用)
True
>>> b.shape = 3,4 # 改变其中一个数组的形状也会改变另一个形状
>>> a.shape
(3, 4)
Python将可变对象作为引用传递,因此函数调用不会复制:
>>> def f(x):
... print(id(x))
...
>>> id(a) # id 是对象的唯一标识符
148293216
>>> f(a)
148293216 #函数调用并没有复制
2、浅复制
不同的数组对象可以共享同一组数据, view方法创建一个查找相同数据的新数组对象。
>>> c = a.view()
>>> c is a
False
>>> c.base is a # c是由a拥有的数据的浅复制
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # 修改c的形状不会修改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]
>>> s[:] = 10 # s[:] = 10 是将所有元素赋值为10
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
3、深拷贝
copy 方法将完整复制数组及其数据。
>>> d = a.copy() # 创建一个新的数组对象
>>> d is a
False
>>> d.base is a #d与a不共享任何数据
False
>>> d[0,0] = 9999
>>> a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
总结:数组之间的赋值,形如’ b=a’ ,只会增加对原数组的引用,当任意引用做改变时,都会修改原数组;使用view()方法获取的数据,实际上只是将数组中的每个元素添加了引用,因此shape变化的时候不会导致另一个也发生变化,但是如果修改数据,所有的数组都会变化;而deep方法是将原数组中的每个元素取出来生成新的引用。
二、有趣的索引和索引技巧
NumPy提供比普通Python序列更多的索引方法。 除了通过整数和切片进行索引之外,如前所述,数组可以由整数和布尔数组的数组建立索引。
1、用索引数组进行索引
>>> a = np.arange(12)**2 # 0...11的平方
>>> i = np.array( [ 1,1,3,8,5 ] ) # 一个索引数组
>>> a[i] #i中所示位置的所有元素
array([ 1, 1, 9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # 一个二维数组索引
>>> a[j] # 生成的形状和j的一样
array([[ 9, 16],
[81, 49]])
当索引数组a是多维的时,单个索引数组指的是a的第一个维度。 以下示例通过使用调色板将标签的图像转换为彩色图像来显示此行为。
>>> 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 ], # 每个值对应palette的一个颜色
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # 生成个2*4*3的数组
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 = 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], # i数组中的所有值都表示a的第一维度
... [1,2] ] )
>>> j = np.array( [ [2,1], # 表示a的第二维度
... [3,3] ] )
>>>
>>> a[i,j] # i,j必须具备相同的维度
array([[ 2, 5],
[ 7, 11]])
>>>
>>> a[i,2] #表示第二维度全是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] # 和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)] # 相当于 a[i,j]
array([[ 2, 5],
[ 7, 11]])
使用数组索引的另一个常见用途是搜索时间依赖系列的最大值:
>>> time = np.linspace(20, 145, 5) # 时间尺度
>>> data = np.sin(np.arange(20)).reshape(5,4) #4个时间依赖的系列
>>> 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) # 列的最大值的索引
>>> ind
array([2, 0, 3, 1])
>>>
>>> time_max = time[ ind] #
33/5000
时间对应于最大值
>>>
>>> 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])
但是,当索引列表包含重复时,赋值完成多次后,将留下最后一次的值:
>>> a = np.arange(5)
>>> a[[0,0,2]]=[1,2,3]
>>> a
array([2, 1, 3, 3, 4])
2、使用布尔型值作为索引
当我们使用整型数索引的数组来索引数组时,我们提供要选择的索引列表。 使用布尔索引,方法是不同的; 我们明确地选择了我们想要的数组中的哪些元素以及我们不需要哪些元素。
>>> a = np.arange(12).reshape(3,4)
>>> b = a > 4
>>> b # b是布尔矩阵,形状和a相同
array([[False, False, False, False],
[False, True, True, True],
[ True, True, True, True]], dtype=bool)
>>> a[b] # 选择之后的数组
array([ 5, 6, 7, 8, 9, 10, 11])
这个属性在设置中非常有用:
>>> a[b] = 0 # 大于4的全设置为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()
以上关于numpy的学习内容是来自官方文档