我在cafe数据集上构建了一个线性回归模型,并希望通过计算留一法交叉验证来验证结果。
我为此编写了自己的函数,如果我使用lm()
对所有数据进行拟合,它可以正常工作,但是当我使用列的子集(来自逐步回归)时,我会得到一个错误。请考虑以下代码:
cafe <- read.table("C:/.../cafedata.txt", header=T)cafe$Date <- as.Date(cafe$Date, format="%d/%m/%Y")#删除第34行cafe <- cafe[-c(34), ]#不需要日期cafe <- cafe[,-1]library(DAAG)#中心化数据cafe.c <- data.frame(lapply(cafe[,2:15], function(x) scale(x, center = FALSE, scale = max(x, na.rm = TRUE))))cafe.c$Day.of.Week <- cafe$Day.of.Weekcafe.c$Sales <- cafe$Sales#留一法交叉验证函数LOOCV <- function(fit, dataset){ # 属性: #------------------------------ # fit: 模型拟合 # dataset: 使用的数据集 # ----------------------------- # 返回每个折叠的平方误差均值 - MSE MSEP_=c() for (idx in 1:nrow(dataset)){ train <- dataset[-c(idx),] test <- dataset[idx,] MSEP_[idx]<-(predict(fit, newdata = test) - dataset[idx,]$Sales)^2 } return(mean(MSEP_))}
然后当我拟合简单的线性模型并调用函数时,它可以工作:
#----------------使用所有属性的简单线性回归-----------------fit.all.c <- lm(cafe.c$Sales ~., data=cafe.c)#MSE:258LOOCV(fit.all.c, cafe.c)
然而,当我仅使用列的子集拟合相同的lm()
时,我得到了一个错误:
#-------------------------逐步线性回归--------------------------step <- stepAIC(fit.all.c, direction="both")fit.step <- lm(cafe.c$Sales ~ cafe.c$Bread.Sand.Sold + cafe.c$Bread.Sand.Waste + cafe.c$Wraps.Waste + cafe.c$Muffins.Sold + cafe.c$Muffins.Waste + cafe.c$Fruit.Cup.Sold + cafe.c$Chips + cafe.c$Sodas + cafe.c$Coffees + cafe.c$Day.of.Week,data=cafe.c)LOOCV(fit.step, cafe.c)
5495.069
有50个或更多的警告(使用warnings()查看前50个)
如果我仔细查看:
idx <- 1train <- cafe.c[-c(idx)]test <- cafe.c[idx](predict(fit.step, newdata = test) -cafe.c[idx]$Sales)^2
我得到了所有行的MSE和一个错误:
警告信息:’newdata’有1行,但找到的变量有47行
编辑
我找到了这个问题,关于这个错误,它说当我给列取不同的名字时会发生这种错误,但这不是我的情况。
回答:
请像以下这样修改你的代码:
fit.step <- lm(Sales ~ Bread.Sand.Sold + Bread.Sand.Waste + Wraps.Waste + Muffins.Sold + Muffins.Waste + Fruit.Cup.Sold + Chips + Sodas + Coffees + Day.of.Week,data=cafe.c)LOOCV(fit.step, cafe.c)# [1] 278.8984idx <- 1train <- cafe.c[-c(idx),]test <- cafe.c[idx,] # 需要选择行,而不是列(predict(fit.step, newdata = test) -cafe.c[idx,]$Sales)^2# 1 # 51.8022
另外,你的LOOCV
实现是不正确的。你必须在每次留一法折叠时在训练数据集上拟合一个新模型。现在你是在整个数据集上训练一次模型,并使用同一个单一模型来计算每个留一法折叠的保留测试数据集上的MSE,但理想情况下,你应该在不同的训练数据集上训练不同的模型。