理解你的数据:机器学习中EDA数据探索完整实战指南
这次我们来看一个很容易被跳过、却直接决定建模上限的环节理解你的数据。在 CampusX 的 100 天机器学习计划里第 19 天专门安排了这节课。名字听起来很朴素但放到整个学习路线里它的位置非常关键——你后面所有特征工程、模型选择、超参数调整都是在“理解数据”的前提下做的。数据没吃透模型跑得再快结果也很难落地。这一课的实操内容可以概括为一句话使用 Python 生态里的 Pandas、Matplotlib、Seaborn对一份结构化数据集做完整的探索性数据分析EDA把数据形态、统计摘要、缺失值、分布、相关性和异常点全部过一遍。今天的文章会给你一套可以直接复制到本地运行的代码流程适合三类读者刚入门机器学习、正在跟百天计划学习的同学有编程基础但建模经验不足、经常在数据预处理阶段卡住的人以及想把数据探索流程标准化减少无效返工的开发者。先说明一点机器学习里的“理解你的数据”不是让你把数据集从头到尾读一遍而是带着建模目标去回答几个具体问题数据长什么样、每一列是什么类型、数据是否完整、数值分布在什么区间、哪些特征和预测目标相关、有没有异常样本。把这些问题搞清楚后面做特征工程和模型评估时判断依据才会扎实。1. 核心学习目标与前置知识速览“理解你的数据”并不是一门独立的工具课而是一套数据探索方法论。下面这张表把这一课的核心目标、依赖工具和主要产出整理成了速览方便你在动手前建立全局认知。能力项说明核心目标通过探索性数据分析EDA全面掌握数据集的 5 个维度结构、类型、缺失、分布、相关性核心工具Python、Pandas、NumPy、Matplotlib、Seaborn、Jupyter Notebook前置知识Python 基础语法、Pandas 的 DataFrame 基本操作、简单的绘图经验主要产出物数据概览笔记、缺失值报告、可视化图表集、建模前数据质量检查清单学习周期按 100 天计划的节奏1 天可以完成基础知识建议额外花 1 天做练习巩固适合场景初次接触一份新数据集、准备做特征工程前的数据摸底、定期数据质量审计不适合场景已经完成数据预处理并进入模型训练阶段时不建议从头再探索一遍从学习路径看这一课处于“数据获取”和“特征工程”之间是典型的承上启下位置。很多人跳过了这一步直接拿着原始数据去训练模型通常会在下面几个地方翻车分类特征没有编码、缺失值被当作有效值参与训练、数值列被默认解析成字符串、训练集和测试集分布不一致却没有被发现。2. 为什么“理解你的数据”是机器学习的第一步机器学习流程通常被形容成一条流水线数据采集、数据清洗、特征工程、模型训练、模型评估、上线部署。大多数新手会把注意力放在模型训练上因为那一步的反馈最直观损失下降、准确率上升。但真正决定模型上限的往往是前面的数据环节有经验的工程师通常会说一句类似的话模型的上限由数据决定模型只是在逼近这个上限。“理解你的数据”在这一刻的作用是给你提供一张建模前的信息全景图。只有当你清楚每一列的业务含义、缺失比例、数值分布和相互之间的关系你才知道该做哪种填充策略、该选哪些特征进入模型、归一化是否必要。以缺失值处理为例如果一列数据的缺失率达到 60% 以上盲目用均值填充会直接污染分布如果是高基数类别特征填充“unknown”可能比统计填充更安全。这些判断依赖你对数据的理解而不是代码能力。从学习节奏来看100 天计划把“理解你的数据”放在第 19 天也侧面说明了这个环节不适合一开始就用大量理论堆砌而是应该在掌握 Python 基础后立刻动手练习。第一次拿到一份新数据集时先跑一遍描述性统计再进行可视化最后形成一份数据探索笔记这套流程比死记硬背算法公式有用得多。3. 环境准备搭建一个数据探索环境在开始具体的数据探索之前需要先准备一个干净、可复现的 Python 环境。这里推荐使用虚拟环境隔离项目依赖避免多个项目之间的包版本冲突。首先是基础环境检查。建议使用 Python 3.9 及以上版本可以通过下面的命令确认当前环境python --version如果还没有安装 Python可以从 Python 官网下载对应系统的安装包安装时勾选“Add Python to PATH”。接下来创建项目目录和虚拟环境mkdir ml_day19_eda cd ml_day19_eda python -m venv ml_data_env激活虚拟环境。Windows 系统执行ml_data_env\Scripts\activatemacOS 或 Linux 系统执行source ml_data_env/bin/activate激活后终端提示符前面会出现ml_data_env说明虚拟环境已生效。然后安装本次课程需要的核心依赖库pip install pandas numpy matplotlib seaborn jupyter missingno安装完成后建议启动 Jupyter Notebook 作为数据探索的交互环境jupyter notebook如果你更习惯 VS Code 或 PyCharm也可以直接打开项目目录并选择ml_data_env作为 Python 解释器。环境准备好之后还需要准备一份测试数据集。“理解你的数据”需要真实的样本才能看到效果这里推荐使用 Seaborn 自带的数据集比如泰坦尼克号乘客数据或鸢尾花数据。泰坦尼克号数据集包含数值特征、类别特征和缺失值非常适合用来演示数据探索的核心操作。加载方式如下import seaborn as sns titanic sns.load_dataset(titanic) print(titanic.head())load_dataset需要联网下载数据。如果离线环境无法下载可以去公开数据网站下载泰坦尼克号数据集的 CSV 文件放到项目根目录然后用pd.read_csv(titanic.csv)读入即可。验证环境的最终标准很简单titanic.head()能正常输出一个 5 行以上的表格说明环境搭建成功。4. 理解你的数据五个关键维度一份结构化数据集通常以表格形式存在行代表样本列代表特征。理解数据本质上就是按下面五个维度拆开审视。4.1 数据形态与结构拿到一份数据的第一步是看它整体长什么样。用 Pandas 读取数据后先确认行数和列数、每列的名称和数据类型。import pandas as pd df pd.read_csv(titanic.csv) print(数据集形状:, df.shape) print(列名:, df.columns.tolist()) print(数据前5行:) print(df.head())shape返回(行数, 列数)让你快速知道这是一份大数据集还是小数据集。head()用于查看前 5 行确认数据内容是否符合预期。dtypes可以检查每一列的类型比如整数int64、浮点数float64、字符串object等。print(df.dtypes)这一步最常见的发现是类型识别错误比如本该是数值型的列被识别成object或者日期列被读成字符串。早期发现这类问题能避免后面建模时反复报错。4.2 统计摘要Pandas 的describe()是一个非常高效的工具一次调用就能得到数值型特征的五数概括最小值、第一四分位数、中位数、第三四分位数、最大值以及均值和标准差。print(df.describe())如果数据集里有类别特征可以加上includeO查看类别列的统计信息print(df.describe(includeO))输出内容包括非空值数量count、唯一值数量unique、出现次数最多的类别top和频率freq。这一步能快速暴露两类问题类别特征的分布是否极端不平衡数值特征的量纲是否差异过大。4.3 缺失值分析缺失值是数据探索中最常见的敌人。理解缺失情况包括每个特征缺失了多少样本、缺失比例有多高、缺失值之间存在什么关系。missing_count df.isnull().sum() missing_ratio df.isnull().mean() missing_info pd.DataFrame({缺失数量: missing_count, 缺失比例: missing_ratio}) print(missing_info[missing_info[缺失数量] 0])输出得到的结果能直观说明哪些列需要处理。缺失比例低于 1%可以选择删除这些行也可以做简单填充缺失比例高且有业务含义则需要谨慎选择填充策略。可视化缺失值可以使用 missingno 库import missingno as msno msno.matrix(df)矩阵图中黑色代表非空、白色代表缺失清晰展示缺失值在样本间的分布模式。4.4 唯一值与类别分布对于类别特征唯一值的数量决定了后续是否要做编码以及编码后的维度有多大。使用nunique()可以查看每一列的不同取值个数。print(df.nunique())某个特征如果只有 2 个唯一值就是典型的二分类变量如果唯一值数量和样本量接近说明这是一个高基数类别特征。类别特征的分布用value_counts()查看print(df[class].value_counts())这条命令会告诉你每个类别的样本数量据此发现类别不平衡问题。以泰坦尼克号数据为例乘客舱位等级class的样本量并不均匀做分类模型时要考虑不平衡的影响。4.5 重复项与异常值数据录入或合并过程中可能出现重复行导致模型过拟合。检查重复项的标准做法是duplicate_count df.duplicated().sum() print(重复行数:, duplicate_count) if duplicate_count 0: df_clean df.drop_duplicates() print(去重后形状:, df_clean.shape)异常值的判断取决于具体业务。简单的数值范围检查可以看describe()里的最大值是否明显不合理更系统的方法是用四分位距识别离群点也就是 IQR 法则。Q1 df[age].quantile(0.25) Q3 df[age].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR outliers df[(df[age] lower_bound) | (df[age] upper_bound)] print(IQR 异常样本数:, len(outliers))异常值不等于错误值需要结合业务判断是保留、修正还是剔除。这一步的目的不是机械删除而是建立对数据分布范围的感觉。5. EDA 实操从数据加载到可视化验证理解了单个维度之后建议在真实数据集上整合一遍完整流程。下面以泰坦尼克号乘客数据集为例演示从加载到生成结论的完整 EDA。开发前期先确定一个业务问题根据乘客信息预测是否生还。带着这个目标再去做数据探索探索的方向就非常明确哪些特征与生还相关。5.1 加载数据集与数据类型修正import pandas as pd import seaborn as sns df sns.load_dataset(titanic) print(初始形状:, df.shape) print(df.dtypes)运行后可以看到deck是类别特征且缺失较多age有缺失survived是模型预测目标。先修正需要转为类别的列categorical_cols [sex, embarked, class, who, alive, alone] df[categorical_cols] df[categorical_cols].astype(category) print(df.dtypes)类型修正的意义在于后续按类别统计时能保持语义一致避免字符串误判。5.2 单变量分布检查先看数值特征age的分布确定是否接近正态分布、是否有极端值import matplotlib.pyplot as plt import seaborn as sns sns.histplot(df[age].dropna(), bins30, kdeTrue) plt.title(Age Distribution) plt.show()再看类别特征sex与目标变量survived的关系sns.countplot(datadf, xsex, huesurvived) plt.title(Survival by Sex) plt.show()从图里通常能看到一个现象女性乘客的生还数量比男性高。这就是理解数据带来的业务判断后续可以作为特征组合的依据。5.3 多变量交叉分析用分组聚合看数值特征和目标值的关系age_survival df.groupby([survived])[age].describe() print(age_survival)也可以看不同舱位等级的生还率class_survival df.groupby(class)[survived].mean() print(class_survival)这种交叉分析能直接回答业务问题“高等级舱位是否影响了生还率”。得到的结果会指导你后续是把这个特征当连续特征使用还是做分箱处理。5.4 形成书面结论EDA 的最后一步是把以上观察写成一段可复用的结论。一个例子数据包含 891 个样本15 个特征其中deck缺失率超过 70%不建议直接建模使用age缺失率约 20%可考虑中位数填充或模型预测填充sex与survived存在明显相关关系适合作为首要特征fare分布右偏且存在离群点建模前建议做 log1p 变换。这段内容写进运行文档后才算是真正走完了“理解你的数据”的闭环。6. 数据可视化把分布、离群点和相关性画出来在“理解你的数据”这个环节可视化不是目的而是辅助判断的手段。按用途可以分成三类分布类、关系类和综合类。6.1 直方图与 KDE直方图适合观察单个数值特征的分布形态KDE 可以让曲线更平滑sns.histplot(df[fare], bins50, kdeTrue) plt.xlim(0, 300) plt.show()如果发现分布有严重的长尾或右偏后续做标准化时要考虑变换。6.2 箱线图箱线图可以同时展示中位数、四分位数和离群点sns.boxplot(datadf, xclass, yfare) plt.show()该图将不同舱位等级的票价中位数和离群情况放到一起对比信息密度比单纯描述性统计更高。6.3 相关性热力图相关性分析通常使用corr()方法计算两两特征之间的相关系数范围在 -1 到 1 之间。numeric_df df.select_dtypes(include[number]) corr_matrix numeric_df.corr() sns.heatmap(corr_matrix, annotTrue, cmapcoolwarm, fmt.2f) plt.show()热力图展示的关键信息是两个特征高度相关时选进模型可能引入多重共线性目标变量与某特征的正负相关性提示该特征的重要度。但对相关性的解释要保持谨慎相关不等于因果。6.4 综合可视化与定制图表的选择初学者最常见的误区是把所有可视化一次性跑出来生成几十张图却不知道看哪里。更规范的做法是先阅读数据字典明确业务含义再有针对性地选择 3 到 5 张关键图表放进 EDA 报告。输出图表时统一设置图片大小和分辨率便于存档和汇报plt.figure(figsize(10, 8)) sns.heatmap(corr_matrix, annotTrue, cmapcoolwarm, fmt.2f) plt.tight_layout() plt.savefig(correlation_matrix.png, dpi150)设置dpi150能保证插入文档时仍保持清晰度。7. 数据质量检查清单建模前必做的 5 项检查为了让“理解你的数据”不只停留在概念层面这里把实践落地为一份可重复执行的检查清单。每次拿到新数据按下面几步走。7.1 类型是否符合语义检查每一列的dtype。身份证号、日期时间、邮编这类看起来像数字但本质是标识符的列应该识别为字符串类别标签应该显式转换为category布尔列要根据业务决定是否保留为 0/1。assert df[age].dtype in [float64, int64], age 列类型异常7.2 缺失率是否超过阈值计算每列缺失比例。超过 30% 的列通常建议剔除10% 到 30% 之间需要谨慎选择填充策略低于 10% 可以直接剔除或简单填充。missing_ratio df.isnull().mean() high_missing missing_ratio[missing_ratio 0.3] print(高缺失列:, high_missing)7.3 是否有重复行重复行可能来自数据合并时未去重也可能是不同来源的重复采集。用duplicated()检查必要时删除。7.4 类别特征的基数是否过高类别特征的基数过高比如超过总样本量的 30%会给模型带来大量虚假维度需要结合业务判断是否合并或采用更复杂的编码方式。cat_cols df.select_dtypes(include[object, category]).columns for col in cat_cols: unique_count df[col].nunique() print(f{col}: {unique_count} 个唯一值)7.5 目标变量的分布是否平衡对分类任务用value_counts(normalizeTrue)查看目标变量比例。如果某类别占比低于 10%在后续建模时要考虑类别不平衡处理比如重采样或调整类别权重。print(df[survived].value_counts(normalizeTrue))这 5 项检查建议用 Jupyter Notebook 写成一个data_quality_report()函数每次处理新数据时直接复用形成标准化的数据探索流程。8. 常见问题与排查方法下面把数据理解阶段最常见的几类问题整理成表格运行时如果遇到错误可以对照排查。问题现象可能原因排查方式解决方案pd.read_csv读入中文乱码文件编码不是 UTF-8检查文件编码格式pd.read_csv(data.csv, encodinggbk)或指定utf-8数值列被识别成 object数据中包含逗号、百分号或空字符串查看该列唯一值观察特殊字符使用pd.to_numeric(column, errorscoerce)强制转换行数和列数和预期不符分隔符不匹配或文件含有合并单元格用文本编辑器打开文件查看指定sep参数如pd.read_csv(data.tsv, sep\t)缺失值数量和业务记录不一致空字符串不被识别为缺失检查.isnull()后是否还有空格字符串使用df.replace(, pd.NA)后再进行缺失统计描述性统计只能看到数值列类别特征没有纳入describe确认输入参数使用df.describe(includeO)查看类别列统计异常值数量远超预期量纲大且右偏IQR 法则会标记大量尾部样本可视化该特征分布图结合业务决定是否做 log 变换而不是直接删除相关性矩阵过大不方便阅读特征数量太多全部纳入热力图只保留数值特征或维护相关性较高的候选特征按业务筛选特征后再画热力图数据集过大导致内存不足一次性read_csv加载了整个文件尝试分块读取或使用dask用pd.read_csv(..., chunksize10000)分块处理训练时才报缺失值错误EDA 阶段没有记录缺失处理方案回看 EDA 报告检查是否有清洗步骤遗漏将数据预处理脚本与 EDA 结论保持一致可视化图表不出图使用了非交互式后端或未调用show检查运行环境是脚本还是Notebook在脚本末尾加plt.show()Notebook 下确认%matplotlib inline生效9. 最佳实践与后续学习建议数据探索具备很强的“一次性投入、长期受益”属性。这次理解了数据结构后续每次建模阶段都会省下大量调试时间。以下几个建议是基于工程经验的总结。第一把探索过程沉淀成一份文档。不需要写成论文只需要保留每列的业务含义、缺失率结论、类型修正记录和可视化图表。这样团队协作时其他成员也能迅速理解数据背景。第二写一个通用的 EDA 函数。把shape、dtypes、isnull().sum()、describe()、nunique()组合成一个basic_overview(df)函数以后处理任何结构化数据集都能一键调用。这不算过度工程而是对重复劳动的合理抽象。第三理解数据要和业务目标绑定。同样一份数据如果目标是预测价格重点探索对象是价格分布和收入类特征如果目标是分类任务重点则放在目标变量的类别平衡和特征区分度上。先想清楚“我为什么要建模”再决定“我需要理解数据的哪些方面”。第四警惕引入未来数据导致的标签泄漏。理解数据时通常会用全部样本进行统计摘要但在特征工程阶段某些填充策略需要使用训练集而不是全部数据的统计结果。这是从“理解数据”过渡到“构建特征”时需要特别注意的环节。第五按 100 天计划的节奏学完这节后建议立刻找一份新数据集独立完成一次探索。完整路线是加载数据、查看结构、检查类型、统计摘要、缺失值分析、可视化分布、相关性分析、形成书面结论。如果能在 1 小时内完整走完这套流程第 19 天的目标就算达到了。后续还可以继续学习特征工程、数据预处理标准化和模型验证策略。理解数据是起点但不是终点。只要保持“先看数据再写代码”的习惯建模效率和质量都会明显提升。