¿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.
-
ejecute
NA_preproc
para obtener casos más completos; -
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 alm
oglm
través del argumento dedata
; -
subset_vec
es el vector de índice pasado alm
oglm
mediante un argumento desubset
.
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 alm
oglm
través del argumento dedata
; -
subset_vec
es el vector de índice pasado alm
oglm
través del argumento desubset
.
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
-
divida su marco de datos por la variable de agrupación (vea
?split.data.frame
); -
trabaje a través de esos marcos de datos uno por uno, aplicando
debug_contr_error2
(la funciónlapply
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 yanyNA(x)
esTRUE
, hagax <- addNA(x)
. El "y" es importante. Six
no tieneNA
,addNA(x)
agregará un nivel<NA>
no utilizado. -
si una variable
x
es un carácter, hagax <- 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 soloNA
.
<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