ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

机器学习入门必学:数据理解与EDA探索性数据分析实战指南

机器学习入门必学:数据理解与EDA探索性数据分析实战指南 很多初学者拿到一份数据集后的第一反应是赶紧把模型跑起来。这个反应本身没有错但往往也是项目翻车的起点。最近在跟着 CampusX 的 100 天机器学习计划推进第 19 天的主题正是“理解你的数据”。这一阶段不讲算法公式也不碰模型调参只做一件事在动手训练之前先把数据本身读明白。从实际项目看绝大多数第一次做机器学习作业或比赛的人最后悔的不是模型选得不好而是没有在数据阶段发现该处理的缺失值、异常分布和目标变量不平衡。本文会围绕数据理解这件事讲清楚它到底包含哪些步骤为什么值得在建模前认真做一遍然后给出一套可以直接照着写代码的流程用一份经典数据集跑通整个过程。无论你是刚入门机器学习还是已经能跑通一些模型但总感觉效果不稳定这篇文章都值得收藏备用。1. 拿到数据就训练是机器学习入门最大的坑先看一个很常见的场景。很多人在学习机器学习时拿到一份数据集比如泰坦尼克号生存预测、房价预测或者垃圾邮件分类第一件事就是导入 RandomForest、SVM 或者 XGBoost。本地调参折腾了好几天训练集上的准确率看起来不错一提交或者换到新数据上效果立刻打回原形甚至不如一个简单的规则基线。问题往往不在模型本身而在数据理解不足。模型只是机器学习流程中的一环。一个典型的监督学习项目完整链路是数据采集、数据理解、数据清洗、特征工程、模型训练、评估、迭代部署。如果把大量时间花在最后几步而跳过数据理解相当于盖楼时没有认真看地基的地质报告。地基里有空洞、有软土你却在上面选择用什么牌子的水泥选得再好也没有意义。数据理解在机器学习工程里通常被叫做 EDAExploratory Data Analysis探索性数据分析。它的核心不是“画几张图看看”而是系统性地回答这份数据到底长什么样、质量如何、有哪些规律、有哪些坑。CampusX 的 100 天计划把“理解你的数据”单独列为第 19 天的主题也是在提醒学习者数据理解不是可有可无的前奏而是需要有意识、有方法、有产出物的一项硬功夫。这里可以给一个明确的判断对于入门阶段的项目把数据理解做扎实比盲目调参对模型效果的提升更明显。因为数据理解决定了特征工程的方向而特征工程决定了模型能达到的上限。你后面用线性回归、决策树还是神经网络都是在逼近这个上限。上限本身早在你理解数据的那一刻就基本定下来了。2. 理解你的数据到底在理解什么有人会觉得理解数据不就是用 df.head() 看几行、用 df.describe() 看几个统计量吗这只是表面动作。真正的数据理解是在回答一组非常具体的问题。一份数据集通常包含多个字段每个字段背后都有对应的业务含义。理解数据就是要弄清楚这些问题数据长什么样有多少行、多少列、每个字段的含义是什么。每个字段是什么类型数值型、分类型、时间型还是文本型。数据有多完整是否存在缺失值、重复值、异常值占比如何。目标变量长什么样如果是分类问题类别是否均衡如果是回归问题分布是否偏斜。特征之间存在什么关系是否有高度相关的特征是否存在冗余。哪些特征与目标变量相关哪些字段可能真正影响预测结果。这几个问题不是一次性看完而是层层递进。先看整体结构再看单特征分布最后看特征与特征、特征与目标之间的关系。每一层都在为下一步做准备。为什么这一步决定了模型上限最简单的解释是垃圾进垃圾出。如果某个特征缺失了 80%你直接把它喂给模型模型只能学到大量噪声如果某个分类特征有 50 个离散取值但大部分样本落在其中 3 个取值上不做处理会让模型过拟合如果目标变量严重不平衡模型的预测结果会偏向多数类准确率很高但实际没有价值。更重要的是数据理解能帮你避免数据泄漏。比如在做时间序列预测时不小心把未来信息混进了训练集或者在做数据预处理时用全量数据的均值和标准差去做标准化再切分训练集和测试集。这些错误都在数据理解阶段容易被发现一旦到了模型阶段排查成本会高很多。所以数据理解并不是一个“走流程”的步骤它的产出质量直接决定了后续每一步是事半功倍还是事倍功半。3. 环境准备与数据集选择3.1 Python 环境与依赖库做数据理解最常用的语言是 Python核心库是 pandas、numpy、matplotlib、seaborn。pandas 负责表格数据处理numpy 负责数值计算matplotlib 和 seaborn 负责可视化。建议使用 Jupyter Notebook 做交互式探索因为每一步输出都能直接看到方便边分析边记录结论。如果你更习惯 VS Code 或 PyCharm也完全可以只是需要把代码写成脚本运行。创建虚拟环境并安装依赖的命令如下# 建议先创建虚拟环境 python -m venv ml_data_env # Windows 激活 ml_data_env\Scripts\activate # macOS / Linux 激活 # source ml_data_env/bin/activate # 安装依赖 pip install pandas numpy matplotlib seaborn版本不需要刻意追求最新。pandas、seaborn 的 API 在近几个版本里保持稳定本文示例代码只使用通用接口你本地的版本只要不是特别旧都能正常运行。如果是在校学生或公司内网环境离线安装时优先选择 Python 3.8 以上版本并且注意安装顺序先 numpy再 pandas最后 seaborn 和 matplotlib。3.2 数据集说明本文选用的示例数据集是 Titanic泰坦尼克号乘客生存预测它几乎是机器学习入门最经典的数据集之一。选择它有三个原因字段类型丰富同时包含数值型特征和分类型特征存在明显的缺失值可以用来讨论数据质量问题目标变量 survived 是二分类便于观察类别分布和特征关系。获取方式有两种。第一种是直接使用 seaborn 内置的数据集代码简单适合快速演示import seaborn as sns df sns.load_dataset(titanic)第二种是去 Kaggle 下载原始的 train.csv然后用 pandas 读取。seaborn 内置版本做了部分整理字段与 Kaggle 版本略有差异但核心字段基本一致。如果你本机无法访问外网可以直接用本地 csv 文件。下面示例以 seaborn 内置数据集为主如果你使用本地文件只需要把加载方式改成import pandas as pd df pd.read_csv(train.csv)后续所有操作与文件来源无关分析思路完全一致。4. 数据理解流程拆解从结构到目标变量数据理解的核心方法论是由粗到细由整体到局部。不要一开始就钻进某一个字段的细节而是先建立全局视图再逐层深入。下面按步骤拆解。4.1 查看数据形状与字段清单拿到数据后第一件事是确认数据的规模。df.shape 返回 (行数, 列数)行数代表样本量列数代表特征数量。样本量决定了你后续可以选择的模型复杂度特征数量则决定了是否需要做降维或特征筛选。# 查看数据形状和字段 print(数据形状, df.shape) print(字段列表, df.columns.tolist()) # 查看前几行 df.head()这一步输出的价值在于让你知道接下来要面对多大的数据范围。Titanic 数据集大概有 800 到 900 行字段在 10 到 15 个之间。如果是几万行高维表格后续分析策略就要优先考虑效率和下游存储问题比如采样或分布式处理。4.2 识别特征类型用 df.info() 可以一次性查看每个字段的非空数量和数据类型。这一步非常重要因为特征类型直接决定了你后续采用哪套处理方案。df.info()在输出中int64 和 float64 是数值型object 通常代表文本或分类值。比如 Titanic 数据集中age 是 float64survived 和 pclass 被存成了 int64sex 和 embarked 是 object。真实项目里这个输出还可能包含 datetime64 类型代表时间特征以及 category 类型代表已经规整好的分类型特征。这里有个容易踩的坑数值型不一定是真正的数值特征。比如 pclass 用 1、2、3 表示舱位等级它虽然是整数但本质上是序数分类特征。如果你只看类型就把它当成连续特征处理后续可能会做出错误的标准化。相反一些看起来是文本的字段比如身份证号、手机号虽然被解析成 object但显然不应该被当成分类特征。所以理解字段的业务含义比机械地看数据类型更重要。4.3 目标变量对监督学习来说必须先把目标变量弄清楚。目标变量是分类还是回归决定了评价指标的选择和模型的类型。比如 survived 是 0/1 二分类那就不能直接用均方误差去评价模型而要考虑准确率、精确率、召回率和 AUC。# 目标变量分布 print(df[survived].value_counts()) print(df[survived].value_counts(normalizeTrue))value_counts(normalizeTrue) 会输出每个类别的占比。如果发现存在比较严重的类别不平衡比如 0 占 90%、1 占 10%后续建模就需要注意采样策略或评价指标的选择而不是单纯追求准确率。5. 统计描述与缺失值读懂数据质量5.1 数值特征的统计描述df.describe() 是对数值型特征做快速统计的利器。它输出 count、mean、std、min、25%、50%、75%、max 等指标。count 是有效值数量从这里你就能初略看出哪些字段存在缺失mean 和 50% 分位数如果差距较大说明分布可能存在偏斜max 和 min 的极值如果偏离正常范围可能存在异常值。df.describe()举个例子Titanic 数据集中 fare 的 max 如果远大于 75% 分位数说明少数极高价舱位把均值拉高了。这样的分布对线性模型不太友好后续可以考虑取对数或做分箱。age 的 count 如果小于总行数说明年龄字段有缺失你需要决定如何处理。5.2 分类特征的频次分析分类特征的价值在于观察类别分布是否合理。如果某个类别只出现几次甚至一次模型很难从中学到可靠规律需要考虑低频类别合并。for col in [sex, pclass, embarked]: print(df[col].value_counts(dropnaFalse))dropnaFalse 可以同时显式地看到缺失值数量避免遗漏。embarked 可能有两个以上的类别且存在少量缺失sex 是二分类非常干净。频次分析的意义是帮你发现哪些类别可能需要特殊处理比如将低频类别合并为 other。5.3 缺失值分析缺失值是数据理解中最常见的问题。判断缺失值严重程度不能只看个数要看比例。missing df.isnull().sum() missing_percent (missing / len(df) * 100).round(2) print(pd.DataFrame({缺失个数: missing, 缺失比例: missing_percent}))一般来说缺失比例低于 5%可以忽略或简单填充5% 到 20%需要结合业务决定填充策略是均值、中位数、众数还是模型预测超过 20%这个特征是否还有保留价值就要打一个问号。处理缺失值的思路差距很大不是简单 dropna 或 fillna 就能解决的关键是在理解缺失原因之后再决定方案。6. 相关性与分组分析找到关键线索6.1 数值特征相关性矩阵相关性分析是理解特征与目标关系最直接的手段。对数值型特征计算相关系数能得到一个矩阵其中每个值代表两个字段之间的线性相关性。值越接近 1 或 -1相关性越强越接近 0说明线性关系越弱。numeric_cols df.select_dtypes(include[number]).columns corr df[numeric_cols].corr() print(corr[survived].sort_values(ascendingFalse))从输出能看出fare 与 survived 的正相关性可能较高pclass 与 survived 呈负相关。这意味着票价越高、舱位等级数字越小生存概率越大。这些结论可以直接指导后续特征工程比如把 pclass 当作有序分类特征处理或者构造 fare 的分箱特征。需要注意相关系数只衡量线性关系不能捕捉非线性关系。比如某个特征在阈值两侧对目标影响完全不同相关性可能接近 0但实际很有用。所以相关性矩阵是一个起点不是终点。6.2 分组聚合对比分组分析比相关性更灵活它能直接对比不同类别下的目标变量差异。对 Titanic 数据来说最经典的分析是看性别和舱位等级对生存率的影响。print(df.groupby(sex)[survived].mean()) print(df.groupby(pclass)[survived].mean())输出会让你看到不同群体之间的均值差异。如果差异明显说明这个特征对目标有较强的区分能力建模时应该保留并充分利用。如果某个特征分组后的目标均值几乎没有变化那这个特征很可能对预测没有帮助可以考虑丢弃。年龄这种连续特征也可以先分箱再看每个年龄段下的生存率。分组分析的优点是可以结合业务视角提出问题比如“什么类型的人更容易获救”然后用数据回答。这一步走完你基本就对各个特征与目标之间的关系有了一个整体判断。6.3 把发现整理成数据理解报告很多人探索完数据就结束画一堆图但没有任何结论沉淀。这等于白做。数据理解的结果应该整理成一份简短报告内容包括数据概况、质量问题清单、特征观察、下一步处理计划。这份报告可以作为特征工程和数据清洗的输入也可以方便回顾和团队协作。7. 完整代码示例Titanic 数据理解全流程7.1 完整代码下面是一段可以完整运行的 Python 脚本包含前面讲到的核心步骤。代码里的中文注释已经标明每一步在做什么。# 文件路径eda_titanic.py import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 设置图表样式 sns.set_style(whitegrid) plt.rcParams[figure.figsize] (12, 6) # 如果系统安装了中文字体可取消下面注释避免绘图中文乱码 # plt.rcParams[font.sans-serif] [SimHei, Arial Unicode MS] # plt.rcParams[axes.unicode_minus] False # 加载数据集 df sns.load_dataset(titanic) print(数据形状, df.shape) print(字段列表, df.columns.tolist()) # 1. 查看前几行 print(\n前5行数据) print(df.head()) # 2. 信息概览 print(\n数据集信息) df.info() # 3. 数值特征统计描述 print(\n数值特征统计) print(df.describe()) # 4. 缺失值统计 print(\n缺失值统计) print(df.isnull().sum()) print(\n缺失比例) print((df.isnull().sum() / len(df) * 100).round(2)) # 5. 目标变量分布 print(\n目标变量 survived 分布) print(df[survived].value_counts(normalizeTrue).round(4)) # 6. 分类特征频次 for col in [sex, pclass, embarked]: print(f\n分类特征 {col} 频次) print(df[col].value_counts(dropnaFalse)) # 7. 数值特征相关性 numeric_cols df.select_dtypes(include[np.number]).columns corr df[numeric_cols].corr() print(\n各数值特征与 survived 的相关系数) print(corr[survived].sort_values(ascendingFalse)) # 8. 可视化 fig, axes plt.subplots(2, 2, figsize(14, 10)) sns.countplot(xsurvived, datadf, axaxes[0][0]) axes[0][0].set_title(Survived Count) sns.histplot(df[age].dropna(), bins30, kdeTrue, axaxes[0][1]) axes[0][1].set_title(Age Distribution) sns.boxplot(xpclass, yfare, datadf, axaxes[1][0]) axes[1][0].set_title(Fare by Pclass) sns.heatmap(corr, annotTrue, cmapcoolwarm, fmt.2f, axaxes[1][1]) axes[1][1].set_title(Correlation Heatmap) plt.tight_layout() plt.savefig(eda_titanic.png, dpi150) plt.show()7.2 运行结果与验证方法如果代码正常运行你会依次看到表格形式的数据形状、字段列表、前 5 行数据、info 输出、数值统计、缺失值统计、目标变量分布等多个输出块最后会打开一张包含 4 个子图的画布同时在工作目录生成 eda_titanic.png 图片。判断你的数据理解是否有效的标准很简单你是否能在看完输出后用 3 到 5 句话概括这份数据。比如总样本约 891 行age 和 embarked 存在缺失survived 分布基本均衡约 38% 存活sex 与 pclass 与生存率高度相关。如果还不能写出这样的结论说明还有字段没有看透。如果脚本报错先看报错发生在哪一步。最常见的是 sns.load_dataset 联网失败那就在确认网络可用后重启内核或者改用 pd.read_csv 读取本地文件如果图里中文显示成方块把中文字体配置取消注释并确认当前系统确实安装了相应字体。这些内容在下一节会做出排查整理。8. 常见问题与排查思路问题现象可能原因排查方式解决方案pd.read_csv 报 UnicodeDecodeError文件编码不是 utf-8查看文件编码格式用 encodinggbk 或 encodingutf-8 重新读取df.info() 显示某列是 object但实际是数值数据中混入文本或空字符串执行 df[col].unique() 查看取值先清洗文本再 astype(float)缺失值统计为 0但实际有空值空字符串或 NA 文本未被解析用 value_counts(dropnaFalse) 检查取值读取时加 na_values[, NA]sns.load_dataset(titanic) 失败seaborn 未安装或网络不可用检查依赖安装与网络改用本地 CSV 文件相关性矩阵报错或全为 NaN所选列全部是 object没有数值列检查 select_dtypes 的结果先筛选数值列再计算相关性热力图中文乱码matplotlib 默认字体不支持中文查看绘图时是否有字体警告设置中文字体并启动内核对配置生效groupby 聚合结果出现 FutureWarningpandas 版本较新类别型分组行为变化阅读 warning 信息分组列确保不是 category 类型或升级查看官方文档遇到报错时不要急着搜索完整错误先看它发生在加载、统计、计算还是绘图阶段再对照上表定位。数据理解阶段出现的错误大多不复杂但养成读日志、拆解问题步骤的排查习惯对后续建模阶段有很大帮助。9. 最佳实践与后续学习方向数据理解是一项可以刻意练习的技能。这里给出几条在真实项目中比较实用的建议。第一先写数据字典。拿到数据后第一件事不是写代码而是把每个字段的含义、类型、取值范围、可能缺失的原因记录下来。可以放在项目的 README 里也可以单独写一个 markdown 文件。数据字典能帮你避免分析到一半忘记字段含义的尴尬。第二用脚本保存探索过程。不要只在一个临时 notebook 里胡乱尝试。把探索步骤整理成 eda.py 或 eda.ipynb输出内容尽量统一方便复现和分享。你过两周再回来看会发现这份脚本比自己脑子里的记忆可靠得多。第三每个发现都要落到下一步动作。比如发现 age 缺失 20%下一步动作是决定采用中位数填充还是建立模型预测缺失值发现 fare 分布偏斜下一步动作是尝试 log1p 变换。数据理解是否到位可以看你能把多少发现转化成具体的特征工程决策。第四注意数据泄漏。在分组统计、缺失值填充、标准化这些操作中尽量只在训练集上计算统计量再应用到测试集。尤其是比赛项目用全量数据做预处理后切分会导致验证结果虚高这一点必须警惕。关于后续学习方向有几条路径可以继续深入。一是系统学习 pandas 的数据处理操作比如 groupby、apply、merge、pivot_table这些是特征工程的高频工具。二是学习数据可视化进阶包括分布图、箱线图、小提琴图、热力图的使用场景和读图方法。三是逐步接触缺失值插补、异常值检测、特征编码等方法把这些内容从理解数据延伸到数据清洗和特征工程阶段。如果你刚做完今天这一步可以立刻做一件事不要训练任何模型只对任选的一份数据集完成数据理解并写出一份包含结论的报告。这个练习坚持下去会让你后面的建模过程顺畅很多。下一次你可以接着学习数据清洗和特征工程那是在“理解数据”之后最应该掌握的下一环。
返回列表