基于两个MultiIndex DataFrame创建大型Series的高效实现
高效匹配MultiIndex DataFrame生成Series的解决方案
问题背景
现有两个带多层索引(MultiIndex)的DataFrame:
- df1的索引
index1定义:pd.MultiIndex.from_tuples([('1','c'),('1','d'),('2','c'),('2','d')], names=['level1','level2']) - df2的索引
index2定义:pd.MultiIndex.from_tuples([(1,'1','c'), (2,'1','c'),(3,'1','d'),(4,'2','d'),(6,'2','d')],names=['level0','level1','level2'])
需求是:基于df1指定列的level1+level2匹配关系,生成一个以df2的level0值为索引的pd.Series。
示例数据与期望输出
示例df1构造代码:
import pandas as pd index1 = pd.MultiIndex.from_tuples([('1','c'),('1','d'),('2','c'),('2','d')], names=['level1','level2']) df1 = pd.DataFrame({'A':['foo','bar','baz','qux'],'B':['foo1','bar1','baz1','qux1']}, index=index1)
期望输出Series:
pd.Series(['foo1','foo1', 'bar1', 'qux1','qux1'], index=[1,2,3,4,6])
当前痛点
原实现通过遍历df2的索引逐个匹配,代码如下:
column_name = 'B' result = [df1[column_name].loc[indx[1:]][0] for indx in df2.index] new_indx = [i[0] for i in df2.index] result = pd.Series(result, index=new_indx)
但df2数据量极大时,遍历操作会导致运行速度极慢,需要无需遍历df2的高效方案(可接受遍历较小的df1)。
高效解决方案
方法1:索引重置+合并(直观易读)
利用pandas的合并操作批量匹配,避免逐行遍历:
column_name = 'B' # 提取df1的level1、level2与目标列的映射关系,转为普通DataFrame mapping_df = df1[column_name].reset_index() # 提取df2的level0、level1、level2信息 df2_info = df2.reset_index()[['level0', 'level1', 'level2']] # 基于level1和level2合并,生成结果Series merged = df2_info.merge(mapping_df, on=['level1', 'level2'], how='left') result_series = merged.set_index('level0')[column_name]
方法2:直接利用索引元组批量匹配(性能最优)
从df2的MultiIndex中提取level1和level2组成元组序列,直接用df1的索引进行批量映射:
column_name = 'B' # 从df2索引中提取level1和level2,组成匹配用的元组列表 match_keys = list(zip( df2.index.get_level_values('level1'), df2.index.get_level_values('level2') )) # 直接通过元组序列从df1中取值,再设置索引为df2的level0 result_series = pd.Series( df1[column_name].loc[match_keys].values, index=df2.index.get_level_values('level0') )
方法3:遍历小df1批量赋值(符合需求中“可遍历df1”)
如果df1数据量很小,可遍历其每个level1+level2组合,批量更新结果Series:
column_name = 'B' # 初始化结果Series,索引为df2的level0 result_series = pd.Series(index=df2.index.get_level_values('level0')) # 遍历df1的每个索引项和对应值 for (l1, l2), val in df1[column_name].items(): # 筛选df2中当前level1+level2对应的所有level0 mask = (df2.index.get_level_values('level1') == l1) & \ (df2.index.get_level_values('level2') == l2) target_level0 = df2.index.get_level_values('level0')[mask] # 批量赋值 result_series.loc[target_level0] = val
结果验证
以上三种方法生成的result_series均与期望输出一致:
level0 1 foo1 2 foo1 3 bar1 4 qux1 6 qux1 Name: B, dtype: object
内容的提问来源于stack exchange,提问作者Merimace
相关产品推荐
相关产品推荐

