Давайте рассмотрим распространенную проблему с пропущенными данными и способы ее решения.
Иногда, когда мы анализируем данные, некоторые значения могут быть утеряны. Обычно пропущенные данные обозначаются в виде вопросительных знаков, нулей или пустых ячеек. Например, в таблице нормализованных потерь присутствует пропущенное значение, представленное как NaN.
Кстати, подписывайтесь на наши социальные сети, мы туда еженедельно публикуем топовый контент! Вот наш канал в телеграм и группа ВК.
Таблица 1: Пример набора данных о машинах
Марка |
Модель |
Год выпуска |
Цена |
Нормализованные потери |
Тип топлива |
Toyota |
Corolla |
2019 |
15000 |
134 |
Бензин |
Honda |
Civic |
2020 |
17000 |
NaN |
Дизель |
Ford |
Mustang |
2018 |
25000 |
235 |
NaN |
Subaru |
Outback |
2017 |
NaN |
178 |
Бензин |
Hyundai |
Elantra |
2021 |
18000 |
156 |
Гибрид |
Nissan |
Sentra |
2019 |
15500 |
170 |
Бензин |
Давайте сначала создадим датафрейм для примера:
import pandas as pd
# Создание DataFrame
data = {
'Марка': ['Toyota', 'Honda', 'Ford', 'Subaru', 'Hyundai', 'Nissan'],
'Модель': ['Corolla', 'Civic', 'Mustang', 'Outback', 'Elantra', 'Sentra'],
'Год выпуска': [2019, 2020, 2018, 2017, 2021, 2019],
'Цена': [15000, 17000, 25000, None, 18000, 15500],
'Нормализованные потери': [134, None, 235, 178, 156, 170],
'Тип топлива': ['Бензин', 'Дизель', None, 'Бензин', 'Гибрид', 'Бензин']
}
df = pd.DataFrame(data)
Как обработать пропущенные данные?
Каждая ситуация уникальна, и подход к ней должен быть индивидуальным. Давайте начнем с восстановления данных.
1 Способ: Восстановление данных
Самый лучший способ, конечно, восстановить данные, если у вас есть доступ к дополнительной информации. Этот способ обеспечивает наиболее точные результаты.
Например, если вам известно, что пропущенное значение нормализованных потерь для Honda Civic составляет 150, то вы можете его восстановить.
Сделать это можно так:
#Заполнение пропущенных значений на основе дополнительной информации
df.loc[df['Модель'] == 'Outback', 'Цена'] = 17625
df.loc[df['Модель'] == 'Civic', 'Потери'] = 150
df.loc[df['Модель'] == 'Mustang', 'Тип топлива'] = 'Дизель'
Таблица 2: Набор данных с заполненными пропущенными значениями
Марка |
Модель |
Год выпуска |
Цена |
Нормализованные потери |
Тип топлива |
Toyota |
Corolla |
2019 |
15000 |
134 |
Бензин |
Honda |
Civic |
2020 |
17000 |
150 |
Дизель |
Ford |
Mustang |
2018 |
25000 |
235 |
Дизель |
Subaru |
Outback |
2017 |
17625 |
178 |
Бензин |
Hyundai |
Elantra |
2021 |
18000 |
156 |
Гибрид |
Nissan |
Sentra |
2019 |
15500 |
170 |
Бензин |
2 Способ: Удаление данных
Но, к сожалению, не всегда данные можно восстановить из источника.
Тогда можно попробовать просто удалить строки или столбцы с пропущенными данными. Если вы анализируете цену и другие атрибуты вам не важны, то отсутствие информации о цене может повлиять на ваши выводы (например, при расчете среднего). В этом случае лучше удалить строки с отсутствующей ценой, чтобы не включать их в расчет.
Это способ будет актуальным, если у вас много данных и небольшое количество пропусков.
# Удаление строк с пропущенными значениями в столбце "Цена"
df = df.dropna(subset=['Цена'])
Таблица 3: Набор данных с удаленными строками, содержащими пропущенные значения
Марка |
Модель |
Год выпуска |
Цена |
Нормализованные потери |
Тип топлива |
Toyota |
Corolla |
2019 |
15000 |
134 |
Бензин |
Honda |
Civic |
2020 |
17000 |
NAN |
Дизель |
Ford |
Mustang |
2018 |
25000 |
235 |
NAN |
Hyundai |
Elantra |
2021 |
18000 |
156 |
Гибрид |
Nissan |
Sentra |
2019 |
15500 |
170 |
Бензин |
3 Способ: замена пропущенных значений средним
Еще один из стандартных методов - замена пропущенных значений средним значением по переменной.
Заменять данные часто лучше, чем удалять, так как информация не теряется. Но вы должны делать это осознанно и помнить, что результат будет менее точным, ведь нам нужно заменить недостающие данные предположением о том, какими они должны быть.
# Замена пропущенных значений средними значениями
df['Цена'].fillna(df['Цена'].mean(), inplace=True)
df['Нормализованные потери'].fillna(df['Нормализованные потери'].mean(), inplace=True)
Таблица 4: Набор данных с замененными пропущенными значениями средними значениями
Марка |
Модель |
Год выпуска |
Цена |
Нормализованные потери |
Тип топлива |
Toyota |
Corolla |
2019 |
15000 |
134 |
Бензин |
Honda |
Civic |
2020 |
17000 |
174.6 |
Дизель |
Ford |
Mustang |
2018 |
25000 |
235 |
NAN |
Subaru |
Outback |
2017 |
18100 |
178 |
Бензин |
Hyundai |
Elantra |
2021 |
18000 |
156 |
Гибрид |
Nissan |
Sentra |
2019 |
15500 |
170 |
Бензин |
На самом деле, хитростей много. И это не единственный практический пример. Например, в Симуляторе «Аналитик данных» от Simulative мы разбираем просто кучу реальных кейсов.
4 Способ: Оставить все, как есть
Но что, если эти значения нельзя усреднить, как в случае с категориальными переменными?
Для такой переменной, как тип топлива, не существует среднего типа топлива, поскольку значения переменных не являются числами. В этом случае можно попробовать использовать наиболее часто встречающееся значение (моду).
# Замена пропущенных значений в столбце "Тип топлива" на наиболее часто встречающееся значение
mode_fuel_type = df['Тип топлива'].mode()[0] # Вычисление моды (наиболее часто встречающегося значения)
df['Тип топлива'].fillna(mode_fuel_type, inplace=True)
Таблица 5: Набор данных с заменой пропущенных значений в столбце "Тип топлива"
Марка |
Модель |
Год выпуска |
Цена |
Нормализованные потери |
Тип топлива |
Toyota |
Corolla |
2019 |
15000 |
134 |
Бензин |
Honda |
Civic |
2020 |
17000 |
174.6 |
Дизель |
Ford |
Mustang |
2018 |
25000 |
235 |
Бензин |
Subaru |
Outback |
2017 |
18100 |
178 |
Бензин |
Hyundai |
Elantra |
2021 |
18000 |
156 |
Гибрид |
Nissan |
Sentra |
2019 |
15500 |
170 |
Бензин |
В этом примере мы заменили пропущенные значения в столбце "Тип топлива" на наиболее часто встречающееся значение, которое в данном случае является "Бензин".
5. Иногда можно использовать дополнительные знания о данных для заполнения пропусков. Например, если известно, что пропущенные значения связаны со старыми автомобилями, и потери у старых автомобилей выше, чем у современных, то можно заполнить пропуски на основе этой информации.
6. В некоторых случаях оставление пропущенных данных без изменений тоже может быть полезным.
Если пропущенные значения возникли случайно и не имеют системного характера, их можно оставить, если они не влияют на анализ.
Если данные собраны из разных источников, пропуски могут отражать различия в доступности информации. Замена или удаление может исказить реальное положение вещей.
Оставление пропущенных данных может служить сигналом о недостоверности данных, что может стимулировать дополнительный анализ.
Замена пропущенных значений может исказить структуру данных. Например, замена средним значением может создать ложное представление о распределении.
В некоторых случаях пропущенные значения важны для контекста данных, особенно в анализе текста или текстовых данных.
Замена пропущенных значений может потребовать дополнительных вычислений и ресурсов, а оставление данных без изменений может быть более эффективным.
Но при оставлении пропущенных данных важно понимать, как это влияет на анализ, и иногда требуется дополнительное исследование влияния пропусков на результаты анализа.
Конечно, каждая ситуация уникальна, и к ней следует относиться по-разному. Тем не менее, мы рассмотрели самые типичные варианты решения проблемы пропущенных данных, пользуйтесь!
sunsexsurf
Вы почти не раскрыли (может, опосредованно об этом было сказано в п.5) идею того, что пропуски можно заполнять усредненным значением по кластеру: т.е. сперва вы разбиваете датасет на кластеры, потом выбираете, каким средним пользоваться (обычное, усеченное, медиана или даже мода) и им заполняете. Тогда вы сможете немного побороться с тем, что посчитаете это усреднение не по всему датасету, а только по тем соседям, которые к этому кластеру относятся. И вот эта идея - часто гораздо более аккаратная по отношению к пропускам, нежели бездумное
.fillna()
по всем данным.