2016-06-13 95 views
2

我有这两个表中的列:如何合并两个表和转行

T1

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']) 

如何创建s2s1,并s3,分别对应于行(每id行数始终是固定的等于3)?

+0

请@ unutbu的解决方案关注 - 它应该是比较快很多矿山 – MaxU

回答

4

UPDATE:

为@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 

OLD答案:

你可以这样做:

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 

说明:

T2id和 “收集” 所有相应的信号转换成列表

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如果你想重命名所有的数字列,你c一个做这种方式(假设你保存结果到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 
+0

能否请您简要评论这个代码背后的逻辑是什么? – Klue

+1

@Klue,我为我的答案添加了一个简短的解释 - 请检查 – MaxU

+1

只是供参考,使用像这样的应用是非惯用和非高性能。使用像@ubuntu soln这样的重塑运营商要好得多。 – Jeff

2

这是我使用groupbyunstack方式:

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 
2

另一种方法是使用groupby/cumcount/pivot

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