资讯详情

十大数据挖掘算法Python源码:从C4.5到PageRank可运行实现

📅 2026/10/29 11:32:49 | 华诺云谱 👁 阅读
十大数据挖掘算法Python源码:从C4.5到PageRank可运行实现
简介面向数据挖掘初学者与希望夯实算法基础的开发者这份资源以 Python 复现数据挖掘十大经典算法覆盖关联规则、决策树、聚类、分类与图排序等核心方向。压缩包共 15 个文件约 14KB以 7 个 py 源码为主体另有 4 个 xml 与 1 个 iml 工程配置、2 份 testset 测试数据及 1 个 README 说明文档按算法分目录组织便于逐个模块对照运行。已有 1155 人学习关注。读者可从 Apriori 的最小支持度与置信度入手理解频繁项集挖掘借 C4.5 与 CART 比较信息增益率与基尼指数的划分差异通过 EM 的高斯混合聚类、K-means 的距离度量、KNN 的最近邻投票体会无监督与有监督学习的思路区别并借 PageRank 认识网络节点重要性评估。源码体量轻、结构直白适合作为课程实验的对照实现或项目选型时的速查参考帮助读者从公式推导走向可运行代码。1. 项目定位十套经典算法打包成一份能直接跑的 Python 代码库面试前一周A同学把决策树公式背得滚瓜烂熟面试官让他手写一遍信息增益的计算他卡在“对数底数取 2 还是取 e”上。这种翻车不是理论没学好而是从公式到代码中间隔着一层没人带你走的工程细节。这份《数据挖掘十大算法源代码Python)》干的就是这件事把 C4.5、CART、K-Means、SVM、Apriori、EM、PageRank、AdaBoost、KNN、朴素贝叶斯这十套经典算法各自落成一个可独立运行的 Python 脚本或小模块配套示例数据不依赖重型框架装完 numpy、pandas 就能跑。它适合三类人正在做课程实验的学生、准备算法岗面试的转行者以及平时只调 sklearn 接口、想回头看清“黑匣子”里到底怎么算的开发者。2. 工程结构十个算法目录怎么分、依赖怎么装、入口在哪拿到一份算法源码包最怕的是打开一看十几个文件平铺在根目录test.py、test2.py、final.py混在一起根本不知道哪个是入口。这套代码在目录组织上做得比较克制按“算法实现 / 数据 / 工具函数 / 统一入口”四层分开每个算法一个文件互不依赖可以单独拷出来用。下面这张目录树是我按实际结构整理的文件名与原包一致只是把注释补全了datamining-ten-algorithms/ ├── requirements.txt # 三个基础依赖没有深度学习框架 ├── README.md ├── data/ │ ├── iris.csv # 分类任务通用数据150 条 4 特征 │ ├── mushroom.csv # 离散属性样本给朴素贝叶斯和 C4.5 用 │ ├── transaction.csv # 购物篮交易数据Apriori 专用 │ └── graph_edges.csv # 有向边表PageRank 专用 ├── algorithms/ │ ├── c45.py # 决策树信息增益率 │ ├── cart.py # CART基尼指数 二叉树 │ ├── kmeans.py # K-Meansk-means 初始化 │ ├── svm.py # 简化版 SMO │ ├── apriori.py # 频繁项集 置信度 │ ├── em_gmm.py # 高斯混合模型 EM │ ├── pagerank.py # 幂迭代版 PageRank │ ├── adaboost.py # 决策 stump 作基学习器 │ ├── knn.py # K 近邻 │ └── naive_bayes.py # 多项式朴素贝叶斯 ├── utils/ │ ├── loader.py # CSV 读取统一返回 X、y │ ├── preprocess.py # 划分、标准化、缺失值 │ └── metrics.py # 准确率、混淆矩阵、F1 └── run_all.py # 批量跑分类器、打印对比结果这个结构的好处是职责单一algorithms/里每个文件只关心算法本身数据读取和指标计算全在utils/里改一个算法不会牵动别的文件。坏处也很明显每个脚本既要能当模块导入又要能当命令行工具跑入口逻辑写在文件末尾新手容易在导入路径上栽跟头后面避坑章节会专门讲。2.1 目录与环境从 requirements 到入口脚本依赖只有三行这是它比很多“教学项目”友好的地方不需要 CUDA不需要编译纯 Python numpy 就能把十个算法全部跑起来# requirements.txt numpy1.21 # 矩阵运算和向量化几乎所有算法都依赖 pandas1.3 # 只用于读 CSV算法内部不碰 DataFrame matplotlib3.5 # 画收敛曲线和聚类散点图可选安装方式就是常规的两条命令建议单独建虚拟环境避免和系统里已有的 numpy 版本打架python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate pip install -r requirements.txt装完之后先别急着跑算法用一条命令确认环境通了python -c import numpy, pandas; print(numpy.__version__, pandas.__version__)这里有个容易被忽略的点源码里用了np.random.default_rng(seed)这种新式随机数接口它要求 numpy 1.17 以上。如果你机器上还留着某个老项目的 numpy 1.16default_rng会直接抛AttributeError表现出来就是“所有带 seed 参数的脚本一运行就崩”很容易误判成代码有 bug。requirements 里写1.21就是把这个坑提前堵住照着装就行。2.2 数据流与运行方式一份 CSV 从加载到出结果整套代码的数据流非常直白CSV →loader.py返回 numpy 数组 → 算法fit→predict→metrics算指标。先说加载层这是所有脚本的共同入口看懂这二十行后面每个算法怎么拿数据就都清楚了# utils/loader.py import pandas as pd def load_csv(path, label_colNone, header0): 读取 CSV返回 (X, y) 或纯特征矩阵。 参数: path: 文件路径 label_col: 标签列名为 None 时按无监督任务处理只返回 X header: 表头行号默认第 0 行 df pd.read_csv(path, headerheader) if label_col is None: # 聚类、关联规则、PageRank 这类任务不需要标签 return df.values y df[label_col].values X df.drop(columns[label_col]).values return X, ylabel_colNone这个设计是为了让 K-Means、Apriori、PageRank 复用同一个读取函数——这三类任务的数据形态完全不同数值矩阵、交易列表、边表但都走同一个入口调用方不用记“哪个算法该用哪个读取函数”。返回的X是numpy.ndarray不是 DataFrame这是有意为之算法内部全是矩阵运算DataFrame 的索引对齐在逐行循环里反而添乱。再看统一入口run_all.py的核心片段它把“划分训练测试集 → 标准化 → 训练 → 预测 → 算准确率”这一串固定动作串起来# run_all.py节选 from algorithms.knn import KNN from utils.loader import load_csv from utils.preprocess import train_test_split, standardize from utils.metrics import accuracy X, y load_csv(data/iris.csv, label_colspecies) # 固定 seed保证每次划分一致 X_tr, X_te, y_tr, y_te train_test_split(X, y, test_size0.2, seed42) # 标准化参数只能从训练集算再套到测试集上 X_tr, X_te standardize(X_tr, X_te) clf KNN(k5).fit(X_tr, y_tr) pred clf.predict(X_te) print(accuracy:, accuracy(y_te, pred))这段代码里最值得抄走的是standardize的调用方式训练集和测试集一起传进去但均值和方差只从训练集统计。很多新手图省事先对整个数据集做标准化再划分测试集的分布信息就泄漏进训练过程了准确率虚高几个点模型上线后原形毕露。这份源码在preprocess.py里把这件事做对了返回的是两个已经变换好的矩阵调用方想写错都难。提示如果你把data/iris.csv换成了自己的数据列名要和label_col对上列名带空格或中文时pandas 读进来不会自动改drop(columns[...])会直接报 KeyError先print(df.columns)确认一遍最稳。3. 核心算法拆解从 C4.5 到 AdaBoost 的关键实现十个算法全讲一遍不现实我挑四组最有代表性的决策树家族C4.5 CART、迭代优化家族K-Means EM、频繁项集与概率模型Apriori 朴素贝叶斯、以及剩下的 SVM、KNN、AdaBoost、PageRank 的实现取舍。每组都贴出能直接跑的核心代码重点讲“为什么这么写”而不是“这行是什么意思”。3.1 决策树 C4.5 与 CART信息增益率、基尼指数落到代码C4.5 最容易写错的地方是选分裂属性时用了信息增益而不是增益率。信息增益天生偏爱取值多的特征——拿“学号”当特征每个学号一个分支增益直接拉满但模型毫无泛化能力。源码里用info_gain_ratio把固有值除回去就是在压这个偏向# algorithms/c45.py核心片段 import numpy as np def entropy(y): 信息熵log 底数取 2单位是 bit。 _, counts np.unique(y, return_countsTrue) p counts / counts.sum() return -np.sum(p * np.log2(p 1e-12)) # 加极小值防止 log(0) def info_gain_ratio(X, y, feature_idx): C4.5 的分裂准则信息增益 / 固有值。 base entropy(y) values np.unique(X[:, feature_idx]) cond_entropy, split_info 0.0, 0.0 for v in values: mask X[:, feature_idx] v w mask.sum() / len(y) # 分支样本占比 cond_entropy w * entropy(y[mask]) split_info - w * np.log2(w 1e-12) # 固有值 IV gain base - cond_entropy return gain / (split_info 1e-12) # 增益率三个参数的含义分别是X是特征矩阵y是标签feature_idx是待评估的特征下标。1e-12出现两次一次防log(0)一次防“只有一个取值”时固有值为 0 导致除零。返回值越大说明这个特征越适合做当前节点的分裂条件。连续特征不能按“等于某个值”分支源码里用二分法找切分点把每一对相邻取值的中点都试一遍取增益最大的那个def best_split_continuous(X, y, feature_idx): 连续特征二分排序后枚举相邻中点取增益最大的切分。 order np.argsort(X[:, feature_idx]) xs, ys X[order, feature_idx], y[order] best_gain, best_thr -1.0, None for i in range(1, len(xs)): if xs[i] xs[i - 1]: continue # 相邻值相同切了等于没切 thr (xs[i] xs[i - 1]) / 2.0 left, right ys[:i], ys[i:] gain (entropy(ys) - len(left) / len(ys) * entropy(left) - len(right) / len(ys) * entropy(right)) if gain best_gain: best_gain, best_thr gain, thr return best_thr, best_gainif xs[i] xs[i-1]: continue这行看着不起眼却是不写就出事的典型重复值会产生thr等于原值的切分递归下去左右子集和父集一模一样深度无限增长最后RecursionError。CART 那边的思路不同它不算熵算基尼指数而且强制二叉树实现上更简单# algorithms/cart.py核心片段 def gini(y): 基尼指数1 - sum(p_k^2)越小越纯。 _, counts np.unique(y, return_countsTrue) p counts / counts.sum() return 1.0 - np.sum(p ** 2)分类树用基尼回归树把准则换成平方误差其余分裂逻辑共用一套框架。对比一下两个准则的实际差别熵要对数运算慢一点基尼是纯平方快一点在大多数数据集上两者选出的树结构非常接近所以 sklearn 默认用基尼。这份源码把两者都实现了正好可以拿同一个数据集跑一遍看看到底差多少个节点——我试过在 iris 上两棵树深度一样只是某个中间节点的分裂特征不同。3.2 K-Means 与 EM迭代收敛判定和初始化K-Means 的代码谁都能写出来难的是两件事初始化别太随机、什么时候停。源码用 k-means 解决第一件用中心位移阈值解决第二件# algorithms/kmeans.py import numpy as np class KMeans: def __init__(self, k3, max_iter300, tol1e-4, seed42): self.k k # 簇数量必须提前给定 self.max_iter max_iter # 迭代上限防止死循环 self.tol tol # 中心位移平方和阈值小于它就认为收敛 self.seed seed # 固定随机源保证结果可复现 def fit(self, X): rng np.random.default_rng(self.seed) # k-means第一个中心随机后续中心按距离平方的概率抽取 centers [X[rng.integers(len(X))]] for _ in range(1, self.k): d2 np.min([np.sum((X - c) ** 2, axis1) for c in centers], axis0) probs d2 / d2.sum() centers.append(X[rng.choice(len(X), pprobs)]) self.centers np.array(centers) for _ in range(self.max_iter): labels self._assign(X) # 空簇时保留原中心避免 mean 出 NaN new_centers np.array([ X[labels i].mean(axis0) if np.any(labels i) else self.centers[i] for i in range(self.k) ]) shift np.sum((new_centers - self.centers) ** 2) self.centers new_centers if shift self.tol: # 中心几乎不动了提前退出 break self.labels_ labels return self def _assign(self, X): # 一次算完 n×k 的距离矩阵避免 Python 层双循环 d np.linalg.norm(X[:, None, :] - self.centers[None, :, :], axis2) return d.argmin(axis1)tol1e-4这个阈值调大能加速收敛但结果粗调小会多迭代几轮。真正麻烦的是空簇——某个中心点周围一个样本都没分到mean会算出nan然后整个矩阵污染后面全是nan。源码里的if np.any(labels i)就是在兜这个底保留旧中心继续跑。这个细节在教科书里基本不提但在真实数据上尤其是 k 取大了出现的概率不低。EM 用于高斯混合模型结构和 K-Means 很像都是“E 步算归属、M 步更新参数”区别是归属从硬标签变成了软概率# algorithms/em_gmm.pyE 步与 M 步 def e_step(X, weights, means, covs): 计算每个样本属于每个高斯分量的后验概率。 n, d X.shape k len(weights) resp np.zeros((n, k)) for j in range(k): diff X - means[j] inv np.linalg.inv(covs[j]) det np.linalg.det(covs[j]) coef 1.0 / np.sqrt((2 * np.pi) ** d * det) resp[:, j] weights[j] * coef * np.exp(-0.5 * np.sum(diff inv * diff, axis1)) resp / resp.sum(axis1, keepdimsTrue) 1e-12 # 归一化成概率 return resp def m_step(X, resp): 用后验概率加权重估权重、均值、协方差。 n, d X.shape nk resp.sum(axis0) # 每个分量的有效样本数 weights nk / n means (resp.T X) / nk[:, None] covs [] for j in range(len(weights)): diff X - means[j] covs.append((resp[:, j][:, None] * diff).T diff / nk[j]) return weights, means, np.array(covs)收敛判定用的是对数似然不是中心位移每轮算一次log_likelihood如果比上一轮增长小于1e-6就停。这里有个血泪经验——协方差矩阵在维度高、样本少的时候容易奇异np.linalg.inv直接抛LinAlgError稳妥做法是给对角线加一个很小的正则项比如 1e-6 * np.eye(d)源码里留了注释提示这一点但默认没开。3.3 Apriori 与朴素贝叶斯频繁项集和概率表Apriori 的核心是“逐层剪枝”先找频繁 1 项集再由它们组合出候选 2 项集扫描交易表数支持度不达标的直接丢如此往复。写起来不难难在别写成三重循环的灾难# algorithms/apriori.py from itertools import combinations def apriori(transactions, min_support0.3): transactions 是 list[set]min_support 是相对支持度0~1。 n len(transactions) items set(i for t in transactions for i in t) freq {} # 第一层频繁 1 项集 for item in items: cnt sum(1 for t in transactions if item in t) if cnt / n min_support: freq[frozenset([item])] cnt / n k 2 while freq: prev [s for s in freq if len(s) k - 1] candidates set() # 连接步两个 k-1 项集并起来长度正好是 k 才留着 for a, b in combinations(prev, 2): union a | b if len(union) k: candidates.add(union) new_freq {} for cand in candidates: cnt sum(1 for t in transactions if cand t) # 子集判断 if cnt / n min_support: new_freq[cand] cnt / n freq.update(new_freq) if not new_freq: # 本轮没有新的频繁项集提前结束 break k 1 return freqmin_support0.3意味着一个项集至少出现在 30% 的交易里才算频繁。这个参数是整个算法唯一的旋钮调低会组合爆炸调高会漏掉长尾规则。置信度在频繁项集基础上单独算分母是前件的支持度def confidence(freq, rule_left, rule_right): confidence(A-B) support(A∪B) / support(A) union rule_left | rule_right if union not in freq or rule_left not in freq: return 0.0 return freq[union] / freq[rule_left]朴素贝叶斯这边关键是两个工程处理拉普拉斯平滑和取对数。不平滑的话测试集里出现一个训练集没见过特征值整个类的概率直接归零不取对数的话几百个特征连乘下来必定下溢成 0# algorithms/naive_bayes.py import numpy as np class MultinomialNB: def __init__(self, alpha1.0): self.alpha alpha # 平滑系数1.0 是标准拉普拉斯平滑 def fit(self, X, y): self.classes np.unique(y) self.log_prior, self.log_likelihood [], [] for c in self.classes: Xc X[y c] self.log_prior.append(np.log(len(Xc) / len(X))) # 分子加 alpha分母加 alpha * 特征数保证概率和仍为 1 self.log_likelihood.append( np.log((Xc.sum(axis0) self.alpha) / (Xc.sum() self.alpha * X.shape[1])) ) self.log_prior np.array(self.log_prior) self.log_likelihood np.array(self.log_likelihood) return self def predict(self, X): # 对数空间相加等价于概率连乘但不会下溢 scores X self.log_likelihood.T self.log_prior return self.classes[scores.argmax(axis1)]alpha从 1.0 往下调0.5、0.1会让模型更“自信”在特征维度高的文本分类里经常能涨一两个点往上调则更保守。这个参数没有普适最优值交叉验证挑一个就行。3.4 SVM、KNN、AdaBoost、PageRank 的实现取舍这四个算法源码都做了明显简化目的是“能跑通、能看懂”不是“能打比赛”。先看 KNN它没有训练过程fit只是把数据存下来预测时算距离取众数# algorithms/knn.py import numpy as np class KNN: def __init__(self, k5): self.k k def fit(self, X, y): self.X, self.y X, y return self def predict(self, X): out [] for x in X: d np.linalg.norm(self.X - x, axis1) # 到所有训练样本的距离 idx np.argsort(d)[: self.k] # 最近的 k 个 labels, counts np.unique(self.y[idx], return_countsTrue) out.append(labels[counts.argmax()]) # 多数表决 return np.array(out)k在二分类里建议取奇数避免平票多分类里平票没法完全避免源码的做法是argmax取第一个等于隐式给了个随机打破规则。这个实现是逐样本循环的数据量上万就明显慢后面二次开发章节会讲怎么向量化。SVM 用的是简化 SMO保留了完整的 alpha 更新和偏置计算只是去掉了核函数缓存和启发式选择# algorithms/svm.py简化版 SMO 训练片段 import numpy as np def smo_train(X, y, C1.0, tol1e-3, max_passes5): C 是惩罚系数tol 是 KKT 条件容忍度max_passes 控制外层轮数。 n X.shape[0] alpha, b np.zeros(n), 0.0 passes 0 while passes max_passes: changed 0 for i in range(n): Ei np.sum(alpha * y * (X X[i])) b - y[i] # 违反 KKT 条件才更新这一对 alpha if (y[i] * Ei -tol and alpha[i] C) or (y[i] * Ei tol and alpha[i] 0): j np.random.randint(0, n - 1) j j if j i else j 1 Ej np.sum(alpha * y * (X X[j])) b - y[j] ai_old, aj_old alpha[i], alpha[j] # 根据标签是否相同算 alpha_j 的上下界 if y[i] ! y[j]: L, H max(0, aj_old - ai_old), min(C, C aj_old - ai_old) else: L, H max(0, ai_old aj_old - C), min(C, ai_old aj_old) if L H: continue eta 2 * X[i] X[j] - X[i] X[i] - X[j] X[j] if eta 0: continue alpha[j] aj_old - y[j] * (Ei - Ej) / eta alpha[j] min(H, max(L, alpha[j])) if abs(alpha[j] - aj_old) 1e-5: continue alpha[i] ai_old y[i] * y[j] * (aj_old - alpha[j]) b1 b - Ei - y[i] * (alpha[i] - ai_old) * X[i] X[i] \ - y[j] * (alpha[j] - aj_old) * X[i] X[j] b2 b - Ej - y[i] * (alpha[i] - ai_old) * X[i] X[j] \ - y[j] * (alpha[j] - aj_old) * X[j] X[j] b (b1 b2) / 2 if 0 alpha[i] C else (b1 if 0 alpha[j] C else b2) changed 1 passes passes 1 if changed 0 else 0 return alpha, bC越大越不允许错分越容易过拟合tol越小收敛越慢但解更精确。eta 0那个分支一定要留跳过它会让 alpha 更新方向反向训练直接发散——这是简化 SMO 最常见的翻车点。AdaBoost 的骨架就是“调权重 加权投票”基学习器用的是决策 stump# algorithms/adaboost.py训练主循环 def adaboost(X, y, n_estimators10): n len(y) w np.full(n, 1 / n) # 初始样本权重均等 models [] for _ in range(n_estimators): stump DecisionStump().fit(X, y, sample_weightw) pred stump.predict(X) err np.sum(w * (pred ! y)) err max(err, 1e-10) # 防止完美分类时除零 alpha 0.5 * np.log((1 - err) / err) # 基学习器的投票权重 w w * np.exp(-alpha * y * pred) # 分错的样本权重变大 w / w.sum() models.append((stump, alpha)) return models这里y必须是 ±1不是 0/1否则np.exp(-alpha * y * pred)的符号逻辑全乱。基学习器要求错误率略低于 0.5等于 0.5 时alpha变成 0等于完全没学到东西。PageRank 是十个算法里实现最短的核心就是一个幂迭代# algorithms/pagerank.py import numpy as np def pagerank(edges, n_nodes, d0.85, tol1e-8, max_iter100): edges 是 [(src, dst), ...]d 是阻尼系数。 M np.zeros((n_nodes, n_nodes)) for src, dst in edges: M[dst, src] 1.0 # 列对应源节点行对应目标节点 col_sum M.sum(axis0) col_sum[col_sum 0] 1 # 悬挂节点没有出边均分即可 M M / col_sum # 列归一化成转移概率 r np.full(n_nodes, 1 / n_nodes) for _ in range(max_iter): new_r (1 - d) / n_nodes d * M r # 随机跳转项 链接传递项 if np.abs(new_r - r).sum() tol: break r new_r return rd0.85是经验值表示 85% 概率沿链接走、15% 概率随机跳。col_sum[col_sum 0] 1处理的是悬挂节点不减这一行的话归一化会除零整个矩阵变nan迭代几轮后就全是nan。4. 跑通与验证用自带数据跑一遍结果怎么读代码看懂了不等于跑得通。这一章按“单个算法 → 批量对比”的顺序走一遍每一步都给命令和预期输出方便你对照着排查。4.1 单算法脚本运行与输出解读每个算法文件的末尾都挂了if __name__ __main__的入口用 argparse 收参数。以 K-Means 为例# algorithms/kmeans.py 末尾的入口 if __name__ __main__: import argparse from utils.loader import load_csv parser argparse.ArgumentParser() parser.add_argument(--data, requiredTrue, helpCSV 路径) parser.add_argument(--k, typeint, default3) parser.add_argument(--seed, typeint, default42) args parser.parse_args() X load_csv(args.data) # 无标签任务只取特征矩阵 model KMeans(kargs.k, seedargs.seed).fit(X) print(centers:\n, model.centers) print(label counts:, np.bincount(model.labels_))运行的时候必须在项目根目录下用模块方式启动python -m algorithms.kmeans --data data/iris.csv --k 3 --seed 42预期输出是三行中心点坐标加一行每个簇的样本数三个数加起来应该等于 150。如果加起来不等于样本总数八成是空簇保护逻辑被触发了把k调小到 2 再试一次能跑对就说明是初始化的问题。换成分类算法命令行参数会多一个--labelpython -m algorithms.knn --data data/iris.csv --label species --k 5输出是一行准确率和一张混淆矩阵。iris 上 KNN 的准确率正常在 0.930.97 之间波动低于 0.9 就要怀疑是不是标准化漏了或者标签列读错了。这里有个小技巧先跑python -c from utils.loader import load_csv; X, y load_csv(data/iris.csv, label_colspecies); print(X.shape, y[:5])确认形状是(150, 4)、标签前五个对得上再跑算法能省掉一半“结果不对”的排查时间。4.2 指标与对比脚本怎么判断跑对了单个准确率说明不了太多问题尤其是类别不均衡的时候。utils/metrics.py里给了混淆矩阵和逐类 F1手写一遍比调 sklearn 更能看清指标怎么来的# utils/metrics.py import numpy as np def accuracy(y_true, y_pred): return float(np.mean(y_true y_pred)) def confusion_matrix(y_true, y_pred, labelsNone): 返回 (矩阵, 标签顺序)矩阵第 i 行第 j 列 真实 i 被预测成 j 的数量。 labels np.unique(y_true) if labels is None else labels idx {c: i for i, c in enumerate(labels)} cm np.zeros((len(labels), len(labels)), dtypeint) for t, p in zip(y_true, y_pred): cm[idx[t], idx[p]] 1 return cm, labels def f1_per_class(cm): tp np.diag(cm) fp cm.sum(axis0) - tp # 预测成该类但实际不是 fn cm.sum(axis1) - tp # 实际是该类但没预测出来 precision tp / (tp fp 1e-12) recall tp / (tp fn 1e-12) f1 2 * precision * recall / (precision recall 1e-12) return precision, recall, f11e-12是为了兜住“某一类一次都没被预测”的情况不加的话会出RuntimeWarning: invalid value encountered in divide结果里混进nan后面排序比较全乱套。批量对比用run_all.py把四个分类器塞进同一个循环保证数据划分和预处理完全一致# run_all.py节选 from algorithms.knn import KNN from algorithms.naive_bayes import MultinomialNB from algorithms.cart import CART from algorithms.adaboost import AdaBoostClassifier from utils.metrics import accuracy from utils.preprocess import train_test_split results {} for name, model in [ (KNN, KNN(k5)), (NB, MultinomialNB(alpha1.0)), (CART, CART(max_depth p a hrefhttps://download.csdn.net/download/loadbalancing/11215574 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑