В большинстве случаев необработанные данные - плохой ввод, они содержат ценную информацию, но они скрыты от разработки хороших моделей машинного обучения до тех пор, пока они не будут преобразованы в форму, более удобную для использования, вот где в игру вступает сила разработки функций.
Разработка функций имеет фундаментальное значение для того, чтобы модели машинного обучения делали точные прогнозы, но также это трудоемкая задача, мы обычно посвящаем каждую часть нашего времени и большие усилия обработке переменных в нашем наборе данных, прежде чем мы сможем их использовать.

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

Я буду использовать три разных набора данных:

  • Набор данных "Титаник"
  • Набор данных о ценах на жилье
  • Бостонский набор данных

Вы также можете проверить этот GitHub. Я загрузил несколько полезных кодов

Https://github.com/MaissaDataScience

Итак, приступим! Принесите теплый напиток, откройте блокнот Jupyter и давайте КОД

Методы проектирования функций

· 1.Величина недостающих данных
· 2.Категория кодирования
· 3.Преобразование переменных
· 4.Дискретизация
· 5.Инжиниринг
· 6. Масштабирование характеристик
· 7. Инжиниринг смешанных переменных
· 8. Инжиниринг по времени

Прежде всего, вам нужно импортировать библиотеки Pandas, Numpy.

import pandas 
import numpy 

1. неверное вменение данных

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

data[['LotFrontage', 'MasVnrArea', 'GarageYrBlt']].isnull().mean()
LotFrontage    0.177397
MasVnrArea     0.005479
GarageYrBlt    0.055479
dtype: float64
def impute_na(df, variable, mean_median): 
   return df[variable].fillna(mean_median)

Среднее / медианное условное исчисление

Вменение среднего / медианного значения состоит из замены всех вхождений пропущенных значений (NA) в переменной средним (если переменная имеет гауссовское распределение) или медианой (если переменная имеет асимметричное распределение). его можно вычислить по непрерывным и дискретным числовым переменным

median = data['LotFrontage'].median()
data[:,'LotFrontage_median'] = impute_na(data, 'LotFrontage',median)
mean = data['MasVnrArea'].mean() 
data[:,'MasVnrArea_median'] = impute_na(data, 'MasVnrArea', median)

Вменение произвольной стоимости

Вменение произвольного значения состоит из замены всех вхождений пропущенных значений (NA) в переменной произвольным значением. Обычно используются произвольные значения: 0, 999, -999 (или другие комбинации девяток), -1 (если распределение положительное) или 1.

data['Age_99'] = impute_na(data, 'age', 99)
data['fare'] = impute_na(X_train, 'fare', 0)

Конец вменения распределения

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

  • Если переменная имеет нормальное распределение, мы можем использовать среднее значение плюс или минус 3-кратное стандартное отклонение.
  • Если переменная искажена, мы можем использовать правило близости IQR
# Because Age looks approximately Gaussian, I use the # mean and std to calculate the replacement value 
data.age.mean() + 3 *data.age.std()
data['Age_imputed'] = impute_na(data,'age',data.age.mean() + 3 * data.age.std())

#Calculate the IQR
IQR =data['LotFrontage'].quantile(0.75)-data['LotFrontage']
.quantile(0.25)
# calculate the upper boundary 
extreme_value =data['LotFrontage'].quantile(0.75) + 3 * IQRdata.loc[:,'LotFrontage_imputed'] = impute_na(data, 'LotFrontage', extreme_value)

Частое вменение категорий | Вменение режима

Вменение режима состоит в замене всех вхождений пропущенных значений (NA) в переменной на режим, который, другими словами, относится к наиболее частому значению или наиболее частой категории на практике. , мы используем эту технику только на категориальных

# Let's find the most frequent category for BsmtQual  data['BsmtQual'].mode()
0    TA dtype: object
data['BsmtQual'].fillna('TA', inplace=True)
0    TA dtype: object
data['BsmtQual'].fillna('TA', inplace=True)

2. категориальное кодирование

Одно горячее кодирование

Одно горячее кодирование состоит из кодирования каждой категориальной переменной различными логическими переменными (также называемыми фиктивными переменными), которые принимают значения 0 или 1, указывающие, присутствует ли категория в наблюдении.

Например, для категориальной переменной «Пол» с метками «женский» и «мужской» мы можем сгенерировать логическую переменную «женский», которая принимает 1, если человек «женский», или 0 в противном случае, или мы можем сгенерировать переменная «мужчина», которая принимает 1, если человек «мужчина», и 0 в противном случае.

#With sklearn
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(categories='auto',drop='first',sparse=False,                                              handle_unknown='ignore')
tmp = encoder.transform(data.fillna('Missing'))
#With get dummies
tmp = pd.get_dummies(data['sex'])

Одно горячее кодирование часто используемых категорий

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

Целочисленное кодирование

Целочисленное кодирование состоит из замены категорий цифрами от 1 до n (или от 0 до n-1, в зависимости от реализации), где n - количество различных категорий переменной.

#first let's create a dictionary with the mappings of categories to numbers
ordinal_mapping = {     k: i     for i, k in enumerate(X_train['Neighborhood'].unique(), 0) }
# replace the labels with the integers
data['Neighborhood'] = data['Neighborhood'].map(ordinal_mapping)

Подсчет или частотное кодирование

При кодировании счетчика мы заменяем категории числом наблюдений, которые показывают эту категорию в наборе данных. Точно так же мы можем заменить категорию частотой или процентом наблюдений в наборе данных. То есть, если 10 из наших 100 наблюдений показывают синий цвет, мы заменим синий на 10, если выполняется кодирование счетчика, или на 0,1, если заменено на частоту.

# replace the labels with the counts
count_map = data['Neighborhood'].value_counts().to_dict()
data['Neighborhood'] =data['Neighborhood'].map(count_map)
# replace the labels with the frequencies
frequency_map = (data['Exterior1st'].value_counts() / data) ).to_dict()
data['Exterior1st'] = data['Exterior1st'].map(frequency_map)

Кодирование с указанием цели

  • Упорядоченное целочисленное кодирование
  • Среднее кодирование
  • Кодировка вероятности
  • Кодировка весов доказательств

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

мы собираемся рассмотреть один из них - Среднее кодирование

Среднее кодирование подразумевает замену категории средним целевым значением для этой категории. Например, если у нас есть переменный город с категориями Лондон, Манчестер и Бристоль, и мы хотим спрогнозировать ставку по умолчанию, если ставка по умолчанию для Лондона составляет 30%, мы заменяем Лондон на 0,3, если ставка по умолчанию для Манчестера равна 20. % заменяем Манчестер на 0,2 и так далее.

data.groupby(['cabin'])['survived'].mean()
ordered_labels =data.groupby(['cabin'])['survived'].mean().to_dict()
data['cabin'] = data['cabin'].map(ordered_labels)

3. преобразование переменной

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

Если переменная не имеет нормального распределения, часто можно найти математическое преобразование для нормализации ее распределения.

Логарифмическое преобразование - np.log (X)

data['GrLivArea_log'] = np.log(data['GrLivArea'])

Взаимное преобразование - 1 / X

data['GrLivArea_reciprocal'] = 1 / (data['GrLivArea'])

Преобразование квадратного корня - X ** (1/2)

data['GrLivArea_sqr'] = data['GrLivArea']**(1/2)

Экспоненциальный

data['GrLivArea_exp'] = data['GrLivArea']**(1/1.5)

Преобразование Бокса-Кокса

Преобразование Бокса-Кокса определяется как:

T (Y) = (Y exp (λ) −1) / λ, если λ! = 0, или log (Y) в противном случае.

где Y - переменная отклика, а λ - параметр преобразования. λ изменяется от -5 до 5. Вкратце, для каждого λ (преобразование проверяет несколько λs) рассчитывается коэффициент корреляции графика вероятности (график QQ ниже, корреляция между упорядоченными значениями и теоретическими квантилями) (это уравнение оптимизации фактически изменяется с реализацией). Значение λ, соответствующее максимальной корреляции на графике, тогда является оптимальным выбором для λ.

import scipy.stats as stats
data['GrLivArea_boxcox'], param = stats.boxcox(data['GrLivArea'])

Преобразование Йео-Джонсона

Йео-Джонсон такой же, как Бокс-Кокс для положительных значений переменной, но у него другие уравнения для отрицательных значений переменной, как описано. Опять же, функция выполняет поиск по группе λ и выбирает тот, который возвращает лучшее. соответствует нормальному распределению.

data['Gr_yeojohnson'], param = stats.yeojohnson(data['GrLivArea'])

4. Дискретность

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

Существует несколько подходов к преобразованию непрерывных переменных в дискретные. Методы дискретизации делятся на 2 категории: контролируемые и неконтролируемые. Неконтролируемые методы не используют никакой информации, кроме распределения переменных, для создания непрерывных бункеров, в которые будут помещены значения. Контролируемые методы обычно используют целевую информацию для создания бинов или интервалов.

Дискретизация равной ширины

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

ширина = (максимальное значение - минимальное значение) / N, а N - количество бинов или интервалов.

# let's capture the range of the variable age
age_range = X_train['age'].max() - X_train['age'].min()
# let's divide the range into 10 equal width bins
age_range / 10
# now let's capture the lower and upper boundaries
min_value = int(np.floor( X_train['age'].min()))
max_value = int(np.ceil( X_train['age'].max()))
# let's round the bin width
inter_value = int(np.round(age_range / 10))
# let's capture the interval limits, so we can pass them to the pandas cut
# function to generate the bins
intervals = [i for i in range(min_value, max_value+inter_value, inter_value)]
# let's make labels to label the different bins
labels = ['Bin_' + str(i) for i in range(1, len(intervals))]
# create binned age / discretise age
# create one column with labels
data['Age_disc_labels'] = pd.cut(x=data['age'],bins=intervals, labels=labels, include_lowest=True)
# and one with bin boundaries
data['Age_disc'] = pd.cut(x=data['age'],bins=intervals include_lowest=True)

Дискретизация с кластеризацией k-средних

Этот метод дискретизации заключается в применении кластеризации k-средних к непрерывной переменной.

Вкратце алгоритм работает следующим образом:

  • 1) Инициализация: случайное создание K центров
  • 2) Каждая точка данных связана с ближайшим центром
  • 3) Каждая центральная позиция пересчитывается как центр связанных с ней точек.

Шаги 2 и 3 повторяются до достижения сходимости. Алгоритм минимизирует попарные квадраты отклонений точек в одном кластере.

from sklearn.preprocessing import KBinsDiscretizer
disc = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='kmeans')
disc.fit(data[['age', 'fare']])
data_t = disc.transform(data[['age', 'fare']])
data_t = pd.DataFrame(data_t, columns = ['age', 'fare'])

Дискретность с деревьями решений

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

5.Outlier Engineering

Выброс - это точка данных, которая значительно отличается от остальных данных.

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

Проверить выбросы

#Visualise Outlier with Boxplot
plt.subplot(1, 3, 3)
sns.boxplot(y=df[variable])
plt.title('Boxplot')
plt.show()

Обрезка

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

# Let's calculate the boundaries outside which sit the outliers
# for skewed distributions distance passed as an argument, gives us the option to estimate 1.5 times or 3 times the IQR to calculate
the boundaries.
def find_skewed_boundaries(df, variable, distance):
  IQR = df[variable].quantile(0.75) - df[variable].quantile(0.25)
  lower_boundary = df[variable].quantile(0.25) - (IQR * distance)
  upper_boundary = df[variable].quantile(0.75) + (IQR * distance)return upper_boundary, lower_boundary
# find limits for RM
RM_upper_limit, RM_lower_limit = find_skewed_boundaries(boston, 'RM', 1.5)
# let's flag the outliers in the data set
outliers_RM = np.where(boston['RM'] > RM_upper_limit, True, np.where(boston['RM'] < RM_lower_limit, True, False))
# let's trimm the dataset
boston_trimmed = boston.loc[~(outliers_RM + outliers_LSTAT + outliers_CRIM), ]

Цензура или ограничение

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

Ограничение правила близости IQR

def find_skewed_boundaries(df, variable, distance):
IQR = df[variable].quantile(0.75) - df[variable].quantile(0.25)      lower_boundary = df[variable].quantile(0.25) - (IQR * distance)
upper_boundary = df[variable].quantile(0.75) + (IQR * distance)
return upper_boundary, lower_boundary
# find limits for RM
RM_upper_limit, RM_lower_limit = find_skewed_boundaries(boston, 'RM', 1.5)
# let's flag the outliers in the data set
outliers_RM = np.where(boston['RM'] > RM_upper_limit, True, np.where(boston['RM'] < RM_lower_limit, True, False))
# Now let's replace the outliers by the maximum and minimum limit
boston['RM']= np.where(boston['RM'] > RM_upper_limit, RM_upper_limit, np.where(boston['RM'] < RM_lower_limit, RM_lower_limit, boston['RM']))

Ограничение гауссовского приближения

# calculate the boundaries outside which sit the outliers
# for a Gaussian distribution
def find_normal_boundaries(df, variable, distance):
upper_boundary = df[variable].mean() + distance * df[variable].std()
lower_boundary = df[variable].mean() - distance * df[variable].std()
return upper_boundary, lower_boundary

# find limits for RM
RM_upper_limit, RM_lower_limit = find_skewed_boundaries(boston, 'RM', 1.5)
RM_upper_limit, RM_lower_limit

# Now let's replace the outliers by the maximum and minimum limitboston['RM']= np.where(boston['RM'] > RM_upper_limit, RM_upper_limit,np.where(boston['RM'] < RM_lower_limit, RM_lower_limit, boston['RM']))

Ограничение квантилей

# the boundaries are the quantiles
def find_boundaries(df, variable):
 lower_boundary = df[variable].quantile(0.05)
 upper_boundary = df[variable].quantile(0.95)
return upper_boundary, lower_boundary
# find limits for RM
RM_upper_limit, RM_lower_limit = find_boundaries(boston, 'RM')
# Now let's replace the outliers by the maximum and minimum limit
boston['RM']= np.where(boston['RM'] > RM_upper_limit, RM_upper_limit, np.where(boston['RM'] < RM_lower_limit, RM_lower_limit, boston['RM']))

6. масштабирование характеристик

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

Стандартизация

Стандартизация включает центрирование переменной на нуле и стандартизацию дисперсии до 1. Процедура включает вычитание среднего значения каждого наблюдения и последующее деление на стандартное отклонение:

z = (x - x_mean) / стандартное

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

Средняя нормализация

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

x_scaled = (x - x_mean) / (x_max - x_min)

from sklearn.preprocessing import StandardScaler ,  RobustScaler
scaler_mean = StandardScaler(with_mean=True, with_std=False)
# set up the robustscaler so that it does NOT remove the median
# but normalises by max()-min(), important for this to set up the
# quantile range to 0 and 100, which represent the min and max values
scaler_minmax = RobustScaler(with_centering=False,with_scaling=True, quantile_range=(0, 100))
scaler_mean.fit(X_train)
scaler_minmax.fit(X_train)
X_train_scaled=scaler_minmax.transform(scaler_mean.transform(X_train))
X_test_scaled=scaler_minmax.transform(scaler_mean.transform(X_test))

Минимальное и максимальное масштабирование сжимает значения от 0 до 1, но среднее значение не центрируется на нуле, а стандартное отклонение варьируется для разных переменных. Он вычитает минимальное значение из всех наблюдений, а затем делит его на диапазон значений:

X_scaled = (X - X.min) / (X.max - X.min)

from sklearn.preprocessing import StandardScaler
scaler_mean = StandardScaler(with_mean=True, with_std=False)
# set up the robustscaler so that it does NOT remove the median
# but normalises by max()-min(), important for this to set up the
# quantile range to 0 and 100, which represent the min and max values
scaler_minmax = RobustScaler(with_centering=False,with_scaling=True, quantile_range=(0, 100))
scaler_mean.fit(X_train)
scaler_minmax.fit(X_train)
X_train_scaled=scaler_minmax.transform(scaler_mean.transform(X_train))
X_test_scaled=scaler_minmax.transform(scaler_mean.transform(X_test))

Масштабирование до квантилей и медианы - RobustScaling

В этой процедуре медиана удаляется из наблюдений, а затем они масштабируются до межквантильного диапазона (IQR). IQR - это диапазон между 1-м квартилем (25-й квантиль) и 3-м квартилем (75-й квантиль).

X_scaled = X - X_median / (X. квантиль (0,75) - X. квантиль (0,25))

from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

7.Инженерия смешанных переменных

Смешанные переменные - это переменные, значения которых содержат как числа, так и метки. Смешанная переменная может содержать числа или метки в разных наблюдениях или числа и метки в каждом наблюдении.

Например, переменная регистрация транспортного средства представляет собой пример букв и цифр, объединенных в каждом наблюдении (например, NK11DGX).

Наблюдения за переменной содержат числа или строки

data['open_il_24m'].unique()
array(['C', 'A', 'B', '0.0', '1.0', '2.0', '4.0', '3.0', '6.0', '5.0',        '9.0', '7.0', '8.0', '13.0', '10.0', '19.0', '11.0', '12.0',        '14.0', '15.0'], dtype=object)
# extract numerical part
data['open_il_24m_numerical'] =   pd.to_numeric(data["open_il_24m"],errors='coerce',downcast='integer' )
# extract categorical part
data['open_il_24m_categorical']=np.where(data['open_il_24m_numerical'].isnull(),data['open_il_24m'],np.nan)

Наблюдения за переменной содержат числа и строки

data['cabin'].unique()

array(['B5', 'C22', 'E12', 'D7', 'A36', 'C101', nan, 'C62', 'B35', 'A23','B58', 'D15', 'C6', 'D35', 'C148', 'C97', 'B49', 'C99', 'C52', 'T','A31', 'C7', 'C103', 'D22', 'E33', 'A21', 'B10', 'B4', 'E40',        'B38', 'E24', 'B51', 'B96', 'C46', 'E31', 'E8', 'B61', 'B77', 'A9',        'C89', 'A14', 'E58', 'E49', 'E52', 'E45', 'B22', 'B26', 'C85',        'E17', 'B71', 'B20', 'A34', 'C86', 'A16', 'A20', 'A18', 'C54',        'C45', 'D20', 'A29', 'C95', 'E25', 'C111', 'C23', 'E36', 'D34',])
# let's extract the numerical and categorical part for cabin
data['cabin_num'] = data['cabin'].str.extract('(\d+)') 
data['cabin_cat'] = data['cabin'].str[0] # captures the first letter

8.Инженерная инженерия

Сроки разработки

Переменные даты - это особый тип категориальной переменной. По своей природе переменные даты содержат множество различных меток, каждая из которых соответствует определенной дате, а иногда и времени. Переменные даты при правильной предварительной обработке могут значительно обогатить набор данных. Например, из переменной даты мы можем извлечь: День, Месяц, Год, Неделя, Семестр…

import datetime
# let's parse the dates, currently cast as strings, into datetime format
data['issue_dt'] = pd.to_datetime(data.issue_d)
# Extracting week of year from date, varies from 1 to 52
data['issue_dt_week'] = data['issue_dt'].dt.week
#Extracting month from date - 1 to 12
data['issue_dt_month'] = data['issue_dt'].dt.month

Инженерное время

Инженерное время - это извлечение различных способов представления времени из метки времени, мы можем извлечь, например, час, минуту, секунду, прошедшее время ...

import datetime
df['hour'] = df['date'].dt.hour
df['min'] = df['date'].dt.minute
df['sec'] = df['date'].dt.second

Справочные и дополнительные ресурсы для чтения:

Https://www.udemy.com/course/feature-engineering-for-machine-learning/

Https://towardsdatascience.com/smarter-ways-to-encode-categorical-data-for-machine-learning-part-1-of-3-6dca2f71b159

Https://towardsdatascience.com/6-different-ways-to-compensate-for-missing-values-data-imputation-with-examples-6022d9ca0779

Https://medium.com/@Cambridge_Spark/tutorial-introduction-to-missing-data-imputation-4912b51c34eb

Http://www.mtome.com/Publications/CiML/CiML-v3-book.pdf

Https://blog.zopa.com/2017/07/20/tips-honing-logistic-regression-models/

Https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1431352

Https://towardsdatascience.com/ways-to-detect-and-remove-the-outliers-404d16608dba

«Https://sebastianraschka.com/Articles/2014_about_feature_scaling.htm фактическиl

Https://scikitlearn.org/stable/auto_examples/preprocessing/plot_all_scalin.html#sphx-glr-auto-examples-preprocessing-plot-all-scaling-py