2026/9/11 1:59:07

Data-Science-For-Beginners 数据准备实战:用 Pandas 完成缺失值处理、去重与数据清洗

Data-Science-For-Beginners 数据准备实战:用 Pandas 完成缺失值处理、去重与数据清洗 Data-Science-For-Beginners 数据准备实战用 Pandas 完成缺失值处理、去重与数据清洗【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners本篇文章源自微软开源课程 Data-Science-For-Beginners 第 2 单元与数据打交道的第 8 课聚焦数据准备Data Preparation这一数据科学流水线中的关键环节。原始数据往往因为来源多样而包含缺失、错误或不完整的信息本课将带你掌握基于 Python 与 Pandas 的清洗与转换技术——包括探索 DataFrame 结构、检测与处理缺失值NaN/None、去除重复数据并结合本仓库的 notebook.ipynb 扩展到类别编码与真实世界数据质量检查。学完本篇你将能独立完成一套从脏数据到可建模数据的完整清洗流程。为什么数据清洗如此重要根据数据来源的不同原始数据常常包含一些不一致之处给后续的分析与建模带来挑战。这类数据可以被归类为脏数据dirty需要通过清洗来修复。在课程阿拉伯语版本 与 英文版本中清洗数据的价值被概括为三点易于使用与复用Ease of use and reuse数据经过正确的组织和规范化后更容易被检索、使用和分享给他人。一致性Consistency数据科学经常需要同时处理多个数据集并把来自不同来源的数据拼接join在一起。确保每个数据集都遵循共同的标准化规范才能保证它们在合并成一个数据集后依然可用。模型准确性Model accuracy经过清洗的数据能够直接提升依赖它的机器学习模型的准确率。常见的清洗目标与策略在动手写代码之前先建立全局视角。课程总结了四类最典型的清洗目标与对应策略探索数据集Exploring a dataset数据探索本课程的 15 课分析数据 有更深入讲解能帮你发现哪些数据需要清洗。通过可视化观察数据集中的值可以建立对剩余数据的预期或发现可以解决的问题。探索手段包括基本查询、可视化和抽样。格式化Formatting数据可能因来源不同而呈现不一致的形式导致搜索和表示数值时出现问题——值明明存在于数据集中却无法在可视化或查询结果中正确展示。常见的格式化问题涉及空白字符、日期和数据类型的处理。格式化标准的确定通常取决于数据的使用者例如日期和数字的表示标准会因国家而异。重复Duplications出现多次的数据会产生不准确的结果通常应当移除。这在合并两个或多个数据集时尤为常见但也有例外——合并后数据集中重复的部分可能携带额外信息而需要保留。缺失数据Missing Data缺失数据会造成结果不准确、不稳健甚至出现偏差。有时可以通过重新加载数据、用 Python 等代码计算并填充缺失值、或者直接删除该值与关联数据来解决。数据丢失的原因很多采取何种措施取决于数据最初丢失的方式与原因。探索 DataFrame 信息先看一眼你的数据数据加载进 pandas 后通常以 DataFrame 形式存在可参考前一课 与 Python 打交道 中对 DataFrame 的详细概述。但如果你的 DataFrame 有 6 万行、400 列该如何快速建立对数据的整体感知pandas 提供了便捷工具来查看 DataFrame 的整体信息以及首尾若干行。本课使用 scikit-learn 内置的经典Iris鸢尾花数据集进行演示import pandas as pd from sklearn.datasets import load_iris iris load_iris() iris_df pd.DataFrame(datairis[data], columnsiris[feature_names])sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) 0 5.1 3.5 1.4 0.2 1 4.9 3.0 1.4 0.2 2 4.7 3.2 1.3 0.2 3 4.6 3.1 1.5 0.2 4 5.0 3.6 1.4 0.2DataFrame.shape 与 DataFrame.columns在 notebook.ipynb 中除了课程正文提到的info()、head()、tail()还补充了两个先行的探索属性iris_df.shape返回(150, 4)表示 150 行、4 列即 150 个数据点、每个数据点含 4 个特征。注意shape是属性而非方法因此不带括号。iris_df.columns返回列名列表sepal length (cm)、sepal width (cm)、petal length (cm)、petal width (cm)用于识别数据集包含的特征。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 位浮点数float64存储。这一输出同时暴露了两个关键信息每列的数据类型Dtype以及每列的非空值数量Non-Null Count——后者正是数据准备阶段处理缺失值的重要入口。DataFrame.describe()统计摘要notebook 还引入了describe()它对数据集的数值列给出单变量统计摘要包括数据点总数、均值、标准差、最小值、下四分位数25%、中位数50%、上四分位数75%和最大值iris_df.describe()DataFrame.head() 与 DataFrame.tail()查看首尾行head()默认返回前 5 行iris_df.head()sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) 0 5.1 3.5 1.4 0.2 1 4.9 3.0 1.4 0.2 2 4.7 3.2 1.3 0.2 3 4.6 3.1 1.5 0.2 4 5.0 3.6 1.4 0.2tail()则相反返回最后 5 行iris_df.tail()sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) 145 6.7 3.0 5.2 2.3 146 6.3 2.5 5.0 1.9 147 6.5 3.0 5.2 2.0 148 6.2 3.4 5.4 2.3 149 5.9 3.0 5.1 1.8在实践中有序数据集里查找异常值时快速查看首尾几行非常实用。notebook 还留了一个练习head()默认返回 5 行如何显示超过 5 行提示是查阅文档iris_df.head?——答案即传入参数head(n)。小结仅通过 DataFrame 的元数据shape、columns、info、describe或首尾若干行的值就能立刻了解所处理数据的规模、形状与内容。处理缺失数据绝大多数实际数据集都包含缺失值而缺失值的处理方式带有微妙的权衡会影响最终分析与真实世界的结果。pandas 如何表示缺失值NaN 与 Nonepandas 用两种方式表示缺失值NaNNot a Number这是 IEEE 浮点数规范中的一个特殊值仅用于表示缺失的浮点数值。任何对NaN的算术运算结果仍是NaN如np.nan 1、np.nan * 0都是NaN但含NaN的数组做聚合如sum()、min()不会报错只是结果未必有意义例如np.array([2, np.nan, 6, 8]).sum()返回nan。NonePython 原生对象用于表示浮点数以外的缺失值。None不能用于非object类型的 NumPy/pandas 数组数组中出现None时会触发数据类型向上转型upcast为 Python 对象导致运算在解释型 Python 层面执行、性能下降且对含None的数组执行sum()、min()等聚合通常会报错——整数与None的加法等运算是未定义的。虽然NaN与None行为略有差异但 pandas 在构造Series/DataFrame时会自动在两者之间切换以保持数据类型同质。因此可以把它们看作 pandas 中空值的两种形态而处理空值的核心方法也由此得名isnull()、notnull()、dropna()、fillna()。关于NaN与None的更深入探讨可参见 15 课的分析 notebook。检测缺失值isnull() 与 notnull()isnull()和notnull()是检测空数据的主要方法两者都返回覆盖在数据之上的布尔掩码Boolean mask。使用 numpy 构造含NaN的示例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的索引用于隔离缺失或非缺失的值配合sum()还能统计缺失值总数example1.isnull().sum()。小结isnull()与notnull()在 DataFrame 上使用时会同时展示结果及对应的索引这对处理数据帮助极大。删除缺失值dropna()除了识别缺失值pandas 还提供了从Series和DataFrame中移除空值的便捷方法。尤其在大型数据集中直接删除缺失值NA往往比用其他方式处理更可取example1 example1.dropna() example10 0 2 dtype: object这与example1[example1.notnull()]的输出相似区别在于dropna不只是基于掩码索引而是真正把缺失值从Series中移除。由于 DataFrame 是二维的删除数据的选项更丰富。先构造一个含NaN的示例 DataFrameexample2 pd.DataFrame([[1, np.nan, 7], [2, 5, 8], [np.nan, 6, 9]]) example20 1 2 0 1.0 NaN 7 1 2.0 5.0 8 2 NaN 6.0 9注意到 pandas 为了容纳NaN把两个列升级成了浮点类型吗这就是前面提到的 upcast 行为。无法从 DataFrame 中只删除单个值只能删除整行或整列。在数据科学中列通常代表变量、行代表观测样本因此删除行更为常见dropna()的默认设置就是删除所有含任意空值的行example2.dropna()0 1 2 1 2.0 5.0 8如需删除含 NA 的列使用axis1或axiscolumnsexample2.dropna(axiscolumns)2 0 7 1 8 2 9注意这样可能删除大量你想保留的数据尤其在较小的数据集中。如果只想删除含多个甚至全部空值的行或列就需要how与thresh两个参数默认howany删除含任意空值的行/列可通过example2.dropna?在代码单元格中查看方法的全部参数。howall只删除全部为空的行/列。给example2增加一列全为NaN的列来演示example2[3] np.nan example20 1 2 3 0 1.0 NaN 7 NaN 1 2.0 5.0 8 NaN 2 NaN 6.0 9 NaNthresh参数提供更细粒度的控制它设定一个行/列需要拥有的非空值数量阈值达到该阈值才被保留example2.dropna(axisrows, thresh3)0 1 2 3 1 2.0 5.0 8 NaN这里第 0 行和第 2 行因为只含两个非空值而被删除。填充缺失值fillna()根据数据集的特点有时用有效值填充空值比删除更合理。虽然可以手动用isnull逐个填充但工作量很大pandas 为此提供了fillna()它返回一份将缺失值替换为你所选值的副本。构造示例Seriesexample3 pd.Series([1, np.nan, 2, None, 3], indexlist(abcde)) example3a 1.0 b NaN c 2.0 d NaN e 3.0 dtype: float64用单一值填充例如用0example3.fillna(0)a 1.0 b 0.0 c 2.0 d 0.0 e 3.0 dtype: float64向前填充forward-fill用最后一个有效值填充空值example3.fillna(methodffill)a 1.0 b 1.0 c 2.0 d 2.0 e 3.0 dtype: float64向后填充back-fill将下一个有效值向后传播以填充空值example3.fillna(methodbfill)a 1.0 b 2.0 c 2.0 d 3.0 e 3.0 dtype: float64DataFrame上的行为与此一致且可以通过axis指定填充方向。沿用前面的example2example2.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 列空值会保留。在较新版本的 pandas 中也可以用等价的独立方法ffill()与bfill()实现同样的效果。按数据类型选择填充策略众数、均值与中位数notebook 进一步给出了针对不同数据类型的填充策略类别数据非数值一般用该列的**众数mode**填充。例如 100 个数据点中 90 个为True、8 个为False、2 个缺失则用True填充那 2 个缺失值先通过value_counts()确认众数再fillna(True, inplaceTrue)就地填充。也可以结合领域知识domain knowledge做近似填充。数值数据两种常见方式——用**中位数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)。此外还可以发挥创意例如example4.fillna(example4.mean())用整个 DataFrame 的均值填充缺失值——注意默认按行方向填充全空的列可能仍无值。小结处理缺失值有多种方式具体策略删除、替换乃至如何替换应由数据本身的特性决定。与数据集接触越多对缺失值处理的把握就越强。类别数据的编码让模型看懂文字机器学习模型只能处理数值无法区分Yes与No但能区分0与1。因此在填充缺失值之后需要把类别数据编码为数值。notebook 介绍了两种方式标签编码Label Encoding把每个类别映射为一个数字。例如航班乘客的舱位[business class, economy class, first class]可编码为[0, 1, 2]。实现方式是构造字典映射后用replace替换label pd.DataFrame([ [10,business class], [20,first class], [30, economy class], [40, economy class], [50, economy class], [60, business class] ], columns[ID,class]) 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。用pd.get_dummies实现one_hot pd.DataFrame([ [10,business class], [20,first class], [30, economy class], [40, economy class], [50, economy class], [60, business class] ], columns[ID,class]) one_hot_data pd.get_dummies(one_hot, columns[class])独热编码适用于类别数量少且数据集规模小或类别之间没有顺序关系。小结编码用于把非数值数据转换为数值数据标签编码与独热编码两种方式可按数据集需求选用。去除重复数据除了缺失数据真实数据集里也常出现重复数据。pandas 提供了检测与移除重复条目的简便方法。识别重复duplicated()duplicated()返回布尔掩码指示 DataFrame 中的条目是否与更早的条目重复example4 pd.DataFrame({letters: [A,B] * 2 [B], numbers: [1, 2, 1, 3, 3]}) example4letters numbers 0 A 1 1 B 2 2 A 1 3 B 3 4 B 3example4.duplicated()0 False 1 False 2 True 3 False 4 True dtype: bool删除重复drop_duplicates()drop_duplicates()返回所有duplicated值为False的数据副本example4.drop_duplicates()letters numbers 0 A 1 1 B 2 3 B 3duplicated与drop_duplicates默认检查所有列但可以指定只检查 DataFrame 中的部分列example4.drop_duplicates([letters])letters numbers 0 A 1 1 B 2小结去除重复数据几乎是每个数据科学项目的必经步骤。重复数据会改变分析结果带来不准确的分析结论真实世界的数据质量检查不止于缺失值与精确重复notebook 在课程正文基础上扩展了一节实战内容专门应对真实数据中更隐蔽的质量问题类别值写法不一致、异常数值离群点、以及带细微差异的近似重复记录。下面给出其中的关键技术。构造一个脏数据集dirty_data pd.DataFrame({ customer_id: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], name: [John Smith, Jane Doe, John Smith, Bob Johnson, Alice Williams, Charlie Brown, John Smith, Eva Martinez, Bob Johnson, Diana Prince, Frank Castle, Alice Williams], age: [25, 32, 25, 45, 28, 199, 25, 31, 45, 27, -5, 28], country: [USA, UK, U.S.A, Canada, USA, United Kingdom, United States, Mexico, canada, USA, UK, usa], purchase_amount: [100.50, 250.00, 105.00, 320.00, 180.00, 90.00, 102.00, 275.00, 325.00, 195.00, 410.00, 185.00] })1. 不一致的类别值country列对同一国家有多种写法USA、U.S.A、United States、usa……。先用unique()、nunique()、value_counts()摸清各种写法及其频次再用映射字典标准化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% 的字符串组合如process.extract(country, unique_countries, scorerfuzz.ratio)。2. 异常数值离群点age列出现199、-5这类显然异常的值。先用领域知识过滤不可能的值年龄小于 0 或大于 120再用统计方法检测离群点IQR四分位距法计算Q1 valid_ages.quantile(0.25)、Q3 valid_ages.quantile(0.75)IQR Q3 - Q1上下界分别为Q1 - 1.5 * IQR与Q3 1.5 * IQR落在界外的即离群点。IQR 法对极端值不敏感更为稳健。Z-score 法用 scipy 的stats.zscore计算每个值偏离均值的标准差倍数通常Z-score 3视为离群点。检测出离群点后处理方式有四种删除若为录入错误、封顶替换为边界值、转为NaN后按缺失值插补、保留若为合法的极端值。例如把不可能年龄替换为NaNdirty_data[age_clean] dirty_data[age].apply( lambda x: np.nan if (x 0 or x 120) else x )3. 近似重复行John Smith与John Smith多一个空格实际上是同一个人。先把名字标准化str.strip().str.lower()再用duplicated(subset, keepFalse)找出同名组更精细的做法是用rapidfuzz模糊匹配定义相似度阈值如 90%自动聚类近似重复。处理方式包括保留首次出现drop_duplicates(keepfirst)、保留末次出现keeplast、聚合多条记录的信息、或标记出来交人工复核。4. 整合为完整清洗流水线notebook 最终把上述步骤封装成一个可复用的clean_dataset(df)函数依次完成类别值标准化country 映射、异常年龄置为NaN、去除姓名重复并在调用前后对比行数、唯一国家数与非法年龄数来验证效果。最佳实践始终保留一份原始脏数据副本绝不覆盖源数据文件——清洗后的数据应使用data_cleaned.csv之类的清晰命名另存。综合实战评估一份表单收集的数据本课的 作业 提供了一个真实感极强的演练场景客户用一个 小型表单 收集客户基础信息请你验证所收集数据的质量。作业配套了 表单数据 CSV 与 作业 notebooknotebook 中已包含读取 CSV、以及用value_counts().plot(kindbar)绘制州与出生月份条形图的代码。打开form.csv即可发现典型问题birth_month,state,pet January,,Cats JAN,CA,Cats Sept,Hawaii,Dog january,AK,Dog July,RI,Cats September,California,Cats April,CA,Dog January,California,Cats November,FL,Dog December,Florida,Catsbirth_month 列同一月份存在多种写法January/JAN/january、Sept/September直接做value_counts()条形图时同一月份会被拆成多个柱子这正是可视化看起来不正确的根源state 列缩写CA、AK、RI、FL与全称Hawaii、California、Florida混用还含有缺失值第 1 行pet 列存在Cats与Dog两种取值表单中使用下拉选择框见 index.html 中的select是三类字段中约束最严格的。作业要求你运用本课技巧针对表单本身提出改进建议使其能捕获准确且一致的信息——例如birth_month从自由文本输入改为月份下拉选择或日期选择器、state提供州名/州缩写列表而非自由输入、并明确必填项校验从而从源头避免脏数据。挑战与延伸学习课程正文还给出了 notebook.ipynb 作为全部讨论内容的完整载体其中每个小节后都附有练习题例如如何显示超过 5 行example4.fillna(methodbfill, axis1)的输出是什么向含None的int_series赋值后其 dtype 如何变化建议逐一尝试。此外课程也推荐通过 Kaggle 的数据清洗挑战系列日期解析、数据缩放与归一化等探索本课未覆盖的技巧——数据清洗是高度依赖动手实践的经验型工作。如果你希望继续深入本系列可以接着学习 第 15 课分析数据该课将进一步讲解探索性数据分析EDA如何与数据清洗相互配合。【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考