Короткий ответ
apply вызывает функцию отдельно для каждого элемента или строки через интерпретатор Python, тогда как векторизованные операции (например, встроенная арифметика pandas/NumPy) выполняются скомпилированным кодом сразу над всем массивом, что обычно намного быстрее на больших данных.
Как это работает подробнее
векторизованные операции (df['a'] + df['b'], str.contains, np.where) написаны на C и обрабатывают весь столбец за один проход без накладных расходов на вызов функции Python для каждой строки; apply(lambda row: ..., axis=1) для каждой строки создаёт отдельный объект Series и вызывает Python-функцию, что существенно медленнее на больших таблицах.
Пример кода
question-122.py
import pandas as pd
import numpy as np
df = pd.DataFrame({'a': range(100000), 'b': range(100000)})
df['sum_apply'] = df.apply(lambda row: row['a'] + row['b'], axis=1)
df['sum_vec'] = df['a'] + df['b']
print(df['sum_apply'].equals(df['sum_vec']))Что использовать на практике
- Практика. apply оправдан, когда логику действительно нельзя выразить встроенными операциями — например, сложная условная логика, зависящая от нескольких столбцов одновременно нестандартным образом, или вызов внешней функции/API; для простых случаев обычно есть векторизованная альтернатива (np.where, np.select, str-методы, map со словарём).
- Проверка. результаты двух подходов должны совпасть (equals вернёт True), при этом замер времени через %timeit покажет, что векторизованная версия выполняется значительно быстрее apply.
Подводные камни
- Частая ошибка. используют apply(axis=1) там, где достаточно np.where или простой арифметики, теряя кратный выигрыш в скорости на больших таблицах; не измеряют реальное время выполнения (%timeit) перед оптимизацией, оптимизируя код интуитивно вместо того, чтобы найти настоящее узкое место.