NumPy二维数组的切片,或者如何从nxn数组(n>m)中提取mxm子矩阵?

2024-03-28 16:48:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我想切一个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复制所需的数组,因此当处理大型数组时,这可能会成为一个问题。为什么这样/这个机制是如何工作的?


Tags: infromimportnumpy数量模式矩阵数组
3条回答

为了回答这个问题,我们必须看看如何在Numpy中索引多维数组。首先假设你的问题中有数组x。分配给x的缓冲区将包含16个从0到15的升序整数。如果您访问一个元素,比如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不会创建一个新的缓冲区,但它会创建一个引用同一缓冲区的新数组对象(否则y将只等于x)。新数组对象将具有不同的形状,然后x,可能会有不同的缓冲区起始偏移量,但将与x共享这些进展(在本例中至少是这样):

y.shape
(2,2)
y.strides
(16, 4)

这样,计算y[i,j]的内存偏移量将得到正确的结果。

但是对于像z=x[[1,3]]这样的事情,NumPy应该怎么做呢?如果原始缓冲区用于z,那么spinds机制将不允许正确的索引。从理论上讲,NumPy可以添加一些比streams更复杂的机制,但这会使元素访问相对昂贵,在某种程度上违背了数组的整体思想。此外,视图不再是真正的轻量级对象。

这在the NumPy documentation on indexing中有详细介绍。

哦,差点忘了你的实际问题:下面是如何使多个列表的索引按预期工作:

x[[[1],[3]],[1,3]]

这是因为索引数组是一个公共形状的broadcasted。 当然,对于这个特定的示例,您还可以使用基本切片:

x[1::2, 1::2]

我不认为x[[1,3]][:,[1,3]]很难阅读。如果你想更清楚自己的意图,你可以:

a[[1,3],:][:,[1,3]]

我不是切片专家,但通常情况下,如果您尝试切片到数组中,并且值是连续的,则会返回一个改变步长值的视图。

例如,在你的输入33和34中,虽然你得到一个2x2数组,但步幅是4。因此,当索引下一行时,指针将移动到内存中的正确位置。

显然,这种机制不能很好地应用于索引数组的情况。因此,numpy必须复制。毕竟,许多其他的矩阵数学函数依赖于大小、步幅和连续内存分配。

如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])]做和我的第一个例子一样的事情。这样可以省去您输入所有这些额外的括号。

相关问题 更多 >