计算列表列

时间:2017-11-21 16:04:06

标签: python pandas vectorization

我有这个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。我继续寻求更快的实施,但这是朝着正确方向迈出的一步。

2 个答案:

答案 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