Python对于numpy花式索引的一些理解

基础概念

最近在学习《Python数据科学手册》,看到2.7《花哨的索引》章节时,产生了一些疑问,经过一段时间的思考后,在书本的基础上得出了一些稍微深一点的理解,特此记录一下,以供后用。

首先,是概念性介绍。花式索引,在概念上非常简单,它意味着传递一个索引数组来一次性获得多个数组元素。

接下来将从源数组与目标数组的维度组合层面展开讨论。

源数组为一维数组,目标数组为一维数组

在以下数组x中,假设我们希望获得三个不同的元素(比如说index = 3,7,4三个位置),可以利用一下两种方法得到想要的元素:

// An basic fancy indexing
import numpy as np
x = np.arange(1,11)
print(x)
// method A:
[x[3],x[7],x[4]]
// method B:
ind = [3,7,4]
x[ind]

method A:
直接得出各位置的值,然后形成list;
执行结果为[4,8,5], 类型为list;

method B:
将各位置形成一个数组,再利用这个数组进行花式索引
执行结果为array[4,8,5],类型为np.array

可以看到,两种方法得到的值是想要的目标元素。

源数组为一维数组,目标数组为多维数组

在上面“基础概念”中,已经通过花哨索引的方式得出了一维目标数组。那么,如果目标数组是多维的,该如何得到呢?

回到花哨索引的概念可以看到,花哨索引得到的结果,形状与索引数组的形状一致。因此,我们可以从索引数组的角度考虑如何得到多维的数组。例如,以下代码:

ind = np.array([[3,7],
                [4,5]])
x[ind]

运行结果为[[4,8],[5,6]]。

这是因为索引数组ind是(2,2)形状的数组,所以得出的数组也为(2,2)形状。然后目标数组的值为源数组在索引数组各索引位置上的值,比如说ind[1][1]的值为5, 那么源数组在位置5上的值即为目标值,即为6。

从一维数组生成三维甚至更多维度数组,与生成二维数组类似,只需要将索引数组变成目标数组形状即可,较为简单,在此省略。

源数组为多维数组,目标数组为一维数组

如果源数组为多维数组,我们希望得到一个一维数组,该如何进行花式索引呢?
方法如下:和标准的索引方式一样,第一个索引指的是行,第二个索引指的是列。

y = np.arange(1,13).reshape((3,4))
row = np.array([0,1,2])
col = np.array([2,1,3])

y[row, col]

得到的目标数组1为array([3,6,12]),其分别为
y[row[0], col[0]],亦即y[0, 2] = 3,
y[row[1], col[1]],亦即y[1, 1] = 6,
y[row[2], col[2]],亦即y[2, 3] = 12。

如果目标为三维甚至更高维度呢?

原理其实相同的,还是这种方式,分别输入在各维度上的坐标。比如我们要在以下(2,3,4)的数组中分别得到阿里纳斯、艾弗森、科比、卡特、乔丹的球衣号的话,可以按以下代码得出:

z = np.arange(24).reshape((2,3,4))
z[[0,0,0,1,1],[0,0,2,0,2],[0,3,0,3,3]]

得出的是,坐标分别为(0,0,0), (0,0,3), (0,2,0),(1,0,3),(1,2,3)位置上z的值,即[0, 3, 8, 15, 23]。

源数组为多维数组,目标数组为多维数组

先从简单的入手,考虑从二维数组得到二维数组。话不多说,直接上代码:

a = np.arange(12).reshape((3,4))
row = np.array([0,1,2])
col = np.array([2,1,3])

a[row[:,np.newaxis], col]

首先,y = [[0,1,2,3], [4,5,6,7], [8,9,10,11]]。

其次,在花哨索引中进行了广播。
row[:, np.newaxis]的形状为(3,1), col的维度为(3,)。
根据广播规则,先将col形状变成(1,3),然后(3,1)和(1,3)分别在维度值为1的方向上进行扩展,都变成(3, 3), (3,3)。
广播后,两个数组分别为[[0,0,0],[1,1,1],[2,2,2]] , [[2,1,3],[2,1,3],[2,1,3]]。
因此得到的目标数组形状为(3, 3)。
目标数组的值则分别为,从扩展后的两个数组中各取对应的值作为坐标,该坐标在源数组中的值。例如目标数组在(1,2)上的值为, row扩展后(1,2)为1作为第一坐标, col扩展后(1,2)的值为3作为第二坐标, 目标则为a[1][3] = 6

最后,得出的目标数组为[ [x[0,2], x[0,1], x[0,3]], [x[1,2], x[1,1], x[1,3]], [x[2,2], x[2,1], x[2,3]],即为[[2,1,3], [6,5,7], [10,9,11]]。

到此处,从二维数组花哨索引成二维数组已经ok。

接下来我考虑了很久的问题来了:如何从三维数组生成二维数组??

经过多次尝试,找到了两条思路:

  1. 将最后一维看成一个整体;
  2. 适当的传递需要的三个参数;

第一种思路,还是直接上代码

b = np.arange(24).reshape((2,3,4))
row = np.array([0,1])
col = np.array([0,2])

print("b is :")
print(b)
print("target is :")
print([b[row,col])

输出为[b[0,0], b[0,2]],如下图:
在这里插入图片描述
这种方式生成的二维数组,最后一个维度的大小必然等于源数组的大小。

那么如何可以生成不一样的二维数组呢?

我想到的是第二种解法:传递合适的三个参数(第一轴、第二轴、第三轴)。
例如,我们想要生成一个(2,3)的目标矩阵,那么可以考虑利用(2,1), (1,3),(1,3)进行广播。

b = np.arange(24).reshape((2,3,4))
row = np.array([0,1])
col = np.array([0,0,2])
thd = np.array([0,1,3])

print("b is :")
print(b)
print("target is :")
print([b[row[:,np.newaxis], col, thd])

直接上结果:array([[ 0, 1, 11], [12, 13, 23]])。
这是因为进行广播后是[[0,0,0], [1,1,1]] [[0,0,2],[0,0,2]] [[0,1,3],[0,1,3]],那么六个元素坐标分别是[ [(0,0,0), (0, 0, 1), (0,2,3)], [(1, 0, 0), (1,0, 1), (1, 2,3)]。因此结果为[[ 0, 1, 11], [12, 13, 23]]。

总结

  1. 花哨索引得到的结果,形状与索引数组的形状一致
  2. 传入的参数顺序,对应轴顺序
  3. 要生成目标形状的矩阵,则需要传递合适的参数,以进行广播
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值