【问题标题】:pandas: how to run a pivot with a multi-index?pandas:如何使用多索引运行数据透视?
【发布时间】:2016-05-26 16:09:55
【问题描述】:

我想在 pandas DataFrame 上运行一个支点,索引是两列,而不是一列。例如,一个字段表示年份,一个字段表示月份,一个显示“item 1”和“item 2”的“item”字段以及一个带有数值的“value”字段。我希望索引为年 + 月。

我设法使它工作的唯一方法是将两个字段合并为一个,然后再次将它们分开。有没有更好的办法?

下面复制的最小代码。非常感谢!

PS 是的,我知道关键字“pivot”和“multi-index”还有其他问题,但我不明白他们是否/如何帮助我解决这个问题。

import pandas as pd
import numpy as np

df= pd.DataFrame()
month = np.arange(1, 13)
values1 = np.random.randint(0, 100, 12)
values2 = np.random.randint(200, 300, 12)


df['month'] = np.hstack((month, month))
df['year'] = 2004
df['value'] = np.hstack((values1, values2))
df['item'] = np.hstack((np.repeat('item 1', 12), np.repeat('item 2', 12)))

# This doesn't work: 
# ValueError: Wrong number of items passed 24, placement implies 2
# mypiv = df.pivot(['year', 'month'], 'item', 'value')

# This doesn't work, either:
# df.set_index(['year', 'month'], inplace=True)
# ValueError: cannot label index with a null key
# mypiv = df.pivot(columns='item', values='value')

# This below works but is not ideal: 
# I have to first concatenate then separate the fields I need
df['new field'] = df['year'] * 100 + df['month']

mypiv = df.pivot('new field', 'item', 'value').reset_index()
mypiv['year'] = mypiv['new field'].apply( lambda x: int(x) / 100)  
mypiv['month'] = mypiv['new field'] % 100

【问题讨论】:

  • 我在Q&A中提供了几个详细的示例和替代方法

标签: python pandas pivot multi-index


【解决方案1】:

以下内容对我有用:

mypiv = df.pivot(index=['year','month'],columns='item')[['values1','values2']]

【讨论】:

    【解决方案2】:

    感谢 gmoutso comment 你可以使用这个:

    def multiindex_pivot(df, index=None, columns=None, values=None):
        if index is None:
            names = list(df.index.names)
            df = df.reset_index()
        else:
            names = index
        list_index = df[names].values
        tuples_index = [tuple(i) for i in list_index] # hashable
        df = df.assign(tuples_index=tuples_index)
        df = df.pivot(index="tuples_index", columns=columns, values=values)
        tuples_index = df.index  # reduced
        index = pd.MultiIndex.from_tuples(tuples_index, names=names)
        df.index = index
        return df
    

    用法:

    df.pipe(multiindex_pivot, index=['idx_column1', 'idx_column2'], columns='foo', values='bar')
    

    您可能希望有一个简单的扁平列结构,并让列成为其预期类型,只需添加以下内容:

    (df
       .infer_objects()  # coerce to the intended column type
       .rename_axis(None, axis=1))  # flatten column headers
    

    【讨论】:

      【解决方案3】:

      您可以分组然后取消堆叠。

      >>> df.groupby(['year', 'month', 'item'])['value'].sum().unstack('item')
      item        item 1  item 2
      year month                
      2004 1          33     250
           2          44     224
           3          41     268
           4          29     232
           5          57     252
           6          61     255
           7          28     254
           8          15     229
           9          29     258
           10         49     207
           11         36     254
           12         23     209
      

      或使用pivot_table:

      >>> df.pivot_table(
              values='value', 
              index=['year', 'month'], 
              columns='item', 
              aggfunc=np.sum)
      item        item 1  item 2
      year month                
      2004 1          33     250
           2          44     224
           3          41     268
           4          29     232
           5          57     252
           6          61     255
           7          28     254
           8          15     229
           9          29     258
           10         49     207
           11         36     254
           12         23     209
      

      【讨论】:

      • @Alexander, pivot_table() 需要 aggfunc 参数,如果没有提供这样的参数,则默认使用 mean() 函数。如果需要sum() 功能,那么pivot_table() 函数应该将aggfunc=sum 添加到调用中。 来源: pandas documentation aggfunc : 函数,函数列表,dict,默认numpy.mean
      • @rp1 好点。这是相同的,因为原始样本数据框每个项目、年和月只有一个值。修改响应以反映更正。
      【解决方案4】:

      我相信如果您在 MultiIndex 中包含 item,那么您可以取消堆叠:

      df.set_index(['year', 'month', 'item']).unstack(level=-1)
      

      这会产生:

                      value      
      item       item 1 item 2
      year month              
      2004 1         21    277
           2         43    244
           3         12    262
           4         80    201
           5         22    287
           6         52    284
           7         90    249
           8         14    229
           9         52    205
           10        76    207
           11        88    259
           12        90    200
      

      它比使用pivot_table 快​​一点,与使用groupby 的速度大致相同或稍慢。

      【讨论】:

      • 也可以直接引用索引级别,例如df.set_index(['year', 'month', 'item']).unstack('fcode').亚历山大在他的回答中的第一种方法也会因非数值数据而失败(在比这里的问题更普遍的问题中)。
      • 这是一个了不起的答案。
      • 我喜欢这个答案,但我在实施时遇到了一些麻烦。我有多列数据,我想将它们展平为一行。这个答案让我得到了我需要去的大部分地方,但我的数据中有大量的 NaN。有什么想法吗?
      • 没关系,我想通了。我使用这样的代码:df = pd.DataFrame(df.set_index(['PRS 3']).unstack(-1)) 然后df = df.transpose()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-15
      • 2023-03-29
      • 2020-08-31
      • 1970-01-01
      • 2020-08-30
      • 2017-05-24
      • 2020-11-11
      相关资源
      最近更新 更多