numpy 数组怪癖:AT 是否返回视图?

数据挖掘 Python 麻木的
2022-03-05 22:48:13

我试图理解 numpy 数组对象,但对以下内容有点迷惑:

A = np.array([[1,2,3],[4,5,6]]) # A is a 2x3 matrix
B = A

A = A.T # A is now the matrix [[1,4],[2,5],[3,6]]
A[0,1]= -1 # A is now the matrix [[1,-1],[2,5],[3,6]]

print(B)

我得到B:

[[1,2,3],[-1,5,6]]

因此,使用赋值 A[0,1]= -1 更改 A 更改了 B (这是可以预料的,因为 numpy 数组是可变对象,而 python 赋值是通过引用进行的)。但令人困惑的是 A = AT 并没有转置 B。此外,如果我问(在A = A.T声明之后):

A is B

我得到错误。AT返回的究竟是什么?它似乎返回了数据的视图,而是将数据对齐到矩阵中的副本。

2个回答

你是对的,B指向原始数组并且A(最后)是数组的转置视图

这里的关键点是让很多人感到困惑的事情:每当您使用=不使用索引(例如,A = <anything>)为变量分配新值时,它会导致该变量指向新位置,但不会改变先前保存在该变量中的数据。

这是正在发生的事情的注释版本:

A = np.array([[1,2,3],[4,5,6]])
# create the array [[1,2,3],[4,5,6]] at some location in memory
# (let's call it loc 1)
# point variable A to loc 1

B = A
# point variable B to the same location as A (loc 1)

A = A.T
# right-hand side:
#    - get the array that A currently points to (from loc 1)
#    - create a transposed view of this array and store the view 
#      somewhere in memory (let's call it loc 2)
# left-hand side: set variable A to point to loc 2
# Note: the `=` assignment does not alter the object at loc 1; instead, 
# it points A to the new object that was created on the right-hand side

A[0,1]= -1
# Push the value -1 into position (0, 1) of the object that A
# currently points to, i.e., the view at loc 2. This updates 
# the A view, but also propagates back to the original, 
# un-transposed array at loc 1 (which B still points to).

# Note that the last two lines are equivalent to this:
X = A.T     # doesn't transpose B
X[0,1]= -1  # propagates through view back to B
# i.e., the A.T could have been assigned to anything, e.g. a new
# variable X. When you do an assignment with `=`, it creates a new
# variable every time, even if it happens to have the same name as 
# an old one, as in your case.

这是更改可变数据并获得意外副作用的一般问题。考虑它的最简单方法是裸变量赋值 ( A = <something>)永远不会改变A当前指向的基础数据。相反,它只是导致A指向新的地方。另一方面,分配A[index] = <something>A.attribute = <something>使用A.data_altering_method(...) 改变 A 所指向的底层对象,因此指向同一对象的任何其他对象也将看到更改。

在您的情况下,您所做的唯一改变基础数据的事情是A[0,1]= -1,并且确实传播到B. A = A.T没有改变基础数据,它只是指出A了一些新的东西(恰好是原始数据的新视图)。所以对A变量的更改没有传播到B. 但是,创建视图而不是副本有点令人惊讶 A = A.T,因此以后的更改会A影响B. 你中间的两行相当于B.T[0,1] = -1.

这些答案也有帮助:

您可以看到矩阵,只有坐标轴 transposed所以你B不会被调换。

更多细微差别取决于原始数组的形状:

In  [1]: import numpy as np

In  [2]: np.ndarray.transpose??
Out [2]:

Returns a view of the array with axes transposed.
For a 1-D array, this has no effect. (To change between column and
row vectors, first cast the 1-D array into a matrix object.)                                                      
For a 2-D array, this is the usual matrix transpose.                                                              
For an n-D array, if axes are given, their order indicates how the                                               
axes are permuted (see Examples). [truncated]

注意:a.T和之间存在细微差别a.transpose()

与 self.transpose() 相同,除了 self.ndim < 2 时返回 self。


还有其他方法,例如np.ndarray.reshape(),可能会或可能不会返回数据的副本,这也取决于数组的形状等因素。