繁体   English   中英

Pandas 分层抽样

[英]Stratified Sampling in Pandas

我查看了Sklearn 分层抽样文档以及pandas 文档以及来自 Pandas 的分层样本基于列的 sklearn 分层抽样,但它们没有解决这个问题。

我正在寻找一种快速的 pandas/sklearn/numpy 方法来从数据集中生成大小为 n 的分层样本。 但是,对于小于指定采样数的行,它应该取所有条目。

具体例子:

在此处输入图像描述

谢谢: :)

将数字传递给样本时,请使用min 考虑数据帧df

df = pd.DataFrame(dict(
        A=[1, 1, 1, 2, 2, 2, 2, 3, 4, 4],
        B=range(10)
    ))

df.groupby('A', group_keys=False).apply(lambda x: x.sample(min(len(x), 2)))

   A  B
1  1  1
2  1  2
3  2  3
6  2  6
7  3  7
9  4  9
8  4  8

扩展groupby答案,我们可以确保样本是均衡的。 为此,当所有类的样本数> = n_samples ,我们可以为所有类取n_samples (先前的答案)。 当少数类包含< n_samples ,我们可以使所有类的样本数与少数类相同。

def stratified_sample_df(df, col, n_samples):
    n = min(n_samples, df[col].value_counts().min())
    df_ = df.groupby(col).apply(lambda x: x.sample(n))
    df_.index = df_.index.droplevel(0)
    return df_

所以我尝试了上面的所有方法,但它们仍然不是我想要的(将解释原因)。

第 1 步:是的,我们需要对目标变量进行groupby ,我们称之为target_variable 所以代码的第一部分将如下所示:
df.groupby('target_variable', group_keys=False)

我正在设置group_keys=False ,因为我不想将索引继承到 output。

第 2 步:使用applytarget_variable中的各个类中采样。

这是我发现上述答案不太普遍的地方。 在我的示例中,这就是我在df中拥有的 label 数字:

array(['S1','S2','normal'], dtype=object),
array([799, 2498,3716391])

所以你可以看到我的target_variable有多不平衡。 我需要做的是确保我将S1标签的数量作为每个 class 的最小样本数。

min(np.unique(df['target_variable'], return_counts=True))

这就是@piRSquared答案所缺乏的。 那么你要在class个数中选择min的,这里是799 ,每个class的个数。这个不是一般规律,可以取其他数。 例如:

max(len(x), min(np.unique(data_use['snd_class'], return_counts=True)[1])

与每个 class 的数量相比,这将为您提供最小的 class 的max

他们回答中的另一个技术问题是,建议您在采样后洗牌 output。 正如您不希望连续行中的所有S1样本,然后是S2 ,依此类推。 你想确保你的行是随机堆叠的。 也就是sample(frac=1)进来的时候,取1是因为我想把shuffle后的数据全部返回。 如果您出于任何原因需要更少,请随意提供一个小数,例如0.6 ,这将返回原始样本的 60%,洗牌。

第 3 步:最后一行对我来说是这样的:
df.groupby('target_variable', group_keys=False).apply(lambda x: x.sample(min(len(x), min(np.unique(df['target_variable'], return_counts=True)[1]))).sample(frac=1))

我在np.unique(df['target_variable]. return_counts=True)[1]中选择索引 1,因为这适合将每个类的编号作为numpy array 欢迎酌情修改。

以下示例总共有N行,其中每个组以其原始比例出现在最接近的整数处,然后使用以下命令重新排列并重置索引:

df = pd.DataFrame(dict(
    A=[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 4, 4, 4],
    B=range(20)
))

简短而甜美:

df.sample(n=N, weights='A', random_state=1).reset_index(drop=True)

长版

df.groupby('A', group_keys=False).apply(lambda x: x.sample(int(np.rint(N*len(x)/len(df))))).sample(frac=1).reset_index(drop=True)

根据用户piRSquared的回复,我们可能有:

import pandas as pd


def stratified_sample(df: pd.DataFrame, groupby_column: str, sampling_rate: float = 0.01) -> pd.DataFrame:
    assert 0.0 < sampling_rate <= 1.0
    assert groupby_column in df.columns

    num_rows = int((df.shape[0] * sampling_rate) // 1)
    num_classes = len(df[groupby_column].unique())
    num_rows_per_class = int(max(1, ((num_rows / num_classes) // 1)))
    df_sample = df.groupby(groupby_column, group_keys=False).apply(lambda x: x.sample(min(len(x), num_rows_per_class)))

    return df_sample

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM