考虑以下由对(A,B)索引的单列表:
Value
A B
a1 b1 1
b2 3
a2 b1 10
b2 30
a3 b1 100
对于B的特定值,如b2,我想获得一个表,列出其所有值以及一个额外的列,如果b1或b2的对应值存在,则给出b2和b1之间的差异。所以结果应该是:
b2 b2-b1
a1 3.0 2.0
a2 30.0 20.0
a3 NaN NaN
a4 40.0 NaN
其中a3的两列中的NaN表示缺少b1和b2的值,而a4的NaN表示只缺少b1。
最初我尝试了基于Calculation between groups in a Pandas multiindex dataframe的解决方案:
import numpy as np
import pandas as pd
import itertools
index = pd.MultiIndex.from_tuples([
('a1', 'b1'),
('a1', 'b2'),
('a2', 'b1'),
('a2', 'b2'),
('a2', 'b3'),
('a3', 'b1'),
('a4', 'b2'),
], names=['A', 'B'])
table = pd.DataFrame({'Value': [1,3, 10, 30, 31, 100, 40]}, index=index)
tmp = pd.concat([table, table.groupby(level='A')['Value'].transform(lambda x: x.loc[:, 'b2'] - x.loc[:, 'b1'])], axis=1)
b2 = tmp.groupby(level='B').get_group('b2')
b2.index = b2.index.droplevel(level='B')
print(b2)
但是这会给出一个没有a3行的表(列名可以用重命名来修复):
Value Value
A
a1 3 2.0
a2 30 20.0
a4 40 NaN
这样做的原因是,似乎变换保留了原始索引,并且额外的NaN没有插入到表中,对吧?有可能解决这个问题吗?
所以我尝试了在组中的列之间使用显式差异的替代方法:
import numpy as np
import pandas as pd
import itertools
index = pd.MultiIndex.from_tuples([
('a1', 'b1'),
('a1', 'b2'),
('a2', 'b1'),
('a2', 'b2'),
('a3', 'b1'),
('a4', 'b2'),
], names=['A', 'B'])
input_table = pd.DataFrame({'Value': [1,3, 10, 30, 100, 40]}, index=index)
grouped = input_table.groupby(level='B')
b1 = grouped.get_group('b1')
b1.index = b1.index.droplevel(level='B')
b2 = grouped.get_group('b2')
b2.index = b2.index.droplevel(level='B')
b2 = b2.rename(columns={'Value': 'b2'})
b2 = pd.concat([b2, b2['b2'] - b1['Value']], axis=1)
b2.rename(columns={0: 'b2-b1'}, inplace=True)
print(b2)
这样可行,但似乎相当复杂,所有代码都删除了级别,因此列之间的差异工作和分隔列重命名。 是否可以使其更简单?
答案 0 :(得分:1)
使用unstack
将B
添加到列上,assign
创建b2-b1
和drop
以删除不需要的列。
In [1120]: table.unstack()['Value'].assign(b2b1=lambda x: x.b2-x.b1).drop(['b1', 'b3'], 1)
Out[1120]:
B b2 b2b1
A
a1 3.0 2.0
a2 30.0 20.0
a3 NaN NaN
a4 40.0 NaN
要删除索引名称,请使用rename_axis
In [1123]: (table.unstack()['Value'].assign(b2b1=lambda x: x.b2-x.b1).drop(['b1', 'b3'], 1)
.rename_axis(None).rename_axis(None, 1))
Out[1123]:
b2 b2b1
a1 3.0 2.0
a2 30.0 20.0
a3 NaN NaN
a4 40.0 NaN
或者,您可以存储unstack
结果
In [1127]: dff = table.unstack()['Value'].rename_axis(None).rename_axis(None, 1)
In [1128]: dff['b2-b1'] = dff['b2'] - dff['b1']
In [1129]: dff
Out[1129]:
b1 b2 b3 b2-b1
a1 1.0 3.0 NaN 2.0
a2 10.0 30.0 31.0 20.0
a3 100.0 NaN NaN NaN
a4 NaN 40.0 NaN NaN
In [1189]: dff[['b1', 'b2-b1']] # get specific columns
Out[1189]:
b1 b2-b1
a1 1.0 2.0
a2 10.0 20.0
a3 100.0 NaN
a4 NaN NaN
详细
In [1124]: table.unstack()
Out[1124]:
Value
B b1 b2 b3
A
a1 1.0 3.0 NaN
a2 10.0 30.0 31.0
a3 100.0 NaN NaN
a4 NaN 40.0 NaN