r regression lm glm r-faq

¿Cómo depurar el error "los contrastes se pueden aplicar solo a factores con 2 o más niveles" de error?



regression lm (2)

Introducción

Se ha explicado bien qué es un "error de contraste": tiene un factor que solo tiene un nivel (o menos) . Pero, en realidad, este simple hecho se puede ocultar fácilmente porque los datos que realmente se utilizan para el ajuste del modelo pueden ser muy diferentes de lo que ha pasado. Esto sucede cuando tiene NA en sus datos, ha subconjunto sus datos, un factor tiene niveles no utilizados, o ha transformado sus variables y obtiene NaN alguna parte. Raramente se encuentra en esta situación ideal donde se puede detectar un factor de un solo nivel directamente desde str(your_data_frame) . Muchas preguntas sobre StackOverflow con respecto a este error no son reproducibles, por lo que las sugerencias de las personas pueden o no funcionar. Por lo tanto, aunque ahora hay 118 publicaciones relacionadas con este problema, los usuarios aún no pueden encontrar una solución adaptativa, por lo que esta pregunta se plantea una y otra vez. Esta respuesta es mi intento, para resolver este asunto "de una vez por todas", o al menos para proporcionar una guía razonable.

Esta respuesta tiene mucha información, así que primero permítanme hacer un resumen rápido.

debug_contr_error 3 funciones de ayuda para usted: debug_contr_error , debug_contr_error2 , NA_preproc .

Te recomiendo que los uses de la siguiente manera.

  1. ejecute NA_preproc para obtener casos más completos;
  2. ejecute su modelo, y si obtiene un "error de contraste", use debug_contr_error2 para la depuración.

La mayor parte de la respuesta muestra paso a paso cómo y por qué se definen estas funciones. Probablemente no sea perjudicial omitir esos procesos de desarrollo, pero no omita las secciones de "Estudios de casos y debates reproducibles".

Respuesta revisada

La respuesta original funciona perfectamente para OP , y ha ayudado con éxito a otros . Pero había fallado en otro lugar por falta de adaptabilidad. Mire la salida de str(ad.train) en la pregunta. Las variables de OP son numéricas o factores; No hay personajes. La respuesta original fue para esta situación. Si tiene variables de caracteres, aunque serán forzadas a factores durante el ajuste lm y glm , el código no las informará ya que no se proporcionaron como factores, así que el is.factor perderá. En esta expansión haré que la respuesta original sea más adaptativa.

Deje dat sea ​​su conjunto de datos pasado a lm o glm . Si no tiene un marco de datos de este tipo, es decir, todas sus variables están dispersas en el entorno global, debe reunirlas en un marco de datos. Lo siguiente puede no ser la mejor manera, pero funciona.

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

Paso 0: subconjunto explícito

Si ha utilizado el argumento de subset de lm o glm , comience por un subconjunto explícito:

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

Paso 1: eliminar casos incompletos

dat <- na.omit(dat)

Puede omitir este paso si ha pasado por el paso 0, ya que el subset elimina automáticamente los casos incompletos .

Paso 2: verificación de modo y conversión

Una columna de marco de datos suele ser un vector atómico, con un modo de lo siguiente: "lógico", "numérico", "complejo", "carácter", "sin procesar". Para la regresión, las variables de diferentes modos se manejan de manera diferente.

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

Una variable lógica es complicada. Puede tratarse como una variable ficticia ( 1 para TRUE ; 0 para FALSE ), por lo tanto, un "numérico", o puede convertirse en un factor de dos niveles. Todo depende de si model.matrix cree que es necesaria una coerción "a factor" a partir de la especificación de la fórmula de su modelo. Por simplicidad, podemos entenderlo como tal: siempre se coacciona a un factor, pero el resultado de aplicar contrastes puede terminar con la misma matriz de modelo como si se manejara como un maniquí directamente.

Algunas personas pueden preguntarse por qué "entero" no está incluido. Porque un vector entero, como 1:4 , tiene un modo "numérico" ( mode(1:4) prueba mode(1:4) ).

Una columna de marco de datos también puede ser una matriz con clase "AsIs", pero dicha matriz debe tener modo "numérico".

Nuestra comprobación es producir un error cuando

  • se encuentra un "complejo" o "en bruto";
  • se encuentra una variable de matriz "lógica" o de "carácter";

y proceda a convertir "lógico" y "carácter" a "numérico" de la clase "factor".

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

Tenga en cuenta que si una columna de marco de datos ya es una variable de factor, no se incluirá en ind1 , ya que una variable de factor tiene modo "numérico" ( mode(factor(letters[1:4])) prueba mode(factor(letters[1:4])) )).

Paso 3: eliminar los niveles de factores no utilizados

No tendremos niveles de factores no utilizados para las variables de factor convertidas del paso 2, es decir, las indexadas por ind1 . Sin embargo, las variables de factor que vienen con dat pueden tener niveles no utilizados (a menudo como resultado del paso 0 y el paso 1). Necesitamos eliminar cualquier posible nivel no utilizado de ellos.

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

Paso 4: resumir las variables de factores

Ahora estamos listos para ver qué y cuántos niveles de factor son realmente utilizados por lm o glm :

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

Para facilitarle la vida, he debug_contr_error esos pasos en una función debug_contr_error .

Entrada:

  • dat es su marco de datos pasado a lm o glm través del argumento de data ;
  • subset_vec es el vector de índice pasado a lm o glm mediante un argumento de subset .

Salida: una lista con

  • nlevels (una lista) proporciona el número de niveles de factores para todas las variables de factores;
  • levels (un vector) proporciona niveles para todas las variables de factores.

La función produce una advertencia, si no hay casos completos o no hay variables de factores para resumir.

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

Aquí hay un pequeño ejemplo construido.

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, vemos un error. Ahora mi debug_contr_error expone que f2 termina con un solo nivel.

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

Tenga en cuenta que la respuesta corta original es inútil aquí, ya que f2 se proporciona como una variable de carácter y no como una variable de factor.

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

Ahora veamos un ejemplo con una variable de matriz 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

Tenga en cuenta que una variable de factor sin niveles también puede causar un "error de contraste". Quizás se pregunte cómo es posible un factor de nivel 0. Bueno, es legítimo: nlevels(factor(character(0))) . Aquí terminará con factores de nivel 0 si no tiene casos completos.

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

Finalmente, veamos una situación en la que si f2 es una variable lógica.

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

Nuestro depurador predecirá un "error de contraste", pero ¿realmente sucederá?

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

No, al menos este no falla ( el coeficiente de NA se debe a la deficiencia de rango del modelo; no se preocupe ):

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

Es difícil para mí encontrar un ejemplo que dé un error, pero tampoco es necesario. En la práctica, no usamos el depurador para la predicción; lo usamos cuando realmente tenemos un error; y en ese caso, el depurador puede localizar la variable del factor infractor.

Quizás algunos puedan argumentar que una variable lógica no es diferente a una variable ficticia. Pero pruebe el ejemplo simple a continuación: depende de su fórmula.

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

Implementación más flexible usando el método "model.frame" de lm

También se recomienda pasar por R: cómo depurar el error "el factor tiene nuevos niveles" para el modelo lineal y la predicción , lo que explica lo que lm y glm hacen bajo el capó en su conjunto de datos. Comprenderá que los pasos 0 a 4 enumerados anteriormente solo están tratando de imitar dicho proceso interno. Recuerde, los datos que realmente se usan para el ajuste del modelo pueden ser muy diferentes de los que ha pasado .

Nuestros pasos no son completamente consistentes con dicho procesamiento interno. Para una comparación, puede recuperar el resultado del procesamiento interno mediante el method = "model.frame" en lm y glm . Pruebe esto en el pequeño ejemplo de dat previamente construido donde f2 es una variable de caracteres.

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 la práctica, model.frame solo realizará el paso 0 y el paso 1. También elimina las variables proporcionadas en su conjunto de datos pero no en la fórmula de su modelo. Por lo tanto, un marco modelo puede tener menos filas y columnas que lo que alimenta lm y glm . La coerción de tipos como se hizo en nuestro paso 2 se realiza mediante la model.matrix posterior, donde puede producirse un "error de contraste".

Hay algunas ventajas para obtener primero este marco de modelo interno, luego pasarlo a debug_contr_error (para que solo realice esencialmente los pasos 2 a 4).

ventaja 1: las variables no utilizadas en la fórmula de su modelo se ignoran

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

ventaja 2: capaz de hacer frente a variables transformadas

Es válido transformar variables en la fórmula del modelo, y model.frame registrará las transformadas en lugar de las originales. Tenga en cuenta que, incluso si su variable original no tiene NA , la transformada puede tener.

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

Dados estos beneficios, escribo otra función que envuelve model.frame y debug_contr_error .

Entrada :

  • form es tu fórmula modelo;
  • dat es el conjunto de datos pasado a lm o glm través del argumento de data ;
  • subset_vec es el vector de índice pasado a lm o glm través del argumento de subset .

Salida: una lista con

  • mf (un marco de datos) proporciona el marco del modelo (con el atributo "términos" descartado);
  • nlevels (una lista) proporciona el número de niveles de factores para todas las variables de factores;
  • levels (un vector) proporciona niveles para todas las variables de factores.

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

Pruebe el ejemplo de transformación de log anterior.

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

Prueba subset_vec también.

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

Ajuste del modelo por grupo y NA como niveles de factores

Si está ajustando el modelo por grupo, es más probable que obtenga un "error de contraste". Necesitas

  1. divida su marco de datos por la variable de agrupación (vea ?split.data.frame );
  2. trabaje a través de esos marcos de datos uno por uno, aplicando debug_contr_error2 (la función lapply puede ser útil para hacer este bucle).

Algunos también me dijeron que no pueden usar na.omit en sus datos, porque terminarán con muy pocas filas para hacer algo sensato. Esto puede ser relajado. En la práctica, se NA_integer_ NA_real_ NA_integer_ y NA_real_ , pero se puede conservar NA_character_ : simplemente agregue NA como nivel de factor. Para lograr esto, debe recorrer las variables en su marco de datos:

  • si una variable x ya es un factor y anyNA(x) es TRUE , haga x <- addNA(x) . El "y" es importante. Si x no tiene NA , addNA(x) agregará un nivel <NA> no utilizado.
  • si una variable x es un carácter, haga x <- factor(x, exclude = NULL) para obligarlo a un factor. exclude = NULL retendrá <NA> como nivel.
  • si x es "lógico", "numérico", "en bruto" o "complejo", no se debe cambiar nada. NA es solo NA .

<NA> nivel de factor <NA> no se eliminará mediante droplevels o na.omit , y es válido para construir una matriz modelo. Verifique los siguientes ejemplos.

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

Una vez que agrega NA como nivel en un factor / carácter, su conjunto de datos podría tener casos más completos de repente. Entonces puedes ejecutar tu modelo. Si aún recibe un "error de contraste", use debug_contr_error2 para ver qué ha sucedido.

Para su comodidad, escribo una función para este preprocesamiento de NA .

Entrada :

  • dat es su conjunto de datos completo .

Salida:

  • un marco de datos, con NA agregado como nivel para factor / carácter.

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 }

Estudios de casos y debates reproducibles

Los siguientes son especialmente seleccionados para estudios de casos reproducibles, ya que acabo de responderlos con las tres funciones auxiliares creadas aquí.

  • ¿Cómo hacer un GLM cuando "los contrastes se pueden aplicar solo a factores con 2 o más niveles"?
  • R: Error en los contrastes al ajustar modelos lineales con `lm`

También hay algunos otros subprocesos de buena calidad resueltos por otros usuarios de StackOverflow:

  • Factores que no se reconocen en una película usando map () (se trata del ajuste del modelo por grupo)
  • ¿Cómo descartar la observación de NA de los factores condicionalmente cuando se realiza una regresión lineal en R? (Esto es similar al caso 1 en la lista anterior)
  • Error de factor / nivel en modelo mixto (otra publicación sobre ajuste de modelo por grupo)

Esta respuesta tiene como objetivo depurar el "error de contraste" durante el ajuste del modelo. Sin embargo, este error también puede aparecer al usar predict para predicción. Tal comportamiento no es con predict.lm o predict.glm , sino con métodos de predicción de algunos paquetes. Aquí hay algunos hilos relacionados en StackOverflow.

  • Predicción en R - GLMM
  • Error en error de ''contrastes''
  • SVM predice en el marco de datos con diferentes niveles de factores
  • Usando predic con svyglm
  • debe un conjunto de datos contener todos los factores en SVM en R
  • Predicciones de probabilidad con modelos mixtos de enlaces acumulativos
  • debe un conjunto de datos contener todos los factores en SVM en R

También tenga en cuenta que la filosofía de esta respuesta se basa en la de lm y glm . Estas dos funciones son un estándar de codificación para muchas rutinas de ajuste de modelos , pero tal vez no todas las rutinas de ajuste de modelos se comporten de manera similar. Por ejemplo, lo siguiente no me parece transparente si mis funciones de ayuda serían realmente útiles.

  • Error con svychisq: ''el contraste se puede aplicar a factores con 2 o más niveles''
  • R empaqueta efectos y plm: "error en contrastes" cuando se intenta graficar efectos marginales
  • Los contrastes se pueden aplicar solo al factor
  • R: lawstat :: levene.test falla mientras Fligner Killeen funciona, así como car :: leveneTest
  • R - error geeglm: los contrastes se pueden aplicar solo a factores con 2 o más niveles

Aunque está un poco fuera de tema, aún es útil saber que a veces un "error de contraste" simplemente proviene de escribir un fragmento de código incorrecto. En los siguientes ejemplos, OP pasó el nombre de sus variables en lugar de sus valores a lm . Dado que un nombre es un carácter de valor único, luego se coacciona a un factor de nivel único y provoca el error.

  • Error en `contrastes <-` (` * tmp * `, value = contr.funs [1 + isOF [nn]]): los contrastes se pueden aplicar solo a factores con 2 o más niveles
  • Recorrer un vector de caracteres para usar en una función

¿Cómo resolver este error después de la depuración?

En la práctica, las personas quieren saber cómo resolver este asunto, ya sea a nivel estadístico o de programación.

Si está ajustando modelos en su conjunto de datos completo, entonces probablemente no haya una solución estadística, a menos que pueda imputar valores perdidos o recopilar más datos. Por lo tanto, simplemente puede recurrir a una solución de codificación para descartar la variable infractora. debug_contr_error2 devuelve nlevels que te ayuda a localizarlos fácilmente. Si no desea soltarlos, reemplácelos por un vector de 1 (como se explica en Cómo hacer un GLM cuando "los contrastes se pueden aplicar solo a factores con 2 o más niveles" ) y deje que lm o glm ocupen de La deficiencia de rango resultante.

Si está ajustando modelos en un subconjunto, puede haber soluciones estadísticas.

Ajustar modelos por grupo no requiere necesariamente que divida su conjunto de datos por grupo y ajuste modelos independientes. Lo siguiente puede darle una idea aproximada:

  • Análisis de regresión R: análisis de datos para una determinada etnia
  • Encontrar la pendiente para múltiples puntos en columnas seleccionadas
  • R: construye modelos separados para cada categoría

Si divide sus datos explícitamente, puede obtener fácilmente un "error de contraste", por lo que debe ajustar la fórmula de su modelo por grupo (es decir, debe generar dinámicamente fórmulas de modelo). Una solución más simple es omitir la construcción de un modelo para este grupo.

También puede dividir aleatoriamente su conjunto de datos en un subconjunto de entrenamiento y un subconjunto de prueba para que pueda realizar la validación cruzada. R: cómo depurar el error "el factor tiene nuevos niveles" para el modelo lineal y la predicción menciona brevemente esto, y será mejor que haga un muestreo estratificado para garantizar el éxito tanto de la estimación del modelo en la parte de entrenamiento como de la predicción en la parte de prueba.

Aquí están todas las variables con las que estoy trabajando:

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

Aquí está la película que estoy tratando de encajar:

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

Sé que son muchas variables (el plan es reducir mediante la selección de variables hacia adelante). Pero incluso sé que son muchas variables, ya sea int o Factor; que, según tengo entendido, las cosas deberían funcionar con un glm. Sin embargo, cada vez que intento adaptarme a este modelo obtengo:

Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels

¿Por qué me parece que R no trata mis variables de Factor como variables de Factor?

Incluso algo tan simple como:

ad.glm.test <- glm(WinLoss ~ factor(Team), data = ad.train, family = binomial(logit))

no funciona! (mismo mensaje de error)

Donde como este:

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

¡Trabajará!

Alguien sabe lo que está pasando aquí? ¿Por qué no puedo ajustar estas variables de Factor a mi glm?

¡Gracias por adelantado!

-Troy


Tal vez como un paso muy rápido, uno es verificar que efectivamente tenga al menos 2 factores. La forma rápida que encontré fue:

df %>% dplyr::mutate_all(as.factor) %>% str