Pandas groupby apply vs transform с определенными функциями
я не понимаю, какие функции приемлемы для groupby
+ transform
операции. Часто я просто гадаю, тестирую, возвращаюсь, пока что-то не сработает, но я чувствую, что должен быть систематический способ определить, будет ли решение работать.
вот минимальный пример. Сначала давайте использовать groupby
+ apply
С set
:
df = pd.DataFrame({'a': [1,2,3,1,2,3,3], 'b':[1,2,3,1,2,3,3], 'type':[1,0,1,0,1,0,1]})
g = df.groupby(['a', 'b'])['type'].apply(set)
print(g)
a b
1 1 {0, 1}
2 2 {0, 1}
3 3 {0, 1}
это работает нормально, но я хочу, чтобы в результате set
вычисляется groupwise в новом столбце исходного фрейм данных. Поэтому я пытаюсь использовать transform
:
df['g'] = df.groupby(['a', 'b'])['type'].transform(set)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
---> 23 df['g'] = df.groupby(['a', 'b'])['type'].transform(set)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'set'
это ошибка, которую я вижу в Pandas v0.19.0. В v0.23.0, я вижу TypeError: 'set' type is unordered
. Конечно, я могу отобразить специально определенный индекс для достижения моего результата:
g = df.groupby(['a', 'b'])['type'].apply(set)
df['g'] = df.set_index(['a', 'b']).index.map(g.get)
print(df)
a b type g
0 1 1 1 {0, 1}
1 2 2 0 {0, 1}
2 3 3 1 {0, 1}
3 1 1 0 {0, 1}
4 2 2 1 {0, 1}
5 3 3 0 {0, 1}
6 3 3 1 {0, 1}
но я думал о пользе transform
было избежать такого явного отображения. Где я ошибся?
версии, которые я использую:
2 ответов
в вашем первом результате вы на самом деле не пытаетесь преобразование ваши ценности, а скорее совокупность их (которые будут работать так, как вы намеревались).
но попадая в коде transform
"документы" истина в том, что
возвращает результат, либо такого же размера, как группа chunk или касается всех размеров группы блока.
когда вы
df.groupby(['a', 'b'])['type'].transform(some_func)
вы на самом деле преобразование каждого pd.Объект серии из каждой группы в новый объект, используя свои
результат преобразования ограничен определенными типами. [например, это не может быть list
, set
, Series
etc. -- это некорректно, спасибо @RafaelC за комментарий] Я не думаю, что это документировано, но при изучении исходного кода groupby.py
и series.py
вы можете найти такие ограничения типа.
С groupby
документация
на
transform
метод возвращает объект это индексируется так же (того же размера), как и сгруппированный. Функция преобразования должна:
- возвращает результат, который является либо тот же размер, что и группа chunk или broadcastable до размера группы chunk (например, скаляр, сгруппированные.преобразование (лямбда x: x.iloc[-1]).
столбцов действуют на группы чанка. Преобразование применяется к первому фрагменту группы с помощью chunk.применять.
не выполняйте операции на месте над фрагментом группы. Групповые фрагменты следует рассматривать как неизменяемые, и изменения в групповом фрагменте могут привести к неожиданным результатам. Например, при использовании fillna inplace должно быть False (сгруппировано.преобразование (лямбда x: x.fillna (inplace=False)).
(необязательно) работает со всем фрагментом группы. Если это поддерживается, используется быстрый путь, начиная со второго фрагмента.
отказ от ответственности: я получил другая ошибка (pandas
версия 0.23.1):
df['g'] = df.groupby(['a', 'b'])['type'].transform(set)
File "***/lib/python3.6/site-packages/pandas/core/groupby/groupby.py", line 3661, in transform
s = klass(res, indexer) s = klass(res, indexer)
File "***/lib/python3.6/site-packages/pandas/core/series.py", line 242, in __init__
"".format(data.__class__.__name__))
TypeError: 'set' type is unordered
обновление
после преобразования группы В набор, pandas
не могу передать его в Series
, потому что он неупорядочен (и имеет разные размеры, чем групповой фрагмент) . Если мы заставим его в список, он станет такого же размера, как и групповой фрагмент, и мы получим только одно значение в строке. Ответ заключается в том, чтобы обернуть его в некоторый контейнер, поэтому результирующий размер объекта станет 1, а затем pandas
сможет транслировать его:
df['g'] = df.groupby(['a', 'b'])['type'].transform(lambda x: np.array(set(x)))
print(df)
a b type g
0 1 1 1 {0, 1}
1 2 2 0 {0, 1}
2 3 3 1 {0, 1}
3 1 1 0 {0, 1}
4 2 2 1 {0, 1}
5 3 3 0 {0, 1}
6 3 3 1 {0, 1}
почему я выбрал np.array
как контейнер? Потому что series.py
(строка 205:206) пройдите этот тип без дальнейших проверок. Поэтому я считаю, что это поведение будет сохранено в будущих версиях.