恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

评分卡建模必知:数据预处理全指南——缺失值、分箱与WOE编码实战

  • 首页
  • 资讯中心
  • /
  • 评分卡建模必知:数据预处理全指南——缺失值、分箱与WOE编码实战

相关资讯

MySQL数据库1:数据库基础 2026/10/3 7:26:53
游戏像素资产生成工具 2026/10/3 7:26:53
一句话驱动真实操作:VisionClaw execute工具调用与Agent技能路由原理详解 2026/10/3 7:26:53

最新资讯

GIKT深度知识追踪:从交互日志到个性化习题推荐的Python实践
射频系统入门:从链路架构到测试调试的关键技术解析
电磁炉线圈加热仿真:涡流损耗与热耦合建模实战指南
基于事件触发机制的孤岛微电网二次电压与频率协同控制仿真
基于MySQL的食谱菜谱数据库设计与检索实践
Dify集成mem0实现AI长期记忆:让智能助手跨会话记住用户偏好

今日推荐

SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成
编译原理实验:递归下降分析器消除左递归与避坑指南
Python协议级爬取Shopee商品数据实战

本周热门

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

评分卡建模必知:数据预处理全指南——缺失值、分箱与WOE编码实战

发布时间:2026/10/3 7:31:54
评分卡建模必知:数据预处理全指南——缺失值、分箱与WOE编码实战 评分卡建模这事很多新手一开始都把注意力放在算法上觉得逻辑回归谁不会啊调个包跑一下分分钟出结果。但真正在风控领域做过几个完整项目的人都知道数据预处理才是整个评分卡建模里最耗时、最考验功底的环节。前两篇我们把数据概况摸底、数据类型修正、重复值处理这些基础工作聊完了这篇作为数据预处理系列的收尾我把剩下的几块硬骨头一次讲透缺失值的高级处理、极端值的截断、变量分箱与WOE编码、IV值筛选还有多重共线性排查。这些东西做完数据才是真正具备了进评分卡模型的资格。这个系列写到第三篇我不想再从头铺垫概念了直接进入正题。文里所有的思路和代码都是我在实际信贷风控项目里一点一点踩坑踩出来的希望能帮你少走弯路。1. 数据预处理在评分卡建模中的整体定位1.1 前两部分做了什么这篇补什么先说清楚系列进度。part.1和part.2重点处理的是数据层面的脏乱差问题查看数据规模和字段分布、识别数据类型错误、处理完全重复的记录、修正日期格式、统一分类字段的取值等。这些是数据预处理的地基工程但离建模还有距离。part.3要处理的是直接影响特征质量和模型效果的深加工环节。从数据处理流程来看大概包括四个步骤缺失值处理不是简单是fillna就能糊弄过去的尤其评分卡讲究变量稳定性和业务可解释性异常值与极端值截断评分卡里的极端值处理方式和一般机器学习项目差异很大变量分箱与WOE编码这是评分卡和普通机器学习模型最大的分水岭IV值筛选与共线性诊断给变量做减法避免模型冗余和过拟合这几个环节环环相扣。缺失值和极端值不处理好分箱就会分得很难看WOE值也会被带偏最后IV值筛选出来的变量集合可能全是假信号。所以这篇的内容虽然看起来是预处理实际上决定了后面评分卡能打成什么样。1.2 数据预处理和评分卡效果之间的关系我在带新人做评分卡的时候经常讲一句话逻辑回归本身非常老实你喂什么特征它就学什么规律但前提是你喂的东西得干净、稳定、有区分度。数据预处理在评分卡里的作用不只是把数据弄干净而是要把原始字段转化成有业务含义、能稳定区分好坏客户的预测变量。举个最简单的例子原始收入字段可能有100万和一个亿的富豪建模时这种极值如果不截断分箱出来第一箱可能是0到2万第二箱变成2万到5000万样本全挤在一个箱子里这个变量基本就废了。而经过合理的分箱和WOE编码之后每一箱对应的是该收入区间内的坏账率水平这种编码方式天然对极值和缺失免疫模型也更稳健。另外评分卡对可解释性的要求很高审批人员需要能看懂每个评分项代表什么。原始字段进模型很难解释收入每增加1元分数增加0.03分这种话但分箱后可以说收入在3000-5000元的客户风险评分为-18分。这就是数据预处理对业务落地的意义。2. 缺失值处理别上来就fillna2.1 先搞清缺失机制再动手缺失值处理的第一步不是填补而是搞清楚为什么会有缺失。缺失机制大概分三种完全随机缺失MCAR字段缺失与否和任何变量无关比如客户填表时手滑漏填随机缺失MAR缺失概率和其他变量有关比如高收入人群更不愿意填写负债信息非随机缺失MNAR缺失本身就和取值有关比如逾期客户更不愿意填收入在实际信贷数据里纯MCAR的情况非常少更多是MAR甚至MNAR。这意味着如果直接删除缺失样本可能会导致样本选择偏误后面建出来的模型对特定人群失效。我把这些机制讲给我团队新人的时候总是让他们先做一件事把每个字段的缺失率算出来再按是否缺失分组对比一下好坏比例和其他字段分布。如果缺失样本的目标变量分布和其他样本差异很大说明这个字段的缺失本身可能就携带信息不能简单粗暴填掉。2.2 评分卡常用的缺失值处理方案评分卡项目里缺失值处理方案通常按变量重要性和缺失比例来选择。缺失比例低于5%的字段用中位数填充基本就够了。为什么不用均值因为信贷数据里大部分连续变量收入、负债、额度使用率都是右偏分布均值会被极端值拉高中位数更稳健。我之前处理一个收入字段均值3.8万、中位数只有6000用均值去填充缺失值完全背离了实际情况。缺失率在5%到30%之间的字段建议不要盲目插补可以在填充后额外增加一个是否缺失的指示变量。这个做法的依据是前面说过缺失可能本身携带信息。比如工作单位电话是否缺失在很多场景下就是一个不错的风险信号。缺失率超过30%的字段除非业务上非常关键否则我倾向于直接弃用。留存变量时也可以先用这个字段构造衍生特征再决定是否保留。模型插补比如用随机森林或者KNN预测缺失值在普通机器学习项目里很常见但在评分卡场景下我没用过几次。原因是评分卡讲究稳定性和可解释性模型插补引入的偏差很难向业务方解释清楚而且如果插补模型用过目标变量还会引入数据泄露。2.3 实操代码缺失值处理示例这一步用pandas就能处理干净我直接给一个实际项目里常用到的处理示例import pandas as pd import numpy as np def process_missing(df, features_meta): features_meta: dict { 字段名: { missing_rate: 0.2, fill_method: median, # median/mode/None add_missing_flag: True, } } for col, meta in features_meta.items(): if col not in df.columns: continue missing_rate df[col].isnull().mean() if missing_rate 0.3: print(f{col} 缺失率 {missing_rate:.2%}直接删除) df df.drop(columns[col]) continue fill_method meta.get(fill_method, median) if fill_method median: fill_val df[col].median() elif fill_method mode: fill_val df[col].mode()[0] else: fill_val 0 df[col] df[col].fillna(fill_val) if meta.get(add_missing_flag, False): df[f{col}_missing] df[col].isnull().astype(int) return df这段代码把缺失率超过30%的字段直接删掉其他字段按预设方法填充并且为指定的关键字段增加缺失指示变量。2.4 缺失值处理的常见误区第一个误区是过度依赖插补。我看到有些项目把缺失值当成纯技术问题什么字段都上模型插补结果变量是填上了但填出来的值和真实值偏差很大反而污染了模型。评分卡是给业务用的不是打比赛插补方法越简单越可解释越好。第二个误区是忽略缺失率随时间的变化。一个字段这个月缺失率5%下个月可能变成20%——这种情况在业务系统升级或者线下收集渠道改变时非常常见。如果建模时没注意上线后模型分数会发生明显波动。所以在做缺失值处理的时候习惯按时间切成训练集和测试集分别看一下缺失率是否稳定。3. 异常值与极端值处理截断比删除更明智3.1 为什么异常值在评分卡里更要命异常值在普通机器学习模型里主要影响是让模型学歪在评分卡里影响要再放大一个数量级因为它会直接影响分箱边界和WOE编码。想象一个字段历史逾期次数绝大多数人都是0-3次但有一个人是50次。如果不去管它分箱的时候很可能单独分出一个箱子34-50次箱内可能只有这一个样本算出来的WOE值极高或者极低IV值也跟着暴涨。最终这个变量进入模型后一旦线上出现一个逾期20次的客户落进了没有样本支撑的箱子里评分结果完全不可信。所以评分卡里的异常值处理不是删掉几个点这么简单而是要确保分箱之后的每一个箱子都有足够的样本支撑。3.2 识别极端值的几种常用方法首先说业务规则。像年龄小于16岁、收入为负、贷款金额大于公司规定上限这些直接按业务逻辑修正或者剔除不需要统计学方法。这块我习惯在做数据清洗的阶段就处理掉part.2里提过不清楚的可以翻一下前面那篇。第二步是用分布方法识别极端值我用得最多的是分位数截断。连续变量通常在1%和99%分位数处做截断把小于1%分位数的值拉到1%分位数大于99%分位数的值拉到99%分位数。这个做法的好处是不改变样本量也不会让分布出现空洞。3sigma原则在评分卡场景下我不太建议用。信贷数据里大部分变量严重偏态先做Box-Cox变换再套3sigma又不是太直白业务方很难理解分位数法更直接。3.3 实操代码分位数截断与业务规则处理def cap_extreme_values(df, col, lower_q0.01, upper_q0.99): lower_bound df[col].quantile(lower_q) upper_bound df[col].quantile(upper_q) df[col] df[col].clip(lower_bound, upper_bound) return df这段代码含义很直白把1%分位数以下的值直接拉到1%分位数99%分位数以上的值拉到99%分位数。中间值完全不受影响。有个容易忽略的点分位数截断应该在划分训练集和测试集之前做而且只从训练集上计算分位数边界然后把同样的边界应用到测试集上。道理很简单测试集是模拟未来线上数据的未来是什么分布你不知道所以不能拿测试集自身的信息去处理数据否则会低估真实场景下的数据波动风险。3.4 处理异常值时要注意的细节截断边界的选择要结合业务。我之前处理过一个客服通话时长字段从分布上看99%分位数是35分钟但业务方告诉我超过30分钟的通话基本都是异常事件正常的营销回访很少超过这个时间。这种情况下边界就应该按照业务标准去截断而不是机械地取99%分位数。另外极值截断之后一定要重新看一下变量分布。有一次我截断完一个负债率字段发现50%以上的样本都堆在截断边界上了说明这个字段本身的数据质量很成问题分箱之前还得再进一步处理。截断不是终点截断之后的效果要回头检查。4. 变量分箱与WOE编码评分卡的灵魂4.1 为什么一定要分箱这个问题我每次培训都会被问到逻辑回归不是可以直接吃连续变量吗为什么评分卡一定要分箱原因有三个层面。第一是处理非线性关系。比如年龄和违约率通常是U型关系年轻人和老年人都偏高中年人偏低如果把年龄直接作为连续变量放进逻辑回归模型只能学到一个单调的线性趋势U型关系学不出来。分箱之后每一箱都有独立的WOE值非线性关系自然就被捕捉到了。第二是增强稳定性。连续变量的细微波动会导致同一个客户前后两次评分不一致通过分箱将变量离散化细微波动只要不跨箱就不会影响得分。第三是可解释性。评分卡的一个核心要求是每个评分项能向业务人员和监管方解释清楚。分箱后可以说该客户年龄落在25-35岁区间该区间的WOE值为0.45对应评分加分为12分这是连续变量做不到的。4.2 常用的分箱方法对比分箱方法我用过很多种各有各的适用场景。等距分箱最直观直接按取值区间宽度均分。但信贷数据里连续变量往往严重右偏等距分箱会导致数据集中在前面的箱子里后面几个箱子可能都没什么样本这种分箱基本不具备区分能力。等频分箱好一些按样本量均分保证每箱样本量均衡。但有时候会把某个取值切到两个箱子里面。这种情况处理起来麻烦常用做法是把同一取值归入同一个箱子允许部分箱子样本量不一致。决策树分箱是我最常用的方法之一。它以目标变量为监督信号自动寻找最优切分点分箱结果通常有较好的区分度。实际操作中用sklearn的DecisionTreeClassifier设置max_depth在3到4之间限制最小叶子节点数然后从树结构中提取分裂阈值作为分箱边界。卡方分箱也经常在评分卡工具包里出现它通过相邻箱体的卡方检验来决定是否合并箱体。对熟悉统计的人来说比较直观但实现起来比树分箱要绕一些。4.3 WOE与IV的数学原理WOE全称Weight of Evidence证据权重核心思想是用每一箱的好坏样本占比来衡量该箱的风险水平。计算公式是WOE ln(坏样本占比 / 好样本占比)这里坏样本占比 该箱坏样本数 / 总坏样本数好样本占比 该箱好样本数 / 总好样本数。如果某一箱的WOE为正说明坏样本在该箱内的集中度高于好样本也就是该箱的违约风险偏高。IV值的公式它本质上是把每个箱子的WOE按好坏占比差异做加权求和IV sum( (坏样本占比 - 好样本占比) * WOE )IV值越高说明变量区分好坏客户的能力越强。这里要特别提醒一点在计算坏样本占比的时候用的是占全部坏样本的比例不是箱内坏样本占箱内总样本的比例。一字之差意思完全不一样。很多新手算出来IV值异常高基本都是栽在这个地方。4.4 实操代码决策树分箱与WOE计算这段代码我给出一个可直接套用的版本import numpy as np import pandas as pd from sklearn.tree import DecisionTreeClassifier def tree_binning(df, col, target, max_depth3, min_leaf_pct0.05): 决策树分箱返回分箱阈值列表 df_used df[[col, target]].dropna() X df_used[[col]] y df_used[target] min_samples_leaf int(len(df_used) * min_leaf_pct) clf DecisionTreeClassifier( max_depthmax_depth, min_samples_leafmin_samples_leaf, min_samples_splitmin_samples_leaf * 2 ) clf.fit(X, y) # 提取树分裂阈值 thresholds [] def recursive_extract(node0): tree clf.tree_ if tree.children_left[node] ! tree.children_right[node]: thresholds.append(tree.threshold[node]) recursive_extract(tree.children_left[node]) recursive_extract(tree.children_right[node]) recursive_extract() thresholds sorted(set(thresholds)) # 给分箱边界加上正负无穷 bins [-np.inf] thresholds [np.inf] return bins def calc_woe_iv(df, col, target, bins): 基于分箱结果计算WOE和IV df_used df[[col, target]].copy() df_used[bin] pd.cut(df_used[col], binsbins, duplicatesdrop) total_bad df_used[target].sum() total_good len(df_used) - total_bad grouped df_used.groupby(bin)[target].agg([sum, count]) grouped.columns [bad, total] grouped[good] grouped[total] - grouped[bad] grouped[bad_dist] grouped[bad] / total_bad grouped[good_dist] grouped[good] / total_good # 这里加一个极小的平滑项避免分母为0 grouped[woe] np.log( (grouped[bad_dist] 1e-6) / (grouped[good_dist] 1e-6) ) grouped[iv] (grouped[bad_dist] - grouped[good_dist]) * grouped[woe] return grouped, grouped[iv].sum()这段代码里有一个细节需要注意决策树分箱的时候我通过限制min_leaf_pct来确保每个箱子至少覆盖5%的样本。这只是初始分箱后续通常还需要手动合并样本量太少或者WOE趋势不符合业务常识的箱子。WOE和IV算完之后一定要检查WOE的单调性。对于连续变量如果WOE出现高-低-高-低这种锯齿状波动说明分箱过细或者某个箱子样本太少。评分卡实践中一般希望连续变量的WOE呈现明显的单调趋势这样业务上才说得通。如果出现了锯齿状就要并箱或者重新分箱。5. IV值筛选与共线性诊断给变量做减法5.1 IV值解读与变量筛选标准变量经过分箱和WOE编码之后第一件事就是看每个变量的IV值。IV值是对变量预测能力的整体度量我通常按照下面这个标准来筛选IV值范围预测能力处理建议0.02基本无预测能力直接淘汰0.020.10弱预测能力结合业务判断一般保留0.100.30中等预测能力重点保留0.300.50强预测能力重点保留0.50可疑的强预测能力警惕过拟合需复查分箱结果IV值大于0.5这个阈值很多人不理解为什么太强了反而可疑。原因在于出现这么高的IV常常是因为某个箱子的WOE值极度偏离其他箱子而这种极端的WOE值往往来自极少的样本量说明分箱边界太细变量在线上的表现很可能和训练集完全对不上。遇到这种情况我一般是把那个极端箱体和相邻箱体合并再重新计算IV。5.2 相关系数与VIF检验IV值筛选解决的是单个变量的预测能力问题但多个强预测能力的变量一起放进模型还会面临多重共线性问题。逻辑回归对特征之间的强相关性很敏感严重的多重共线性会导致系数符号和实际业务方向相反或者系数标准误膨胀模型的稳定性大打折扣。我习惯的排查方式是两步走。第一步看皮尔逊相关系数矩阵对数值型变量绝对值超过0.7就算高相关需要警惕第二步是计算VIF方差膨胀因子VIF大于10说明该变量的共线性问题比较严重。在评分卡场景下处理共线性不一定是把其中一个变量删掉也可能是把两个相关系数高的变量做衍生合并。比如收入和月消费相关性强但月消费/收入这个比率同时包含了两个变量的信息业务意义也更明确。5.3 实操代码完整的变量筛选流水线这里给一个综合的筛选流程示例from statsmodels.stats.outliers_influence import variance_inflation_factor def select_vars_by_iv(df, target, cols, iv_threshold0.02): 第一步按IV值筛选变量 selected [] iv_dict {} for col in cols: # 简单等频分箱后进行WOE计算 try: bins pd.qcut(df[col], q5, duplicatesdrop) except: continue df_used df[[col, target]].copy() df_used[bin] bins _, iv calc_woe_iv(df_used, col, target, binsNone) # 这里calc_woe_iv需要做适配实际项目可以封装函数 iv_dict[col] iv if iv iv_threshold: selected.append(col) return selected, iv_dict def check_vif(df, cols): 第二步计算VIF剔除高共线性变量 X df[cols].fillna(0) X (X - X.mean()) / (X.std() 1e-6) vif_data pd.DataFrame({ var: X.columns, vif: [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] }) return vif_data.sort_values(vif, ascendingFalse)注意第一个函数里我把分箱后的边界作为参数传入calc_woe_iv时其实是不对的实际项目更合理的做法是先分箱生成bin列再在bin列上直接计算WOE和IV。上面那段代码重点是展示流程思路函数体里我已经注释了需要适配的地方不要直接复制去用。完整流程应该是分箱 → 计算WOE → 计算IV → 按阈值筛选 → 检查相关性 → 计算VIF → 人工复核。这个流程我用Python脚本跑过无数次核心不在于某个函数多复杂而是每一步都要留出人工检查空间。自动筛选只是第一步最后留下来的变量集合还要经过业务方评审。我在一个项目里就遇到过这种情况某个变量的区分能力很强但业务规则要求它不能进入评分卡因为监管上对某些个人信息的使用有限制。这种流程外的控制条件必须在变量筛选阶段就考虑清楚。6. 踩坑实录与避坑指南6.1 分箱时最容易被忽略的边界问题分箱的边界处理是我见过新人踩得最多的坑。用pd.qcut的时候如果数据里有大量重复值会直接报错Bin edges must be unique这个问题倒是好解决加个duplicatesdrop就行。更隐蔽的问题是分箱边界和业务理解不一致。有个很典型的案例处理年龄这个变量的时候等频分箱自动把边界切在42岁但业务方习惯用青年/中年/老年这种三段划分站在模型角度它没错站在业务沟通角度却很难达成共识。我在实际项目里的做法是先用自动分箱得到初步边界然后结合业务语义做手工微调把边界归整到整数或者符合业务习惯的阈值上。这样能达到建模效果和业务可解释性的平衡。另外分箱的边界必须在训练集上确定好然后固定下来测试集和线上数据都沿用同一套边界。否则每次重算分箱的边界都会有波动模型上线后就变成了When your model changes every time you retrain的经典困境。6.2 WOE计算中坏样本占比为0的问题坏样本占比为0也就是某个箱子里面全是好客户没有坏客户。很多新手第一次遇到这种情况直接取ln(0)就NaN了程序报错。有人会乱填一个很小的数比如1e-6然后算出来一个很极端的WOEIV值被顶得很高。这种做法其实是自欺欺人。正确的处理思路是如果箱子里的坏样本为0说明这个箱子的风险极低可以直接把它和相邻的箱子合并而不是强行保留一个没有坏样本的独立箱子。如果业务上确实认为应该单独保留一个空白风险区那也要在WOE计算时做保守平滑并且对最终IV值持有怀疑态度。我在实际项目中遇到这种情况通常会回头看一下变量在这个箱子上的样本量。如果样本量足够大但没有坏客户那这个变量确实有极强的区分能力如果样本量很小那就纯粹是偶然必须合并箱子。6.3 时间穿越与数据泄露风险这个问题在评分卡项目里极其隐蔽我见过不止一个团队踩过坑。表现为模型在训练集上表现特别好IV值高得离谱但上线之后效果一塌糊涂。排查了两周最后定位到问题是特征里包含了未来信息。典型的例子是在构建样本的时候把历史逾期次数算错了时间窗口把表现期观察是否变成坏客户的这段时间内的逾期也算了进去。这个字段本质上包含了目标变量的信息所以它的预测能力当然非常强IV值能轻松超过0.5。在数据预处理阶段处理时间窗口字段时必须严格控制时间边界。观察期特征只使用观察期之前的数据加上不同时间段的滞后项时必须确认所有滞后特征在样本点上都是已知的不是未来函数。这一点我在每一篇里都要强调一次因为真的重要。6.4 关于评分卡数据预处理的总经验做了这么多年评分卡项目我的体会是数据预处理70%是耐心活30%才是技术活。耐心体现在每个字段的缺失分布、异常分布、分箱效果都要逐个检查没有捷径。技术体现在面对各种边界情况的时候能不能迅速定位问题并且找到合理的业务解释。还有一条很实用的经验所有的数据预处理逻辑都应该写成可复用的函数而不是在notebook里手拉手地改。评分卡建模是一个反复迭代的过程变量要调、样本要换、参数要改每一步都可能要回到数据预处理重新跑一遍。如果你每次都是手动在notebook里执行一遍等到要回溯或者重跑的时候就会感到非常痛苦。这个系列的数据预处理部分到这是真正告一段落了。接下来应该进入评分卡最核心的建模环节——逻辑回归拟合、分数刻度映射、模型评估。如果你也正在做评分卡项目数据处理阶段碰到什么具体问题欢迎在评论区交流我看到了都会回复。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号