切片NumPy二维数组,或者如何从一个nxn数组(n> m)中提取一个mxm子matrix?
我想分割一个NumPy nxn数组。 我想提取该数组的m行和列的任意select(即在行数/列数中没有任何模式),使其成为一个新的mxm数组。 对于这个例子,让我们说数组是4×4,我想从中提取一个2×2数组。
这是我们的arrays:
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]]
要删除的行和列是相同的。 最简单的情况是当我想提取一个在开始或结束的2×2子matrix,即:
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]])
但是如果我需要删除另一个行/列的混合呢? 如果我需要删除第一行和第三行,从而提取子matrix[[5,7],[13,15]]
? 可以有任何行/行的组合。 我读了一些地方,我只需要索引我的数组使用数组/行列索引列表,但这似乎并没有工作:
In [35]: x[[1,3],[1,3]] Out[35]: array([ 5, 15])
我find了一个办法,那就是:
In [61]: x[[1,3]][:,[1,3]] Out[61]: array([[ 5, 7], [13, 15]])
第一个问题是,这是不可读的,虽然我可以忍受。 如果有人有更好的解决scheme,我一定会喜欢听的。
其他的事情是我读了一个论坛索引arrays数组强制NumPy复制所需的数组,因此,当处理大型数组这可能成为一个问题。 为什么这个/这个机制是如何工作的?
要回答这个问题,我们必须看看如何索引一个multidimensional array在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大小的因子) :
x.strides (16, 4)
当y
像上面那样被提取的时候,NumPy不会创build一个新的缓冲区,但是它创build了一个引用相同缓冲区的新的数组对象(否则y
就等于x
)。新的数组对象将有不同的形状,然后x
和也许是一个不同的起始偏移到缓冲区,但将与x
(在这种情况下,至less)共享步伐:
y.shape (2,2) y.strides (16, 4)
这样,计算y[i,j]
的存储器偏移量将产生正确的结果。
但是NumPy应该为z=x[[1,3]]
做些什么呢? 如果原始缓冲区用于z
则strides机制将不允许正确的索引。 NumPy在理论上可以添加一些比步幅更复杂的机制,但是这会使得元素访问相对昂贵,从某种程度上违背了数组的整个想法。 另外,视图不再是一个真正轻量级的对象。
这在编制索引的NumPy文档中有深入的介绍 。
哦,几乎忘了你的实际问题:下面是如何使索引与多个列表按预期工作:
x[[[1],[3]],[1,3]]
这是因为索引数组被广播到一个普通的形状。 当然,对于这个特定的例子,你也可以做基本的切片:
x[1::2, 1::2]
正如Sven提到的, x[[[0],[2]],[1,3]]
将返回与1和3列匹配的0和2行,而x[[0,2],[1,3]]
将返回数组中的值x [0,1]和x [2,3]。
对于我给出的第一个例子, numpy.ix_
有一个有用的function。 你可以用x[numpy.ix_([0,2],[1,3])]
来做同样的事情。 这可以使您不必input所有这些额外的括号。
我不认为x [[1,3]] [:,[1,3]]几乎不可读。 如果你想更清楚你的意图,你可以这样做:
a[[1,3],:][:,[1,3]]
我不是切片专家,但通常情况下,如果您尝试切片到一个数组中,并且这些值是连续的,则会返回一个视图,其中的步幅值会更改。
例如在input33和34中,尽pipe你得到了一个2×2的数组,但是步幅是4.因此,当你索引下一行时,指针移动到内存中的正确位置。
显然,这种机制并不适用于一系列指数的情况。 因此,numpy将不得不复制。 毕竟,许多其他的matrixmath函数依赖于大小,步幅和连续的内存分配。
如果你想跳过每一行和其他列,那么你可以用基本的切片来完成:
In [49]: x=np.arange(16).reshape((4,4)) In [50]: x[1:4:2,1:4:2] Out[50]: array([[ 5, 7], [13, 15]])
这将返回一个视图,而不是数组的副本。
In [51]: y=x[1:4:2,1:4:2] In [52]: y[0,0]=100 In [53]: x # <---- Notice x[1,1] has changed Out[53]: array([[ 0, 1, 2, 3], [ 4, 100, 6, 7], [ 8, 9, 10, 11], [ 12, 13, 14, 15]])
而z=x[(1,3),:][:,(1,3)]
使用高级索引,从而返回一个副本:
In [58]: x=np.arange(16).reshape((4,4)) In [59]: z=x[(1,3),:][:,(1,3)] In [60]: z Out[60]: array([[ 5, 7], [13, 15]]) In [61]: z[0,0]=0
请注意x
不变:
In [62]: x Out[62]: array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]])
如果你想select任意的行和列,那么你不能使用基本的切片。 您必须使用高级索引,使用像x[rows,:][:,columns]
,其中rows
和columns
是序列。 这当然会给你一个原始数组的副本,而不是视图。 这是人们应该期待的,因为numpy数组使用连续内存(具有常量步长),并且将无法生成具有任意行和列的视图(因为这将需要非常量步长)。
有了numpy,你可以为索引的每个组件传递一个切片 – 所以,上面的x[0:2,0:2]
示例就起作用了。
如果您只想均匀地跳过列或行,则可以使用三个组件(即开始,停止,步骤)传递片。
再次,对于上面的例子:
>>> x[1:4:2, 1:4:2] array([[ 5, 7], [13, 15]])
基本上就是:第一维切片,从索引1开始,当索引大于等于4时停止,在每一个索引中加上2。 第二维也是一样。 再说一遍:这只适用于不断的步骤。
你在内部做了一些完全不同的语法 – x[[1,3]][:,[1,3]]
实际上做的是创build一个新的数组,包括原始数组中的第1行和第3行x[[1,3]]
部分),然后重新切片 – 创build第三个数组 – 仅包括前一个数组的第1列和第3列。