0%

数据清洗之处理缺失值

丢弃缺失值 & 通过自动化工作流补全

这是来自 Kaggle Learn 的 Data Cleaning 课程。

在这个课程中,你将会学习如何处理一些常见的数据清洗问题,自己动手完成五个练习,它们都是一些真实的、杂乱的数据集。

初窥数据集

首先我们导入将要使用到的库和数据集。作为范例,这里使用到了美国足球比赛发生事件的数据集。

1
2
3
4
5
6
7
8
9
# 使用的模块
import pandas as pd
import numpy as np

# 读入所有数据
nfl_data = pd.read_csv("")

# 设置随机种子以供复现
np.random.seed(0)

当我们获得一个新的数据集的时候,我们需要做的第一件事情就是查看其中的一部分数据。这将有助于确定数据是否正确读入并且给我们一些进行下一步操作的启示。在这一案例中,缺失数据将会以NaN或者None的形式出现。

1
2
# 查看数据集的前五行
nfl_data.head()
Date GameID Drive qtr down time TimeUnder TimeSecs PlayTimeDiff SideofField yacEPA Home_WP_pre Away_WP_pre Home_WP_post Away_WP_post Win_Prob WPA airWPA
0 2009-09-10 2009091000 1 1 NaN 15:00 15 3600.0 0.0 TEN NaN 0.485675 0.514325 0.546433 0.453567 0.485675 0.060758 NaN
1 2009-09-10 2009091000 1 1 1.0 14:53 15 3593.0 7.0 PIT 1.146076 0.546433 0.453567 0.551088 0.448912 0.546433 0.004655 -0.032244
2 2009-09-10 2009091000 1 1 2.0 14:16 15 3556.0 37.0 PIT NaN 0.551088 0.448912 0.510793 0.489207 0.551088 -0.040295 NaN
3 2009-09-10 2009091000 1 1 3.0 13:35 14 3515.0 41.0 PIT -5.031425 0.510793 0.489207 0.461217 0.538783 0.510793 -0.049576 0.106663
4 2009-09-10 2009091000 1 1 4.0 13:27 14 3507.0 8.0 PIT NaN 0.461217 0.538783 0.558929 0.441071 0.461217 0.097712 NaN

啊啊啊我们已经可以看到缺失数据的存在了!

数据集中到底有多少缺失数据点呢?

下面我们看看每一列都有多少缺失值

1
2
3
4
5
# 得到每一行缺失值的个数
missing_values_count = nfl_data.isnull().sum()

# 前十列的缺失值个数
missing_values_count[0:10]
Column Name Missing value numbers
Date 0
GameID 0
Drive 0
qtr 0
down 61154
time 0
TimeUnder 224
TimeSecs 224
PlayTimeDiff 444
SideofField 528

看起来数据集中的缺失值还不少。此时计算数据的缺失百分比有助于我们俩了解问题的严重性。

1
2
3
4
5
6
7
8
9
10
# 总共有多少缺失值
total_cells = np.product(nfl_data.shape)
total_missing = missing_values_count.sum()

# 缺失数据所占百分比
percent_missing = (total_missing/total_cells)*100
print(percent_missing)

Out[1]:
24.87214126835169

哦偶,几乎四分之一的数据产生了缺失。那么下一步,我们就是要更仔细地查看一下每一列地缺失情况并且搞清楚究竟发生了什么状况。

搞清楚为什么数据会缺失

数据直觉(或者说是数据敏感度?)是我们在进入数据科学领域非常关键的一点,简单来说就是要真切的对手头的数据进行观察并且尝试搞清楚为什么它会这样子以及它会对我们的数据分析产生什么影响。这可能会是数据科学令人感到非常沮丧的一部分,特别是当你初入一个领域并且没有相关的经验的时候。当面对缺失值的时候,你需要利用你的直觉想清楚为什么这个值会缺失。其中有一个十分重要的问题,或许你可以常常反问自己。

Is this value missing because it wasn’t recorded or because it doesn’t exist?

这个值缺失究竟是因为它没有被记录还是因为它不存在?

如果一个数据缺失仅仅是因为它压根不存在,那么你根本不需要费劲去猜它原来究竟是什么,你可能只是想让它保持NaN就行了。如果一个数据缺失是因为它没有被记录,那么你可以根据这一列的其他数据尝试去猜它的值究竟是什么。

1
2
# 前十列缺失值个数
missing_values_count[0:10]

(undone)

丢弃缺失值

如果你很着急或者没有什么理由搞清楚这些缺失值产生的原因,那么你可以选择简单地把含有缺失值的行或者列直接删除。(贴士:我通常不建议在一些重要的项目上使用这种方法,往往花费时间去浏览你的数据并且将每一列中所含缺失值都看一遍以真正了解你的数据集)

1
2
# 删除所有带有缺失值的行(样本点)
nfl_data.dropna()
Date GameID Drive qtr down time TimeUnder TimeSecs PlayTimeDiff SideofField yacEPA Home_WP_pre Away_WP_pre Home_WP_post Away_WP_post Win_Prob WPA airWPA

好家伙,看起来我们直接将所有数据都删除了!这是因为我们的每一行数据中都至少含有一个缺失值。移除至少含一个缺失值的那些列或许更有可行性。

1
2
3
# 删除所有至少有一个缺失值的列
columns_with_na_dropped = nfl_data.dropna(axis=1)
columns_with_na_dropped.head()
Date GameID Drive qtr TimeUnder ydstogo ydsnet PlayAttempted Yards.Gained sp Timeout_Indicator Timeout_Team posteam_timeouts_pre HomeTimeouts_Remaining_Pre AwayTimeouts_Remaining_Pre HomeTimeouts_Remaining_Post AwayTimeouts_Remaining_Post ExPoint_Prob
0 2009-09-10 2009091000 1 1 15 0 0 1 39 0 0 None 3 3 3 3 3 0.0
1 2009-09-10 2009091000 1 1 15 10 5 1 5 0 0 None 3 3 3 3 3 0.0
2 2009-09-10 2009091000 1 1 15 5 2 1 -3 0 0 None 3 3 3 3 3 0.0
3 2009-09-10 2009091000 1 1 14 8 2 1 0 0 0 None 3 3 3 3 3 0.0
4 2009-09-10 2009091000 1 1 14 8 2 1 0 0 0 None 3 3 3 3 3 0.0
1
2
3
4
5
6
7
# 我们丢失了多少数据
print("Columns in orginal dataset: %d \n" % nfl_data.shape[1])
print("Columns with na's dropped: %d \n" % columns_with_na_dropped.shape[1])

Out[2]:
Columns in original dataset: 102
Columns with na's dropped: 41

这样子我们丢失了不少数据,但是至少我们成功从我们的数据中移除了所有NaN数据。

自动填充缺失值

另一种选择时尝试填补这些缺失值,在下一部分,我们只取该数据集的一小部分以方便我们将数据输出。

1
2
3
# 取NFL数据集中的一个小部分
subset_nfl_data = nfl_data.loc[:, 'EPA':'Season'].head()
subset_nfl_data
EPA airEPA yacEPA Home_WP_pre Away_WP_pre Home_WP_post Away_WP_post Win_Prob WPA airWPA yacWPA Season
0 2.014474 NaN NaN 0.485675 0.514325 0.546433 0.453567 0.485675 0.060758 NaN NaN 2009
1 0.077907 -1.068169 1.146076 0.546433 0.453567 0.551088 0.448912 0.546433 0.004655 -0.032244 0.036899 2009
2 -1.402760 NaN NaN 0.551088 0.448912 0.510793 0.489207 0.551088 -0.040295 NaN NaN 2009
3 -1.712583 3.318841 -5.031425 0.510793 0.489207 0.461217 0.538783 0.510793 -0.049576 0.106663 -0.156239 2009
4 2.097796 NaN NaN 0.461217 0.538783 0.558929 0.441071 0.461217 0.097712 NaN NaN 2009

我们可以使用pandasfillna()函数对数据框中的元素进行填充。

1
2
# 将所有NA值替换为0
subset_nfl_data.fillna(0)
EPA airEPA yacEPA Home_WP_pre Away_WP_pre Home_WP_post Away_WP_post Win_Prob WPA airWPA yacWPA Season
0 2.014474 0.000000 0.000000 0.485675 0.514325 0.546433 0.453567 0.485675 0.060758 0.000000 0.000000 2009
1 0.077907 -1.068169 1.146076 0.546433 0.453567 0.551088 0.448912 0.546433 0.004655 -0.032244 0.036899 2009
2 -1.402760 0.000000 0.000000 0.551088 0.448912 0.510793 0.489207 0.551088 -0.040295 0.000000 0.000000 2009
3 -1.712583 3.318841 -5.031425 0.510793 0.489207 0.461217 0.538783 0.510793 -0.049576 0.106663 -0.156239 2009
4 2.097796 0.000000 0.000000 0.461217 0.538783 0.558929 0.441071 0.461217 0.097712 0.000000 0.000000 2009
1
2
3
# 将所有 NA 替换为同一列中紧随其后的值,
# 然后将所有剩余的NA替换为 0
subset_nfl_data.fillna(method="bfill", axis=0).fillna(0)
EPA airEPA yacEPA Home_WP_pre Away_WP_pre Home_WP_post Away_WP_post Win_Prob WPA airWPA yacWPA Season
0 2.014474 -1.068169 1.146076 0.485675 0.514325 0.546433 0.453567 0.485675 0.060758 -0.032244 0.036899 2009
1 0.077907 -1.068169 1.146076 0.546433 0.453567 0.551088 0.448912 0.546433 0.004655 -0.032244 0.036899 2009
2 -1.402760 3.318841 -5.031425 0.551088 0.448912 0.510793 0.489207 0.551088 -0.040295 0.106663 -0.156239 2009
3 -1.712583 3.318841 -5.031425 0.510793 0.489207 0.461217 0.538783 0.510793 -0.049576 0.106663 -0.156239 2009
4 2.097796 0.000000 0.000000 0.461217 0.538783 0.558929 0.441071 0.461217 0.097712 0.000000 0.000000 2009

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from learntools.core import binder 
binder.bind(globals())
from learntools.data_cleaning.ex1 import *
print("Setup Complete!")

# Take a first look at the data

#modules we'll use
import pandas as pd
import numpy as np

# read in all our data
sf_permits = pd.read_csv("../input/building-permit-applications-data/Building_Permits.csv")

# set seed fot reproducibility
np.random.seed(0)

# check the head of the dataset
sf_permits.head()

# How many missing data points do we have?
missing_values_count = sf_permits.isnull().sum()
total_cells = np.product(sf_permits.shape)
total_missing = missing_values_count.sum()
percent_missing = (total_missing/total_cells)*100
print(percent_missing)

# Figure out why the data is missing

# drop missing values: rows
columns_with_na_dropped = sf_permits.dropna()
print(columns_with_na_dropped.shape[1])

# undone