代码之家  ›  专栏  ›  技术社区  ›  Georg Heiler

每组1:1分层抽样

  •  1
  • Georg Heiler  · 技术社区  · 5 年前

    如何在python中执行1:1分层抽样?

    假设熊猫数据帧 df 严重失衡。它包含一个二进制组和多个分类子组列。

    df = pd.DataFrame({'id':[1,2,3,4,5], 'group':[0,1,0,1,0], 'sub_category_1':[1,2,2,1,1], 'sub_category_2':[1,2,2,1,1], 'value':[1,2,3,1,2]})
    display(df)
    display(df[df.group == 1])
    display(df[df.group == 0])
    df.group.value_counts()
    

    对于主管道的每个构件 group==1 我要找一个匹配的 group==0

    < P> StratifiedShuffleSplit 从SciKit学习将只返回数据的随机部分,而不是1:1匹配。

    1 回复  |  直到 5 年前
        1
  •  1
  •   Dani Mesejo    5 年前

    如果我理解正确,你可以用 np.random.permutation

    import numpy as np
    import pandas as pd
    
    np.random.seed(42)
    
    df = pd.DataFrame({'id': [1, 2, 3, 4, 5], 'group': [0, 1, 0, 1, 0], 'sub_category_1': [1, 2, 2, 1, 1],
                       'sub_category_2': [1, 2, 2, 1, 1], 'value': [1, 2, 3, 1, 2]})
    
    # create new column with an identifier for a combination of categories
    columns = ['sub_category_1', 'sub_category_2']
    labels = df.loc[:, columns].apply(lambda x: ''.join(map(str, x.values)), axis=1)
    values, keys = pd.factorize(labels)
    df['label'] = labels.map(dict(zip(keys, values)))
    
    # build distribution of sub-categories combinations
    distribution = df[df.group == 1].label.value_counts().to_dict()
    
    # select from group 0 only those rows that are in the same sub-categories combinations
    mask = (df.group == 0) & (df.label.isin(distribution))
    
    # do random sampling
    selected = np.ravel([np.random.permutation(group.index)[:distribution[name]] for name, group in df.loc[mask].groupby(['label'])])
    
    # display result
    result = df.drop('label', axis=1).iloc[selected]
    print(result)
    

    输出

       group  id  sub_category_1  sub_category_2  value
    4      0   5               1               1      2
    2      0   3               2               2      3
    

    请注意,此解决方案假定 group 1 小于中相应子组的大小 group 0 . 更强大的版本包括使用 np.random.choice 替换为:

    selected = np.ravel([np.random.choice(group.index, distribution[name], replace=True) for name, group in df.loc[mask].groupby(['label'])])
    

    有选择的版本与有排列的版本没有相同的假设,尽管它要求每个子类别组合至少有一个元素。