一个 pandas 列表的频率统计

4

我有一个pandas DataFrame,其中一列包含由管道符分隔的字符串。这些字符串是电影类型。它们看起来像这样:

Genre
Adventure|Animation|Children|Comedy|Fantasy
Comedy|Romance
...

我使用了str.split将它们转换为列表并放回了单元格中。就像这样:
Genre 
[Adventure, Animation, Children, Comedy, Fantasy]
[Adventure, Children, Fantasy]
[Comedy, Romance]
[Comedy, Drama, Romance]
[Comedy]

我希望获得所有类型的总数。例如,喜剧出现了多少次?冒险出现了多少次?我似乎无法解决这个问题。
这会看起来像:
Comedy    4
Adventure 2
Animation 1
(...and so on...)
2个回答

4

作为一个for循环俱乐部的成员,我建议使用Python的C加速例程——itertools.chaincollections.Counter——以提高性能。

from itertools import chain
from collections import Counter

pd.Series(
    Counter(chain.from_iterable(x.split('|') for x in df.Genre)))

Adventure    1
Animation    1
Children     1
Comedy       2
Fantasy      1
Romance      1
dtype: int64

为什么我认为CPython函数比pandas的“矢量化”字符串函数更好?它们本质上很难矢量化。你可以在For loops with pandas - When should I care?中了解更多。


如果你必须处理NaN,你可以调用一个能优雅地处理异常的函数:

def try_split(x):
    try:
        return x.split('|')
    except AttributeError:
        return []

pd.Series(
    Counter(chain.from_iterable(try_split(x) for x in df.Genre)))

通俗易懂地说,你可以使用 split, stack, 和 value_counts 来实现这个功能。
df['Genre'].str.split('|', expand=True).stack().value_counts()

Comedy       2
Romance      1
Children     1
Animation    1
Fantasy      1
Adventure    1
dtype: int64

即使是微小的数据框,时间差异也很明显。

%timeit df['Genre'].str.get_dummies(sep='|').sum()
%timeit df['Genre'].str.split('|', expand=True).stack().value_counts()
%%timeit
pd.Series(
    Counter(chain.from_iterable(try_split(x) for x in df.Genre)))

2.8 ms ± 68.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.4 ms ± 210 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
320 µs ± 9.71 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

1
谢谢你提供的所有示例!我从中学到了很多东西!我尝试了几种方法,你指出时间问题是非常正确的。我只有27,000条记录,但已经很明显了。谢谢! - broepke

3

我也赞成使用chain+for

只是为了记录,另一种可能的方法是使用get_dummies

df['Genre'].str.get_dummies(sep='|').sum()

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