我有这个Pandas DataFrame,其中有一列包含列表:
>>> 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)
如果那是真的,我增加一个计数器。但正如许多SO问题和博客文章中所述,迭代DataFrame的行很慢并且可以更快地完成。
因此,在我的下一次迭代中,我向DataFrame添加了一列,其中包含列表v
的副本:
>>> df['V'] = [[2, 3]] * len(df)
>>> df
V m
0 [2, 3] [1, 2, 3]
1 [2, 3] [5, 3, 2]
2 [2, 3] [2, 5]
3 [2, 3] [3, 8, 1]
4 [2, 3] [9]
5 [2, 3] [2, 6, 3]
和一个辅助函数,它只是像我之前那样返回包含布尔值:
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()
但这比矢量化慢。
现在这些方法工作,并且矢量化比初始方法快得多,但感觉有点笨重(创建具有相同值的列,以这种方式使用辅助函数)。所以我的问题,性能是关键,是否有更好/更快的方式来计算列表列表中包含列表的次数?由于列表中没有重复项,可能检查{是否{ {1}}或其他什么,但我不知道如何以及这是否是最佳解决方案。
编辑:因为有人问;这是一个使用字符串而不是整数的示例:
len(union(df['m'], df['V'])) == len(df['m'])
但是如果你想要一个更广泛的DataFrame,可以使用它生成它:
>>> 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
编辑:
Divakar或Vaishali的解决方案都不比使用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))
的解决方案快。不管有没有人能打败它。
Jon Clements带来了一个大约快30%且更清洁的解决方案:np.vectorize
。我继续寻求更快的实施,但这是朝着正确方向迈出的一步。
答案 0 :(得分:4)
您可以使用DataFrame.apply
和内置set.issubset
方法,然后使用.sum()
,它们都在比Python等效的更低级别(通常为C级别)运行。
subset_wanted = {2, 3}
count = df.m.apply(subset_wanted.issubset).sum()
除了编写自定义C级功能时,我看不到更多的时间了,这个功能相当于自定义sum
并且检查那里' sa子集以逐行确定0/1。在这一点上,你无论如何都可以运行成千上万次。
答案 1 :(得分:0)
因为你看起来更像是一种类似行为的行为
(df.m.apply(lambda x: set(x).intersection(set([2,3]))) == set([2,3])).sum()
返回
3