2025, Dec 27 06:01
Почему не стоит мутировать строки в Pandas DataFrame.apply
Почему мутации в Pandas DataFrame.apply не поддерживаются, чем это грозит и как корректно вычитать среднее по столбцу через векторизованные операции.
Использование Pandas DataFrame.apply с пользовательской функцией, которая изменяет свой аргумент, — частая ошибка новичков. На учебных примерах или небольших данных такой код может выглядеть рабочим, но официальная документация прямо предупреждает: изменять объекты внутри apply «не поддерживается». Что именно означает «не поддерживается» на практике и как надежно получить тот же результат?
Пример, из-за которого возникает вопрос
Сценарий простой: посчитать среднее числового столбца и вычесть его из каждой строки с помощью apply. Пример ниже отражает эту идею, но с другими именами переменных, чтобы сосредоточиться на механике, а не на конкретном туториале:
mean_pts = ratings_df.points.mean()
ratings_df.points.map(lambda v: v - mean_pts)
def center_points(row_obj):
row_obj.points = row_obj.points - mean_pts
return row_obj
ratings_df.apply(center_points, axis='columns')Функция center_points изменяет переданную ей строку и возвращает её же. На беглом запуске результат выглядит корректным, что и порождает вопрос: раз работает, почему в документации сказано, что изменять нельзя?
Что здесь означает «не поддерживается»
«Не поддерживается в DataFrame.apply()» означает, что Pandas не гарантирует результат, если ваша функция меняет объект, переданный в apply. Если вы получите неожиданное или неверное поведение, такая непоследовательность не считается багом Pandas. И наоборот, даже если сегодня всё совпадает с ожиданиями, будущие версии не обязаны сохранять это поведение. Никаких встроенных ограничений, которые помешают вам мутировать объект внутри apply, нет — это валидный Python, — но Pandas не обещает устойчивой семантики в таком случае.
Рекомендуемый подход
Безопасная модель мышления для DataFrame.apply — создавать новый объект на основе старого, а не менять строки на месте. Если вы всё же используете apply, относитесь к нему как к преобразованию, которое возвращает новый DataFrame или Series:
df_out = ratings_df.apply(some_func, axis='columns')Для арифметики по столбцам предпочтительнее встроенные операции Pandas или NumPy. Они векторизованы, обычно быстрее и проще. В нашем случае вычитание среднего из столбца — это прямое столбцовое действие. apply вовсе не нужен.
Решение без мутаций в apply
Того же эффекта — вычесть среднее из столбца points — можно добиться напрямую и эффективно:
mean_pts = ratings_df.points.mean()
ratings_df.points -= mean_ptsЭтот подход использует векторизованную арифметику для модификации столбца и соответствует рекомендуемой работе со столбцами DataFrame.
Почему это важно
Опора на мутацию внутри apply привязывает ваш код к поведению, которое Pandas не гарантирует. Это делает конвейеры хрупкими и порождает тонкие несоответствия, которые трудно отлавливать — особенно между версиями. Векторизованные операции или использование apply как чистого преобразования дают более ясные намерения, лучшую производительность и большую стабильность со временем.
Выводы
Если нужно скорректировать значения в столбце, выбирайте прямое, векторизованное присваивание, а не изменение строк внутри apply. Когда всё‑таки прибегаете к apply, проектируйте функцию так, чтобы она возвращала новое значение или новую строку, а не меняла аргумент на месте. Так преобразования остаются предсказуемыми и совместимыми с будущими версиями.