如何为Pandas多级索引DataFrame添加空子列及子子列?
问题背景
我正在使用Pandas处理多级索引的多列DataFrame,包含16个一级列(如Climate Change、Ozone Depletion等),每个一级列下有两个二级子列:Agriculture和Transformation。我想为每个一级列新增一个名为Transport的空二级子列,后续填充数据。
初始尝试的问题
尝试过如下循环代码:
for i in list(df.columns.levels[0]): df[i, 'Transport'] = ''
但该操作会重复生成带空值的一级列,无法正确分组。我希望找到无需循环的实现方式;若必须用循环,如何让新创建的列正确归组?
已查看相关问题但未解决需求。
测试代码(补充)
import pandas as pd import numpy as np df = pd.DataFrame([[1,2,3,4], [5,6,7,8],[9,10,11,12]], columns = pd.MultiIndex.from_product([('Acidification', 'Climate Change'), ('Agriculture', 'Transformation')]), index=['potato', 'tomato', 'onion'])
使用@Laurent的方案并修改一行代码后,得到了预期结果:
dfs = [] for col in df.columns.levels[0]: tmp = df.copy()[col] tmp.loc[:, ("Transport")] = "" # 修改的行 dfs.append(tmp) new_df = pd.concat(dfs, axis=1, keys=df.columns.levels[0])
但我希望有更通用的实现方法。
问题1
现在我想为所有一级列下的Transport二级子列新增两个三级子列(National和Import),该如何实现?尝试如下代码时出现unhashable type: 'slice'错误:
# 尝试沿用之前的逻辑 dfn = [] for col in new_df.columns.levels[0]: for scol in new_df.columns.levels[1]: tmp = new_df.copy()[col][scol] tmp.loc[:, ('National')] = "" tmp.loc[:, ('Import')] = "" dfn.append(tmp) new_dfn = pd.concat(dfn, axis=1, keys=new_df.columns.levels[0])
问题2
是否存在通用方法,可为所有列中的指定子列添加新的子子列?
解决方案
一、无需循环添加二级子列Transport
直接通过重构多级索引列实现,避免循环和列混乱:
# 获取原有一级列集合,将Transport加入二级列集合 level0 = df.columns.levels[0] level1 = df.columns.levels[1].union(['Transport']) # 生成新的多级列索引 new_columns = pd.MultiIndex.from_product([level0, level1]) # 重新索引DataFrame,新增列自动填充空字符串 new_df = df.reindex(columns=new_columns).fillna('')
这种方法会自动将Transport列归属于对应一级列下,不会出现重复列问题。
二、为Transport添加三级子列National和Import
方法1:分步构造三级索引
针对问题1的错误,核心是正确处理三级索引的元组构造:
# 整理所有列的元组列表,为Transport扩展三级子列 existing_cols = [] for col in new_df.columns: if col[1] != 'Transport': # 非Transport列补充空三级层,保持层级统一 existing_cols.append((col[0], col[1], '')) else: # 为Transport添加两个三级子列 existing_cols.extend([(col[0], col[1], 'National'), (col[0], col[1], 'Import')]) # 转换为三级MultiIndex new_columns_3lvl = pd.MultiIndex.from_tuples(existing_cols, names=['Level0', 'Level1', 'Level2']) # 重新索引并填充空值 new_dfn = new_df.reindex(columns=new_columns_3lvl, level=[0,1]).fillna('')
方法2:通用多级列扩展函数
针对问题2,编写通用函数实现任意层级的子列扩展:
def add_subcols_to_multilevel(df, target_level, target_subcol, new_subcols, fill_value=''): """ 为多级索引DataFrame的指定子列添加新的子子列 参数: df: 原多级索引DataFrame target_level: 目标子列所在层级(从0开始计数) target_subcol: 要扩展的目标子列名称 new_subcols: 新增的子子列列表 fill_value: 新增列的填充值 返回: 扩展后的多级索引DataFrame """ new_cols = [] for col_tuple in df.columns: if col_tuple[target_level] == target_subcol: # 为目标子列生成所有新的子子列元组 for subcol in new_subcols: new_tuple = col_tuple[:target_level+1] + (subcol,) + col_tuple[target_level+1:] new_cols.append(new_tuple) else: # 非目标子列补充空层级,保持结构统一 if len(col_tuple) == target_level+1: new_tuple = col_tuple + ('',) new_cols.append(new_tuple) else: new_cols.append(col_tuple) # 生成新索引并重新匹配数据 new_multi_cols = pd.MultiIndex.from_tuples(new_cols) return df.reindex(columns=new_multi_cols).fillna(fill_value) # 使用示例:为Level1的Transport添加National和Import三级子列 new_dfn = add_subcols_to_multilevel(new_df, target_level=1, target_subcol='Transport', new_subcols=['National', 'Import'])
该函数可灵活处理不同层级的扩展需求,适配二级加三级、更高层级扩展等场景。
三、循环方式的正确实现(若需保留循环)
如果一定要用循环,需针对每个一级列单独处理后再拼接:
dfs = [] for level0_col in df.columns.levels[0]: # 提取当前一级列下的所有二级列 sub_df = df[level0_col].copy() # 添加Transport二级列 sub_df['Transport'] = '' dfs.append(sub_df) # 拼接时指定keys作为一级列名,自动生成正确的多级索引 new_df = pd.concat(dfs, axis=1, keys=df.columns.levels[0])
内容的提问来源于stack exchange,提问作者M. Merida-Floriano
相关产品推荐
相关产品推荐

