如何访问NumPy多维数组的第i列?


461

假设我有:

test = numpy.array([[1, 2], [3, 4], [5, 6]])

test[i]使我得到数组的第i行(例如[1, 2])。如何访问第ith列?(例如[1, 3, 5])。另外,这将是一项昂贵的操作吗?

Answers:


684
>>> test[:,0]
array([1, 3, 5])

同样,

>>> test[1,:]
array([3, 4])

使您可以访问行。NumPy参考资料的第1.4节(索引)对此进行了介绍。这很快,至少以我的经验而言。它肯定比循环访问每个元素要快得多。


11
这样创建一个副本,是否有可能获得引用,就像我获得对列的引用一样,对此引用的任何更改都将反映在原始数组中。
harmand '16

@harmands这不会创建副本,而是创建视图。
rinspy

69

如果您想一次访问多个列,则可以执行以下操作:

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

当然,在这种情况下,您不只是访问数据;您要返回副本(花式索引)
John Greenall 2014年

14
test[:,[0,2]]只是访问数据,例如,test[:, [0,2]] = something将修改测试,而不创建另一个数组。但是copy_test = test[:, [0,2]]实际上确实按照您所说的创建了副本。
Akavall 2014年

3
这样创建一个副本,是否有可能获得引用,就像我获得对某些列的引用一样,此引用的任何更改都反映在原始数组中?
harmand '16

@ harman786,您可以将修改后的数组重新分配给旧数组。
Tamoghna Chowdhury

为什么test[:,[0,2]]只访问数据而test[:, [0, 2]][:, [0, 1]]没有访问数据?再次做同样的事情会有不同的结果,这似乎非常不直观。
mapf

65
>>> test[:,0]
array([1, 3, 5])

该命令为您提供了行向量,如果您只想在其上循环,就可以了,但是如果您要与其他尺寸为3xN的数组进行堆叠,则可以

ValueError: all the input arrays must have same number of dimensions

>>> test[:,[0]]
array([[1],
       [3],
       [5]])

为您提供列向量,以便您可以进行串联或hstack操作。

例如

>>> np.hstack((test, test[:,[0]]))
array([[1, 2, 1],
       [3, 4, 3],
       [5, 6, 5]])

1
索引一次也可以处理多于一列的数据,因此最后一个示例可以是test [:,[0,1,0]]或test [:,[range(test.shape [1])+ [0]] ]
lib

5
+1用于指定[:,[0]] vs [:,0]以获得列向量而不是行向量。正是我想要的行为。还要+1以lib以获得其他索引注释。这个答案应该和最上面的答案一起出现。
dhj 2015年

1
必须选择这个答案
Gusev Slava

22

您还可以转置并返回一行:

In [4]: test.T[0]
Out[4]: array([1, 3, 5])

我一直在这样做了一段时间寻找最快的方式访问列之前,我不知道这是快,慢,或只是一样的测试[:[0]
何塞·查莫罗


5

尽管问题已得到回答,但让我提及一些细微差别。

假设您对数组的第一列感兴趣

arr = numpy.array([[1, 2],
                   [3, 4],
                   [5, 6]])

从其他答案中已经知道,要以“行向量”(shape的数组(3,))的形式获取它,可以使用切片:

arr_c1_ref = arr[:, 1]  # creates a reference to the 1st column of the arr
arr_c1_copy = arr[:, 1].copy()  # creates a copy of the 1st column of the arr

要检查一个数组是视图还是另一个数组的副本,可以执行以下操作:

arr_c1_ref.base is arr  # True
arr_c1_copy.base is arr  # False

参见ndarray.base

除了两者之间的明显区别(修改arr_c1_ref将影响arr),遍历它们中每一个的字节步数也不同:

arr_c1_ref.strides[0]  # 8 bytes
arr_c1_copy.strides[0]  # 4 bytes

大步。为什么这很重要?假设您有一个很大的数组,A而不是arr

A = np.random.randint(2, size=(10000,10000), dtype='int32')
A_c1_ref = A[:, 1] 
A_c1_copy = A[:, 1].copy()

并且您要计算第一列的所有元素的总和,即A_c1_ref.sum()A_c1_copy.sum()。使用复制的版本要快得多:

%timeit A_c1_ref.sum()  # ~248 µs
%timeit A_c1_copy.sum()  # ~12.8 µs

这是由于前面提到的跨步数不同:

A_c1_ref.strides[0]  # 40000 bytes
A_c1_copy.strides[0]  # 4 bytes

尽管使用列副本似乎更好,但由于创建副本需要时间并使用更多的内存(在这种情况下,我花了大约200 µs的时间来创建副本)并不总是正确的。 A_c1_copy)。但是,如果我们首先需要复制,或者需要在数组的特定列上执行许多不同的操作,并且可以牺牲内存以提高速度,那么复制就可以了。

如果我们有兴趣主要使用列,最好以列大('F')顺序而不是行大('C')顺序创建数组(这是默认设置) ),然后像以前一样进行切片以获取一列而不复制它:

A = np.asfortranarray(A)  # or np.array(A, order='F')
A_c1_ref = A[:, 1]
A_c1_ref.strides[0]  # 4 bytes
%timeit A_c1_ref.sum()  # ~12.6 µs vs ~248 µs

现在,在列视图上执行求和运算(或其他任何运算)要快得多。

最后,让我注意到,转置数组并使用行切片与在原始数组上使用列切片相同,因为转置是通过交换原始数组的形状和步幅来完成的。

A.T[1,:].strides[0]  # 40000

3
>>> test
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> ncol = test.shape[1]
>>> ncol
5L

然后,您可以通过以下方式选择第二至第四列:

>>> test[0:, 1:(ncol - 1)]
array([[1, 2, 3],
       [6, 7, 8]])
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.