【问题标题】:How to merge two tables and transpose rows to columns如何合并两个表并将行转置为列
【发布时间】:2016-09-07 06:14:21
【问题描述】:

我有这两张表:

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

如何创建列s1s2s3,每列对应一行(每个id的行数总是固定的,等于3)?

【问题讨论】:

  • 请注意@unutbu 的解决方案 - 与我的相比,它应该快得多

标签: python pandas dataframe


【解决方案1】:

更新:

正如@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如果你想重命名所有数字列,你可以这样做(假设你将结果保存到rsltDF):

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

【讨论】:

  • 能否请您简要评论一下这段代码背后的逻辑?
  • @Klue,我已经为我的回答添加了一个简短的解释 - 请检查
  • 仅供参考,像这样使用 apply 是非惯用且非性能的。使用像 @ubuntu soln 这样的整形运算符会好很多。
【解决方案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

【讨论】:

    【解决方案3】:

    这是我使用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-08
      • 1970-01-01
      • 2014-01-27
      相关资源
      最近更新 更多