【问题标题】:Stratified samples from Pandas来自 Pandas 的分层样本
【发布时间】:2017-04-23 10:37:42
【问题描述】:

我有一个 pandas DataFrame,大致如下:

cli_id | X1 | X2 | X3 | ... | Xn |  Y  |
----------------------------------------
123    | 1  | A  | XX | ... | 4  | 0.1 |
456    | 2  | B  | XY | ... | 5  | 0.2 |
789    | 1  | B  | XY | ... | 5  | 0.3 |
101    | 2  | A  | XX | ... | 4  | 0.1 |
...

我有客户 ID、少数分类属性和 Y,它是事件的概率,其值从 0 到 1 乘以 0.1。

我需要在大小为 200 的 Y 的每一组(所以 10 倍)中抽取一个分层样本

我经常在拆分成训练/测试时使用它来获取分层样本:

def stratifiedSplit(X,y,size):
    sss = StratifiedShuffleSplit(y, n_iter=1, test_size=size, random_state=0)

    for train_index, test_index in sss:
        X_train, X_test = X.iloc[train_index], X.iloc[test_index]
        y_train, y_test = y.iloc[train_index], y.iloc[test_index]

    return X_train, X_test, y_train, y_test

但我不知道在这种情况下如何修改它。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    如果每个组的样本数量相同,或者每个组的比例恒定,您可以尝试类似

    df.groupby('Y').apply(lambda x: x.sample(n=200))
    

    df.groupby('Y').apply(lambda x: x.sample(frac=.1))
    

    要针对多个变量执行分层抽样,只需针对更多变量进行分组。为此可能需要构造新的分箱变量。

    但是,如果组大小太小 w.r.t.像groupsize 1和proportion .25这样的比例,则不返回任何物品。这是由于pythons对int函数int(0.25)=0的舍入实现@

    【讨论】:

    • 假设我有一个包含 100 000 行的 DataFrame,我想从中抽取 10 000 个样本,但每组至少有 10 个样本,你将如何处理这个问题?使用您的代码,我从每组中获得 10 个样本,但这会产生 70k 个样本
    • 这是一个不同的问题,因为您没有对每组进行随机抽样。你可以做什么:使用我的方法对每组所需的 10 个进行抽样。比对其余所有数据进行随机抽样并填写多达 10k 条记录。
    • 当我使用你的方法时,我得到了 70k 个样本。我想将其减少到 10k,同时每组至少有 10 个剩余样本
    • 你可以只写例如 10 而不是 frac。然后你会从每组中收到 10 个样本。您能否创建一个新问题并链接到这个问题?
    • 如果你想在这个命令之后有一个普通的DataFrame(而不是MultiIndex),执行:df_test = df_stratified.droplevel(level=0)。然后您可以使用索引来拆分火车:df_train = df[~df.index.isin(df_test.index)]
    【解决方案2】:

    我不完全确定你是不是这个意思:

    strats = []
    for k in range(11):
        y_val = k*0.1
        dummy_df = your_df[your_df['Y'] == y_val]
        stats.append( dummy_df.sample(200) )
    

    这会生成一个仅包含您想要的 Y 值的虚拟数据帧,然后抽取 200 个样本。

    好的,因此您需要不同的块具有相同的结构。我想这有点难,我会这样做:

    首先,我会得到X1 的直方图:

    hist, edges = np.histogram(your_df['X1'], bins=np.linespace(min_x, max_x, nbins))
    

    我们现在有一个带有nbins bins 的直方图。

    现在的策略是根据X1 的值绘制一定数量的行。我们会从观测值多的bin中抽取更多,从观测较少的bin中抽取更多,这样X的结构就被保留了。

    具体来说,每个 bin 的相对贡献应该是:

    rel = [float(i) / sum(hist) for i in hist]
    

    这将类似于[0.1, 0.2, 0.1, 0.3, 0.3]

    如果我们想要 200 个样本,我们需要绘制:

    draws_in_bin = [int(i*200) for i in rel]
    

    现在我们知道要从每个 bin 中提取多少观察值:

    strats = []
    for k in range(11):
            y_val = k*0.1
    
            #get a dataframe for every value of Y
            dummy_df = your_df[your_df['Y'] == y_val]
    
            bin_strat = []
            for left_edge, right_edge, n_draws in zip(edges[:-1], edges[1:], draws_in_bin):
    
                 bin_df = dummy_df[ (dummy_df['X1']> left_edge) 
                                  & (dummy_df['X1']< right_edge) ]
    
                 bin_strat.append(bin_df.sample(n_draws))
                 # this takes the right number of draws out 
                 # of the X1 bin where we currently are
                 # Note that every element of bin_strat is a dataframe
                 # with a number of entries that corresponds to the 
                 # structure of draws_in_bin
            #
            #concatenate the dataframes for every bin and append to the list
            strats.append( pd.concat(bin_strat) )
    

    【讨论】:

    • 好的,这会将 DataFrame 分成 11 个折叠,并以随机方式填充每个折叠 200 行。这是我的目标之一。第二个是将这些折叠分层,例如X1 在每个折叠中将具有大致相同的结构。
    • 其实我想这就是我所需要的。我会放宽分层的约束,使用随机样本。
    • 啊!只是回答了我将如何去做。如果您有兴趣,请看一看。它不是很直接或优雅,但我想它应该可以工作。
    • 我喜欢你如何解决分层的想法。很有用。谢谢!
    • 一般情况下,如果X1Y不相关,应该随机抽取分层结果。虽然只有 200 个样本,但您可能会观察到差异。
    猜你喜欢
    • 2020-09-01
    • 2017-10-22
    • 2019-03-31
    • 1970-01-01
    • 2019-10-04
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    • 1970-01-01
    相关资源
    最近更新 更多