我经常拥有一个带有大型多索引的数据框,以及一个带有多索引的辅助数据框,该多索引是较大索引的子集。辅助数据帧通常是某种查找表。我经常想要将查找表中的列添加到更大的数据帧中。主DataFrame通常非常大,所以我想有效地做到这一点。
这是一个虚构的例子,我想将df2加入到df1:
In [11]: 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']]
In [12]: tuples = list(zip(*arrays))
In [13]: index = pd.MultiIndex.from_tuples(tuples, names=['Body', 'Season','Item'])
In [14]: df1 = pd.DataFrame(np.random.randn(8,2), index=index,columns=['A','B'])
In [15]: df1
Out[15]:
A B
Body Season Item
sun summer one -0.121588 0.272774
winter one 0.233562 -2.005623
two -1.034642 0.315065
moon summer one 0.184548 0.820873
two 0.838290 0.495047
three 0.450813 -2.040089
winter one -1.149993 -0.498148
two 2.406824 -2.031849
[8 rows x 2 columns]
In [16]: index2= pd.MultiIndex.from_tuples([('sun','summer'),('sun','winter'),('moon','summer'),('moon','winter')],names=['Body','Season'])
In [17]: df2 = pd.DataFrame(['Good','Bad','Ugly','Confused'],index=index2,columns = ['Mood'])
In [18]: df2
Out[18]:
Mood
Body Season
sun summer Good
winter Bad
moon summer Ugly
winter Confused
[4 rows x 1 columns]
现在,假设我想将df2中的列添加到df1?这条线是我找到工作的唯一方法:
In [19]: df1 = df1.reset_index().join(df2,on=['Body','Season']).set_index(df1.index.names)
In [20]: df1
Out[20]:
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()时,都会创建一个数据帧的副本。我经常使用非常大的数据帧,这看起来非常低效。
有更好的方法吗?
答案 0 :(得分:7)
我知道这很旧,但是join in Pandas 1.0.3(和at least since 0.24)允许合并具有部分匹配索引的multiIndex数据帧。
按照您的示例:
df1 = df1.join(df2, on=['Body','Season'])
答案 1 :(得分:6)
这不是在ATM内部实施的,但推荐使用自己的解决方案,请参阅here以及issue
如果你想让它看起来更漂亮,你可以简单地将它包装在一个函数中。 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'])
但是,按定义合并会创建新数据,因此不确定这实际上会为您节省多少。
更好的方法是构建更小的帧,然后进行更大的合并。您也可能想要执行this
之类的操作