Как объединить две таблицы и перенести строки в столбцы

У меня есть эти две таблицы:

Т1

id  x       y
8   42      1.9
9   30      1.9

T2

id  signal
8   55
8   56  
8   59
9   57
9   58  
9   60

Цель состоит в том, чтобы получить новую таблицу T3:

id  x       y       s1      s2      s3
8   42      1.9     55      56      58
9   30      1.9     57      58      60

Если я выполняю эту операцию, то она выполняет только слияние без транспонирования:

pd.merge(T1, T2, on=['id'])

Как создать столбцы s1, s2 и s3, каждый из которых соответствует строке (количество строк в id всегда фиксировано и равно 3)?


person Klue    schedule 13.06.2016    source источник
comment
пожалуйста, обратите внимание на решение @unutbu - оно должно быть намного быстрее по сравнению с моим   -  person MaxU    schedule 13.06.2016


Ответы (3)


ОБНОВЛЕНИЕ:

как написал @Jeff в своем комментарии, решение @ubuntu должно быть быстрее и более идиоматично по сравнению с моим:

In [40]: T1.merge(
   ....:     T2.pivot_table(index='id',
   ....:                    values='signal',
   ....:                    columns='s' + T2.groupby(['id'])['signal'].cumcount().astype(str))
   ....:       .reset_index()
   ....: )
Out[40]:
   id   x    y  s0  s1  s2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60

СТАРЫЙ ответ:

вы можете сделать это следующим образом:

In [209]: %paste
(t1.set_index('id')
   .join(t2.groupby('id')['signal']
           .apply(lambda x: x.tolist())
           .apply(pd.Series))
   .reset_index()
)
## -- End pasted text --
Out[209]:
   id   x    y   0   1   2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60

Объяснение:

сгруппировать T2 по id и "собрать" все соответствующие сигналы в списки

In [211]: t2.groupby('id')['signal'].apply(lambda x: x.tolist())
Out[211]:
id
8    [55, 56, 59]
9    [57, 58, 60]
Name: signal, dtype: object

расширить списки до столбцов

In [213]: t2.groupby('id')['signal'].apply(lambda x: x.tolist()).apply(pd.Series)
Out[213]:
     0   1   2
id
8   55  56  59
9   57  58  60

и, наконец, соедините обе таблицы по индексу id

PS если вы хотите переименовать все числовые столбцы, вы можете сделать это так (при условии, что вы сохранили результат в rslt DF):

In [224]: rslt.columns = [c if c in ['id','x','y'] else 's{}'.format(c) for c in rslt.columns.tolist()]

In [225]: rslt
Out[225]:
   id   x    y  s0  s1  s2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60
person MaxU    schedule 13.06.2016
comment
Не могли бы вы кратко прокомментировать логику этого кода? - person Klue; 13.06.2016
comment
@Klue, я добавил краткое объяснение к своему ответу - пожалуйста, проверьте - person MaxU; 13.06.2016
comment
просто к вашему сведению, использование приложения подобно этому неидиоматично и неэффективно. Использование операторов изменения формы, таких как @ubuntu soln, намного лучше. - person Jeff; 13.06.2016

Вот мой способ использования groupby и unstack:

df = df1.merge(df2.groupby('id')['signal'].apply(lambda x: x.reset_index(drop=True)).unstack().reset_index())

df
Out[63]: 
   id   x    y   0   1   2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60

Если я разделю их:

df2t = df2.groupby('id')['signal'].apply(lambda x: x.reset_index(drop=True)).unstack().reset_index()

df2t
Out[59]: 
   id   0   1   2
0   8  55  56  59
1   9  57  58  60

df = df1.merge(df2t)

df
Out[61]: 
   id   x    y   0   1   2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60
person MaThMaX    schedule 13.06.2016

Еще один способ — использовать groupby/cumcount/основной:

import pandas as pd
T1 = pd.DataFrame({'id': [8, 9], 'x': [42, 30], 'y': [1.9, 1.9]})
T2 = pd.DataFrame({'id': [8, 8, 8, 9, 9, 9], 'signal': [55, 56, 59, 57, 58, 60]})
T2['col'] = 's' + T2.groupby(['id'])['signal'].cumcount().astype(str)
T2 = T2.pivot(index='id', columns='col', values='signal').reset_index()
result = pd.merge(T1, T2)
print(result)

урожаи

   id   x    y  s0  s1  s2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60

Основная хитрость заключается в том, чтобы использовать groupby/cumcount для добавления кумулятивного количества для каждой группы в T2:

In [81]: T2['col'] = 's' + T2.groupby(['id'])['signal'].cumcount().astype(str); T2
Out[81]: 
   id  signal col
0   8      55  s0
1   8      56  s1
2   8      59  s2
3   9      57  s0
4   9      58  s1
5   9      60  s2

Затем pivot можно использовать для преобразования T2 в желаемую форму (или, по крайней мере, близкую к ней):

In [82]: T2 = T2.pivot(index='id', columns='col', values='signal').reset_index(); T2
Out[82]: 
col  id  s0  s1  s2
0     8  55  56  59
1     9  57  58  60

и result можно получить слиянием:

In [83]: pd.merge(T1, T2)
Out[83]: 
   id   x    y  s0  s1  s2
0   8  42  1.9  55  56  59
1   9  30  1.9  57  58  60
person unutbu    schedule 13.06.2016