【问题标题】:How to summarize a pandas DataFrame by group with value counts of multiple columns?如何用多列的值计数按组汇总 pandas DataFrame?
【发布时间】:2020-05-23 15:21:33
【问题描述】:

如果这是一个骗局,请指路。我checked 几个questions 来了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

我的目标是比较As 和Bs 列并通过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)

在我的实际数据中,我能够通过以相当不令人满意的方式将 _matches 强制为 pd.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

这两种方法似乎都比较做作,以实现应该相对常见的用途。我想我的问题是,我在这里忽略了一些明显的东西吗?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    您可以在字典上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
    

    【讨论】:

    • 这看似简单,很好!你救了我一个不眠之夜:) 你也晚安。编辑:奇怪的是fill_value 似乎在这里没有做任何事情,但NaN0 对我有用:)
    • 啊,需要做:df.groupby('grp').agg({'A_match':'value_counts', 'B_match':'value_counts'}).fillna(0).unstack(-1),因为NaN不是由unstack引起的。
    猜你喜欢
    • 1970-01-01
    • 2020-06-28
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    相关资源
    最近更新 更多