RFCV函数

2022-08-30  本文已影响0人  皮尔洛_dys
# function
rfcv1 <- function(trainx, trainy, cv.fold = 5, scale = "log", step = 0.5, mtry = function(p) max(1, floor(sqrt(p))), recursive = FALSE, 
                  ipt = NULL, ...) {
  #判断标签值是否为因子类型
  classRF <- is.factor(trainy)#结果为True
  #获取整个数据的行数和列数,其中行数=n(样本数),列数=p(特征数量)
  n <- nrow(trainx)
  p <- ncol(trainx)
  #以step的倒数为底对特征数量求对数得到k,然后再用特征数量乘以step的k次方(实际上就是用特征数量连续的除以step,得到一个从满特征到1的等比数列)
  #这一步实际上在不断地削减特征数量,以便于最终精简构建模型所需要的特征数量,以达到用尽量少的特征但得到精良精确模型的目的。
  #最终得到的等比数列存储在n.var中
  #711 474 316 211 140  94  62  42  28  18  12   8   5   4   2   1   0
  if (scale == "log") {
    k <- floor(log(p, base = 1/step))
    n.var <- round(p * step^(0:(k - 1)))
    same <- diff(n.var) == 0
    if (any(same)) 
      n.var <- n.var[-which(same)]
    if (!1 %in% n.var) 
      n.var <- c(n.var, 1)
  } else {
    n.var <- seq(from = p, to = 1, by = step)
  }
  #计算等比数列的长度
  k <- length(n.var)
  #构建一个向量cv.pred,其长度与等比数列相等但每一个元素都是一个列表
  cv.pred <- vector(k, mode = "list")
  #先将整个数据的标签填入cv.pred
  for (i in 1:k) cv.pred[[i]] <- trainy
  #
  if (classRF) {
    f <- trainy#将标签复制给f
    if (is.null(ipt)) 
      ipt <- nlevels(trainy) + 1#ipt复制标签的种类加1
  } else {
    f <- factor(rep(1:5, length = length(trainy))[order(order(trainy))])
    if (is.null(ipt)) 
      ipt <- 1
  }
  nlvl <- table(f)#将分类标签的table复制给nlvl
  idx <- numeric(n)#idx=样本数
  #整个数据中有两种样本标签A,B,其中A有100个B有103个,根据cv.fold的值不断重复1-cv.fold得到两个循环列表,列表长度分别等于A和B的个数
  #然后将循环列表的顺序打乱分别填写在idx的对应A和B的位置上
  #这样A和B的样本就被大致均等的标记为5份,以便于后期的交叉验证
  for (i in 1:length(nlvl)) {
    idx[which(f == levels(f)[i])] <- sample(rep(1:cv.fold, length = nlvl[i]))
  }
  
  res = list()
  #循环cv.fold次,即进行cv.fold次交叉验证
  for (i in 1:cv.fold) {
    #当循环进入第i次时,将没有被i标记的样本作为训练集,用被i标记过的样本作为测试集进行随机森林建模及验证
    all.rf <- randomForest(trainx[idx != i, , drop = FALSE], trainy[idx != i], trainx[idx == i, , drop = FALSE], trainy[idx == 
                                                                                                                          i], mtry = mtry(p), importance = TRUE)
    #将测试集预测得到的结果复制给cv.pred的对应位置,idx==1说明使用的是非i标记数据所构成训练集的全部特征的预测结果
    cv.pred[[1]][idx == i] <- all.rf$test$predicted
    #随机森林模型会根据预测结果给出训练集所使用特征的importance,将这些特征的importance进行排序(从高到低)并记录其索引,以方便
    #后续模型精简过程中提取前n个重要的特征进行建模
    impvar <- (1:p)[order(all.rf$importance[, ipt], decreasing = TRUE)]
    res[[i]] <- impvar
    #开始从第二数量特征遍历
    for (j in 2:k) {
      imp.idx <- impvar[1:n.var[j]]#挑选出按照重要性从高到低排列的前n个importance的索引
      #用五分之一的数据验证模型,用其他数据的前n个特征训练模型
      sub.rf <- randomForest(trainx[idx != i, imp.idx, drop = FALSE], trainy[idx != i], trainx[idx == i, imp.idx, drop = FALSE], 
                             trainy[idx == i], mtry = mtry(n.var[j]), importance = recursive)
      #将预测结果存储在对应位置上
      cv.pred[[j]][idx == i] <- sub.rf$test$predicted
      if (recursive) {
        impvar <- (1:length(imp.idx))[order(sub.rf$importance[, ipt], decreasing = TRUE)]
      }
      NULL
    }
    NULL
  }
  #经历上述循环后,会得到一个预测见过的向量,其中不仅包含了k折交叉验证的的结果,每次交叉验证还包含了使用不同数量特征预测得到的结果
  #共k.fold*(等比数列长度)个结果
  if (classRF) {
    error.cv <- sapply(cv.pred, function(x) mean(trainy != x))#计算不同数量特征建模五次交叉验证的平均错误率
  } else {
    error.cv <- sapply(cv.pred, function(x) mean((trainy - x)^2))
  }
  names(error.cv) <- names(cv.pred) <- n.var
  #n.var=存储的是特征数量除以step的等比数列,error.cv是不同特征五次交叉验证的平均错误率,predicted是不同数量特征验证得到的结果,res是importance排名的索引
  list(n.var = n.var, error.cv = error.cv, predicted = cv.pred, res = res)
} 
上一篇下一篇

猜你喜欢

热点阅读