NumPy通过使用索引列表为每行选择特定的列索引


90

我正在努力选择NumPy矩阵每行的特定列。

假设我有以下矩阵,我将其称为X

[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

list每行还有一个列索引,我将其称为Y

[1, 0, 2]

我需要获取值:

[2]
[4]
[9]

除了使用listwith索引之外Y,我还可以生成形状与X每个列都是0-1值范围内的bool/的形状相同的矩阵int,指示这是否是必需的列。

[0, 1, 0]
[1, 0, 0]
[0, 0, 1]

我知道这可以通过遍历数组并选择所需的列值来完成。但是,这将在大数据数组上频繁执行,这就是为什么它必须尽可能快地运行。

因此,我想知道是否有更好的解决方案?

谢谢。


答案对您来说更好吗?stackoverflow.com/a/17081678/5046896
GoingMyWay

Answers:


102

如果您有一个布尔数组,则可以像这样直接选择:

>>> a = np.array([True, True, True, False, False])
>>> b = np.array([1,2,3,4,5])
>>> b[a]
array([1, 2, 3])

与最初的示例一起使用,您可以执行以下操作:

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> b = np.array([[False,True,False],[True,False,False],[False,False,True]])
>>> a[b]
array([2, 4, 9])

您还可以添加一个arange并对其进行直接选择,尽管这取决于生成布尔数组的方式以及代码看起来像YMMV的形式。

>>> a = np.array([[1,2,3], [4,5,6], [7,8,9]])
>>> a[np.arange(len(a)), [1,0,2]]
array([2, 4, 9])

希望能有所帮助,如果您还有其他问题,请告诉我。


11
使用的示例为+1 arange。这对我从多个矩阵中检索不同的块特别有用(因此基本上是本例的3D情况)
Griddo

1
嗨,您能解释一下为什么我们必须使用arange代替:吗?我知道您的方式有效,而我的无效,但我想了解原因。
marcotama'6

@tamzord因为它是一个numpy数组,而不是普通的python列表,所以:语法不能以相同的方式工作。
Slater Victoroff '16

1
@SlaterTyranus,感谢您的回复。经过一番阅读,我的理解是:与高级索引混合意味着:“对于沿着每个子空间:,应用给定的高级索引”。我的理解正确吗?
marcotama

@tamzord解释“子空间”的
含义

35

您可以执行以下操作:

In [7]: a = np.array([[1, 2, 3],
   ...: [4, 5, 6],
   ...: [7, 8, 9]])

In [8]: lst = [1, 0, 2]

In [9]: a[np.arange(len(a)), lst]
Out[9]: array([2, 4, 9])

有关为多维数组建立索引的更多信息:http : //docs.scipy.org/doc/numpy/user/basics.indexing.html#indexing-多维数组


1
努力理解为什么需要范围而不是简单的':'或范围。
MadmanLee19年

@MadmanLee Hi,使用:将输出多次len(a)结果,而是指示每行的索引将打印预期结果。
GoingMyWay

1
我认为这是解决此问题的正确而优雅的方法。
GoingMyWay

6

一种简单的方法可能类似于:

In [1]: a = np.array([[1, 2, 3],
   ...: [4, 5, 6],
   ...: [7, 8, 9]])

In [2]: y = [1, 0, 2]  #list of indices we want to select from matrix 'a'

range(a.shape[0]) 将返回 array([0, 1, 2])

In [3]: a[range(a.shape[0]), y] #we're selecting y indices from every row
Out[3]: array([2, 4, 9])

1
请考虑添加说明。
souki

@souki我现在添加了解释。谢谢
Dhaval Mayatra '18

6

最新numpy版本添加了take_along_axis(和put_along_axis),可以清楚地进行此索引编制。

In [101]: a = np.arange(1,10).reshape(3,3)                                                             
In [102]: b = np.array([1,0,2])                                                                        
In [103]: np.take_along_axis(a, b[:,None], axis=1)                                                     
Out[103]: 
array([[2],
       [4],
       [9]])

它的运行方式与:

In [104]: a[np.arange(3), b]                                                                           
Out[104]: array([2, 4, 9])

但具有不同的轴处理方式。这是特别针对应用的结果argsortargmax


3

您可以使用迭代器来实现。像这样:

np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)

时间:

N = 1000
X = np.zeros(shape=(N, N))
Y = np.arange(N)

#@Aशwini चhaudhary
%timeit X[np.arange(len(X)), Y]
10000 loops, best of 3: 30.7 us per loop

#mine
%timeit np.fromiter((row[index] for row, index in zip(X, Y)), dtype=int)
1000 loops, best of 3: 1.15 ms per loop

#mine
%timeit np.diag(X.T[Y])
10 loops, best of 3: 20.8 ms per loop

1
OP提到它应该在大型阵列上快速运行,因此您的基准测试不是很有代表性。我很好奇您的最后一个方法对(很多)更大的数组如何执行!

@moarningsun:已更新。np.diag(X.T[Y])太慢了...但是np.diag(X.T)太快了(10us)。我不知道为什么
稻川敬(Kei Minagawa)2014年

0

另一个聪明的方法是先转置数组,然后再对其进行索引。最后,选择对角线,它总是正确的答案。

X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
Y = np.array([1, 0, 2, 2])

np.diag(X.T[Y])

一步步:

原始数组:

>>> X
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])

>>> Y
array([1, 0, 2, 2])

转置以使其能够正确索引。

>>> X.T
array([[ 1,  4,  7, 10],
       [ 2,  5,  8, 11],
       [ 3,  6,  9, 12]])

按Y顺序获取行。

>>> X.T[Y]
array([[ 2,  5,  8, 11],
       [ 1,  4,  7, 10],
       [ 3,  6,  9, 12],
       [ 3,  6,  9, 12]])

对角线现在应该变得清晰了。

>>> np.diag(X.T[Y])
array([ 2,  4,  9, 12]

1
从技术上讲,它看起来非常优雅。但是,我发现当您处理大型数组时,这种方法会完全爆炸。就我而言,NumPy吞没了30GB的交换空间并填满了我的SSD。我建议改为使用高级索引方法。
5nefarious
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.