为什么高级索引要创建切片矩阵的副本?

2024-04-26 14:39:00 发布

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

如果我们只将矩阵a的一部分赋给另一个变量(view1),这个变量将只显示矩阵的相关组件的视图。但是,如果使用高级索引将矩阵a的一部分分配给另一个变量(view2),则将创建矩阵的相关组件的副本。为什么高级索引(当我们有一个切片参数列表而不是一个元组序列对象)会这样做?我看了文件,但还是不明白。你知道吗

Advanced indexing

A = np.arange(15)
A = A.reshape((3,5))
print(A, "\n")
view1 = A[:, [3]]
print("'view1' before the matrix A change =", view1)
view2 = A[:, 3]
print("'view2' before the matrix A change =", view2, "\n")
# Change in the 4th column od the matrix A
A[:, 3] = 5
print(A, "\n")
print("'view1' after the matrix A change =", view1)
print("'view2' after the matrix A change =", view2)

Tags: the视图列表参数副本组件切片矩阵
1条回答
网友
1楼 · 发布于 2024-04-26 14:39:00

首先,我将详细解释您的代码的作用。它创建一个二维3x5矩阵A,然后从中创建一个二维3x1列矩阵view1和一个一维向量view2view1view2中的信息与矩阵A的第四列相同。然后,您的代码会更改矩阵Aview2也会收到这些更改,但view1不会更改。结论是view2实际上是A的一个视图,它不是单独的数据,而只是查看矩阵A的一部分。然而,view1A中信息的副本。创建view1时,它是单独的数据,刚开始时与A一致,但可能会出现分歧。你知道吗

向量view2A的视图,因为它的定义A[:, 3]是第四列A的基本切片。列矩阵view1是一个副本,因为它的定义A[:, [3]]是“高级索引”或“花式索引”。3周围的括号阻止它成为基本片。定义中的一个细微变化说明了原因:view3 = A[:, [3,0,1]]创建一个3x3矩阵,该矩阵按顺序包含A的第四列、第一列和第二列。你知道吗

你的问题很好:切片和高级索引的结果看起来很相似,那么为什么第一个是视图,第二个是副本?你知道吗

简单的回答是:为了方便和速度,就像计算机科学中的许多其他决策一样。你知道吗

下面的图形显示了如何在内存中设置numpy数组,与Python的标准列表形成对比。(此图来自Python数据科学手册,第2章,第1节。)

enter image description here

我们看到numpy的数组不是很灵活。数组的维数有一个位置,3x5表示A,3x1表示view1,3表示view2,3x3表示view3。之后是跨步,基本上是从一个细胞到下一个细胞在特定维度上的偏移量。对于数组来说就是这样,而不是项值本身。这样可以保持较低的内存使用率,但可以防止Python列表中的列表可以做一些奇特的事情。(我在这里简化了一些细节。)

在您的示例中,A的数据是

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14

第一个维度是行,要从一个单元格转到下一行的相应单元格,numpy必须跳转5个值。例如,第一行第四列的值是3。移动到下一行,我们再移动5个位置来查看值8,而要获得下一行,我们再移动5个位置来获得值13。第二个维度是列,它的跨距是1:只更改列的3后面的值是4。所以矩阵A的跨步是51。你知道吗

view2的切片很简单,因此数据与矩阵A的数据相同。不需要复制数据。那么view2A有什么不同呢?不是像A的数据指针那样指向值0view2的数据指针指向相同数据中的3。它的维度块表示只有一个维度有3个值。它只有一个步幅,但它的步幅是5。换句话说,view2中的第一个值是3,为了找到下一个值,numpy跳过5(步长)位置来找到8,然后再次找到13。数组view2使用与A相同的数据,但维度和步长不同,因此看起来不同。换句话说,它是A的视图。你知道吗

view3的高级索引是不同的。在同一行中,值从A的第四列跳到第一列,再跳到第二列。没有任何步骤可以告诉numpy以这种方式访问这些值。因此,numpy必须从A复制一些数据,以创建一个新的、简单的数组来处理view3. 复制是因为无法查看。澄清一下,这是view3

[[ 3  0  1]
 [ 8  5  6]
 [13 10 11]]

现在回答你的问题:view1怎么样?对于那个特殊的情况,numpy有可能将其视为一种观点。然而,view1是通过高级索引创建的,我们已经看到,通常不可能为这些创建视图。因此,numpy采用了方便、快速和一致的方法,并声明所有高级索引的场合都会创建副本,即使在视图可能的情况下也是如此。这很方便,因为numpy不需要尝试找出一个视图是否可行。它很快,记住numpy的一个主要优点是它很快,所以在可能的情况下省去创建视图的尝试会加快速度。而且它是一致的,因为所有高级索引的使用都创建副本,而不是一些创建视图和一些创建副本。你知道吗

相关问题 更多 >