Orange3 数据挖掘工作流:可视化建模与 Python 脚本复现 简介Orange 是一款基于组件的数据挖掘与机器学习软件套装面向数据分析初学者、教学人员及需要快速验证建模思路的开发者内置可视化编程前端与 Python 脚本扩展可完成数据预处理、特征探索、建模、模式评估与结果可视化等流程。压缩包共收录约 2000 个文件整体约 19.26MB内容以 png 界面与图表、py 脚本、txt/rst 文档说明为主并包含 svg、cpp/hpp、tab、csv 等素材、源码、数据表与示例配置便于了解组件实现和示例数据组织。该资源已有 2102 人学习下载适合作为 Orange 组件、示例工作流和跨平台 Qt 图形界面的参考资料也可用于课堂教学演示、脚本开发入门和数据分析项目原型搭建帮助读者较快理解从数据接入、预处理到建模评估的完整工具链。1. 数据挖掘不用先写代码Orange 把建模流程摊在画布上很多人第一次接触数据挖掘和机器学习卡住的地方不是算法而是我该先做什么、后做什么。Orange 这个开源项目解决的就是这件事它把读数据、清洗、特征变换、训练、评估这几步做成一块块可以拖拽的组件连上线就能跑出一份带交叉验证的结果而它背后又是纯 Python 的Orange3库画布上每个组件的参数都能用同样的几行脚本复现出来。对刚要把一套流程跑通的人它是可视化试验台对要交付可复现脚本的人它是把探索过程直接转成代码的桥梁。下面按装环境、喂数据、搭流程、写脚本、避坑的顺序走一遍。2. 环境与数据入口pip 装完之后先解决列类型在画布上拖组件之前有两件事必须先落地一是 Python 环境别和系统里已有的东西打架二是数据和列类型要提前定死。这两步做得糙后面所有模型的结论都不可信。2.1 用独立环境装 Orange3避开 Qt 与科学计算栈的版本冲突Orange 的图形界面依赖 Qt底层又依赖 numpy、scipy、scikit-learn、pandas 这一整串。如果直接往系统 Python 里装很容易撞上已有的 Qt 绑定或者旧版 scikit-learn表现出来就是双击图标闪退、菜单里组件灰掉、插件装不上。常见做法是给每个项目单独开一个虚拟环境。# 建一个干净环境Python 版本别太老3.10 往上比较省心 python -m venv orange-env # Linux / macOS 激活 source orange-env/bin/activate # Windows 激活 # orange-env\Scripts\activate # 升级打包工具再装核心包 python -m pip install --upgrade pip setuptools wheel pip install orange3 # 起图形界面 python -m Orange.canvas这段命令的逻辑很直白venv隔离依赖pip install orange3会把运行时需要的一整串科学计算包一起拉下来最后那句python -m Orange.canvas是启动入口——注意不是orange3这个命令从模块方式启动能避免 PATH 里指向另一个解释器的问题。装完先别急着开界面用几行代码确认库能正常导入、版本能打印出来import Orange import numpy, scipy, sklearn, pandas # 打印各层依赖的实际版本出问题时这张表比任何报错都值钱 print(orange3 :, Orange.__version__) print(numpy :, numpy.__version__) print(scipy :, scipy.__version__) print(sklearn :, sklearn.__version__) print(pandas :, pandas.__version__)这段脚本的作用是留一份环境快照。Orange 的组件在渲染时会去读底层库的接口一旦 scikit-learn 的版本跳得太多某些组件的参数面板会直接报错但界面不崩属于典型黑匣子现象。把版本打出来存一份后面换机器复现时直接对着看。参数层面要注意的是不要在一个环境里同时装多个大版本的 Orange也不要为了让某个插件装上而强行pip install -U scikit-learn去覆盖已有版本。要装插件就在这个干净环境里装让 pip 自己去解依赖。2.2 CSV 进 Orange 之前先想清楚哪些列是连续、哪些是分类Orange 的数据模型把列分成三类特征features、目标target/class、元数据metas。其中特征和目标的取值又分连续numeric和离散categorical。这个分类不是装饰它决定了后面哪些算法能用需要距离度量的算法kNN、k-Means、SVM遇到没编码的字符串列会直接失效逻辑回归遇到没归一化的连续列会收敛得很差。Orange 读 CSV 时有自己的一套类型标记语法。第一行写列名第二行写类型标记第三行写角色标记。这套写法很多人不知道结果全靠自动推断推断错了再回来改来回好几轮。标记含义什么时候用c离散categorical性别、渠道、等级这类取值有限的列s字符串string订单号、备注一般放元数据不参与建模#元数据meta只跟着行显示不进入特征矩阵空连续numeric金额、时长、次数第三行写class标为目标列分类任务的标签第三行写meta标为元数据同上只是显式声明一份带标记的 CSV 长这样客户编号,访问次数,平均停留时长,来源渠道,是否流失 s,,,c,class meta,,,,meta C0001,12,3.5,搜索,否 C0002,3,0.8,推荐,是第二行四个位置分别是s字符串、空连续、空连续、c离散第五个位置空着表示目标列的类型交给 Orange 按取值推断——因为只有是/否两个值会被识别成二分类。第三行标了class和meta明确角色。如果不用这套标记那就得保证三件事表头只有一行且没有合并单元格缺失值统一写成空或者?不要写NA、null、-文件编码用不带 BOM 的 UTF-8。Excel 另存 CSV 默认带 BOMOrange 读到第一列列名时会多出一个不可见字符界面上看不出来但写脚本时按列名取属性会抛KeyError这个坑踩过的人都记得。提示拿不准列类型时先在 File 组件里看一眼每列图标——连续列是数值图标离散列是分类图标字符串列带引号。图标错了就说明推断错了回去改 CSV 的类型标记行比在界面里一列一列手改快得多。2.3 用 Table 把同一份数据读进脚本验证所见即所得画布上看到的数据和脚本里读到的数据必须是同一份。这一步验证过了后面在界面里调参数、在脚本里批处理才不会出现两套结论。from Orange.data import Table # 直接按路径读Orange 会自动识别 CSV 的分隔符和上面的类型标记 data Table(customers.csv) # 打印域信息有哪些特征、目标是什么、哪些是元数据 print(data.domain) print(目标列, data.domain.class_var) print(特征, [a.name for a in data.domain.attributes]) print(元数据, [a.name for a in data.domain.metas]) # 三个矩阵的形状X 特征值、Y 目标值、metas 元数据 print(data.X.shape, data.Y.shape, data.metas.shape) # 看缺失值分布这一步决定后面要不要挂 Impute import numpy as np print(特征缺失数, np.isnan(data.X).sum())这里几个属性值得记住data.domain.attributes只含特征data.domain.class_var是目标变量没有目标时为Nonedata.domain.metas是元数据。data.X永远是个二维浮点数组data.Y是一维data.metas是对象数组。参数层面的关键点是domainOrange 里几乎每个组件之间传的都是Table对象而Table的核心就是domain加三个矩阵。想改列类型正确做法是构造一个新的Domain再生成新Table而不是去改data.X里的值——X是浮点数组离散变量在这里存的是索引直接改数字等于改了类别编号后果很难查。from Orange.data import Domain, ContinuousVariable, DiscreteVariable # 把访问次数从连续改成离散取值按分位数切成三档 visits data.domain[访问次数] binned DiscreteVariable.make( 访问次数分档, values[低, 中, 高], compute_valueOrange.preprocess.Discretize( methodOrange.preprocess.Discretize.EqualFreq(n3) )(visits) ) # 用新变量替换旧变量其余列保持不动 new_domain Domain( [binned] [a for a in data.domain.attributes if a.name ! 访问次数], data.domain.class_var, data.domain.metas, ) data2 Table.from_table(new_domain, data) print(data2.domain)这段做的是域重写Discretize.EqualFreq(n3)表示按等频切三档compute_value是 Orange 的惰性求值机制——变量本身记住我怎么从原变量算出来等到真正生成Table时才计算。Table.from_table(new_domain, data)就是触发这次计算的地方。为什么不用Discretize组件在画布上点两者结果一样但脚本里做的好处是这一步能进版本管理下次换数据直接重跑。这也是 Orange 的双面性界面适合探索脚本适合固化。3. 画布上的建模流程从 Impute 到 Test Score 的最小闭环一个能得出结论的 Orange 工作流组件不会超过十个。但顺序错了、连错口出来的数字照样漂亮只是不可信。这一章把最小闭环拆开讲。3.1 预处理组件的连接顺序为什么不能随便调一条稳妥的顺序是File→Impute→按需Continuize/Discretize→Normalize→Feature Constructor→ 学习器 →Test Score。每一步都在解决下一步的前置条件。Impute放在最前面是因为后面所有组件碰到缺失值的行为都不一致Normalize会跳过缺失Discretize会把缺失单独归一类学习器有的报错有的静默丢弃整行。先补齐后面才有确定行为。它的参数面板里几个选项的差别很实际选项行为适用场景Average / Mode连续列填均值离散列填众数缺失比例低5% 以内分布大致对称Model-based imputer用其余列训练一个模型来预测缺失值列之间相关性明显缺失不是随机产生As a distinct value缺失单独成一类缺失本身有业务含义比如从未填写Remove rows with missing values直接删行缺失比例高且样本量充足Continuize处理离散特征。默认是 one-hot把 k 个取值的列拆成 k 个 0/1 列。如果某个列是有序等级低/中/高用 one-hot 会丢掉顺序信息这时候应该选 Treat as ordinal把它映射成 0/1/2。这个选择对线性模型影响很大对树模型影响很小——树模型只关心切分点不关心数值间隔。Normalize必须在Continuize之后。原因是归一化作用在数值尺度上离散列 one-hot 之后是 0/1归一化会把它们变成负数反而破坏了稀疏性而连续列如果量纲差两个数量级比如金额是万级、次数是个位数距离度量会被金额完全主导。常规做法是只对连续列做标准化Orange 的Normalize组件默认就只处理连续特征。注意Feature Constructor放在归一化之后是因为新构造的特征比如两个连续列的比值需要基于已清洗的值计算。如果先构造再清洗缺失值会通过运算扩散一个缺失变成两个缺失。3.2 三类模型的参数落点逻辑回归、随机森林、kNN同一个数据集上跑三种模型目的不是挑一个最高的分数而是看不同假设下的结果差异有多大。差异小说明信号稳定差异大说明模型选择本身就在影响结论这时候更该去看特征重要性而不是盯准确率。from Orange.data import Table from Orange.preprocess import Impute, Continuize, Normalize from Orange.classification import LogisticRegressionLearner from Orange.ensemble import RandomForestLearner from Orange.classification import KNNLearner from Orange.evaluation import CrossValidation, CA, AUC, F1 data Table(customers.csv) # 预处理链补缺失 - 离散转 one-hot - 连续标准化 pre [ Impute(methodImpute.Model()), Continuize(multinomial_treatmentContinuize.Indicators), Normalize(zero_basedFalse), # False 表示按标准差标准化不是压到 [0,1] ] # 逻辑回归C 是正则化强度的倒数越小正则越强 lr LogisticRegressionLearner( penaltyl2, C1.0, preprocessorspre, ) # 随机森林树的数量给够深度先不限制靠叶子最小样本数控复杂度 rf RandomForestLearner( n_estimators200, max_depthNone, min_samples_leaf5, max_featuressqrt, random_state42, preprocessorspre, ) # kNNk 取奇数避免二分类平票距离用欧氏 knn KNNLearner( n_neighbors7, metriceuclidean, preprocessorspre, ) learners [lr, rf, knn] res CrossValidation(k10, stratifiedTrue, random_state42)(data, learners) print(CA :, CA(res)) print(AUC:, AUC(res)) print(F1 :, F1(res, averageweighted))逐段看。Impute(methodImpute.Model())用的是模型补缺适合列间有相关性的场景如果数据量小、缺失多换成Impute.Average()更稳。Continuize.Indicators就是 one-hotk 个取值产生 k 列。如果某个离散列取值特别多比如上百个城市one-hot 会把特征维度炸开这时候改成Continuize.Remove直接丢掉或者先做取值合并。Normalize(zero_basedFalse)是按标准差标准化减均值除标准差True是压到 [0,1]。两种都行但 kNN 和逻辑回归更常用前者因为对异常值的敏感度低一些。三个模型的参数里最值得先动的是RandomForestLearner的min_samples_leaf。默认值很小数据有噪声时会过拟合到每条记录训练集分数虚高、交叉验证分数掉一截。调到 3 到 10 之间通常能看到交叉验证分数先升后降中间那个拐点就是比较合适的取值。CrossValidation(k10, stratifiedTrue)的分层很重要类别不平衡时不分层某些折里正例可能只有一两个AUC 会算出极端值。random_state固定住是为了让同一份代码两次运行结果一致——这在调参时是必需的否则你分不清分数变化来自参数还是来自切分。3.3 Test Score 的结果怎么读别只看第一列界面上Test Score出来是一张表行是模型列是指标。默认第一列是 AUC 或者 CA取决于目标列类型。多数人扫一眼第一列就下结论这一步最容易翻车。AUC衡量的是排序能力和阈值无关。它只看正负样本的相对得分所以对类别不平衡不敏感这在流失预测、欺诈识别这类场景里比准确率可靠得多。但 AUC 有个盲区它不关心概率校准得好不好。一个模型 AUC 很高输出概率全挤在 0.4 到 0.6 之间拿去做业务决策比如按概率分档定价就没法用。CA分类准确率在类别不平衡时会骗人。正例占 5% 的数据集全预测负例也有 95%。这时候要看F1和MCC前者是精确率和召回率的调和平均后者把四个混淆矩阵格子都考虑进去了不平衡下更稳。Precision / Recall是一对业务上要哪个取决于错误代价。漏掉一个高风险客户代价大看 Recall误伤一个正常客户代价大看 Precision。Test Score里还有个容易忽略的设置Resampling 方式。交叉验证是默认重复次数Repeat train/test可以设成 3 到 5把随机切分带来的方差平均掉。如果样本量很少几百条以内改成 Leave-one-out虽然慢但方差最小。数据量大到几十万条交叉验证的十折训练成本就很高了这时候用 Test on train data 快速看是否严重过拟合再用 Test on test data 加独立验证集做最终判断。提示目标列是连续变量时Test Score会切到回归指标MSE、RMSE、MAE、R2。这时候 AUC 那列直接消失不是界面出问题是任务类型变了。4. Python 脚本里的 Orange3把工作流变成可批处理的代码画布适合探索不适合每天跑。数据每周更新、模型要重新训练、结果要写进报表这些都得靠脚本。Orange3 作为库用起来比想象中轻关键是理解Table和Domain这两个对象。4.1 Table 与 Domain 的操作边界Table是数据的容器Domain是列的定义。所有变换本质上都是生成新 Domain再用它生成新 Table。理解这一点就不会去改data.X里的值。from Orange.data import Table, Domain, ContinuousVariable, DiscreteVariable data Table(customers.csv) # 按行筛选只保留访问次数大于 5 的记录 mask data[:, 访问次数].X.flatten() 5 filtered data[mask] print(len(data), -, len(filtered)) # 按列选取取前三个特征加目标列其余丢掉 cols [a.name for a in data.domain.attributes[:3]] sub data[:, cols [data.domain.class_var.name]] print(sub.domain) # 手写构造一个 Domain把某一列升成目标列 target data.domain[是否流失] new_domain Domain( [a for a in data.domain.attributes if a.name ! 是否流失], target, ) relabeled Table.from_table(new_domain, data) print(relabeled.domain.class_var)data[mask]的行筛选返回新Table原对象不变。data[:, cols]的列选取语法和 pandas 有点像但第一个冒号是行、第二个是列列可以用名字列表。Table.from_table(new_domain, data)是重写域的标准入口——它按新域的定义从原表里重新取值比手工拼数组安全得多。Domain构造函数的三个参数依次是特征列表、目标变量、元数据列表。目标可以是None无监督任务元数据可以省略。这里最容易出错的是把同一个变量同时放进特征和目标运行不报错但评估结果会异常好——因为标签泄进了特征。4.2 Python Script 组件里的 in_data / out_data 契约画布上有个Python Script组件它把脚本嵌进工作流里跑。这个组件的输入输出变量名是固定的写错名字不会报错只会拿到None。# 输入变量组件连了什么就有什么没连的是 None # in_data : Table 上游数据 # in_learner : Learner 上游学习器 # in_classifier: Model 上游已训练模型 # in_object : 任意对象 上游自定义对象 # 输出变量不赋值就是没输出 # out_data : Table # out_learner : Learner # out_classifier: Model # out_object : 任意对象 import numpy as np from Orange.data import Table, Domain, ContinuousVariable out_data None if in_data is not None: # 例给每个样本加一列访问频次的对数 freq in_data[:, 访问次数].X.flatten() log_freq np.log1p(np.nan_to_num(freq)) new_var ContinuousVariable(访问次数对数) new_domain Domain( list(in_data.domain.attributes) [new_var], in_data.domain.class_var, in_data.domain.metas, ) out_data Table.from_table(new_domain, in_data) # 显式把算好的值写回新列 out_data[:, new_var.name] log_freq.reshape(-1, 1)这段的关键在最后两行。Table.from_table只负责按新域重建结构新加的列默认是缺失值得手动赋值。赋值时右边必须是二维数组reshape(-1, 1)因为out_data[:, 列名]拿到的切片按列处理。in_data为None的判断不能省。组件在画布上可能还没连线或者上游数据为空这时脚本直接跑会抛AttributeError而且报错栈藏在组件的日志面板里界面上只看到一个红叉。参数层面Python Script组件本身没有参数面板所有配置都写在代码里。这意味着脚本里的常量比如np.log1p的底、阈值改一次就得重存一次工作流。常见做法是把这些常量提到脚本顶部用注释标清楚业务含义别散在中间。4.3 保存与复现 .ows 工作流画布上搭好的流程存下来是一个.ows文件。它本质是个压缩包里面记录组件类型、位置、连线和每个组件的参数设置以及一份版本信息。复现时最容易出问题的地方是版本。同一个.ows在新版本 Orange 里打开组件可能会被自动升级个别参数被重命名或删除界面上看不出来但结果会变。血泪经验是交付工作流时把环境快照和 .ows 一起存。第 2 章那段打印版本的脚本就是干这个用的。另一个细节是数据源路径。File组件里记的是绝对路径还是相对路径直接决定了换台机器能不能打开。相对路径的前提是.ows和数据文件在同一个目录树下交付时整包一起给。如果要把工作流变成无人值守的批处理正确做法不是去解析.ows而是把流程用脚本重写一遍就是 3.2 节那段代码用Table读数据、用Learner训练、用CrossValidation评估。.ows只作为当时是怎么试的这份记录的载体。5. 避坑五个把结论带偏的常见问题5.1 数据侧预处理放错位置交叉验证会泄漏现象交叉验证 AUC 跑到 0.95换个数据集或者换个随机种子掉到 0.7 左右。模型本身没变分数却大幅波动。原因Impute和Normalize如果单独挂在File后面、并且数据在进入Test Score之前就已经被处理过一遍那么填充用的均值、标准化用的标准差里包含了验证折的信息。这叫数据泄漏验证折的统计量污染了训练过程分数虚高。解决预处理要么作为Learner的preprocessors参数传入像 3.2 节那样让它在每一折内部重新拟合要么在画布上把预处理组件连到每个学习器的输入口而不是连在File和数据分叉点之间。判据很简单预处理是否见过验证数据。见过就是泄漏。5.2 建模侧不平衡数据下准确率骗人现象模型准确率 0.93业务方很满意上线后发现该识别出来的样本几乎一个都没抓到。原因正例占比 7%全预测负例就能拿到 0.93。Test Score默认第一列可能显示 CA这个数字在没有基线对比时完全没意义。解决先算一个全预测多数类的基线把它和模型结果并排放。然后切到 AUC、F1、MCC 看。如果确实要做不平衡建模Orange 里可以用Learner的class_weight参数多数学习器支持给少数类加权或者在上游用Select Rows做过采样/欠采样——但采样只能在训练折内做放到全量数据上做又会回到 5.1 的泄漏问题。5.3 建模侧随机森林不设随机种子调参结论不可复现现象同一个参数组合跑两遍分数差 0.02来回试了十几轮不知道哪次是真的提升。原因随机森林的样本自助采样和特征随机选择都带随机性不固定random_state时两次训练用的是不同的随机子集分数波动本身就盖过了参数带来的差异。解决所有带随机性的学习器统一设random_state。更稳妥的做法是配合CrossValidation的重复次数重复 3 到 5 次看分数的均值和标准差而不是单次结果。判断一个参数是否真的有用看的是改动后均值提升是否超过重复实验的标准差不是看某一次跑出来的数字。5.4 环境侧插件装上了组件是灰的现象pip install某个 Orange 插件显示成功重启画布后菜单里能看到对应分类但组件图标是灰的拖不动。原因插件依赖的底层库版本和当前环境冲突或者插件用了当前 Orange 版本已经移除的接口。装的时候 pip 没报错是因为它只检查了包是否存在没检查运行时接口。解决先在命令行单独import一次插件模块把真实报错打出来python -c import orangecontrib.example; print(ok)如果这一步就失败报错信息里会指明缺哪个接口或者哪个库版本不对。按报错把冲突的包降到兼容版本别盲目升级 Orange 本身——升级可能让其他已经跑通的组件失效。装插件前先看一眼它的依赖声明和 2.1 节存的环境快照对一遍。5.5 环境侧CSV 带 BOM按列名取属性抛 KeyError现象界面里数据加载正常、列名显示也正常但脚本里data.domain[某列名]抛KeyError把列名打印出来看着一模一样。原因Excel 另存的 CSV 默认带 UTF-8 BOM第一个列名前面多了一个不可见字符。界面上渲染时被吞掉了字符串比较时却实打实存在。解决读的时候显式指定编码或者先做一次清洗from Orange.data import Table # 让 pandas 先按 utf-8-sig 读掉 BOM再交给 Orange import pandas as pd df pd.read_csv(customers.csv, encodingutf-8-sig, sep,) data Table.from_numpy( domainNone, # 传 None 让 Orange 自动推断域 Xdf.select_dtypes(number).to_numpy(), )或者更直接的办法是回到源头用文本编辑器把文件另存为不带 BOM 的 UTF-8。批量处理时utf-8-sig是更省事的写法——它对不带 BOM 的文件同样有效属于可以无脑加上的参数。6. 进阶用 Rank 加重复交叉验证判断特征重要性是不是真的Rank组件给出的特征重要性排序很多人当成结论直接用。但它只是一个模型、一次训练下的结果。树模型的特征重要性在高相关特征之间会互相抢分两个高度相关的列重要性可能一个 0.3、一个 0.05而实际上它们提供的信息差不多。直接按这个排序做特征筛选很容易把有用的一列删掉。我的做法是把重要性当成一个分布来看而不是一个点。具体是三步换模型、换种子、看排名稳定性。import numpy as np from Orange.data import Table from Orange.preprocess import Impute, Continuize, Normalize from Orange.ensemble import RandomForestLearner from Orange.classification import LogisticRegressionLearner from Orange.evaluation import CrossValidation data Table(customers.csv) pre [Impute(methodImpute.Average()), Continuize(), Normalize()] def importance_ranking(learner, data, seeds(0, 1, 2, 3, 4)): 在不同随机种子下训练收集每次的特征重要性排序 names [a.name for a in data.domain.attributes] ranks [] for s in seeds: learner.random_state s model learner(data) # 树模型和线性模型都能导出 feature_importance imp model.feature_importance if hasattr(model, feature_importance) \ else np.abs(model.coefficients).flatten()[:len(names)] order np.argsort(-imp) ranks.append({names[i]: r for r, i in enumerate(order)}) return ranks rf_ranks importance_ranking(RandomForestLearner(n_estimators200, min_samples_leaf5, preprocessorspre), data) # 汇总每个特征在 5 次实验里的平均排名和排名波动 names [a.name for a in data.domain.attributes] summary [] for n in names: r [rank[n] for rank in rf_ranks] summary.append((n, np.mean(r), np.std(r))) for name, mean_rank, std_rank in sorted(summary, keylambda x: x[1]): print(f{name:20s} 平均排名 {mean_rank:.1f} 波动 {std_rank:.2f})这段的判读逻辑是平均排名靠前、波动小的特征是真正稳定的信号。平均排名靠前但波动很大的说明它和另一个相关特征在抢位置删掉它模型可能一样好平均排名靠后但波动小的基本可以确认是噪声。然后换一个假设完全不同的模型做对照。随机森林基于不纯度减少逻辑回归基于系数绝对值两者对同一批特征给出的排序如果高度一致说明信号是真的如果差得很远说明重要性更多反映的是模型偏好不是数据本身的结构。# 用逻辑回归做一次对照看两套排序的 Spearman 相关 from scipy.stats import spearmanr lr LogisticRegressionLearner(C0.1, preprocessorspre) lr_model lr(data) lr_imp np.abs(lr_model.coefficients).flatten()[:len(names)] rf_mean np.array([np.mean([r[n] for r in rf_ranks]) for n in names]) rho, p spearmanr(rf_mean, -lr_imp) # 取负号排名越小越重要重要性越大越重要 print(f两种模型排序一致性 rho{rho:.2f}, p{p:.3f})C0.1是刻意加大正则强度。正则强的时候冗余特征的系数会被压向 0相关性高的特征群会自然收敛到一个代表上这个结果比不设正则时更能反映去掉冗余之后还剩什么。spearmanr这里做的是秩相关不看数值大小只看排序正好对应重要性排序是否一致这个问题。还有一个容易被忽略的点min_samples_leaf会改变重要性分布。叶子样本数设得太小模型会记住噪声噪声列的重要性被抬高设得太大模型欠拟合真正有用的弱信号列被压平。所以交叉验证选出来的那个min_samples_leaf同时也应该是算重要性的那个值两处别用不同的设置。从那以后我每次拿特征重要性出结论都强制走一遍多种子 双模型的对照——单次、单模型的重要性排序我最多拿它当个探索方向不敢往报告里写。希望帮到你。本文还有配套的精品资源点击获取