[英]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***
我的问题是:
为什么不同的平台,采用相同的方法,使用相同的数据,会产生这样的差异? 例如,使用第 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
可以做些什么来确保不同平台之间的值尽可能相似?
这种差异将使建模过程在不同平台上产生不同的结果。
这显然不太理想。
让我们等待您进行广泛的经验比较并进行简单的具体比较。
为了简单起见,我将使用iris
。
让我们缩放和比较:
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
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 产生的缩放值!
让我们在第一行引入一个 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]])
仍然不在那里,但也许越来越近了。
请参阅此代码。 简而言之,插入符号会在删除不完整的案例之后缩放数据,然后再对它们进行 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 的特殊情况无法产生相同的结果:
毫不奇怪,这些特性在这两个图书馆的手册中都没有得到很好的记录。
但是以同样的方式做事,数字会加起来。
一个有点混乱的观察
估算值具有完全相同的值,最小到小数点后 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.