如何在保留多级列结构时对Pandas子列应用get_dummies()
如何在多级列DataFrame中对指定子列做独热编码并保留层级结构
嘿,我明白你遇到的问题了——直接用get_dummies替换原列会因为维度不匹配出NaN,而且还想保留原来的多级列结构对吧?我来给你一步步解决这个问题。
先明确需求和示例
你有一个带多级列的Pandas DataFrame,想把所有第二级为one的列转换成独热编码,同时让编码后的列依然归属于原来的第一层级(比如A、B、C),最终得到像你期望的那种结构。
先回顾下你的示例输入:
import pandas as pd df = pd.DataFrame({ ('A','one'):['a','a','b'], ('A','two'):['b','a','a'], ('B','one'):['b','b','a'], ('B','two'):['a','a','a'], ('C','one'):['b','a','b'], ('C','two'):['a','b','a'], })
输出的初始DataFrame长这样:
A B C one two one two one two 0 a b b a b a 1 a a b a a b 2 b a a a b a
你想要的结果是把每个one列拆成对应的独热编码列,比如A下的one变成one_a和one_b,还得留在A的层级下。
你之前的尝试为什么不行?
你试过直接赋值:
df.loc[:, (slice(None),'one')] = pd.get_dummies(df.loc[:, (slice(None),'one')])
结果全是NaN,这很正常——原DataFrame里每个第一层级下只有1个one列,但get_dummies返回的是多列,Pandas没法把多列的值塞进单列的位置,自然就出现缺失值了。而且drop_first=True只能生成一列,不符合你要完整独热向量的需求。
正确的解决方案
我们得换个思路:先把要编码的列提出来处理,调整好多级列结构,再和原DataFrame的其他列合并。
具体代码实现(针对你的示例)
import pandas as pd # 1. 把所有第二级是'one'的列提取出来 one_subset = df.loc[:, (slice(None), 'one')] # 2. 生成独热编码,然后把列名改成多级结构 dummies = pd.get_dummies(one_subset, prefix_sep='_') # 把像"A_one_a"这样的列名拆成('A', 'one_a')的多级元组 dummies.columns = pd.MultiIndex.from_tuples( [col.split('_', 1) for col in dummies.columns], names=df.columns.names ) # 3. 去掉原DataFrame里的'one'列,再合并编码后的列 non_one_cols = df.drop(columns='one', level=1) # 合并后按第一层级排序,再调整列顺序让编码列在前 result = pd.concat([non_one_cols, dummies], axis=1).sort_index(axis=1, level=0) # 手动调整列顺序匹配你的期望输出(如果需要的话) result = result[[ ('A','one_a'), ('A','one_b'), ('A','two'), ('B','one_a'), ('B','one_b'), ('B','two'), ('C','one_a'), ('C','one_b'), ('C','two') ]] print(result)
运行后就能得到你想要的输出:
A B C one_a one_b two one_a one_b two one_a one_b two 0 1 0 b 0 1 a 0 1 a 1 1 0 a 0 1 a 1 0 b 2 0 1 a 1 0 a 0 1 a
更通用的版本(适配任意数量的第一层级列)
如果你的DataFrame不止A、B、C这几个第一层级列,用下面的代码更省心,不需要手动列名:
import pandas as pd # 提取要编码的子列 one_subset = df.loc[:, (slice(None), 'one')] # 生成独热编码并重构多级列 dummies = pd.get_dummies(one_subset, prefix_sep='_') dummies.columns = pd.MultiIndex.from_tuples( [tuple(col.split('_', 1)) for col in dummies.columns], names=df.columns.names ) # 保留非one列 non_one_cols = df.drop(columns='one', level=1) # 合并并排序列 result = pd.concat([non_one_cols, dummies], axis=1).sort_index(axis=1, level=[0,1]) # 自动调整每组内的顺序:编码列在前,原非one列在后 grouped_cols = [] for group in result.columns.get_level_values(0).unique(): # 收集当前组的编码列和非编码列 code_cols = [col for col in result.columns if col[0]==group and col[1].startswith('one_')] other_cols = [col for col in result.columns if col[0]==group and not col[1].startswith('one_')] grouped_cols.extend(code_cols + other_cols) result = result[grouped_cols] print(result)
这个版本会自动识别所有第一层级组,把每个组里的编码列放在前面,原列放在后面,完全不用手动指定。
核心思路总结
- 分离处理:先把要编码的列单独提出来,避免直接修改原DataFrame导致维度不匹配。
- 重构列结构:把
get_dummies生成的扁平列名转换成多级列,确保编码后的列仍属于原层级。 - 合并整合:去掉原列,把编码后的列和剩余列合并,再调整顺序达到期望效果。
内容的提问来源于stack exchange,提问作者tog
相关产品推荐
相关产品推荐

