NumPy-切片和索引
对于内容的引用说明:
Numpy 笔记(二): 多维数组的切片(slicing)和索引(indexing):http://www.zmonster.me/2016/03/09/numpy-slicing-and-indexing.html,感谢ZMonster's Blog原创内容。
ndarray对象的内容可以通过索引或切片来访问和修改,ndarray对象中的元素遵循基于零的索引。 有三种可用的索引方法:字段访问,基本切片和高级索引。
基本切片是 Python 中基本切片概念到 n 维的扩展。 通过将start
,stop
和step
参数提供给内置的slice
函数来构造一个 Pythonslice
对象。 此slice
对象被传递给数组来提取数组的一部分。简而言之,切片是有索性。
1、基本原理是这样:
1、切片需要使用序列生成,即冒号运算符‘:‘,一个单冒号为选取整个序列,也可以指定范围,如1:5表示1~4(包括)这4个元素。同时可以指定步长如1:6:2表示选取1,3,5这3个元素,其中start=1,stop=6,step=2,这个与Python的列表切片是一致的。
2、然后,使用逗号,来区分轴(axis),轴可以简单地理解为在某个维度上的投影。例如axis=0指的就是行,axis=1指的就是列。那么第一个选取就是行,第二个选取的就是列,中间用逗号隔开,例如下面的一条语句。
arr[:,2] |
---|
在行上选取的是:,即整个序列,在列上选取的是2,即第3列,这样得到的结果就是第三列(注意这时候得到的是行向量而不是列向量)
2、数组的索引和切片抽象表示:
- 索引:获取数组中特定位置元素的过程。
切片:获取数组元素子集的过程。
一维数组的索引和切片:与Python的列表类似
a[n]
:返回第n+1个元素。如果n为负数,则返回倒着数第n个元素。a[n:m:k]
:起始编号n,终止编号m,步长k,用冒号分割。如果n为空,即n = 0
;如果m为空,即m = len(a)
。
多维数组的索引和切片:
a[n,m,k,...]
:每个维度一个索引值,最外list中第n个元素,倒数第二外list中第m个元素,以此类推。如果n为负数,则返回倒着数第n个元素。a[n1:m1:k1,n2:m2:k2,n3:m3:k3,...]
:每个维度的切片方法与一维数组相同。顺序为从外到内
3、切片操作
Numpy中多维数组的切片操作与Python中list的切片操作一样,同样由start,stop,step三个部分组成。
示例1
import numpy as np
a = np.arange(10)
s = slice(2,7,2)
print(a[s])
输出如下:
[2 4 6]
在上面的例子中,ndarray
对象由arange()
函数创建。 然后,分别用起始,终止和步长值2
,7
和2
定义切片对象。 当这个切片对象传递给ndarray
时,会对它的一部分进行切片,从索引2
到7
,步长为2
。
通过将由冒号分隔的切片参数(start:stop:step
)直接提供给ndarray
对象,也可以获得相同的结果。
示例2
import numpy as np
a = np.arange(10)
b = a[2:7:2]
print(b)
输出如下:
[2 4 6]
如果只输入一个参数,则将返回与索引对应的单个项目。 如果使用a:
,则从该索引向后的所有项目将被提取。 如果使用两个参数(以:
分隔),则对两个索引(不包括停止索引)之间的元素以默认步骤进行切片。
示例3
#对单个元素进行切片
import numpy as np
a = np.arange(10)
b = a[5]
print(b)
输出如下:
5
示例4
#对始于索引的元素进行切片
import numpy as np
a = np.arange(10)
print(a[2:])
输出如下:
[2 3 4 5 6 7 8 9]
示例5
#对索引之间的元素进行切片
import numpy as np
a = np.arange(10)
print(a[2:5])
输出如下:
[2 3 4]
上面的描述也可用于多维ndarray
。
示例6
import numpy as np
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a)
# 对始于索引的元素进行切片
print('\n现在我们从索引 a[1:] 开始对数组切片')
print(a[1:])
输出如下:
[[1 2 3]
[3 4 5]
[4 5 6]]
现在我们从索引 a[1:] 开始对数组切片
[[3 4 5]
[4 5 6]]
切片还可以包括省略号(...
),来使选择元组的长度与数组的维度相同。 如果在行位置使用省略号,它将返回包含行中元素的ndarray
。
示例7
#省略号必须是三个
#最开始的数组
import numpy as np
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a)
# 这会返回第二列元素的数组:
print('\n第二列的元素是:')
print(a[...,1])
# 现在我们从第二行切片所有元素:
print('\n第二行的元素是:')
print(a[1,...])
# 现在我们从第二列向后切片所有元素:
print('\n第二列及其剩余元素是:')
print(a[...,1:])
输出如下:
我们的数组是:
[[1 2 3]
[3 4 5]
[4 5 6]]
第二列的元素是:
[2 4 5]
第二行的元素是:
[3 4 5]
第二列及其剩余元素是:
[[2 3]
[4 5]
[5 6]]
4、索性操作
最简单的情况,对于一个多维数组来说,最简单的情况就是访问其中一个特定位置的元素了,如下所示:
# coding: utf-8
import numpy as np
arr = np.array([
[1, 2, 3, 4],
[2, 4, 6, 8],
[3, 6, 9, 12],
[4, 8, 12, 16]
])
print( '第二行第二列的值:', arr[1, 1])
第二行第二列的值: 4
相比之下,如果用Python的list来表示上述二维数组,获取同一个位置的元素的方法为:
# coding: utf-8
arr = [
[1, 2, 3, 4],
[2, 4, 6, 8],
[3, 6, 9, 12],
[4, 8, 12, 16]
]
print ('第二行第二列的值:', arr[1][1])
try:
print ('第二行第二列的值(尝试用 Numpy 的方式获取):', arr[1, 1])
except Exception as e:
print(str(e))
输出如下:
第二行第二列的值: 4
list indices must be integers or slices, not tuple
如果只是二维数组,这种差别可能看起来并不大,但想象一下假如有一个10维的数组,用Python的标准做法需要写10对中括号,而用Numpy依然只需要一对。
获取多个元素
事实上,在Numpy的索引操作方式`x=arr[obj]`中,obj不仅仅可以是一个用逗号分隔开的数字序列,还可以是更复杂的内容。
用逗号分隔的数组序列
- 序列的长度和多维数组的维数要一致
- 序列中每个数组的长度要一致
import numpy as np
arr = np.array([
[1, 2, 3, 4],
[2, 4, 6, 8],
[3, 6, 9, 12],
[4, 8, 12, 16]
])
print(arr[[0, 2], [3, 1]])
输出:
[4 6]
以上面这个例子来说,其含义是:选择第一行和第三行,然后对第一行选择第四列,对第三行选择第二列。
2.boolean/maskindex
这个不太好翻译,所以就用原来的英语表达。
所谓booleanindex,就是用一个由boolean类型值组成的数组来选择元素的方法。比如说对下面这样多维数组
array([[1, 2, 3, 4],
[2, 4, 6, 8],
[3, 6, 9, 12],
[4, 8, 12, 16]])
如果要取其中值大于5的元素,就可以用上 boolean index 了,如下所示:
import numpy as np
arr = np.array([[1, 2, 3, 4],
[2, 4, 6, 8],
[3, 6, 9, 12],
[4, 8, 12, 16]])
mask = arr
>
5
print ('boolean mask is:')
print (mask)
print (arr[mask])
输出如下:
boolean mask is:
[[False False False False]
[False False True True]
[False True True True]
[False True True True]]
[ 6 8 6 9 12 8 12 16]
除了比较运算能产生booleanmask数组以外,Numpy本身也提供了一些工具方法:
- numpy.iscomplex
- numpy.isreal
- numpy.isfinite
- numpy.isinf
- numpy.isnan
5、切片和索引的同异
切片和索引都是访问多维数组中元素的方法,这是两者的共同点,不同之处有:
- 切片得到的是原多维数组的一个视图(view),修改切片中的内容会导致原多维数组的内容也发生变化
- 切片得到在多维数组中连续(或按特定步长连续)排列的值,而索引可以得到任意位置的值,自由度更大一些
不考虑第一点的话,切片的操作是可以用索引操作来实现的,不过这没有必要就是了。
对于第一点,见下面的实验:
import numpy as np
arr = np.arange(12).reshape(2, 6)
print('array is:')
print( arr)
slc = arr[:, 2:5]
print ('slice is:')
print (slc)
slc[1, 2] = 10000
print ('modified slice is:')
print (slc)
print ('array is now:')
print (arr)
输出如下:
array is:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
slice is:
[[ 2 3 4]
[ 8 9 10]]
modified slice is:
[[ 2 3 4]
[ 8 9 10000]]
array is now:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10000 11]]