# 数据处理:数据准备 |![ Sketchnote by [(@sketchthedocs)](https://sketchthedocs.dev) ](../../sketchnotes/08-DataPreparation.png)| |:---:| |数据准备 - _由 [@nitya](https://twitter.com/nitya) 绘制的速记图_ | ## [课前测验](https://ff-quizzes.netlify.app/en/ds/quiz/14) 根据数据来源,原始数据可能存在一些不一致性,这会在分析和建模时带来挑战。换句话说,这些数据可以被归类为“脏数据”,需要进行清理。本课程重点介绍清理和转换数据的技术,以解决数据缺失、不准确或不完整的问题。本课程中涉及的主题将使用 Python 和 Pandas 库,并在本目录中的[笔记本](../../../../2-Working-With-Data/08-data-preparation/notebook.ipynb)中进行演示。 ## 清理数据的重要性 - **易于使用和复用**:当数据被正确组织和规范化时,更容易搜索、使用和与他人共享。 - **一致性**:数据科学通常需要处理多个数据集,不同来源的数据集需要合并在一起。确保每个单独的数据集具有统一的标准化,可以保证在合并为一个数据集时数据仍然有用。 - **模型准确性**:清理过的数据可以提高依赖于这些数据的模型的准确性。 ## 常见的清理目标和策略 - **探索数据集**:数据探索(将在[后续课程](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/4-Data-Science-Lifecycle/15-analyzing)中介绍)可以帮助发现需要清理的数据。通过可视化观察数据集中的值,可以设定对其余数据的期望,或者提供解决问题的思路。探索可以包括基本查询、可视化和采样。 - **格式化**:根据数据来源,数据在呈现方式上可能存在不一致性。这会导致在搜索和表示值时出现问题,数据虽然在数据集中存在,但在可视化或查询结果中未正确表示。常见的格式化问题包括解决空白、日期和数据类型问题。解决格式化问题通常由使用数据的人来决定。例如,不同国家对日期和数字的表示标准可能不同。 - **重复数据**:重复数据可能会产生不准确的结果,通常需要删除。这在合并两个或多个数据集时很常见。然而,有些情况下,合并数据集中的重复数据可能包含额外信息,需要保留。 - **缺失数据**:缺失数据可能导致结果不准确或偏差。有时可以通过重新加载数据、使用 Python 等代码填充缺失值,或者简单地删除缺失值及其相关数据来解决。数据缺失的原因有很多,解决缺失值的方式可能取决于数据缺失的原因和方式。 ## 探索 DataFrame 信息 > **学习目标**:在本小节结束时,您应该能够熟练地查找存储在 pandas DataFrame 中的数据的一般信息。 将数据加载到 pandas 后,通常会以 DataFrame 的形式存在(详细概述请参考之前的[课程](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/2-Working-With-Data/07-python#dataframe))。然而,如果您的 DataFrame 数据集有 60,000 行和 400 列,如何开始了解您正在处理的数据?幸运的是,[pandas](https://pandas.pydata.org/) 提供了一些方便的工具,可以快速查看 DataFrame 的整体信息以及前几行和后几行。 为了探索这些功能,我们将导入 Python 的 scikit-learn 库,并使用一个经典数据集:**Iris 数据集**。 ```python import pandas as pd from sklearn.datasets import load_iris iris = load_iris() iris_df = pd.DataFrame(data=iris['data'], columns=iris['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.2 | - **DataFrame.info**:首先,`info()` 方法用于打印 `DataFrame` 中内容的摘要。让我们看看这个数据集包含哪些内容: ```python 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 条记录,分布在四列中,没有空值。所有数据都存储为 64 位浮点数。 - **DataFrame.head()**:接下来,为了检查 `DataFrame` 的实际内容,我们使用 `head()` 方法。让我们看看 `iris_df` 的前几行: ```python 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.2 ``` - **DataFrame.tail()**:相反,为了检查 `DataFrame` 的最后几行,我们使用 `tail()` 方法: ```python 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 ``` > **总结**:仅通过查看 DataFrame 的元数据或前几行和后几行的值,您就可以立即了解您正在处理的数据的大小、形状和内容。 ## 处理缺失数据 > **学习目标**:在本小节结束时,您应该知道如何替换或删除 DataFrame 中的空值。 大多数时候,您想使用(或必须使用)的数据集中会有缺失值。处理缺失数据时需要权衡,这可能会影响最终分析和实际结果。 Pandas 处理缺失值有两种方式。第一种您在之前的章节中已经见过:`NaN`,即非数字。这实际上是 IEEE 浮点规范的一部分,用于表示缺失的浮点值。 对于浮点值以外的缺失值,pandas 使用 Python 的 `None` 对象。虽然同时遇到两种表示缺失值的方式可能会让人困惑,但这种设计选择有其合理的编程原因,实际上,这种方式为绝大多数情况提供了良好的折中方案。尽管如此,`None` 和 `NaN` 都有一些限制,您需要注意它们的使用方式。 在[笔记本](https://github.com/microsoft/Data-Science-For-Beginners/blob/main/4-Data-Science-Lifecycle/15-analyzing/notebook.ipynb)中了解更多关于 `NaN` 和 `None` 的信息! - **检测空值**:在 `pandas` 中,`isnull()` 和 `notnull()` 方法是检测空数据的主要方法。两者都会返回数据的布尔掩码。我们将使用 `numpy` 来处理 `NaN` 值: ```python 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 将其视为有效值。`''` 则稍微复杂一些。虽然我们在第 1 节中用它表示空字符串值,但它仍然是一个字符串对象,而不是 pandas 所认为的空值。 现在,让我们反过来以更实际的方式使用这些方法。您可以直接将布尔掩码用作 `Series` 或 `DataFrame` 的索引,这在处理孤立的缺失值(或存在值)时非常有用。 > **总结**:在 `DataFrame` 中使用 `isnull()` 和 `notnull()` 方法时,它们会显示结果及其索引,这在处理数据时非常有帮助。 - **删除空值**:除了识别缺失值,pandas 还提供了一种方便的方法来从 `Series` 和 `DataFrame` 中删除空值。(特别是在处理大型数据集时,通常建议直接删除缺失值,而不是以其他方式处理它们。)让我们回到 `example1` 来看看实际效果: ```python example1 = example1.dropna() example1 ``` ``` 0 0 2 dtype: object ``` 注意,这应该与您的 `example3[example3.notnull()]` 输出类似。不同之处在于,`dropna` 删除了 `Series` `example1` 中的缺失值,而不是仅仅索引掩码值。 由于 `DataFrame` 是二维的,它提供了更多删除数据的选项。 ```python example2 = pd.DataFrame([[1, np.nan, 7], [2, 5, 8], [np.nan, 6, 9]]) example2 ``` | | 0 | 1 | 2 | |------|---|---|---| |0 |1.0|NaN|7 | |1 |2.0|5.0|8 | |2 |NaN|6.0|9 | (您是否注意到 pandas 将两列数据提升为浮点类型以容纳 `NaN`?) 您无法从 `DataFrame` 中删除单个值,因此必须删除整行或整列。根据您的需求,您可能需要删除其中之一,pandas 提供了两者的选项。由于在数据科学中,列通常表示变量,行表示观察值,您更可能删除数据行;`dropna()` 的默认设置是删除所有包含任何空值的行: ```python example2.dropna() ``` ``` 0 1 2 1 2.0 5.0 8 ``` 如果需要,您可以从列中删除 NA 值。使用 `axis=1` 来实现: ```python example2.dropna(axis='columns') ``` ``` 2 0 7 1 8 2 9 ``` 注意,这可能会删除您可能想保留的大量数据,特别是在较小的数据集中。如果您只想删除包含多个或所有空值的行或列,可以在 `dropna` 中使用 `how` 和 `thresh` 参数指定这些设置。 默认情况下,`how='any'`(如果您想自己检查或查看方法的其他参数,请在代码单元中运行 `example4.dropna?`)。您也可以指定 `how='all'`,以便仅删除包含所有空值的行或列。让我们扩展示例 `DataFrame` 来看看实际效果。 ```python example2[3] = np.nan example2 ``` | |0 |1 |2 |3 | |------|---|---|---|---| |0 |1.0|NaN|7 |NaN| |1 |2.0|5.0|8 |NaN| |2 |NaN|6.0|9 |NaN| `thresh` 参数提供了更细粒度的控制:您可以设置行或列需要具有的*非空值*数量,以便保留: ```python example2.dropna(axis='rows', thresh=3) ``` ``` 0 1 2 3 1 2.0 5.0 8 NaN ``` 这里,第一行和最后一行被删除,因为它们只有两个非空值。 - **填充空值**:根据您的数据集,有时填充空值比删除它们更合理。您可以使用 `isnull` 来就地填充,但这可能很费力,特别是当您有很多值需要填充时。由于这是数据科学中的常见任务,pandas 提供了 `fillna` 方法,它返回一个 `Series` 或 `DataFrame` 的副本,其中的缺失值被替换为您选择的值。让我们创建另一个示例 `Series` 来看看实际效果。 ```python example3 = pd.Series([1, np.nan, 2, None, 3], index=list('abcde')) example3 ``` ``` a 1.0 b NaN c 2.0 d NaN e 3.0 dtype: float64 ``` 您可以用单个值(例如 `0`)填充所有空条目: ```python example3.fillna(0) ``` ``` a 1.0 b 0.0 c 2.0 d 0.0 e 3.0 dtype: float64 ``` 您可以**向前填充**空值,即使用最后一个有效值填充空值: ```python example3.fillna(method='ffill') ``` ``` a 1.0 b 1.0 c 2.0 d 2.0 e 3.0 dtype: float64 ``` 您也可以**向后填充**,即将下一个有效值向后传播以填充空值: ```python example3.fillna(method='bfill') ``` ``` a 1.0 b 2.0 c 2.0 d 3.0 e 3.0 dtype: float64 ``` 正如您可能猜到的,这对 `DataFrame` 同样适用,但您还可以指定填充空值的 `axis`。再次使用之前的 `example2`: ```python example2.fillna(method='ffill', axis=1) ``` ``` 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 ``` 注意,当没有可用的前一个值进行向前填充时,空值仍然保留。 > **要点:** 处理数据集中缺失值的方法有很多种。具体采用哪种策略(删除、替换,甚至是如何替换)应根据数据的具体情况来决定。随着你处理和分析数据集的经验不断增加,你会逐渐形成更好的应对缺失值的直觉和方法。 ## 删除重复数据 > **学习目标:** 在本小节结束时,您应该能够熟练识别并删除 DataFrame 中的重复值。 除了缺失数据之外,您在实际数据集中还会经常遇到重复数据。幸运的是,`pandas` 提供了一种简单的方法来检测和删除重复条目。 - **识别重复项:`duplicated`**:您可以使用 pandas 中的 `duplicated` 方法轻松找到重复值,该方法返回一个布尔掩码,指示 `DataFrame` 中某个条目是否与之前的条目重复。让我们创建另一个示例 `DataFrame` 来实际操作一下。 ```python example4 = pd.DataFrame({'letters': ['A','B'] * 2 + ['B'], 'numbers': [1, 2, 1, 3, 3]}) example4 ``` | |letters|numbers| |------|-------|-------| |0 |A |1 | |1 |B |2 | |2 |A |1 | |3 |B |3 | |4 |B |3 | ```python example4.duplicated() ``` ``` 0 False 1 False 2 True 3 False 4 True dtype: bool ``` - **删除重复项:`drop_duplicates`**:简单地返回一个数据副本,其中所有 `duplicated` 值均为 `False`: ```python example4.drop_duplicates() ``` ``` letters numbers 0 A 1 1 B 2 3 B 3 ``` `duplicated` 和 `drop_duplicates` 默认会考虑所有列,但您可以指定它们仅检查 `DataFrame` 中的某些列: ```python example4.drop_duplicates(['letters']) ``` ``` letters numbers 0 A 1 1 B 2 ``` > **要点:** 删除重复数据是几乎每个数据科学项目中的重要步骤。重复数据可能会改变您的分析结果并导致不准确的结论! ## 🚀 挑战 所有讨论的材料都提供为 [Jupyter Notebook](https://github.com/microsoft/Data-Science-For-Beginners/blob/main/2-Working-With-Data/08-data-preparation/notebook.ipynb)。此外,每个部分后面都有练习,试着完成它们吧! ## [课后测验](https://ff-quizzes.netlify.app/en/ds/quiz/15) ## 复习与自学 有很多方法可以发现和处理数据准备工作,用于分析和建模,而清理数据是一个需要“动手实践”的重要步骤。试试 Kaggle 上的这些挑战,探索本课未涉及的技术。 - [数据清理挑战:解析日期](https://www.kaggle.com/rtatman/data-cleaning-challenge-parsing-dates/) - [数据清理挑战:数据缩放与归一化](https://www.kaggle.com/rtatman/data-cleaning-challenge-scale-and-normalize-data) ## 作业 [评估表单数据](assignment.md) --- **免责声明**: 本文档使用AI翻译服务[Co-op Translator](https://github.com/Azure/co-op-translator)进行翻译。尽管我们努力确保准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息,建议使用专业人工翻译。对于因使用本翻译而引起的任何误解或误读,我们概不负责。