Python:有条件地从数据帧中选择统一样本

Python:有条件地从数据帧中选择统一样本,python,pandas,dataframe,uniform,Python,Pandas,Dataframe,Uniform,假设我有一个这样的数据帧 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  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
有统一的次数。我假设
类别1
中每种类型的数量相等。我知道这可以通过pandas使用
pandas.sample()
实现。然而,我还想确保我选择的样本同样具有
category2
。因此,例如,如果我的样本量为5,我希望得到如下结果:

a  1
a  2
b  10
b  11
b  10
a 1
a 1
b 10
b 10
b 10
我不想要这样的东西:

a  1
a  2
b  10
b  11
b  10
a 1
a 1
b 10
b 10
b 10
虽然这是一个有效的随机样本
n=4
,但它不能满足我的要求,因为我想尽量改变
类别2
的类型

请注意,在第一个示例中,由于
a
只采样了两次,因此
3
没有从
category2
中表示出来。这没关系。目标是尽可能统一地表示该样本数据


如果有助于提供一个更清晰的例子,那么可以将水果、蔬菜、肉类、谷物、垃圾分类。在10个样本中,我希望尽可能多地代表每个类别。所以理想情况下,每个都有两个。然后,属于所选类别的这两个选定行中的每一行都将具有子类别,这些子类别也将尽可能统一地表示。因此,例如,水果可以有红色水果、黄色水果等子类别。对于从10种水果中选择的2种水果类别,红色水果和黄色水果都将在样本中表示。当然,如果我们有更大的样本量,我们会包括更多的水果子类别(绿色水果、蓝色水果等)。

诀窍是建立一个平衡的数组。我提供了一种笨拙的方法。然后通过引用平衡数组循环执行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)

示范

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

要测试它,请执行以下操作:

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))

df.sample
中使用权重关键字时,这很简单:

>>>  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']
中的每个值进行百分比计数,然后将这些值倒过来,这使得序列中所有值的权重都很均匀。

那么
df呢。删除重复项(子集=['category1','category2'])。示例(n=4)
?@MaxU IIUC,这将创建一个偏差,其中匹配特定对值的第一行是所选的行。我不确定这是OP想要的。嗯。。。这是一个有趣的问题…@MaxU我在想,在你的优秀评论的基础上使用df.reindex(np.random.permutation(df.index)).drop_duplicates(subset=['Category1','Category2'])sample(n=4),这是一个问题,但即使这样,如果样本大小是这样的话,比如说,某对样本中需要两个样本呢?
删除重复的
使这不可能。