В чем разница между apply и applymap в Pandas?

Ответ

Основное отличие — в уровне применения функции к 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) — такой же костыль, но для операций, которые нужно впендюрить в каждую отдельную ячейку таблицы, и иначе никак.

Короче, выбирай инструмент по размеру задачи, а не тыкай наугад, а то будет тебе хиросима с производительностью.