r - 如何调试 “contrasts can be applied only to factors with 2 or more levels”错误?

标签 r regression lm glm r-faq

这是我正在使用的所有变量:

str(ad.train)
$ Date                : Factor w/ 427 levels "2012-03-24","2012-03-29",..: 4 7 12 14 19 21 24 29 31 34 ...
 $ Team                : Factor w/ 18 levels "Adelaide","Brisbane Lions",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Season              : int  2012 2012 2012 2012 2012 2012 2012 2012 2012 2012 ...
 $ Round               : Factor w/ 28 levels "EF","GF","PF",..: 5 16 21 22 23 24 25 26 27 6 ...
 $ Score               : int  137 82 84 96 110 99 122 124 49 111 ...
 $ Margin              : int  69 18 -56 46 19 5 50 69 -26 29 ...
 $ WinLoss             : Factor w/ 2 levels "0","1": 2 2 1 2 2 2 2 2 1 2 ...
 $ Opposition          : Factor w/ 18 levels "Adelaide","Brisbane Lions",..: 8 18 10 9 13 16 7 3 4 6 ...
 $ Venue               : Factor w/ 19 levels "Adelaide Oval",..: 4 7 10 7 7 13 7 6 7 15 ...
 $ Disposals           : int  406 360 304 370 359 362 365 345 324 351 ...
 $ Kicks               : int  252 215 170 225 221 218 224 230 205 215 ...
 $ Marks               : int  109 102 52 41 95 78 93 110 69 85 ...
 $ Handballs           : int  154 145 134 145 138 144 141 115 119 136 ...
 $ Goals               : int  19 11 12 13 16 15 19 19 6 17 ...
 $ Behinds             : int  19 14 9 16 11 6 7 9 12 6 ...
 $ Hitouts             : int  42 41 34 47 45 70 48 54 46 34 ...
 $ Tackles             : int  73 53 51 76 65 63 65 67 77 58 ...
 $ Rebound50s          : int  28 34 23 24 32 48 39 31 34 29 ...
 $ Inside50s           : int  73 49 49 56 61 45 47 50 49 48 ...
 $ Clearances          : int  39 33 38 52 37 43 43 48 37 52 ...
 $ Clangers            : int  47 38 44 62 49 46 32 24 31 41 ...
 $ FreesFor            : int  15 14 15 18 17 15 19 14 18 20 ...
 $ ContendedPossessions: int  152 141 149 192 138 164 148 151 160 155 ...
 $ ContestedMarks      : int  10 16 11 3 12 12 17 14 15 11 ...
 $ MarksInside50       : int  16 13 10 8 12 9 14 13 6 12 ...
 $ OnePercenters       : int  42 54 30 58 24 56 32 53 50 57 ...
 $ Bounces             : int  1 6 4 4 1 7 11 14 0 4 ...
 $ GoalAssists         : int  15 6 9 10 9 12 13 14 5 14 ...

这是我要适应的glm:
ad.glm.all <- glm(WinLoss ~ factor(Team) + Season  + Round + Score  + Margin + Opposition + Venue + Disposals + Kicks + Marks + Handballs + Goals + Behinds + Hitouts + Tackles + Rebound50s + Inside50s+ Clearances+ Clangers+ FreesFor + ContendedPossessions + ContestedMarks + MarksInside50 + OnePercenters + Bounces+GoalAssists, 
                  data = ad.train, family = binomial(logit))

我知道有很多变量(计划是通过选择正向变量来减少)。但是,即使知道很多变量,它们要么是int要么是Factor。据我了解,这应该只是一闪而过。但是,每次我尝试拟合此模型时,我都会得到:
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels

在我看来,R似乎出于某种原因未将我的Factor变量视为Factor变量?

甚至很简单:
ad.glm.test <- glm(WinLoss ~ factor(Team), data = ad.train, family = binomial(logit))

不起作用! (相同的错误信息)

像这样:
ad.glm.test <- glm(WinLoss ~ Clearances, data = ad.train, family = binomial(logit))

将工作!

有人知道这是怎么回事吗?为什么我无法将这些Factor变量拟合为我的glm?

提前致谢!

-特洛伊

最佳答案

介绍

What a "contrasts error" is has been well explained: you have a factor that only has one level (or less)。但是实际上,这个简单的事实很容易被掩盖,因为实际用于模型拟合的数据可能与您传递的数据完全不同。这种情况发生在您的数据中包含NA,将数据子集化, factor具有未使用的级别,或者您已转换变量并在某处获取NaNYou are rarely in this ideal situation where a single-level factor can be spotted from str(your_data_frame) directly.关于此错误的有关StackOverflow的许多问题都无法重现,因此人们的建议可能有效也可能无效。因此,尽管现在有关于此问题的118 posts,但用户仍然找不到自适应解决方案,因此该问题一再提出。这是我的尝试,试图“一劳永逸”地解决此问题,或者至少提供合理的指导。

该答案具有丰富的信息,因此让我先做一个简短的总结。

我为您定义了3个辅助函数:debug_contr_errordebug_contr_error2NA_preproc

我建议您按以下方式使用它们。

  • 运行NA_preproc以获得更完整的案例;
  • 运行您的模型,如果出现“对比度错误”,请使用debug_contr_error2进行调试。

  • 大多数答案将逐步向您展示如何定义这些功能以及为什么定义这些功能。跳过那些开发过程可能不会有任何危害,但是不要跳过“可复制的案例研究和讨论”中的部分。

    修改后的答案

    original answer works perfectly for OPhas successfully helped some others。但是it had failed somewhere else缺乏适应性。查看问题中str(ad.train)的输出。 OP的变量是数字或因子;没有字符。最初的答案就是针对这种情况。如果您有字符变量,尽管在lmglm拟合期间它们将被强制转换为因子,但是由于未将它们作为因子提供,因此代码不会报告它们,因此is.factor会错过它们。在此扩展中,我将使原始答案都更具适应性。

    dat为传递给lmglm的数据集。如果没有这样的数据框架,也就是说,所有变量都分散在全局环境中,则需要将它们收集到数据框架中。以下可能不是最好的方法,但它可以工作。
    ## `form` is your model formula, here is an example
    y <- x1 <- x2 <- x3 <- 1:4
    x4 <- matrix(1:8, 4)
    form <- y ~ bs(x1) + poly(x2) + I(1 / x3) + x4
    
    ## to gather variables `model.frame.default(form)` is the easiest way 
    ## but it does too much: it drops `NA` and transforms variables
    ## we want something more primitive
    
    ## first get variable names
    vn <- all.vars(form)
    #[1] "y"  "x1" "x2" "x3" "x4"
    
    ## `get_all_vars(form)` gets you a data frame
    ## but it is buggy for matrix variables so don't use it
    ## instead, first use `mget` to gather variables into a list
    lst <- mget(vn)
    
    ## don't do `data.frame(lst)`; it is buggy with matrix variables
    ## need to first protect matrix variables by `I()` then do `data.frame`
    lst_protect <- lapply(lst, function (x) if (is.matrix(x)) I(x) else x)
    dat <- data.frame(lst_protect)
    str(dat)
    #'data.frame':  4 obs. of  5 variables:
    # $ y : int  1 2 3 4
    # $ x1: int  1 2 3 4
    # $ x2: int  1 2 3 4
    # $ x3: int  1 2 3 4
    # $ x4: 'AsIs' int [1:4, 1:2] 1 2 3 4 5 6 7 8
    
    ## note the 'AsIs' for matrix variable `x4`
    ## in comparison, try the following buggy ones yourself
    str(get_all_vars(form))
    str(data.frame(lst))
    

    步骤0:明确设置

    如果您使用了subsetlmglm参数,请从一个显式子设置开始:
    ## `subset_vec` is what you pass to `lm` via `subset` argument
    ## it can either be a logical vector of length `nrow(dat)`
    ## or a shorter positive integer vector giving position index
    ## note however, `base::subset` expects logical vector for `subset` argument
    ## so a rigorous check is necessary here
    if (mode(subset_vec) == "logical") {
      if (length(subset_vec) != nrow(dat)) {
        stop("'logical' `subset_vec` provided but length does not match `nrow(dat)`")
        }
      subset_log_vec <- subset_vec
      } else if (mode(subset_vec) == "numeric") {
      ## check range
      ran <- range(subset_vec)
      if (ran[1] < 1 || ran[2] > nrow(dat)) {
        stop("'numeric' `subset_vec` provided but values are out of bound")
        } else {
        subset_log_vec <- logical(nrow(dat))
        subset_log_vec[as.integer(subset_vec)] <- TRUE
        } 
      } else {
      stop("`subset_vec` must be either 'logical' or 'numeric'")
      }
    dat <- base::subset(dat, subset = subset_log_vec)
    

    步骤1:删除不完整的案件
    dat <- na.omit(dat)
    

    subset automatically removes incomplete cases开始,如果您已执行第0步,则可以跳过此步骤。

    步骤2:模式检查和转换

    数据框列通常是原子 vector ,其模式如下:“逻辑”,“数字”,“复杂”,“字符”,“原始”。对于回归,对不同模式的变量的处理方式不同。
    "logical",   it depends
    "numeric",   nothing to do
    "complex",   not allowed by `model.matrix`, though allowed by `model.frame`
    "character", converted to "numeric" with "factor" class by `model.matrix`
    "raw",       not allowed by `model.matrix`, though allowed by `model.frame`
    

    逻辑变量很棘手。可以将其视为虚拟变量(1TRUE0FALSE),因此是“数字”,也可以将其强制为两级因子。这完全取决于model.matrix是否认为从模型公式的规范中需要“强制分解”强制。为简单起见,我们可以这样理解:它总是被强制转换为一个因子,但是应用对比的结果可能会以相同的模型矩阵结束,就像直接将其作为虚拟对象一样。

    某些人可能想知道为什么不包括“整数”。因为像1:4这样的整数 vector 具有“数字”模式(请尝试mode(1:4))。

    数据帧列也可以是具有“AsIs”类的矩阵,但是这种矩阵必须具有“数字”模式。

    我们的检查是在以下情况下产生错误
  • 找到“复杂”或“原始”;
  • 找到“逻辑”或“字符”矩阵变量;

  • 并将“逻辑”和“字符”转换为“因子”类的“数字”。
    ## get mode of all vars
    var_mode <- sapply(dat, mode)
    
    ## produce error if complex or raw is found
    if (any(var_mode %in% c("complex", "raw"))) stop("complex or raw not allowed!")
    
    ## get class of all vars
    var_class <- sapply(dat, class)
    
    ## produce error if an "AsIs" object has "logical" or "character" mode
    if (any(var_mode[var_class == "AsIs"] %in% c("logical", "character"))) {
      stop("matrix variables with 'AsIs' class must be 'numeric'")
      }
    
    ## identify columns that needs be coerced to factors
    ind1 <- which(var_mode %in% c("logical", "character"))
    
    ## coerce logical / character to factor with `as.factor`
    dat[ind1] <- lapply(dat[ind1], as.factor)
    

    请注意,如果数据框列已经是一个因子变量,则它不会包含在ind1中,因为因子变量具有“数字”模式(尝试mode(factor(letters[1:4])))。

    步骤3:删除未使用的因子水平

    对于第2步转换的因子变量,即ind1索引的因子变量,我们将没有未使用的因子水平。但是,dat附带的因子变量可能具有未使用的级别(通常是步骤0和步骤1的结果)。我们需要从中删除任何可能的未使用级别。
    ## index of factor columns
    fctr <- which(sapply(dat, is.factor))
    
    ## factor variables that have skipped explicit conversion in step 2
    ## don't simply do `ind2 <- fctr[-ind1]`; buggy if `ind1` is `integer(0)`
    ind2 <- if (length(ind1) > 0L) fctr[-ind1] else fctr
    
    ## drop unused levels
    dat[ind2] <- lapply(dat[ind2], droplevels)
    

    步骤4:总结因子变量

    现在我们准备看看lmglm实际使用了哪些因子水平以及多少因子水平:
    ## export factor levels actually used by `lm` and `glm`
    lev <- lapply(dat[fctr], levels)
    
    ## count number of levels
    nl <- lengths(lev)
    

    为了使您的生活更轻松,我将这些步骤打包为一个函数debug_contr_error

    输入:
  • dat是通过lm参数传递给glmdata的数据帧;
  • subset_vec是通过lm参数传递给glmsubset的索引 vector 。

  • 输出:具有以下内容的列表
  • nlevels(一个列表)给出所有因子变量的因子级别数;
  • levels( vector )给出所有因子变量的水平。

  • 如果没有完整的案例或没有要汇总的因子变量,该函数将产生警告。
    debug_contr_error <- function (dat, subset_vec = NULL) {
      if (!is.null(subset_vec)) {
        ## step 0
        if (mode(subset_vec) == "logical") {
          if (length(subset_vec) != nrow(dat)) {
            stop("'logical' `subset_vec` provided but length does not match `nrow(dat)`")
            }
          subset_log_vec <- subset_vec
          } else if (mode(subset_vec) == "numeric") {
          ## check range
          ran <- range(subset_vec)
          if (ran[1] < 1 || ran[2] > nrow(dat)) {
            stop("'numeric' `subset_vec` provided but values are out of bound")
            } else {
            subset_log_vec <- logical(nrow(dat))
            subset_log_vec[as.integer(subset_vec)] <- TRUE
            } 
          } else {
          stop("`subset_vec` must be either 'logical' or 'numeric'")
          }
        dat <- base::subset(dat, subset = subset_log_vec)
        } else {
        ## step 1
        dat <- stats::na.omit(dat)
        }
      if (nrow(dat) == 0L) warning("no complete cases")
      ## step 2
      var_mode <- sapply(dat, mode)
      if (any(var_mode %in% c("complex", "raw"))) stop("complex or raw not allowed!")
      var_class <- sapply(dat, class)
      if (any(var_mode[var_class == "AsIs"] %in% c("logical", "character"))) {
        stop("matrix variables with 'AsIs' class must be 'numeric'")
        }
      ind1 <- which(var_mode %in% c("logical", "character"))
      dat[ind1] <- lapply(dat[ind1], as.factor)
      ## step 3
      fctr <- which(sapply(dat, is.factor))
      if (length(fctr) == 0L) warning("no factor variables to summary")
      ind2 <- if (length(ind1) > 0L) fctr[-ind1] else fctr
      dat[ind2] <- lapply(dat[ind2], base::droplevels.factor)
      ## step 4
      lev <- lapply(dat[fctr], base::levels.default)
      nl <- lengths(lev)
      ## return
      list(nlevels = nl, levels = lev)
      }
    

    这是一个构造的小例子。
    dat <- data.frame(y = 1:4,
                      x = c(1:3, NA),
                      f1 = gl(2, 2, labels = letters[1:2]),
                      f2 = c("A", "A", "A", "B"),
                      stringsAsFactors = FALSE)
    
    #  y  x f1 f2
    #1 1  1  a  A
    #2 2  2  a  A
    #3 3  3  b  A
    #4 4 NA  b  B
    
    str(dat)
    #'data.frame':  4 obs. of  4 variables:
    # $ y : int  1 2 3 4
    # $ x : int  1 2 3 NA
    # $ f1: Factor w/ 2 levels "a","b": 1 1 2 2
    # $ f2: chr  "A" "A" "A" "B"
    
    lm(y ~ x + f1 + f2, dat)
    #Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
    #  contrasts can be applied only to factors with 2 or more levels
    

    好,我们看到一个错误。现在,我的debug_contr_error公开了f2以单个级别结束。
    debug_contr_error(dat)
    #$nlevels
    #f1 f2 
    # 2  1 
    #
    #$levels
    #$levels$f1
    #[1] "a" "b"
    #
    #$levels$f2
    #[1] "A"
    

    请注意,最初的简短答案在这里是没有希望的,因为f2是作为字符变量而不是因子变量提供的。
    ## old answer
    tmp <- na.omit(dat)
    fctr <- lapply(tmp[sapply(tmp, is.factor)], droplevels)
    sapply(fctr, nlevels)
    #f1 
    # 2 
    rm(tmp, fctr)
    

    现在,让我们看一个带有矩阵变量x的示例。
    dat <- data.frame(X = I(rbind(matrix(1:6, 3), NA)),
                      f = c("a", "a", "a", "b"),
                      y = 1:4)
    
    dat
    #  X.1 X.2 f y
    #1   1   4 a 1
    #2   2   5 a 2
    #3   3   6 a 3
    #4  NA  NA b 4
    
    str(dat)
    #'data.frame':  4 obs. of  3 variables:
    # $ X: 'AsIs' int [1:4, 1:2] 1 2 3 NA 4 5 6 NA
    # $ f: Factor w/ 2 levels "a","b": 1 1 1 2
    # $ y: int  1 2 3 4
    
    lm(y ~ X + f, data = dat)
    #Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
    #  contrasts can be applied only to factors with 2 or more levels
    
    debug_contr_error(dat)$nlevels
    #f 
    #1
    

    请注意,没有级别的因子变量也可能导致“对比度误差”。您可能想知道0级因子是如何可能的。好吧,这是合法的:nlevels(factor(character(0)))。如果没有完整的案例,您将得到0级因子。
    dat <- data.frame(y = 1:4,
                      x = rep(NA_real_, 4),
                      f1 = gl(2, 2, labels = letters[1:2]),
                      f2 = c("A", "A", "A", "B"),
                      stringsAsFactors = FALSE)
    
    lm(y ~ x + f1 + f2, dat)
    #Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
    #  contrasts can be applied only to factors with 2 or more levels
    
    debug_contr_error(dat)$nlevels
    #f1 f2 
    # 0  0    ## all values are 0
    #Warning message:
    #In debug_contr_error(dat) : no complete cases
    

    最后,让我们看看f2是逻辑变量的情况。
    dat <- data.frame(y = 1:4,
                      x = c(1:3, NA),
                      f1 = gl(2, 2, labels = letters[1:2]),
                      f2 = c(TRUE, TRUE, TRUE, FALSE))
    
    dat
    #  y  x f1    f2
    #1 1  1  a  TRUE
    #2 2  2  a  TRUE
    #3 3  3  b  TRUE
    #4 4 NA  b FALSE
    
    str(dat)
    #'data.frame':  4 obs. of  4 variables:
    # $ y : int  1 2 3 4
    # $ x : int  1 2 3 NA
    # $ f1: Factor w/ 2 levels "a","b": 1 1 2 2
    # $ f2: logi  TRUE TRUE TRUE FALSE
    

    我们的调试器将预测“对比度错误”,但是它真的会发生吗?
    debug_contr_error(dat)$nlevels
    #f1 f2 
    # 2  1 
    

    不,至少这一点不会失败(the NA coefficient is due to the rank-deficiency of the model; don't worry):
    lm(y ~ x + f1 + f2, data = dat)
    #Coefficients:
    #(Intercept)            x          f1b       f2TRUE  
    #          0            1            0           NA
    

    我很难举一个例子给出一个错误,但也没有必要。实际上,我们不使用调试器进行预测。当我们确实遇到错误时使用它;在这种情况下,调试器可以找到违规因素变量。

    也许有人可能会认为逻辑变量与虚拟变量没有什么不同。但是,请尝试以下简单示例:它确实取决于您的公式。
    u <- c(TRUE, TRUE, FALSE, FALSE)
    v <- c(1, 1, 0, 0)  ## "numeric" dummy of `u`
    
    model.matrix(~ u)
    #  (Intercept) uTRUE
    #1           1     1
    #2           1     1
    #3           1     0
    #4           1     0
    
    model.matrix(~ v)
    #  (Intercept) v
    #1           1 1
    #2           1 1
    #3           1 0
    #4           1 0
    
    model.matrix(~ u - 1)
    #  uFALSE uTRUE
    #1      0     1
    #2      0     1
    #3      1     0
    #4      1     0
    
    model.matrix(~ v - 1)
    #  v
    #1 1
    #2 1
    #3 0
    #4 0
    

    使用"model.frame"lm方法更灵活的实现

    还建议您仔细阅读R: how to debug "factor has new levels" error for linear model and prediction,它说明了数据集的底层lmglm的作用。您将了解,上面列出的步骤0到4只是试图模仿这种内部过程。请记住,实际用于模型拟合的数据可能与您在中传递的数据完全不同。

    我们的步骤与这种内部处理并不完全一致。为了进行比较,可以使用method = "model.frame"lm中的glm检索内部处理的结果。在先前构造的小示例dat上尝试此操作,其中f2是字符变量。
    dat_internal <- lm(y ~ x + f1 + f2, dat, method = "model.frame")
    
    dat_internal
    #  y x f1 f2
    #1 1 1  a  A
    #2 2 2  a  A
    #3 3 3  b  A
    
    str(dat_internal)
    #'data.frame':  3 obs. of  4 variables:
    # $ y : int  1 2 3
    # $ x : int  1 2 3
    # $ f1: Factor w/ 2 levels "a","b": 1 1 2
    # $ f2: chr  "A" "A" "A"
    ## [.."terms" attribute is truncated..]
    

    实际上,model.frame将仅执行步骤0和步骤1。它还会删除数据集中提供的变量,而不是模型公式中提供的变量。因此,模型框架的行数和列数都可能少于lmglm所提供的内容。在第二步中完成的类型强制由后面的model.matrix完成,其中可能会产生“对比度错误”。

    首先获得此内部模型框架,然后将其传递给debug_contr_error(这样,它实际上仅执行步骤2至4)有一些优点。

    的优点1:模型公式中未使用的变量被忽略
    ## no variable `f1` in formula
    dat_internal <- lm(y ~ x + f2, dat, method = "model.frame")
    
    ## compare the following
    debug_contr_error(dat)$nlevels
    #f1 f2 
    # 2  1 
    
    debug_contr_error(dat_internal)$nlevels
    #f2 
    # 1 
    

    优势2:能够处理转换后的变量

    在模型公式中转换变量是有效的,并且model.frame将记录转换后的变量而不是原始变量。请注意,即使您的原始变量没有NA,转换后的变量也可以拥有。
    dat <- data.frame(y = 1:4, x = c(1:3, -1), f = rep(letters[1:2], c(3, 1)))
    #  y  x f
    #1 1  1 a
    #2 2  2 a
    #3 3  3 a
    #4 4 -1 b
    
    lm(y ~ log(x) + f, data = dat)
    #Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
    #  contrasts can be applied only to factors with 2 or more levels
    #In addition: Warning message:
    #In log(x) : NaNs produced
    
    # directly using `debug_contr_error` is hopeless here
    debug_contr_error(dat)$nlevels
    #f 
    #2 
    
    ## this works
    dat_internal <- lm(y ~ log(x) + f, data = dat, method = "model.frame")
    #  y    log(x) f
    #1 1 0.0000000 a
    #2 2 0.6931472 a
    #3 3 1.0986123 a
    
    debug_contr_error(dat_internal)$nlevels
    #f 
    #1
    

    鉴于这些好处,我编写了另一个包装model.framedebug_contr_error的函数。

    输入:
  • form是您的模型公式;
  • dat是通过lm参数传递给glmdata的数据集;
  • subset_vec是通过lm参数传递给glmsubset的索引 vector 。

  • 输出:具有以下内容的列表
  • mf(数据框)给出模型框(删除了“terms”属性);
  • nlevels(一个列表)给出所有因子变量的因子级别数;
  • levels( vector )给出所有因子变量的水平。

  • ## note: this function relies on `debug_contr_error`
    debug_contr_error2 <- function (form, dat, subset_vec = NULL) {
      ## step 0
      if (!is.null(subset_vec)) {
        if (mode(subset_vec) == "logical") {
          if (length(subset_vec) != nrow(dat)) {
            stop("'logical' `subset_vec` provided but length does not match `nrow(dat)`")
            }
          subset_log_vec <- subset_vec
          } else if (mode(subset_vec) == "numeric") {
          ## check range
          ran <- range(subset_vec)
          if (ran[1] < 1 || ran[2] > nrow(dat)) {
            stop("'numeric' `subset_vec` provided but values are out of bound")
            } else {
            subset_log_vec <- logical(nrow(dat))
            subset_log_vec[as.integer(subset_vec)] <- TRUE
            } 
          } else {
          stop("`subset_vec` must be either 'logical' or 'numeric'")
          }
        dat <- base::subset(dat, subset = subset_log_vec)
        }
      ## step 0 and 1
      dat_internal <- stats::lm(form, data = dat, method = "model.frame")
      attr(dat_internal, "terms") <- NULL
      ## rely on `debug_contr_error` for steps 2 to 4
      c(list(mf = dat_internal), debug_contr_error(dat_internal, NULL))
      }
    

    尝试前面的log转换示例。
    debug_contr_error2(y ~ log(x) + f, dat)
    #$mf
    #  y    log(x) f
    #1 1 0.0000000 a
    #2 2 0.6931472 a
    #3 3 1.0986123 a
    #
    #$nlevels
    #f 
    #1 
    #
    #$levels
    #$levels$f
    #[1] "a"
    #
    #
    #Warning message:
    #In log(x) : NaNs produced
    

    也尝试subset_vec
    ## or: debug_contr_error2(y ~ log(x) + f, dat, c(T, F, T, T))
    debug_contr_error2(y ~ log(x) + f, dat, c(1,3,4))
    #$mf
    #  y   log(x) f
    #1 1 0.000000 a
    #3 3 1.098612 a
    #
    #$nlevels
    #f 
    #1 
    #
    #$levels
    #$levels$f
    #[1] "a"
    #
    #
    #Warning message:
    #In log(x) : NaNs produced
    

    每组的模型拟合和NA作为因子水平

    If you are fitting model by group, you are more likely to get a "contrasts error".您需要
  • 通过分组变量拆分数据框架(请参阅?split.data.frame);
  • 应用debug_contr_error2逐一处理这些数据帧(lapply函数可有助于完成此循环)。

  • Some also told me that they can not use na.omit on their data, because it will end up too few rows to do anything sensible.这可以放松。实际上,必须省略NA_integer_NA_real_,但可以保留NA_character_:只需将NA添加为因子级别。为此,您需要遍历数据框中的变量:
  • 如果变量x已经是一个因子并且anyNA(x)TRUE ,请执行x <- addNA(x)。 “和”很重要。如果x没有NAaddNA(x)将添加一个未使用的<NA>级别。
  • 如果变量x是字符,请执行x <- factor(x, exclude = NULL)将其强制为一个因子。 exclude = NULL将保留<NA>作为一个级别。
  • 如果x是“逻辑”,“数字”,“原始”或“复杂”,则不应进行任何更改。 NA只是NA
  • <NA>droplevels不会删除na.omit因子级别,它对于构建模型矩阵有效。检查以下示例。
    ## x is a factor with NA
    
    x <- factor(c(letters[1:4], NA))  ## default: `exclude = NA`
    #[1] a    b    c    d    <NA>     ## there is an NA value
    #Levels: a b c d                  ## but NA is not a level
    
    na.omit(x)  ## NA is gone
    #[1] a b c d
    #[.. attributes truncated..]
    #Levels: a b c d
    
    x <- addNA(x)  ## now add NA into a valid level
    #[1] a    b    c    d    <NA>
    #Levels: a b c d <NA>  ## it appears here
    
    droplevels(x)    ## it can not be dropped
    #[1] a    b    c    d    <NA>
    #Levels: a b c d <NA>
    
    na.omit(x)  ## it is not omitted
    #[1] a    b    c    d    <NA>
    #Levels: a b c d <NA>
    
    model.matrix(~ x)   ## and it is valid to be in a design matrix
    #  (Intercept) xb xc xd xNA
    #1           1  0  0  0   0
    #2           1  1  0  0   0
    #3           1  0  1  0   0
    #4           1  0  0  1   0
    #5           1  0  0  0   1
    
    ## x is a character with NA
    
    x <- c(letters[1:4], NA)
    #[1] "a" "b" "c" "d" NA 
    
    as.factor(x)  ## this calls `factor(x)` with default `exclude = NA`
    #[1] a    b    c    d    <NA>     ## there is an NA value
    #Levels: a b c d                  ## but NA is not a level
    
    factor(x, exclude = NULL)      ## we want `exclude = NULL`
    #[1] a    b    c    d    <NA>
    #Levels: a b c d <NA>          ## now NA is a level
    

    一旦将NA添加为一个因子/字符级别,您的数据集可能突然有了更完整的案例。然后,您可以运行模型。如果仍然出现“对比度错误”,请使用debug_contr_error2查看发生了什么。

    为了方便起见,我为此NA预处理编写了一个函数。

    输入:
  • dat是您的完整数据集。

  • 输出:
  • 一个数据帧,并添加NA作为因子/字符的级别。

  • NA_preproc <- function (dat) {
      for (j in 1:ncol(dat)) {
        x <- dat[[j]]
        if (is.factor(x) && anyNA(x)) dat[[j]] <- base::addNA(x)
        if (is.character(x)) dat[[j]] <- factor(x, exclude = NULL)
        }
      dat
      }
    

    可重复的案例研究和讨论

    以下是专门为可重复的案例研究选择的,因为我刚刚使用此处创建的三个帮助器函数回答了它们。
  • How to do a GLM when "contrasts can be applied only to factors with 2 or more levels"?
  • R: Error in contrasts when fitting linear models with `lm`

  • 其他StackOverflow用户还解决了其他一些优质线程:
  • Factors not being recognised in a lm using map()(这是关于按组拟合模型的)
  • How to drop NA observation of factors conditionally when doing linear regression in R?(与上一个列表中的情况1类似)
  • Factor/level error in mixed model(有关按组拟合模型的另一篇文章)

  • 该答案旨在调试模型拟合过程中的“对比度误差”。但是,使用predict进行预测时也会出现此错误。这种行为与predict.lmpredict.glm无关,而与某些软件包中的predict方法有关。这是StackOverflow上的一些相关线程。
  • Prediction in R - GLMM
  • Error in `contrasts' Error
  • SVM predict on dataframe with different factor levels
  • Using predict with svyglm
  • must a dataset contain all factors in SVM in R
  • Probability predictions with cumulative link mixed models
  • must a dataset contain all factors in SVM in R

  • 另请注意,此答案的原理基于lmglmThese two functions are a coding standard for many model fitting routines,但并非所有模型拟合例程的行为都相似。例如,以下内容对我而言似乎并不透明,即我的助手功能是否真正有用。
  • Error with svychisq - 'contrast can be applied to factors with 2 or more levels'
  • R packages effects & plm : "error in contrasts" when trying to plot marginal effects
  • Contrasts can be applied only to factor
  • R: lawstat::levene.test fails while Fligner Killeen works, as well as car::leveneTest
  • R - geeglm Error: contrasts can be applied only to factors with 2 or more levels

  • 尽管有些偏离主题,但知道有时“对比度错误”仅源于编写错误的代码,仍然很有用。在以下示例中,OP将其变量的名称而不是其值传递给lm。由于名称是单值字符,因此稍后将其强制为单级因子并导致错误。
  • Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels
  • Loop through a character vector to use in a function


  • 调试后如何解决此错误?

    在实践中,人们想知道如何在统计级别或编程级别解决此问题。

    如果您要在完整的数据集上拟合模型,那么除非您可以估算缺失值或收集更多数据,否则可能没有统计解决方案。因此,您可以简单地使用一种编码解决方案来删除有害变量。 debug_contr_error2返回nlevels,可帮助您轻松找到它们。如果不想删除它们,则将它们替换为1的 vector (如How to do a GLM when "contrasts can be applied only to factors with 2 or more levels"?中所述),然后让lmglm处理所产生的秩不足。

    如果您要对子集拟合模型,则可以有统计解决方案。

    按组拟合模型不一定要求您按组拆分数据集并拟合独立模型。以下内容可能会给您一个大概的想法:
  • R regression analysis: analyzing data for a certain ethnicity
  • Finding the slope for multiple points in selected columns
  • R: build separate models for each category

  • 如果确实显式拆分数据,则很容易会出现“对比度误差”,因此必须按组调整模型公式(即,需要动态生成模型公式)。一个更简单的解决方案是跳过为此组建立模型。

    您也可以将数据集随机划分为训练子集和测试子集,以便进行交叉验证。 R: how to debug "factor has new levels" error for linear model and prediction简要提到了这一点,您最好进行分层抽样,以确保训练部分的模型估计和测试部分的预测都成功。

    关于r - 如何调试 “contrasts can be applied only to factors with 2 or more levels”错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44200195/

    相关文章:

    r - 聚合/Group_by R中的第二个最小值

    r - 需要在R中跳过不同数量的行

    r - 如何在 lm 中指定参数估计之间的关系?

    r - 如何调整重叠{siar}中的椭圆重叠计算以匹配ggbiplot中的正态概率椭圆体?

    r - 复制数据框的观察结果,但也替换 R 中的特定变量值

    r - 在 R 中保存回归方程的最佳方法?

    r - 如何在 R 中对 'user-defined' 公式进行线性回归?

    R中的回归树

    r - 如何使用存储在 data.table 或 tibble 中的线性模型添加预测列?

    r - 如何用新数据预测 lm.wfit 后的结果?