如何按组汇总具有多个列的值的Pandas DataFrame?

时间:2020-05-23 06:36:59

标签: python pandas dataframe

如果这是一个骗子,请引导。我checkedquestions来了一些close,但不能解决我的问题。

我有一个虚拟的DataFrame,如下所示:

   grp  Ax  Bx  Ay  By  A_match  B_match
0  foo   3   2   2   2    False     True
1  foo   2   1   1   0    False    False
2  foo   4   3   0   3    False     True
3  foo   4   3   1   4    False    False
4  foo   4   4   3   0    False    False
5  bar   3   0   3   0     True     True
6  bar   3   4   0   3    False    False
7  bar   1   2   1   2     True     True
8  bar   1   3   4   1    False    False
9  bar   1   1   0   3    False    False

我的目标是比较AB的列,并按grp总结结果,从而:

           A_match       B_match      
           False  True   False True 
grp                                 
bar            3     2       3     2
foo            5     0       3     2 

因此,我如下添加了两个_match列,以获取上面的df

df['A_match'] = df['Ax'].eq(df['Ay'])
df['B_match'] = df['Bx'].eq(df['By'])

根据我的理解,我希望可以做这样的事情,但是不起作用:

df.groupby('grp')[['A_match', 'B_match']].agg(pd.Series.value_counts)

# trunc'd Traceback:
# ... ValueError: no results ...
# ... During handling of the above exception, another exception occurred: ...
# ... ValueError: could not broadcast input array from shape (5,7) into shape (5)

在我的实际数据中,我能够通过以一种不太令人满意的方式强迫_matchpd.Categorical来回避这一点。但是,我已经注意到成功与失败的关系,即使使用了这个伪数据,即使使用pd.Categorial,我也仍然得到如上所述的确切错误:

df['A_match'] = pd.Categorical(df['Ax'].eq(df['Ay']).values, categories=[True, False])
df['B_match'] = pd.Categorical(df['Bx'].eq(df['By']).values, categories=[True, False])
df.groupby('grp')[['A_match', 'B_match']].agg(pd.Series.value_counts)

# ... ValueError: could not broadcast input array from shape (5,7) into shape (5)

对我来说毫无意义-形状(5,7)甚至从哪里来?我上次检查时,每个agg都会通过形状(5,)。而且甚至agg的运行似乎也与我想象的不同,它应该与Series相对:

>>> df.groupby('grp')[['A_match', 'B_match']].agg(lambda x: type(x))
                                 A_match                              B_match
grp                                                                          
bar  <class 'pandas.core.series.Series'>  <class 'pandas.core.series.Series'>
foo  <class 'pandas.core.series.Series'>  <class 'pandas.core.series.Series'>

# Good - it's Series, I should be able to call value_counts directly?

>>> df.groupby('grp')[['A_match', 'B_match']].agg(lambda x: x.value_counts())

# AttributeError: 'DataFrame' object has no attribute 'value_counts'  <-- ?!?!? Where did 'DataFrame' come from?

我最终能够使用以下组合,但由于引入了许多不必要的axis名称,因此仍然不能令人满意。

>>> df.melt(id_vars='grp', value_vars=['A_match', 'B_match']).reset_index().pivot_table(index='grp', columns=['variable', 'value'], aggfunc=pd.Series.count)
           index                    
variable A_match       B_match      
value      False True    False True 
grp                                 
bar            3     2       3     2
foo            5     0       3     2

这两种方法似乎都非常想实现某些应该相对通用的用法。我想我的问题是,我是否忽略了这里明显的内容?

1 个答案:

答案 0 :(得分:3)

您可以在字典上agg

(df.groupby('grp').agg({'A_match':'value_counts',
                      'B_match':'value_counts'})
   .unstack(-1, fill_value=0)
)

输出:

      A_match       B_match      
      False  True   False  True 
bar     3.0   2.0       3     2
foo     5.0   NaN       3     2