Даунсэмплинг для более чем 2 классов - PullRequest
0 голосов
/ 12 марта 2019

Я создаю простой код, который позволяет понижать частоту дискретизации, когда ваша целевая переменная имеет более 2 классов.

Пусть df будет нашим произвольным набором данных и 'TARGET_VAR' категориальной переменной с более чем 2 классами.

import pandas as pd
label='TARGET_VAR' #define the target variable

num_class=df[label].value_counts() #creates list with the count of each class value
temp=pd.DataFrame() #create empty dataframe to be filled up

for cl in num_class.index: #loop through classes
    #iteratively downsample every class according to the smallest
    #class 'min(num_class)' and append it to the dataframe.
    temp=temp.append(df[df[label]==cl].sample(min(num_class)))

df=temp #redefine initial dataframe as the subsample one

del temp, num_class #delete temporary dataframe

Теперь мне стало интересно, есть ли способ сделать это более изысканным способом? например без необходимости создания временного набора данных? Я попытался найти способ «векторизации» операции для нескольких классов, но ничего не получилось. Ниже моя идея, которую легко реализовать для 2 классов, но я понятия не имею, как расширить ее на случай нескольких классов.

Это отлично работает, если у вас есть 2 класса

 df= pd.concat([df[df[label]==num_class.idxmin()],\
 df[df[label]!=num_class.idxmin()].sample(min(num_class))])

Это позволяет вам выбрать правильное количество наблюдений для других классов, но классы не обязательно будут представлены одинаково.

 df1= pd.concat([df[df[label]==num_class.idxmin()],\
 df[df[label]!=num_class.idxmin()].sample(min(num_class)*(len(num_class)-1))])

Ответы [ 3 ]

3 голосов
/ 12 марта 2019

Вы можете попробовать что-то похожее на это:

label='TARGET_VAR'

g = df.groupby(label, group_keys=False)
balanced_df = pd.DataFrame(g.apply(lambda x: x.sample(g.size().min()))).reset_index(drop=True)

Я полагаю, что это даст желаемый результат, не стесняйтесь задавать любые дополнительные вопросы.

Редактировать

Исправлен код в соответствии с предложением ОП.

1 голос
/ 12 марта 2019

Ответ Густаво верен, но имеет небольшую проблему (и по какой-то причине я не могу отредактировать его ответ).

label='TARGET_VAR'

g = df.groupby(label, group_keys=False)
balanced_df = pd.DataFrame(g.apply(lambda x: 
x.sample(g.size().min()).
reset_index(drop=True)))

Здесь индекс будет сброшен для каждой группы, и итоговый кадр данных будет иметьповторяющиеся индексы строк.Если мы определим количество элементов в классе меньшинства как n:

idx, data 
0,   ...
1,   ...
.,   ...
.,   ...
.,   ...
n,   ...
0,   ...
1,   ...
.,   ...
.,   ...
.,   ...
n,   ...

Следующая настройка решит проблему

g = df.groupby(label, group_keys=False)
balanced_df = pd.DataFrame(g.apply(lambda x: 
x.sample(g.size().min()))).reset_index(drop=True)

Если мы теперь определим общее количество элементовbalanced_df как N=n*k, где k - количество разных классов.Индекс будет выглядеть так:

idx, data 
0,   ...
1,   ...
.,   ...
.,   ...
.,   ...
N,   ...
1 голос
/ 12 марта 2019

Этот код используется для oversampling экземпляров класса меньшинства или undersampling экземпляра класса большинства. Его следует использовать только на тренировочном наборе. Примечание: activity это метка

balanced_df=Pdf_train.groupby('activity',as_index = False,group_keys=False).apply(lambda s: s.sample(100,replace=True))
...