Заполнение пропущенных значений
Заменяйте NaN константой, средним значением столбца, предыдущим или последующим значением с помощью fillna() и его параметра method.
«Заполнение пропущенных значений» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Введение в fillna()
Импутация — это замена пропущенных значений разумными подстановками вместо удаления строки. Основной инструмент Pandas для этого — fillna(): он заменяет каждый NaN в Series или DataFrame указанным Вами значением. В отличие от удаления, импутация сохраняет все строки, что особенно важно при небольшом объёме данных или когда характер пропусков несёт полезную информацию.
В простейшем случае передаётся скаляр: df['col'].fillna(0).
import pandas as pd
import numpy as np
df = pd.DataFrame({
'product': ['A', 'B', 'C', 'D'],
'price': [10.0, np.nan, 30.0, np.nan]
})
# Fill all NaN in 'price' with 0
filled = df['price'].fillna(0)
print(filled)
# 0 10.0
# 1 0.0
# 2 30.0
# 3 0.0Заполнение средним или медианным значением столбца
Заполнение столбца его средним значением (для симметричных распределений) или медианой (для асимметричных распределений) — одна из самых распространённых стратегий импутации. Эти статистики отражают центральную тенденцию данных и поэтому минимизируют искажение распределения столбца. Всегда вычисляйте статистику на обучающей выборке, чтобы избежать утечки данных.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'salary': [50000, np.nan, 70000, 80000, np.nan, 60000]
})
mean_salary = df['salary'].mean()
median_salary = df['salary'].median()
df['salary_mean_filled'] = df['salary'].fillna(mean_salary)
df['salary_median_filled'] = df['salary'].fillna(median_salary)
print(df)
# salary salary_mean_filled salary_median_filled
# 0 50000.0 50000.0 50000.0
# 1 NaN 65000.0 65000.0
# 2 70000.0 70000.0 70000.0Заполнение категориальных данных модой
Для категориальных столбцов заполнение средним или медианой не имеет смысла. Вместо этого используйте моду — наиболее часто встречающееся значение. Series.mode()[0] возвращает самое распространённое значение (индекс [0] обрабатывает случай с несколькими модами).
import pandas as pd
import numpy as np
df = pd.DataFrame({
'status': ['active', 'inactive', None, 'active', None, 'active']
})
most_common = df['status'].mode()[0]
print('Mode:', most_common) # active
df['status'] = df['status'].fillna(most_common)
print(df['status'].tolist())
# ['active', 'inactive', 'active', 'active', 'active', 'active']Прямое заполнение с помощью ffill()
Прямое заполнение (также называемое переносом последнего наблюдения вперёд, LOCF) распространяет последнее допустимое (не равное NaN) значение вперёд, заполняя последующие позиции NaN. Этот способ идеально подходит для данных временных рядов, где измерение остаётся неизменным до обновления, например для статуса устройства, уровней цен или показаний датчика, изменяющихся только при определённых событиях.
import pandas as pd
import numpy as np
price = pd.Series(
[100, np.nan, np.nan, 105, np.nan, 110],
index=pd.date_range('2024-01', periods=6, freq='ME')
)
filled = price.ffill()
print(filled)
# 2024-01-31 100.0
# 2024-02-29 100.0
# 2024-03-31 100.0
# 2024-04-30 105.0
# 2024-05-31 105.0
# 2024-06-30 110.0Обратное заполнение с помощью bfill()
Заполнение назад (перенос следующего наблюдения назад, NOCB) распространяет следующее допустимое значение назад, заполняя предшествующие позиции NaN. Это полезно, когда будущие данные должны заполнить пропущенные значения в прошлом — например, если Вы получаете данные на конец месяца и Вам нужно заполнить дни этого месяца до поступления отчёта.
import pandas as pd
import numpy as np
df = pd.DataFrame({'value': [np.nan, np.nan, 3, np.nan, 5]})
print(df['value'].bfill())
# 0 3.0
# 1 3.0
# 2 3.0
# 3 5.0
# 4 5.0
# dtype: float64Параметр limit для заполнения вперёд и назад
И ffill(), и bfill() принимают параметр limit, который ограничивает количество подряд идущих значений NaN для заполнения. Это важно, когда нужно переносить значение вперёд только через небольшой пробел — длинные пробелы должны оставаться NaN, чтобы показать: данные действительно отсутствуют, а не просто ещё не поступили.
import pandas as pd
import numpy as np
s = pd.Series([1.0, np.nan, np.nan, np.nan, 5.0])
# Fill only the first NaN gap, leave the rest
print(s.ffill(limit=1))
# 0 1.0
# 1 1.0 <- filled
# 2 NaN <- still NaN (limit reached)
# 3 NaN
# 4 5.0Разное заполнение столбцов
В реальном DataFrame разным столбцам могут требоваться разные стратегии заполнения. Передайте в fillna() словарь, где ключами являются имена столбцов, а значениями — значения для заполнения. Это позволяет выполнить заполнение для каждого столбца отдельно одним вызовом, что аккуратнее, чем последовательно вызывать несколько отдельных fillna.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'age': [25, np.nan, 30],
'income': [50000, np.nan, 70000],
'country': ['USA', np.nan, 'UK']
})
fill_values = {
'age': df['age'].median(),
'income': df['income'].mean(),
'country': 'Unknown'
}
filled = df.fillna(fill_values)
print(filled)
# age income country
# 0 25.0 50000.0 USA
# 1 27.5 60000.0 Unknown
# 2 30.0 70000.0 UKЗаполнение с учётом групп
Более сложная стратегия — заполнять значения NaN не общим средним столбца, а средним или медианой группы. Например, пропущенную зарплату можно заполнить медианной зарплатой для соответствующей категории должностей. Это сохраняет структуру подгрупп и даёт более реалистичные значения, чем общая статистика.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'dept': ['Eng', 'Eng', 'HR', 'HR', 'Eng'],
'salary': [90000, np.nan, 50000, np.nan, 110000]
})
# Fill salary NaN with the median salary for each department
df['salary'] = df.groupby('dept')['salary'].transform(
lambda x: x.fillna(x.median())
)
print(df)
# dept salary
# 0 Eng 90000.0
# 1 Eng 100000.0 <- group median (90k+110k)/2
# 2 HR 50000.0
# 3 HR 50000.0
# 4 Eng 110000.0Заполнение постоянным маркерным значением
Иногда правильным значением для заполнения будет маркерное значение, обозначающее «неизвестно», а не реальный результат измерения. Например, -1 для неизвестного возраста, «N/A» для неизвестной категории или False для неизвестного логического флага. Маркерные значения допустимы, если последующий код явно проверяет их и обрабатывает отдельно от настоящих данных.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'user_id': [1, 2, 3],
'referral_source': ['email', np.nan, 'ad'],
'trial_days': [14, np.nan, 7]
})
df['referral_source'] = df['referral_source'].fillna('unknown')
df['trial_days'] = df['trial_days'].fillna(-1).astype(int)
print(df)
# user_id referral_source trial_days
# 0 1 email 14
# 1 2 unknown -1
# 2 3 ad 7Объединение вызовов fillna() в конвейер
Как и все методы Pandas, fillna() возвращает новый DataFrame и удобно включается в цепочку методов. Вызов .fillna() после .dropna() реализует двухэтапную стратегию: сначала удалить строки с пропусками в критически важных столбцах, затем заполнить оставшиеся необязательные значения NaN разумными значениями по умолчанию — всё в одном понятном конвейере.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'user_id': [1, None, 3, 4],
'score': [88, 95, np.nan, 76],
'label': ['A', 'B', None, 'D']
})
cleaned = (
df
.dropna(subset=['user_id'])
.fillna({'score': df['score'].mean(), 'label': 'Unknown'})
)
print(cleaned)
# user_id score label
# 0 1.0 88.0 A
# 2 3.0 86.3 Unknown
# 3 4.0 76.0 DПроверка отсутствия NaN
После заполнения всегда проверяйте, что в целевых столбцах не осталось значений NaN. Вызовите df.isna().sum() или убедитесь, что количество равно нулю. Неожиданное ненулевое количество означает, что в заполнении не был учтён какой-то случай — например, тип данных столбца не позволил выполнить заполнение или Вы забыли добавить столбец в словарь.
import pandas as pd
import numpy as np
df = pd.DataFrame({'a': [1.0, np.nan, 3.0], 'b': [np.nan, 2.0, 3.0]})
filled = df.fillna(0)
# Verify
assert filled.isna().sum().sum() == 0, 'Some NaN remain!'
print('All NaN filled successfully')
print(filled.isna().sum())
# a 0
# b 0
# dtype: int64Быстрая проверка
Проверьте, насколько хорошо Вы поняли заполнение пропущенных значений в Pandas.
Итоги урока
В этом уроке Вы узнали, что fillna(scalar) заменяет все значения NaN постоянным значением, fillna(mean/median) заполняет пропуски статистикой столбца, а ffill()/bfill() распространяют соседние значения во временных рядах. Передавайте в fillna() словарь для задания отдельных стратегий по столбцам и используйте groupby().transform() для заполнения с учётом групп. Далее мы рассмотрим интерполяцию и случаи, когда следует выбирать продвинутые методы заполнения.
Часто задаваемые вопросы
Урок «Заполнение пропущенных значений» бесплатный?
Да — полный текст урока «Заполнение пропущенных значений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Чему я научусь в уроке «Заполнение пропущенных значений»?
Заменяйте NaN константой, средним значением столбца, предыдущим или последующим значением с помощью fillna() и его параметра method. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?
Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Заполнение пропущенных значений»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?
Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Поиск пропущенных значений
- Удаление пропущенных значений
- Заполнение пропущенных значений
- Интерполяция и продвинутое заполнение