跳至主要内容

Python 数据清洗新手指南

探索在 Python 中进行数据清洗的基本原则,了解通过处理缺失值、异常值、重复项和不一致来为分析做好数据准备的重要性。
· 11分钟 阅读

使用 AI 探索

数据清洗简介

当您开始一个新的数据项目时,最初获取的数据很少能立刻完美契合您的分析需求。因此,在每个新项目开始时进行数据清洗至关重要。

数据清洗是一个移除错误、异常值和不一致之处,并确保所有数据采用适合您分析的格式的过程。包含大量错误或未经过此清洗流程的数据通常被称为脏数据。

这一步对许多初级数据从业者来说似乎是可选的,但请相信,它至关重要!缺乏适当的数据清洗,您的模型可能得出错误结论,图表可能呈现虚假趋势,统计结果也可能严重失真。

想象一个极小的数据集。您要和朋友们平均分糖果,需要确定每个人能分到多少。您将数据输入到下表中。

人员

糖果数量

Peter

5

Sandy

20

Sandy

20

Joseph

3

Amed

12

如果此时您计算“糖果数量”一列的均值,可能会得出每个人应得到 12 颗糖的结论。

然而,如果您尝试给每个人 12 颗糖,您会在分完前就不够了,因为表中 Sandy 被重复记录。您的存货只有 40 颗而不是 60 颗(而且实际上只有 4 个人,而不是 5 个)!

如果不花时间清洗数据集,您的分析就有可能得出错误结论。

在与朋友分糖果的案例中,最大后果或许只是让朋友不高兴。但在商业环境中,忽视清洗数据集可能导致分析建议推出错误的产品、招聘错误数量的员工、投资错误的股票,甚至向客户收取错误的价格!

对于更复杂的分析,数据清洗更加重要。复杂分析中,您对结果的预期可能不那么明确,这意味着当结果有误时您也未必能察觉。

全面的数据清洗,配合仔细的测试,将有助于缓解这些担忧。在本教程中,我们将介绍在 Python 中进行数据清洗所需了解的内容。

理解脏数据

导致数据变“脏”的错误和不一致类型有很多。下面我们来看看几种最常见的类型以及它们为何会带来问题。

缺失值

不完整的数据集极为常见。您的数据集中可能缺失数年的数据,只包含某位客户的部分信息,或未包含公司全部产品。

缺失值会对您的分析产生多重影响。关键数据的大量缺失会使结果产生偏差。此外,DataFrame 中的NaN 值或空单元格可能会导致部分 Python 代码出错,让您在构建模型时非常挫败。

异常值

异常值是远离常态、不能代表整体数据的数值。异常值可能源于输入错误或特殊情境。区分真正的异常值与具有信息价值的极端情境非常重要。异常值会扭曲结果,最终给出错误答案。

重复项

正如我们上面所见,重复的数据条目会在分析中让某个条目被过度代表,导致错误结论。需要留意多次出现的重复项,以及包含冲突或更新信息的重复项。

错误数据

有时,数据集中的数值就是错的。可能是客户姓名拼写错误、产品编号错误、信息过时,或数据标签不正确。

有时判断数据是否有误并不容易,这就是为什么数据源验证如此重要!请记住,分析的质量取决于数据的质量。

不一致

不一致的形式多种多样。可能是数据记录不一致,暗示存在拼写错误或录入错误。如果您看到客户“逆生长”、某原料的 ID 号发生变化,或某产品同时出现两个价格,就值得仔细检查以确保一切正确。

另一类麻烦的不一致是数据格式上的不一致。不同的值可能使用不同单位(千米 vs. 英里 vs. 英寸)、不同样式(Month Day, Year vs. Day-Month-Year)、不同数据类型(浮点数 vs. 整数),甚至不同文件类型(.jpg vs. .png)。

这些不一致会让您的代码难以(甚至无法)正确解释数值,可能导致分析不正确,或代码根本无法运行。

数据探索与预处理

在将数据用于复杂分析之前,深入理解数据集至关重要。为建立这种理解,您应进行数据探索,可将其视为分析前的“预分析”。

下面是对表格化数据集进行数据探索的一些典型步骤:

  1. 首先也是最重要的,查看数据集的来源,判断该来源是否存在任何可能影响数据质量或可靠性的偏见或倾向。
  2. 了解数据的背景以及任何未在数据内部体现、但可能影响数据的因素。
  3. 确定您有多少个变量。在表格数据集中,变量通常对应列,而每条数据记录对应一行。
  4. 确定每个变量内有多少类别。例如,如果某个变量是“水果类型”,您应大致了解该变量下包含多少种不同水果。
  5. 查看每列的摘要统计,包括均值、中位数、方差和标准差。
  6. 如有可能,绘制每个变量以及尽可能多的变量对,认真观察图形。留意任何偏斜、异常值、趋势或可能值得进一步研究的相关性。
  7. 如果您的数据集属于关系型数据库,仔细查看各关系,确保理解不同表之间如何关联。
  8. 如适用,使用pandas 库的 profiling 功能生成概况报告,这将为您提供有价值的数据集信息。

为何探索与预处理很重要

这些步骤看似与您计划的分析无关,但您始终应执行它们,原因有三。首先,它能让您深入理解数据集的边界,这是信任最终分析结果的基础。

其次,数据探索可能会引导您关注此前未曾考虑的重要趋势和分析方向。这些内容可能会丰富您的既定分析,或提示需要纳入考虑的复杂因素。

第三,这一预分析可能是您发现脏数据的第一条线索。您可能首次看到异常值,意识到类别数是预期的两倍,或者发现去年的数据收集方式与前年不同。所有这些关键信息都应促使您对数据集保持好奇心。

有关数据探索的更多信息,请查看 DataCamp 的Python 数据探索课程或这篇面向绝对初学者的教程。

Python 中的数据清洗技术

接下来我们通过解决一些常见的脏数据问题,具体探讨在 Python 中的数据清洗技术。

处理缺失值

在大型数据集中,不可避免地会有某些记录的部分字段缺失。这不仅意味着您丢失了宝贵数据,NaN 项还可能干扰某些 Python 函数,使您的模型不准确。

当您发现缺失值时,可以选择将该记录从分析中剔除,或尝试填补一个合理的替代值。如果数据集规模适中,建议先查看包含缺失值的行,以便决定最佳处理方式。

import pandas as pd # Identify rows with NaN valuesrows_with_nan = df[df.isnull().any(axis=1)] #View the rows with NaN valuesprint(rows_with_nan)

删除记录通常不是首选,因为这意味着将潜在有价值的信息排除在分析之外。

但有时删除是最佳方案,例如该记录没有提供足够的其他信息以保留(比如只有日期而没有任何有价值的信息)。

删除也是处理含缺失值记录的最简单方法。因此,如果时间紧迫且该记录的其余信息对分析不重要,删除含 NaN 的记录也是一种选择。

您可以使用 Pandas 库的 .dropna() 方法,删除 DataFrame 中包含 NaN 的行。

import pandas as pd # Assuming df is your DataFramedf.dropna(inplace=True)

更普遍推荐的做法是填补一个合理的值。寻找合理填充值本身就是一门艺术,但有多种成熟方法可作为起点。

Pandas 的 .fillna() 方法可使用均值填补缺失值,从而尽量不改变分布。

import pandas as pd # Assuming df is your DataFrame# Replace NaN values with the mean of the columndf.fillna(df.mean(), inplace=True)

scikit-learn 库也提供了简单易用的填补函数。

from sklearn.impute import SimpleImputerimport pandas as pd # Assuming df is your DataFrameimputer = SimpleImputer(strategy='mean')df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)

如果您需要更复杂的填补方法,可以考虑使用 KNN 或回归填补来找到合适的值。最终选择取决于您的数据、需求和可用资源。

异常值检测与处理

异常值的处理有些棘手。有些看似异常的值其实很重要,例如股市对 COVID-19 或全球经济衰退等危机的反应。但另一些可能只是输入错误或罕见且无关的情况,需要移除。

辨别二者往往取决于对数据背景的理解(此前的数据探索应当对您有所帮助),以及对分析目标的清晰把握。

许多异常值在绘图时即可见,但您也可以使用统计方法识别异常值。

一种常见方法是为每个数据点计算 Z 分数,并剔除 Z 分数极端的数值。

import numpy as npimport pandas as pd # Generate some sample datanp.random.seed(0)data = np.random.randint(low=0, high=11, size=1000)# Add some outliersdata[0] = 100data[1] = -100 # Calculate Z-scoresz_scores = (data - np.mean(data)) / np.std(data) # Identify outliers based on Z-score threshold (e.g., 3)threshold = 3outliers = np.where(np.abs(z_scores) > threshold)[0] print("Outliers identified using Z-score method:")print(data[outliers])

另一种方法是计算分布的四分位距(IQR),将小于 Q1−(1.5 × IQR) 或大于 Q3 + (1.5 × IQR) 的值归为潜在异常值。

import numpy as npimport pandas as pd # Generate some sample datanp.random.seed(0)data = np.random.randint(low=0, high=11, size=1000)# Add some outliersdata[0] = 100data[1] = -100 # Calculate quartiles and IQRq1 = np.percentile(data, 25)q3 = np.percentile(data, 75)iqr = q3 - q1 # Identify outliers based on IQRlower_bound = q1 - (1.5 * iqr)upper_bound = q3 + (1.5 * iqr)outliers = np.where((data < lower_bound) | (data > upper_bound))[0] print("Outliers identified using IQR method:")print(data[outliers])

一旦识别出异常值并确定其有问题,可采用多种方法处理。如果确定异常值源于错误,或许只需纠错即可。

在其他情况下,可以将异常值从数据集中移除,或用不那么极端、能保留总体分布形状的值替代。

封顶(Capping)是一种方法,您为数据分布设置上下限阈值,并将超出边界的数值替换为指定值。

import pandas as pdimport numpy as np # Create a sample DataFrame with outliersdata = {    'A': [100, 90, 85, 88, 110, 115, 120, 130, 140],    'B': [1, 2, 3, 4, 5, 6, 7, 8, 9]}df = pd.DataFrame(data) # Define the lower and upper thresholds for capping (Here I used the 5th and 95th percentiles)lower_threshold = df.quantile(0.05)upper_threshold = df.quantile(0.95) # Cap outlierscapped_df = df.clip(lower=lower_threshold, upper=upper_threshold, axis=1) print("Original DataFrame:")print(df)print("\nCapped DataFrame:")print(capped_df)

在某些情况下,您可以通过变换数据来减小异常值的影响,例如进行平方根变换或对数变换。

在进行变换时需谨慎,如果不小心,长远来看可能引入更多问题。决定是否变换前需考虑以下几点:

  1. 理解底层数据分布:在应用任何变换前,了解数据分布及变换对其影响很重要。
  2. 选择合适的变换:根据数据分布选择恰当的变换方法。
  3. 处理零值与负值:某些变换不适用于包含零或负值的数据。例如进行对数时可添加一个很小的常数以避免问题。
  4. 验证变换后数据:应用变换后,验证结果分布是否满足分析假设。
  5. 考虑可解释性:变换后的数据可能不如原始数据易于解释。确保相关方理解变换对结果解读的影响。

要了解更多数据变换知识,建议学习统计课程,例如 DataCamp 的Python 统计学入门或Python 统计基础。

处理重复项

我们已经见识到重复项如何破坏分析。幸运的是,Python 使识别和处理重复项变得简单。

使用 Python 的 pandas 库中的 duplicated() 方法,您可以轻松识别 DataFrame 中的重复行以便检查。

import pandas as pd# Assuming 'df' is your DataFrameduplicate_rows = df[df.duplicated()]

这将返回一个包含重复行的 DataFrame。如果重复项不多,建议您实际查看这些重复项。

大多数重复项可能是完全相同的副本,可直接使用 Pandas 的 drop_duplicates() 方法删除:

# Removing duplicatescleaned_df = df.drop_duplicates()

在某些情况下,将重复记录合并并聚合信息可能更合适。例如,如果重复项代表同一实体的多条记录,我们可以使用聚合函数进行合并:

# Merging duplicates by aggregating valuesmerged_df = df.groupby(list_of_columns).agg({'column_to_merge': 'sum'})

这会将重复行合并为一行,并基于指定函数(如求和、求均值等)聚合数值。

import pandas as pd#Sample DataFramedata = {     'customer_id' : [102, 102, 101, 103, 102]     'product_id' : ['A', 'B', 'A', 'C', 'B']     'quantity_sold : [5, 3, 2, 1, 4]}df = pd.DataFrame(data)df}

# Merging duplicates by aggregating valuesmerged_df = df.groupby(['cutomer_id', 'product_id']).agg({'quantity_sold': 'sum'}).reset_index()merged_df

解决不一致问题

不同类型的不一致需要不同的解决方案。由错误数据录入或拼写错误导致的不一致,可能需要由熟悉业务的人来更正。或者,也可将错误数据视作缺失值进行填补,或视情况直接从数据集中移除。

数据格式上的不一致可通过标准化方法加以修正。去除字符串首尾空格可用 .strip() 方法;.upper() 与 .lower() 方法可统一字符串大小写;将日期转换为 datetime(使用 pd.to_datetime)可统一日期格式。

您还可以使用 .astype() 方法确保 DataFrame 某列中的每个值具有相同数据类型。

您可能还需要进行的其他格式不一致修正包括:

  • 单位换算
  • 邮箱、电话与地址标准化
  • 移除字符串中的标点符号
  • 使用取值映射处理常见缩写
value_mapping = {'M': 'Male', 'F': 'Female'}standardized_value = value_mapping.get('M', 'Unknown')

数据变换与特征工程

数据变换与特征工程是将原始数据转换为更适合机器学习算法与统计分析的格式的预处理技术。本节我们将简要介绍一些常见的数据变换与特征工程技术。

归一化与标准化

归一化与标准化是将特征缩放到相近量级的两种技术,这对许多机器学习算法非常重要。

归一化将数据缩放到固定范围,通常在 0 到 1 之间。这能确保所有特征在相同尺度上,避免因量级较大而掩盖其他特征。

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(data)

标准化则将数据变换为均值为 0、标准差为 1。当数据具有不同尺度与范围时,标准化有助于确保各特征对模型的影响可比。

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
standardized_data = scaler.fit_transform(data)

如何缩放数据取决于数据的具体特性与模型的需求。想进一步了解归一化与标准化的区别,请参阅我们的专题教程《Normalization vs. Standardization: How to Know the Difference》。

编码分类变量

在处理非数值特征时,对分类变量进行编码是机器学习中的关键预处理步骤。分类变量代表定性数据,如类型、类别或标签。要在机器学习算法中使用这些变量,需将其转换为数值格式。

一种常用技术是独热编码(one-hot encoding),将每个类别转换为一个二进制向量。该方法特别适用于名义型分类变量(不同类别之间没有固有顺序或层级)。

import pandas as pd

encoded_data = pd.get_dummies(data, columns=['categorical_column'])
import pandas as pd

# Create a sample DataFrame with a categorical column
data = {
    'ID': [1, 2, 3, 4, 5],
    'Color': ['Red', 'Blue', 'Green', 'Red', 'Blue']
}
df = pd.DataFrame(data)

# Perform one-hot encoding
encoded_data = pd.get_dummies(df, columns=['Color'])

print("Original DataFrame")
print(df)

print("\nEncoded DataFrame:")
print(encoded_data)

另一种方法是标签编码(label encoding),为每个类别分配唯一整数。每个类别映射到一个数值,有效地将分类标签转换为有序数。这种方法适用于有序型分类变量(类别之间存在自然顺序或等级)。

from sklearn.preprocessing import LabelEncoderencoder = LabelEncoder()encoded_data['categorical_column'] = encoder.fit_transform(data['categorical_column'])
from sklearn.preprocessing import LabelEncoderimport pandas as pd#Create a sample DataFrame with categorical column data = {    'ID' : [1, 2, 3, 4, 5]    'Color' : [ 'Red', 'Blue', 'Green', 'Red', 'Green' ]}df = pd.DataFrame(data)#Perform label encodingencoder  = LabelEncoder()df ['Color_LabelEncoded'] = encoder.fit_transform(df['Color'])print("Original DataFrame:")print(df[['ID', 'Color']])print("\nDataFrame with Label Encoded Column:")print(df[['ID', 'Color_LabelEncoded']])

选择合适的编码方法需要基于分类变量的性质与算法要求。对于名义变量通常优先使用独热编码,而有序变量适合使用标签编码。

特征工程技术

特征工程是在机器学习建模中创建新特征或改造现有特征的步骤。当原始数据缺乏能直接助力学习任务的特征,或现有特征不适合学习算法有效利用时,特征工程尤为常见。

设想您在处理房价数据集,想要基于房间数、居住面积、房屋状况等特征预测房屋售价。然而,数据集中并未直接包含能体现房屋整体状况的特征。

此时,您可以通过组合现有特征或提取相关信息来创建新特征。

例如,您可以汇总房屋各组成部分的状况评级(如厨房状况、地下室质量、房龄、近期翻修情况等),创建一个名为“整体状况”的特征。

当特征与目标变量之间的关系为非线性时,可以通过将现有特征升幂来生成多项式特征,以捕捉变量间可能无法线性分离的复杂关系。

from sklearn.preprocessing import PolynomialFeatures

poly = PolynomialFeatures(degree=2)
polynomial_features = poly.fit_transform(data)

在其他情况下,您可能面对高维数据集或特征多重共线性问题。此时可采用主成分分析(PCA)等技术,在保留大部分有效信息的同时降低维度。

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
transformed_features = pca.fit_transform(data)

特征工程确保您的数据以正确格式呈现,便于模型使用。特征工程技术众多,具体使用哪些高度取决于您的目标。

想了解更多有用技术,请查看这门特征工程课程。

在 Python 中清洗数据:最佳实践与技巧

数据清洗是任何数据分析或机器学习项目中的关键步骤。以下是一些有助于优化数据清洗流程的最佳实践:

原始数据单独存放

务必保留原始数据!

这是进行数据清洗时最重要的建议。将原始数据文件与清洗、处理后的版本分开保存。这样可确保始终有参考点,并在需要时轻松回退到原始数据。

我个人总是在做任何修改之前复制一份原始数据文件,并在文件名后加上“-RAW”后缀,以标识这是原件。

为数据清洗代码撰写文档

在代码中添加注释,解释每一步清洗操作的目的及所做的任何假设。

警惕意外后果

请确保数据清洗没有显著改变分布或引入任何无意的偏差。清洗后反复进行数据探索有助于确认方向正确。

保留数据清洗日志

如果清洗流程较长或是自动化的,建议维护一份单独的文档,记录每一步清洗的细节。

诸如日期、具体操作以及遇到的问题等细节,日后可能非常有用。

请将此文档存放在便于访问的位置,例如与数据或代码位于同一项目文件夹。在自动化、定期更新的管道中,考虑将日志作为数据清洗流程的自动组成部分,以便随时检查并确保一切顺利运行。

编写可复用函数

识别常见的数据清洗任务,并将其封装为可复用的函数。这样您就能在多个数据集上应用相同的清洗步骤。如果您需要映射公司特定缩写,这尤其有用。

使用 DataCamp 的数据清洗清单,确保不遗漏任何事项!

结语

数据清洗不仅不是枯燥的琐事,还是构成每个成功的数据分析与机器学习项目基石的关键步骤。通过确保数据准确、一致、可靠,您为做出明智决策和获得有意义的洞见打下坚实基础。

脏数据会导致错误结论和有缺陷的分析,其后果可能非常严重——无论是与朋友分糖果还是进行商务决策。将时间与精力投入到数据清洗上,长远来看将获得回报,确保您的分析值得信赖、结果可付诸行动。

想获取更多信息与交互式练习,请查看DataCamp 的数据清洗课程。若想动手实践,试试这份数据清洗跟练。

Amberle McKee

作者

Amberle McKee

我拥有博士学位,在生物研究环境中从事数据相关工作已有13年。我使用包括 Python、MATLAB 和 R 在内的多种编程语言开发软件。我热衷于与世界分享我对学习的热爱。

主题