Python pandas:有条件地从数据框

时间:2016-09-12 19:49:44

标签: 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统一次数。我假设category1中每种类型的数量相等。我知道这可以通过使用pandas.sample()的pandas来完成。但是,我还想确保我选择的样本同样具有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个。然后,属于所选类别的那两个所选行中的每一个将具有也被尽可能均匀地表示的子类别。因此,例如,水果可以具有red_fruits,yellow_fruits等的子类别。对于从10中选择的2个水果类别,red_fruits和yellow_fruits都将在样本中表示。当然,如果我们有更大的样本量,我们会包含更多的水果子类别(green_fruits,blue_fruits等)。

3 个答案:

答案 0 :(得分: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)

enter image description here

答案 1 :(得分:1)

这是一个解决方案,它可以按组对真实的随机样本进行分层(不会每次都获得相等的样本,但是从统计角度来看,这样做的平均水平可能还是更好):

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

答案 2 :(得分:1)

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