Ответ
Основное отличие — в уровне применения функции к DataFrame.
applyприменяет функцию вдоль оси (по умолчаниюaxis=0, т.е. к каждому столбцу). Функция получает на вход целый Series (столбец или строку) и возвращает одно значение для агрегации или новый Series для преобразования.applymap(в Pandas 2.1.0+ рекомендуется использоватьmap) применяет функцию к каждому элементу DataFrame поэлементно.
Пример:
import pandas as pd
df = pd.DataFrame({'A': [1, 4], 'B': [9, 16]})
print(df)
# A B
# 0 1 9
# 1 4 16
# apply: работаем со столбцами как с целыми объектами
# Находим размах (max - min) для каждого столбца
print(df.apply(lambda col: col.max() - col.min()))
# A 3
# B 7
# dtype: int64
# applymap (map): работаем с каждым элементом отдельно
# Извлекаем квадратный корень из каждого числа
print(df.applymap(lambda x: x ** 0.5))
# A B
# 0 1.0 3.0
# 1 2.0 4.0
Практическое правило: Для операций, которые можно выразить через векторизованные операции NumPy или встроенные методы Pandas (например, df * 2, df.sum()), они всегда будут значительно быстрее, чем apply или applymap. apply стоит использовать для сложных, не векторизуемых преобразований на уровне столбцов/строк, а applymap (или map) — для таких же операций, но на уровне отдельных ячеек.
Ответ 18+ 🔞
Давай разберемся, в чём тут собака зарыта, а то народ постоянно путается, как будто это одно и то же. Ситуация, бля, проще пареной репы, если вникнуть.
Представь, что твой DataFrame — это такой офигенный бутерброд с данными. Так вот, apply и applymap — это два абсолютно разных инструмента, чтобы этот бутерброд или съесть целиком, или разобрать по крошкам.
apply — это как взять целый кусок (столбец или строку) и отправить его в мясорубку твоей функции. Она получает на вход целый Series (всю колонку, если axis=0, или всю строку, если axis=1), делает с ним что хочет, и на выходе плюёт одним значением (если агрегируешь) или новым Series (если преобразовываешь). Это операция уровня слоя в бутерброде.
applymap (а в новых пандах ласково просят использовать просто map для датафреймов) — это уже микрохирургия, ёпта. Он берёт каждый отдельный элемент, каждый кусочек помидорки или сыра, и к нему индивидуально применяет функцию. Поэлементно, без дураков.
Смотри на живом примере, а то волнение ебать, что не поймёшь:
import pandas as pd
# Допустим, есть у нас простой датафрейм, два на два
df = pd.DataFrame({'A': [1, 4], 'B': [9, 16]})
print(df)
# A B
# 0 1 9
# 1 4 16
# Ситуация первая: используем apply.
# Хотим понять размах данных в каждом столбце (максимум минус минимум).
# Функция получает на ВЕСЬ столбец 'A' [1, 4], потом ВЕСЬ столбец 'B' [9, 16].
print(df.apply(lambda col: col.max() - col.min()))
# A 3 # 4 - 1 = 3
# B 7 # 16 - 9 = 7
# dtype: int64
# Ситуация вторая: используем applymap (он же map).
# Хотим из КАЖДОЙ ПИКСЕЛИ в таблице корень квадратный извлечь.
# Функция тупо получает на вход число 1, потом 4, потом 9, потом 16.
print(df.applymap(lambda x: x ** 0.5))
# A B
# 0 1.0 3.0 # sqrt(1), sqrt(9)
# 1 2.0 4.0 # sqrt(4), sqrt(16)
Уловил разницу? В первом случае функция вызывалась всего два раза (по разу на столбец), а во втором — четыре раза (по разу на каждую ячейку).
Главное практическое правило, которое спасёт тебе кучу времени и нервов: если операцию можно сделать векторизованно (то есть тупо df * 2, df.sum(), df.str.upper()), делай ТАК. Это в овердохуища раз быстрее, потому что работает на низком уровне, а не через питоновские циклы в обёртке.
apply — это твой спасательный круг для сложной, нестандартной хуйни, которую нужно провернуть над целой колонкой или строкой за раз. А applymap (map) — такой же костыль, но для операций, которые нужно впендюрить в каждую отдельную ячейку таблицы, и иначе никак.
Короче, выбирай инструмент по размеру задачи, а не тыкай наугад, а то будет тебе хиросима с производительностью.