Numpy(五)ndarrays的切片

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开始序列);
  • 一个整数
  • 元组(一个或者多个两个对象的组合)

Ellipsisnewaxis可混合在在上述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)

slicerange对象都可以作为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 Ni序号。

有几点需要注意的:

  • 假设数组的维度是 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值