Pandas:如何在多层行上进行groupby计数?

6
我有以下数据框:
|----|----|
| A  | B  |
| a1 | b1 |
| a2 | b1 |
| a1 | b2 |
| a2 | b3 |

我希望通过每个A计算B的数量,并得出以下结果:
|----|----|-------|
| A  | B  | Count |
| a1 | b1 |  1    |
|    | b2 |  1    |
|    | b3 |  NaN  |
| a2 | b1 |  1    |
|    | b2 |  NaN  |
|    | b3 |  1    |

我通常用df.groupby([B])[A].count()进行这种操作,但在这种类似数据透视表的情况下,我感到有些困惑。谢谢你提前帮忙。
更新:df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 20422 entries, 180 to 96430
Data columns (total 2 columns):
B    20422 non-null object
A             20422 non-null object
dtypes: object(2)
memory usage: 478.6+ KB

我正在使用 df.groupby([B])[A].value_counts().unstack().stack(dropna=False).reset_index(name="Count")

|--|----|----|-------|
|  | A  | B  | Count |
|0 | a1 | b1 |  1    |
|1 | a1 | b2 |  1    |
|2 | a1 | b3 |  NaN  |
|3 | a2 | b1 |  1    |
|4 | a2 | b2 |  NaN  |
|5 | a2 | b3 |  1    |
2个回答

5

1) 一种方法是按"A"进行分组,并使用value_counts计算"B"下元素的不同计数。然后使用unstackstack进行融合,使用dropna=False获得所需的DF

df.groupby('A')['B'].value_counts().unstack().stack(dropna=False).reset_index(name="Count")

2) 如果我们在堆叠后将零计数元素替换为np.NaNpd.crosstab 也提供了一个很好的替代方法:

pd.crosstab(df['A'], df['B']).stack().replace({0:np.nan}).reset_index(name="Count")

两种方法都会产生以下结果:

enter image description here


编辑1:

要以特定格式显示分组键"A"(即保留第一个出现并用空字符串替换其余部分)

df_g = pd.crosstab(df['A'], df['B']).stack().replace({0:np.nan}).reset_index(name="Count")
df_g.loc[df_g.duplicated('A'), "A"] = ""

enter image description here

编辑2:

如果您想将"A"作为一个完整的单元格,成为多级索引的DF的一部分:

df.groupby('A')['B'].value_counts().unstack().stack(dropna=False
                    ).reset_index(name="Count").set_index(['A', 'B'])

enter image description here


谢谢,但不幸的是这并没有帮助到我。它确实在新的计数列上进行计算,但没有按A分组。可能第一个“count”列是原因,请查看我的更新。同时感谢@Tim的回答,但问题仍然存在。 - Novitoll
好的,如果问题出在单元格的显示上,请查看编辑。 - Nickil Maveli
谢谢,这个方法可行。但可能不是正确的方式,因为它没有将单元格合并为一个单元格,而是将重复项设置为空字符串。所以行数可能是问题所在。我很感激你的努力,我可能会采用你的分组方法进行一些修改 :) df_g = df.groupby('A')['B'].value_counts().unstack().stack(dropna=False).reset_index(name="Count") df_g.groupby([A, B])['Count'].sum().to_frame() - Novitoll
1
edit2是正确且不错的解决方案。太棒了!谢谢。 - Novitoll

1
你可以按照两个列进行分组,并访问每个分组的大小:
 df.groupby(['A', 'B']).size()

返回:
A   B 
a1  b1    1
    b2    1
a2  b1    1
    b3    1
dtype: int64

它不会为不存在的组合提供NaN。

2
在编程中,尽量避免使用apply函数,特别是当groupby已经有了完全相同的自定义函数时。可以考虑使用groupby.size方法。 - Nickil Maveli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接