带有索引列的列不变的数据透视数据框

时间:2018-07-09 19:05:53

标签: python pandas dataframe stata

假设我有以下数据帧,其中YZID内都是恒定的:

   ID TYPE  X    Y   Z
0   1    A  1  foo  10
1   1    B  2  foo  10
2   2    A  3  bar  20
3   2    B  4  bar  20
4   3    A  5  baz  30
5   3    B  6  baz  30

我想将数据从“长”格式更改为“宽”格式:

   ID  XA  XB    Y   Z
0   1   1   2  foo  10
1   2   3   4  bar  20
2   3   5   6  baz  30

但是,如果我使用pandas.DataFrame.pivot()

df_new = df.pivot(index='ID', columns='TYPE')

我将得到YZ的副本:

      X       Y        Z    
TYPE  A  B    A    B   A   B
ID                          
1     1  2  foo  foo  10  10
2     3  4  bar  bar  20  20
3     5  6  baz  baz  30  30

要获得所需的输出,我可以执行以下操作:

import pandas as pd

df = pd.DataFrame({'ID': [1, 1, 2, 2, 3, 3],
                   'TYPE': ['A', 'B', 'A', 'B', 'A', 'B'],
                   'X': [1, 2, 3, 4, 5, 6],
                   'Y': ['foo', 'foo', 'bar', 'bar', 'baz', 'baz'],
                   'Z': [10, 10, 20, 20, 30, 30]})


def long_to_wide(df, i, j, varlist):
    df_wide = df.pivot(index='ID', columns='TYPE')
    df_wide.columns = [''.join(col).strip() for col in df_wide.columns.values]
    df_wide.reset_index(inplace=True)

    for var in varlist:
        if pd.Series.equals(df_wide[var + 'A'], df_wide[var + 'B']):
            df_wide.drop((var + 'B'), axis = 1, inplace = True)
        else:
            raise
            # Error handling of some sort...
        df_wide = df_wide.rename(columns={var + 'A': var})

    return df_wide


df_new = long_to_wide(df, 'ID', 'TYPE', ['Y', 'Z'])

但是,我觉得这一定不必要。例如,要在Stata中获得所需的输出,可以运行以下任一方法:

reshape wide X, i(ID) j(TYPE)

reshape wide X, i(ID Y Z) j(TYPE)

这种情况非常普遍,因此我认为应该有一个内置的方法来处理它。但是在浏览Pandas文档以及此处的Stack Overflow之后,我还没有找到一个更简单的解决方案。

有一个吗?

2 个答案:

答案 0 :(得分:2)

我对此有了更好的了解,并且功能pandas.DataFrame.pivot()实际上按预期执行。与Stata的reshape(它是一个命令)不同,它在后台做了很多事情,pivot()只是简单地重新排列数据。

@Heleemur的解决方案很聪明并且效果很好,但是通常,重命名或删除重复项是您的责任。

这是一个基于pivot()(或pivot_table())的直观解决方案:

import pandas as pd

df = pd.DataFrame({'ID': [1, 1, 2, 2, 3, 3],
                   'TYPE': ['A', 'B', 'A', 'B', 'A', 'B'],
                   'X': [1, 2, 3, 4, 5, 6],
                   'Y': ['foo', 'foo', 'bar', 'bar', 'baz', 'baz'],
                   'Z': [10, 10, 20, 20, 30, 30]})

wanted = df.pivot(index='ID', columns='TYPE')[[('X','A'), ('X','B'), ('Y','A'), ('Z','A')]].reset_index()
wanted.columns = wanted.columns.get_level_values(0)
wanted.columns = ['ID', 'XA', 'XB', 'Y', 'Z']
wanted

   ID  XA  XB    Y   Z
0   1   1   2  foo  10
1   2   3   4  bar  20
2   3   5   6  baz  30

另一种方法是:

wanted = df.pivot(index='ID', columns='TYPE').reset_index()
wanted.columns = [' '.join(col)for col in wanted.columns.values]
wanted = wanted.iloc[:, [0,2] + list(range(1, len(wanted.columns)-1, 2))]
wanted

   ID   X B  X A  Y A  Z A
0    1    2    1  foo   10
1    2    4    3  bar   20
2    3    6    5  baz   30

wanted.columns = ['ID', 'XB', 'XA', 'Y', 'Z']
wanted

   ID  XB  XA    Y   Z
0   1   2   1  foo  10
1   2   4   3  bar  20
2   3   6   5  baz  30

在具有更多列的较大数据框中,您可能希望保留原始名称。


编辑:

这与@Heleemur中带有pivot_table()的解决方案等效:

wanted = df.pivot_table(index=['ID', 'Y', 'Z'], columns='TYPE').reset_index()
wanted.columns = [''.join(c) for c in wanted.columns.values]
wanted

   ID    Y   Z  XA  XB
0   1  foo  10   1   2
1   2  bar  20   3   4
2   3  baz  30   5   6

答案 1 :(得分:1)

我通过设置索引,合并多索引列名称和重置索引来做到这一点。我确信也可以通过数据透视表来实现(将df定义为示例数据框)。

df2 = df.set_index(['ID', 'Y', 'Z', 'TYPE']).unstack()
df2.columns = [''.join(c) for c in df2.columns.values]
df2.reset_index()

outputs:

   ID    Y   Z  XA  XB
0   1  foo  10   1   2
1   2  bar  20   3   4
2   3  baz  30   5   6