apply vs map vs applymap в Pandas: шпаргалка
Зачем знать разницу
Один из классических вопросов на собесе аналитика с Python. Методы похожие, но работают по-разному — и знать это нужно.
Краткая шпаргалка
| Метод | На чём работает | Что принимает | Что возвращает |
|---|---|---|---|
Series.map() |
Series | dict, Series, функция | Series |
Series.apply() |
Series | функция | Series |
DataFrame.apply() |
DataFrame | функция | Series или DataFrame |
DataFrame.applymap() |
DataFrame | функция | DataFrame (elemwise) |
Series.map()
Маппинг значений по словарю, Series или функции.
С dict
s = pd.Series(['a', 'b', 'c', 'a'])
mapping = {'a': 1, 'b': 2, 'c': 3}
s.map(mapping)
# [1, 2, 3, 1]С Series (lookup-таблица)
lookup = pd.Series({'a': 10, 'b': 20, 'c': 30})
s.map(lookup)
# [10, 20, 30, 10]С функцией
s = pd.Series([1, 2, 3])
s.map(lambda x: x * 2)
# [2, 4, 6]Особенность: если ключ не найден — NaN. Используйте fillna или param na_action.
Series.apply()
Применяет функцию к каждому элементу. Похоже на map с функцией, но поддерживает аргументы.
s = pd.Series([1, 2, 3])
s.apply(lambda x: x ** 2)
# [1, 4, 9]
# С аргументами
s.apply(lambda x, n: x ** n, n=3)
# [1, 8, 27]Возвращает Series или множественные значения
s.apply(lambda x: pd.Series([x, x**2], index=['orig', 'sq']))
# DataFrame с колонками orig и sqЕсли хочется сразу закрепить тему на практике — открой тренажёр в Telegram. 10 минут в день — и синтаксис в пальцах.
DataFrame.apply()
Применяет функцию к строке или столбцу (не к ячейке!).
По столбцам (по умолчанию axis=0)
df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
df.apply(sum)
# a 6
# b 15По строкам (axis=1)
df.apply(sum, axis=1)
# 0 5
# 1 7
# 2 9С произвольной функцией
df.apply(lambda row: row['a'] + row['b'], axis=1)DataFrame.applymap()
Применяется к каждой ячейке. Deprecated в новых версиях — используйте map:
# Старое
df.applymap(lambda x: x * 2)
# Новое (pandas 2.1+)
df.map(lambda x: x * 2)Когда что использовать
map для Series
- Маппинг категории → код:
s.map({'a': 1, 'b': 2}). - Простая трансформация:
s.map(str.lower).
apply для Series
- Сложная функция с аргументами:
s.apply(my_func, arg=5). - Функция возвращает несколько значений:
s.apply(lambda x: pd.Series([x, -x])).
apply для DataFrame
- Функции по строкам/столбцам: sum, max, custom aggregations.
axis=1— построчно,axis=0— по столбцам (реже).
applymap / map для DataFrame
- Функция для каждой ячейки: форматирование, преобразование типов.
Производительность
Медленнее векторизации, но приемлемо:
applymap / .mapна DataFrame — самое медленное.apply(axis=1)— медленно, цикл по строкам.apply(axis=0)— быстрее..map— быстрее всех этих.
Ещё быстрее — NumPy-операции:
# ❌ Медленно
df['sum'] = df.apply(lambda row: row['a'] + row['b'], axis=1)
# ✅ Быстро — векторизация
df['sum'] = df['a'] + df['b']На собесе часто спрашивают: «как ускорить этот apply?» Ответ — векторизация через + - * / или np.where.
Типичные задачи
1. Категория → число
df['category_code'] = df['category'].map({'A': 1, 'B': 2, 'C': 3})2. Длина строки в столбце
df['name_len'] = df['name'].apply(len)
# или
df['name_len'] = df['name'].str.len() # быстрее3. Сумма по строке
df['total'] = df[['a', 'b', 'c']].sum(axis=1)
# НЕ через apply — это векторизовано4. Условная метка
df['label'] = df['amount'].apply(lambda x: 'high' if x > 1000 else 'low')
# Лучше через np.where:
df['label'] = np.where(df['amount'] > 1000, 'high', 'low')5. Сложная трансформация row-wise
def classify(row):
if row['age'] < 18:
return 'child'
elif row['income'] < 30000:
return 'low'
else:
return 'high'
df['class'] = df.apply(classify, axis=1)Чтобы не только читать теорию, но и решать реальные задачи — загляните в бот Карьерника. Там по каждой теме подборка вопросов с разборами.
10 задач с собесов
1. Маппинг категории в лейбл
df['label'] = df['cat'].map({'a': 'Cat A', 'b': 'Cat B'})2. Нижний регистр для строк
df['name'].map(str.lower)
# Быстрее: df['name'].str.lower()3. Применить условие row-wise
df.apply(lambda row: row['x'] if row['y'] > 0 else -row['x'], axis=1)4. Подсчёт символов в ячейках DataFrame
df.map(len) # pandas 2.1+
# Или df.applymap(len) в старых версиях5. Разбить колонку «ФИО» на три
df[['fam', 'name', 'otch']] = df['fio'].str.split(' ', expand=True)
# Через apply — медленнее6. Применить функцию к группам
df.groupby('cat').apply(lambda g: g.sort_values('x').head(1))7. Безопасный map с fillna
df['code'].map(mapping).fillna(-1)8. Применить функцию с аргументами
df['x'].apply(lambda x, n: x * n, n=10)9. map по Series из другой таблицы
user_to_city = users.set_index('user_id')['city']
orders['city'] = orders['user_id'].map(user_to_city)
# Быстрее, чем merge для simple lookup10. apply против векторизации
# Медленно
df['x2'] = df['x'].apply(lambda x: x ** 2)
# Быстро
df['x2'] = df['x'] ** 2Как тренироваться
Разницу учить на практике: попробуйте 3-4 задачи через каждый метод, почувствуйте, где какой подходит.
Совет: на собесе, если показывают код с apply и спрашивают «как улучшить» — почти всегда ответ «векторизация». Это знак технической зрелости.
Читайте также
FAQ
Чем apply отличается от map?
map работает только с Series, принимает dict/Series/функция, не поддерживает kwargs. apply — универсальный, с kwargs, работает на Series и DataFrame.
Почему applymap deprecated?
В pandas 2.1+ DataFrame.map заменяет applymap. Функциональность та же, имя согласовано с Series.map.
Как ускорить медленный apply?
- Векторизовать (обычно
+*илиnp.where). - Использовать
.strметоды для строк. - Если через функцию — попробовать
numbaилиcython.
Когда apply всё-таки уместен?
Когда логика сложная, не выражается через векторные операции. Например, классификация с 5 условиями или parsing сложной строки.