不丢失维度信息的Numpy索引切片

135
我正在使用numpy,并希望在不丢失维度信息的情况下索引一行。
import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:]
xslice.shape   # >> (10,)  
在这个例子中,xslice现在是1维的,但我希望它是(1,10)的维度。 在R中,我会使用X [10,:, drop = F]。在numpy中是否有类似的东西。我在文档中找不到它,并且没有看到类似的问题被问到。
谢谢!
7个回答

123

另一个解决方案是执行

X[[10],:]
或者
I = array([10])
X[I,:]

当使用索引列表(或数组)进行索引时,数组的维度将被保留。这很好因为它让你可以选择保留维度或者压缩。


5
这将复制数组数据。 - Per
1
这并不总是适用。例如:x = np.array([[1,2,3,4]]),如果你用 x[[0],[1,2]] 对其进行切片,你将获得一维数组 array([2, 3]) 。我认为,在选择列向量或行向量时,最好使切片简单化,然后使用 np.reshape。因此,在我的示例中,它将是 np.reshape(x[0,[1,2]],[1,2]) - Alexander
1
其他人请注意,末尾的分号非常重要。X[[10]]会被解释为X[10],并且形状会变小;同样,X[[10, 20]] == X[10, 20],形状更小。 - Ben Usman
7
警告:不要将这种索引方式与普通的整数索引混淆!如果您有形状为(10,20,30)的 a,那么 a[0, :, [0]] 的形状将为 (1, 20) 而不是 (20, 1),因为后者中的索引会被广播到 a[[0], :, [0]],这通常不是您所期望的!而 a[0, :, :1] 将按预期给出 (20, 1)。此外,请参见上面的注释以了解单个索引的奇怪边缘情况。总体而言,似乎这种方法具有过多的边缘情况。 - Ben Usman

71

最简单的方法可能是使用 x[None, 10, :] 或等价的(但更易读的)x[np.newaxis, 10, :]Nonenp.newaxis 将数组的维度增加1,这样在切片消除一个维度后,就回到原来的状态了。

至于为什么它不是默认值,个人认为,经常拥有单例维度的数组会很快变得烦人。我猜测 numpy 开发人员也有同感。

此外,numpy 能够很好地处理广播数组,因此通常没有保留切片所来自的数组的维度的必要。如果这样做了,那么像下面这样的东西:

a = np.zeros((100,100,10))
b = np.zeros(100,10)
a[0,:,:] = b

两者都不会起作用,或者实现起来会更加困难。(至少这是我猜测numpy开发人员在切片时放弃维度信息的原因)


7
@Lisa: x[None, 10] 可以满足你的需求。 - naught101
好的。在您切割的维度旁边放上 None - Mad Physicist
1
这个示例在对b进行赋值时,元组缺少额外的括号; 应该是b = np.zeros((100,10)) - Jerzy
为什么要使用三个索引而不是两个?我的意思是 X[10,None](以您的代码为例)。 - greenoldman
18
“通常情况下,保留数组的维度往往没有多大意义。”……但是这样做肯定会完全搞砸矩阵乘法(np.matmul()@)。我就因为这个问题而受到了打击。 - Jean-François Corbett
显示剩余4条评论

32

我找到了几个合理的解决方案。

1)使用numpy.take(X,[10],0)

2)使用这种奇怪的索引方式X[10:11:, :]

理想情况下,这应该是默认设置。我从来没有理解为什么会有维度被丢弃。但这是一个关于Numpy的讨论...


4
在 Python 列表中,当进行索引操作时,“维度”会被删除,而在切片操作时则会保留。例如,alist[0] 只返回一个元素,而 alist[0:1] 返回一个列表。 - hpaulj
7
应该接受 Option 2 作为答案,它可以写成 slice(n, n+1) 来提取索引 n,因为它是唯一自然地扩展到 n 维情况的方法。 - norok2
1
在Python 3.7.5中,选项2似乎可以写成X[10:11, :](即11后面没有额外的冒号)。 - Joe

18

我更喜欢的另一种方式是使用范围来索引,而不是使用单个数字进行索引。也就是说,使用X[10:11,:]。(注意10:11不包括11)。

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10:11,:]
xslice.shape   # >> (1,10)

这也使得更高维度的理解更加容易,没有None的翻转和弄清楚使用哪个轴来使用哪个索引。此外,无需进行有关数组大小的额外簿记,只需对于常规索引中使用的任何i,使用i:i+1即可。

b = np.ones((2, 3, 4))
b.shape # >> (2, 3, 4)
b[1:2,:,:].shape  # >> (1, 3, 4)
b[:, 2:3, :].shape .  # >> (2, 1, 4)

这太棒了。我刚刚发现了自己保持尺寸的方法,本来想建议一下,但看到你已经发布了。我认为这应该是最好的回复,而不是那些并不真正有效的回复。 - Daniel Morris

5

除了gnebehay提出的通过列表或数组进行索引的解决方案(链接),还可以使用元组:

X[(10,),:]

1

如果您正在按可能在运行时长度为1的数组进行索引,则特别令人恼火。对于这种情况,有np.ix_

some_array[np.ix_(row_index,column_index)]

0

我一直在使用np.reshape来实现如下所示的相同效果

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:].reshape(1, -1)
xslice.shape   # >> (1, 10)  



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接