【缺失值处理】拉格朗日插值法—随机森林算法填充—sklearn填充(均值/众数/中位数)

在进行缺失值填充之前,要先对缺失的变量进行业务上的了解,即变量的含义、获取方式、计算逻辑,以便知道该变量为什么会出现缺失值、缺失值代表什么含义。

缺失值的处理

对于缺失值的处理,从总体上来说分为删除存在缺失值的个案缺失值插补

  • 不处理
  • 删除存在缺失值的样本(或特征)
  • 缺失值插补

这里可以阅读以下《美团机器学习实战》中关于缺失值的说明:
在这里插入图片描述

一般主观数据不推荐插补的方法,插补主要是针对客观数据,它的可靠性有保证。

附上常用的插补方法
在这里插入图片描述
在这里插入图片描述

准备数据

使用泰坦尼克数据集:

import seaborn as sns
# 导出泰坦尼克数据集
df = sns.load_dataset('titanic')
df.shape  # 891, 15)
df.info()

在这里插入图片描述
数据字段含义网上有解释

可能会存在样本重复,即有超过一行的样本所显示的所有特征都一样

#去除重复值
df.drop_duplicates(inplace=True)
df.shape

#恢复索引
df.index = range(df.shape[0])

缺失情况

df.isnull().sum()
'''
survived         0
pclass           0
sex              0
age            177
sibsp            0
parch            0
fare             0
embarked         2
class            0
who              0
adult_male       0
deck           688
embark_town      2
alive            0
alone            0
dtype: int64
'''

缺失占比

df.isna().mean()
'''
survived       0.000000
pclass         0.000000
sex            0.000000
age            0.198653
sibsp          0.000000
parch          0.000000
fare           0.000000
embarked       0.002245
class          0.000000
who            0.000000
adult_male     0.000000
deck           0.772166
embark_town    0.002245
alive          0.000000
alone          0.000000
dtype: float64
'''

1 sklearn填充

在sklearn当中,使用 impute.SimpleImputerr 来处理缺失值,参数为

sklearn.impute.SimpleImputer (
missing_values=nan, 
strategy=’mean’, 
fill_value=None, 
verbose=0,
copy=True)
参数含义
missing_values数据中缺失值是什么,默认np.nan
strategy填补缺失值的策略,默认均值
输入“mean”使用均值填补(仅对数值型特征可用)
输入“median”使用中位数填补(仅对数值型特征可用)
输入“most_frequent”使用众数填补(对数值型和字符型特征都可用)
输入“constant”表示请参考参数“fill_value”中的值(对数值型和字符型特征都可用)
fill_value当参数strategy为“constant”的时候可用,可输入字符串或数字表示要填充的值,常用0
copy默认为True,将创建特征矩阵的副本,反之则会将缺失值填补到原本的特征矩阵中。

传统地,

  • 如果是分类型特征,采用众数进行填补。
  • 如果是连续型特征,采用均值进行填补。

还要考虑

  • 均值一般适用于近似正态分布数据,观测值较为均匀散布均值周围;
  • 中位数一般适用于偏态分布或者有离群点数据,中位数是更好地代表数据中心趋势;
  • 众数一般用于类别变量,无大小、先后顺序之分。

在现实工作时,使用最多的是易于理解的均值或者中位数。

(1)使用均值进行填补(连续型特征)

import numpy as np
import pandas as pd

# 去掉标签
X_missing = df.drop(['survived'],axis=1)

# 查看缺失情况
missing = X_missing.isna().sum()
missing = pd.DataFrame(data={'特征': missing.index,'缺失值个数':missing.values})
#通过~取反,选取不包含数字0的行
missing = missing[~missing['缺失值个数'].isin([0])]
# 缺失比例
missing['缺失比例'] =  missing['缺失值个数']/X_missing.shape[0]
missing
'''
         特征  缺失值个数  缺失比例
2           age    177   0.198653
6      embarked      2   0.002245
10         deck    688   0.772166
11  embark_town      2   0.002245
'''
# 排序
missing.sort_values(by='缺失比例',ascending=False)

#柱形图可视化
import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
fig = plt.figure(figsize=(18,6))
plt.bar(np.arange(missing.shape[0]), list(missing['缺失比例'].values), align = 'center',color=['red','green','yellow','steelblue'])
plt.title('Histogram of missing value of variables')
plt.xlabel('variables names')
plt.ylabel('missing rate')
# 添加x轴标签,并旋转90度
plt.xticks(np.arange(missing.shape[0]),list(missing['特征']))
pl.xticks(rotation=90)
# 添加数值显示
for x,y in enumerate(list(missing['缺失比例'].values)):
    plt.text(x,y+0.12,'{:.2%}'.format(y),ha='center',rotation=90)    
plt.ylim([0,1.2])
plt.show()

在这里插入图片描述

X_missing[missing['特征']].info()
'''
RangeIndex: 891 entries, 0 to 890
Data columns (total 4 columns):
 #   Column       Non-Null Count  Dtype   
---  ------       --------------  -----   
 0   age          714 non-null    float64 
 1   embarked     889 non-null    object  
 2   deck         203 non-null    category
 3   embark_town  889 non-null    object 
'''

X_missing['embarked'].unique() # array(['S', 'C', 'Q', nan], dtype=object)
X_missing['deck'].unique() # [NaN, C, E, G, D, A, B, F]
X_missing['embark_town'].unique() # array(['Southampton', 'Cherbourg', 'Queenstown', nan], dtype=object)

deck 列缺失比例达到77%,这里考虑直接删除该列

X_missing.drop(['deck'],axis=1,inplace=True)
# 把各个类型  分离出来   方便采用不同的填补方法
# 数值型(即数据类型为int、float的列)
X_missing[missing['特征']].select_dtypes(include='number').columns
# 除数据类型为number外其他的列
X_missing[missing['特征']].select_dtypes(exclude='number').columns
# 
X_missing[missing['特征']].select_dtypes(include='object').columns
X_missing[missing['特征']].select_dtypes(include=['int', 'datetime', 'object']).columns

使用均值进行填补,仅对数值型特征可用,这里对age进行填补

#填补年龄
Age = X_missing.loc[:,"age"].values.reshape(-1,1) #sklearn当中特征矩阵必须是二维

from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer() #实例化,默认均值填补
#imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')

imp_mean = imp_mean.fit_transform(Age) #fit_transform一步完成调取结果
X_missing.loc[:,"age"] = imp_mean
X_missing.info()
'''
 #   Column       Non-Null Count  Dtype   
---  ------       --------------  -----   
 0   pclass       891 non-null    int64   
 1   sex          891 non-null    object  
 2   age          891 non-null    float64 
 3   sibsp        891 non-null    int64   
 4   parch        891 non-null    int64   
 5   fare         891 non-null    float64 
 6   embarked     889 non-null    object  
 7   class        891 non-null    category
 8   who          891 non-null    object  
 9   adult_male   891 non-null    bool    
 10  embark_town  889 non-null    object  
 11  alive        891 non-null    object  
 12  alone        891 non-null    bool 
'''

(2)使用中位数、0进行填补(连续型特征)

也可以用中位数填补或用0填补

imp_median = SimpleImputer() #实例化
imp_median = SimpleImputer(strategy="median") #用中位数填补
imp_median = imp_median.fit_transform(Age)

imp_0 = SimpleImputer() #实例化
imp_0 = SimpleImputer(strategy="constant",fill_value=0) #用0填补
imp_0 = imp_0.fit_transform(Age)

最需要注意的一点是SimpleImputer传入的参数至少要是二维,如果将直接索引出的一列特征传入的话,是会发生报错的,所以必须利用reshape()将一维转化为二维。之后的操作就是先实例化、然后训练模型,最后用填充后的数据覆盖之前的数据。


(3)使用众数进行填补(离散型特征)

使用众数填补embarked、embark_town

# 查看缺失情况
missing = X_missing.isna().sum()
missing = pd.DataFrame(data={'特征': missing.index,'缺失值个数':missing.values})
#通过~取反,选取不包含数字0的行
missing = missing[~missing['缺失值个数'].isin([0])]
# 缺失比例
missing['缺失比例'] =  missing['缺失值个数']/X_missing.shape[0]
missing
'''
         特征  缺失值个数  缺失比例
6      embarked      2  0.002245
10  embark_town      2  0.002245
'''

X_missing['embarked'].unique() # array(['S', 'C', 'Q', nan], dtype=object)
X_missing['embark_town'].unique() # array(['Southampton', 'Cherbourg', 'Queenstown', nan], dtype=object)
imp_most = SimpleImputer() #实例化
imp_most = SimpleImputer(strategy = "most_frequent")
X_missing[missing['特征']] = imp_most.fit_transform(X_missing[missing['特征']])
X_missing.isnull().sum()
'''
pclass         0
sex            0
age            0
sibsp          0
parch          0
fare           0
embarked       0
class          0
who            0
adult_male     0
embark_town    0
alive          0
alone          0
dtype: int64
'''

SimpleImputer优于fillna()之处在于前者可以一行语句指定填充值的形式,而利用fillna()需要多行重复语句才能实现,或者需要提前计算某列的平均值、中位数或者众数。

比如,用fillna()填充

# 用中位数填充
X_missing.loc[:,"age"] = X_missing.loc[:,"age"].fillna(X_missing.loc[:,"age"].median())

或者更一般的
# 均值填充
data['col'] = data['col'].fillna(data['col'].means())
# 中位数填充
data['col'] = data['col'].fillna(data['col'].median())
# 众数填充
data['col'] = data['col'].fillna(stats.mode(data['col'])[0][0])

(4)KNN填补

官网:https://scikit-learn.org/stable/modules/generated/sklearn.impute.KNNImputer.html

使用sklearn的impute 模块中的KNNImputer 函数。KNNImputer通过欧几里德距离矩阵寻找最近邻,帮助估算观测中出现的缺失值。

每个样本的缺失值都是使用n_neighbors训练集中找到的最近邻的平均值估算的 。如果两个都不缺失的特征都接近,则两个样本接近。

class sklearn.impute.KNNImputer(*, 
missing_values=nan, 
n_neighbors=5, 
weights='uniform', 
metric='nan_euclidean', 
copy=True, 
add_indicator=False)

在这里插入图片描述

import numpy as np
from sklearn.impute import KNNImputer
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
imputer = KNNImputer(n_neighbors=2)
imputer.fit_transform(X)
'''
array([[1. , 2. , 4. ],
       [3. , 4. , 3. ],
       [5.5, 6. , 5. ],
       [8. , 8. , 7. ]])
'''
# 对于离散数据
data = [['a1',12,'M'],['a2',10,np.nan],['a3',14,'F'],['a4',8,'M'],['a5',7,'F']
        ,['a6',20,'F'],['a7',7,'M']]
df = pd.DataFrame(data,columns=['name','age','gender'])
'''
  name  age gender
0   a1   12      M
1   a2   10    NaN
2   a3   14      F
3   a4    8      M
4   a5    7      F
5   a6   20      F
6   a7    7      M
'''
# 将离散数据转化为数值型
df['gender_map'] = df.gender.map({'F':1,'M':0})
df 
'''
  name  age gender  gender_map
0   a1   12      M         0.0
1   a2   10    NaN         NaN
2   a3   14      F         1.0
3   a4    8      M         0.0
4   a5    7      F         1.0
5   a6   20      F         1.0
6   a7    7      M         0.0
'''
df_fill = df[['age','gender_map']]
'''
    age  gender_map
0   12         0.0
1   10         NaN
2   14         1.0
3    8         0.0
4    7         1.0
5   20         1.0
6    7         0.0
'''
# 填补
from sklearn.impute import KNNImputer
impute = KNNImputer(n_neighbors = 2)
df_filled = impute.fit_transform(df_fill)
df_filled
'''
array([[12.,  0.],
       [10.,  0.],
       [14.,  1.],
       [ 8.,  0.],
       [ 7.,  1.],
       [20.,  1.],
       [ 7.,  0.]])
'''

另外,还有其他的实现KNN填充方式

利用knn算法填充,其实是把目标列当做目标标量,利用非缺失的数据进行knn算法拟合,最后对目标列缺失进行预测。(对于连续特征一般是加权平均,对于离散特征一般是加权投票)

fancyimpute 类

from fancyimpute import KNN
fill_knn = KNN(k=3).fit_transform(data)
data = pd.DataFrame(fill_knn)

sklearn类

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor

def knn_filled_func(x_train, y_train, test, k = 3, dispersed = True):
    # params: x_train 为目标列不含缺失值的数据(不包括目标列)
    # params: y_train 为不含缺失值的目标列
    # params: test 为目标列为缺失值的数据(不包括目标列)
    if dispersed:
        knn= KNeighborsClassifier(n_neighbors = k, weights = "distance")
    else:
        knn= KNeighborsRegressor(n_neighbors = k, weights = "distance")

    knn.fit(x_train, y_train)
    return test.index, knn.predict(test)

2 随机森林回归进行填补

现实中,很少用算法(如随机森林)填补缺失值,因为算法填补很慢,不如均值或者0。另外,算法插补方法,领导不一定能理解,造成不必要的麻烦。

具体的

在现实中,其实非常少用到算法来进行填补,有以下几个理由:

  1. 算法是黑箱解释性不强。如果你是一个数据挖掘工程师,你使用算法来填补缺失值后,你不懂机器学习的老板或者同事问你的缺失值是怎么来的,你可能需要从头到尾帮他/她把随机森林解释一遍,这种效率过低的事情是不可能做的,而许多老板和上级不会接受他们无法理解的东西。
  2. 算法填补太过缓慢,运行–次森林需要有至少100棵树才能够基本保证森林的稳定性,而填补一个列就需要很长的时间。在我们并不知道森林的填补结果是好是坏的情况下,填补一个很大的数据集风险非常高,有可能需要跑好几个小时,但填补出来的结果却不怎么优秀,这明显是–个低效的方法。

但这种方法还是值得学习的

随机森林插补法原理

对于一个有n个特征的数据来说,其中特征T有缺失值,我们就把特征T当作标签,其他的

n-1个特征 + 原本的标签 = 新的特征矩阵

那对于T来说,它没有缺失的部分,就是我们的 Y_train,这部分数据既有标签也有特征,而它缺失的部分,只有特征没有标签,就是我们需要预测的部分。

特征T不缺失的值对应的其他n-1个特征 + 本来的标签:X_train
特征T不缺失的值:Y_train

特征T缺失的值对应的其他n-1个特征 + 本来的标签:X_test
特征T缺失的值:未知,我们需要预测的 Y_test

那如果数据中除了特征T之外,其他特征也有缺失值怎么办?

答案是遍历所有的特征,从缺失最少的开始进行填补(因为填补缺失最少的特征所需要的准确信息最少)。填补一个特征时,先将其他特征的缺失值用0代替,每完成一次回归预测,就将预测值放到原本的特征矩阵中,再继续填补下一个特征。每一次填补完毕,有缺失值的特征会减少一个,所以每次循环后,需要用0来填补的特征就越来越少。当进行到最后一个特征时(这个特征应该是所有特征中缺失值最多的),已经没有任何的其他特征需要用0来进行填补了,而我们已经使用回归为其他特征填补了大量有效信息,可以用来填补缺失最多的特征。遍历所有的特征后,数据就完整,不再有缺失值了。

代码

换一个数据集

(1)构造缺失数据集

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score

dataset = load_boston()
# 总共506*13=6578个数据,不含缺失值
# 完整的数据
X_full, y_full = dataset.data, dataset.target
n_samples = X_full.shape[0] # 样本 506
n_features = X_full.shape[1] # 特征 13

rng = np.random.RandomState(0) # 种子
# 缺失率为0.2,即构造的缺失值占20%
missing_rate = 0.2
n_missing_samples = int(np.floor(n_samples * n_features * missing_rate))
# 缺失值索引
missing_col_index = rng.randint(0,n_features,n_missing_samples)
missing_row_index = rng.randint(0,n_samples,n_missing_samples)

X_missing = X_full.copy()
y_missing = y_full.copy()

# 替换,构造缺失值数据集
X_missing[missing_row_index,missing_col_index] = np.nan
X_missing = pd.DataFrame(X_missing,columns=dataset.feature_names)

在这里插入图片描述

用随机森林回归来填补缺失值

X_missing_reg = X_missing.copy()
# 查看缺失情况
missing = X_missing_reg .isna().sum()
missing = pd.DataFrame(data={'特征': missing.index,'缺失值个数':missing.values})
#通过~取反,选取不包含数字0的行
missing = missing[~missing['缺失值个数'].isin([0])]
# 缺失比例
missing['缺失比例'] =  missing['缺失值个数']/X_missing_reg .shape[0]
missing
'''
       特征  缺失值个数 缺失比例
0      CRIM     91  0.179842
1        ZN     88  0.173913
2     INDUS     93  0.183794
3      CHAS    109  0.215415
4       NOX     96  0.189723
5        RM    100  0.197628
6       AGE     93  0.183794
7       DIS     85  0.167984
8       RAD     87  0.171937
9       TAX     92  0.181818
10  PTRATIO     88  0.173913
11        B     93  0.183794
12    LSTAT     88  0.173913
'''


X_df = X_missing_reg.isnull().sum()
# 得出列名 缺失值最少的列名 到 缺失值最多的列名
colname= X_df[~X_df.isin([0])].sort_values().index.values
# 缺失值从小到大的特征顺序
sortindex =[]
for i in colname:
    sortindex .append(X_missing_reg.columns.tolist().index(str(i)))

# 遍历所有的特征,从缺失最少的开始进行填补,每完成一次回归预测,就将预测值放到原本的特征矩阵中,再继续填补下一个特征
for i in sortindex:
    #构建我们的新特征矩阵和新标签
    df = X_missing_reg # 充当中间数据集
    fillc = df.iloc[:,i] # 缺失值最少的特征列
    
    
    # 除了第 i 特征列,剩下的特征列+原有的完整标签 = 新的特征矩阵
    df = pd.concat([df.drop(df.columns[i],axis=1),pd.DataFrame(y_full)],axis=1)
    
    #在新特征矩阵中,对含有缺失值的列,进行0的填补 ,没循环一次,用0填充的列越来越少
    df_0 =SimpleImputer(missing_values=np.nan,strategy='constant',fill_value=0).fit_transform(df)
   
    #找出训练集和测试集
    # 标签
    Ytrain = fillc[fillc.notnull()] # 没有缺失的部分,就是 Y_train
    Ytest = fillc[fillc.isnull()] # 不是需要Ytest的值,而是Ytest的索引
    
    # 特征矩阵
    Xtrain = df_0[Ytrain.index,:]
    Xtest = df_0[Ytest.index,:] # 有缺失值的特征情况
    
    rfc = RandomForestRegressor(n_estimators=100) # 实例化
    rfc = rfc.fit(Xtrain, Ytrain) # 训练
    Ypredict = rfc.predict(Xtest) # 预测结果,就是要填补缺失值的值
    
    
    #将填补好的特征返回到我们的原始的特征矩阵中
    X_missing_reg.loc[X_missing_reg.iloc[:,i].isnull(),X_missing_reg.columns[i]] = Ypredict

# 最后,再次查看缺失值是否全部被替换
missing2 = X_missing_reg.isna().sum()
missing2 = pd.DataFrame(data={'列名': missing2.index,'缺失值个数':missing2.values})
#通过~取反,选取不包含数字0的行
missing2[~missing2['缺失值个数'].isin([0])]

...
Empty DataFrame
Columns: [列名, 缺失值个数]
Index: []
...

在这里插入图片描述

均值/0/随机森林填补——三种方法效果对比

1)使用均值进行填补
#使用均值进行填补
from sklearn.impute import SimpleImputer
imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')
X_missing_mean = imp_mean.fit_transform(X_missing)2)使用 0 进行填补
#使用0进行填补
imp_0 = SimpleImputer(missing_values=np.nan, strategy="constant",fill_value=0)
X_missing_0 = imp_0.fit_transform(X_missing)

对比

X = [X_full,X_missing_mean,X_missing_0,X_missing_reg]
mse = []
std = []
for x in X:
    estimator = RandomForestRegressor(random_state=0, n_estimators=100)
    scores = cross_val_score(estimator,x,y_full,scoring='neg_mean_squared_error',cv=5).mean()
    # 负的均方误差
    mse.append(scores * -1)
[*zip(['X_full','X_missing_mean','X_missing_0','X_missing_reg'],mse)]

'''
[('X_full', 21.571667100368845),
 ('X_missing_mean', 30.82886303277032),
 ('X_missing_0', 35.91138076010484),
 ('X_missing_reg', 20.881091922248093)]
'''

# 可视化呈现
from pyecharts import options as opts
from pyecharts.charts import Bar
c = (
    Bar(init_opts=opts.InitOpts())
    .add_xaxis(['X_full','X_missing_mean','X_missing_0','X_missing_reg'])
    .add_yaxis("",mse)
    .render("bar.html")
)

在这里插入图片描述
或者

x_labels = ['Full data','Zero Imputation','Mean Imputation','Regressor Imputation']
colors = ['r', 'g', 'b', 'orange']
plt.figure(figsize=(12, 6))
ax = plt.subplot(111)
for i in np.arange(len(mse)):
    ax.barh(i, mse[i],color=colors[i], alpha=0.6, align='center')
ax.set_title('Imputation Techniques with Boston Data')
ax.set_xlim(left=np.min(mse) * 0.9,right=np.max(mse) * 1.1)
ax.set_yticks(np.arange(len(mse)))
ax.set_xlabel('MSE')
ax.set_yticklabels(x_labels)
plt.show()

在这里插入图片描述

3 拉格朗日插值法

原理

该插值的原理部分不在叙述

可以学习:
https://mp.weixin.qq.com/s/YoT0a7sMuvl7SUdPHyMBLg

https://mp.weixin.qq.com/s/Zoy3HHkO3AMPn_8ED_idoA

代码

网上拉格朗日插值代码

在这里插入图片描述

import pandas as pd #导入数据分析库Pandas
from scipy.interpolate import lagrange #导入拉格朗日插值函数

data = pd.read_excel(r'C:\LG.xlsx', header=None) #读入数据


data_missing = data.copy()
# 查看缺失情况
missing = data_missing .isna().sum()
missing = pd.DataFrame(data_missing={'特征': missing.index,'缺失值个数':missing.values})
#通过~取反,选取不包含数字0的行
missing = missing[~missing['缺失值个数'].isin([0])]
# 缺失比例
missing['缺失比例'] =  missing['缺失值个数']/data_missing .shape[0]
missing
'''
   特征 缺失值个数 缺失比例
0   0      1      0.2
1   1      2      0.4
2   2      1      0.2
3   3      1      0.2
'''

#自定义列向量插值函数
#s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
def ployinterp_column(s, n, k=5):
  if n>=k:
    if n+1+k<len(s):
      y = s[list(range(n-k, n)) + list(range(n+1, n+1+k))]
    else:
      y = s[list(range(n-k, n)) + list(range(n+1, len(s)))]  
  else:
    if n+1+k<len(s):
      y = s[list(range(0, n)) + list(range(n+1, n+1+k))]
    else:
      y = s[list(range(0, n)) + list(range(n+1, len(s)))]
  y = y[y.notnull()] #剔除空值
  return lagrange(y.index, list(y))(n) #插值并返回插值结果,但是有些值更无理


#逐个元素判断是否需要插值
for i in data_missing.columns:    
  for j in range(len(data_missing)):
    if (data_missing[i].isnull())[j]: #如果为空即插值。       
      data_missing.loc[j][i]= ployinterp_column(data_missing[i], j)

print('插值完成!')


# 查看缺失情况
data_missing.isna().sum()

在这里插入图片描述
试一下,随机森林插补法部分构造的缺失数据集

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score

dataset = load_boston()
# 总共506*13=6578个数据,不含缺失值
# 完整的数据
X_full, y_full = dataset.data, dataset.target
n_samples = X_full.shape[0] # 样本 506
n_features = X_full.shape[1] # 特征 13

rng = np.random.RandomState(0) # 种子
# 缺失率为0.2,即构造的缺失值占20%
missing_rate = 0.2
n_missing_samples = int(np.floor(n_samples * n_features * missing_rate))
# 缺失值索引
missing_col_index = rng.randint(0,n_features,n_missing_samples)
missing_row_index = rng.randint(0,n_samples,n_missing_samples)

X_missing = X_full.copy()
y_missing = y_full.copy()

# 替换,构造缺失值数据集
X_missing[missing_row_index,missing_col_index] = np.nan
X_missing = pd.DataFrame(X_missing,columns=dataset.feature_names)


X_missing_LG = X_missing.copy()
# 查看缺失情况
missing = X_missing_LG .isna().sum()
missing = pd.DataFrame(data={'特征': missing.index,'缺失值个数':missing.values})
#通过~取反,选取不包含数字0的行
missing = missing[~missing['缺失值个数'].isin([0])]
# 缺失比例
missing['缺失比例'] =  missing['缺失值个数']/X_missing_LG .shape[0]
missing

在这里插入图片描述
使用拉格朗日插值

#自定义列向量插值函数
#s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
def ployinterp_column(s, n, k=5):
  if n>=k:
    if n+1+k<len(s):
      y = s[list(range(n-k, n)) + list(range(n+1, n+1+k))]
    else:
      y = s[list(range(n-k, n)) + list(range(n+1, len(s)))]  
  else:
    if n+1+k<len(s):
      y = s[list(range(0, n)) + list(range(n+1, n+1+k))]
    else:
      y = s[list(range(0, n)) + list(range(n+1, len(s)))]
  y = y[y.notnull()] #剔除空值
  return lagrange(y.index, list(y))(n) #插值并返回插值结果,但是有些值更无理

import time
time_start=time.time()
#逐个元素判断是否需要插值
for i in X_missing_LG.columns:    
  for j in range(len(X_missing_LG)):
    if (X_missing_LG[i].isnull())[j]: #如果为空即插值。       
      X_missing_LG.loc[j][i]= ployinterp_column(X_missing_LG[i], j)
time_end=time.time()
print('totally cost',time_end-time_start)
print('插值完成!')

'''
totally cost 8.05620813369751
插值完成!
'''

在这里插入图片描述

对比拉格朗日插值法—随机森林插值—均值填补—0填补

X = [X_full,X_missing_mean,X_missing_0,X_missing_reg,X_missing_LG]
mse = []
std = []
for x in X:
    estimator = RandomForestRegressor(random_state=0, n_estimators=100)
    scores = cross_val_score(estimator,x,y_full,scoring='neg_mean_squared_error',cv=5).mean()
    # 负的均方误差
    mse.append(scores * -1)
    
    
# 画图
x_labels = ['X_full','X_missing_0','X_missing_mean','X_missing_reg','X_missing_LG']
colors = ['r', 'g', 'b', 'orange','pink']
plt.figure(figsize=(8, 6))
ax = plt.subplot(111)
for i in np.arange(len(mse)):
    ax.barh(i, mse[i],color=colors[i], alpha=0.6, align='center')
ax.set_title('Imputation Techniques with Boston Data')
ax.axvline(x=min(mse), c="r", ls="--", lw=2)
ax.set_xlim(left=np.min(mse) * 0.9,right=np.max(mse) * 1.1)
ax.set_yticks(np.arange(len(mse)))
ax.set_xlabel('MSE')
ax.set_yticklabels(x_labels)
plt.show()

在这里插入图片描述

4 其他(删除包含缺失行/列,用前/后一行,前后均值替换等)

df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)

删除包含缺失值的行:

df.dropna(axis = 0)

删除包含缺失值的列:

df.dropna(axis = 1)

如果一列里缺失值超过10%,则删除该列:

df.dropna(thresh=len(df)*0.9, axis=1)

去掉缺失比例大于80%以上的变量

df=df.dropna(thresh=len(df)*0.2, axis=1)
def find_missing(data):
    #统计缺失值个数
    missing_num = data.isna().sum(axis=0).sort_values(ascending=False)
    missing_prop = missing_num/float(len(data)) #计算缺失值比例
    drop_index = missing_prop[missing_prop>0.1].index.tolist() #过滤要删除特征名
    return drop_index
data2 = data.copy()
data2.drop(find_missing(data2),axis = 1)

用一个标量替换缺失值:

df.fillna(value=10)

用上一行对应位置的值替换缺失值:

df.fillna(axis=0, method='ffill')

用前一列对应位置的值替换缺失值:

df.fillna(axis=1, method='ffill')

用下一行对应位置的值替换缺失值:

df.fillna(axis=0, method='bfill')

用后一列对应位置的值替换缺失值:

df.fillna(axis=1, method='bfill')

使用某一列的平均值替换缺失值:

df['Age'].fillna(value=df['Age'].mean(), inplace=True)

去除所有值都为NaN的行

df.dropna(how='all')

去除非缺失值小于4个的行

df.dropna(thresh=4)

去除在特定列出现NaN的行

df.dropna(subset=['C'])
df.dropna(axis=0, subset=['a','b'], inplace=True)

interpolate()插值法,缺失值前后数值的均值,但是若缺失值前后也存在缺失,则不进行计算插补。

df['a'] = df['a'].interpolate()

参考:菜菜的sklearn课堂——随机森林部分
数据分析之Pandas缺失数据处理

  • 24
    点赞
  • 185
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值