Python 合并数据帧时如何保持列多索引值

Python 合并数据帧时如何保持列多索引值,python,pandas,merge,Python,Pandas,Merge,我有两个数据帧,如下所示: df1 = pd.DataFrame({('Q1', 'SubQ1'):[1, 2, 3], ('Q1', 'SubQ2'):[1, 2, 3], ('Q2', 'SubQ1'):[1, 2, 3]}) df1['ID'] = ['a', 'b', 'c'] df2 = pd.DataFrame({'item_id': ['a', 'b', 'c'], 'url':['a.com', 'blah.com', 'company.com']}) df1: Q

我有两个数据帧,如下所示:

df1 = pd.DataFrame({('Q1', 'SubQ1'):[1, 2, 3], ('Q1', 'SubQ2'):[1, 2, 3], ('Q2', 'SubQ1'):[1, 2, 3]})
df1['ID'] = ['a', 'b', 'c']

df2 = pd.DataFrame({'item_id': ['a', 'b', 'c'], 'url':['a.com', 'blah.com', 'company.com']})
df1

     Q1          Q2 ID
  SubQ1 SubQ2 SubQ1   
0     1     1     1  a
1     2     2     2  b
2     3     3     3  c
df2

  item_id          url
0       a        a.com
1       b     blah.com
2       c  company.com
请注意,
df1
有一些列具有层次索引(例如
('Q1','SubQ1')
),还有一些列具有普通索引(例如
ID

我想在
ID
item\u ID
字段上合并这两个数据帧。使用:

result = pd.merge(df1, df2, left_on='ID', right_on='item_id')
给出:

   (Q1, SubQ1)  (Q1, SubQ2)  (Q2, SubQ1) (ID, ) item_id          url
0            1            1            1      a       a        a.com
1            2            2            2      b       b     blah.com
2            3            3            3      c       c  company.com
如您所见,合并本身工作正常,但多索引已丢失并恢复为元组。我尝试使用
pd.MultiIndex.from\u tuples
重新创建多索引,如下所示:

result.columns = pd.MultiIndex.from_tuples(result)
但这会导致
item_id
url
列出现问题,只使用其名称的前两个字符:

     Q1          Q2 ID  i            u
  SubQ1 SubQ2 SubQ1     t            r
0     1     1     1  a  a        a.com
1     2     2     2  b  b     blah.com
2     3     3     3  c  c  company.com
df2
中的列转换为一个元素元组(即
('item\u id',)
而不仅仅是
'item\u id'
)没有区别


如何合并这两个数据帧并正确保留多索引?或者,我如何获得合并的结果并返回具有适当多索引的列,而不弄乱
项目id
url
列的名称?

id的列不是“非层次”的。它由
('ID',)
表示。但是,
pandas
允许您仅引用第一级列,其方式看起来就像是引用单级列结构。这意味着这应该可以工作
df1['ID']
以及
df1[('ID',)]
以及
df1.loc[:,('ID',)]
。但是如果碰巧顶层
'ID'
在第二层中有更多与之相关联的列,
df1['ID']
将返回一个数据帧。我觉得这个解决方案更合适,它看起来很像@JohnGalt在评论中的答案

df1.assign(u=df1[('ID', '')].map(df2.set_index('item_id').url))

     Q1          Q2 ID            u
  SubQ1 SubQ2 SubQ1                
0     1     1     1  a        a.com
1     2     2     2  b     blah.com
2     3     3     3  c  company.com

将单级列数据帧连接到多级列数据帧是困难的。我不得不人为地增加一个关卡

def rnm(d):
    d = d.copy()
    d.columns = [d.columns, [''] * len(d.columns)]
    return d

df1.join(rnm(df2.set_index('item_id')), on=('ID',))

     Q1          Q2 ID          url
  SubQ1 SubQ2 SubQ1                
0     1     1     1  a        a.com
1     2     2     2  b     blah.com
2     3     3     3  c  company.com

如果你不能打败他们,加入他们(合并前使两个数据帧具有相同数量的索引级别):

屈服

     Q1          Q2 ID item_id          url
  SubQ1 SubQ2 SubQ1                        
0     1     1     1  a       a        a.com
1     2     2     2  b       b     blah.com
2     3     3     3  c       c  company.com
这也避免了出现
UserWarning

pandas/core/reforme/merge.py:551:UserWarning:不同级别之间的合并可能会产生意外结果(左侧2个级别,右侧1个级别)


此解决方案更加灵活,因为您不必在concat之前插入列级别,您可以使用它来concat任意数量的级别:

import pandas as pd

df1 = pd.DataFrame({('A', 'b'): [1, 2], ('A', 'c'): [3, 4]})

df2 = pd.DataFrame({'Zaa': [1, 2]})

df3 = pd.DataFrame({('Maaa', 'k', 'l'): [1, 2]})

df = pd.concat([df1, df2, df3], axis=1)
cols = [col if isinstance(col, tuple) else (col, ) for col in df.columns]
df.columns = pd.MultiIndex.from_tuples(cols)

df1.assign(u=df1.ID.map(df2.set_index('item_ID')['url']))
对您有用吗?它适用于这个示例,但在实际情况中,我想在联接中引入多个列,因此我必须多次这样做才能得到每个列,我觉得这并不理想。
import pandas as pd

df1 = pd.DataFrame({('A', 'b'): [1, 2], ('A', 'c'): [3, 4]})

df2 = pd.DataFrame({'Zaa': [1, 2]})

df3 = pd.DataFrame({('Maaa', 'k', 'l'): [1, 2]})

df = pd.concat([df1, df2, df3], axis=1)
cols = [col if isinstance(col, tuple) else (col, ) for col in df.columns]
df.columns = pd.MultiIndex.from_tuples(cols)