使用 Pandas 进行数据准备:缺失值、重复数据与分类编码的完整清洗实战(Data-Science-For-Beginners 第 8 课)
使用 Pandas 进行数据准备缺失值、重复数据与分类编码的完整清洗实战Data-Science-For-Beginners 第 8 课【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners原始数据受来源影响往往包含各种不一致之处例如缺失值、错误格式、重复记录等——这类数据通常被归类为脏数据dirty data直接用于分析与建模会显著降低结果质量。本文基于开源教程仓库 Data-Science-For-Beginners 中 2-Working-With-Data/08-data-preparation 一课系统讲解使用 Python 与 Pandas 清洗与转换数据的核心技术探索 DataFrame、处理缺失值、消除重复数据、编码分类特征并给出可复制的代码示例与源码级佐证。读完本文你将掌握一套从脏数据到可用于建模的干净数据集的标准工作流。为什么数据清洗如此重要在数据科学流程中数据清洗不是可有可无的步骤而是决定后续分析成败的基础。其价值主要体现在三个方面易用性与可复用性数据被正确组织与标准化后更容易被检索、使用和分享团队成员之间协作成本更低。一致性数据科学经常需要合并来自多个来源的数据集。只有保证每个单独数据集都遵循统一的标准化规则合并后的数据集才依然有价值否则拼接结果会因口径不一而失真。模型准确率被清洗过的数据能直接提升依赖它的模型的准确率。机器学习模型本身无法处理缺失数据脏数据输入往往导致偏差甚至错误的结果。以本仓库的 表单数据评估作业 为例客户通过一个 小型 HTML 表单 收集用户基础信息并提供了 data/form.csv 记录。从 assignment.ipynb 的预置代码可以看到这份表单数据明显存在问题——birth_month列中January与JAN、january混用Sept与September并存state列同时出现CA、California、Hawaii、NaN。这正是典型的数据准备场景在建模或可视化之前必须先统一格式、补齐缺失值。数据清洗的常见目标与策略课程将清洗目标归纳为以下四类每一类都对应具体的处理手段数据集探索Exploring a dataset数据探索在 4-Data-Science-Lifecycle/15-analyzing 一课中深入讲解能帮助你发现哪些数据需要清洗。通过可视化与采样观察数据分布可以为后续处理建立预期。探索手段包括基础查询、可视化与抽样。格式问题Formatting不同来源的数据在展示形式上常有不一致例如空白字符、日期格式、数据类型等。这会导致数据明明在表中却无法被正确检索或可视化的问题。格式标准往往依赖使用者的领域约定——例如不同国家对日期和数字的表示方式差异很大。重复数据Duplications同一记录出现多次会产生不准确的分析结果通常应予删除。这在合并多个数据集时尤为常见。但也有例外合并后的重复记录中可能携带补充信息需要酌情保留。缺失数据Missing Data缺失值会导致结果偏弱或有偏。解决方案包括重新加载数据、用 Python 代码填充缺失值或者直接删除对应数据。处理策略取决于数据为何缺失、如何缺失。探索 DataFrame快速掌握数据全貌数据加载进 Pandas 后通常是 DataFrame详见前一课 2-Working-With-Data/07-python 中的 DataFrame 章节。当数据集有 60,000 行、400 列时如何快速了解自己面对的是什么Pandas 提供了一系列便捷工具。课程以经典Iris鸢尾花数据集为例展开import pandas as pd from sklearn.datasets import load_iris iris load_iris() iris_df pd.DataFrame(datairis[data], columnsiris[feature_names])加载后得到如下结构前 5 行sepal length (cm)sepal width (cm)petal length (cm)petal width (cm)05.13.51.40.214.93.01.40.224.73.21.30.234.63.11.50.245.03.61.40.2DataFrame.shape 与 DataFrame.columns在 配套 notebook 中作者先通过shape属性确认数据规模——输出(150, 4)即 150 行观测/样本、4 列特征再通过columns属性确认每列的名称。注意shape和columns是属性而非方法调用时不带括号。iris_df.shape # (150, 4) iris_df.columns # Index([sepal length (cm), sepal width (cm), ...], dtypeobject)DataFrame.info()数据摘要info()方法打印 DataFrame 内容的摘要是数据准备的第一步体检iris_df.info()RangeIndex: 150 entries, 0 to 149 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 sepal length (cm) 150 non-null float64 1 sepal width (cm) 150 non-null float64 2 petal length (cm) 150 non-null float64 3 petal width (cm) 150 non-null float64 dtypes: float64(4) memory usage: 4.8 KB从输出可得知Iris 数据集有 150 条记录、4 列、无空值全部以 64 位浮点数存储。Non-Null Count一列尤其关键——它是判断缺失值严重程度的第一个指标。DataFrame.describe()数值统计概览当数据以数值为主时describe()能对每列输出单变量统计摘要——count计数、mean均值、std标准差、min/max极值以及 25%/50%/75% 分位数。以sepal length (cm)为例count 150、mean 5.843、std 0.828、min 4.3、max 7.9。这套统计量能快速暴露异常值例如某列最大值远超合理范围年龄 199 岁这类录入错误就是清洗的切入点。DataFrame.head() 与 DataFrame.tail()首尾行检查head()默认返回前 5 行tail()默认返回后 5 行二者配合可用于观察有序数据集中的极端值iris_df.head() # 前 5 行 iris_df.tail() # 后 5 行索引 145~149结论仅通过查看 DataFrame 的元数据行数、列数、类型、空值统计和首尾几行就能立即建立起对数据规模、形状和内容的直观认识。课程在 notebook 中为head()设置了练习——思考如何通过参数显示超过 5 行答案是iris_df.head(n)查阅文档可运行iris_df.head?。理解缺失值的两种形态NaN 与 None现实数据集几乎都会含缺失值而如何处理缺失值蕴含微妙的权衡会直接影响最终分析与真实世界的结果。Pandas 用两种方式表示缺失NaNNot a Number源自 IEEE 浮点规范的特殊值仅用于表示浮点类型的缺失。其算术行为特殊任何对NaN的运算结果仍是NaNnp.nan 1与np.nan * 0均为nan因此带着NaN做聚合如sum()、min()虽然不报错但结果可能全部为nan。NonePython 原生对象用于表示非浮点数据的缺失。None会把 NumPy/pandas 数组拉回纯 Python 世界包含None的数组会被 upcast 为dtypeobject聚合操作如example1.sum()通常会抛出TypeError: unsupported operand type(s) for : int and NoneType。两者看似语义相同但各有约束。好在 pandas 在设计上把None与NaN视为空值的两种变体在Series/DataFrame的类型统一过程中会自动在二者间切换——可以认为它们是 Pandas 中null的两种风味。处理缺失值的核心方法也因此在命名上保持统一isnull()生成布尔掩码标记缺失值notnull()isnull()的反向操作dropna()返回过滤掉缺失值的数据fillna()返回填充/插补缺失值后的数据副本检测缺失值isnull() 与 notnull()isnull()与notnull()是检测空值的主要工具两者都返回作用于数据的布尔掩码。用一个混合了多种疑似空值的 Series 演示import numpy as np example1 pd.Series([0, np.nan, , None]) example1.isnull()0 False 1 True 2 False 3 True dtype: bool注意输出中的两个反直觉点0虽然是算术意义上的零但它是完全合法的整数Pandas 按正常值处理空字符串虽然常被用来表示空但就 Pandas 而言它仍是字符串对象不算空值。布尔掩码可以直接用作Series或DataFrame的索引如example1[example1.notnull()]便于提取存在或缺失的孤立值对掩码求和example3.isnull().sum()则可统计缺失值总数。结论isnull()与notnull()在 DataFrame 上的输出会同时显示结果和索引这在你排查数据时非常有帮助。删除缺失值dropna() 的完整参数体系对于大规模数据集直接删除缺失值往往比费力修补更明智。Pandas 提供dropna()便捷地移除Series与DataFrame中的空值example1 example1.dropna() # 0 0 # 2 # dtype: object与example1[example1.notnull()]的纯索引不同dropna()是真正把缺失值从数据中移除。DataFrame 是二维结构因此删除方式更丰富。由于数据科学中列通常代表变量、行代表观测绝大多数场景是删行——dropna()的默认行为正是删除含任意空值的所有行example2 pd.DataFrame([[1, np.nan, 7], [2, 5, 8], [np.nan, 6, 9]]) example2.dropna() # 仅保留无缺失值的行索引 1 example2.dropna(axiscolumns) # 删除含空值的列仅剩第 2 列注意到 Pandas 为了容纳NaN把其中两列自动 upcast 成了浮点类型。默认的howany意味着只要行/列中含有任一空值就会被删除若只想删除全部为空的行或列改用howallexample2[3] np.nan # 新增一列全为 NaN 的列thresh参数提供更细粒度的控制——设置某行/列需要拥有的非空值数量下限example2.dropna(axisrows, thresh3) # 0 1 2 3 # 1 2.0 5.0 8 NaN这里第一行和最后一行因只含两个非空值而被删除。要查看dropna的全部参数可在 notebook 单元格中运行example4.dropna?。要点① 删除空值只在数据集足够大时才划算② 某行/列大部分数据缺失时可整行/整列删除③axis决定删行还是删列④how默认any可设为all仅删全空行/列。填充缺失值fillna() 的多种策略当数据量小、或删除损失过大时用有效值填充缺失值往往更合理。Pandas 的fillna()返回一份替换了缺失值的数据副本example3 pd.Series([1, np.nan, 2, None, 3], indexlist(abcde)) example3.fillna(0) # 用常量 0 填充 example3.fillna(methodffill) # 前向填充用上一个有效值填充 example3.fillna(methodbfill) # 后向填充用下一个有效值传播回来前向填充输出[1, 1, 2, 2, 3]后向填充输出[1, 2, 2, 3, 3]。DataFrame 上还可指定填充的axisexample2.fillna(methodffill, axis1) # 0 1 2 3 # 0 1.0 1.0 7.0 7.0 # 1 2.0 5.0 8.0 8.0 # 2 NaN 6.0 9.0 9.0注意当前向填充没有可用的前值时如上表第 2 行第 0 列空值保持原样。fillna的用法可以很灵活——例如用整个 DataFrame 的平均值填充example4.fillna(example4.mean())但此时默认按行方向填充全空列仍可能无法被填上。结合领域知识的填充策略notebook 进一步给出了按数据类型选择填充策略的实战经验分类非数值数据通常用该列的**众数mode**填充。例如 100 个数据点中 90 个为True、8 个为False、2 个缺失则用True填充缺失项。代码路径为先用value_counts()观察分布再fillna(True, inplaceTrue)。数值数据两种常见做法——用**中位数median填充数据有偏、含离群值时中位数对异常值稳健或用均值mean**填充数据已归一化时均值与中位数接近。示例fill_with_mean[0].fillna(np.mean(fill_with_mean[0]), inplaceTrue)与fill_with_median[1].fillna(fill_with_median[1].median(), inplaceTrue)展示了两种操作。结论处理缺失值没有银弹。选择删除还是替换、以及如何替换都应由数据的实际特征决定——练得越多判断越准。处理重复数据duplicated() 与 drop_duplicates()真实数据集在缺失值之外还常含重复数据。Pandas 用duplicated()检测重复返回布尔掩码标记某行是否是之前某行的重复用drop_duplicates()删除重复example4 pd.DataFrame({letters: [A,B] * 2 [B], numbers: [1, 2, 1, 3, 3]}) example4.duplicated() # 0 False # 1 False # 2 True # 3 False # 4 True example4.drop_duplicates() # 返回所有 duplicated 为 False 的行的副本 # letters numbers # 0 A 1 # 1 B 2 # 3 B 3默认情况下两者都考虑所有列也可以只针对部分列判断重复example4.drop_duplicates([letters]) # letters numbers # 0 A 1 # 1 B 2结论删除重复数据几乎是每个数据科学项目的必备步骤——重复数据会改变分析结果带来不准确的信息。编码分类数据让机器学习模型看懂文本机器学习模型只能处理数值无法区分Yes与No但能区分0与1。因此在填充缺失值后还需要把分类数据编码为数值形式。notebook 介绍了两种编码方式。标签编码Label Encoding标签编码把每个类别映射为一个数字。例如乘客舱位[business class, economy class, first class]可转换为[0, 1, 2]。notebook 不依赖 scikit-learn而是直接用字典映射实现class_labels {business class: 0, economy class: 1, first class: 2} label[class] label[class].replace(class_labels)适用场景① 类别数量较多② 类别之间存在有序关系如等级、档次。独热编码One Hot Encoding独热编码为每个类别新增一列每个数据点依据是否属于该类别填入 0 或 1。若有 n 个类别则追加 n 列。同样以舱位为例会生成class_business class、class_economy class、class_first class三列one_hot_data pd.get_dummies(one_hot, columns[class])每列只含 0/1指示该数据点是否属于对应类别。适用场景① 类别数量较少且数据集规模不大② 类别之间没有自然顺序。要点① 编码的目的是把非数值数据转为数值② 标签编码与独热编码各有适用场景按数据集需求选择。真实世界数据质量检查不一致类别、离群值与近似重复除了精确的缺失值与完全重复真实数据往往存在更隐蔽的问题。notebook 的后半部分专门演示了三类进阶质量检查并构建了一个包含 12 条客户记录的脏数据示例dirty_data姓名有重复与多余空格、年龄出现 199 和 -5、国家出现USA/U.S.A/United States/usa等拼写变体。不一致类别值的检测与标准化先检查唯一值与频数分布定位问题再建立映射字典统一口径country_mapping { usa: USA, u.s.a: USA, united states: USA, uk: UK, united kingdom: UK, canada: Canada, mexico: Mexico } dirty_data[country_clean] dirty_data[country].str.lower().map(country_mapping)更复杂的情况可用rapidfuzz库做模糊匹配自动找出相似度超过 70% 的字符串组合notebook 中通过process.extract(country, unique_countries, scorerfuzz.ratio, limit3)实现未安装时给出pip install rapidfuzz的提示。异常数值离群值检测领域知识与统计方法结合先通过describe()与领域知识筛选不可能的值如年龄 0或 120再辅以两种统计方法IQR四分位距法计算 Q1、Q3 与 IQR将Q1 - 1.5*IQR与Q3 1.5*IQR之外的点标记为离群值。该方法对极端值不敏感较为稳健。Z-Score 法基于均值与标准差计算标准化分数需scipynp.abs(stats.zscore(...))通常Z-Score 3判定为离群值。离群值的处理有四种路线删除若为录入错误、封顶替换为边界值、置为NaN走插补流程、或保留若为合法极端值。notebook 示例用apply把不可能年龄替换为np.nan以进入后续插补。近似重复行归一化与模糊匹配先对姓名做strip()lower()归一化再用duplicated([name_normalized], keepFalse)找出精确重复更进一步封装find_near_duplicates(df, column, threshold90)函数用rapidfuzz找出相似度达阈值以上的近似重复组。处理方式包括保留首条drop_duplicates(subset[...], keepfirst)、保留末条、聚合信息或人工复核。完整清洗管道Pipelinenotebook 最终将所有技巧封装进clean_dataset(df)函数按序执行标准化国家 → 清洗异常年龄 → 去除姓名重复并对比清洗前后行数、唯一国家数与非法年龄数def clean_dataset(df): cleaned df.copy() # 1. 标准化分类值 cleaned[country] cleaned[country].str.lower().map(country_mapping) # 2. 清洗异常年龄 cleaned[age] cleaned[age].apply( lambda x: np.nan if (x 0 or x 120) else x) # 3. 去除近似重复姓名先规整空白 cleaned[name] cleaned[name].str.strip() cleaned cleaned.drop_duplicates(subset[name], keepfirst) return cleaned课程给出的核心经验① 不一致类别是常态务必检查唯一值并用映射或模糊匹配标准化② 离群值用领域知识 统计方法IQR、Z-Score双管齐下③ 近似重复比完全重复更难发现需归一化转小写、去空白与模糊匹配结合④ 数据清洗是迭代过程⑤记录清洗决策保证可复现与透明。最佳实践是永远保留一份原始脏数据副本不要覆盖源文件用data_cleaned.csv之类清晰命名保存清洗结果。动手实践从表单数据中发现问题本课配套作业 Evaluating Data from a Form 提供了一个真实场景客户正在测试一个小型表单以收集客户基础信息并提供了 表单数据 CSV 和基础可视化。客户发现部分可视化结果看起来不正确却不知如何修复。作业要求你运用本课技巧对表单提出改进建议使其能捕获准确一致的信息评分标准分三个等级Exemplary / Adequate / Needs Improvement可在 assignment.ipynb 中直接操作。从预置代码可见birth_month列存在January/JAN/january/Sept/September的大小写与缩写混用state列存在NaN、双字母缩写CA与全称California并存——这恰好是格式统一 缺失值处理 类别标准化三合一的绝佳练习。课程脉络与延伸阅读本课是 2-Working-With-Data 单元数据处理的收尾环节承接 07-python 的 Pandas 基础并为后续 4-Data-Science-Lifecycle/15-analyzing数据深入分析铺路。全部演示代码与练习均可从 notebook.ipynb 获得每个小节都附带练习题进阶方向包括日期解析、数据缩放与归一化等本课未覆盖的技术。数据准备是熟能生巧的实践性环节——建议在真实或模拟的脏数据上反复练习逐步建立起自己的清洗判断力。【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考