使用随机森林回归来填充数据集中的缺失值

使用随机森林回归来填充数据集中的缺失值

数据的准备

原始数据

  1. 我们使用的原始数据集如下所示。
  2. 以下数据集是SicKit Learn中,波士顿房价数据的钱 10 列,可以用如下的代码获取到:
1
2
3
4
5
6
from sklearn.datasets import load_boston
dataset = load_boston()
X_full, y_full = dataset.data, dataset.target
TenOfX = X_full_df.iloc[:, :10]
# TenOfX 即为我们的测试数据。
# 为了评估我们的填充效果,我们需要对这些数据做一下归一化处理。
0 1 2 3 4 5 6 7 8 9
0 0.000000 0.18 0.067815 0.0 0.314815 0.577505 0.641607 0.269203 0.000000 0.208015
1 0.000236 0.00 0.242302 0.0 0.172840 0.547998 0.782698 0.348962 0.043478 0.104962
2 0.000236 0.00 0.242302 0.0 0.172840 0.694386 0.599382 0.348962 0.043478 0.104962
3 0.000293 0.00 0.063050 0.0 0.150206 0.658555 0.441813 0.448545 0.086957 0.066794
4 0.000705 0.00 0.063050 0.0 0.150206 0.687105 0.528321 0.448545 0.086957 0.066794
501 0.000633 0.00 0.420455 0.0 0.386831 0.580954 0.681771 0.122671 0.000000 0.164122
502 0.000438 0.00 0.420455 0.0 0.386831 0.490324 0.760041 0.105293 0.000000 0.164122
503 0.000612 0.00 0.420455 0.0 0.386831 0.654340 0.907312 0.094381 0.000000 0.164122
504 0.001161 0.00 0.420455 0.0 0.386831 0.619467 0.889804 0.114514 0.000000 0.164122
505 0.000462 0.00 0.420455 0.0 0.386831 0.473079 0.802266 0.125072 0.000000 0.164122

造一些假数据进去

上面的表格中,共有5060个数据,而且,表格中的数据是完整的。

1
2
3
4
5
6
7
8
9
10
11
12
TenOfX.isnull().sum()
# 0 0
# 1 0
# 2 0
# 3 0
# 4 0
# 5 0
# 6 0
# 7 0
# 8 0
# 9 0
# dtype: int64

我们假设这组数据中,有 50%的数据是缺失的,也就是有2530个数据是:numpy.NaN

所有数据要随机遍布在数据集的各行各列当中,而一个缺失的数据会需要一个行索引和一个列索引,如果能够创造一个数组,包含个2530
分布在0~506中间的行索引,和2530个分布在0~13之间的列索引,那我们就可以利用索引来为数据中的任意2530个位置赋空值。

确定缺失值总数(n_missing_samples

1
2
3
4
5
6
7
n_samples = TenOfX.shape[0]
n_features = TenOfX.shape[1]
#首先确定我们希望放入的缺失数据的比例,在这里我们假设是50%,那总共就要有2530个数据缺失
rng = np.random.RandomState(0)
missing_rate = 0.5
n_missing_samples = int(np.floor(n_samples * n_features * missing_rate))
#np.floor向下取整,返回.0格式的浮点数

2530 个缺失的特征编号序列

1
missing_features = rng.randint(0,n_features,n_missing_samples)

numpy.random.randint(low, high=None, size=None, dtype='l')

  1. low : int 产生随机数的最小值
  2. high : int, optional 给随机数设置个上限,即产生的随机数必须小于 high
  3. size : int or tuple of ints, optional 输出的大小,可以是整数,或者元组

2530 个缺失的样本编号序列

1
missing_samples = rng.randint(0,n_samples,n_missing_samples)

填充空值

1
2
for i in range(0,n_missing_samples):
Ten_missing.loc[missing_samples[i],missing_features[i]] = np.NaN

填充缺失值后的数据表格

0 1 2 3 4 5 6 7 8 9
0 NaN 0.18 0.067815 0.0 0.314815 NaN 0.641607 NaN 0.000000 NaN
1 0.000236 0.00 0.242302 0.0 0.172840 NaN NaN 0.348962 NaN NaN
2 NaN 0.00 0.242302 NaN NaN NaN NaN NaN NaN 0.104962
3 NaN 0.00 NaN 0.0 NaN NaN 0.441813 NaN 0.086957 NaN
4 0.000705 NaN 0.063050 NaN NaN 0.687105 0.528321 0.448545 NaN 0.066794
501 0.000633 NaN 0.420455 NaN 0.386831 NaN 0.681771 0.122671 NaN 0.164122
502 NaN 0.00 0.420455 0.0 0.386831 0.490324 0.760041 0.105293 0.000000 0.164122
503 0.000612 NaN NaN NaN 0.386831 0.654340 0.907312 NaN 0.000000 NaN
504 0.001161 NaN NaN 0.0 0.386831 0.619467 0.889804 NaN NaN 0.164122
505 0.000462 0.00 0.420455 0.0 NaN 0.473079 NaN NaN 0.000000 0.164122

缺失值数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Ten_missing.isnull().sum()
# 0 189
# 1 208
# 2 199
# 3 220
# 4 202
# 5 206
# 6 191
# 7 196
# 8 189
# 9 199
# dtype: int64
Ten_missing.isnull().sum().sum()
# 1999

为啥不是 2530 个缺失值啊? 因为,我们的样本数量特征数量都比较少,而且我们要缺失50%的数据,所以很容易随机到样本编号和特征编号都一样的数据,重复设置为空了.

使用0填充缺失值

因为我们要做好几次实验,所以,为了最后评价的公平,我们把这些缺失值复制一下,不要直接在上面修改。

1
Ten_missing_0 = Ten_missing.copy()

使用如下的代码填充 0.

1
2
3
imp_0 = SimpleImputer(missing_values=np.nan, strategy="constant",fill_value=0)
Ten_missing_0 = pd.DataFrame(imp_0.fit_transform(Ten_missing_0))
Ten_missiong_0

得到如下的结果。

0 1 2 3 4 5 6 7 8 9
0 0.000000 0.18 0.067815 0.0 0.314815 0.000000 0.641607 0.000000 0.000000 0.000000
1 0.000236 0.00 0.242302 0.0 0.172840 0.000000 0.000000 0.348962 0.000000 0.000000
2 0.000000 0.00 0.242302 0.0 0.000000 0.000000 0.000000 0.000000 0.000000 0.104962
3 0.000000 0.00 0.000000 0.0 0.000000 0.000000 0.441813 0.000000 0.086957 0.000000
4 0.000705 0.00 0.063050 0.0 0.000000 0.687105 0.528321 0.448545 0.000000 0.066794
501 0.000633 0.00 0.420455 0.0 0.386831 0.000000 0.681771 0.122671 0.000000 0.164122
502 0.000000 0.00 0.420455 0.0 0.386831 0.490324 0.760041 0.105293 0.000000 0.164122
503 0.000612 0.00 0.000000 0.0 0.386831 0.654340 0.907312 0.000000 0.000000 0.000000
504 0.001161 0.00 0.000000 0.0 0.386831 0.619467 0.889804 0.000000 0.000000 0.164122
505 0.000462 0.00 0.420455 0.0 0.000000 0.473079 0.000000 0.000000 0.000000 0.164122

我们要评估这个结果和原来的结果相差有多少。所以,我们定义一个函数,来计算。

我打算使用均方误差来进行评估,使用的公式如下:

$$
Error = \frac{1}{N} \sum_{i} \sum_{j} [TenOfMissing_{ij} - TenOfX_{ij}]^2 \\
i \in missing\_features \\
j \in missing\_samples \\
N = missing\_samples.shape[0]= 2530
$$

具体函数如下:

1
2
def evaluator(missFixed, oringe, n_samples):
return (((missFixed - oringe)**2).sum().sum())/n_samples

结果为:

0.15378726137338652

使用均值来填充

我们新复制一份数据,然后使用如下代码来用均值填充。

1
2
3
4
#使用Mean进行填补
imp_mean = SimpleImputer(missing_values=np.nan, strategy="mean")
TenMean = imp_mean.fit_transform(TenMean)
pd.DataFrame(TenMean)

得到了如下的数据:

0 1 2 3 4 5 6 7 8 9
0 0.039600 0.180000 0.067815 0.000000 0.314815 0.527091 0.641607 0.243633 0.000000 0.426493
1 0.000236 0.000000 0.242302 0.000000 0.172840 0.527091 0.685188 0.348962 0.357701 0.426493
2 0.039600 0.000000 0.242302 0.087413 0.353054 0.527091 0.685188 0.243633 0.357701 0.104962
3 0.039600 0.000000 0.391448 0.000000 0.353054 0.527091 0.441813 0.243633 0.086957 0.426493
4 0.000705 0.112819 0.063050 0.087413 0.353054 0.687105 0.528321 0.448545 0.357701 0.066794
501 0.000633 0.112819 0.420455 0.087413 0.386831 0.527091 0.681771 0.122671 0.357701 0.164122
502 0.039600 0.000000 0.420455 0.000000 0.386831 0.490324 0.760041 0.105293 0.000000 0.164122
503 0.000612 0.112819 0.391448 0.087413 0.386831 0.654340 0.907312 0.243633 0.000000 0.426493
504 0.001161 0.112819 0.391448 0.000000 0.386831 0.619467 0.889804 0.243633 0.357701 0.164122
505 0.000462 0.000000 0.420455 0.000000 0.353054 0.473079 0.685188 0.243633 0.000000 0.164122

然后我们使用评估器,来对这份数据进行评估,得到的值为:

0.04693971768814396

使用随机森林来填充

复制新的数据

1
TenRF = Ten_missing.copy()

构建我们的新特征矩阵和新标签

1
2
df = TenRF
readyToFill = df.iloc[:, i]

在新特征矩阵中,对含有缺失值的列,进行 0 的填补

1
2
trainWithFakeFill = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=0) \
.fit_transform(df.iloc[:, df.columns != i])

找出我们的训练集和测试集

1
2
3
4
YTrain = readyToFill[readyToFill.notnull()]
YTest = readyToFill[readyToFill.isnull()]
XTrain = trainWithFakeFill[YTrain.index, :]
XTest = trainWithFakeFill[YTest.index, :]

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

1
2
3
rfc = RandomForestRegressor(n_estimators=100)
rfc = rfc.fit(XTrain, YTrain)
YPredict = rfc.predict(XTest)

将填补好的特征返回到我们的原始的特征矩阵中

1
TenRF.loc[TenRF.iloc[:,i].isnull(),i] = YPredict

去循环上述过程,最终得到结果:

如果不调整 RFR 的任何参数,得到的值为:

0.017283231313295726

如果将 RFR 的criterion参数调整为mse

0.0169885810532522

注:

如果有需要的话,可以对决策树进行参数优化,以达到最佳。

结论

  1. 就我们提到三种填充缺失值的方式,随机森林的效果还是蛮好的,在simpleImputer的策略可选项中,还有众数和中位可以选择,效果分别如下:
填充 0 中位数 众数 填充均值 随机森林回归器
0.15378726137338652 0.05478160362567096 0.10875458022229394 0.04693971768814396 0.0169885810532522
  1. 如果说,我们需要填充的值是一个离散的量,我们可能需要使用随机森林分类器来解决,其具体位置在:

sklearn.ensemble.RandomForestClassifier.

使用随机森林回归来填充数据集中的缺失值

https://www.borgor.cn/posts/aee0e1d8.html

作者

Cyrusky

发布于

2020-03-28

更新于

2024-11-18

许可协议

评论