2025, Dec 13 00:01
Предсказуемые цепочки преобразований в pandas при Copy-On-Write
Разберём, как строить предсказуемые цепочки преобразований в pandas при Copy-On-Write: без inplace-мутаций, с rename, assign, apply. Конвейер для DataFrame.
Когда вы начинаете нормализовать данные в pandas, один из первых практических вопросов звучит просто: изменит ли метод текущий DataFrame или вернёт новый? Если включён режим Copy-On-Write, самый надёжный способ сохранить предсказуемость — использовать только операции, которые возвращают новый DataFrame, и собирать их в единый конвейер. Такой подход убирает угадайку вокруг изменений «на месте», делает код читабельным и упрощает проверку отдельных шагов.
Постановка задачи
Рассмотрим цепочку преобразований для данных, загруженных из листа Excel. Логика ниже работает, но на каждом шаге одна и та же переменная переназначается: в одном месте смешиваются присваивания, мутации и переиндексация.
import pandas as pd
pd.options.mode.copy_on_write = True
data = pd.read_excel("my_excel_file.xls", sheet_name="my_sheet", usecols="A:N")
data = data.dropna(how='all')
data = data.iloc[:-1, :]
data.columns.array[0] = "Resource"
data = data.astype({"Resource": int})
data.columns = data.columns.str.replace('Avg of ', '').str.replace('Others', 'others')
data = data.set_index("Resource")
data = data.sort_index(axis=0)
data = data / 100
data = data.round(4)
data = data.reindex(columns=sorted(data))
Что на самом деле вызывает путаницу
Смешение прямых изменений атрибутов с вызовами методов размывает границу: где возвращается новый объект, а где меняется состояние. В одних строках идёт присваивание результата вызова, в других — прямое обращение к атрибутам вроде массива columns. Стиль получается неоднородным и подталкивает к постоянным переназначениям, особенно если вы стараетесь действовать «безопасно» при включённом Copy-On-Write.
Предсказуемый подход на основе цепочек
Простое правило: придерживайтесь методов, которые возвращают DataFrame, и компонуйте их. Любую строку можно закомментировать, чтобы проверить отдельный шаг. Вариант ниже передаёт ту же логику, не опираясь на неявные мутации и не смешивая стили.
import pandas as pd
pd.options.mode.copy_on_write = True
frame = pd.read_excel("my_excel_file.xls", sheet_name="my_sheet", usecols="A:N")
frame = (
frame
.dropna(how='all')
.iloc[:-1, :]
.rename(columns={frame.columns[0]: "Resource"})
.astype({"Resource": int})
.rename(columns=lambda s: s.replace('Avg of ', '').replace('Others', 'others'))
.set_index("Resource")
.sort_index(axis=0)
.div(100)
.round(4)
.sort_index(axis=1)
)
Так сохраняется замысел каждого шага и гарантируется, что каждая операция создаёт новый DataFrame. Конвейер линейный, наглядный и легко меняется.
Приёмы, которые удобно переиспользовать
Нужно привести к числовому типу отдельные столбцы, а остальные не трогать? Применяйте функцию по столбцам с условием. Так преобразования остаются явными и локальными.
cleaned = (
frame
.apply(lambda col: pd.to_numeric(col) if col.name in ['Quantity'] else col)
)
Если нужно привести к числу один столбец и вернуть его в составе цепочки, используйте assign с лямбда-функцией, читающей из текущего объекта.
updated = (
frame
.assign(Resource=lambda x: x['Resource'].apply(pd.to_numeric, errors='coerce'))
)
Хотите переименовать все столбцы разом, без явного словаря соответствий? Задайте новый заголовок явно.
renamed = (
frame
.set_axis(['Product', 'Quantity'], axis=1)
)
Все эти приёмы сводятся к одной идее: отдавайте предпочтение цепочечным операциям, возвращающим DataFrame, чтобы свободно компоновать их без побочных эффектов «на месте».
Почему это важно
Цепочки из методов, возвращающих DataFrame, формируют устойчивую модель в голове. Каждый шаг самодостаточен: можно отключить его, не затрагивая остальное, и не смешивать неявные мутации с переназначениями. При Copy-On-Write такой стиль делает ход преобразований предсказуемым и помогает чётко понимать, где именно меняются данные.
Итоги
Предпочитайте конвейер из методов, возвращающих DataFrame, и избегайте прямых присваиваний атрибутам и неявных мутаций. Для работы с заголовками используйте rename и set_axis, для точечных обновлений столбцов — assign, а для выборочных преобразований — apply. Так логика подготовки данных остаётся явной, проверяемой и удобной в сопровождении.