在熊猫和缺失值中的特定组之间的MultiIndex和操作

时间:2017-09-15 08:57:49

标签: python pandas

考虑以下由对(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)

这样可行,但似乎相当复杂,所有代码都删除了级别,因此列之间的差异工作和分隔列重命名。 是否可以使其更简单?

1 个答案:

答案 0 :(得分:1)

使用unstackB添加到列上,assign创建b2-b1drop以删除不需要的列。

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