我将这个问题放在相当大的范围内,希望可以使其更容易理解,但请随时跳过实际的问题。
语境
这是我正在做的工作,引发了这个问题:
我正在使用API来访问一些表格数据,该表格数据实际上是带标签的N维数组。数据作为(实际数据值的)列表的扁平列表以及不同轴及其标签的列表返回,例如:
raw_data = [
['nrm', 'nrf'],
['ngm', 'ngf'],
['nbm', 'nbf'],
['srm', 'srf'],
['sgm', 'sgf'],
['sbm', 'sbf'],
['erm', 'erf'],
['egm', 'egf'],
['ebm', 'ebf'],
['wrm', 'wrf'],
['wgm', 'wgf'],
['wbm', 'wbf'],
]
axes = [
('Gender', ['Male', 'Female']),
('Color', ['Red', 'Green', 'Blue']),
('Location', ['North', 'South', 'East', 'West']),
]
数据通常是数字的,但是我在这里使用了字符串,因此您可以轻松地看到它如何与标签匹配,例如
nrm
是North, Red, Male
的值。当您穿过列表时(在列表中),数据在轴0上循环,然后在列表中向下时,在轴1和2上循环,其中轴1(在“内侧”)变化最快,然后是2(对于高维数据继续“向外”工作,即:
axis 0 ->
a a [ # # # # # # ]
x x [ # # # # # # ]
i i [ # # # # # # ]
s s [ # R A W # ]
[ # D A T A # ]
2 1 [ # # # # # # ]
↓ ↓ [ # # # # # # ]
[ # # # # # # ]
我想重塑此数据并使其与标签匹配,我使用以下内容将其输出到Pandas(多索引)DataFrame中:
import numpy as np
import pandas as pd
names = [name for (name, _) in axes]
labels = [labels for (_, labels) in axes]
sizes = tuple(len(L) for L in labels) # (2, 3, 4)
data_as_array = np.array(raw_data) # shape = (12, 2) = (3*4, 2)
A = len(sizes) # number of axes
new_shape = (*sizes[1:],sizes[0]) # (3, 4, 2)
data = data_as_array.reshape(new_shape, order="F").transpose(A - 1, *range(A - 1))
# With my numbers: data_as_array.reshape((3, 4, 2), order="F").transpose(2, 0, 1)
df = pd.DataFrame(
data.ravel(),
index=pd.MultiIndex.from_product(labels, names=names),
columns=["Value"],
)
(我已经在注释中指出了示例中的某些特定值,但是该代码旨在针对任何N维数据进行通用化。)
这给出:
Value
Gender Color Location
Male Red North nrm
South srm
East erm
West wrm
Green North ngm
South sgm
East egm
West wgm
Blue North nbm
South sbm
East ebm
West wbm
Female Red North nrf
South srf
East erf
West wrf
Green North ngf
South sgf
East egf
West wgf
Blue North nbf
South sbf
East ebf
West wbf
所有这些都是期望和期望的,您可以看到这些值最终都放置在正确的位置,即贴在其匹配的标签上。
题
我的实际问题与这一行有关:
data = data_as_array.reshape(new_shape, order="F").transpose(A - 1, *range(A - 1))
在我的示例中,具体数字为:
data = data_as_array.reshape((3, 4, 2), order="F").transpose(2, 0, 1)
经过一番试验,我发现以下三个条件都是相同的(第一个是原始版本):
data1 = data_as_array.reshape(new_shape, order="F").transpose(D - 1, *range(D - 1))
data2 = data_as_array.T.reshape(*reversed(new_shape)).T.transpose(D - 1, *range(D - 1))
data3 = data_as_array.reshape(*reversed(sizes)).T
但这让我开始思考(这是我的最后一个问题!):
我可以使用任何规则来操纵表达式吗?
data1
到data3
?特别是,
transpose()
和reshape()
似乎紧密相连,并且可能存在一种方法来吸收移调到reshape()
中的作用,以便您可以将其删除或至少将其转换为整洁的.T
(按照data3
)。我的尝试
我设法建立了以下规则:
a.reshape(shape, order="F") == a.T.reshape(*reversed(shape)).T
您可以在两面都应用
.T
,或者用a.T
代替a
以获得这些变化:a.reshape(shape) == a.T.reshape(*reversed(shape), order="F").T
a.reshape(shape).T == a.T.reshape(*reversed(shape), order="F")
a.T.reshape(shape) == a.reshape(*reversed(shape), order="F").T
a.reshape(shape, order="F") == a.T.reshape(*reversed(shape)).T
a.reshape(shape, order="F").T == a.T.reshape(*reversed(shape))
a.T.reshape(shape, order="F") == a.reshape(*reversed(shape)).T
我认为这实际上是行主排序和列主排序之间的区别以及它们之间的关系的定义。
但是我没有设法做的就是显示如何去做:
data = data_as_array.reshape((3, 4, 2), order="F").transpose(2, 0, 1)
至:
data = data_as_array.reshape((4, 3, 2))
因此,以某种方式使换位进入重塑。
但是我什至不确定这通常是正确的还是特定于我的数据或例如3个维度。
编辑:
需要澄清的是,我对
.T
直移转置的工作方式感到满意,上面的规则对此进行了介绍。 (请注意,对于3轴,.T
等效于.tranpose(2, 1, 0)
,对于.tranpose(n-1, n-2, ... 2, 1, 0)
轴,通常是n
。)在使用
.transpose()
的情况下,您正在执行我很好奇的“部分”移调,例如.tranpose(1, 0, 2)
-除了反转轴的顺序外,您还需要执行其他操作。一些参考:
这涵盖了行主要和列主要的区别:How do you unroll a Numpy array of (mxn) dimentions into a single vector(而且我可以很容易地看到数据中的情况)
这样的SO答案对解释移调确实很有帮助,并且基本上还涵盖了重塑:https://stackoverflow.com/a/32034565/9219425(查看奇妙的图表!),包括涵盖移调如何影响形状和步幅。我编写了一个模仿此过程的算法,以查看是否可以使事情变得更清楚(例如,转置可能与交换算法中
for
循环的顺序相对应),但这并没有真正的帮助。 最佳答案
我暂时不会尝试处理您的所有情况,但以下是重塑,转置和顺序交互方式的说明:
In [176]: x = np.arange(12)
In [177]: x.strides, x.shape
Out[177]: ((8,), (12,))
In [178]: y = x.reshape(3,4)
In [179]: y.strides, y.shape
Out[179]: ((32, 8), (3, 4)) # (32=4*8)
In [180]: z = y.T
In [181]: z.strides, z.shape
Out[181]: ((8, 32), (4, 3)) # strides has been switched
In [182]: w = x.reshape(4,3, order='F')
In [183]: w.strides, w.shape
Out[183]: ((8, 32), (4, 3))
In [184]: z
Out[184]:
array([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
In [185]: w
Out[185]:
array([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
带“ F”的
reshape
与转置产生相同的结果。ravel
,本质上是reshape(-1)
(至1d)In [186]: w.ravel() # order C
Out[186]: array([ 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11])
In [187]: w.ravel(order='F')
Out[187]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
请注意,
w
(和z
)是view
的x
:In [190]: w.base
Out[190]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
In [191]: x.__array_interface__
Out[191]:
{'data': (139649452400704, False),
'strides': None,
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (12,),
'version': 3}
In [192]: w.__array_interface__
Out[192]:
{'data': (139649452400704, False), # same data buffer address
'strides': (8, 32),
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (4, 3),
'version': 3}
对于部分转置:
In [194]: x = np.arange(24)
In [195]: y = x.reshape(2,3,4)
In [196]: y.strides
Out[196]: (96, 32, 8)
In [197]: z = y.transpose(1,0,2)
In [198]: z
Out[198]:
array([[[ 0, 1, 2, 3],
[12, 13, 14, 15]],
[[ 4, 5, 6, 7],
[16, 17, 18, 19]],
[[ 8, 9, 10, 11],
[20, 21, 22, 23]]])
In [199]: z.shape
Out[199]: (3, 2, 4)
In [200]: z.strides
Out[200]: (32, 96, 8)
部分转置具有排列的形状和跨度。结果既不是F阶也不是C阶。
基础中的元素顺序:
In [201]: z.ravel(order='K')
Out[201]:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23])
按行顺序:
In [202]: z.ravel(order='C')
Out[202]:
array([ 0, 1, 2, 3, 12, 13, 14, 15, 4, 5, 6, 7, 16, 17, 18, 19, 8,
9, 10, 11, 20, 21, 22, 23])
按列排序:
In [203]: z.ravel(order='F')
Out[203]:
array([ 0, 4, 8, 12, 16, 20, 1, 5, 9, 13, 17, 21, 2, 6, 10, 14, 18,
22, 3, 7, 11, 15, 19, 23])
关于python - numpy reshape()和transpose()之间有交互规则吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57897700/