可能值得看看转置的作用,以便我们清楚您所说的“物理转置”是什么意思。
从一个小的 (4,3) 数组开始:
In [51]: arr = np.array([[1,2,3],[10,11,12],[22,23,24],[30,32,34]])
In [52]: arr
Out[52]:
array([[ 1, 2, 3],
[10, 11, 12],
[22, 23, 24],
[30, 32, 34]])
这是用一维数据缓冲区存储的,我们可以用ravel显示:
In [53]: arr.ravel()
Out[53]: array([ 1, 2, 3, 10, 11, 12, 22, 23, 24, 30, 32, 34])
和strides 告诉它将列步进 8 个字节,将行步进 24 (3*8):
In [54]: arr.strides
Out[54]: (24, 8)
我们可以用“F”的顺序来解惑——这是逐行向下的:
In [55]: arr.ravel(order='F')
Out[55]: array([ 1, 10, 22, 30, 2, 11, 23, 32, 3, 12, 24, 34])
虽然 [53] 是 view,但 [55] 是副本。
现在转置:
In [57]: arrt=arr.T
In [58]: arrt
Out[58]:
array([[ 1, 10, 22, 30],
[ 2, 11, 23, 32],
[ 3, 12, 24, 34]])
这是view;我们可以遍历 [53] 数据缓冲区,以 8 字节步长向下行。使用arrt 进行计算基本上与使用arr 一样快。使用strided 迭代,“F”顺序与“C”顺序一样快。
In [59]: arrt.strides
Out[59]: (8, 24)
原来的顺序:
In [60]: arrt.ravel(order='F')
Out[60]: array([ 1, 2, 3, 10, 11, 12, 22, 23, 24, 30, 32, 34])
但是做一个'C' ravel 会创建一个副本,与 [55] 相同
In [61]: arrt.ravel(order='C')
Out[61]: array([ 1, 10, 22, 30, 2, 11, 23, 32, 3, 12, 24, 34])
复制转置会生成一个以“C”顺序转置的数组。这是你的“物理转置”:
In [62]: arrc = arrt.copy()
In [63]: arrc.strides
Out[63]: (32, 8)
像 [61] 那样重塑转置确实会生成副本,但通常我们不需要显式地生成副本。我认为这样做的唯一原因是避免在以后的计算中出现多个冗余副本。