Обработка пропущенных значений в Pandas
Реальные данные часто содержат пропущенные значения (NaN). Pandas предоставляет богатый набор функций для обработки пропущенных данных. В этом разделе подробно рассматривается использование fillna, dropna, interpolate и других методов.
Представление пропущенных значений
В Pandas,NaN(Not a Number) представляет пропущенные значения, которые происходят из библиотеки NumPy.
Пример
import numpy as np
# Создать данные, содержащие пропущенные значения
s = pd.Series([1, 2, np.nan, 4, 5])
print("Series, содержащий NaN:")
print(s)
print(f"Есть пропущенные значения: {s.isna().any()}")
print()
# Пропущенные значения в DataFrame
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [np.nan, 2, 3, 4],
"C": [1, 2, 3, np.nan]
})
print("DataFrame, содержащий пропущенные значения:")
print(df)
print()
# Обнаружение пропущенных значений
print("Позиции пропущенных значений:")
print(df.isna())
print()
# Подсчет пропущенных значений по столбцам
print("Количество пропущенных значений по столбцам:")
print(df.isna().sum())
В Pandas,
NaN、None、pandas.NAвсе распознаются как пропущенные значения. Используяisna()илиisnull()может единообразно обнаруживать эти пропущенные значения.
dropna: Удаление пропущенных значений
Удаление строк
Пример
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [1, np.nan, 3, 4],
"C": [1, 2, 3, np.nan]
})
print("Исходные данные:")
print(df)
print()
# Удаление строк, содержащих пропущенные значения (по умолчанию)
print("Удаление строк с пропущенными значениями:")
print(df.dropna())
print()
# how='all': Удалять только если все значения пропущены
print("Удаляются только строки, в которых все значения пропущены:")
print(df.dropna(how="all"))
print()
# thresh: оставить не менее N не пропущенных значений
print("Сохранить строки, содержащие не менее 2 не пропущенных значений:")
print(df.dropna(thresh=2))
Удалить столбцы
Пример
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4],
"B": [np.nan, np.nan, np.nan, np.nan], # Все значения пропущены
"C": [1, 2, 3, 4]
})
print("Исходные данные:")
print(df)
print()
# Удалить столбцы, содержащие пропущенные значения
print("Удалить столбцы с пропущенными значениями:")
print(df.dropna(axis=1))
print()
# how='all': удалить столбцы, в которых все значения пропущены
print("Удалить столбцы, в которых все значения пропущены:")
print(df.dropna(axis=1, how="all"))
fillna: Заполнить пропущенные значения
Заполнение фиксированным значением
Пример
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [np.nan, 2, 3, np.nan, 5],
"C": [1, 2, 3, 4, np.nan]
})
print("Исходные данные:")
print(df)
print()
# Заполнить 0
print("Заполнение 0:")
print(df.fillna(0))
print()
# Заполнить разные столбцы указанными значениями
print("Разные столбцы, заполненные разными значениями:")
print(df.fillna({"A": 0, "B": 99, "C": -1}))
print()
# Заполнить предыдущим значением (прямое заполнение)
print("Прямое заполнение:")
print(df.fillna(method="ffill"))
print()
# Заполнить следующим значением (обратное заполнение)
print("Обратное заполнение:")
print(df.fillna(method="bfill"))
Заполнение статистическими значениями
Пример
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5, 6],
"B": [10, np.nan, 30, np.nan, 50, 60]
})
print("Исходные данные:")
print(df)
print()
# Заполнить средним значением
print("Заполнить средним:")
print(df.fillna(df.mean()))
print()
# Заполнить медианой
print("Заполнить медианой:")
print(df.fillna(df.median()))
print()
# Заполнить по столбцам соответственно
print("Столбец A использует среднее, столбец B использует 0:")
print(df.fillna({"A": df["A"].mean(), "B": 0}))
Заполнение вперёд и заполнение назад особенно полезны во временных рядах и позволяют поддерживать непрерывность данных. Какой метод выбрать, зависит от бизнес-смысла данных.
interpolate: Заполнение интерполяцией
Интерполяция — это более умный метод заполнения, который может выводить пропущенные значения из соседних данных.
Линейная интерполяция
Пример
import numpy as np
s = pd.Series([1, 2, np.nan, 4, 5, np.nan, 7])
print("Исходный ряд:")
print(s)
print()
# Линейная интерполяция (по умолчанию)
print("Линейная интерполяция:")
print(s.interpolate())
print()
# Укажите метод интерполяции
print("взвешенная по времени интерполяция (временной ряд):")
s2 = pd.Series([1, np.nan, np.nan, 4], index=pd.date_range("2024-01-01", periods=4, freq="D"))
print(s2.interpolate(method="time"))
print()
# Заполнить ближайшим значением
print("Заполнить ближайшим значением:")
print(s.interpolate(method="nearest"))
Интерполяция DataFrame
Пример
import numpy as np
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [10, np.nan, 30, 40, np.nan]
})
print("Исходные данные:")
print(df)
print()
# Интерполировать в исходном DataFrame
df_interpolated = df.interpolate(method="linear")
print("После линейной интерполяции:")
print(df_interpolated)
print()
# Ограничить диапазон интерполяции
print("Заполнить только первое и последнее из последовательных пропущенных значений (макс. 1):")
print(df.interpolate(limit=1))
Практика: работа с реальными данными
Пример
import numpy as np
# Смоделировать реальные бизнес-данные
np.random.seed(42)
n = 20
df = pd.DataFrame({
"Дата": pd.date_range("2024-01-01", periods=n),
"Сумма продаж": np.random.choice([100, 200, np.nan, 300, np.nan], n),
"Количество клиентов": np.random.choice([10, np.nan, 20, 30], n),
"Conversion rate": np.random.choice([0.05, 0.1, np.nan, 0.15], n)
})
print("Пропущенные данные в исходных данных:")
print(df.isna().sum())
print()
# Стратегия обработки:
# 1. Заполнить сумму продаж средним значением предыдущего и следующего значений (бизнес допускает колебания)
df["Sales amount"] = df["Sales amount"].interpolate()
# 2. Заполнить количество клиентов средним значением
df["Customer count"] = df["Customer count"].fillna(df["Customer count"].mean())
# 3. Заполнить коэффициент конверсии значением 0 (означает отсутствие конверсии)
df["Conversion rate"] = df["Conversion rate"].fillna(0)
print("Обработанные данные:")
print(df)
print()
print("Пропущенные данные после обработки:")
print(df.isna().sum())
Часто задаваемые вопросы
1. Создаёт ли fillna новый объект или изменяет на месте?
fillnaПо умолчанию он возвращает новый объект. Используйтеinplace=Trueможет изменять на месте.
2. Пропущенные значения остаются после интерполяции
Если пропущенные значения находятся в начале или конце последовательности, интерполяция не может их заполнить, и требуется дополнительная обработка.
3. Различайте NaN и пустые строки
Пустая строка""не является пропущенным значением, нужно использоватьreplace("", np.nan)для преобразования.
Другие расширенияПри выборе метода заполнения учитывайте бизнес-смысл: заполнение вперёд подходит для временных рядов, заполнение назад — для статических данных, среднее — для числовых данных, а интерполяция — для непрерывных данных.