Pandas MultiIndex DataFrame如何为指定列子集批量设置新值
Pandas多层索引DataFrame批量修改指定子集列值方法
针对多层列索引的DataFrame,要批量选中所有满足条件的列(比如示例里所有最内层标签为Temp的温度列)统一做值修改,不要逐列写索引硬编码,下面两种写法稳妥无坑,不会触发SettingWithCopyWarning。
示例数据构造
先复现问题里的测试表:
import pandas as pd import numpy as np # 构建多层行/列索引 index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]], names=['year', 'visit']) columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'],['Day', 'Night'], ['HR', 'Temp']]) # 生成模拟数据 data = np.round(np.random.randn(4, 12), 1) data[:, ::2] *= 10 data += 37 hd = pd.DataFrame(data, index=index, columns=columns)
原始表结构如下,三层列索引从外到内分别是人名、时段、指标类型,我们需要修改所有Temp指标的值,比如把摄氏度转成华氏度,HR(心率)列保持不动:
Bob Guido Sue Day Night Day Night Day Night HR Temp HR Temp HR Temp HR Temp HR Temp HR Temp year visit 2013 1 38.0 37.5 21.0 36.6 33.0 37.4 38.0 35.8 15.0 38.5 27.0 37.0 2 47.0 36.5 37.0 36.3 31.0 38.8 37.0 38.4 62.0 34.9 45.0 35.6 2014 1 51.0 35.5 41.0 35.9 26.0 36.7 33.0 36.3 18.0 34.6 39.0 38.0 2 46.0 37.6 29.0 37.3 42.0 37.0 31.0 37.0 47.0 37.3 30.0 36.0
方法1:用IndexSlice按层级切片赋值(推荐)
这是pandas官方推荐的多层索引读写写法,逻辑清晰,只要明确目标列在各层的索引位置就能精准选中,不会出现链式索引问题。
摄氏度转华氏度的代码如下:
# 华氏度 = 摄氏度 * 1.8 + 32 hd.loc[:, pd.IndexSlice[:, :, 'Temp']] = hd.loc[:, pd.IndexSlice[:, :, 'Temp']] * 1.8 + 32
参数说明:
loc里逗号分隔行、列筛选条件,前面的:表示选中所有行pd.IndexSlice[:, :, 'Temp']对应三层列索引的筛选规则:前两层(人名、时段)全选,第三层只选标签为Temp的列,刚好覆盖所有温度列
方法2:布尔索引筛选列(适合层级位置不固定的场景)
如果记不清目标标签在第几层,或者列层级顺序可能变动,可以直接按层级值做布尔判断筛选列,容错性更高:
# 筛选所有最内层(层级序号-1)标签为Temp的列 temp_mask = hd.columns.get_level_values(-1) == 'Temp' hd.loc[:, temp_mask] = hd.loc[:, temp_mask] * 1.8 + 32
如果列索引设置了层级名,还可以直接用层级名筛选,不用记序号,比如列第三层名字是metric,就可以写成hd.columns.get_level_values('metric') == 'Temp',代码可读性更好。
避坑提示
不要直接用hd[筛选出的temp列列表] = 新值的写法,这种属于链式索引,在部分pandas版本里不会真正修改原表数据,还会弹出SettingWithCopyWarning警告,所有赋值操作都要走loc明确指定行列范围,保证修改是在原DataFrame上生效的。
内容的提问来源于stack exchange,提问作者ctyyyy
相关产品推荐
相关产品推荐

