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) пройдите этот тип без дальнейших проверок. Поэтому я считаю, что это поведение будет сохранено в будущих версиях.