【问题标题】:pandas get unique values from column of lists熊猫从列表列中获取唯一值
【发布时间】:2021-01-23 23:51:11
【问题描述】:

如何在 pandas 或 numpy 中获取一列列表的唯一值,以便第二列来自

将导致'action', 'crime', 'drama'

我能想到的最接近(但不起作用)的解决方案是:

 genres = data['Genre'].unique()

但这会导致 TypeError 说明列表如何不可散列。

TypeError: unhashable type: 'list'

设置似乎是个好主意,但是

genres = data.apply(set(), columns=['Genre'], axis=1)

但也会导致 TypeError: set() takes no keyword arguments

【问题讨论】:

    标签: python pandas numpy unique


    【解决方案1】:

    你可以使用explode:

    data = pd.DataFrame([
        {
            "title": "The Godfather: Part II",
            "genres": ["crime", "drama"],
            "director": "Fracis Ford Coppola"
        },
        {
            "title": "The Dark Knight",
            "genres": ["action", "crime", "drama"],
            "director": "Christopher Nolan"
        }
    ])
    # Changed from data.explode("genres")["genres"].unique() as suggested by rafaelc
    data["genres"].explode().unique() 
    

    结果:

    array(['crime', 'drama', 'action'], dtype=object)
    

    【讨论】:

    • @rafaelc 是的!而且速度也快得多。 :P(虽然没有itertools 快​​)
    • 这很好...但是如果您需要将列表保持为一个整体,这将不起作用...
    • @LucasAimaretto 这不是问题的一部分,无论如何都是一个奇怪的要求。
    【解决方案2】:

    如果您只想查找唯一值,我建议使用 itertools.chain.from_iterable 连接所有这些列表

    import itertools
    
    >>> np.unique([*itertools.chain.from_iterable(df.Genre)])
    array(['action', 'crime', 'drama'], dtype='<U6')
    

    甚至更快

    >>> set(itertools.chain.from_iterable(df.Genre))
    {'action', 'crime', 'drama'}
    

    Timings

    df = pd.DataFrame({'Genre':[['crime','drama'],['action','crime','drama']]})
    df = pd.concat([df]*10000)
    
    %timeit set(itertools.chain.from_iterable(df.Genre))
    100 loops, best of 3: 2.55 ms per loo
        
    %timeit set([x for y in df['Genre'] for x in y])
    100 loops, best of 3: 4.09 ms per loop
    
    %timeit np.unique([*itertools.chain.from_iterable(df.Genre)])
    100 loops, best of 3: 12.8 ms per loop
    
    %timeit np.unique(df['Genre'].sum())
    1 loop, best of 3: 1.65 s per loop
    
    %timeit set(df['Genre'].sum())
    1 loop, best of 3: 1.66 s per loop
    

    【讨论】:

      【解决方案3】:

      这里有一些选项:

      # toy data
      df = pd.DataFrame({'Genre':[['crime','drama'],['action','crime','drama']]})
      
      np.unique(df['Genre'].sum())
      # 109 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
      
      set(df['Genre'].sum())
      # 87 µs ± 1.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
      
      set([x  for y in df['Genre'] for x in y])
      # 11.8 µs ± 126 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
      

      【讨论】:

        【解决方案4】:

        如果您只是想提取信息而不是添加回 DataFrame,您可以在 for 循环中使用 Python 的 set 方法:

        import pandas as pd
        df = pd.DataFrame({'movie':[[1,2,3],[1,2,6]]})
        out = set()
        for row in df['movie']:
            out.update({item for item in row})
        print(out)
        

        如果需要,您也可以将其包装在应用调用中(这将返回 None 但更新集合):

        out = set()
        df['movie'].apply(lambda x: out.update({item for item in x}))
        

        我个人认为 for 循环读起来更清晰一些。

        【讨论】:

          【解决方案5】:

          利用sets 的力量实现链式唯一性。 我已经在大型列表中使用了这种技术,在 envs 等大数据中。这里的主要优点是减少了生成最终平面列表所需的时间。

          1. 将列表列转换为集合
          2. 使用union将所有集合减少为最终集合

          试试:

          from functools import reduce # for python 3
          
          l = df.Genre.dropna().tolist()
          sets = [ set(i) for i in l ]
          final_set = reduce(lambda x, y: x.union(y), sets)
          
          • 在像 envs' 这样的大数据中,像 spark 一样,使用 map 将每个列表转换为一个集合,然后像上面一样 reduce。
          • 如果您需要从所有列表中获取所有常见值,请将 union 更改为 intersection

          【讨论】:

            【解决方案6】:

            不确定它是否正是您想要的,但这将允许您将其转换为集合。

            import pandas as pd
            import numpy as np
            
            df = pd.DataFrame({'Movie':['The Godfather', 'Dark Knight'], 'Genre': [['Crime', 'Drama'],['Crime', 'Drama', 'Action']]})
            
            genres = []
            for sublist in df['Genre']:
                for item in sublist:
                    genres.append(item)
            
            genre_set = set(genres)
            
            print(genre_set)
            

            输出:{'Action', 'Drama', 'Crime'}

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-06-25
              • 1970-01-01
              • 1970-01-01
              • 2015-01-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多