Skip to content

Comment déboguer l'erreur "les contrastes ne peuvent être appliqués qu'aux facteurs à 2 niveaux ou plus" ?

Après cette vaste collecte d'informations, nous avons pu résoudre ce casse-tête que certains lecteurs peuvent avoir. Nous vous laissons la réponse et nous voulons vous être d'un grand soutien.

Solution :

Introduction

Ce qu'est une " erreur de contraste " a été bien expliqué : vous avez un facteur qui n'a qu'un seul niveau (ou moins). Mais en réalité, ce simple fait peut être facilement occulté car les données qui sont réellement utilisées pour l'ajustement du modèle peuvent être très différentes de celles que vous avez passées. Cela se produit lorsque vous avez NA dans vos données, vous avez sous-ensemblé vos données, un facteur a des niveaux inutilisés, ou vous avez transformé vos variables et obtenez NaN quelque part. Vous êtes rarement dans cette situation idéale où un facteur à un seul niveau peut être repéré à partir de str(your_data_frame) directement. De nombreuses questions sur StackOverflow concernant cette erreur ne sont pas reproductibles, donc les suggestions des gens peuvent ou non fonctionner. Par conséquent, bien qu'il y ait maintenant 118 messages concernant ce problème, les utilisateurs ne peuvent toujours pas trouver une solution adaptée, de sorte que cette question est soulevée encore et encore. Cette réponse est ma tentative, pour résoudre cette question "une fois pour toutes", ou au moins pour fournir un guide raisonnable.

Cette réponse contient de riches informations, alors laissez-moi d'abord faire un résumé rapide.

J'ai défini 3 fonctions d'aide pour vous : debug_contr_error, debug_contr_error2, NA_preproc.

Je vous recommande de les utiliser de la manière suivante .

  1. exécutez NA_preproc pour obtenir des cas plus complets ;
  2. exécutez votre modèle, et si vous obtenez une "erreur de contrastes", utilisez debug_contr_error2 pour le débogage.

La plupart de la réponse vous montre étape par étape comment et pourquoi ces fonctions sont définies. Il n'y a probablement aucun mal à sauter ces processus de développement, mais ne sautez pas les sections de "Reproducible case studies and Discussions".


Réponse révisée

La réponse originale fonctionne parfaitement pour OP, et a aidé avec succès quelques autres. Mais elle avait échoué ailleurs par manque d'adaptabilité. Regardez la sortie de str(ad.train) dans la question. Les variables de OP sont numériques ou des facteurs ; il n'y a pas de caractères. La réponse originale était pour cette situation. Si vous avez des variables de type caractère, bien qu'elles soient contraintes à des facteurs au cours de l'opération lm et glm fitting, elles ne seront pas signalées par le code puisqu'elles n'ont pas été fournies en tant que facteurs alors... is.factor les manquera. Dans cette extension, je vais rendre la réponse originale à la fois plus adaptative.

Soit dat est votre jeu de données passé à lm ou glm. Si vous ne disposez pas facilement d'un tel cadre de données, c'est-à-dire que toutes vos variables sont dispersées dans l'environnement global, vous devez les rassembler dans un cadre de données. La méthode suivante n'est peut-être pas la meilleure, mais elle fonctionne.

## `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))

Étape 0 : sous-ensemble explicite

Si vous avez utilisé le subset de lm ou glm, commencer par un sous-ensemble explicite :

## `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)

Étape 1 : suppression des cas incomplets

dat <- na.omit(dat)

Vous pouvez sauter cette étape si vous avez passé l'étape 0, puisque subset supprime automatiquement les cas incomplets.

Étape 2 : vérification et conversion des modes

Une colonne de cadre de données est généralement un vecteur atomique, avec un mode parmi les suivants : "logique", "numérique", "complexe", "caractère", "brut". Pour la régression, les variables de différents modes sont traitées différemment.

"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`

Une variable logique est délicate. Elle peut soit être traitée comme une variable muette (1 pour TRUE; 0 pour FALSE) donc un "numérique", ou bien il peut être contraint à un facteur à deux niveaux. Tout dépend si model.matrix pense qu'une coercition "vers un facteur" est nécessaire à partir de la spécification de votre formule de modèle. Pour la simplicité, nous pouvons le comprendre comme tel : il est toujours coercitif à un facteur, mais le résultat de l'application des contrastes peut aboutir à la même matrice de modèle que s'il était traité comme un dummy directement.

Certaines personnes peuvent se demander pourquoi "integer" n'est pas inclus. Parce qu'un vecteur entier, comme 1:4, dispose d'un mode "numérique" (essayer mode(1:4)).

Une colonne de cadre de données peut également être une matrice avec la classe "AsIs", mais une telle matrice doit avoir un mode "numérique".

Notre vérification consiste à produire une erreur lorsque

  • un "complexe" ou "brut" est trouvé.d;
  • une variable matricielle "logique" ou "caractère" est foun...d;

et procéder à la conversion des "logiques" et "caractères" en "numériques" de la classe "facteur".

## 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)

Notez que si une colonne de la trame de données est déjà une variable de type "facteur", elle ne sera pas incluse dans l'opération ind1, car une variable factorielle a un mode "numérique" (essayez... mode(factor(letters[1:4]))).

étape 3 : suppression des niveaux de facteurs non utilisés

Nous n'aurons pas de niveaux factoriels inutilisés pour les variables factorielles converties à partir de l'étape 2, c'est-à-dire celles indexées par ind1. Cependant, les variables factorielles qui viennent avec dat pourraient avoir des niveaux inutilisés (souvent comme résultat de l'étape 0 et de l'étape 1). Nous devons supprimer tous les niveaux inutilisés possibles de ces variables.

## 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)

étape 4 : résumer les variables factorielles

Maintenant, nous sommes prêts à voir quels et combien de niveaux de facteurs sont réellement utilisés par... lm ou glm:

## export factor levels actually used by `lm` and `glm`
lev <- lapply(dat[fctr], levels)

## count number of levels
nl <- lengths(lev)

Pour vous faciliter la vie, j'ai regroupé ces étapes dans une fonction debug_contr_error.

Input :

  • dat est votre cadre de données passé à lm ou glm via data argument ;
  • subset_vec est le vecteur d'index passé à lm ou glm via subset argument.

Sortie : une liste avec

  • nlevels (une liste) donne le nombre de niveaux de facteur pour toutes les variables de facteur ;
  • levels (un vecteur) donne les niveaux pour toutes les variables factorielles.

La fonction produit un avertissement, s'il n'y a pas de cas complets ou pas de variables factorielles à résumer.

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)
  }

Voici un minuscule exemple construit.

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

Bien, nous voyons une erreur. Maintenant mon debug_contr_error expose que f2 se retrouve avec un seul niveau.

debug_contr_error(dat)
#$nlevels
#f1 f2 
# 2  1 
#
#$levels
#$levels$f1
#[1] "a" "b"
#
#$levels$f2
#[1] "A"

Notez que la réponse courte originale est sans espoir ici, car f2 est fournie comme une variable de caractère et non comme une variable de facteur.

## old answer
tmp <- na.omit(dat)
fctr <- lapply(tmp[sapply(tmp, is.factor)], droplevels)
sapply(fctr, nlevels)
#f1 
# 2 
rm(tmp, fctr)

Voyons maintenant un exemple avec une variable matricielle. 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

Notez qu'une variable factorielle sans niveau peut également provoquer une "erreur de contraste". Vous vous demandez peut-être comment un facteur à 0 niveau est possible. Et bien c'est légitime : nlevels(factor(character(0))). Ici, vous vous retrouverez avec un facteur à 0 niveau si vous n'avez pas de cas complets.

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

Enfin, voyons une situation dans laquelle si f2 est une variable logique.

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

Notre débogueur va prédire une "erreur de contraste", mais cela va-t-il vraiment se produire ?

debug_contr_error(dat)$nlevels
#f1 f2 
# 2  1 

Non, au moins celle-ci n'échoue pas (la variable NA coefficient est dû à la déficience de rang du modèle ; ne vous inquiétez pas) :

lm(y ~ x + f1 + f2, data = dat)
#Coefficients:
#(Intercept)            x          f1b       f2TRUE  
#          0            1            0           NA

Il m'est difficile de trouver un exemple donnant une erreur, mais ce n'est pas non plus nécessaire. En pratique, on n'utilise pas le débogueur pour la prédiction ; on l'utilise quand on a vraiment une erreur ; et dans ce cas, le débogueur peut localiser la variable facteur incriminée.

Certains diront peut-être qu'une variable logique n'est pas différente d'une variable fictive. Mais essayez l'exemple simple ci-dessous : cela dépend effectivement de votre formule.

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

Mise en œuvre plus souple en utilisant "model.frame" méthode de lm

Il vous est également conseillé de passer par R : comment déboguer l'erreur "facteur a de nouveaux niveaux" pour le modèle linéaire et la prédiction, qui explique ce que... lm et glm font sous le capot sur votre ensemble de données. Vous comprendrez que les étapes 0 à 4 énumérées ci-dessus ne font qu'essayer d'imiter ce processus interne. Rappelez-vous, les données qui sont réellement utilisées pour l'ajustement du modèle peuvent être très différentes de celles que vous avez passées dans la base de données..

Nos étapes ne sont pas complètement cohérentes avec un tel traitement interne. Pour une comparaison, vous pouvez récupérer le résultat du traitement interne en utilisant. method = "model.frame" dans lm et glm. Essayez ceci sur le minuscule exemple construit précédemment datf2 est une variable de caractère.

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..]

En pratique, model.frame n'effectuera que l'étape 0 et l'étape 1. Il laisse également tomber les variables fournies dans votre ensemble de données mais pas dans votre formule de modèle. Donc, un cadre de modèle peut avoir à la fois moins de lignes et de colonnes que ce que vous alimentez. lm et glm. La coercition de type, comme cela a été fait dans notre étape 2, est faite par la suite. model.matrix où une "erreur de contraste" peut être produite.

Il y a quelques avantages à obtenir d'abord cette trame de modèle interne, puis à la passer à debug_contr_error (pour qu'elle n'effectue essentiellement que les étapes 2 à 4).

avantage 1 : les variables non utilisées dans votre formule de modèle sont ignorées.

## 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 

avantage 2 : capable de faire face à des variables transformées

Il est valide de transformer les variables dans la formule du modèle, et model.frame enregistrera celles qui ont été transformées au lieu de celles d'origine. Notez que, même si votre variable d'origine n'a aucune NA, celle qui est transformée peut en avoir.

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

Compte tenu de ces avantages, j'écris une autre fonction enveloppant... model.frame et debug_contr_error.

Entrée:

  • form est votre formule modèlea;
  • dat est le jeu de données passé à lm ou glm via data argument ;
  • subset_vec est le vecteur d'index passé à lm ou glm via subset argument.

Sortie : une liste avec

  • mf (un cadre de données) donne le cadre du modèle (avec l'attribut "terms" abandonné) ;
  • nlevels (une liste) donne le nombre de niveaux de facteur pour toutes les variables de facteur ;
  • levels (un vecteur) donne les niveaux pour toutes les variables factorielles.
## 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))
  }

Essayez le précédent log exemple de transformation.

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

Essayez subset_vec aussi bien.

## 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

Ajustement du modèle par groupe et NA comme niveaux de facteurs.

Si vous ajustez le modèle par groupe, vous êtes plus susceptible d'obtenir une "erreur de contrastes". Vous devez

  1. diviser votre cadre de données par la variable de regroupement (cf. ?split.data.frame);
  2. travailler à travers ces cadres de données un par un, en appliquant debug_contr_error2 (lapply peut être utile pour faire cette boucle).

Certains m'ont également dit qu'ils ne peuvent pas utiliser na.omit sur leurs données, parce qu'ils se retrouveront avec trop peu de lignes pour faire quelque chose de sensé. Cela peut être assoupli. En pratique, c'est le NA_integer_ et NA_real_ qui doivent être omises, mais NA_character_ peut être conservé : il suffit d'ajouter NA comme niveau de facteur. Pour cela, vous devez boucler les variables de votre cadre de données :

  • si une variable x est déjà un facteur et anyNA(x) est TRUE faites x <- addNA(x). Le "et" est important. Si x n'a pas NA, addNA(x) ajoutera un inutilisé.
  • si une variable x est un caractère, faites x <- factor(x, exclude = NULL) pour la contraindre à un facteur. exclude = NULL retiendra comme un niveau.
  • si x est "logique", "numérique", "brut" ou "complexe", rien ne doit être modifié. NA est juste NA.

le niveau du facteur ne sera pas abandonné par droplevels ou na.omitet il est valide pour construire une matrice de modèle. Vérifiez les exemples suivants .

## x is a factor with NA

x <- factor(c(letters[1:4], NA))  ## default: `exclude = NA`
#[1] a    b    c    d         ## 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    
#Levels: a b c d   ## it appears here

droplevels(x)    ## it can not be dropped
#[1] a    b    c    d    
#Levels: a b c d 

na.omit(x)  ## it is not omitted
#[1] a    b    c    d    
#Levels: a b c d 

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         ## 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    
#Levels: a b c d           ## now NA is a level

Une fois que vous avez ajouté NA comme un niveau dans un facteur / caractère, votre ensemble de données pourrait soudainement avoir des cas plus complets. Vous pouvez alors exécuter votre modèle. Si vous obtenez toujours une "erreur de contraste", utilisez debug_contr_error2 pour voir ce qui s'est passé.

Pour votre confort, j'écris une fonction pour cela NA prétraitement.

Entrée:

  • dat est votre complet complet.

Sortie :

  • un cadre de données, avec NA ajouté comme niveau pour le facteur / caractère.
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
  }

Études de cas reproductibles et discussions

Les suivants sont spécialement sélectionnés pour les études de cas reproductibles, car je viens d'y répondre avec les trois fonctions d'aide créées ici.

  • Comment faire un GLM lorsque "les contrastes ne peuvent être appliqués qu'aux facteurs à 2 niveaux ou plus" ?
  • R : erreur dans les contrastes lors de l'ajustement de modèles linéaires avec `lm`.

Il y a aussi quelques autres fils de discussion de bonne qualité résolus par d'autres utilisateurs de StackOverflow :

  • Facteurs non reconnus dans un lm utilisant map() (ceci concerne l'ajustement de modèle par groupe).
  • Comment abandonner l'observation NA des facteurs de manière conditionnelle lors d'une régression linéaire dans R ? (ceci est similaire au cas 1 dans la liste précédente).
  • Erreur de facteur/niveau dans un modèle mixte (un autre post sur l'ajustement de modèle par groupe).

Cette réponse vise à déboguer l'"erreur de contrastes" lors de l'ajustement du modèle. Cependant, cette erreur peut aussi apparaître lors de l'utilisation de predict pour la prédiction. Un tel comportement n'est pas avec predict.lm ou predict.glmmais avec les méthodes de prédiction de certains paquets. Voici quelques fils de discussion connexes sur StackOverflow.

  • Prédiction dans R - GLMM
  • Erreur dans l'erreur `contrasts'.
  • Prédiction SVM sur dataframe avec différents niveaux de facteurs.
  • Utilisation de predict avec svyglm
  • Un jeu de données doit-il contenir tous les facteurs dans le SVM en R ?
  • Prédictions de probabilité avec des modèles mixtes à liens cumulatifs
  • Un jeu de données doit-il contenir tous les facteurs des SVM en R ?

Notez aussi que la philosophie de cette réponse est basée sur celle de lm et de glm. Ces deux fonctions constituent une norme de codage pour de nombreuses routines d'ajustement de modèle, mais peut-être que toutes les routines d'ajustement de modèle ne se comportent pas de la même manière. Par exemple, ce qui suit ne me semble pas transparent pour savoir si mes fonctions d'aide seraient réellement utiles.

  • Erreur avec svychisq - 'le contraste peut être appliqué aux facteurs avec 2 niveaux ou plus'.
  • Paquets R effects & plm : "error in contrasts" quand on essaie de tracer les effets marginaux.
  • Les contrastes peuvent être appliqués uniquement au facteur
  • R : lawstat::levene.test échoue alors que Fligner Killeen fonctionne, ainsi que car::leveneTest.
  • R - geeglm Erreur : les contrastes ne peuvent être appliqués qu'aux facteurs avec 2 niveaux ou plus.

Bien qu'un peu hors sujet, il est toujours utile de savoir que parfois une "erreur de contrastes" provient simplement de l'écriture d'un mauvais morceau de code. Dans les exemples suivants, OP a passé le nom de ses variables plutôt que leurs valeurs à... lm. Comme un nom est un caractère à valeur unique, il est ensuite contraint à un facteur à un seul niveau et provoque l'erreur.

  • Erreur dans `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : les contrastes ne peuvent être appliqués qu'aux facteurs à 2 niveaux ou plus.
  • Boucler sur un vecteur de caractères pour l'utiliser dans une fonction.

Comment résoudre cette erreur après débogage ?

En pratique, les gens veulent savoir comment résoudre cette question, soit au niveau statistique, soit au niveau de la programmation.

Si vous ajustez des modèles sur votre ensemble de données complet, alors il n'y a probablement pas de solution statistique, sauf si vous pouvez imputer les valeurs manquantes ou collecter plus de données. Ainsi, vous pouvez simplement vous tourner vers une solution de codage pour abandonner la variable incriminée. debug_contr_error2 renvoie à nlevels ce qui vous permet de les localiser facilement. Si vous ne voulez pas les abandonner, remplacez-les par un vecteur de 1 (comme expliqué dans Comment faire un GLM lorsque "les contrastes ne peuvent être appliqués qu'à des facteurs à 2 niveaux ou plus" ? lm ou glm s'occuper de la déficience de rang qui en résulte.

Si vous ajustez des modèles sur un sous-ensemble, il peut y avoir des solutions statistiques.

L'ajustement de modèles par groupe ne nécessite pas nécessairement que vous divisiez votre ensemble de données par groupe et que vous ajustiez des modèles indépendants. Ce qui suit peut vous donner une idée approximative :

  • Analyse de régression R : analyse des données pour une certaine ethnie.
  • Trouver la pente pour plusieurs points dans des colonnes sélectionnées.
  • R : construire des modèles séparés pour chaque catégorie

Si vous divisez vos données explicitement, vous pouvez facilement obtenir une "erreur de contrastes", et donc devoir ajuster votre formule de modèle par groupe (c'est-à-dire que vous devez générer dynamiquement des formules de modèle). Une solution plus simple est de sauter la construction d'un modèle pour ce groupe.

Vous pouvez également partitionner aléatoirement votre jeu de données en un sous-ensemble d'entraînement et un sous-ensemble de test afin de pouvoir effectuer une validation croisée. R : comment déboguer l'erreur "facteur a de nouveaux niveaux" pour le modèle linéaire et la prédiction mentionne brièvement cela, et vous feriez mieux de faire un échantillonnage stratifié pour assurer le succès à la fois de l'estimation du modèle sur la partie formation et de la prédiction sur la partie test.

Vous pouvez ajouter de la valeur à nos informations en donnant votre ancienneté dans les références.



Utilisez notre moteur de recherche

Ricerca
Generic filters

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.