Как использовать LabelBinarizer для одного горячего кодирования и обучения и тестирования правильно - PullRequest
2 голосов
/ 10 января 2020

Предположим, у меня есть такой набор поездов:

Name   | day
------------
First  |  0
Second |  1
Third  |  1
Forth  |  2

И набор тестов, который не содержит всех этих имен или дней. Вот так:

Name   | day
------------
First  |  2
Second |  1
Forth  |  0

У меня есть следующий код для преобразования этих столбцов в закодированные объекты:

features_to_encode = ['Name', 'day']
label_final = pd.DataFrame()

for feature in features_to_encode:
    label_campaign = LabelBinarizer()
    label_results = label_campaign.fit_transform(df[feature])
    label_results = pd.DataFrame(label_results, columns=label_campaign.classes_)
    label_final = pd.concat([label_final, label_results], axis=1)

df_encoded = label_final.join(df)

Для получения следующего вывода в поезде ( это прекрасно работает ):

First  | Second  | Third  | Forth | 0 | 1 | 2 | 
-----------------------------------------------
  1    |    0    |   0    |   0   | 1 | 0 | 0 |
  0    |    1    |   0    |   0   | 0 | 1 | 0 |
  0    |    0    |   1    |   0   | 0 | 1 | 0 |
  0    |    0    |   0    |   1   | 0 | 0 | 1 |

Однако, когда я запускаю это на тестовых данных (новые данные), я получаю несоответствующих функций, если тестовые данные не содержат точно все те же имена и дни, что и данные поезда . Поэтому, если бы я запустил подобный код в этом тестовом образце, я бы получил:

First  | Second  | Forth | 0 | 1 | 2 | 
--------------------------------------
  1    |    0    |   0   | 0 | 0 | 1 |
  0    |    1    |   0   | 0 | 1 | 0 |
  0    |    0    |   1   | 1 | 0 | 0 |

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

First  | Second  | Third  | Forth | 0 | 1 | 2 | 
-----------------------------------------------
  1    |    0    |   0    |   0   | 0 | 0 | 1 |
  0    |    1    |   0    |   0   | 0 | 1 | 0 |
  0    |    0    |   0    |   1   | 1 | 0 | 0 |

Я уже пытался добавить dict, чтобы перехватить результаты fit_transform, но я не уверен, сработает ли это или что с этим делать потом:

features_to_encode = ['Name', 'day']
label_final = pd.DataFrame()

labels = {}--------------------------------------------------------------------> TRIED THIS
for feature in features_to_encode:
    label_campaign = LabelBinarizer()
    label_results = label_campaign.fit_transform(df[feature])
    labels[feature] = label_results--------------------------------------------> WITH THIS
    label_results = pd.DataFrame(label_results, columns=label_campaign.classes_)
    label_final = pd.concat([label_final, label_results], axis=1)

df_encoded = label_final.join(df)

Любая помощь приветствуется. Спасибо =)

Ответы [ 3 ]

1 голос
/ 10 января 2020

pd.CategoricalDtype и pd.get_dummies

names_cat = pd.CategoricalDtype(['First', 'Second', 'Third', 'Forth'])
days_cat = pd.CategoricalDtype([0, 1, 2, 3, 4])

dumb_names = pd.get_dummies(df.Name.astype(names_cat))
dumb_names.columns = dumb_names.columns.to_numpy()

dumb_days = pd.get_dummies(df.day.astype(days_cat))
dumb_days.columns = dumb_days.columns.to_numpy()

   First  Second  Third  Forth  0  1  2  3  4
0      1       0      0      0  0  0  1  0  0
1      0       1      0      0  0  1  0  0  0
2      0       0      0      1  1  0  0  0  0

LabelBinarizer.classes_

from sklearn.preprocessing import LabelBinarizer

lb_0 = LabelBinarizer()
lb_1 = LabelBinarizer()

lb_0.classes_ = ['First', 'Second', 'Third', 'Forth']
lb_1.classes_ = [0, 1, 2, 3, 4]

a = lb_0.transform(df.Name)
b = lb_1.transform(df.day)

data = np.column_stack([a, b])
idx = df.index
col = np.concatenate([lb_0.classes_, lb_1.classes_])

result = pd.DataFrame(data, idx, col)
result

   First  Second  Third  Forth  0  1  2  3  4
0      1       0      0      0  0  0  1  0  0
1      0       1      0      0  0  1  0  0  0
2      0       0      0      1  1  0  0  0  0

reindex

cols = ['First', 'Second', 'Third', 'Forth', 0, 1, 2]
result = pd.concat(map(pd.get_dummies, map(df.get, df)), axis=1)
result.reindex(columns=cols, fill_value=0)

   First  Second  Third  Forth  0  1  2
0      1       0      0      0  0  0  1
1      0       1      0      0  0  1  0
2      0       0      0      1  1  0  0
1 голос
/ 11 января 2020

Другой подход, возможно, лучше подходит, если у вас есть общие значения для разных переменных и если вы планируете автоматизировать код для нескольких столбцов для кодирования:

df_train = pd.DataFrame({'Name': ['First', 'Second', 'Third', 'Fourth'], 'Day': [2,1,1,2]})
df_test = pd.DataFrame({'Name': ['First', 'Second', 'Fourth'], 'Day': [2,1,0]})
categories = []

cols_to_encode = ['Name', 'Day']
# Union of all values in both training and testing datasets:
for col in cols_to_encode:
    categories.append(list(set(df_train[col].unique().tolist() + df_test[col].unique().tolist())))

# Sorts the class names under each variable    
for cat in categories:
    cat.sort()

for col_name, cat in zip(cols_to_encode, categories):
    df_test[col_name] =  pd.Categorical(df_test[col_name], categories=cat)
df_test = pd.get_dummies(df_test, columns=cols_to_encode)

df_test

>>

    Name_First  Name_Second Name_Third  Name_Fourth Day_0   Day_1   Day_2   Day_3   Day_4
0   1           0           0           0           0       0       1       0    0 
1   0           1           0           0           0       1       0       0    0
2   0           0           0           1           1       0       0       0    0

0 голосов
/ 10 января 2020

Нечто подобное должно работать. Я обычно работаю с фреймами данных до последнего раза, потому что с ними легче работать. X должен быть вашим тестовым фреймом непосредственно перед прогнозированием. original_cols должен быть списком столбцов ваших тренировочных наборов. Дайте мне знать, если это работает для вас.

def normalize_X(X, original_cols):


    missing_cols= set(original_cols) - set(X.columns)
    extra_cols= set(X.columns) - set(original_cols)
    if len(missing_cols)>0:

        print(f'missing columns: {", ".join(missing_cols)}')
        for col in (missing_cols):

            X[col] = 0
    if len(extra_cols)>0:

        print(f'Columns to drop: {", ".join(extra_cols)} ',)
        X = X.drop(columns = extra_cols)
    X = X[original_cols]
    return X
...