Pandas统计列表列中包含的列表的出现次数

2024-04-18 13:42:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Pandas数据框,其中有一列列表:

>>> df = pd.DataFrame({'m': [[1,2,3], [5,3,2], [2,5], [3,8,1], [9], [2,6,3]]})
>>> df
           m
0  [1, 2, 3]
1  [5, 3, 2]
2     [2, 5]
3  [3, 8, 1]
4        [9]
5  [2, 6, 3]

我想计算一个列表v = [2, 3]包含在DataFrame的列表中的次数。所以在这个例子中,正确的答案应该是3。现在这只是一个例子,在我的实际数据中,df['m']可以包含900多万行,而列表实际上是包含多达20个元素的字符串列表。如果重要的话,还有一些更详细的信息:v的元素不包含重复项,m的列表也不包含重复项,因此它们可以是集合而不是列表。在

程序的第一次迭代遍历每一行并检查all(e in data['m'][i] for e in v),如果是真的,我增加一个计数器。但是,正如许多问题和博客文章中提到的那样,迭代数据帧的行很慢,而且可以更快地完成。在

因此,在下一次迭代中,我向DataFrame添加了一列,其中包含列表v的副本:

^{pr2}$

以及一个helper函数,它简单地像我以前那样返回包含布尔值:

def all_helper(l1, l2):
    return all(v in l1 for v in l2)

然后我可以使用np.vectorize来添加一个具有布尔值的列:

df['bool'] = np.vectorize(all_helper)(df['m'], df['V'])

最后,用一个简单的df['bool'].sum()计算这些布尔值的和

我还尝试使用.apply()

df['bool'] = df.apply(lambda row: all(w in row['m'] for w in v), axis=1)
count = df['bool'].sum()

但这比矢量化慢。在

现在这些方法都可以工作了,向量化比最初的方法快得多,但是感觉有点笨拙(以这种方式使用helper函数创建具有相同值的列)。所以我的问题是,性能是关键,有没有更好/更快的方法来计算列表列中包含的次数?由于列表不包含重复项,可能需要检查len(union(df['m'], df['V'])) == len(df['m'])或其他内容,但我不知道如何以及这是否是最佳解决方案。在

编辑:既然有人问了,下面是一个用字符串代替整数的例子:

>>> df = pd.DataFrame({'m': [["aa","ab","ac"], ["aa","ac","ad"], ["ba","bb"], ["ac","ca","cc"], ["aa"], ["ac","da","aa"]]})
>>> v = ["aa", "ac"]
>>> df
                    m
0  ["aa", "ab", "ac"]
1  ["aa", "ac", "ad"]
2        ["ba", "bb"]
3  ["ac", "ca", "cc"]
4              ["aa"]
5  ["ac", "da", "aa"]

>>> count_occurrence(df, v)
3

但如果需要更广泛的数据帧,可以使用以下命令生成:

import string

n = 10000
df = pd.DataFrame({'m': [list(set([''.join(np.random.choice(list(string.ascii_lowercase)[:5], np.random.randint(3, 4))) for _ in range(np.random.randint(1, 10))])) for _ in range(n)]})
v = ["abc", 'cde']
print(count_occurrence(df, v))

编辑: Divakar或Vaishali的解决方案都比不上使用np.vectorize的解决方案快。不知道有没有人能打败它。在

Jon Clements提供了一个大约快30%和更干净的解决方案:df.m.apply(set(v).issubset).sum()。我继续寻找更快的实现,但这是朝着正确方向迈出的一步。在


Tags: 数据inhelperdataframedf列表fornp
2条回答

您可以使用DataFrame.apply和内置的set.issubset方法,然后使用.sum(),它们都是在比Python等价物更低的级别(通常是C级别)上操作的。在

subset_wanted = {2, 3}
count = df.m.apply(subset_wanted.issubset).sum()

我看不出比编写一个定制的C级函数(相当于一个自定义的sum)和一个检查有一个子集来逐行确定0/1。在这一点上,你可以运行这个成千上万次无论如何。在

因为你看起来更像一个固定的行为

(df.m.apply(lambda x: set(x).intersection(set([2,3]))) == set([2,3])).sum()

退货

^{pr2}$

相关问题 更多 >