我把这个问题放在了相当多的上下文中,希望能让你更容易理解,但可以跳过实际的问题。
上下文
以下是我所做的引发这个问题的工作:
我正在使用一个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
**?**到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)
,对于n
轴的一般情况,则等价于.tranpose(n-1, n-2, ... 2, 1, 0)
。)
在使用.transpose()
的情况下,您正在执行我很好奇的“部分”转置,例如,.tranpose(1, 0, 2)
--在这里,您所做的不仅仅是颠倒轴的顺序。
一些参考资料:
for
循环的顺序),但它并没有真正的帮助。发布于 2019-09-11 23:18:18
我不打算讨论你的所有案例(目前),但这里有一个例子说明了重塑、转置和秩序如何相互作用:
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
of 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])
https://stackoverflow.com/questions/57897700
复制相似问题