Может ли pandas группировать агрегат в список, а не суммировать, означать и т.д.?

У меня был успех с использованием функции groupby для суммирования или усреднения заданной переменной по группам, но есть ли способ агрегировать в список значений, а не для получения единственного результата? (И будет ли это еще называться агрегацией?)

Я не совсем уверен, что это подход, который я должен принимать, так что ниже приведен пример преобразования, которое я хотел бы сделать, с игрушечными данными.

То есть, если данные выглядят примерно так:

    A    B    C  
    1    10   22
    1    12   20
    1    11   8
    1    10   10
    2    11   13
    2    12   10 
    3    14   0

То, что я пытаюсь закончить, это что-то вроде следующего. Я не совсем уверен, что это можно сделать, объединив группы в списки, и я довольно потерял, куда идти отсюда.

Гипотетический вывод:

     A    B    C  New1  New2  New3  New4  New5  New6
    1    10   22  12    20    11    8     10    10
    2    11   13  12    10 
    3    14   0

Может быть, мне следовало бы сфокусироваться? Порядок, по которому данные помещаются в столбцы, не имеет значения - все столбцы B через New6 в этом примере эквивалентны. Все предложения/исправления очень ценятся.

Ответ 1

мое решение немного дольше, чем вы можете ожидать, я уверен, что его можно сократить, но:

g = df.groupby("A").apply(lambda x: pd.concat((x["B"], x["C"])))
k = g.reset_index()
k["i"] = k1.index
k["rn"] = k1.groupby("A")["i"].rank()
k.pivot_table(rows="A", cols="rn", values=0)

# output
# rn   1   2   3   4   5   6
# A                         
# 1   10  12  11  22  20   8
# 2   10  11  10  13 NaN NaN
# 3   14  10 NaN NaN NaN NaN

Немного объяснения. Первая строка, g = df.groupby("A").apply(lambda x: pd.concat((x["B"], x["C"]))). Эта одна группа df на A, а затем поместите столбцы B и C в один столбец:

A   
1  0    10
   1    12
   2    11
   0    22
   1    20
   2     8
2  3    10
   4    11
   3    10
   4    13
3  5    14
   5    10

Затем k = g.reset_index(), создавая последовательный индекс, результат:

    A  level_1   0
0   1        0  10
1   1        1  12
2   1        2  11
3   1        0  22
4   1        1  20
5   1        2   8
6   2        3  10
7   2        4  11
8   2        3  10
9   2        4  13
10  3        5  14
11  3        5  10

Теперь я хочу переместить этот индекс в столбец (я хотел бы услышать, как я могу сделать последовательный столбец без сброса индекса), k["i"] = k1.index:

    A  level_1   0   i
0   1        0  10   0
1   1        1  12   1
2   1        2  11   2
3   1        0  22   3
4   1        1  20   4
5   1        2   8   5
6   2        3  10   6
7   2        4  11   7
8   2        3  10   8
9   2        4  13   9
10  3        5  14  10
11  3        5  10  11

Теперь k["rn"] = k1.groupby("A")["i"].rank() добавит row_number внутри каждого A (например, row_number() over(partition by A order by i) в SQL:

    A  level_1   0   i  rn
0   1        0  10   0   1
1   1        1  12   1   2
2   1        2  11   2   3
3   1        0  22   3   4
4   1        1  20   4   5
5   1        2   8   5   6
6   2        3  10   6   1
7   2        4  11   7   2
8   2        3  10   8   3
9   2        4  13   9   4
10  3        5  14  10   1
11  3        5  10  11   2

И, наконец, только поворот с помощью k.pivot_table(rows="A", cols="rn", values=0):

rn   1   2   3   4   5   6
A                         
1   10  12  11  22  20   8
2   10  11  10  13 NaN NaN
3   14  10 NaN NaN NaN NaN

Ответ 2

Я использовал следующие

grouped = df.groupby('A')

df = grouped.aggregate(lambda x: tuple(x))

df['grouped'] = df['B'] + df['C']

Ответ 3

Я отвечаю на вопрос, как указано в его заголовке и первом предложении: следующие значения агрегируются в списки.

import pandas as pd

df = pd.DataFrame( {'A' : [1, 1, 1, 1, 2, 2, 3], 'B' : [10, 12, 11, 10, 11, 12, 14], 'C' : [22, 20,     8, 10, 13, 10, 0]})
print df

# Old version
# df2=df.groupby(['A']).apply(lambda tdf: pd.Series(  dict([[vv,tdf[vv].unique().tolist()] for vv in tdf if vv not in ['A']])  )) 
df2 = df.groupby('A').aggregate(lambda tdf: tdf.unique().tolist())
print df2

Вывод следующий:

In [3]: run tmp
   A   B   C
0  1  10  22
1  1  12  20
2  1  11   8
3  1  10  10
4  2  11  13
5  2  12  10
6  3  14   0

[7 rows x 3 columns]
              B                C
A                               
1  [10, 12, 11]  [22, 20, 8, 10]
2      [11, 12]         [13, 10]
3          [14]              [0]

[3 rows x 2 columns]

Ответ 4

Вот один лайнер

# if list of unique items is desired, use set
df.groupby('A',as_index=False)['B'].aggregate(lambda x: set(x))

# if duplicate items are okay, use list
df.groupby('A',as_index=False)['B'].aggregate(lambda x: list(x))

Ответ 5

Подобное решение, но довольно прозрачное (я думаю). Вы можете получить полный список или уникальные списки.

df = pd.DataFrame({'A':[1,1,2,2,2,3,3,3,4,5], 
                   'B':[6,7, 8,8,9, 9,9,10,11,12], 
                   'C':['foo']*10})

df
Out[24]: 
   A   B    C
0  1   6  foo
1  1   7  foo
2  2   8  foo
3  2   8  foo
4  2   9  foo
5  3   9  foo
6  3   9  foo
7  3  10  foo
8  4  11  foo
9  5  12  foo

list_agg = df.groupby(by='A').agg({'B':lambda x: list(x), 
                                   'C':lambda x: tuple(x)})

list_agg
Out[26]: 
                 C           B
A                             
1       (foo, foo)      [6, 7]
2  (foo, foo, foo)   [8, 8, 9]
3  (foo, foo, foo)  [9, 9, 10]
4           (foo,)        [11]
5           (foo,)        [12]

unique_list_agg = df.groupby(by='A').agg({'B':lambda x: list(pd.unique(x)), 
                                          'C':lambda x: tuple(pd.unique(x))})

unique_list_agg
Out[28]: 
        C        B
A                 
1  (foo,)   [6, 7]
2  (foo,)   [8, 9]
3  (foo,)  [9, 10]
4  (foo,)     [11]
5  (foo,)     [12]

Ответ 6

Я боролся с одними и теми же проблемами, и ответ заключается в том, что да, вы можете использовать grouby для получения списков. Я не уверен на 100%, что я делаю это самым питоническим способом, но здесь для чего стоит моя попытка дойти до вашего вопроса. Вы можете создавать списки данных, содержащихся в таких группах:

import pandas as pd
import numpy as np
from itertools import chain

Data = {'A' : [1, 1, 1, 1, 2, 2, 3], 'B' : [10, 12, 11, 10, 11, 12, 14], 'C' : [22, 20,     8, 10, 13, 10, 0]}
DF = pd.DataFrame(Data)
DFGrouped = DF.groupby('A')

OutputLists = []

for group in DFGrouped:
    AList = list(group[1].A)
    BList = list(group[1].B)
    CList = list(group[1].C)
    print list(group[1].A)
    print list(group[1].B)
    print list(group[1].C)
    ZIP =  zip(BList, CList)
    print ZIP
    OutputLists.append(list(chain(*ZIP)))

OutputLists

Это выводит ваши данные в список списков, так, как мне кажется, вы этого хотите. Затем вы можете создать кадр данных. Вышеприведенные заявления для печати приведены только в иллюстративных целях. Наиболее эффективным (с точки зрения кода) способом сделать это с использованием моего метода является следующее:

import pandas as pd
import numpy as np
from itertools import chain

Data = {'A' : [1, 1, 1, 1, 2, 2, 3], 'B' : [10, 12, 11, 10, 11, 12, 14], 'C' : [22, 20, 8, 10, 13, 10, 0]}
DF = pd.DataFrame(Data)
DFGrouped = DF.groupby('A')
OutputLists = []
for group in DFGrouped:
    ZIPPED = zip(group[1].B, group[1].C)
    OutputLists.append(list(chain(*ZIPPED)))
OutputLists

Ключом к получению списков из сгруппированных данных, насколько я могу судить, является признание того, что сами данные хранятся в группе [1] для каждой группы в ваших сгруппированных данных.

надеюсь, что это поможет!

Ответ 7

df2 = df.groupby('A').aggregate(lambda tdf: tdf.unique().tolist())

Кажется, это работает идеально, но результирующий информационный кадр имеет два слоя столбцов, а df.columns показывает только один столбец в информационном кадре. Чтобы исправить это, используйте:

df2_copy=df2.copy()
df2_copy = df2_copy.reset_index(col_level=0)

Вы можете просмотреть уровни столбцов, используя: df2_copy.columns = df2_copy.columns.get_level_values (0)

df2_copy() должен решить это.