尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
pd.get_dummies()新列名生成规则与避坑指南
我一直觉得搞懂pd.get_dummies()生成的新列名称比搞懂独热编码本身更重要。因为大多数报错、数据对不齐、模型特征数量异常根源往往不是“有没有编码成功”而是“新列到底叫什么名字”。很多人看完文档知道get_dummies()能把分类变量转成 0/1 矩阵但真到自己手里一跑看到列名是A_a、A_b或者突然冒出一个nan列就完全不知道发生了什么。这篇文章就从“新列名”这个角度出发把pd.get_dummies()的列名生成规则、边界情况、冲突场景和内存问题一次说清楚。这篇文章适合所有用 Pandas 做数据处理的人尤其是刚接触特征工程、经常被列名搞到怀疑人生的朋友。内容不会停留在 API 层面我会结合大量真实跑出来的示例讲清楚为什么有些列名会出现、为什么你写了相同的代码却得到不同的列、以及怎么用最小代价避免这些坑。1. 列名生成的核心规则prefix、分隔符和原类别值的组合逻辑1.1 不传 prefix 时新列名就是原类别值先看最简单的场景。假设有一列颜色数据直接调用pd.get_dummies()import pandas as pd df pd.DataFrame({color: [red, blue, green, red]}) encoded pd.get_dummies(df[color]) print(encoded)输出结果如下blue green red 0 0 0 1 1 1 0 0 2 0 1 0 3 0 0 1这时候新列名就是blue、green、red这三个原类别值。也就是说如果prefix参数不指定对于单独的 Series 输入get_dummies()直接拿原始值当列名。但这里有一个很多人忽略的细节列名的类型。如果你的分类值是整数比如[1, 2, 3]返回的列名很可能不是字符串1、2、3而是直接以整数形式出现df2 pd.DataFrame({num: [1, 2, 3, 1]}) encoded2 pd.get_dummies(df2[num]) print(encoded2.columns) print(encoded2.columns.dtype)输出Index([1, 2, 3], dtypeint64)这就带来了一个非常隐蔽的坑当你后来用字符串去索引这些列时比如df_encoded[1]会直接 KeyError而是必须用df_encoded[1]。我自己就在这个上面翻过车写特征筛选逻辑时统一把列名map(str)了一下才解决问题。如果你希望所有列名统一成字符串有两个办法在传给get_dummies()之前先把列转成字符串类型df[num].astype(str)。生成之后再统一改列名encoded.columns encoded.columns.astype(str)。这两种方式各有适用场景第一种会顺手改变后续类别映射的表示第二种只影响编码后的列更稳妥。1.2 多列输入或指定 prefix 时默认的列名拼接结构上面说的“直接用原值当列名”只是冰山一角。实际工程里我们很少只对一列做独热更多是直接传入多个列df3 pd.DataFrame({ color: [red, blue, green], size: [S, M, L] }) encoded3 pd.get_dummies(df3) print(encoded3)输出color_blue color_green color_red size_L size_M size_S 0 0 0 1 0 0 1 1 1 0 0 0 1 0 2 0 1 0 1 0 0注意看列名变成了color_blue、color_green、color_red、size_L、size_M、size_S。也就是说多列输入时默认行为是「原列名 下划线 原类别值」。这种设计的意图很明显就是为了避免不同列的类别值撞车。假如有weather列和season列它们都包含spring这个值如果不加前缀两列就会各自生成一个叫spring的列导致特征矩阵里出现重复列名。加了color_和size_这种前缀后互不干扰。再来看prefix参数的直接控制。文档里的定义是给生成的列加前缀。它可以接收字符串比如prefixc则列名变成c_red、c_blue。字符串列表比如prefix[c, s]这会按列的顺序依次应用color列用csize列用s。字典比如prefix{color: c, size: s}按列名显式指定。还要注意prefix_sep参数它控制前缀和类别值之间的分隔符默认是下划线_。很多人不知道它可以改成任意字符串比如prefix_sep__encoded4 pd.get_dummies(df3, prefix_sep__) print(encoded4.columns)输出Index([color__blue, color__green, color__red, size__L, size__M, size__S], dtypeobject)这种双下划线的写法在后续特征解析时很好用比如从列名反推“哪一列来自哪个原始字段”用col.split(__)比col.split(_)可靠得多。因为原始类别值里可能本来就带下划线比如very_good用单下划线切分就会得到[color, very, good]完全乱掉。这个细节我强烈建议处理真实脏数据时用上。2. 为什么有时候新列数量“超出预期”2.1 category 类型中未出现的类别也会生成新列这是get_dummies()一个非常容易让人困惑的地方。普通的 object 列有几个不同的值就生成几列但如果是category类型行为就不一样了。category类型允许你提前定义一组类别哪怕数据里没出现它依然是合法类别。get_dummies()会忠实地为这些“未曾谋面”的类别也生成列。举个例子cat pd.Categorical([a, b, a], categories[a, b, c, d]) df_cat pd.DataFrame({cat: cat}) encoded_cat pd.get_dummies(df_cat[cat]) print(encoded_cat)输出a b c d 0 1 0 0 0 1 0 1 0 0 2 1 0 0 0数据里明明没有c和d但列名c、d还是出现了。这对机器学习场景反而是好事因为训练集和测试集的类别空间需要统一如果某个类别只在测试集出现而训练集没有对应列模型就会报特征数量不一致。事先用Categorical固定好类别全集可以避免这种错位。不过也正因为这个特性新手经常认为自己代码写错了。如果你发现编码后的列数比预期多先检查一下原列是不是category类型print(df_cat[cat].dtype) # 输出 categorycategory类型的categories属性就是你所有可能出现的类别get_dummies()就是根据这个属性来生成列的。2.2 缺失值、None、空字符串生成的列名差异处理缺失值时get_dummies()的默认行为是不为NaN单独生成列而是让这一行在所有类别列上都为 0。也就是说缺失值本身被“吞掉”了。看下面的例子df_na pd.DataFrame({status: [ok, None, fail, ok]}) encoded_na pd.get_dummies(df_na[status]) print(encoded_na)输出fail ok 0 0 1 1 0 0 2 1 0 3 0 1第二行是None它没有对应的列所有值为 0。这里有一个实际问题如果缺失值本身有业务含义比如“用户未填写状态”和“用户填写了‘未知’”它们应该是有区别的。前者是缺失后者是明确的类别unknown。如果你希望缺失值也能形成一列有两个思路编码前先fillna(MISSING)把缺失值变成一个显式类别。这样生成的列名就会包含一个MISSING列。编码后再根据原列是否为缺失值手动补一列。这种方式适合你对缺失值有特殊权重设定的场景。另一个容易混淆的是None、np.nan和空字符串。这三者在get_dummies()中处理方式略不同。None和np.nan一般都会被当作缺失值忽略但空字符串是一个合法类别会生成一个列名是空字符串的列打印的时候看起来就像“无名列”很容易让人误判。我遇到过一个真实案例某张表里性别字段既有np.nan又有空字符串还有male、female。直接get_dummies()后你会发现除了male、female两列还有一个列名是空字符串的列。如果不注意后续把这列喂给模型时特征明明是 0/1但你根本不知道它代表什么。正确做法是在编码前统一清洗df[gender] df[gender].fillna().replace(, UNKNOWN)把“空”变成有业务含义的UNKNOWN再做独热列名就不会出现空白。2.3 布尔列和数值列的独热结果差异很大get_dummies()对bool类型列的处理也很有意思。如果你直接传入一个布尔列df_bool pd.DataFrame({flag: [True, False, True]}) encoded_bool pd.get_dummies(df_bool[flag]) print(encoded_bool)输出False True 0 0 1 1 1 0 2 0 1它会生成False和True两列列名就是这两个布尔值本身。这在某些模型里会产生冗余因为两列是完全线性相关的知道True列的值False列的值就确定了。实际工程中遇到布尔列我一般只用一列也就是只保留True列或者直接把布尔列转成 0/1 的整数列不参与独热。数值列的情况更值得警惕。假如有一列是[1, 2, 3]它本质上是定序或连续型数据你却把它丢给get_dummies()df_num pd.DataFrame({score: [1, 2, 3, 3, 2]}) encoded_num pd.get_dummies(df_num[score]) print(encoded_num)输出1 2 3 0 1 0 0 1 0 1 0 2 0 0 1 3 0 0 1 4 0 1 0get_dummies()可不会管你是不是数值型它只认“不同的值”。如果你没提前意识到一个本来只有 3 个取值的整数列被硬生生拆成 3 列。不是说这样一定错但如果score是 1 到 100 的分数你就会得到 100 列特征空间直接爆炸。所以实操中我给数值列做独热之前会先问自己三个问题这个列是定性还是定量它的取值空间是否有穷且小如果转成字符串再独热业务上是否合理如果答案不明确优先用astype(str)转成字符串再处理因为字符串列更直观也能避免数值被误当成离散类别的尴尬。3. 多列一起编码时的新列名冲突问题3.1 不同原始列生成了同名前缀导致的列名重叠刚才提到多列输入时默认会加列名前缀但如果你的原始列名本身就包含下划线或者两列前缀恰好相同事情就会变得复杂。比如你有两列a_b和a它们的类别值如果也复杂生成的列名可能会出现“看上去重复”的情况。更常见的问题是两次分别调用get_dummies()再把结果拼接concat到一起产生重复列名。这种做法在建模中很普遍比如先处理类别特征再处理文本特征最后拼在一起。此时如果不做列名去重后面使用sklearn的模型训练接口时经常报错。我建议所有get_dummies()输出都必须立刻检查列名是否唯一encoded_all pd.concat([encoded_demo, encoded_text], axis1) assert encoded_all.columns.is_unique, 存在重复列名这句断言可以救你很多次。不要等到模型训练报错才回头查找。3.2 列名中含有特殊字符时后续索引和拼接的隐藏坑如果原始类别值里带有空格、斜杠、括号等特殊字符生成的列名也会原样保留这些字符。比如类别值是High Income生成的列名就是target_High Income带一个空格。后续你用df[target_High Income]访问没问题但如果你用点号访问df.target_High Income就会直接语法错误。这时最稳妥的做法是生成后做一次列名规范化encoded.columns encoded.columns.str.replace(r[^A-Za-z0-9_\u4e00-\u9fa5], _, regexTrue)把不合适的字符替换成下划线保证所有列名都能作为合法变量名。但要注意这样处理后从列名反推原类别的映射关系会被破坏所以最好先另存一份映射字典col_map {new_col: old_col for new_col, old_col in zip(encoded.columns, original_columns)}有了映射后续做特征解释时才不会迷失方向。3.3 显式指定 prefix 为 False 时列名冲突几乎必然出现有一种做法是故意关闭前缀prefixFalse。当输入是 DataFrame 且prefixFalse时生成的列名可能不带任何前缀这种行为通常会导致多列类别值合并成同一批列名。大部分情况下你应该避免在生产代码里用这个参数除非你明确知道列数不会冲突。如果你确实需要prefixFalse的“精简列名”效果建议事后手动用原列名做前缀for col in original_cols: dummies pd.get_dummies(df[col], prefixcol)这样每个列都显式带上自己的名字逻辑清晰也不会出现奇怪的默认行为。4. 从新列名反推数据关系映射、恢复与特征对齐4.1 保存一份“新列名到原始类别值”的映射字典独热编码的真正难点不在生成而在事后恢复。模型跑完你需要知道某个系数对应的是哪个原始类别。如果你只是拿到一堆x0_A_1这样的列名不保存映射分析阶段会非常痛苦。我日常的做法是编码时顺手建立一个映射表用 DataFrame 存起来def encode_with_mapping(df, col): dummies pd.get_dummies(df[col], prefixcol) mapping pd.DataFrame({ encoded_col: dummies.columns, original_col: col, category_value: dummies.columns.str.replace(f^{col}_, , regexTrue) }) return dummies, mapping之后每次拿到一个新数据集先用同样的映射表reindex对齐列防止训练和预测时列数不一致X_test_encoded X_test_encoded.reindex(columnstrain_dummy_columns, fill_value0)这招是特征工程里最实用的方式。reindex会自动补全训练时存在的列如果测试集里多出训练时没见过的类别这些列也不会被保留而是被丢弃因为reindex只保留你指定的列。这正好符合建模要求测试数据的特征空间必须和训练数据一致。4.2 逆操作从独热列名称反推原始类别有时候我们需要从编码后的结果恢复原始数据比如调试、可视化和解释模型。最简单的思路是利用idxmax()找到每一行值最大的列列名本身就代表了类别值original_series encoded.idxmax(axis1)不过这个方法有两个前提每个样本只能属于一个类别这是独热编码的基本假设。缺失值所在的行所有列都是 0idxmax()会返回第一个列名显然不对。需要先用max(axis1)判断是否全为 0is_missing encoded.max(axis1) 0 original_series[is_missing] None这样就能把缺失值也还原出来。如果你的新列名带了前缀比如color_red记得把color_去掉original_series encoded.idxmax(axis1).str.replace(color_, , regexTrue)我觉得这个技巧比某些文档里推荐的“手动反转矩阵”要直观得多尤其适合列数不多、单标签分类的场景。5. 新列名引发的高维灾难列爆炸时的替代方案5.1 什么时候一定要关注新列数量get_dummies()列爆炸是特征工程里最经典的“隐形炸弹”。表面上代码运行正常但内存却悄悄上涨。我给过一个很直观的比喻如果一个原始列有 100 个不同取值get_dummies()会把这 1 列变成 100 列。如果这个列是“用户 ID”而你有 10 万用户那就会生成 10 万个新列等于把你的特征矩阵硬生生撑大十万倍。这种情况绝对不是独热编码该用的地方。判断标准其实很简单编码前先看一眼nunique()unique_count df[col].nunique() if unique_count 30: print(小心这个列可能不适合直接用 get_dummies())经验上类别数超过 20~30 时就要犹豫了。类别数过多就算内存撑得住模型训练速度和可解释性也会急剧下降。5.2 三种降维思路及它们的适用场景第一种改用pd.factorize()或LabelEncoder做序数编码。适用于有明显顺序关系的列比如学历等级小学 中学 大学直接映射为0、1、2既压缩维度又保留顺序信息。第二种对低频类别进行合并。把出现次数低于阈值的类别统一归为other再交给get_dummies()。这样列数会大幅减少而且other本身也能承载一部分低频分布信息。实现方式threshold 10 value_counts df[col].value_counts() rare_categories value_counts[value_counts threshold].index df[col] df[col].replace(rare_categories, other)第三种使用sklearn的OneHotEncoder。它也能做独热编码但更灵活支持min_frequency、max_categories等参数控制列的数量还可以输出稀疏矩阵。如果你的项目已经用sklearn优先用它而不是手动get_dummies()。但如果你只是做简单的数据探索或快速特征拼接get_dummies()的便利性仍然无可替代。记住一个原则类别基数低时用get_dummies()类别基数高时换方案。5.3 使用sparseTrue或dtypeuint8降低新列带来的内存压力很多时候类别数量只是“中等偏高”比如 50 个你仍然想用独热编码。此时可以考虑两个内存优化参数。一个是sparseTrue。这种模式下生成的 DataFrame 内部使用稀疏数据结构大量重复的 0 不会重复存储。新列名完全不变还是col_A、col_B这种格式但内存占用可能下降到原来的几分之一。要注意的是稀疏 DataFrame 在部分模型库中支持度不如普通 DataFrame送到sklearn之前可能需要.to_dense()或spmatrix转换具体看模型接口。另一个是dtypeuint8。默认get_dummies()输出的列是uint8吗如果不指定有些版本默认输出float64存储 0/1 却用 8 个字节相当浪费。显式写成pd.get_dummies(df[col], dtypeuint8)新列里的取值还是 0 和 1但内存会明显下降。配合稀疏模式更是可以应付大多数中等规模数据。这里还有一个常见误用dtypeuint8不会影响列名只是改变了存储类型。所以如果你把列名当作特征名存储完全不需要额外处理。6. 新列名前缀在建模流程中的复用与对齐6.1 训练集和测试集的列名对齐方法实际建模时训练集和测试集往往不是同一份数据。假设训练集里fruit列有apple、banana、orange测试集里多了一个peach直接分别跑get_dummies()两边列名集合就不一致了。模型训练时用 3 列预测时给 4 列接口直接报错。正确做法是先统一类别全集。一种方案是把训练集和测试集放在一起处理再拆分但这样可能引入数据泄漏。更可控的方案是只在训练集上生成列名测试集用reindex对齐dummy_columns pd.get_dummies(train[fruit], prefixfruit).columns train_encoded pd.get_dummies(train[fruit], prefixfruit) test_encoded pd.get_dummies(test[fruit], prefixfruit).reindex(columnsdummy_columns, fill_value0)这段代码看起来很基础但如果你只有一个管道脚本容易出现“测试集多了一列fruit_peach而训练集没有”的报错。reindex会把多出来的列彻底删除不参与预测。如果你希望测试集里的未知类别也能被识别成一个统一标记可以在编码前把测试集中训练集没见过的类别替换成unknown这样列名里就会有一个代表未知类别的列而不是直接丢弃信息。6.2 将 get_dummies 封装成可复用的特征工程函数基于上面所有踩坑经验我习惯把独热编码封装成一个类既能保存映射又能对齐训练测试数据。这个类不需要很复杂核心是这几个方法fit从训练数据中提取所有列名保存为新列的完整列表。transform对新数据执行get_dummies()然后reindex对齐到训练列名列表。这里的关键是列名列表必须从训练阶段固定下来之后任何数据集都使用同一个列表。这个思路和sklearn的OneHotEncoder几乎一致但基于get_dummies()实现的好处是直观调试方便也不依赖额外的库。还有一个小细节保存列名时建议连同前缀和分隔符一起保存而不要只保存原列名。因为不同前缀会让同一个原始列生成不同列名。比如fruit_apple和food_apple表面上都含apple但含义完全不同。列名列表一旦混入这样的“同名不同义”列后续特征筛选时你会一头雾水。6.3 列名集合变化时如何快速定位问题如果你发现两次运行生成的列名数量不一样不要急着改代码先用一个简单脚本差异对比set(train_dummy_columns).symmetric_difference(set(test_dummy_columns))这个操作会直接告诉你哪些列只在训练里出现哪些列只在测试里出现。大多数情况下差异来自两个原因某一列在训练或测试中存在未见过类别某一列在该数据集里被全部填充成同一个值导致只生成一个列。第二种情况尤其危险某列在测试集里只有一个值用astype(category)时会因为 categories 不同而生成不同列。这也是为什么前面建议用Categorical显式指定类别全集而不是依赖数据自动推断。写在最后的经验之谈我在做特征工程的时候曾经被get_dummies()的新列名坑过很多次其中最深刻的一次是某跨平台系统项目里我把用户行为表中的“事件来源”字段直接送入独热编码没有提前检查nunique()。结果一跑生成了超过 3 万个新列内存当场爆掉程序直接崩溃。后来我养成了一个习惯任何列在做get_dummies()之前先查看它的类别分布、缺失值和取值数量再决定用独热还是别的编码方式。如果你只从这篇文章里带走一句话那就是get_dummies()的新列名不是随机的它由prefix、prefix_sep、原始列名和原始类别值共同决定理解了这个组合逻辑你就能预测出所有将要生成的列。下次遇到“为什么多了一列”“为什么训练测试列数对不上”“为什么内存突然涨了”这类问题先打印一下columns答案基本都会浮现出来。
RELATED

相关推荐

Unity Android原生凭据管理实战:直连Credential Manager实现免密登录

Unity Android原生凭据管理实战:直连Credential Manager实现免密登录

简介:本资源是一个面向Unity开发者、聚焦Google登录集成的完整测试工程,专为解决跨平台应用中安全认证流程复杂、凭证管理困难等实际问题而设计,适用于中高级移动/桌面端Unity项目开发与安全模块接入实践。压缩包共2000个文件,体量…

📅 2026/10/20 8:50:00
深度学习视觉汽车尾气检测系统:黑烟识别与边缘部署实战

深度学习视觉汽车尾气检测系统:黑烟识别与边缘部署实战

1. 项目概述1.1 这个系统到底解决什么问题汽车尾气检测这件事,传统做法大家都不陌生:年检线上那一套工况法检测,车开到指定工位,接上诊断仪,按照标准工况跑一圈,然后测尾气里的污染物浓度。这套方法准确率高…

📅 2026/10/20 8:44:58
基于深度学习的汽车尾气检测系统:从光谱到浓度预测实战解析

基于深度学习的汽车尾气检测系统:从光谱到浓度预测实战解析

1. 为什么要把深度学习拉进尾气检测这一行先说个我自己的经历。前年我去一个机动车检测站现场调研,站长对着电脑上一堆波形数据叹气,说人工判断尾气排放是否达标,经常出现同一辆车两个检验员给出不同结论的情况。再往深了说,传统分…

📅 2026/10/20 8:44:58
MORE NEWS

更多资讯

📰

pyVideoTrans实战:从语音识别到自动配音的视频翻译工具指南

简介:pyVideoTrans是一款开源的视频翻译配音工具,主要面向视频创作者、字幕翻译者和需要批量处理音视频的用户,可将一种语言视频自动生成指定语言字幕并完成配音。它集成语音识别、字幕翻译、配音合成、字幕格式转换、人声分离、音视频合并与…

📰

YALMIP资源下载与安装全指南:MATLAB路径配置、求解器接口与避坑实战

简介:YALMIP 官方 R20230609 版资源包,是面向 MATLAB 环境下运筹优化、自动控制及算法研究者的完整建模工具箱,可用来快速构建与求解线性规划、二次规划、混合整数规划、非线性规划以及动态最优控制问题。压缩包共 1307 个文件,以…

📰

51单片机智能交通灯:车流感知与自适应配时实现

简介:本资源是一套完整的智能交通灯控制器嵌入式开发实践包,面向电子类、自动化及物联网方向的本科课程设计、毕业设计与单片机初学者,解决交通信号系统动态调度算法实现与软硬件联合验证问题。压缩包共17个文件,86KB,…

📰

dart_scope_functions鸿蒙适配实践:Dart作用域函数在Flutter中的迁移全解析

先提个问题:你写 Flutter 代码时,有没有经历过这种“工具人时刻”——定义一个对象,判空,取值,塞进构造器,再判空,再赋值……一个本该两行说完的逻辑,硬生生被样板代码撑出十行。想用…

📰

AI验布机值不值得上?先对照这三个问题再做决定

灯箱下面,验布工一坐就是八个小时,眼睛盯着匀速走过的布面,发现疵点就停车、划记号、填单子。这是制衣厂最熟悉不过的场景。但这几年越来越多的厂开始问一句话:要不要上AI验布机?我的回答通常是:别急着看设…

📰

OneSearch-V2:面向工作流的轻量级统一搜索中间件

1. 项目概述:OneSearch-V2 不是“又一个搜索框”,而是一次底层交互范式的重定义OneSearch-V2 这个名字乍看平平无奇,甚至容易被误认为是某个旧工具的简单迭代版本。但我在过去三年里深度参与过多个跨平台统一检索系统的架构设计与落地实施&am…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬