Python中有四种内置的用于存储数据集合的方法,列表(List)就是其中的一种方法,我们通过下标访问列表中的元素:
s=[1,2,3,4,5]
s[i] #访问第i+1元素
s[i:j] #下标从i到j(不含)
s[i:j:k] #下标从i到j(不含),步长为k
与序列类似,ndarrays可使用标准Python语法x[obj]语法完成切片。其中x表示数组对象,obj是选择对象(selection)。根据obj不同主要分为两种切片方法:
- 基础切片(Basic indexing)
- 高级切片(Advanced indexing)
掌握这些方法可以方便裁剪、筛选你想要的数据。
一、基础切片(Basic indexing)
当选择对象obj是以下情况时,被定义成基础切片:
- 内置Slice对象,通常作为下标序列(左闭右开,0开始序列);
- 一个整数
- 元组(一个或者多个两个对象的组合)
Ellipsis和newaxis可混合在在上述obj中使用。
1.1 单元素切片(Single element indexing)
这个和Python的序列切片一样,允许负序号。
x=np.arange(10)
x[2] #ok 2
x[-2] #ok 8
接下来我们将x序列重塑成二维数组,因为维数变成了二,所以必须提供两个数码以确保正确找到单元素。
x.shape=(2,5)
x[1,3] # 8
x[1,-1] # 9
切片的选择对象
obj之一是元组,如x[(1,3)]但是我们经常省略它,只写成x[1,3],当然你也可以按照C语言思维x[1][3],可以但是没有必要,事实上x[1][3]切片了两次而且产生了临时对象,效率更低。
假如中括号中的引索数字个数小于array的维数,那么它切片的结果是一个子序列:
x[0] # 第一行子序列,[0, 1, 2, 3, 4]
x[0][2] # 2, x[0,2]是其等价形式但是更加简洁
1.2 带步长和起末位置的切片(Slicing and striding)
slice和range对象都可以作为obj,其构造函数为slice(start, stop, step=1) range(start, stop, step),通常我们不会直接使用它,但是很多第三方库如numpy会基于该对象进行范围的指定。(PS. 在Python中指定范围基本上都是左闭右开这种方式,而且下标是从0开始)。
r=range(0,10,2)
s=slice(0,10,2)
x=np.arange(0,10,1)
xx=x[r] # [0 2 4 6 8]
xxx=x[s] # [0 2 4 6 8]
如果平时使用时也这么构造的话,显得有点滑稽了,所以我们会用等价语法进行替代i:j:k,其中i表示起点序号 j表示终点序号 k选取步长。看个简单的例子:
x=np.arange(0,10,1)
xx=x[0:10:2] # [0 2 4 6 8]
如果不进行某个维度不进行slice,那么slice默认为以下行为:
x[1:10:5, ::-1] # 简写形式
obj = (slice(1, 10, 5), slice(None, None, -1)) # 等价形式
x[obj] .
对于一个长度为 N N N的一个维度,如果起末需要是一个负数 i i i,那么它选中的是 N − i N-i N−i序号。
有几点需要注意的:
- 假设数组的维度是
N
N
N,如果
obj都是整数且等于 N N N那么选中的是一个数; - 如果数组的维度是
N
N
N,如果
obj都是slice对象,那么等价于选取子数组(矩阵); - 一个数组进行切片,使用的是引用,任何后续的操作都会影响被切片的数组,如需避免请使用copy进行深拷贝;
M=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
Msub1=M[1,3] # obj是等于数组维度的整数,
# 所以它是一个scalar(int float等最小单元)8
Msub2=M[0:2,0:2] # obj是两个slice,也就是子数组 [[1,2],[5,6]]
1.3 维度检索工具(Dimensional indexing tools)
有很多用来简化表达式的方法,省略号(Ellipsis)就是其中的一种。省略号用来填充那些没有填写的维度,填充含义是:全选。如:
x[..., 0]
x[:, :, 0] # 等价表达
newaxis是另一种用于填充新的维度,它是None的别名:
x = np.array([[[1],[2],[3]], [[4],[5],[6]]])
print(x.shape) # (2, 3, 1)
print(x[:,None,:,:,None].shape) # (2, 1, 3, 1, 1)
print(x[:,np.newaxis,:,:,np.newaxis].shape) # (2, 1, 3, 1, 1)
二、进阶切片(Advanced indexing)
当选择对象obj是以下情况时,被定义成进阶切片:
- 非元组序列(如list)
- ndarray(数据类型是bool或者整数)
- 元组但至少含有一个序列对象或者ndarray
注意:进阶切片默认返回的是深拷贝,也就是对其进行操作不会改变原有的数据。
2.1 整数数组索引
整数数组索引允许我们在目标数组维度限制下进行任意数据的选取。每一个整数数组都代表一定数量该维度的切片。
下面这个是简单的、选取指定序号的元素组成新的数组的例子:
import numpy as np
x=np.arange(1,10,1)
print(x) # [1 2 3 4 5 6 7 8 9]
A0_5=x[np.array([0,1,2,3,4])]
print(A0_5)# [1 2 3 4 5]
A555=x[np.array([1,1,1])] # [1 1 1]
选取时不要超过对应维度的限制,否则会出现IndexError:
x=np.array([[1,2],[3,4],[5,6]])
print(x[np.array([1,-1])] # [[3,4],[5,6]], 各轴下标均在范围内,OK!
print(x[np.array([3,4])]) # error, 轴0超范围限制
每一个维度都会不大于给定切片范围,它可以由序列、np.array或者slice组成。
import numpy as np
import matplotlib.pyplot as plt
x=np.array([[1,2,11,22],[3,4,33,44],[5,6,44,66]])
r23=x[np.array([0])] # 选择0 2 行
c23=x[:,np.array([0,2])] # 选择0 2 列
r23_m1=x[[0],:] # 选择0 2 行
c23_m1=x[:,[0,2]] # 选择0 2 列
按照指定各个维度进行切片,如 0,0 2,1 4,2:
import numpy as np
x=np.arange(10,1,-1)
print(x[np.array([3,3,1,8])]) # [7 7 9 2]
y=np.arange(35).reshape(5,7)
print(y[np.array([0, 2, 4]), np.array([0, 1, 2])]) # [ 0 15 30]
。如果中括号中给出的不是两个对应的数组,那么将会尝试通过broadcasting方式扩展成我们所需要的形式。如:
y[np.array([0, 2, 4]), 1]
你可以选择一个矩阵的某些行,就好像这样:
y[np.array([0, 2, 4])]
array([[ 0, 1, 2, 3, 4, 5, 6],
[14, 15, 16, 17, 18, 19, 20],
[28, 29, 30, 31, 32, 33, 34]])
2.2 布尔型切片
此类进阶切片出现在obj对象是一个布尔类型的,比如说比较的运算。
这个通常会用在数据筛选上,比如我们想要从一个数组中取出所有非NAN的数据:
x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]])
x[~np.isnan(x)] # [1., 2., 3.],注意这里的shape发生了改变
或者想要将数组中所有x小于2的元素减10:
x=np.arange(0,6).reshape(2,3) # [[0,1,2],[3,4,5]]
x[x<=2]-=10 # [[-10,-9,-8],[3,4,5]]
此外,逻辑数组也是会broadcast的:
x=np.arange(0,12).reshape(3,4) # [[0,1,2,3],[4,5,6,7],[8,9,10,11]]
b=[True,False,True]
print(x[b]) #等价于 x[b,...]
或者可以选择所有加起来小于或者等于2的行:
x = np.array([[0, 1], [1, 1], [2, 2]])
rowsum = x.sum(-1) # np.array有sum方法,负数表示倒数第几个轴,对于矩阵来说
x[rowsum <= 2, :]
更加复杂的应用:下面这段代码筛选了行和被2整除的,1 3 列:
x = np.array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
rows = (x.sum(-1) % 2) == 0
columns = [0, 2]
x[np.ix_(rows, columns)] # [[3,5],[9,11]]
这里的np.ix_表示网状扩展。
[1]https://numpy.org/doc/stable/user/basics.indexing.html#basic-indexing
1933

被折叠的 条评论
为什么被折叠?



