6 советов по работе с нулевыми значениями

Включает итерационный метод, среднее и медианное заполнение с групповым, средним и медианным заполнением

Нулевые значения — большая проблема в машинном обучении и глубоком обучении. Если вы используете sklearn, TensorFlow или любые другие пакеты машинного обучения или глубокого обучения, необходимо очищать нулевые значения перед передачей данных в среду машинного обучения или глубокого обучения. В противном случае это даст вам длинное и уродливое сообщение об ошибке.

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

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

import pandas as pd
import numpy as np
import seaborn as sns
titanic = sns.load_dataset("titanic")
titanic

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

titanic.isnull().sum()

Выход:

survived         0
pclass           0
sex              0
age            177
sibsp            0
parch            0
fare             0
embarked         2
class            0
who              0
adult_male       0
deck           688
embark_town      2
alive            0
alone            0
dtype: int64

В столбце age указано 177, а в столбце embark_town 2 нулевых значения. Но столбец колоды имеет наибольшее количество нулевых значений 688 из 891 строки данных. Я хотел бы полностью удалить этот столбец для машинного обучения или любого другого типа анализа данных.

Мы сосредоточимся на столбцах age и embark_town и разберемся с нулевыми значениями этих столбцов.

Давайте начнем!

Я начну с самой простой стратегии и постепенно перейду к более сложным трюкам.

1. Просто бросьте

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

titanic.dropna()

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

2. Заполнение нулями

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

titanic['age'].fillna(0)

Выход:

0      22.0
1      38.0
2      26.0
3      35.0
4      35.0
       ... 
886    27.0
887    19.0
888     0.0
889    26.0
890    32.0
Name: age, Length: 891, dtype: float64

Посмотрите на строку 888. Раньше она была нулевой, а теперь равна нулю. Это также очень наивный подход. Особенно в этом случае возраст не может быть равен нулю.

3. Заполнение вперед и назад

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

titanic['age'].ffill()

Выход:

0      22.0
1      38.0
2      26.0
3      35.0
4      35.0
       ... 
886    27.0
887    19.0
888    19.0
889    26.0
890    32.0
Name: age, Length: 891, dtype: float64

Обратите внимание, что строка 888 теперь равна 19, как и 887.

titanic['age'].bfill()

Выход:

0      22.0
1      38.0
2      26.0
3      35.0
4      35.0
       ... 
886    27.0
887    19.0
888    26.0
889    26.0
890    32.0
Name: age, Length: 891, dtype: float64

Эта строка 888 взяла значение из строки 889.

4. Среднее и медианное заполнение

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

titanic['age'].fillna(titanic['age'].median(), inplace = True)
titanic['age']

Выход:

0      22.0
1      38.0
2      26.0
3      35.0
4      35.0
       ... 
886    27.0
887    19.0
888    28.0
889    26.0
890    32.0
Name: age, Length: 891, dtype: float64

Теперь нулевое значение в строке 888 равно 28, что является медианой возраста столбца.

5. Среднее и медианное заполнение с помощью Groupby

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

Давайте посмотрим средний возраст «pclass» и «live», если они вообще различаются:

titanic.groupby(['pclass', 'alive'])['age'].mean()

Выход:

pclass  alive
1       no       43.695312
        yes      35.368197
2       no       33.544444
        yes      25.901566
3       no       26.555556
        yes      20.646118
Name: age, dtype: float64

Да, они сильно различаются.

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

titanic['age'].fillna(titanic.groupby(['pclass', 'sex'])['age'].transform('mean'))

Выход:

0      22.00
1      38.00
2      26.00
3      35.00
4      35.00
       ...  
886    27.00
887    19.00
888    21.75
889    26.00
890    32.00
Name: age, Length: 891, dtype: float64

Теперь строка 888 стала 21. По сравнению с 28

Вменение категориальных нулевых значений

Столбец embark_town является категориальным столбцом, и мы можем применить к нему тот же процесс, что и в предыдущем примере.

Но перед этим значения столбца embark_town нужно выразить числовыми значениями:

titanic['embark_town'] = titanic['embark_town'].astype('category')
titanic['embark_town'] = titanic['embark_town'].cat.codes
titanic['embark_town']

Выход:

0      2
1      0
2      2
3      2
4      2
      ..
886    2
887    2
888    2
889    0
890    1
Name: embark_town, Length: 891, dtype: int8

Я сгруппирую данные по «pclass» и «alive» и заполню нулевые значения медианой.

titanic['embark_town'] = titanic['embark_town'].fillna(titanic.groupby(['pclass', 'alive'])['embark_town'].transform('median'))

6. Итеративное вменение с помощью модели машинного обучения

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

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

Для этой демонстрации я использую только несколько столбцов. Помните, в начале мы проверили, какие столбцы имеют нулевые значения, и увидели, что столбцы age, embark_town и deck имеют нулевые значения.

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

Я использую RandomForestRegressor здесь. Вы можете использовать любую другую модель регрессии.

from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.ensemble import RandomForestRegressor
titanic1 = titanic[['survived', 'pclass', 'age', 'sibsp', 'fare', 'embark_town']]

Давайте сделаем вменение сейчас, и я сохраню вмененный набор данных как titanic2.

imptr = IterativeImputer(RandomForestRegressor(), max_iter=10, random_state=0)
titanic2 = pd.DataFrame(imptr.fit_transform(titanic1), columns = titanic1.columns)
titanic2

Возраст в строке 888 теперь 32 года!

Мы должны проверить, есть ли нулевые значения в наборе данных titanic2:

titanic2.isnull().sum()

Выход:

survived       0
pclass         0
age            0
sibsp          0
fare           0
deck           0
embark_town    0
dtype: int64

Ни в одном из столбцов больше нет нулевых значений.

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

Заключение

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

Пожалуйста, не стесняйтесь подписываться на меня в Twitter и Facebook page.

Подробнее Чтение