我想切片NumPy nxn数组 . 我想提取该数组的m行和列的任意选择(即行数/列数没有任何模式),使其成为一个新的mxm数组 . 对于这个例子,让我们说数组是4x4,我想从中提取一个2x2数组 .
这是我们的数组:
from numpy import *
x = range(16)
x = reshape(x,(4,4))
print x
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]]
要删除的行和列是相同的 . 最简单的情况是当我想要提取一个位于开头或结尾的2x2子矩阵时,即:
In [33]: x[0:2,0:2]
Out[33]:
array([[0, 1],
[4, 5]])
In [34]: x[2:,2:]
Out[34]:
array([[10, 11],
[14, 15]])
但是,如果我需要删除另一行/列的混合物怎么办?如果我需要删除第一行和第三行/行,从而提取子矩阵 [[5,7],[13,15]]
怎么办?行/行可以有任何组合 . 我读到某个地方,我只需要使用行和列的索引数组/列表索引我的数组,但这似乎不起作用:
In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])
我发现了一种方法,即:
In [61]: x[[1,3]][:,[1,3]]
Out[61]:
array([[ 5, 7],
[13, 15]])
第一个问题是它几乎不可读,尽管我可以忍受它 . 如果有人有更好的解决方案,我当然希望听到它 .
另一件事是我读了on a forum,使用数组索引数组强制NumPy制作所需数组的副本,因此当处理大数组时,这可能会成为一个问题 . 为什么这样/这个机制如何运作?
7 回答
正如Sven所提到的,
x[[[0],[2]],[1,3]]
将返回与1和3列匹配的0和2行,而x[[0,2],[1,3]]
将返回数组中的值x [0,1]和x [2,3] .有一个很有用的功能来完成我给出的第一个例子,
numpy.ix_
. 您可以使用x[numpy.ix_([0,2],[1,3])]
执行与第一个示例相同的操作 . 这可以使您不必输入所有这些额外的括号 .要回答这个问题,我们必须看看如何在Numpy中对多维数组进行索引 . 我们首先要说你的问题中有数组
x
. 分配给x
的缓冲区将包含从0到15的16个升序整数 . 如果访问一个元素,比如x[i,j]
,NumPy必须计算出该元素相对于缓冲区开头的内存位置 . 这是通过实际计算i*x.shape[1]+j
(并乘以int的大小来获得实际的内存偏移量)来完成的 .如果通过基本切片(如
y = x[0:2,0:2]
)提取子数组,则生成的对象将与x
共享基础缓冲区 . 但是如果你访问y[i,j]
会发生什么? NumPy不能使用i*y.shape[1]+j
来计算数组中的偏移量,因为属于y
的数据在内存中不连续 .NumPy通过引入步幅解决了这个问题 . 在计算访问
x[i,j]
的内存偏移量时,实际计算的是i*x.strides[0]+j*x.strides[1]
(这已经包含了int大小的因子):当像上面那样提取
y
时,NumPy不会创建一个新的缓冲区,但它会创建一个引用相同缓冲区的新数组对象(否则y
将等于x
. )新的数组对象将具有不同的形状,然后x
和可能是一个不同的起始偏移量到缓冲区,但将与x
(至少在这种情况下)共享步幅:这样,计算
y[i,j]
的内存偏移量将产生正确的结果 .但NumPy应该为
z=x[[1,3]]
做些什么?如果原始缓冲区用于z
,则strides机制将不允许正确索引 . 理论上,NumPy可以添加一些比步幅更复杂的机制,但是这会使元素访问相对昂贵,从某种程度上违背了数组的整个想法 . 此外,视图不再是一个非常轻量级的对象 .这在the NumPy documentation on indexing中有详细介绍 .
哦,几乎忘记了你的实际问题:以下是如何使多个列表的索引按预期工作:
这是因为索引数组是broadcasted到一个共同的形状 . 当然,对于这个特定示例,您还可以使用基本切片:
我认为
x[[1,3]][:,[1,3]]
几乎不可读 . 如果您想更清楚自己的意图,可以这样做:我不是切片方面的专家,但通常情况下,如果您尝试切片到数组并且值是连续的,那么您将返回一个视图,其中步幅值已更改 .
例如在输入33和34中,虽然你得到一个2x2数组,但是步幅是4.因此,当你索引下一行时,指针移动到内存中的正确位置 .
显然,这种机制并不适用于一系列指数 . 因此,numpy必须制作副本 . 毕竟,许多其他矩阵数学函数依赖于大小,步幅和连续的内存分配 .
如果要跳过每隔一行和每隔一列,那么可以使用基本切片来执行此操作:
这将返回一个视图,而不是数组的副本 .
而
z=x[(1,3),:][:,(1,3)]
使用高级索引,因此返回一个副本:请注意
x
未更改:如果您希望选择任意行和列,那么您可以使用高级索引,使用类似
x[rows,:][:,columns]
的内容,其中rows
和columns
是序列 . 这当然会为您提供原始数组的副本,而不是视图 . 这是人们应该期待的,因为numpy数组使用连续的内存(具有常量步幅),并且无法生成具有任意行和列的视图(因为这将需要非常量的步幅) .使用numpy,您可以为索引的每个组件传递一个切片 - 因此,上面的
x[0:2,0:2]
示例有效 .如果您只想均匀地跳过列或行,则可以传递包含三个组件的切片(即启动,停止,步骤) .
再次,对于上面的例子:
基本上是:第一维中的切片,索引为1的开始,当索引等于或大于4时停止,并在每次传递中向索引添加2 . 第二个维度也是如此 . 再说一遍:这只适用于不断的步骤 .
你必须在内部做一些完全不同的语法 -
x[[1,3]][:,[1,3]]
实际上做的是创建一个新数组,包括原始数组中的第1行和第3行(使用x[[1,3]]
部分完成),然后重新切片 - 创建第三个数组 - 仅包括前一个数组的第1列和第3列 .我在这里有一个类似的问题:Writting in sub-ndarray of a ndarray in the most pythonian way. Python 2 .
根据您的案例的上一篇文章的解决方案,解决方案看起来像:
使用ix_:
这是:
我不确定这是多么有效但你可以使用range()在两个轴上切片