Sklearn数据预处理详解:从缺失值清洗到特征编码与Pipeline实战

发布时间:2026/10/22 15:02:13

Sklearn数据预处理详解:从缺失值清洗到特征编码与Pipeline实战 做机器学习时间长了你会发现一个很朴素的道理模型上限其实在数据里预处理决定了下限。特征量纲不一致、缺失值乱填、类别列没编码、训练集和测试集各自“美丽”这些问题不解决后面的模型调参再用心效果也会被拖垮。sklearn里的数据预处理Preprocessing就是这么个不起眼却绕不过去的模块。这篇是sklearn专题的第三篇我准备把从缺失值清洗、数值缩放、类别编码到流水线组合这套东西完整拆一遍给出的代码和思路都是自己实际跑过、踩过坑才沉淀下来的适合刚在sklearn里完成建模、开始关心“为什么同样数据别人分数高那么多”的同学也适合那些想让预处理代码不再手忙脚乱的开发者。整个专题我习惯把它划分成四个层次先想清楚模型要什么数据再逐列把脏数据洗干净然后把特征变成模型“吃得懂”的数值形态最后把一串操作固化成可复用的流水线。这篇文章就按这个脉络走用一个虚构的房价预测场景贯穿始终每段代码都会解释“为什么这么写”而不是单纯贴一份作业。如果你已经入了sklearn的门但每次预处理都要临时翻文档、调参数这篇应该能帮你把思路彻底捋顺。1. 先把思路理顺数据预处理要解决什么问题1.1 模型为什么对“脏数据”这么敏感很多人第一次接触数据预处理是被报错逼的字符串列丢进模型直接报错或者全DataFrame转成数值后效果莫名其妙变差。这些现象背后其实是同一个逻辑模型本质是数学函数它对输入的数据形式是有假设的。拿线性回归和SVM这类模型来说它们依赖距离计算或者梯度下降。假如面积这个特征的取值范围是30到300房龄是0到40两者直接放进模型面积因为数值大对距离的贡献天然就压过房龄量纲在这里直接扭曲了权重。树模型对数值量纲不太敏感但它也有自己的痛点类别特征如果直接编码成[0,1,2]模型会把这个数值当成连续变量对待认为2是1的两倍、2比1更“大”这就凭空捏造了一种排序关系。还有一类更隐蔽的问题就是数据分布。线性模型通常默认特征和标签之间的关系近似线性如果某个特征本身是对数分布或严重偏态直接丢进模型哪怕量纲对了模型也很难拟合出正确关系。数据预处理的本质就是不断消除这些“模型假设”和“现实数据”之间的错位。1.2 预处理模块的“家族图谱”sklearn里的数据预处理能力并不是全集中在一个类里而是散布在好几个子模块中。我一开始就是没摸清这张地图经常一个功能找半天。按用途拆开来看这几个模块是主力模块典型类/函数解决的问题sklearn.imputeSimpleImputer, MissingIndicator缺失值填充与缺失标志sklearn.preprocessingStandardScaler, MinMaxScaler, OneHotEncoder, OrdinalEncoder, FunctionTransformer数值缩放、编码、自定义变换sklearn.composeColumnTransformer对不同列分别做不同预处理sklearn.pipelinePipeline, make_pipeline把多个步骤串成流水线sklearn.model_selectiontrain_test_split划分训练集与测试集sklearn.feature_selectionSelectKBest等预处理后的特征筛选这个家族视图很重要因为预处理不是一步操作而是一条链缺失值清洗应该在缩放之前类别编码通常和数值缩放并行最后统一交给下游模型。不同模块各管一段Pipeline负责把它们粘起来。还有两个经常和预处理混在一起的动作需要单独拎出来认清特征选择sklearn.feature_selection是减少特征数量属于“筛”特征提取如PCA是做降维压缩属于“并”。筛和并可能在预处理前后反复迭代但和“清洗编码缩放”不是一个环节别混在一个步骤里写不然调试的时候分不清问题出在哪。1.3 一个贯穿全篇的实操数据为了不让你看代码看得很飘我这一整篇统一用一个虚构的房屋成交记录数据集来演示数据规模不大但品种齐全面积连续数值少量缺失房龄连续数值极个别年份异常装修等级字符串类别取值是“毛坯、简装、精装”存在自然顺序朝向字符串类别取值是“朝南、朝北、东西”无内在顺序所在楼层连续数值范围1到50成交价格标签列用来做纵向验证这个数据集很典型有数值、有有序类别、有无序类别又有缺失和噪声把预处理的常见场景全部覆盖了。后面每个环节我都会用这几列来讲你完全可以照着代码在自己机器上复现。下面进入正题先处理最基础的缺失值和脏数值。2. 数据清洗缺失值怎么填异常值怎么办2.1 缺失值第一课先弄清楚数据是怎么丢的很多人拿到NaN就机械地填中位数这种做法不是不对而是没有问最关键的“为什么缺失”。统计上会把缺失机制分成几类完全随机缺失指缺失和任何变量都无关比如录入员手滑漏填了一个面积随机缺失指缺失跟其他已观测变量有关比如高楼层数据更容易缺失非随机缺失则指缺失本身跟缺失值有关比如贵的房子面积故意不填全。三种场景下填充策略的含义完全不同。完全随机缺失用均值、中位数这类“无信息填充”是安全的随机缺失如果只用列内统计忽略其他列会丢失一些可推测的模式非随机缺失则更麻烦填一个普通值进去反而可能系统性地扭曲分布。实操上我不会为了判断缺失机制做一堆统计检验但通常做两件很朴素的事先按列统计缺失率缺失超过30%的特征要怀疑其保留价值再对比“缺失样本”和“非缺失样本”在标签上的分布如果差异明显说明缺失本身可能带着信息。这个信息很多时候比猜测填充还要有价值。2.2 SimpleImputer用起来以及四种策略怎么选sklearn里面对缺失值主力工具是SimpleImputer。它默认对每列单独做统计填充核心就一个strategy参数from sklearn.impute import SimpleImputer # 中位数填充适合面积这类可能有偏的数值特征 imp_median SimpleImputer(strategymedian) X_area_filled imp_median.fit_transform(X_train[[面积]]) # 若是类别列most_frequent是最常用的 imp_mode SimpleImputer(strategymost_frequent) X_decor_filled imp_mode.fit_transform(X_train[[装修等级]])四种strategy的使用场景我在实战里总结成一个口诀mean数据近似正态、无明显异常值的时候用。均值受极端值影响大一个离谱的500平别墅能直接把平均值拉高。median数据偏态或存在异常值时优先选。价格、面积这类取值永远为正、大值拖尾明显中位数远比均值稳健。most_frequent类别型特征专用填众数。注意它不分顺序对有序类别也只是填个最多出现的值。constant有领域知识时使用。比如“收入未填写”可以填-1然后配合缺失指示器把“故意不填”作为一个有业务含义的信号。还有一个很容易被忽略的参数add_indicator。设成True时SimpleImputer会额外生成一列“是否缺失”的0/1标志。这个参数在风控、营销场景非常有价值用户没填收入这件事本身可能就是风险特征。缺失率不高但业务上敏感时宁可保留标志也不让缺失信息悄悄消失。2.3 缺失值填充的“先后顺序”陷阱SimpleImputer虽然好用但它有个很实际的问题它对每列单独做统计完全不看其他列。假如面积和楼层高度相关面积缺失但楼层没缺失直接用列中位数填充就会丢掉这种列间关系。这时候更工程化的做法是先用规则把可推断的缺失补上再交给SimpleImputer兜底。我处理表格数据时通常先在数据层做一轮业务规则填充比如“装修等级为毛坯但成交价填写异常”这类多列联动业务校验先把能确定的补掉剩下的真正的“随机洞”才交给统计填充。还有两个细节经常让人翻车。第一缺失值不一定叫NaN。很多数据导出工具会把缺失写成空字符串、-999、0或者“Unknown”。SimpleImputer默认只认np.nan如果是空字符串你需要先把数据统一处理成NaN否则字符串列会直接把类型搞乱。第二任何填充都必须在训练集上先fit。测试集的缺失值要用训练集学到的统计量去填绝对不能拿测试集自己的均值、中位数来填不然就是数据泄露后面会专门展开。2.4 异常值要不要删怎么处理更稳妥异常值这个问题比缺失值还让人纠结因为“异常”的定义本身有主观性。我见过太多人在建模前用3倍标准差框掉一堆点结果模型上线后泛化很差——因为现实世界本来就存在合理的极端值别墅不是异常它是样本的真实分布。我现在的处理习惯是分场景看模型。树模型对单个异常点基本免疫阈值切分决定了它不会被一个500平的点拉着走这种情况异常值可以先不管。线性模型、KNN、神经网络这类受距离或梯度影响的模型异常值会显著拉偏结果。这时候先不急着删几个更稳的路径是用RobustScaler这类对异常不敏感的缩放器、对偏态特征做log变换压缩长尾、用分位数截断而不是硬性删除。需要快速看异常情况时我经常会用IQR规则做一次扫描但不把它当自动删除依据import numpy as np def outlier_flags(col): q1, q3 np.percentile(col, [25, 75]) iqr q3 - q1 lower q1 - 1.5 * iqr upper q3 1.5 * iqr return (col lower) | (col upper) # 房龄列里出现负数大概率是录入错误 age_flags outlier_flags(df[房龄])跑完这个函数重点看两类结果一类是“明显不可能”的值比如房龄-5这类属于错误数据直接修正或剔除另一类是“数值很大但业务上可能”的值比如500平豪宅这类属于分布长尾保留并在缩放环节处理。把“错误”和“极端”分开对待比一刀切删点安全得多。3. 数值特征缩放、变换与空间扩展3.1 四款缩放器怎么选对比讲透清洗完缺失值和脏数值下一步就是统一量纲。sklearn里最常用的四款缩放器很多人只知道用StandardScaler但选型差了会直接影响模型表现。我把它们的原理和适用场景对列放在一起缩放器数学逻辑输出范围适用场景对异常值敏感度StandardScalerz(x-μ)/σ大致[-3,3]但不限线性模型、逻辑回归、PCA特征近似正态时敏感MinMaxScaler(x-min)/(max-min)[0,1]可自定义范围卷积输入、有明确边界的特征如像素敏感RobustScaler(x-median)/IQR大致[-1.5,1.5]之外仍有值数据含较多异常值、分布偏态不敏感MaxAbsScalerx / max(x)[-1,1]StandardScaler是默认好马但只有在特征分布不是极端偏态的时候才适合。它做的事情是把数据变成均值为0、方差为1的分布公式就是z (x - μ) / σ。凡是用距离、梯度、正则化惩罚的模型标准化之后各特征对目标的影响才能在同一个尺度上比较。MinMaxScaler则适合你知道特征有天然边界的场景。比如图像像素本来就是0到255缩放到0到1很自然。但要注意它会被一个极端值支配某一列一个超大值整列都会被压成紧贴0到1的小片区域。而且训练集里的max在测试集不一定同样是最大值新数据进来完全可能超出边界这在线上预测时需要小心。RobustScaler是我处理含噪声数据时的首选。它用中位数和四分位距代替均值和标准差极端值对它的影响非常有限。房价Demo里如果面积存在别墅这种长尾我一般直接用RobustScaler比先删点再标准化省事且安全。3.2 训练集与测试集fit和transform必须分开缩放器使用中最大的坑我看大多数人第一年都会踩对测试集也调用了fit_transform导致训练和测试各自被标准化到不同的空间。正确写法是训练集上fit然后只transform训练集和测试集from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train[[面积, 房龄, 所在楼层]]) X_test_scaled scaler.transform(X_test[[面积, 房龄, 所在楼层]])为什么要这样标准化用的μ和σ应该是训练集样本的统计量代表模型训练时看到的数据分布。如果测试集也单独fit测试集的μ和σ会偷偷掺进来。更直白一点模型在训练时根本不该“听说”测试集长什么样哪怕只是均值。对树模型、朴素贝叶斯这类不做缩放的模型这个坑暂时无关痛痒但对线性模型、SVM、神经网络不正确的fit/transform分配会在线下评估时给你一个虚高的分数。顺带提一个自查方法训练时保存的scaler对象后续预测时一定原样拿过来transform。如果哪天发现上线代码里又重新用数据fit了一下scaler先停下来检查数据泄露。3.3 模型学不会非线性多项式特征和分箱来帮忙缩放解决统一量纲但很多场景下模型还缺少“表达非线性”的能力。线性回归天然只能学一条直线可面积和价格的关系哪儿是直线往往是边际递减。两种情况可以快速给线性模型“加戏”PolynomialFeatures和分箱。PolynomialFeatures会生成原始特征的高次项和交叉项比如面积、房龄两列在degree2时会额外生成面积^2、房龄^2、面积×房龄from sklearn.preprocessing import PolynomialFeatures poly PolynomialFeatures(degree2, include_biasFalse) X_train_poly poly.fit_transform(X_train[[面积, 房龄]])加了高次项后线性模型就能拟合出弯曲的关系。但这里面有个非常实在的提醒degree不要无脑调高。3次、4次之后特征数量爆炸式增长很容易过拟合而且交叉项非常难解释。我的习惯是degree最多2且只在特征数量少的时候用。分箱则是另一种思路用KBinsDiscretizer把连续变量切成几段每段作为一个类别输入。这种操作对逻辑回归这类模型特别友好本质上是让模型在段内学一个常数效应。比如把面积分成80-100、100-120、120-150几档业务解释也顺了“120平到150平的房子每平单价溢价明显”。分箱后输出可以是数值编码也可以继续OneHot配合后续Pipeline用起来很顺手。4. 类别特征从序号编码到一键编码4.1 第一个大坑别拿LabelEncoder编码特征这里必须讲一个很多人都会踩的经典坑。网上很多老教程教人处理字符串列时随手写一个循环from sklearn.preprocessing import LabelEncoder # 危险示例用LabelEncoder处理特征列 for col in [装修等级, 朝向]: le LabelEncoder() df[col] le.fit_transform(df[col])这段代码能跑通但隐患很大。LabelEncoder的设计初衷是编码标签y的它生成的整数编码和模型无关完全按字符串的排序规则给编号。比如“毛坯、精装、简装”三个词如果按字母序排顺序可能变成“简装(0)、毛坯(1)、精装(2)”这跟业务上的“低中高”根本不匹配。大多数模型尤其树模型和线性模型会把0、1、2当成有大小关系的连续值于是模型学到了一条完全虚假的排序关系。很多人跑完发现特征重要性里“装修等级”异常重要结果却是这个虚假顺序在捣鬼。不要问我是怎么知道的。类别特征的标准做法是两个正规军有序类别用OrdinalEncoder无序类别用OneHotEncoder。4.2 OrdinalEncoder和OneHotEncoder分工明确OrdinalEncoder适用于类别本身有内在顺序的场景。比如“装修等级”天然有毛坯 → 简装 → 精装的递进关系给它0、1、2编码就没问题因为顺序感是真实存在的。但要注意传categories参数显式指定顺序别放着不管from sklearn.preprocessing import OrdinalEncoder ord_enc OrdinalEncoder(categories[[毛坯, 简装, 精装]]) X_train_decor ord_enc.fit_transform(X_train[[装修等级]])只要你不传categoriessklearn就会按字符串字典序排那个灾难和LabelEncoder是一样的。所以凡是有业务顺序的类别显式写清楚categories是必须动作。OneHotEncoder则适合“朝向”这种互斥并列的类别。它会把“朝南、朝北、东西”三列各自变成0/1模型不再误判大小关系from sklearn.preprocessing import OneHotEncoder one_hot OneHotEncoder(handle_unknownignore) X_train_orient one_hot.fit_transform(X_train[[朝向]]) X_test_orient one_hot.transform(X_test[[朝向]])输出是稀疏矩阵这一点需要心里有数。OneHotEncoding的本质是制造大量0值稀疏存储能大幅省内存。如果后续要接pandas、要打印特征名再考虑转成dense或设置set_output(transformpandas)。4.3 OneHot的三个参数drop、handle_unknown和低频合并OneHotEncoder的默认行为其实有几个粗糙之处我逐个讲。drop参数当某一列的类别本身是二值如“是否临街”: 是/否时OneHot会生成两列但这两列完全线性相关是1-否。很多线性模型会因此出现共线性问题权重解释也会乱。设dropfirst或者dropif_binary可以去掉多余一列。树模型无所谓但线性模型强烈建议处理。handle_unknown训练集里没有、测试集里突然出现的类别。如果设成默认的error预测阶段直接崩。设成ignore后新类别会变成一列全零模型就当它“不属于任何已知类别”。这个参数我通常总是开着省得线上被未知值炸断。再往后sklearn新版还支持了min_frequency和max_categories。前者会把出现频率低于阈值的类别自动合并成一类后者限制最终类别总数。这项能力在处理高基数的城市名、设备型号时特别好用比如几十万个用户ID做OneHot是不可理喻的低频类并成“其他”是实用策略。我在模糊匹配场景里常用min_frequency0.01这种量级的参数。4.4 类别太多又没有业务顺序怎么办OneHot的高基数问题值得单独说。比如数据里有个“板块”特征有几百个选项OneHot之后直接给你几百维样本量不够时模型几乎必炸。这种场景我推荐两条路第一条是用低频合并前面说的min_frequency就是一种自动化做法。第二条是目标编码把每个类别的标签均值作为该类的编码特征。但目标编码有个著名陷阱直接全局计算均值会把标签信息泄露进特征必须放在交叉验证折内去算。所以会复杂很多一般等模型验证阶段再上。预处理阶段我的铁律是高基数类别先用低频合并顶着不擅自引入目标编码。5. 让预处理进入工程化Pipeline与ColumnTransformer5.1 没有Pipeline之前我的代码有多乱如果你靠手写变量保存每个scaler和encoder代码大概会长这样先定义几个对象fit各自列然后一个个transform拼接。中间一旦增加一个特征或者调整一个参数就要重新同步训练和预测两端。而一旦漏了一列没缩放模型直接静默变差你还要花很久查为什么分数不对。所以我强烈建议从第一天开始就用Pipeline来组织预处理。Pipeline的核心思想很简单把一串fit和transform步骤打包成一个对象训练时整体fit预测时整体transform。你不再需要各自保存五个变量只需要保存一个流水线对象测试和上线的变换逻辑自然不会错位。5.2 一个直接能跑的Pipeline示例拿只含数值列的子集举例。这组代码把缺失填充、标准化、线性回归串成一条链from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression pipe Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, StandardScaler()), (regressor, LinearRegression()) ]) pipe.fit(X_train[[面积, 房龄]], y_train) pred pipe.predict(X_test[[面积, 房龄]])Pipeline有个内在规则除了最后一步中间步骤必须有transform方法最后一步只需要fit和predict。训练时它会自动把前一个步骤的输出喂给下一个步骤预测时自动执行除了最后一步之外的所有变换。这个机制就是它不串味、不遗漏的根因。这里还有个新版本的福利Pipeline可以将每一步的输出设置成DataFrame而不是只会出现数组。只要你前一个步骤支持set_output最后预测结果你就有了特征名。不过旧版本上这个能力还在演进依赖版本较低时不要强求。5.3 ColumnTransformer不同类型列各自处理的正确姿势真实数据通常是数值列和类别列混杂。ColumnTransformer做的事情很简单指定哪些列走数值处理通道、哪些列走类别处理通道最后把结果并排拼接。from sklearn.compose import ColumnTransformer preprocess ColumnTransformer([ (num, Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, RobustScaler()) ]), [面积, 房龄, 所在楼层]), (cat, OneHotEncoder(handle_unknownignore), [朝向]) ]) X_train_processed preprocess.fit_transform(X_train) X_test_processed preprocess.transform(X_test)这样一串写下来每条通道的逻辑一目了然数值列走填充抗异常缩放类别列走OneHot。后面如果加了新列只需要修改列名列表不需要改任何业务代码之外的东西。我建议把所有特征都在ColumnTransformer里统一定义像“户口本”一样把每个特征的预处理路径写明管理大型项目时极度好用。把ColumnTransformer和Pipeline叠起来就是最终形态full_pipe Pipeline([ (preprocess, preprocess), (regressor, LinearRegression()) ]) full_pipe.fit(X_train, y_train) pred full_pipe.predict(X_test)这时候你手里只有两个对象一个预处理配置和一个模型。这就是我在不同项目间复用的时候最喜欢的样子。复制项目后只需要改动ColumnTransformer的列名和Pipeline的模型对象其余逻辑全部原样继承。5.4 其实还有更快的方法make_pipeline和make_column_transformer上面用的Pipeline([...])和ColumnTransformer([...])是比较显式的写法。sklearn还提供了两个快捷函数make_pipeline和make_column_transformer它们会根据传入对象自动起名字。比如make_pipeline(SimpleImputer(), StandardScaler(), LinearRegression())就能生成同等的流水线省去手动命名步骤名的环节。日常脚本里我倾向于用这套简写但进入需要长期维护的代码库时我建议回到显式Pipeline写法。原因很简单显式命名后你想单独检查中间步骤的输出比如调出某一步的对象会方便得多而make_这类写法虽然省事却让中间步骤不好直接取用。6. 常见问题与排查记录6.1 数据泄露最隐蔽也是最容易犯的错这是预处理里危害最大、最难被发现的坑必须单独讲。典型错误顺序是把scaler.fit_transform接在train_test_split之前或者对全量数据做OneHot的fit。这么做的结果就是测试集的统计信息悄悄流进训练阶段。我举个直观例子假设你整个数据集有1000套房均价300万你在划分前就fit标准化用的是1000套房的均值和方差但实际上模型只能看到700套训练样本测试集300套房的信息被泄露了。线下验证分数比真实泛化成绩更高上线后绩效崩掉就是这类问题的经典表现。正确的顺序永远是谁训练谁fit。train_test_split先执行scaler只在训练子集上fit然后transform测试子集。这个原则虽然简单却值得你每次写代码前默念一遍。按这个思路前面代码里的所有fit对象都必须在训练集上ColumnTransformer、Pipeline的整体fit也同理。6.2 版本差异带来的“隐性”崩溃我踩过最莫名其妙的一个坑是换环境后OneHotEncoder的handle_unknown变成了报错模式。旧版本里默认是error如果测试集出现了训练集没见过的类别预测阶段会直接崩掉。解决方案很无脑但有效建立一个预处理“标准配置”文件把OneHotEncoder(handle_unknownignore)、OrdinalEncoder的categories等敏感参数固定下来不依赖默认值。特别是多人协作的项目里默认值是最不可信的东西。线上环境和你本地环境版本差几个小版本行为就可能不一样。把关键参数的显式声明写清楚比指望队友自觉靠谱得多。6.3 稀疏矩阵和特征名对不上怎么办ColumnTransformer和OneHotEncoder输出的稀疏矩阵非常省内存但在某些场景会带来麻烦。比如你要看特征重要性却发现特征名字只剩“列0、列1”或者直接用pandas DataFrame去切列时类型对不上。新版sklearn的解法是set_output。在ColumnTransformer之前调用set_output(transformpandas)输出就变成DataFrame特征名也会自动拼接出每列的含义。比如朝向_朝南、朝向_朝北这种名字就出来了后面做特征筛选、结果解释就顺很多preprocess.set_output(transformpandas)有一点要注意set_output在proprocessor和model两端都要考虑如果管道里的模型接受稀疏输入但你设成pandas反而可能影响性能。实操上我一般只在调试和交付报告中开启DataFrame输出训练阶段保持紧凑形态。6.4 自定义变换FunctionTransformer的正确用法Preprocessing库里肯定覆盖不了所有奇奇怪怪的清洗逻辑比如日期转星期几、对数值取log、文本去空格。这种时候很多人会回到pandas层面“手工操作”但手工操作又会破坏Pipeline的完整性。sklearn给了个FunctionTransformer能把自定义函数包成sklearn风格的变换器顺利接进Pipelinefrom sklearn.preprocessing import FunctionTransformer import numpy as np def log1p_transform(x): return np.log1p(x) log_transformer FunctionTransformer(log1p_transform) # 后续可以放进Pipeline的某一步自定义函数带进Pipeline有个细节值得说如果函数只支持一维数组而sklearn内部传递的是二维数组很容易把shape搞崩。稳妥做法是直接在函数里加一个reshape或确保它是向量化操作这样不依赖外部状态放进Pipeline也不会跟全局变量起冲突。把这个专题串起来我实际跑通的一条完整流水线最后我想完整给出一条我实际用于房价Demo的预处理流水线它是前面所有讨论的一个综合用例。数据列不复杂但预处理逻辑完整覆盖了缺失、缩放、编码和模型组合from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import RobustScaler, OneHotEncoder from sklearn.ensemble import RandomForestRegressor preprocess ColumnTransformer([ (num, Pipeline([ (imputer, SimpleImputer(strategymedian)), (scaler, RobustScaler()) ]), [面积, 房龄, 所在楼层]), (cat, OneHotEncoder(handle_unknownignore), [朝向]), (decor, OrdinalEncoder(categories[[毛坯, 简装, 精装]]), [装修等级]) ]) model Pipeline([ (preprocess, preprocess), (regressor, RandomForestRegressor(n_estimators100, random_state42)) ]) model.fit(X_train, y_train) print(model.score(X_test, y_test))这条流水线里我特意用了RobustScaler而不是StandardScaler就是因为房龄和面积里都有长尾朝向走OneHot装修等级走OrdinalEncoder且顺序显式指定。整个流程在同一个对象里完成训练和预测完全一致。你可以把这条代码当模板替换成自己的列名和数据基本可以立马上手。预处理看起来是“脏活累活”但它直接决定模型能爬多高。我自己在这个环节上花的时间往往比建模调参还多而且回报率极高。很多人抱怨同一批数据为什么别人跑出来的效果就是好一些差别可能不在模型就在这些预处理细节上缺失值是不是真按业务逻辑处理的、缩放器选对了没、编码有没有引入虚假顺序、测试集有没有被提前偷看。把这几个问题一次理清比调两轮超参数实在得多。
延伸阅读

更多相关文章

2026/10/22 15:02:13

Python数据可视化实战:从Pyecharts到ECharts交互式看板搭建

1. 为什么Python开发者要学ECharts先说个我自己的体会。很多做数据分析的人,一提到可视化,第一反应就是Matplotlib或者Seaborn。这两个库确实经典,静态图表、论文插图、快速出图,都没得挑。但如果你做过需要和业务方反复沟通的数据…

2026/10/22 14:57:12

IMX6ULL嵌入式Linux驱动开发实战:从字符设备到设备树与中断调试

入门Linux驱动开发,很多人第一脚就踩空。拿着内核源码从进程管理开始啃,啃到调度器、内存分配就放弃了,回头发现连一个像样的驱动都没写过。我自己带过不少新人也面过不少候选人,一个很明显的感受是:驱动开发的核心不在…

2026/10/22 14:57:12

J2ME老工程实战:System.getProperty取IMEI与基站LAC/CID联动解析

简介:这是一份面向J2ME移动开发者的实用代码包,聚焦如何通过Java ME API获取手机IMEI码与基站Cell ID信息,适合初学移动设备编程或需要实现基础定位功能的读者参考。压缩包共20个文件,包含6个class、4个java源码、3个properties配…

2026/10/22 16:08:11

C++覆盖率失真?用gcovr排除标记精准控制统计范围

代码覆盖率这件事,做C项目的应该都不陌生。用gcov和gcovr跑完一轮测试,拿到一份覆盖率报告,看着总行号、分支数、函数数,心里第一反应是“这数字到底可不可信”。我见过太多次这样的场景:辛辛苦苦补了一堆测试用例&…

2026/10/22 16:08:11

Apusic任意文件上传漏洞自查:用Python脚本快速检测中间件风险

企业安全自查这件事,最怕的就是“知道有风险,却不知道自家系统有没有中招”。正好前阵子帮某单位做中间件安全自查,遇到的就是Apusic应用服务器的任意文件上传漏洞,网上公开信息零零散散,工具也大多只针对某几个常见版…

2026/10/22 16:08:11

AI日报系统设计:日期驱动的可信内容流水线

1. 项目概述:这不是一份新闻简报,而是一套可复用的AI内容生产流水线“AI 日报 2026-10-05”这个标题乍看像某天的科技资讯合集,但真正有价值的部分藏在日期格式与命名逻辑里——它不是静态快照,而是动态生成系统的一个时间戳实例…

2026/10/22 16:08:11

C++容器适配器stack与queue:从底层原理到实战应用

不知道你在刷题或者做工程的时候有没有过这种感觉——需要临时存一批数据,后进先出;需要排队处理任务,先进先出。大部分人第一反应是去数组或者链表里自己维护一个结构,写一半才发现边界条件一堆,bug藏得深。其实C标准…

2026/10/22 16:08:11

基于Python的推荐系统毕业设计:算法选型与评测体系全解析

每年到毕设季,我都会遇到一批选了类似题目的学生,题目清一色写的是“基于Python的智能推荐系统设计与实现”。说实话,这个题目本身没有错,它是一个非常标准、也很有展示空间的综合型题目,能覆盖数据采集、算法设计、系…

2026/10/22 16:03:05

Neo4j认证限流AuthenticationRateLimit的排查与解决

刚接到这个问题的时候,我第一个反应不是“密码错了”,因为 Neo.ClientError.Security.AuthenticationRateLimit 这串报错里的关键词其实是 RateLimit ,而不是 incorrect authentication 。前者才是真正的异常类型,后者只是对…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑