Pandas的分层抽样

irtuqstp  于 2022-11-20  发布在  其他
关注(0)|答案(5)|浏览(215)

我已经查看了Sklearn stratified sampling docspandas docs以及Stratified samples from Pandassklearn stratified sampling based on a column,但它们都没有解决这个问题。
我正在寻找一种快速的pandas/sklearn/numpy方法来从数据集中生成大小为n的分层样本。但是,对于少于指定采样数的行,它应该取所有的条目。
具体例子:

谢谢你们!

hmtdttj4

hmtdttj41#

当传递数字到sample时使用min。考虑 Dataframe 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
nzkunb0c

nzkunb0c2#

扩展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_
sc4hvdpw

sc4hvdpw3#

下面的示例总共有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)
pu3pd22g

pu3pd22g4#

所以我尝试了上面所有的方法,它们仍然不太是我想要的(会解释为什么)。

步骤1:是的,我们需要groupby这个目标变量,让我们称之为target_variable。所以代码的第一部分看起来像这样:
df.groupby('target_variable', group_keys=False)

我设置了group_keys=False,因为我没有尝试将索引继承到输出中。

步骤2:使用applytarget_variable内的各种类中进行采样。

这就是我发现上面的答案不太普遍的地方。在我的例子中,这是我在df中的标签编号:

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

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

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

这就是@piRSquared答案所缺少的。然后你要在类编号的min(这里是799)和每个类的编号之间进行选择。这不是一个通用规则,你可以选择其他编号。例如:

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

它将给予最小类的max与每个类的数量的比较。
他们回答中的另一个技术问题是,建议您在采样后对输出进行洗牌。就像您不希望所有S1样本都在连续的行中,那么S2,你要确保你的行是随机堆叠的,这时sample(frac=1)就进来了,值1是因为我想在洗牌后返回所有的数据。如果您出于任何原因需要更少的样本,请随意提供像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。请根据需要随意修改。

sdnqo3pr

sdnqo3pr5#

根据使用者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

相关问题