dg.groupby(...). agg (set) производит отличный результат по сравнению с df.groupby(...). agg (lambda x: set (x))

Отвечая на этот вопрос, оказалось, что df.groupby(...).agg(set) и df.groupby(...).agg(lambda x: set(x)) дают разные результаты.

Данные:

df = pd.DataFrame({
       'user_id': [1, 2, 3, 4, 1, 2, 3], 
       'class_type': ['Krav Maga', 'Yoga', 'Ju-jitsu', 'Krav Maga', 
                      'Ju-jitsu','Krav Maga', 'Karate'], 
       'instructor': ['Bob', 'Alice','Bob', 'Alice','Alice', 'Alice','Bob']})

Демо-версия:

In [36]: df.groupby('user_id').agg(lambda x: set(x))
Out[36]:
                    class_type    instructor
user_id
1        {Krav Maga, Ju-jitsu}  {Alice, Bob}
2            {Yoga, Krav Maga}       {Alice}
3           {Ju-jitsu, Karate}         {Bob}
4                  {Krav Maga}       {Alice}

In [37]: df.groupby('user_id').agg(set)
Out[37]:
                                class_type                         instructor
user_id
1        {user_id, class_type, instructor}  {user_id, class_type, instructor}
2        {user_id, class_type, instructor}  {user_id, class_type, instructor}
3        {user_id, class_type, instructor}  {user_id, class_type, instructor}
4        {user_id, class_type, instructor}  {user_id, class_type, instructor}

Я бы ожидал такого же поведения здесь - знаете ли вы, что мне не хватает?

Ответ 1

Хорошо, что здесь происходит, это то, что set не обрабатывается, поскольку он не is_list_like в _aggregate:

elif is_list_like(arg) and arg not in compat.string_types:

см. источник

это не is_list_like поэтому он возвращает None в цепочку звонков, чтобы закончить в этой строке:

results.append(colg.aggregate(a))

см. источник

это вызывает TypeError как TypeError: 'type' object is not iterable

который затем поднимает:

if not len(results):
    raise ValueError("no results")

см. источник

поэтому, поскольку у нас нет результатов, мы вызываем _aggregate_generic:

см. источник

это затем вызывает:

result[name] = self._try_cast(func(data, *args, **kwargs)

см. источник

Затем это заканчивается так:

(Pdb) n
> c:\programdata\anaconda3\lib\site-packages\pandas\core\groupby.py(3779)_aggregate_generic()
-> return self._wrap_generic_output(result, obj)

(Pdb) result
{1: {'user_id', 'instructor', 'class_type'}, 2: {'user_id', 'instructor', 'class_type'}, 3: {'user_id', 'instructor', 'class_type'}, 4: {'user_id', 'instructor', 'class_type'}}

Я запускаю немного другую версию панд, но эквивалентная строка источника - https://github.com/pandas-dev/pandas/blob/v0.22.0/pandas/core/groupby.py#L3779

По сути, потому что set не считается функцией или итерируемым, он просто сворачивается к вызову ctor в последовательной последовательности, которая в этом случае является столбцами, здесь вы можете увидеть тот же эффект:

In [8]:

df.groupby('user_id').agg(lambda x: print(set(x.columns)))
{'class_type', 'instructor', 'user_id'}
{'class_type', 'instructor', 'user_id'}
{'class_type', 'instructor', 'user_id'}
{'class_type', 'instructor', 'user_id'}
Out[8]: 
        class_type instructor
user_id                      
1             None       None
2             None       None
3             None       None
4             None       None

но когда вы используете lambda которая является анонимной функцией, это работает так, как ожидалось.

Ответ 2

Возможно, как отметил agg применяет встроенные функции python, рассматривая объект groupby как мини-фрейм данных, тогда как при передаче определенной функции он применяет его для каждого столбца. Пример, иллюстрирующий это, - это печать.

df.groupby('user_id').agg(print,end='\n\n')

 class_type instructor  user_id
0  Krav Maga        Bob        1
4   Ju-jitsu      Alice        1

  class_type instructor  user_id
1       Yoga      Alice        2
5  Krav Maga      Alice        2

  class_type instructor  user_id
2   Ju-jitsu        Bob        3
6     Karate        Bob        3


df.groupby('user_id').agg(lambda x : print(x,end='\n\n'))

0    Krav Maga
4     Ju-jitsu
Name: class_type, dtype: object

1         Yoga
5    Krav Maga
Name: class_type, dtype: object

2    Ju-jitsu
6      Karate
Name: class_type, dtype: object

3    Krav Maga
Name: class_type, dtype: object

...

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