[英]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_
所以我尝试了上面的所有方法,但它们仍然不是我想要的(将解释原因)。
groupby
,我们称之为target_variable
。 所以代码的第一部分将如下所示:df.groupby('target_variable', group_keys=False)
我正在设置group_keys=False
,因为我不想将索引继承到 output。
apply
从target_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%,洗牌。
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.