在Pandas DataFrame value_counts中为未观察到的分类包含零计数

qxsslcnc  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(69)

在Pandas中,在Categorical系列上调用value_counts将确保每个可能的值都得到一个计数,即使该计数为零,所有这些都是微妙的,轻度记录的,可能很少关心,但要紧紧抓住。我们要掉进兔子洞了。
假设我定义了一个DataFrame,其中有两个Categorical列,如下所示:

import pandas as pd
abc_categorical = pd.CategoricalDtype(['a', 'b', 'c'], ordered=True)
size_categorical = pd.CategoricalDtype(['s', 'm', 'l'], ordered=True)

df = pd.DataFrame({
        'a':     pd.Categorical(list('aaabababababa'), dtype=abc_categorical),
        'tsize': pd.Categorical(list('smmssmlmlslsl'), dtype=size_categorical),
    }
)
a   tsize
0   a   s
1   a   m
2   a   m
3   b   s
4   a   s
5   b   m
6   a   l
7   b   m
8   a   l
9   b   s
10  a   l
11  b   s
12  a   l

Series.value_counts

我没有'a'列的值为'c'的行,所以Series.value_counts给我们一个零计数。

df.a.value_counts(sort=False)
a    8
b    5
c    0
Name: a, dtype: int64

因为我们费心定义了一个Categorical,Pandas知道列'a'可以取值'c',但这在数据中从未发生过,所以我们得到了0。到目前为止,一切顺利。

DataFrame.value_counts

然而,如果我调用DataFrame.value_counts来计算两列的组合值,那么对于'b'和'l'组合,我也不会得到零,其中有零,而'c'则没有。

df.value_counts(sort=False)
a  tsize
a  s        2
   m        2
   l        4
b  s        3
   m        2
dtype: int64

真扫兴!

交叉表?

pandas.crosstab函数做得稍微好一点,对'b'和'l'的计数为零,但仍然忽略了'c'值。

pd.crosstab(df.a, df.tsize)
tsize   s   m   l
a
    a   2   2   4
    b   3   2   0

预期结果

我认为DataFrame.value_counts应该返回如下内容:

count
a   tsize
a   s       2
    m       2
    l       4
b   s       3
    m       2
    l       0
c   s       0
    m       0
    l       0

value_counts应该和Series.value_counts做同样的事情,或者至少提供一个选项来这样做,也许是“value_counts(include_zeros=True)"。对于这一点,交叉表也应该这样做。

我的实际问题

我的问题是:有没有一种简洁的惯用方法让Pandas来计算类别的计数值 *,包括那些计数为零的类别 *?
注:在Pandas 2.0.1的上下文中询问

6psbrbz9

6psbrbz91#

是的,DataFrame.groupby尊重Categorical

Groupby

counts = df.groupby(["a", "tsize"]).size()
counts

DataFrameGroupBy.size以Series的形式返回每个组中的行数,该Series的索引是两个分类类型的乘积。

a  tsize
a  s        2
   m        2
   l        4
b  s        3
   m        2
   l        0
c  s        0
   m        0
   l        0
dtype: int64

太棒了!所有的数和零。此外,请注意,T恤的大小是在适当的小,中,大的顺序,而不是字母顺序。这要归功于另一个聪明的Pandas工具包。

分类索引

索引打印本身的方式使它看起来像一个字符串元组数组,但这不是它的本质。

counts.index
MultiIndex([('a', 'l'),
            ('a', 'm'),
            ('a', 's'),
            ('b', 'l'),
            ('b', 'm'),
            ('b', 's'),
            ('c', 'l'),
            ('c', 'm'),
            ('c', 's')],
           names=['a', 'tsize'])

Pandas提供了一个CategoricalIndex,如果我们深入到MultiIndex的级别,我们可以看到它们在工作。

counts.index.get_level_values('a')
CategoricalIndex(['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'], categories=['a', 'b', 'c'], ordered=True, dtype='category', name='a')
counts.index.get_level_values('tsize')
CategoricalIndex(['s', 'm', 'l', 's', 'm', 'l', 's', 'm', 'l'], categories=['s', 'm', 'l'], ordered=True, dtype='category', name='tsize')

数据透视表

为了完整性,pivot_table也尊重Categoricals,并给出与groupby相同的结果。

pd.pivot_table(df, index=["a", "tsize"], aggfunc='size')

观察选项

如果你 * 不想要 * 零呢?groupbypivot_table都接受默认为False的“observed”参数。如果observed设置为True,我们只获得分类分组的观察值,复制value_counts的行为。
好极了!

相关问题