带有实例的数据清理快速指南

126 阅读7分钟

数据清理是每个数据科学项目的关键部分,这一点毋庸置疑。但这里你应该知道的是:为适当的分析准备数据往往是费时和具有挑战性的。数据科学家往往会在这个层面的数据探索上花费大量的时间。此外,这个步骤有时需要领域知识。

下面是通常发生的情况:

数据科学家经常从不同的来源获得数据。有问题的数据可能是不一致的,肮脏的,或缺失的。这就是为什么数据清洗是如此重要。这时,数据科学家会做出如何处理此类问题的决定,并尽可能多地保留有价值的数据。

在这篇文章中,我介绍了一些每个数据科学家都需要知道的方便的数据清洗技术。

让我们开始进行数据清洗

我在本文中要使用的数据来自一家旅游公司,该公司提供从伦敦到新西兰和澳大利亚的旅行。

我们得到两个表:

  • 旅行者(用户)
  • 旅行(旅行记录)

首先,我们需要导入Pandas:

import pandas as pd

如果你想学习这个教程,请准备好这些数据:

# preparing data traveler = pd.DataFrame({'user_id': [136, 284, 101, 529, 800, 823], 'age': [None, 38, 30, 43, 49, 28], 'name': ["Ann", "Ben", "Tom", "Bianca", "Caroline", "Kate"]}) travel = pd.DataFrame({'user_id': [101, 284, 136, 800, 101, 800, 823, 529, 284], 'date_of_journey': ['2018-01-16', '2017-07-13','2019-10-10','2018/03/20', '2019-12-24', '2017-10-17','2016/11/02', '2019/09/14', '2019-08-07'],'duration': [10, 10,7,13,7,11,14, 8, 12], 'destination': ["New Zeland", "australia", "Australia", "New_Zealand", "Australia/","Australia", "New Zealand", "Australia", "New_zealand"], 'cost': [None, 2325.0, 1760.0, 2740.0, 4000.0, 2475.0, 3140.0, 1840.0, 2910.0], 'currency': [None, 'EUR', 'GBP', 'GBP', 'GBP','EUR', 'GBP', 'GBP', 'GBP']})

traveler.to_csv("traveler.csv", index=False) travel.to_csv("travel.csv", index=False)

现在我们已经得到了我们的数据,是时候做一些数据清理了。

1.合并两个CSV文件的数据

我们的第一步是合并两个CSV文件的数据。我们需要的数据被分离到两个表中,所以我们需要使用两个文件中的user_id列来合并它们。下面是你如何在Pandas中合并这些数据。

traveler = pd.read_csv("traveler.csv") travel = pd.read_csv("travel.csv")

data = pd.merge(travel, traveler, on='user_id') data

2.使数据保持一致

2a.日期

为了在时间上创建数据的可视化,我们需要对其进行转换,以获得列内相同的数据格式。在我们的例子中,一列包含了几种日期格式,所以我们需要使用infer_datetime_format,让Pandas尝试推断出合适的格式。请注意,如果用户对日期格式太有创意,该库可能无法正常工作。

data.date_of_journey = pd.to_datetime(data.date_of_journey, infer_datetime_format=True) data

2b.成本

当我们分析成本和货币列时,我们很容易注意到,成本是以两种货币给出的。我们希望我们的数据是一致的,所以把成本值改为(例如)英镑是一个好主意。

我们可以通过使用Pandas切片来做到这一点。首先,我们只需要选择成本值以货币 "EUR "表示的单元格,并更新这些单元格(乘以0.8)。在第二步中,我们将货币栏中的所有 "EUR "改为 "GBP"。

data.loc[data.currency == 'EUR', ['cost']] = data.cost\*0.8 data.currency.replace("EUR", "GBP", inplace=True) data

2c.目的地

正如我们所看到的,目的地列包含比预期更多的唯一值。它应该包括两个值,这就是为什么我们需要通过将每个字符串分别改为new_zealand或australia来解决这个问题。在第二个单元格中,我们有目的地列中出现的所有类别。

data.destination.describe()
# We need to get all compromised categories and try to unify them categories = data.destination.drop_duplicates() print(categories)
data.destination.loc[data.destination.str.lower().str.startswith('n', na=False)] = "new_zealand" data.destination.loc[data.destination.str.lower().str.startswith('a', na=False)] = "australia" data

3.删除列

如果有一些我们认为可以从分析中剔除的列,我们可以在这一点上删除它们。在我们的例子中,我们将删除货币,因为它对所有记录都是一样的。我们还将删除姓名列,因为我们的分析不需要这个信息。

data = data.drop(['currency', 'name'], axis=1) data

4.缺失值

我们可以在第一个单元格中检查是否有任何缺失值 data.isna()。

如果我们发现缺失值,我们可以执行以下两个动作之一:

  1. 如果我们想不出如何以一种不会产生误导的方式来填补缺失值,我们可以通过使用data.dropna()删除包含不完整数据的行。
  2. 如果我们有足够的知识来填补缺失值,我们肯定应该这样做。这里的想法是尽可能多地保留数据/行。要做到这一点,我们可以使用许多不同的策略,但这项任务也需要领域知识。例如,在只包含正值的那一列中,我们可以用(-1)来填补空值,以突出其差异。另一个解决方案是使用一些任意选择的值或计算值,如:平均值、最大值、最小值。
data.isna()

在我们的例子中,我们要用以下方法来填补缺失的值:

  • cost用 "new_zealand "的平均旅行费用。
  • 年龄,用从年龄列计算出来的平均值
values = {'cost': data.cost.where(data.destination=='new_zealand').mean(), 'age': round(data.age.mean())} print('values:',values) data = data.fillna(value=values, limit=1) data

5.值的映射

现在是时候把目标列的值映射到{'new_zealand':0, 'australia':1},这对机器来说比字符串更易读。

data.destination.replace(('new_zealand', 'australia'), (0,1), inplace=True) data

6.离群值

离群值是指与其他观测值明显不同的观测值,有时也称为异常观测值或异常现象。注意离群值的最简单的方法是通过绘制图表。为了做到这一点,我们将使用散点图boxplot

在这两张图中,我们可以看到一个观察值与其他观察值有明显的不同(用红色标记)。利用我们的业务知识,我们可以决定这样的观察是否是一个错误,或者也许我们可以以某种方式解释它,最终,它对我们有价值。例如,在分析健康数据时,离群值可以表明可能由疾病引起的异常情况。

在我们的情况下,假设这个异常值是由用户旅行的日期(从圣诞节到新年前夜)造成的,这可能导致旅行费用暴涨。我们可以决定删除它,因为这样的情况是不正常的,可能会产生误导。

为了检测异常值,我们还可以使用聚类,分析标准差,以及许多其他策略。

import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Rectangle plt.rcParams['figure.figsize'] = [10, 7]

x = data.duration y = data.cost z = data.destination

fig, ax = plt.subplots()

scatter = ax.scatter(x, y, c=z, s=np.pi\*100, marker='o', alpha=0.5)

\# produce a legend with the unique colors from the scatter legend = ax.legend(\*scatter.legend_elements(), loc=1, title="Destination") ax.add_artist(legend) legend.get_texts()[0].set_text('new_zealand') legend.get_texts()[1].set_text('australia')

plt.title('Travel costs vs travel duration for Australia and New Zealand') plt.xlabel('travel duration') plt.ylabel('travel costs') plt.show()
boxplot = data.boxplot(column=['cost'], by=['destination']) plt.xticks([1, 2], ['new_zealand', 'australia']);
data = data.drop(data.index[data.cost == data.cost.max()], axis=0) data

7.缩放

有时,缩放数据是有用的或必要的,因为许多分析方法对数据规模很敏感。但这是什么意思呢?缩放指的是改变特征的范围。例如,缩放是指我们转换数据以适应一个特定的范围,如[0-1]。

我们在这里可以使用几种缩放方法。我们的选择是Min-Max算法,它使用最小和最大值进行计算。

我们将尝试对成本列进行缩放。

from sklearn import preprocessing

scaler = preprocessing.MinMaxScaler() scaler.fit(data[['cost']]) print('Scaled cost value: \\n', scaler.transform(data[['cost']]))
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 6)) ax = plt.subplot(121) plt.scatter(data.duration, data.cost, s=np.pi\*100, marker='o', alpha=0.5) plt.title('Cost data') plt.xlabel('duration', multialignment='center') plt.ylabel('cost', multialignment='center') plt.grid(True)

plt.subplot(122) plt.scatter(data.duration, scaler.transform(data[['cost']]), s=np.pi\*100, marker='o', alpha=0.5) plt.title('Scaled cost data') plt.xlabel('duration', multialignment='center') plt.ylabel('cost', multialignment='center') plt.grid(True)

plt.show()

归一化

请记住,缩放与归一化不是一回事,即使有时这两个术语会相互混淆。正常化是一个更彻底的过程,它将数据分布的形状改变为钟形曲线(正态分布)。

我希望这篇文章能帮助你迈出数据清理的第一步,为进一步的分析打下基础。