Питон 1: 1 стратифицированная выборка для каждой группы - PullRequest
0 голосов
/ 12 февраля 2019

Как можно сделать стратифицированную выборку 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 с.

StratifiedShuffleSplit из scikit-learn возвращает только случайную часть данных, а не 1: 1.

1 Ответ

0 голосов
/ 12 февраля 2019

Если я правильно понял, вы могли бы использовать 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 меньше, чем размер соответствующей подгруппы в 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'])])

Версия с выбором не имеет того же предположения, что и версия с перестановкой, хотя она требует как минимумодин элемент для каждой комбинации подкатегорий.

...