【问题标题】:get list of occurrences using pandas使用 pandas 获取事件列表
【发布时间】:2020-12-11 05:38:58
【问题描述】:

给定一个动物列表,例如:

animals = ['cat', 'dog', 'hamster', 'dolphin']

和熊猫数据框,df:

id    animals
1     dog,cat
2     dog
3     cat,dolphin
4     cat,dog
5     hamster,dolphin 

我想获得一个新的数据框来显示每种动物的出现情况,例如:

animal    ids
cat       1,3,4
dog       1,2,4
hamster   5        
dolphin   3,5

我知道我可以运行一个循环并准备它,但我有超过 80,000 个单词的列表以及超过 100 万行的数据框,因此使用循环来完成它需要很长时间。有没有更简单快捷的方法来使用数据框获取结果?

【问题讨论】:

    标签: python pandas performance numpy dataframe


    【解决方案1】:

    让我们试试get_dummies 然后dot

    df.animals.str.get_dummies(',').T.dot(df.id.astype(str)+',').str[:-1]
    Out[307]: 
    cat        1,3,4
    dog        1,2,4
    dolphin      3,5
    hamster        5
    dtype: object
    

    如果考虑列表添加reindex

    df.animals.str.get_dummies(',').T.dot(df.id.astype(str)+',').str[:-1].reindex(animals)
    Out[308]: 
    cat        1,3,4
    dog        1,2,4
    hamster        5
    dolphin      3,5
    dtype: object
    

    【讨论】:

    • 谢谢 Ben 的回答,我很感激。我选择安迪的答案只是因为它更快。
    【解决方案2】:

    基于 NumPy 的性能。 -

    def list_occ(df):
        id_col='id'
        item_col='animals'
        
        sidx = np.argsort(animals)
        s = [i.split(',') for i in df[item_col]]
        d = np.concatenate(s)
        
        p = sidx[np.searchsorted(animals, d, sorter=sidx)]
        C = np.bincount(p, minlength=len(animals))
        
        l = list(map(len,s))
        r = np.repeat(np.arange(len(l)), l)
        v = df[id_col].values[r[np.lexsort((r,p))]]
        
        out = pd.DataFrame({'ids':np.split(v, C[:-1].cumsum())}, index=animals)
        return out
    

    示例运行 -

    In [41]: df
    Out[41]: 
      id          animals
    0  1          dog,cat
    1  2              dog
    2  3      cat,dolphin
    3  4          cat,dog
    4  5  hamster,dolphin
    
    In [42]: animals
    Out[42]: ['cat', 'dog', 'hamster', 'dolphin']
    
    In [43]: list_occ(df)
    Out[43]: 
                   ids
    cat      [1, 3, 4]
    dog      [1, 2, 4]
    hamster        [5]
    dolphin     [3, 5]
    

    基准测试

    使用给定的样本并简单地增加项目的数量。

    # Setup
    N = 100 # scale factor
    s = [i.split(',') for i in df['animals']]
    df_big = pd.DataFrame({'animals':[[j+str(ID) for j in i] for i in s for ID in range(1,N+1)]})
    df_big['id'] = range(1, len(df_big)+1)
    animals = np.unique(np.concatenate(df_big.animals)).tolist()
    df_big['animals'] = [','.join(i) for i in df_big.animals]
    df = df_big
    

    时间安排 -

    # Using given df & scaling it up by replicating elems with progressive IDs
    In [9]: N = 100 # scale factor
       ...: s = [i.split(',') for i in df['animals']]
       ...: df_big = pd.DataFrame({'animals':[[j+str(ID) for j in i] for i in s for ID in range(1,N+1)]})
       ...: df_big['id'] = range(1, len(df_big)+1)
       ...: animals = np.unique(np.concatenate(df_big.animals)).tolist()
       ...: df_big['animals'] = [','.join(i) for i in df_big.animals]
       ...: df = df_big
    
    # @BEN_YO's soln-1
    In [10]: %timeit df.animals.str.get_dummies(',').T.dot(df.id.astype(str)+',').str[:-1]
    163 ms ± 2.94 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    # @BEN_YO's soln-2
    In [11]: %timeit df.animals.str.get_dummies(',').T.dot(df.id.astype(str)+',').str[:-1].reindex(animals)
    166 ms ± 4.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    # @Andy L.'s soln
    %timeit (df.astype(str).assign(animals=df.animals.str.split(',')).explode('animals').groupby('animals').id.agg(','.join).reset_index())
    13.4 ms ± 74 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    In [12]: %timeit list_occ(df)
    2.81 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    【讨论】:

    • 放下手,很好的答案,很好的时机!非常感谢!但有一件事,当我用我的数据集在笔记本中尝试你的答案时,它导致内核重新启动。我试了5次,每次都一样。
    • @AhmetCetin 这可能是因为我们有数百万行。只是为了确认您可以使用减少的数据集运行它,比如使用 df.iloc[:100],然后使用 df.iloc[:1000] 等等,直到您看到我们开始看到该错误的点?
    • 你好 divakar,我会做这个测试,只是我现在不在,但我选择了 Andy 的解决方案,因为它可以毫无问题地处理大数据集,而且速度真的足够快。我真的很感谢你的回答,我想说这绝对是我在 SO 中看到的最好的答案之一(完整的比较,性能测试)。谢谢一百万。
    【解决方案3】:

    使用str.splitexplodeagg.join

    df_final = (df.astype(str).assign(animals=df.animals.str.split(','))
                              .explode('animals').groupby('animals').id.agg(','.join)
                              .reset_index())
    
    Out[155]:
       animals     id
    0      cat  1,3,4
    1      dog  1,2,4
    2  dolphin    3,5
    3  hamster      5
    

    【讨论】:

    • 这很好用,我在数据集中只注意到一个问题。拆分后如何修剪动物列中的空格(左,右)?我试过: df_final = (df.astype(str).assign(animals=df.animals.str.split(',').str.strip()) .explode('animals').groupby('animals') .id.agg(','.join) .reset_index()) 但它返回空数据帧
    • 别在意上面的评论,我在运行您的解决方案之前处理了它。 Divakar 的解决方案在看起来确实很小的数据集中执行得更快,但在应用于大数据集时会崩溃。我有大约 150 万行的数据集,您的解决方案可以毫无问题地处理它,而且速度肯定足够快。感谢一百万您的帮助,
    • 嗨,安迪,如果你有时间,你能在这里查看我的其他问题吗? stackoverflow.com/questions/63535547/… 这是这个的延续,有两个答案,但由于我的数据集很大,它们都因为内存而崩溃。由于您的解决方案在这里很有效,我想您可能会提出一些建议,前提是它不会成为问题。
    猜你喜欢
    • 2018-12-29
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多