繁体   English   中英

R 的插入符号 knnImpute 和 Python 的 sklearn.impute KNNImputer 结果之间的对应关系

[英]Correspondence between R's caret knnImpute and Python's sklearn.impute KNNImputer results

我试图在不同的编程平台上复制机器学习过程,但与 python 的 sklearn 过程相比,我从 R Caret 的预处理 function 中获得了不同的估算值。 使用下面的示例数据集和过程:

library(caret)

set.seed(197)
simulated.ds <- data.frame(
  var1 = rbinom(n=10000, size=1, prob=0.05),
  var2 = rbinom(n=10000, size=1, prob=0.4),
  var3 = rbinom(n=10000, size=1, prob=0.2),
  var4 = rbinom(n=10000, size=1, prob=0.03),
  var5 = rbinom(n=10000, size=1, prob=0.7),
  var6 = rbinom(n=10000, size=1, prob=0.1),
  var7 = rbinom(n=10000, size=1, prob=0.2)
)


set.seed(50)
ind1 <- sample(c(1:10000), 1250)
simulated.ds$var1[ind1] <- NA

set.seed(150)
ind2 <- sample(c(1:10000), 1250)
simulated.ds$var2[ind2] <- NA

set.seed(1000)
ind5 <- sample(c(1:10000), 1250)
simulated.ds$var5[ind5] <- NA

set.seed(500)
ind6 <- sample(c(1:10000), 1250)
simulated.ds$var6[ind6] <- NA

write.csv(simulated.ds, "rawDataR.csv", row.names = F)

prepRoutine <- caret::preProcess(simulated.ds, method = "knnImpute", k=5)
imputed_dataset <- predict(prepRoutine, simulated.ds)

write.csv(imputed_dataset, "imputedDataR.csv", row.names = F)

当我通过尝试使用下面的代码复制 Python 3 中的插补过程来预处理相同的模拟数据集时,插补数据集的结果是不同的。

import random
import pandas as pd
import numpy as np
from sklearn.impute import KNNImputer
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

rawDataR = pd.read_csv("C:/Users/AfrikanaScholar/Documents/rawDataR.csv")
imputedDataR = pd.read_csv("C:/Users/AfrikanaScholar/Documents/imputedDataR.csv")

random.seed(197)
scaler = StandardScaler()
imputer = KNNImputer(n_neighbors=5, weights="distance")

data_pipeline = Pipeline([
    ('scaler', scaler),
    ('imputer', imputer)
])

imputedDataPy = data_pipeline.fit_transform(rawDataR)

dataPy = pd.DataFrame(imputedDataPy).round(3)
dataR = imputedDataR.round(3) 

np.array_equal(dataR.values, dataPy.values)
>> ***False***

我的问题是:

  1. 为什么不同的平台,采用相同的方法,使用相同的数据,会产生这样的差异? 例如,使用第 5 列的估算值,

    从 python 3 开始:

     Counter({ -1.522: 2644, -1.086: 55, -0.65: 193, -0.215: 485, 0.221: 478, 0.398: 1, 0.657: 6144 })

    从 R:

     -1.522 -1.086 -0.65 -0.215 0.221 0.657 2638 3 222 272 582 6283
  2. 可以做些什么来确保不同平台之间的值尽可能相似?

这种差异将使建模过程在不同平台上产生不同的结果。

这显然不太理想。

让我们等待您进行广泛的经验比较并进行简单的具体比较。

为了简单起见,我将使用iris

让我们缩放和比较:

R


x <- as.matrix( iris[,1:4] )
write.csv( x, "~/iris.csv", row.names=FALSE )

x_scaled = scale(x)

x[1,]
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
  -0.8976739    1.0156020   -1.3357516   -1.3110521 

Python

x = pd.read_csv("iris.csv")
scaler = StandardScaler()
x_scaled = scaler.fit_transform(x)
x_scaled[:1]
>>> x_scaled[:1]
array([[-0.90068117,  1.01900435, -1.34022653, -1.3154443 ]])

他们甚至无法跨平台正确扩展数据!

在这一点上,钟声可能正在响起。

因为居中的数据确实匹配:

R

> scale(x,scale=FALSE)[1,]
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
  -0.7433333    0.4426667   -2.3580000   -0.9993333 

>>> centerer = StandardScaler( with_std=False )
>>> x_centered = centerer.fit_transform(x)
>>> x_centered[:1]
array([[-0.74333333,  0.44266667, -2.358     , -0.99933333]])

如果你在 R 中做有偏差的标准差,你现在确实得到了匹配值:

R

> x_centered <- scale( x_orig, scale=FALSE )
> sweep( x_centered, 2, sqrt( apply(x_orig,2,var) * (n-1)/n ), FUN="/" ) %>% head(n=1)
     Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,]   -0.9006812    1.019004    -1.340227   -1.315444

这些也是 python 产生的缩放值!

没有缩放差异的 KNN 插补

让我们在第一行引入一个 NA:

x_withNA <- x_scaled
x_withNA[1,2] <- NA
write.csv( x_withNA, "~/iris_withNA.csv", row.names=FALSE )

在 R 中:

> prepRoutine <- caret::preProcess(x_withNA, method = "knnImpute", k=5)
> predict(prepRoutine, x_withNA)[1,]
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
  -0.8976739    1.0225507   -1.3357516   -1.3110521 

在 Py 中(从 R 读取预缩放数据):

>>> x_withNA = pd.read_csv("iris_withNA.csv")
>>> imputer = KNNImputer(n_neighbors=5)
>>> x_imputed = imputer.fit_transform(x_withNA)
>>> x_imputed[:1]
array([[-0.89767388,  1.01560199, -1.33575163, -1.31105215]])

仍然不在那里,但也许越来越近了。

caret::knnImpute 以自己的方式扩展

请参阅此代码 简而言之,插入符号会在删除不完整的案例之后缩放数据,然后再对它们进行 knn 插补。

所以这里的这一行:

prepRoutine <- caret::preProcess(x_withNA, method = "knnImpute", k=5)

crates 一个 object ,其中用于 knn 的参考数据 (x_withNA) 被缩放并删除了 NA 行,这显然也与 python 不同。

所以让我们使用插入符号,让我们注入我们缩放的参考数据,然后扔掉他的

prepRoutine$data <- x_withNA[-1,]
predict(prepRoutine, x_withNA)[1,]

你瞧: - 现在来自 R:

Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
  -0.8976739    1.0156020   -1.3357516   -1.3110521

并且只是从 Python 粘贴那些:

array([[-0.89767388,  1.01560199, -1.33575163, -1.31105215]])

这必须通过比赛!

结论

由于设计理念上的一些非常细微的差异,这种具有单个 NA 的特殊情况无法产生相同的结果:

  1. python随总体标准差 sqrt(sum(xu)/n) 缩放。
  2. R/caret随样本标准差 sqrt(sum(xu)/(n-1)) 缩放。
  3. python基于来自不完整行和完整行的数据进行缩放。
  4. R/caret仅使用来自完整行的数据进行缩放。

毫不奇怪,这些特性在这两个图书馆的手册中都没有得到很好的记录。

但是以同样的方式做事,数字会加起来。


一个有点混乱的观察

估算值具有完全相同的值,最小到小数点后 20 位左右。 这是可疑的。 这是因为在这方面我选择的数据是不明智的。 虹膜数据只有一位小数精度。

估算了Sepal.Width的行是这样的, 3.5是被删除的值:

Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
         5.1          3.5          1.4          0.2 

5 个最近的邻居是:

     Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,]          5.1         3.4          1.5         0.2
[2,]          5.1         3.8          1.6         0.2
[3,]          5.0         3.3          1.4         0.2
[4,]          5.0         3.6          1.4         0.2
[5,]          5.2         3.4          1.4         0.2

顺便说一句,这些邻居的Sepal.Width的平均值恰好是3.5 ,这与模拟的缺失值有关。

如果我事先知道这一点,我会在数据中添加少量噪音。

我现在不重做这一切。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM