假设我有以下数据帧,其中Y
和Z
在ID
内都是恒定的:
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')
我将得到Y
和Z
的副本:
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之后,我还没有找到一个更简单的解决方案。
有一个吗?
答案 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