Два основных различия между apply
иtransform
Есть два основных различия между transform
и apply
групповыми методами.
- Входные данные:
apply
неявно передает все столбцы для каждой группы как DataFrame пользовательской функции.
- В то время как
transform
каждый столбец для каждой группы индивидуально в виде серии в пользовательскую функцию.
- Вывод:
apply
Переданная пользовательская функция может возвращать скаляр или Series или DataFrame (или массив numpy или даже список) .
- Переданная пользовательская функция
transform
должна возвращать последовательность (одномерный ряд, массив или список) той же длины, что и группа .
Таким образом, transform
работает только с одной серией за раз и apply
работает со всем DataFrame одновременно.
Проверка пользовательской функции
Это может помочь немного проверить входные данные для вашей пользовательской функции, переданной apply
или transform
.
Примеры
Давайте создадим пример данных и проверим группы, чтобы вы могли понять, о чем я говорю:
import pandas as pd
import numpy as np
df = pd.DataFrame({'State':['Texas', 'Texas', 'Florida', 'Florida'],
'a':[4,5,1,3], 'b':[6,10,3,11]})
State a b
0 Texas 4 6
1 Texas 5 10
2 Florida 1 3
3 Florida 3 11
Давайте создадим простую пользовательскую функцию, которая распечатывает тип неявно переданного объекта, а затем выдает ошибку, чтобы выполнение могло быть остановлено.
def inspect(x):
print(type(x))
raise
Теперь давайте передадим эту функцию как groupby, так apply
и transform
методам, чтобы увидеть, какой объект ей передан:
df.groupby('State').apply(inspect)
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
RuntimeError
Как видите, DataFrame передается в inspect
функцию. Вы можете быть удивлены, почему тип DataFrame был напечатан дважды. Панды управляют первой группой дважды. Это делает это, чтобы определить, есть ли быстрый способ завершить вычисление или нет. Это небольшая деталь, о которой вам не стоит беспокоиться.
Теперь давайте сделаем то же самое с transform
df.groupby('State').transform(inspect)
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
RuntimeError
Передано Серию - совершенно другой объект Панд.
Таким образом, transform
разрешено работать только с одной серией одновременно. Для него не является невозможным воздействовать на две колонки одновременно. Таким образом, если мы попытаемся вычесть столбец a
из b
нашей пользовательской функции, мы получим ошибку transform
. Увидеть ниже:
def subtract_two(x):
return x['a'] - x['b']
df.groupby('State').transform(subtract_two)
KeyError: ('a', 'occurred at index a')
Мы получаем KeyError, поскольку pandas пытается найти индекс Series, a
который не существует. Вы можете выполнить эту операцию, так apply
как она содержит весь DataFrame:
df.groupby('State').apply(subtract_two)
State
Florida 2 -2
3 -8
Texas 0 -2
1 -5
dtype: int64
Вывод представляет собой серию и немного сбивает с толку, поскольку исходный индекс сохраняется, но у нас есть доступ ко всем столбцам.
Отображение переданного объекта панды
Это может помочь еще больше отобразить весь объект pandas в пользовательской функции, чтобы вы могли точно видеть, с чем вы работаете. Вы можете использовать print
операторы, которые мне нравятся, чтобы использовать display
функцию из IPython.display
модуля, чтобы DataFrames красиво выводились в HTML в блокноте jupyter:
from IPython.display import display
def subtract_two(x):
display(x)
return x['a'] - x['b']
Скриншот:
Преобразование должно возвращать одномерную последовательность того же размера, что и группа
Другое отличие состоит в том, что transform
должна возвращать одномерную последовательность того же размера, что и группа. В этом конкретном случае каждая группа имеет две строки, поэтому transform
должна возвращать последовательность из двух строк. Если это не так, возникает ошибка:
def return_three(x):
return np.array([1, 2, 3])
df.groupby('State').transform(return_three)
ValueError: transform must return a scalar value for each group
Сообщение об ошибке на самом деле не описывает проблему. Вы должны вернуть последовательность той же длины, что и группа. Итак, такая функция будет работать:
def rand_group_len(x):
return np.random.rand(len(x))
df.groupby('State').transform(rand_group_len)
a b
0 0.962070 0.151440
1 0.440956 0.782176
2 0.642218 0.483257
3 0.056047 0.238208
Возвращение одного скалярного объекта также работает для transform
Если вы вернете только один скаляр из своей пользовательской функции, то transform
будете использовать его для каждой строки в группе:
def group_sum(x):
return x.sum()
df.groupby('State').transform(group_sum)
a b
0 9 16
1 9 16
2 4 14
3 4 14
transform
должна возвращать число, строку или ту же форму, что и аргумент. если это число, то число будет установлено для всех элементов в группе, если это строка, она будет транслироваться на все строки в группе. В вашем коде лямбда-функция возвращает столбец, который нельзя передать группе.