【问题标题】:Python: Random selection per groupPython:每组随机选择
【发布时间】:2014-04-23 17:04:54
【问题描述】:

假设我有一个看起来像这样的数据框:

Name Group_Id
AAA  1
ABC  1
CCC  2
XYZ  2
DEF  3 
YYH  3

如何为每个Group_Id 随机选择一个(或多个)行?假设我想要每个Group_Id 随机抽奖,我会得到:

Name Group_Id
AAA  1
XYZ  2
DEF  3

【问题讨论】:

    标签: python random pandas


    【解决方案1】:
    size = 2        # sample size
    replace = True  # with replacement
    fn = lambda obj: obj.loc[np.random.choice(obj.index, size, replace),:]
    df.groupby('Group_Id', as_index=False).apply(fn)
    

    【讨论】:

    • 如果您不想让每组的同一行出现两次,请设置 replace=False。
    • 如果使用时间戳列执行此操作,在其中创建基于频率的 pd.Grouper(),建议的方法会生成两个名称相同的时间戳索引列。不太理想的情况。另请参阅下面我发布了一个更简单(代码更少,更容易记住,总体上更简单)的方法来做完全相同的事情。
    【解决方案2】:

    0.16.x 开始,pd.DataFrame.sample 提供了一种从对象轴返回随机项目样本的方法。

    In [664]: df.groupby('Group_Id').apply(lambda x: x.sample(1)).reset_index(drop=True)
    Out[664]:
      Name  Group_Id
    0  ABC         1
    1  XYZ         2
    2  DEF         3
    

    【讨论】:

    • 或者:df.groupby('Group_Id').apply(pd.DataFrame.sample, n=1).reset_index(drop=True)
    【解决方案3】:

    有两种方法可以非常简单地做到这一点,一种除了基本的 pandas 语法外不使用任何东西:

    df[['x','y']].groupby('x').agg(pd.DataFrame.sample)
    

    对于 50k 行数据集,这需要 14.4 毫秒。

    另一种稍快的方法涉及 numpy。

    df[['x','y']].groupby('x').agg(np.random.choice)
    

    对于(相同的)50k 行数据集,这需要 10.9 毫秒。

    一般来说,在使用 pandas 时,最好坚持使用其原生语法。特别是对于初学者。

    【讨论】:

    • 这是最流行的答案,但它如何推广到每组采样n 项目? :-)
    • @matanster agg 对我不起作用,但 apply 对我有用。它还可以接受函数的参数:df[['x','y']].groupby('x').apply(pd.DataFrame.sample, n=n, replace=False)。请参阅文档pandas.pydata.org/pandas-docs/stable/reference/api/…
    • 这个方法会从每组的每一列中进行不同的随机选择,而不是选择每组的整行。 @ihadanny 的答案将选择整行,而且速度更快。
    【解决方案4】:

    在优雅的单行中使用 groupby 和 random.choice:

    df.groupby('Group_Id').apply(lambda x :x.iloc[random.choice(range(0,len(x)))])
    

    【讨论】:

    • random.choice(range(0,len(x))) 最好写成np.random.randint(0, len(x))
    • 有些人可能会稍微倾向于使用 numpy.random.choice,它允许您指定 a) 从总体中抽取的样本数量和 b) 如果您想要替换。 docs.scipy.org/doc/numpy-1.9.2/reference/generated/…
    • df.sample(frac = 1.0).groupby('Group_Id').head(1) 快得多
    • @ihadanny 的建议更像是“pandaeic”,也可以推广到 n>1,尽管比 other answer below
    【解决方案5】:

    对于每组随机选择一行,请尝试df.sample(frac = 1.0).groupby('Group_Id').head(1)

    【讨论】:

    • 这是最快的答案,它适用于选择跨多列的行。
    【解决方案6】:

    如果一个组的样本少于所需的样本大小n,则提供的解决方案将失败。这解决了这个问题:

    n = 10
    df.groupby('Group_Id').apply(lambda x: x.sample(min(n,len(x)))).reset_index(drop=True)
    

    【讨论】:

      【解决方案7】:

      【讨论】:

      • 我相信您必须添加 random_state 才能使其工作。
      【解决方案8】:

      一种非常熊猫式的方式:

      takesamp = lambda d: d.sample(n)
      df = df.groupby('Group_Id').apply(takesamp)
      

      【讨论】:

        【解决方案9】:

        使用random.choice,您可以执行以下操作:

        import random
        name_group = {'AAA': 1, 'ABC':1, 'CCC':2, 'XYZ':2, 'DEF':3, 'YYH':3}
        
        names = [name for name in name_group.iterkeys()] #create a list out of the keys in the name_group dict
        
        first_name = random.choice(names)
        first_group = name_group[first_name]
        print first_name, first_group
        

        random.choice(seq)

        Return a random element from the non-empty sequence seq. If seq is empty, raises IndexError.
        

        【讨论】:

          【解决方案10】:

          您可以使用pandas.groupbypandas.concatrandom.sample 的组合:

          import pandas as pd
          import random
          
          df = pd.DataFrame({
                  'Name': ['AAA', 'ABC', 'CCC', 'XYZ', 'DEF', 'YYH'],
                  'Group_ID': [1,1,2,2,3,3]
               })
          
          grouped = df.groupby('Group_ID')
          df_sampled = pd.concat([d.ix[random.sample(d.index, 1)] for _, d in grouped]).reset_index(drop=True)
          print df_sampled
          

          输出:

             Group_ID Name
          0         1  AAA
          1         2  XYZ
          2         3  DEF
          

          【讨论】:

            【解决方案11】:

            我又找到了一个:

            size=2
            count_s = df['Id'].value_counts()
            df.iloc[np.concatenate([previous_count + np.random.choice(count, size) 
                                    for count, previous_count in zip(count_s, 
                                                                     count_s.shift(fill_value=0))])]
            

            【讨论】:

              猜你喜欢
              • 2013-04-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-01-01
              • 2019-05-05
              • 1970-01-01
              相关资源
              最近更新 更多