我将这个问题放在相当大的范围内,希望可以使其更容易理解,但请随时跳过实际的问题。



语境

这是我正在做的工作,引发了这个问题:

我正在使用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']),
]


数据通常是数字的,但是我在这里使用了字符串,因此您可以轻松地看到它如何与标签匹配,例如nrmNorth, 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


但这让我开始思考(这是我的最后一个问题!):

我可以使用任何规则来操纵表达式吗? data1data3

特别是,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)是viewx

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/

10-12 19:35