当前位置: 首页 > news >正文

从‘读心术’到决策树:用Pandas和NumPy复现ID3算法,实战筛选最佳特征

从‘读心术’到决策树:用Pandas和NumPy复现ID3算法,实战筛选最佳特征

想象一下你在玩一个猜谜游戏:朋友心中默想一个动物,你只能通过是非题来缩小范围。"是哺乳动物吗?""生活在水中吗?"每个问题的价值其实不同——有些问题能瞬间排除大量选项,有些则收效甚微。这种"问题价值量化"的直觉,正是决策树算法中信息增益的核心思想。本文将带您用Python基础工具包,从零实现经典的ID3决策树算法,体验机器如何像人类一样通过智能提问完成分类任务。

1. 信息论基础:决策树的数学语言

1.1 信息熵:不确定性的度量标准

1948年,香农将热力学中的熵概念引入信息领域,用数学量化了信息的不确定性。在决策树中,熵衡量了数据集的"混乱程度":

import numpy as np def entropy(labels): """计算信息熵""" _, counts = np.unique(labels, return_counts=True) probabilities = counts / len(labels) return -np.sum(probabilities * np.log2(probabilities))

示例:假设一个袋子有3红球和1蓝球:

print(entropy(['红','红','红','蓝'])) # 输出0.811

当概率分布越均匀(如2红2蓝时熵为1),熵值越大;当全部为同一类别(如4红球)时,熵降为0。

1.2 条件熵与信息增益

条件熵表示已知某个特征取值后的剩余不确定性,而信息增益则是原始熵与条件熵的差值:

概念数学表达现实类比
信息熵H(D)-Σp(x)log₂p(x)初始猜测的难度
条件熵H(DA)Σp(a)H(D
信息增益IGH(D) - H(DA)

用Python实现信息增益计算:

def information_gain(data, feature, target): """计算特征的信息增益""" total_entropy = entropy(data[target]) # 计算加权条件熵 values, counts = np.unique(data[feature], return_counts=True) weighted_entropy = 0 for v, c in zip(values, counts): subset = data[data[feature] == v][target] weighted_entropy += (c/len(data)) * entropy(subset) return total_entropy - weighted_entropy

2. ID3算法实现:从理论到代码

2.1 算法核心流程

ID3算法的本质是一个贪心搜索过程:

  1. 计算当前数据集所有特征的信息增益
  2. 选择增益最大的特征作为划分节点
  3. 对每个特征值创建分支,递归执行上述过程

递归终止条件

  • 所有样本属于同一类别
  • 没有剩余特征可供划分
  • 分支样本数低于阈值

2.2 完整Python实现

使用Pandas处理数据,构建可解释的决策树结构:

import pandas as pd class ID3DecisionTree: def __init__(self, max_depth=None): self.tree = {} self.max_depth = max_depth def fit(self, X, y, depth=0): # 终止条件检查 if len(np.unique(y)) == 1: return y.iloc[0] if len(X.columns) == 0 or (self.max_depth and depth >= self.max_depth): return y.mode()[0] # 选择最佳分裂特征 gains = {col: information_gain(pd.concat([X,y], axis=1), col, y.name) for col in X.columns} best_feature = max(gains, key=gains.get) # 构建子树 tree = {best_feature: {}} for value in X[best_feature].unique(): subset_X = X[X[best_feature] == value].drop(best_feature, axis=1) subset_y = y[X[best_feature] == value] tree[best_feature][value] = self.fit(subset_X, subset_y, depth+1) return tree

2.3 可视化决策过程

通过DataFrame展示特征选择过程(以鸢尾花数据集为例):

特征信息增益分裂优先级
花瓣长度0.9181
花瓣宽度0.8622
萼片长度0.3113
萼片宽度0.1564

这个结果直观显示为什么决策树通常会优先使用花瓣特征进行分类。

3. 实战对比:手动实现 vs Scikit-learn

3.1 泰坦尼克号生存预测

加载并预处理数据:

titanic = pd.read_csv('titanic.csv') features = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch'] X = titanic[features].fillna({'Age': titanic['Age'].median()}) X['Sex'] = X['Sex'].map({'male':0, 'female':1}) y = titanic['Survived']

3.2 手动ID3实现结果

manual_tree = ID3DecisionTree(max_depth=3) manual_tree.fit(X, y)

典型输出结构:

{ "Sex": { 0: { "Pclass": { 1: {"Age": {...}}, 2: 0.17, # 生存率 3: 0.11 } }, 1: { "Pclass": { 1: 0.96, 2: 0.92, 3: 0.50 } } } }

3.3 与Scikit-learn对比

from sklearn.tree import DecisionTreeClassifier sklearn_tree = DecisionTreeClassifier(criterion='entropy', max_depth=3) sklearn_tree.fit(X, y)

对比发现:

  • 相同点:都优先选择性别作为首要分裂特征
  • 差异点
    • Scikit-learn使用CART算法,支持连续值处理
    • 手动ID3只能处理离散特征
    • Scikit-learn实现了剪枝等优化

4. 进阶讨论:ID3的局限与改进

4.1 算法局限性

  • 无法处理连续特征:需要预先离散化
  • 偏向多值特征:倾向于选择取值多的特征
  • 无剪枝机制:容易过拟合

4.2 改进方案对比

算法改进点适用场景
C4.5引入信息增益率特征取值差异大的数据
CART支持回归任务需要统一框架的场景
随机森林集成多棵树+特征随机选择高维数据

4.3 处理连续特征的技巧

通过二分法寻找最佳分割点:

def find_best_split(series, target): unique_values = np.sort(series.unique()) gains = [] for threshold in unique_values[1:-1]: left = target[series <= threshold] right = target[series > threshold] gain = entropy(target) - (len(left)/len(target)*entropy(left) + len(right)/len(target)*entropy(right)) gains.append(gain) return unique_values[np.argmax(gains) + 1]

在真实项目中,手动实现决策树更多是教育意义。但理解这些底层原理,能帮助我们在使用现成库时做出更明智的参数选择,比如:

  • 当特征取值很多时,改用criterion='gini'可能更好
  • 对于类别不平衡数据,调整class_weight参数
  • 监控max_depth防止过拟合
http://www.zskr.cn/news/1423069.html

相关文章:

  • Kiro Agent Hooks:文件一保存,AI 自动帮你跑测试、补文档、查规范
  • 告别迷茫!CANoe 11.0保姆级界面导航:从打开官方例程到看懂每个功能区
  • 实验20 自动灭火场景实验
  • 量子计算在动态平均场理论中的创新应用
  • 2026 年 Q1 云厂商财报增速亮眼,“卖算力”难撑利润,谁能过渡到“卖不可替代性”?
  • 从手机屏幕到摄影打光:搞懂色温与显色性,让你的照片和视频告别‘阴间滤镜’
  • 从胎儿到AI:用“知道”框架重新理解意识与感知的连续谱
  • StateFlow 与 SharedFlow:Google 为什么要设计两套 Flow?—— 从一次 tryEmit(false) 到 WindowLeaked,彻底理解 Flow 的设计思想
  • 基于Arduino与MPU6050的模型火箭智能降落伞释放系统全解析
  • 终极指南:如何免费快速解码QQ音乐加密文件(qmcdump完整教程)
  • 基于ESP32与Node.js的物联网智能时钟:从架构设计到FreeRTOS任务调度
  • 别再手动调坐标了!OpenPnP导入Gerber/坐标文件后,用这3个Mark点搞定全板自动校正
  • Wallpaper Engine下载器:3步轻松获取Steam创意工坊动态壁纸的完整指南
  • 构建安全合规的大规模健康研究平台:FAIR原则与隐私计算实践
  • Aspose.Cells企业级应用实战:从License机制解析到合规批量处理方案设计
  • 零基础入门网页开发:HTML与CSS核心概念与实践指南
  • 构建可信机器学习算法:从可解释性、公平性到鲁棒性的工程实践
  • 告别iOS开发噩梦:如何用Xcode开发者磁盘映像解决版本不匹配问题
  • 从零打造复古智能手表:ESP32-S3与HCMS-2971的硬件开发全记录
  • ADI DSP开发者论坛实战:如何高效搜索SC589问题与获取官方支持(附中文关键词)
  • 手把手教你用Redriver芯片搞定USB4/PCIe Gen4信号衰减问题(附电路设计要点)
  • 学术写作中文献引用的规范与实践:从原理到工具全解析
  • Docker部署RabbitMQ后,你的Spring Boot项目连不上?可能是vhost权限在作祟
  • STM32 USB MSC实战避坑指南:解决W25Q64模拟U盘的速度与格式化问题
  • 如何免费观看Twitch订阅专属内容:终极无限制观看指南
  • 【限时开放】Claude文档生成企业级配置清单(含12个行业模板、8类安全合规校验规则、6套CI/CD集成脚本)
  • 免费在线音频转文字软件推荐:2026保姆级教程一看就会
  • yuzu模拟器完整教程:免费在PC上玩Switch游戏的终极指南
  • 基于Adafruit CPX与3D打印的智能交互直升机模型制作全攻略
  • [特殊字符] 书匠策AI:你的论文“私人门诊“开张了!教育博主实测全流程科普