Python中的缺失数据处理方法

在数据清洗的过程中,还有一个非常重要的问题就是处理缺失的数据,尤其是在我们拿到样本比较小的数据时,能够通过一些合理的方式去插补这些缺失的数据对我们接下来的分析尤为重要。这篇将会介绍一些处理数据缺失的处理方法和手段。

一般情况下,缺失的数据通常会以NA, nan, 0, .等来表示,造成缺失数据的原因一般也是两种,一个是人为错误,还有就是技术错误。在遍历缺失数据时,有一个非常好用的可视化库missingno。接下来会经常使用这个库进行可视化我们的缺失数据,以便更好的去理解数据缺失的原因:

1
2
3
4
5
#需要配合matplotlib来使用missingno库
import missingno as msno
import matplotlib.pyplot as plt
msno.matrix(df)
plt.show()

首先我们需要了解处理缺失数据的一个基本流程:

转化所有缺失值为空 –>分析缺失值的数量及类型–>合理删除或插补缺失数据 –>比较评估表现

这篇将会使用datacamp中的diabetes数据。这个链接下载下来的数据是一个xls文件,可能是文件类型出现了问题,可自行修改成csv后缀。其中我对原数据进行了一些修改从而更好的完成这篇文章的例子(该文使用的数据下载地址):点击下载

关于数据的解释可以在kaggle中找到。

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
import pandas as pd
import numpy as np
df = pd.read_csv('datasets/pima-indians-diabetes.csv')
df.head()
Out [1]:
Pregnant Glucose Diastolic_BP ... Diabetes_Pedigree Age Class
0 6 129.0 90.0 ... 0.582 60 0
1 12 92.0 62.0 ... 0.926 44 1
2 1 90.0 68.0 ... 1.138 36 0
3 1 109.0 60.0 ... 0.947 21 0
4 1 73.0 50.0 ... 0.248 21 0
df.info()
Out [2]:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 768 entries, 0 to 767
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Pregnant 768 non-null int64
1 Glucose 763 non-null float64
2 Diastolic_BP 733 non-null float64
3 Skin_Fold 768 non-null object
4 Serum_Insulin 394 non-null float64
5 BMI 768 non-null float64
6 Diabetes_Pedigree 768 non-null float64
7 Age 768 non-null int64
8 Class 768 non-null int64
dtypes: float64(5), int64(3), object(1)
memory usage: 54.1+ KB

通过.head()我们大致对数据有了一个了解,通过.info()我们可以判断出变量GlucoseDiastolic_BPSerum_Insulin这三个变量是有数据缺失的。除了这三个可以很直观的看出有缺失值的变量外,很多时候数据还会存在一些隐藏的缺失情况。我们观察发现,所有变量类型都是数字类型,只有Skin_Fold是一个对象(object)类型,虽然它在.info()中包含了768条完整的数据,但是这要引起我们的重视,所以要对其进行重点观察:

1
2
3
4
5
6
7
8
skin_fold_unique = df.Skin_Fold.unique()
np.sort(skin_fold_unique)
Out [3]:
array(['.', '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', '35', '36', '37', '38', '39', '40', '41',
'42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52',
'54', '56', '60', '63', '7', '8', '99'], dtype=object)

转化所有缺失值为空

通过.unique()Skin_Fold唯一值的选取和排序,我们看到Skin_Fold有的数据为”.”,我们知道”.”也是常见的一种缺失数据标记,所以我们需要对其进行处理。根据处理缺失数据的流程,第一步是把所有的缺失数据找出来并转化为空值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
df = pd.read_csv('datasets/pima-indians-diabetes.csv', na_values='.')
df.info()
Out [3]:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 768 entries, 0 to 767
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Pregnant 768 non-null int64
1 Glucose 763 non-null float64
2 Diastolic_BP 733 non-null float64
3 Skin_Fold 541 non-null float64
4 Serum_Insulin 394 non-null float64
5 BMI 768 non-null float64
6 Diabetes_Pedigree 768 non-null float64
7 Age 768 non-null int64
8 Class 768 non-null int64
dtypes: float64(6), int64(3)
memory usage: 54.1 KB

由于出现空值的Skin_Fold变量情况比较简单,我们可以只需在读取数据的时候在.read_csv()中加入na_value参数即可把所有”.”转化为nan

我们还可以使用.describe()函数去观察一下我们int和float类型的数据,确保没有异常数据出现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
df.describe()
Out [4]:
Pregnant Glucose Diastolic_BP Serum_Insulin BMI
count 768.000000 763.000000 733.000000 394.000000 768.000000
mean 3.845052 121.686763 72.405184 155.548223 31.992578
std 3.369578 30.535641 12.382158 118.775855 7.884160
min 0.000000 44.000000 24.000000 14.000000 0.000000
25% 1.000000 99.000000 64.000000 76.250000 27.300000
50% 3.000000 117.000000 72.000000 125.000000 32.000000
75% 6.000000 141.000000 80.000000 190.000000 36.600000
max 17.000000 199.000000 122.000000 846.000000 67.100000
Diabetes_Pedigree Age Class
count 768.000000 768.000000 768.000000
mean 0.471876 33.240885 0.348958
std 0.331329 11.760232 0.476951
min 0.078000 21.000000 0.000000
25% 0.243750 24.000000 0.000000
50% 0.372500 29.000000 0.000000
75% 0.626250 41.000000 1.000000
max 2.420000 81.000000 1.000000

通过观察我们发现,变量BMI有一些异常值的出现,BMI出现了最小值为0的情况,我们知道BMI是不可能为0的。所以我们需要对其进行处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df.BMI[df.BMI == 0]
df.BMI[df.BMI == 0] = np.nan
df.BMI[np.isnan(df.BMI)]
Out [5]:
170 NaN
171 NaN
759 NaN
760 NaN
761 NaN
762 NaN
763 NaN
764 NaN
765 NaN
766 NaN
767 NaN
Name: BMI, dtype: float64

分析缺失值的数量及类型

在合理的把缺失数据转化为Nan之后,我们在第二步需要分析缺失值的数量和类型。有两个函数可以筛选出缺失值,它们分别是.isnull().isna()

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
35
36
df_nullity = df.isnull()
df_nullity.head()
Out [6]:
Pregnant Glucose Diastolic_BP ... Diabetes_Pedigree Age Class
0 False False False ... False False False
1 False False False ... False False False
2 False False False ... False False False
3 False False False ... False False False
4 False False False ... False False False
#统计总的缺失值数量
df_nullity.sum()
Out [7]:
Pregnant 0
Glucose 5
Diastolic_BP 35
Skin_Fold 227
Serum_Insulin 374
BMI 11
Diabetes_Pedigree 0
Age 0
Class 0
dtype: int64
#缺失值占比
df_nullity.mean() * 100
Out [8]:
Pregnant 0.000000
Glucose 0.651042
Diastolic_BP 4.557292
Skin_Fold 29.557292
Serum_Insulin 48.697917
BMI 1.432292
Diabetes_Pedigree 0.000000
Age 0.000000
Class 0.000000
dtype: float64

接下来我们将会使用在文章最开始提到的缺失数据可视化包missingno,来对缺失的数据进行更加直观的可视化处理:

1
2
3
4
import missingno as msno
import matplotlib.pyplot as plt
msno.bar(df)
msno.matrix(df)



缺失值是随机发生的吗?是如何发生的呢?不同的变量缺失值之间是否存在一些相关性?

一般来讲,发生缺失值有三种情况:

  1. 完全随机缺失(MCAR):缺失值与其他变量没有任何关联;
  2. 随机缺失(MAR):缺失值与其他变量但不是与其他缺失变量纯在相关性;
  3. 非随机缺失(MNAR):缺失值之间存在相关性。

理解缺失值的存在原因以及它们之间的相关性对于对缺失值的处理至关重要。我们通过对df的matrix观察可以发现,Glucose变量和BMI变量为完全随机缺失,Diastolic_BP变量为随机缺失,Skin_FoldSerum_Insulin两个变量的缺失值存在相关行,所以它们是非随机缺失。

为了更加直观的观察缺失值之间的关系,我们还可以使用missingno包中的热力图及树形决策图来进行可视化:

1
2
msno.heatmap(df)
msno.dendrogram(df)



通过这两张图,我们可以非常直观的看到Skin_FoldSerum_Insulin两个变量的缺失值存在强相关性。

合理删除或插补缺失数据

合理删除缺失数据

在分析完缺失值的情况之后,我们可以根据实际情况对缺失数据进行一些处理。对于缺失数据,一般的处理方式有两种:1. 删除缺失数据; 2. 插补缺失数据。

对于完全随机缺失(MCAR)的情况一般处理方法就是直接删除,其中删除方式有两种:1. 成对删除(pairwise); 2. 列表删除(listwise)。

两种删除方式各自有其优点和缺点。一般情况推荐使用成对删除方式,但是当数据确实量较小的时候,为了得到更完整的分析结果,可以考虑使用列表删除。

1
2
3
4
5
6
df['Glucose'].isnull().sum()
Out [9]:
5
df['BMI'].isnull().sum()
Out [9]:
11

Glucose变量和BMI由于是完全随机缺失且缺失数据量较少,所以我们这边对它们采用列表删除整行数据:

1
2
df.dropna(subset=["Glucose", 'BMI'], how='any', inplace=True)
msno.matrix(df)

再次绘制matrix观察对缺失值的删除情况,我们发现Glucose变量和BMI的缺失值已经被完全删除了,这里再次强调一下,仅仅删除缺失数据当我们可以确定数据是完全随机缺失的时候。




## 一般插补方法

在对完全随机缺失的数据进行删除操作之后,我们需要对那些因为其他原因缺失的数据进行插补操作。目前我们还有以下变量存在缺失数据:

Diastolic_BP

Skin_Fold

Serum_Insulin

缺失数据插补一般有两种方式,一种为一般方式还有一种为高级方式。一般方式通常是为缺失数据插补平均数、中位数、众数或者常量,高级插补方式中需要使用到机器学习的一些算法来进行插补,比如KNN,MICE等。使用哪种方式要根据不同的情况进行合理的选择,需要对数据进行合理的插补又不能过度拟合模型。一般方式插补数据需要用到一个python包sklearn:

1
from sklearn.impute import SimpleImputer


平均数插补:

1
2
3
df_mean = df.copy(deep=True)
mean_imputer = SimpleImputer(strategy='mean')
df_mean.loc[:, :] = mean_imputer.fit_transform(df_mean)


中位数插补:

1
2
3
df_median = df.copy(deep=True)
median_imputer = SimpleImputer(strategy='median')
df_median.loc[:, :] = mean_imputer.fit_transform(df_median)


众数插补:

1
2
3
df_mode = df.copy(deep=True)
mode_imputer = SimpleImputer(strategy='mode')
df_mode.loc[:, :] = mean_imputer.fit_transform(df_mode)


常量插补:

1
2
3
df_constant = df.copy(deep=True)
constant_imputer = SimpleImputer(strategy='constant', fill_value=0)
df_constant.loc[:, :] = constant_imputer.fit_transform(df_constant)


可视化一下我们的插补情况:

1
2
3
nullity = df['Serum_Insulin'].isnull()+df['Glucose'].isnull()
df_mean.plot(x='Serum_Insulin', y='Glucose', kind='scatter', alpha=0.5,
c=nullity, cmap='rainbow', title='Mean Imputation')




Serum_Insulin变量和Glucose变量从图中的紫色点可以看出存在一定的正相关性,但是插补的数据(红色点)并没有呈现应有的相关性,所以该插补方式可能不是特别合理。

我们观察一下另外其他的几种插补情况:

1
2
3
4
5
6
7
8
9
10
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 10))
nullity = df['Serum_Insulin'].isnull()+df['Glucose'].isnull()
imputations = {'Mean Imputation': df_mean,
'Median Imputation': df_median,
'Most Frequent Imputation': df_mode,
'Constant Imputation': df_constant}
for ax, df_key in zip(axes.flatten(), imputations):
imputations[df_key].plot(x='Serum_Insulin', y='Glucose', kind='scatter',
alpha=0.5, c=nullity, cmap='rainbow', ax=ax,
colorbar=False, title=df_key)



出现了与mean插补方式类似的情况。为了更好的提升插补效果,我们还可以使用更高级的插补方法,例如KNN或者MICE。

## 高级插补方法

KNN是K-Nearest Neighbor的缩写,其运行基本原理是选择缺失的数据点最接近的K个数据点的完整数据,然后取这K个点的平均数,并用这个平均数插补该缺失数据。KNN百度百科。下图展示了KNN的基本原理,其中“五角星”所表示的就是要估算的值:



KNN属于机器学习的算法,在python中使用KNN插补需要安装fancyimpute库。根据不同的操作环境,安装方式略有不同。如果你使用的是Windows操作系统,在安装的时候遇到了困难可以参考这篇文章。当然,这篇文章里的问题可能只是众多问题中的一小部分,所以遇到其它问题时,可以去寻求更多的帮助。

下面是使用KNN插补方法的演示及其插补效果可视化的情况:

1
2
3
4
5
6
7
8
9
from fancyimpute import KNN
knn_imputer = KNN()
df_knn = df.copy(deep=True)
df_knn.loc[:, :] = knn_imputer.fit_transform(df_knn)
nullity = df['Serum_Insulin'].isnull()+df['Glucose'].isnull()
df_knn.plot(x='Serum_Insulin', y='Glucose', kind='scatter', alpha=0.5,
c=nullity, cmap='rainbow', title='KNN Imputation')


可以看出,KNN插补提升了对缺失数据的插补效果,体现出了相应的相关性。

除了KNN插补方式,还有一种常见的高级插补方式MICE,全称Multiple Imputations by Chained Equations 。与KNN不同的是,MICE使用的是多重回归的方式对缺失数据进行插补,对随机样本数据进行多重回归之后,然后取回归值得平均数对缺失值插补,其使用方式与之前的插补方式大同小异,除了需要多导入一个IterativeImputer :

1
2
3
4
5
6
7
8
9
from fancyimpute import IterativeImputer
MICE_imputer = IterativeImputer()
df_MICE = df.copy(deep=True)
df_MICE.loc[:, :] = MICE_imputer.fit_transform(df_MICE)
nullity = df['Serum_Insulin'].isnull()+df['Glucose'].isnull()
df_MICE.plot(x='Serum_Insulin', y='Glucose', kind='scatter', alpha=0.5,
c=nullity, cmap='rainbow', title='MICE Imputation


这么多插补方法,该使用哪种?哪种更好呢?其实对于这个问题并没有标准答案,我们需要根据实际情况来选择最合适的插补方法。这时,我们就进入了处理缺失值的最后一个步骤,比较和评估插补缺失数据后的表现。

比较评估表现

数据插补的终极目的是为了提升模型的表现,所以在插补缺失数据时这一点是首要考虑的。一般来说使用机器学习模型来插补的数据表现可能会更好,我们需要选择表现最好的插补情况。在评估表现的过程中,使用密度图(density plot)可以很直观的看到数据的分布情况,并且还是一个很好的检查插补偏差的方式。接下来,我们将使用线性模型来验证以下插补后的数据框df与去除所有缺失值的数据框df_cc各自的表现:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import statsmodels.api as sm
#去除df中所有的缺失值,获得完整的数据框df_cc
df_cc = df.dropna(how='any')
#取出所有除'Class'外的变量
X = sm.add_constant(df_cc.iloc[:, :-1])
#取出'Class'变量
y = df_cc['Class']
#线性ordinary least square拟合模型
lm = sm.OLS(y, X).fit()
print(lm.summary())
Out [10]:
OLS Regression Results
==============================================================================
Dep. Variable: Class R-squared: 0.346
Model: OLS Adj. R-squared: 0.332
Method: Least Squares F-statistic: 25.30
Date: Wed, 03 Jun 2020 Prob (F-statistic): 2.65e-31
Time: 16:37:25 Log-Likelihood: -177.76
No. Observations: 392 AIC: 373.5
Df Residuals: 383 BIC: 409.3
Df Model: 8
Covariance Type: nonrobust
=====================================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------------
const -1.1027 0.144 -7.681 0.000 -1.385 -0.820
Pregnant 0.0130 0.008 1.549 0.122 -0.003 0.029
Glucose 0.0064 0.001 7.855 0.000 0.005 0.008
Diastolic_BP 5.465e-05 0.002 0.032 0.975 -0.003 0.003
Skin_Fold 0.0017 0.003 0.665 0.506 -0.003 0.007
Serum_Insulin -0.0001 0.000 -0.603 0.547 -0.001 0.000
BMI 0.0093 0.004 2.391 0.017 0.002 0.017
Diabetes_Pedigree 0.1572 0.058 2.708 0.007 0.043 0.271
Age 0.0059 0.003 2.109 0.036 0.000 0.011
==============================================================================
Omnibus: 9.511 Durbin-Watson: 2.148
Prob(Omnibus): 0.009 Jarque-Bera (JB): 9.387
Skew: 0.344 Prob(JB): 0.00916
Kurtosis: 2.682 Cond. No. 1.77e+03
==============================================================================
Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.77e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
print('\nAdjusted R-squared: ', lm.rsquared_adj)
Out [11]:
Adjusted R-squared: 0.33210805003287613
print('\nCoefficcients:\n', lm.params)
Out [12]:
Coefficcients:
const -1.102677
Pregnant 0.012953
Glucose 0.006409
Diastolic_BP 0.000055
Skin_Fold 0.001678
Serum_Insulin -0.000123
BMI 0.009325
Diabetes_Pedigree 0.157192
Age 0.005878
dtype: float64

接下来我们比较以下采用不同插补方式过后模型的表现,首先我们选用adjust R-square来评估平均数插补、KNN插补和MICE插补:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 平均数插补
X = sm.add_constant(df_mean.iloc[:, :-1])
y = df['Class']
lm_mean = sm.OLS(y, X).fit()
# KNN插补
X = sm.add_constant(df_knn.iloc[:, :-1])
lm_KNN = sm.OLS(y, X).fit()
# MICE插补
X = sm.add_constant(df_MICE.iloc[:, :-1])
lm_MICE = sm.OLS(y, X).fit()
r_squared = pd.DataFrame({'Complete Case': lm.rsquared_adj,
'Mean Imputation': lm_mean.rsquared_adj,
'KNN Imputation': lm_KNN.rsquared_adj,
'MICE Imputation': lm_MICE.rsquared_adj},
index=['Adj. R-squared'])
print(r_squared)
Out [13]:
Complete Case Mean Imputation KNN Imputation MICE Imputation
const -1.102677 -1.012616 -1.021117 -1.025887
Pregnant 0.012953 0.019565 0.019141 0.019390
Glucose 0.006409 0.006501 0.006640 0.006643
Diastolic_BP 0.000055 -0.001212 -0.001372 -0.001268
Skin_Fold 0.001678 0.000100 0.001426 0.000607
Serum_Insulin -0.000123 -0.000100 -0.000144 -0.000122
BMI 0.009325 0.014151 0.013240 0.013806
Diabetes_Pedigree 0.157192 0.137044 0.137003 0.136800
Age 0.005878 0.002161 0.002236 0.002174
coeff = pd.DataFrame({'Complete Case': lm.params,
'Mean Imputation': lm_mean.params,
'KNN Imputation': lm_KNN.params,
'MICE Imputation': lm_MICE.params})
print(coeff)
Out [14]:
Complete Case Mean Imputation KNN Imputation MICE Imputation
Adj. R-squared 0.332108 0.312339 0.313224 0.31249
r_squares = {'Mean Imputation': lm_mean.rsquared_adj,
'KNN Imputation': lm_KNN.rsquared_adj,
'MICE Imputation': lm_MICE.rsquared_adj}
# 选取最佳R-square
best_imputation = max(r_squares, key=r_squares.get)
print("最佳插补方式: ", best_imputation)
Out [15]:
最佳插补方式: KNN Imputation

我们根据adjust R-square得到的结论是使用KNN插补方法获得的模型拟合最佳。同时我们还可以通过绘制密度图来观察插补后的数据与去除所有缺失数据的完整数据框的分布,这样可以直观的看到哪种插补方式的分布与完整数据框的分布最接近,这样可以避免我们选取的插补方式存在较大的偏差:

1
2
3
4
5
6
7
8
df_cc['Skin_Fold'].plot(kind='kde', c='red', linewidth=3)
df_mean['Skin_Fold'].plot(kind='kde')
df_knn['Skin_Fold'].plot(kind='kde')
df_MICE['Skin_Fold'].plot(kind='kde')
labels = ['Baseline (Complete Case)', 'Mean Imputation', 'KNN Imputation', 'MICE Imputation']
plt.legend(labels)
plt.xlabel('Skin Fold')
plt.show()


通过密度图,可以看出使用平均数插补(mean imputation)的结果蓝线比完整数据框Baseline红线的偏差较大,KNN和MICE插补与原完整数据较为接近,所以这里我们就可以淘汰平均数插补了。结合之前的adjust R-square的结果,KNN可能是最佳的插补方式。

借此,我们就完成了对缺失数据处理的整个基本流程。

插补分类变量中的缺失值

有些时候,我们的数据中还有可能出现一些分类变量数据缺失的现象,我们需要对其进行插补。对分类变量中的缺失数据进行插补更加复杂,因为一般分类数据通常是字符串类型。所以一般我们需要先对其进行编码,比如用0,1代表不同的分类,然后再用一些技巧对缺失数据进行插补。这个例子我不打算引入新的dataset,所以就直接修改一下原本的diabetes的‘Class’列,并用它来进行插补演示:

1
2
3
4
5
6
7
8
import pandas as pd
import numpy as np
df = pd.read_csv('datasets/pima-indians-diabetes.csv', na_values='.')
#分别将'Class'为1的和为0的标记为患病和健康,并创建新的列'outcome'
df.loc[df['Class'] == 1, 'Outcome'] = 'diabetes'
df.loc[df['Class'] == 0, 'Outcome'] = 'healthy'
#将前50个Outcome设为缺失数据
df.loc[0:49,'outcome'] = np.nan

我们先看一下被处理过的数据长什么样子:



然后我们对这一列的缺失值进行插补:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sklearn.preprocessing import OrdinalEncoder
#创建编码器
enc = OrdinalEncoder()
#获取outcome中的非缺失数据
outcome = df['Outcome']
outcome_not_null = outcome[outcome.notnull()]
#重塑outcome_not_null数据
reshaped_vals = outcome_not_null.values.reshape(-1, 1)
#编码数据
encoded_vals = enc.fit_transform(reshaped_vals)
#将编码后的数据放回替换之前的Outcome
df.loc[outcome.notnull(), 'Outcome'] = np.squeeze(encoded_vals)
#拷贝原df到df_KNN_imputed
df_KNN_imputed = df.copy(deep=True)
#创建KNN插补器
KNN_imputer = KNN()
#插补缺失数据并将其保留为整数
df_KNN_imputed.loc[:,:] = np.round(KNN_imputer.fit_transform(df_KNN_imputed))
#重塑数据
reshaped = df_KNN_imputed['Outcome'].values.reshape(-1, 1)
#将Outcome中的编码后的数据还原为分类数据
df_KNN_imputed['Outcome'] = enc.inverse_transform(reshaped)

插补之后的数据如下图所示:



由于我们的Class变量其实是完整的,我们在例子最开始在Outcome中特意删除了50个数据,把这50个数据变为了缺失数据,所以当我们使用KNN插补之后,我们可以把插补后的结果跟原’Class’的真实数据进行一下比较,看下插补的效果如何:

1
2
3
4
5
6
7
#打印一下插补数据跟'Class'真实数据不相符的个数
print(len(df_KNN_imputed[0:50][(df_KNN_imputed['Class'] == 1) & \
(df_KNN_imputed['Outcome'] == 'healthy')]) + \
len(df_KNN_imputed[0:50][(df_KNN_imputed['Class'] == 0) & \
(df_KNN_imputed['Outcome'] == 'diabetes')]))
Out [16]:
9

准确率大概有80%以上,效果可以说还算不错。

0%