基于多重索引层高效连接两个数据框

34 投票
2 回答
41717 浏览
提问于 2025-04-18 07:57

我经常会有一个包含多个索引的大数据表(dataframe),还有一个较小的、也是多重索引的数据表,这个小的数据表通常是某种查找表。我常常想把这个查找表中的列添加到大数据表中。因为大数据表通常很大,所以我希望这个过程能高效一些。

这里有一个假想的例子,我构造了两个数据表 df1df2

import pandas as pd
import numpy as np

arrays = [['sun', 'sun', 'sun', 'moon', 'moon', 'moon', 'moon', 'moon'],
          ['summer', 'winter', 'winter', 'summer', 'summer', 'summer', 'winter', 'winter'],
          ['one', 'one', 'two', 'one', 'two', 'three', 'one', 'two']]

tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['Body', 'Season','Item'])
df1 = pd.DataFrame(np.random.randn(8,2), index=index,columns=['A','B'])

index2= pd.MultiIndex.from_tuples([('sun','summer'),('sun','winter'),('moon','summer'),('moon','winter')],
                                  names=['Body','Season'])

df2 = pd.DataFrame(['Good','Bad','Ugly','Confused'],index=index2,columns = ['Mood'])

这是数据表的内容:

df1

                    A         B
Body Season Item                     
sun  summer one   -0.409372  0.638502
     winter one    1.448772 -1.460596
            two   -0.495634 -0.839063
moon summer one    1.296035 -1.439349
            two   -1.002667  0.508394
            three -1.247748 -0.645782
     winter one   -1.848857 -0.858759
            two    0.559172  2.202957

df2

                 Mood
Body Season          
sun  summer      Good
     winter       Bad
moon summer      Ugly
     winter  Confused

现在,假设我想把 df2 的列添加到 df1 中?我找到的唯一方法是这一行:

df1 = df1.reset_index().join(df2,on=['Body','Season']).set_index(df1.index.names)

结果是:

           A         B      Mood
Body Season Item
sun  summer one   -0.121588  0.272774      Good
     winter one    0.233562 -2.005623       Bad
            two   -1.034642  0.315065       Bad
moon summer one    0.184548  0.820873      Ugly
            two    0.838290  0.495047      Ugly
            three  0.450813 -2.040089      Ugly
     winter one   -1.149993 -0.498148  Confused
            two    2.406824 -2.031849  Confused

[8 rows x 3 columns]

这个方法可以实现,但有两个问题。首先,这一行代码看起来很复杂。需要先重置索引,然后再重新创建多重索引,让这个简单的操作显得不必要地复杂。其次,如果我理解没错,每次运行 reset_index() 和 set_index() 时,都会创建一个数据表的副本。我经常处理非常大的数据表,这样做似乎效率很低。

有没有更好的方法来实现这个呢?

2 个回答

13

这个功能目前还没有内部实现,但你提到的解决方案是推荐的,可以参考这里这个问题

如果你想让代码看起来更整洁,可以把它放在一个函数里。reset_index/set_index确实会复制数据(不过你可以传一个参数inplace=True来改变这个行为);实际上,这些操作是在原地进行的,因为它们只是修改了索引属性。

你可以添加一个好用的函数,比如:

def merge_multi(self, df, on):
    return self.reset_index().join(df,on=on).set_index(self.index.names)
DataFrame.merge_multi = merge_multi

df1.merge_multi(df2,on=['Body','Season'])

不过,合并数据本身就会创建新的数据,所以不太确定这样做能节省多少资源。

更好的方法是先构建较小的数据框,然后再进行大的合并。你可能还想看看这个链接里的内容。

25

现在,join 方法可以合并带有多重索引的 DataFrame,即使它们的索引部分匹配也没问题。

根据你的例子:

df1 = df1.join(df2, on=['Body','Season'])

确保在 on 参数中指定的列顺序,和另一个 DataFrame 的索引顺序完全一致,因为 on 参数是用来匹配你调用的 DataFrame 中标签的顺序,和你要合并的 other DataFrame 的索引顺序。

或者,你也可以直接使用 join,不指定 on 参数,这样默认会使用两个 DataFrame 之间的公共索引级别:

df1 = df1.join(df2)

合并后的 df1

                          A         B      Mood
Body Season Item                               
sun  summer one   -0.483779  0.981052      Good
     winter one   -0.309939  0.803862       Bad
            two   -0.413732  0.025331       Bad
moon summer one   -0.926068 -1.316808      Ugly
            two    0.221627 -0.226154      Ugly
            three  1.064856  0.402827      Ugly
     winter one    0.526461 -0.932231  Confused
            two   -0.296415 -0.812374  Confused

撰写回答