Обработка пропущенных значений в Pandas

Реальные данные часто содержат пропущенные значения (NaN). Pandas предоставляет богатый набор функций для обработки пропущенных данных. В этом разделе подробно рассматривается использование fillna, dropna, interpolate и других методов.


Представление пропущенных значений

В Pandas,NaN(Not a Number) представляет пропущенные значения, которые происходят из библиотеки NumPy.

Пример

import pandas as pd
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 pandas as pd
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 pandas as pd
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 pandas as pd
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 pandas as pd
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 pandas as pd
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 pandas as pd
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 pandas as pd
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)для преобразования.

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

Другие расширения