Data-Science-For-Beginners 第 8 课:使用 Pandas 完成数据准备与清洗实战指南 Data-Science-For-Beginners 第 8 课使用 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-Working-With-Data」模块第 8 课的中文技术指南。原始文档为 translations/bg/2-Working-With-Data/08-data-preparation/README.md所有代码示例均可直接在配套 Jupyter Notebook 2-Working-With-Data/08-data-preparation/notebook.ipynb 中运行验证。读完本文你将掌握用 pandas 探索 DataFrame 结构、检测并处理缺失值NaN / None、去除重复记录三大类数据准备技能并能独立完成课后「表单数据评估」实战作业。无论数据来自何处原始数据raw data都可能包含不一致之处给后续分析与建模带来挑战。换句话说这些数据可被归类为“脏数据”dirty data需要清洗。本课聚焦于清洗与转换数据的技术以应对缺失、不准确或不完整的数据问题。所有主题均使用 Python 与 Pandas 库完整演示见配套 notebook.ipynb。为什么要清洗数据数据清洗不是可有可无的收尾工作它直接决定数据能否被有效使用。课程文档从三个层面阐述了其重要性易于使用与复用Ease of use and reuse当数据被妥善组织与规范化后搜索、使用和与他人共享都更轻松。一致性Consistency数据科学经常需要联合多个数据集不同来源的数据需要被合并join。确保每个数据集有共同的标准格式才能保证合并后的数据依然有用。模型准确性Model accuracy被清洗过的数据能提升依赖它的机器学习模型的准确率。这一点在 notebook 中也有呼应机器学习模型无法自行处理缺失数据因此在把数据送入模型之前必须先处理这些缺失值。常见清洗目标与策略在动手写代码前先明确要解决哪几类数据问题。课程将常见目标归纳为四类探索数据集Exploring a dataset数据探索在本课程 4-Data-Science-Lifecycle/15-analyzing/README.md 中有详细讲解能帮助你发现需要清洗的数据。目视检查数据集中的取值可以建立对整体数据的预期或发现可解决的问题。探索通常包括基本查询、可视化与抽样。格式化Formatting由于来源不同数据在呈现方式上可能不一致导致可视化或查询结果中取值“看得见却取不到”。常见格式化问题包括空白字符whitespace、日期格式、数据类型。解决格式化问题通常取决于数据使用者——例如不同国家对日期和数字的呈现标准就不一样。重复Duplications多次出现的数据会产生不准确的结果通常应被移除。这在合并两个或多个数据集时尤为常见。不过也有例外合并数据集中的重复部分有时包含额外信息可能需要保留。缺失数据Missing Data缺失数据会导致不准确以及薄弱或有偏的结果。有时可通过“重新加载”数据解决或用 Python 代码与计算填充缺失值或直接移除该值及其对应数据。数据缺失的原因很多采取的补救动作应视缺失的方式与原因而定。探索 DataFrame 信息学习目标本小节结束后你应该能熟练地在 pandas DataFrame 中找到关于数据的一般信息。数据加载进 pandas 后通常会以 DataFrame 形式存在详细概念见前一课 2-Working-With-Data/07-python/README.md。但如果你的 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])shape 与 columns先看规模与特征名notebook 在info()之前先介绍了两组属性注意它们是属性而非方法所以不带括号iris_df.shape # 输出(150, 4) iris_df.columns # Index([sepal length (cm), sepal width (cm), petal length (cm), # petal width (cm)], dtypeobject)shape告诉我们数据规模为 150 行 4 列每行是一个数据点每列是该数据点的一个特征columns则给出特征名称用于识别数据集包含哪些特征。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从输出可以立刻得到两个关键信息① 每列的数据类型dtype——本数据全部为 64 位浮点数float64② 每列的非空值数量Non-Null Count——本数据 4 列全部 150 non-null即没有缺失记录。这两项正是数据准备的核心切入点。DataFrame.describe数值列的统计摘要notebook 补充了课程 README 中未展开的方法describe()当数据集包含大量数值列时可对每列做单变量统计均值、中位数、四分位数等iris_df.describe()sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) count 150.000000 150.000000 150.000000 150.000000 mean 5.843333 3.057333 3.758000 1.199333 std 0.828066 0.435866 1.765298 0.762238 min 4.300000 2.000000 1.000000 0.100000 25% 5.100000 2.800000 1.600000 0.300000 50% 5.800000 3.000000 4.350000 1.300000 75% 6.400000 3.300000 5.100000 1.800000 max 7.900000 4.400000 6.900000 2.500000输出依次为每列的样本数、均值、标准差、最小值、下四分位数25%、中位数50%、上四分位数75%与最大值可用于快速发现异常分布。head 与 tail查看头尾数据head()查看前几行tail()查看最后几行iris_df.head() iris_df.tail()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.2 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两者默认返回 5 行也可传入整数参数返回指定行数notebook 中留了练习如何显示超过 5 行答案是iris_df.head(n)。在实际工作中head/tail 对于在有序数据集中查找异常值尤其有用。结论仅凭 DataFrame 的元数据信息或头尾几个值你就能立即对数据的规模、形状与内容形成直观认识。处理缺失数据学习目标本小节结束后你应该知道如何替换或移除 DataFrame 中的 null 值。大多数实际数据集都包含缺失值。缺失数据的处理方式带有微妙的权衡会直接影响最终分析与真实世界的结果。notebook 明确指出处理缺失数据的两种基本方式① 删除包含缺失值的行② 用其他值替换缺失值。下面先理解 pandas 中缺失值的两种表示再逐一学习检测、删除、填充三大操作。NaN 与 None两种“空”pandas 以两种方式处理缺失值NaNNot a Number这是 IEEE 浮点数规范中的特殊值仅用于表示浮点数的缺失。对NaN的任何算术运算结果仍是NaN如np.nan 1与np.nan * 0都返回nan不过含NaN的数组做聚合如sum()、min()、max()不会报错只是结果同样是nan。NonePython 对象用于非浮点类型数据的缺失。由于None来自 Python它只能存在于dtype为object的数组中一旦出现None整个数组会被“向上转型”upcast为 object 类型。这带来两个副作用一是运算回落到解释型 Python 代码层面大数据集下性能会变慢二是对含None的数组执行sum()或min()等聚合通常会直接报错如TypeError: unsupported operand type(s) for : int and NoneType。虽然两者行为略有差异但 pandas 在设计上可以互换处理它们在 Series 与 DataFrame 为保证数据同质而向上转型的过程中pandas 会在None与NaN之间自动切换。因此可以把它们视为 pandas 中“null”的两种形态。这一点也体现在核心方法命名上isnull()、notnull()、dropna()、fillna()。补充NaN只用于浮点缺失整数、字符串或布尔类型没有对应的NaN等价物。检测 null 值isnull 与 notnullisnull()与notnull()是检测 null 数据的主要方法都返回布尔掩码Boolean maskimport 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 看来不是null。布尔掩码可以直接作为 Series 或 DataFrame 的索引使用便于隔离出缺失或非缺失的值example1[example1.notnull()] # 等价于 example1.dropna() 的结果 example1.isnull().sum() # 统计缺失值总数输出 2结论isnull()与notnull()在 DataFrame 上的结果类似它们同时显示结果值及其索引这在和数据“搏斗”时极其有用。删除 null 值dropna在大数据集上通常直接删除缺失值比用其他方式处理更可取。对 Series 使用dropna()example1 example1.dropna() example10 0 2 dtype: object注意这与example1[example1.notnull()]的输出相似区别在于dropna是把缺失值真正从 Series 中移除而非仅做掩码索引。DataFrame 是二维的因此删除方式更多不能只删单个值必须整行或整列删除。由于数据科学中列一般代表变量、行代表观测更常删除的是行dropna()的默认行为就是删除任何包含 null 值的行example2 pd.DataFrame([[1, np.nan, 7], [2, 5, 8], [np.nan, 6, 9]]) example2.dropna() # 只保留第 1 行 [2.0, 5.0, 8]顺带一提因为要容纳NaNpandas 把两列自动向上转型成了浮点数。若需删除列使用axis1即axiscolumnsexample2.dropna(axiscolumns) # 只保留第 2 列 [7, 8, 9]但按列删除可能误删大量想保留的数据尤其在小数据集上。如果只想删除含若干甚至全部null 值的行/列可用how与thresh参数精细控制how默认any含任意 null 即删除设为all则只删除全部为 null 的行或列。thresh设定一行/列需要拥有的非 null 值个数门槛低于门槛才被删除。先给example2增加一列全为NaN的新列example2[3] np.nan example20 1 2 3 0 1.0 NaN 7.0 NaN 1 2.0 5.0 8.0 NaN 2 NaN 6.0 9.0 NaNexample2.dropna(axisrows, thresh3)0 1 2 3 1 2.0 5.0 8.0 NaN这里首尾两行因只含 2 个非 null 值低于门槛 3被删除中间行保留。想查看dropna的全部参数可在代码单元格中执行example2.dropna?。填充 null 值fillna有时填充 null 比删除更有意义。用isnull逐处填充费时费力pandas 为此提供了fillna它返回一个缺失值被替换后的 Series 或 DataFrame副本example3 pd.Series([1, np.nan, 2, None, 3], indexlist(abcde))用单一常量填充如0example3.fillna(0)a 1.0 b 0.0 c 2.0 d 0.0 e 3.0 dtype: float64向前填充forward-fill用上一个有效值填充 nullexample3.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 用法相同且可指定填充方向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 列null 会原样保留。用统计量填充mode / mean / mediannotebook 还演示了三种基于统计量的填充策略比固定常量更贴近数据分布众数 mode针对分类型非数值列例如一列布尔/类别数据100 个样本中 90 个为True、8 个为False、2 个缺失可用出现次数最多的True填充。notebook 中的示例用fill_with_mode[2].value_counts()先确认众数再执行fill_with_mode[2].fillna(True, inplaceTrue)。均值 mean针对数值列fill_with_mean[0].fillna(np.mean(fill_with_mean[0]), inplaceTrue)。中位数 median对离群值更稳健fill_with_median[1].fillna(fill_with_median[1].median(), inplaceTrue)。整表均值example4.fillna(example4.mean())用整个 DataFrame 各列的均值填充。填充策略的选择应结合领域知识与数据特点——这正是“权衡”所在。核心思想处理缺失值的方式多种多样删除、替换以及替换的方法具体策略必须由数据本身的特殊性决定。处理的数据越多你越能培养出这种判断力。删除重复数据学习目标本小节结束后你应该能熟练识别并删除 DataFrame 中的重复值。除了缺失数据真实数据集里还经常出现重复数据。pandas 提供了简便的检测与删除工具。识别重复duplicatedduplicated返回布尔掩码指示某条记录是否为此前某条记录的重复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 dtype: bool行 0 与行 2 均为(A, 1)因此行 2 被判为重复行 3 与行 4 均为(B, 3)因此行 4 重复。删除重复drop_duplicatesdrop_duplicates直接返回所有duplicated为False的副本example4.drop_duplicates()letters numbers 0 A 1 1 B 2 3 B 3duplicated与drop_duplicates默认检查所有列但都可以通过传参只针对部分列判断重复example4.drop_duplicates([letters])letters numbers 0 A 1 1 B 2仅按letters列判断时B只保留第一次出现的行 1。结论删除重复数据几乎是每个数据科学项目必备的一步——重复数据会改变分析结果给你不准确的结果notebook 中的综合实战清洗一份“脏数据”notebook 在最后用一个真实的dirty_data案例串起所有方法是值得完整复现的参考流程定位数值列中的异常dirty_data[age].describe()查看 age 列的统计摘要发现异常取值删除非数值的年龄age_nonan dirty_data[age].dropna()把年龄列中的无效/缺失值剔除后再分析识别重复姓名duplicate_names dirty_data[dirty_data.duplicated([name_normalized], keepFalse)]先对姓名做规范化归一化再按列找出重复记录去重并保留首条cleaned_data dirty_data.drop_duplicates(subset[name_normalized], keepfirst)保留末条cleaned cleaned.drop_duplicates(subset[name], keeplast)。其中keep参数控制保留哪一条first保留首次出现、last保留末次出现、False则全部删除。这个“清洗 pipeline”示范了探索 → 检测 → 删除/填充 → 去重的完整链路。课后挑战与作业所有讨论过的材料都以 Jupyter Notebook 形式提供每个小节后都有练习建议逐一尝试。课程还配套一个真实场景作业 评估表单数据背景一位客户用 一个小表单 收集其客户群的基础信息并把结果交给你验证。你可以在浏览器中打开index.html查看表单。数据提供了 CSV 记录数据集含表单记录与一些基础可视化客户指出部分可视化看起来不对但不知如何解决。可在 作业 Notebook 中探索。要求运用本课技术就“如何改进表单以采集准确、一致的信息”提出建议。从作业 notebook 的示例输出可以看到这份数据确实“脏”birth_month列混有January、JAN、Sept、january、September等多种大小写与缩写形态state列混有CA、Hawaii、California、Florida、NaN等不同写法与缺失值pet列也需统一。这正是格式化大小写/缩写统一、缺失值NaN填充或删除、规范化状态全名 ↔ 缩写映射等技术的用武之地。复习与自学数据准备与清洗是高度“动手实践”的步骤发现并处理数据问题的方法远不止本课覆盖的这些。建议进一步探索数据清洗挑战日期解析Parsing Dates数据清洗挑战数据缩放与归一化Scale and Normalize Data本课完整代码与全部输出位于 2-Working-With-Data/08-data-preparation/notebook.ipynb数据集位于 data/ 目录可供反复练习后续课程 4-Data-Science-Lifecycle/15-analyzing/README.md 还将继续讲解如何基于清洗后的数据做分析。【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考