Почему apply и циклы в pandas медленные и как писать быстрее?
- Что такое векторизация в pandas?
- Как обработать в pandas файл, который не помещается в память?
Векторные операции над столбцами (df["a"] * df["b"], np.where, .str, .dt) выполняются в C над массивами. apply по строкам и циклы iterrows вызывают Python-функцию на каждую строку — медленнее в десятки–сотни раз. Для больших данных: правильные типы (category, int32), чтение частями (chunksize), колоночные форматы (Parquet) и движки Polars или DuckDB.
- Условия — np.where, np.select, а не apply с if
- category для строк с малым числом значений сокращает память в разы
- Parquet читает только нужные столбцы и хранит типы
А ещё DuckDB позволяет запускать SQL прямо по Parquet и датафреймам
Подробно
Почему apply медленный. pandas хранит столбцы как массивы NumPy. Векторная операция — один вызов C-кода на весь столбец. apply(axis=1) создаёт объект строки и вызывает Python-функцию миллионы раз.
Приёмы ускорения:
- арифметика, сравнения,
np.where,np.select— вместо условий в apply; - методы
.strи.dtдля строк и дат; mergeсо справочником вместоmapс функцией;groupby().transformвместо цикла по группам.
Большие данные:
- типы:
category,int32,float32, даты как datetime; read_csv(chunksize=…)— обработка частями с агрегированием;- Parquet вместо CSV — в разы быстрее и меньше;
- Polars — многопоточный, ленивое выполнение;
- DuckDB — SQL-движок в процессе, хорошо справляется с данными больше памяти.
Пример
# медленно
df["segment"] = df.apply(lambda r: "vip" if r.amount > 10_000 else "regular", axis=1)
# быстро
df["segment"] = np.where(df["amount"] > 10_000, "vip", "regular")
# память
df["city"] = df["city"].astype("category")
df = pd.read_parquet("orders.parquet", columns=["user_id", "amount", "city"])Что хотят услышать
- Векторизация и почему apply медленный.
- Конкретные приёмы замены.
- Работа с данными, не помещающимися в память.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗apply по строкам компилируется в C так же, как векторные операции, поэтому по скорости они почти не отличаются
- ✗iterrows — самый быстрый способ обойти DataFrame: он не создаёт копий и не вызывает Python
- ✗Тип category увеличивает расход памяти и замедляет группировку, поэтому строки лучше хранить как object
- ✓Векторные операции идут в C, а apply и iterrows вызывают Python на строку
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.