如何用Python按条件为多层索引列的DataFrame生成新列?
处理二级索引DataFrame的格式化需求(Pythonic实现)
原始DataFrame
metric grp Avg P95 mean ci_low ci_up mean ci_low ci_up 0 a CONTROL 8.202862 8.100596 8.306985 17.122063 16.832979 17.400000 1 a EXPERIMENT 8.243680 8.141428 8.351050 16.709375 16.383333 17.043500 2 a diff 0.040819 -0.102122 0.185837 -0.412688 -0.855854 0.050104 3 a rel_diff 0.005016 -0.012299 0.022944 -0.024022 -0.049377 0.002982 4 b CONTROL 15.513669 15.316762 15.720606 31.893576 31.357300 32.600350 5 b EXPERIMENT 15.486001 15.293760 15.672843 31.317389 30.808775 31.839000 6 b diff -0.027668 -0.300167 0.251497 -0.576186 -1.450138 0.223063 7 b rel_diff -0.001740 -0.019249 0.016368 -0.017967 -0.044785 0.007031
需求
- 为每个一级列
Avg、P95创建格式为mean [ci_low, ci_up]的新列; - 对
rel_diff行,将所有列格式化为百分比(如0.005016转为0.5%); - 其余行将所有列四舍五入到小数点后三位。
Pythonic实现方案
1. 合并多级列并格式化
利用Pandas的层级列操作和apply函数,一次性完成列合并与条件格式化:
import pandas as pd # 假设数据已加载为DataFrame df # 获取一级列的唯一名称 level1_cols = df.columns.get_level_values(0).unique() # 为每个一级列生成合并后的格式化列 for col in level1_cols: # 定位当前一级列下的三个二级列 mean_col = (col, 'mean') ci_low_col = (col, 'ci_low') ci_up_col = (col, 'ci_up') # 定义行级格式化逻辑 def format_entry(row): if row['grp'] == 'rel_diff': # 百分比格式化:乘100后保留1位小数 mean = f"{row[mean_col] * 100:.1f}%" ci_low = f"{row[ci_low_col] * 100:.1f}%" ci_up = f"{row[ci_up_col] * 100:.1f}%" else: # 普通行保留3位小数 mean = f"{row[mean_col]:.3f}" ci_low = f"{row[ci_low_col]:.3f}" ci_up = f"{row[ci_up_col]:.3f}" return f"{mean} [{ci_low}, {ci_up}]" # 添加新列,列名为一级列名 df[col] = df.apply(format_entry, axis=1) # 清理旧的多级列,保留核心列和新生成的格式化列 df = df[['metric', 'grp'] + list(level1_cols)]
2. 单独格式化原数值列(可选)
如果需要保留原数值列并分别格式化,可通过布尔掩码批量处理:
# 定位rel_diff行 rel_diff_mask = df['grp'] == 'rel_diff' # 提取所有数值列 numeric_cols = df.select_dtypes(include=['float64']).columns # 批量格式化rel_diff行为百分比 df.loc[rel_diff_mask, numeric_cols] = df.loc[rel_diff_mask, numeric_cols].applymap( lambda x: f"{x * 100:.1f}%" ) # 批量格式化其他行为三位小数 non_rel_diff_mask = ~rel_diff_mask df.loc[non_rel_diff_mask, numeric_cols] = df.loc[non_rel_diff_mask, numeric_cols].applymap( lambda x: f"{x:.3f}" )
最终效果示例
处理后的DataFrame如下:
metric grp Avg P95 0 a CONTROL 8.203 [8.101, 8.307] 17.122 [16.833, 17.400] 1 a EXPERIMENT 8.244 [8.141, 8.351] 16.709 [16.383, 17.044] 2 a diff 0.041 [-0.102, 0.186] -0.413 [-0.856, 0.050] 3 a rel_diff 0.5% [-1.2%, 2.3%] -2.4% [-4.9%, 0.3%] 4 b CONTROL 15.514 [15.317, 15.721] 31.894 [31.357, 32.600] 5 b EXPERIMENT 15.486 [15.294, 15.673] 31.317 [30.809, 31.839] 6 b diff -0.028 [-0.300, 0.251] -0.576 [-1.450, 0.223] 7 b rel_diff -0.2% [-1.9%, 1.6%] -1.8% [-4.5%, 0.7%]
核心Pythonic特性
- 使用
applymap/apply实现批量元素操作,避免低效的逐行循环; - 借助布尔掩码
loc精准定位目标行,实现高效条件处理; - 利用多级列的
get_level_values方法便捷提取层级信息,符合Pandas原生设计; - 格式化逻辑封装为轻量函数,保持代码简洁可读。
内容的提问来源于stack exchange,提问作者SixSigma
相关产品推荐
相关产品推荐

