基于多重索引层高效连接两个数据框
我经常会有一个包含多个索引的大数据表(dataframe),还有一个较小的、也是多重索引的数据表,这个小的数据表通常是某种查找表。我常常想把这个查找表中的列添加到大数据表中。因为大数据表通常很大,所以我希望这个过程能高效一些。
这里有一个假想的例子,我构造了两个数据表 df1
和 df2
:
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 个回答
这个功能目前还没有内部实现,但你提到的解决方案是推荐的,可以参考这里和这个问题
如果你想让代码看起来更整洁,可以把它放在一个函数里。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'])
不过,合并数据本身就会创建新的数据,所以不太确定这样做能节省多少资源。
更好的方法是先构建较小的数据框,然后再进行大的合并。你可能还想看看这个链接里的内容。
现在,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