【问题标题】:How to convert and reshape MultiIndex to 3D Numpy array?如何将 MultiIndex 转换和重塑为 3D Numpy 数组?
【发布时间】:2022-01-03 00:50:27
【问题描述】:

我在数据框中有 4D 数据。我需要将其转换为 3D Numpy 数组。我可以用 for 循环来做到这一点,但有没有更有效的方法?

# Data:
df = pd.DataFrame()
df['variable'] = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'A',
       'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D']
df['date'] = [101,102,103]*8
df['itemID'] = ['item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item2',
       'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2']
df['value1'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12]
df['value2'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12]
df['value3'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12]
df['value4'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12]

# Pivoting:
pivoted = df.pivot(index=['itemID', 'date'], columns='variable', values=[*df.columns[df.columns.str.startswith('value')]])
pivoted.index.levshape

关卡形状为:(2, 3)

它看起来像这样:

# To Numpy:
pivoted2array = pivoted.to_numpy()
pivoted2array.shape

形状现在是:(6, 16)

# Reshaping to 3D:
pivoted2array3d = pivoted2array.reshape(*pivoted.index.levshape,-1)
pivoted2array3d.shape

形状现在是:(2, 3, 16)

它看起来像这样:

array([[[ 1,  2,  3,  4,  1,  2,  3,  4,  1,  2,  3,  4,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  5,  6,  7,  8,  5,  6,  7,  8,  5,  6,  7,  8],
        [ 9, 10, 11, 12,  9, 10, 11, 12,  9, 10, 11, 12,  9, 10, 11, 12]],

       [[ 1,  2,  3,  4,  1,  2,  3,  4,  1,  2,  3,  4,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  5,  6,  7,  8,  5,  6,  7,  8,  5,  6,  7,  8],
        [ 9, 10, 11, 12,  9, 10, 11, 12,  9, 10, 11, 12,  9, 10, 11, 12]]])

这是我用 for 循环转换(重新排序)值的麻烦部分:

dimension3 = []
for k in range(pivoted2array3d.shape[0]): # unique items
    for j in range(pivoted2array3d.shape[1]):  # unique dates
        for i in range(pivoted2array3d.shape[2])[0:pivoted2array3d.shape[2]:4]: 
            element = pivoted2array3d[k][j][i] 
            dimension3.append(element)
        for l in range(pivoted2array3d.shape[2])[0+1:pivoted2array3d.shape[2]:4]: 
            element = pivoted2array3d[k][j][l] 
            dimension3.append(element)
        for m in range(pivoted2array3d.shape[2])[0+2:pivoted2array3d.shape[2]:4]: 
            element = pivoted2array3d[k][j][m] 
            dimension3.append(element)
        for n in range(pivoted2array3d.shape[2])[0+3:pivoted2array3d.shape[2]:4]: 
            element = pivoted2array3d[k][j][n] 
            dimension3.append(element)
len(dimension3)

因此,我有一个长度为 96 的列表。

然后我将它重新整形为 3D Numpy 数组:

final = np.array(dimension3).reshape(*pivoted2array3d.shape)
final.shape

它又有了形状:(2, 3, 16)

最终的结果看起来像这样:

array([[[ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4],
        [ 5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8],
        [ 9,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12]],

       [[ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4],
        [ 5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8],
        [ 9,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12]]])

是否有计算更优雅的方式来重新排列我的数组?有没有办法减少重塑步骤?我真的很想学习如何使用 Numpy 操作!

我的真实数据包括数千个项目、数百个日期、数十个变量和值变量。

测试建议的解决方案

感谢 Shubham Sharma、Quang Hoang 和 mathfux 提供的解决方案。我只为 item1 添加了一个日期,并且需要填写 item2 的缺失日期,从而使初始数据变得更加复杂。建议的解决方案仍然有效。

新数据:

df = pd.DataFrame()
df['variable'] = ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'A',
       'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D', 'A', 'B', 'C', 'D']
df['date'] = [101,102,103]*8 + [104,104,104,104]
df['itemID'] = ['item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item1', 'item2',
       'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2', 'item2']
df['value1'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12,13,13,13,13]
df['value2'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12,13,13,13,13]
df['value3'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12,13,13,13,13]
df['value4'] = [1,5,9,2,6,10,3,7,11,4,8,12,1,5,9,2,6,10,3,7,11,4,8,12,13,13,13,13]

透视和重新索引:

pivoted = df.pivot(index=['itemID', 'date'], columns='variable', values=[*df.columns[df.columns.str.startswith('value')]])
m = pd.MultiIndex.from_product([df['itemID'].unique(),df['date'].unique()], names=pivoted.index.names)
pt = pivoted.reindex(m, fill_value = 0)

解决方案 1:

%%time
pt.sort_index(level=1, axis=1)\
       .values.reshape(*pivoted.index.levshape[:2], -1)

CPU 时间:用户 895 µs,系统:135 µs,总计:1.03 ms 挂壁时间:930 µs

解决方案 2:

%%time
pt.stack(level=0).unstack().to_numpy().reshape(-1, df.date.nunique(), pt.shape[1])

CPU 时间:用户 6.53 毫秒,系统:1.62 毫秒,总计:8.15 毫秒 挂墙时间:6.58 毫秒

解决方案 3:

%%time
pt.to_numpy().reshape(2,df.date.nunique(),4,4).swapaxes(2,3).reshape(2,df.date.nunique(),16)

CPU 时间:用户 387 µs,系统:24 µs,总计:411 µs 挂壁时间:397 µs

【问题讨论】:

    标签: python arrays pandas numpy numpy-slicing


    【解决方案1】:

    np.swapaxes 似乎可以满足您的需要:arr.reshape(2,3,4,4).swapaxes(2,3).reshape(2,3,16)

    主要思想是交换最内部数据中的轴:

    [ 1,  2,  3,  4,  1,  2,  3,  4,  1,  2,  3,  4,  1,  2,  3,  4] ->
    [[ 1,  2,  3,  4],  [1,  2,  3,  4],  [1,  2,  3,  4],  [1,  2,  3,  4]] ->
    [ 1,  1,  1,  1], [2,  2,  2,  2], [3,  3,  3,  3],  [4,  4,  4,  4]] ->
    [ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4]
    

    【讨论】:

    • 这是最快的解决方案。
    【解决方案2】:

    看来你只需要交换pivoted的列级别:

    a = df.pivot(index=['itemID','date'], columns=['variable']).stack(level=0).unstack()
    a.to_numpy().reshape(-1, df.date.nunique(), a.shape[1])
    

    输出:

    array([[[ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4],
            [ 5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8],
            [ 9,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12]],
    
           [[ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4],
            [ 5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8],
            [ 9,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12]]])
    

    【讨论】:

      【解决方案3】:

      我们可以尝试使用索引级别对列进行排序,然后reshape

      pivoted.sort_index(level=1, axis=1)\
             .values.reshape(*pivoted.index.levshape[:2], -1)
      

      array([[[ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4],
              [ 5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8],
              [ 9,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12]],
      
             [[ 1,  1,  1,  1,  2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4],
              [ 5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8],
              [ 9,  9,  9,  9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12]]])
      

      【讨论】:

        猜你喜欢
        • 2019-07-13
        • 2021-08-06
        • 2018-03-17
        • 2017-11-11
        • 2017-09-18
        • 2020-04-22
        • 2015-10-19
        • 2023-03-22
        • 2021-03-21
        相关资源
        最近更新 更多