Вопрос или проблема
Когда я работаю с dataframe в pandas, мне нравится использовать цепочки методов, потому что это делает рабочий процесс похожим на подход tidyverse в R, где вы используете строку пайпов.
Рассмотрим пример из этого ответа:
N = 10
df = (
pd.DataFrame({"x": np.random.random(N)})
.assign(y=lambda d: d['x']*0.5)
.assign(z=lambda d: d.y * 2)
.assign(w=lambda d: d.z*0.5)
)
Я слышал, что манипуляции dataframe с использованием lambda неэффективны, потому что это не векторизованная операция, а под капотом идет какое-то циклическое выполнение.
Является ли это проблемой для примеров, подобных приведенному выше? Есть ли альтернативы использованию lambda в цепочке методов, которые сохраняют подход, похожий на tidyverse?
Вы можете использовать один assign
, это предотвратит создание промежуточного DataFrame на каждом шаге:
df = (pd.DataFrame({'x': np.random.random(N)})
.assign(y=lambda d: d['x'] * 0.5,
z=lambda d: d.y * 2,
w=lambda d: d.z * 0.5,
)
)
Существует значительный прирост производительности:
NB. Я только измеряю .assign(x,y,z)
против .assign(x).assign(y).assign(z)
, DataFrame предварительно сгенерирован.
Ответ или решение
Проблемы производительности использования lambda для присвоения переменных в pandas в цепочках методов
Когда вы работаете с библиотекой pandas для анализа данных, использование цепочек методов становится популярным подходом, который позволяет организовать и структурировать код более читабельно. Тем не менее, применение функции lambda
в этой структуре может вызвать проблемы с производительностью. Далее мы рассмотрим, почему это происходит и какие существуют эффективные альтернативы.
Причины снижения производительности
-
Неэффективность векторов. Основная проблема при использовании
lambda
в методах.assign()
заключается в том, что при каждом вызовеassign
создается новый промежуточный DataFrame. Когда используетсяlambda
, pandas вынужден выполнять цикл для вычисления значений. Это отличается от векторизированных операций, которые оптимизированы для обработки данных сразу целыми массивами. -
Создание промежуточных DataFrame. Каждое использование метода
.assign()
возвращает новый DataFrame, в котором результат предыдущего присвоения становится новой временной версией исходных данных. Каждое последующее присвоение обрабатывается на основе предыдущего DataFrame, что ведет к дополнительным затратам по памяти и времени. -
Потенциальное накопление нагрузки. Если вы используете несколько связанных друг с другом присвоений, большая часть нагрузки может накапливаться, что в конечном счете замедляет выполнение кода. Это особенно актуально для больших наборов данных, где каждая дополнительная операция может значительно увеличить общее время обработки.
Альтернативы использованию lambda
Существует несколько способов улучшить производительность и избежать указанных выше проблем, при этом сохранив стиль использования цепочек методов, похожий на подход tidyverse в R.
-
Единственный вызов .assign(). Один из самых простых способов для повышения производительности заключается в объединении всех ваших присвоений в один вызов метода
.assign()
. Это позволит избежать создания промежуточных DataFrame. Например:df = (pd.DataFrame({'x': np.random.random(N)}) .assign(y=lambda d: d['x'] * 0.5, z=lambda d: d.y * 2, w=lambda d: d.z * 0.5) )
В данном примере, переменные
y
,z
иw
рассчитываются одновременно, что сокращает общее время выполнения. -
Использование .pipe() для сложных преобразований. В случае, когда ваши операции становятся слишком сложными, можно использовать метод
.pipe()
для передачи вашего DataFrame в пользовательскую функцию. Это позволяет вам создавать более чистый и структурированный код и может улучшить читаемость, хотя и не всегда решает проблемы производительности.def compute_columns(df): df['y'] = df['x'] * 0.5 df['z'] = df['y'] * 2 df['w'] = df['z'] * 0.5 return df df = pd.DataFrame({"x": np.random.random(N)}).pipe(compute_columns)
-
Параллельные вычисления с Dask. Если объем данных велик и вас беспокоят производительность и время выполнения, рассмотрите возможность использования Dask, который обеспечивает механизмы для распределенных вычислений. Он предоставляет интерфейс, очень похожий на pandas, и способен обрабатывать большие объемы данных благодаря параллельной обработке.
Заключение
Использование lambda
внутри цепочек методов в pandas может привести к снижению производительности из-за неэффективного использования памяти и времени обработки. Оптимизация ваших операций с помощью единого вызова .assign()
или других предложенных решений может значительно улучшить производительность вашего кода. Стоит помнить, что выбор оптимального подхода будет зависеть от конкретного сценария и объемов данных, с которыми вы работаете.