当前位置: 首页 > 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.jsqmd.com/news/911764/

相关文章:

  • RePKG终极指南:Wallpaper Engine资源提取与转换的完整教程
  • 从零到一:用Agile Controller-Campus搭建一个完整的802.1X有线准入实验环境(含交换机配置)
  • 文档理解技术演进:从OCR到多模态大模型的智能解析实践
  • ADuC812 A/D转换器编程与配置详解
  • ncmdump完全指南:3分钟掌握网易云音乐NCM文件解密技巧
  • Kiro Agent Hooks:文件一保存,AI 自动帮你跑测试、补文档、查规范
  • AutoCAD字体缺失终极解决方案:如何通过智能插件实现企业级字体自动管理?
  • 告别迷茫!CANoe 11.0保姆级界面导航:从打开官方例程到看懂每个功能区
  • C++ -- 队列std::queue
  • IAR vs Keil:STM32开发环境怎么选?从工程模板搭建看两者差异与迁移要点
  • Meshroom:零基础开启专业3D重建的完整指南
  • LeetCode 补拙笔记 日期:2026.05.29 题目:1559. 二维网格图中探测环
  • 专业级英雄联盟回放解析解决方案:跨版本兼容性深度技术解析
  • 实验20 自动灭火场景实验
  • 海思Hi3518E VPSS配置避坑指南:从GROUP到CHANNEL,手把手搞定视频处理子系统
  • 5分钟快速上手洛雪音乐助手:免费跨平台音乐聚合播放器终极指南
  • 郑州郑东新区家电维修清洗|维小达 专业空调、冰箱、洗衣机、热水器、电视、油烟机、灶具、消毒柜、小家电维修清洗一站式服务 - 维小达科技
  • 四步终极指南:用OpenCore Legacy Patcher让老Mac免费升级最新系统
  • 别让变量名拖后腿!C语言标识符命名规则详解(附ZZULIOJ 1138题实战解析)
  • 量子计算在动态平均场理论中的创新应用
  • 2026 年 Q1 云厂商财报增速亮眼,“卖算力”难撑利润,谁能过渡到“卖不可替代性”?
  • 基于树莓派与CNN的工业缺陷检测系统:从硬件搭建到模型部署全流程
  • 从手机屏幕到摄影打光:搞懂色温与显色性,让你的照片和视频告别‘阴间滤镜’
  • 基于ESP32与FreeRTOS的工业液体定量控制系统设计与实现
  • ESP32驱动CRT电视板与SHARP TFT屏:模拟视频系统改造全解析
  • 一键永久激活Windows和Office:KMS智能激活完整解决方案
  • 基于ESP32的DIY四轴飞行器:从硬件设计到PID控制全解析
  • 从胎儿到AI:用“知道”框架重新理解意识与感知的连续谱
  • StateFlow 与 SharedFlow:Google 为什么要设计两套 Flow?—— 从一次 tryEmit(false) 到 WindowLeaked,彻底理解 Flow 的设计思想
  • 面试官的提问与燕双非的回答:Java 技术栈在电商场景中的应用