You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用索引计算RMSE时生成NA值的问题(波士顿房价数据集)

波士顿房价数据集RMSE计算出现NA值的问题解决

问题重现

使用MASS包的波士顿房价数据集,通过索引方式存储5次交叉验证的RMSE时,结果出现NA值:

library(MASS)
library(glmnet)

for (i in 1:5){
  
  idx <- sample(seq(1, 3), size = nrow(MASS::Boston), replace = TRUE, prob = c(.6, .2, .2))
  train <- MASS::Boston[idx == 1,]
  test <- MASS::Boston[idx == 2,]
  validation <- MASS::Boston[idx == 3,]

  elastic.test.RMSE <- 0
  elastic.test.pred <- 0
  y <- train$medv
  x <- data.matrix(train %>% dplyr::select(-medv))
  elastic.model <- glmnet(x, y, alpha = 0.5)
  elastic.cv <- cv.glmnet(x, y, alpha = 0.5)
  best.elastic.lambda <- elastic.cv$lambda.min
  best.elastic.model <- glmnet(x, y, alpha = 0, lambda = best.elastic.lambda)
  elastic.test.pred <- predict(best.elastic.model, s = best.elastic.lambda, newx = data.matrix(test %>% dplyr::select(-medv)))
  elastic.test.RMSE[i] <- Metrics::rmse(actual = test$medv, predicted = elastic.test.pred)
}

运行后输出:

[1] 0.000000       NA       NA       NA 4.019411

但改用数据框存储RMSE时,结果完全正常:

elastic.test.RMSE.df <- data.frame(elastic.test.RMSE)
library(MASS)
library(glmnet)

for (i in 1:5){
  
  idx <- sample(seq(1, 3), size = nrow(MASS::Boston), replace = TRUE, prob = c(.6, .2, .2))
  train <- MASS::Boston[idx == 1,]
  test <- MASS::Boston[idx == 2,]
  validation <- MASS::Boston[idx == 3,]

  elastic.test.RMSE <- 0
  elastic.test.pred <- 0
  y <- train$medv
  x <- data.matrix(train %>% dplyr::select(-medv))
  elastic.model <- glmnet(x, y, alpha = 0.5)
  elastic.cv <- cv.glmnet(x, y, alpha = 0.5)
  best.elastic.lambda <- elastic.cv$lambda.min
  best.elastic.model <- glmnet(x, y, alpha = 0, lambda = best.elastic.lambda)
  elastic.test.pred <- predict(best.elastic.model, s = best.elastic.lambda, newx = data.matrix(test %>% dplyr::select(-medv)))
  elastic.test.RMSE <- Metrics::rmse(actual = test$medv, predicted = elastic.test.pred)
  elastic.test.RMSE.df <- rbind(elastic.test.RMSE.df, elastic.test.RMSE)
}

运行后输出:

> elastic.test.RMSE.df
  elastic.test.RMSE
1          5.213519
2          4.806393
3          5.412275
4          5.749699
5          5.192845
6          4.229541

问题根源

出现NA的核心原因有两个:

  1. 向量初始化位置错误:在循环内部每次执行elastic.test.RMSE <- 0,会将之前存储的向量重置为单个数值0。当后续赋值elastic.test.RMSE[i]时,R会自动扩展向量,但之前迭代的结果会丢失,且如果某次计算返回NA,就会直接保留在向量中。
  2. 测试集可能为空:使用sample随机划分时,存在概率性的idx==2样本数为0的情况,此时test是空数据框。Metrics::rmse在实际值和预测值都为空的情况下会返回NA。

另外,原代码中还有一个参数不一致的小问题:交叉验证用的是alpha=0.5(弹性网),但训练最优模型时写成了alpha=0(岭回归),这会导致模型类型不匹配,也可能影响结果稳定性。

修正后的代码

针对上述问题,调整代码如下:

library(MASS)
library(glmnet)
library(dplyr)
library(Metrics)

# 循环外初始化长度为5的数值向量
elastic.test.RMSE <- numeric(5)

for (i in 1:5){
  # 循环直到获得非空的测试集
  idx <- NULL
  while(is.null(idx) || sum(idx == 2) == 0){
    idx <- sample(seq(1, 3), size = nrow(MASS::Boston), replace = TRUE, prob = c(.6, .2, .2))
  }
  
  train <- MASS::Boston[idx == 1,]
  test <- MASS::Boston[idx == 2,]
  validation <- MASS::Boston[idx == 3,]

  y <- train$medv
  x <- data.matrix(train %>% select(-medv))
  # 直接交叉验证,无需提前训练基础模型
  elastic.cv <- cv.glmnet(x, y, alpha = 0.5)
  best.elastic.lambda <- elastic.cv$lambda.min
  # 保持alpha参数与交叉验证一致
  best.elastic.model <- glmnet(x, y, alpha = 0.5, lambda = best.elastic.lambda)
  elastic.test.pred <- predict(best.elastic.model, s = best.elastic.lambda, newx = data.matrix(test %>% select(-medv)))
  elastic.test.RMSE[i] <- rmse(actual = test$medv, predicted = elastic.test.pred)
}

# 查看结果
elastic.test.RMSE

关键修改说明

  • 向量初始化移至循环外:预先创建长度为5的数值向量,避免每次迭代重置覆盖之前的计算结果。
  • 确保测试集非空:通过while循环重新划分数据集,直到得到包含样本的测试集,彻底避免空数据集导致的NA。
  • 统一模型参数:将best.elastic.model的alpha参数修正为0.5,与交叉验证的设置保持一致,保证模型类型匹配。

内容的提问来源于stack exchange,提问作者Russ Conte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:53:17