【问题标题】:Python pandas: conditionally select a uniform sample from a dataframePython pandas:有条件地从数据框中选择统一样本
【发布时间】:2016-09-12 19:49:44
【问题描述】:

假设我有一个这样的数据框

category1  category2   other_col   another_col ....
a          1
a          2
a          2        
a          3
a          3
a          1
b          10
b          10
b          10
b          11
b          11
b          11

我想从我的数据框中获取一个样本,以便category1 的次数一致。我假设category1 中每种类型的数量相同。我知道这可以通过使用pandas.sample() 的熊猫来完成。但是,我还想确保我选择的样本也同样具有 category2 的代表。因此,例如,如果我的样本量为 5,我会想要这样的东西:

a  1
a  2
b  10
b  11
b  10

我会想要这样的东西:

a 1
a 1
b 10
b 10
b 10

虽然这是n=4 的有效随机样本,但它不符合我的要求,因为我想尽可能多地改变category2 的类型。

请注意,在第一个示例中,因为 a 仅采样了两次,所以 3 没有从 category2 中表示。这没关系。目标是尽可能统一地表示样本数据。

如果它有助于提供一个更清晰的示例,可以使用类别 fruitvegetablesmeatgrainsjunk。在 10 个样本大小中,我希望尽可能多地代表每个类别。所以理想情况下,每个 2 个。然后,属于所选类别的这 2 个所选行中的每一行都将具有子类别,这些子类别也尽可能统一地表示。因此,例如,水果可能有 red_fruits、yellow_fruits 等子类别。对于从 10 个水果类别中选择的 2 个水果类别,red_fruits 和 yellow_fruits 都将在样本中表示。当然,如果我们有更大的样本量,我们会包括更多的水果子类别(green_fruits、blue_fruits 等)。

【问题讨论】:

  • df.drop_duplicates(subset=['category1','category2']).sample(n=4) 呢?
  • @MaxU IIUC,这会产生偏差,其中与特定对值匹配的第一行是选择的行。我不确定这是 OP 想要的。
  • 嗯...这是一个有趣的问题...
  • @MaxU 我在想可以在df.reindex(np.random.permutation(df.index)).drop_duplicates(subset=['Category1','Category2']).sample(n=4) 的出色评论的基础上进行构建,但即使这样也是一个问题 - 如果样本量如此之大,比如说,需要从一些样本中提取 2 个样本怎么办?一对? drop_duplicates 使这成为不可能。

标签: python pandas dataframe uniform


【解决方案1】:

Trick 正在建立一个平衡的阵列。我提供了一种笨拙的方法。然后通过引用平衡数组循环遍历 groupby 对象采样。

def rep_sample(df, col, n, *args, **kwargs):
    nu = df[col].nunique()
    m = len(df)
    mpb = n // nu
    mku = n - mpb * nu
    fills = np.zeros(nu)
    fills[:mku] = 1

    sample_sizes = (np.ones(nu) * mpb + fills).astype(int)

    gb = df.groupby(col)

    sample = lambda sub_df, i: sub_df.sample(sample_sizes[i], *args, **kwargs)

    subs = [sample(sub_df, i) for i, (_, sub_df) in enumerate(gb)]

    return pd.concat(subs)

演示

rep_sample(df, 'category1', 5)

【讨论】:

    【解决方案2】:

    这是一个按组分层的真正随机样本的解决方案(不会每次都得到相同的样本,但从统计角度来看,这可能会更好):

    import numpy as np
    import pandas as pd
    
    def stratified_sample(df, sample_size_per_class, strat_cols):
    
        if isinstance(strat_cols, str):
            strat_cols = [strat_cols]
    
        #make randomizer (separately, in case we need it later?)
        vcs = {}
        randomizer = {}
        for c in strat_cols:
    
            #calculate number of distinct classes relative to sample size
            _vc = df[c].value_counts()
            vcs[c] = (_vc / sample_size_per_class).round(0).astype(int).to_dict()
    
            #randomizer will divvy up the bigger groups into chunks of size approximate to the smallest
            randomizer[c] = lambda v: np.random.randint(0, vcs[c][v], size=None)
    
        #apply randomizer
        randomized_cols = []
        for c in strat_cols:
            stratname = '_stratified_%s' % c
            randomized_cols.append(stratname)
            df[stratname] = df[c].apply(randomizer[c])
    
    
        #return first random case which should be approximately n_samples
        return df[df[randomized_cols].max(axis=1) == 0][set(df.columns).difference(randomized_cols)]
    

    测试它:

    test = pd.DataFrame({'category1':[0,0,0,0,0,0,1,1,1,1,1,1],
                        'category2':[1,2,2,3,3,1,10,10,10,11,11,11]})
    
    lens = []
    for i in range(1000):
        lens.append(
            len(
                stratified_sample(test, 3, ['category1','category2'])
            )
        )
    
    print(np.mean(lens))
    

    【讨论】:

      【解决方案3】:

      当您在 df.sample 中使用 weights 关键字时,这很简单:

      >>>  df.sample(n = 5, weights = (df['category2'].value_counts()/len(df['category2']))**-1)
      

      输出:

          category1   category2
      2   "a"         2
      1   "a"         2
      10  "b"         11
      3   "a"         3
      11  "b"         11
      

      为了解释,权重如下所示:

      11    4.0
      10    4.0
      3     6.0
      2     6.0
      1     6.0
      

      我只是对df['category2'] 中的每个值进行百分比计数,然后反转这些值,这样可以在系列中的所有值之间形成一个很好的统一权重。

      【讨论】:

      • 只有当category2 是离散的但不是连续的或广泛的唯一值时,此方法才有效。
      猜你喜欢
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多