python 1:1 每组分层抽样
python 1:1 stratified sampling per each group
如何在 python 中执行 1:1 分层抽样?
假设 Pandas 数据框 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
与
的单个匹配项
来自 scikit-learn 的 StratifiedShuffleSplit
只会 return 数据的随机部分,而不是 1:1 匹配。
如果我理解正确的话你可以使用 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
的每个可能 sub_category 组合的大小小于 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'])])
带有选择的版本与带有排列的版本没有相同的假设,尽管它要求每个子类别组合至少有一个元素。
如何在 python 中执行 1:1 分层抽样?
假设 Pandas 数据框 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
与
来自 scikit-learn 的 StratifiedShuffleSplit
只会 return 数据的随机部分,而不是 1:1 匹配。
如果我理解正确的话你可以使用 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
的每个可能 sub_category 组合的大小小于 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'])])
带有选择的版本与带有排列的版本没有相同的假设,尽管它要求每个子类别组合至少有一个元素。