在讲解多维数组的切片操作之前,我们看一看简单的一维数组切片是如何进行操作的,我们知道在 Python 的列表类型中有浅拷贝和深拷贝之说,在一个列表上进行切片操作,会生成一个新的列表。也就是说列表的切片操作不会影响到列表源数据。
In [1]: a=[1,2,3,4,5,6]
In [2]: a[::]
Out[4]: [1, 2, 3, 4, 5, 6]
In [3]: a=[1,2,3,4,5,6]
In [4]: id(a)
#原列表内存地址
Out[4]: 136347440
In [5]: id(a[::])
#切片后新列表的内存地址
Out[5]: 136153128
由上述代码可见内存地址发生了变化。下面我们看看 Numpy 数组的切片是怎样的,如下:
In [1]: import numpy as np
In [2]: data=np.array([1,2,3,4])
#切片操作
In [3]: data[0:4]
Out[3]: array([1, 2, 3, 4])
In [4]: id(data)
Out[4]: 84768232
#切片赋值操作
In [5]: data[0:4]=1
In [6]: data
Out[6]: array([1, 1, 1, 1])
#内存地址相同
In [7]: id(data)
Out[7]: 84768232
由此可见,当你传递一个数值给数组的切片的时候,数据被传递给了整个切片,这代表数据并不是被复制了,而是说明数组切片是在源数组上进行的。我们继续上面的演示:
#截取data1生成新的数组
In [12]: data1=data[1:3]
In [13]: data1
Out[13]: array([1, 1])
#分别利用索引给data和data1的0位置进行赋值
In [14]: data[0]=12134
In [15]: data1[0]=12134
#在调用data原数组
In [16]: data
Out[16]: array([12134, 12134, 1, 1])
可以看到非常神奇的事情发生了,当我们改变 data1时候,最终的变化也会体现在原数组上,初次接触到 Numpy 的小伙伴也许会感到惊讶,那么为什么会是这个样子的呢?
其实 Numpy 设计之初是针对非常大的数组来开发的,这就带来一个问题,如果 Numpy 每一次切片操作都要复制数据,这将会引起多少的内存占用。所以 Numpy 采用这种设计方式比较适合自身的特性。如果你就是想要一份数组切片的拷贝的话,这里需要使用 copy() 方法,将会得到一个副本,而上述过程中对原数组切片操作后得到的数组称为视图,比如原数组是a,切片后得到 b 数组,那么 b 叫做 a 的视图,这在下一篇文章我们还会讲解。
如果操作副本的话,将不会影响到源数组数据。
下面我们看二维数组的切片操作,示例如下:
In [1]: import numpy as np
In [2]: a=np.array([[1,2,3],[4,5,6]])
In [3]: a
Out[3]:
array([[1, 2, 3],
[4, 5, 6]])
In [4]: a[:]
Out[4]:
array([[1, 2, 3],
[4, 5, 6]])
In [5]: a[0:1]
Out[5]: array([[1, 2, 3]])
In [6]: a[1:2]
Out[6]: array([[4, 5, 6]])
In [7]: a[:2]
Out[7]:
array([[1, 2, 3],
[4, 5, 6]])
数组的切片操作,如 a[:2] 表示前两行,即 a 数组的全部,此时把 a 看做一个整体来进行操作,同时我们还可以进行多数组切片,这与多数组索引的使用方法类似,如下所示:
In [1]: import numpy as np
In [2]: a=np.array([[1,2,3],[4,5,6]])
In [3]: a
Out[3]:
array([[1, 2, 3],
[4, 5, 6]])
In [4]: a[:,1]
Out[4]: array([2, 5])
In [5]: a[1,:]
Out[5]: array([4, 5, 6])
In [6]: a[:,:]
Out[6]:
array([[1, 2, 3],
[4, 5, 6]])
In [7]: a[0,:]
Out[7]: array([1, 2, 3])
In [8]: a[1:,:]
Out[8]: array([[4, 5, 6]])
对于二维数组来说,我们要按照行列的思想去理解它,形如 a[n,:]、a[:,n]、a[m:n,:]、a[:,m:n],都属于二维数组的切片形式,中括号中的第一个位置代表截取行,第二个位置代表截取列,中间需用逗号隔开。注意,我们要将 m:n 看做是一个整体,使用切片操作二维数组的中括号中就有一个冒号,三维数组就会有两个冒号,以此类推,且每个冒号与之间用逗号隔开,如 a[1:,:] 它的含义可以这样理解:
按照上面二维数组的操作方式,理解三维数组的切片操作会简单一些,还是按照三维数组中 [个数:行:列] 的思想去理解它。三维的切片形如 b[n,::]、b[:,n:]、b[::,n]、b[:,:,n]、b[m:n,::]、b[:,m:n:]、b[::,m:n]、b[:,:,m:n],下面看一些简单的示例操作,方便大家进行理解:
In [15]: b=np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
#生成三维数组,由两个二维数组组成
In [16]: b
Out[16]:
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
In [16]: b[1,1,:]
Out[16]: array([10, 11, 12])
#最后生成的是两行两列的数组
In [17]: b[:,:,1]
Out[17]:
array([[ 2,5],
[ 8,11]])
只要记住中括号中的三个位置分别代着不同的操作,这样理解起来就变得非常简单了。本节知识的需要各位小伙伴以注重理解为先,根据示例大家要多多的动手操作,用心去体会它的使用方法。