Voici toutes les variables avec lesquelles je travaille:
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 ...
Voici ce que je cherche à adapter:
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))
Je sais que c'est beaucoup de variables (le plan est de réduire via la sélection de variable en avant). Mais même savoir que beaucoup de variables sont soit int ou Factor; qui, si je comprends bien, devrait tout simplement fonctionner avec un médecin. Cependant, chaque fois que j'essaye de m'adapter à ce modèle, je reçois:
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels
Quel genre de chose me fait penser que R ne traite pas mes variables de facteur comme des variables de facteur pour une raison quelconque?
Même quelque chose d'aussi simple que:
ad.glm.test <- glm(WinLoss ~ factor(Team), data = ad.train, family = binomial(logit))
ne fonctionne pas! (même message d'erreur)
Où comme ceci:
ad.glm.test <- glm(WinLoss ~ Clearances, data = ad.train, family = binomial(logit))
Marchera!
Quelqu'un sait ce qui se passe ici? Pourquoi ne puis-je pas adapter ces variables de facteur à mon GLM?
Merci d'avance!
-Troy
Ce qu'est une "erreur de contraste" a été bien expliqué: vous avez un facteur qui n'a qu'un niveau (ou moins) . Mais en réalité, ce simple fait peut facilement être masqué, car les données réellement utilisées pour l'ajustement du modèle peuvent être très différentes de celles que vous avez transmises. Cela se produit lorsque vous avez NA
dans vos données, vous avez sous-configuré 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é directement de str(your_data_frame)
. De nombreuses questions sur StackOverflow concernant cette erreur ne sont pas reproductibles; par conséquent, les suggestions des utilisateurs peuvent ou non. peut ne pas fonctionner. Par conséquent, bien qu’il y ait à ce jour 118 messages , les utilisateurs ne trouvent toujours pas de solution adaptative, de sorte que cette question est posée à plusieurs reprises. Cette réponse est ma tentative, pour résoudre cette question "une fois pour toutes", ou du moins pour fournir un guide raisonnable.
Cette réponse est riche en informations, alors laissez-moi d’abord faire un bref résumé.
J'ai défini 3 fonctions d'assistance pour vous: debug_contr_error
, debug_contr_error2
, NA_preproc
.
Je vous recommande de les utiliser de la manière suivante.
NA_preproc
pour obtenir des cas plus complets;debug_contr_error2
pour le débogage.La plupart des réponses vous montrent pas à pas comment et pourquoi ces fonctions sont définies. Il n'y a probablement aucun mal à ignorer ces processus de développement, mais ne sautez pas les sections de "Etudes de cas reproductibles et discussions".
Le réponse originalefonctionne parfaitement pour OP , et a aidé avec succès certains autres . Mais il avait échoué ailleurs faute d'adaptabilité. Regardez le résultat de str(ad.train)
dans la question. Les variables de OP sont numériques ou des facteurs; il n'y a pas de personnages. La réponse initiale était pour cette situation. Si vous avez des variables de caractère, bien qu'elles soient forcées à prendre en compte des facteurs lors de l'ajustement lm
et glm
, elles ne seront pas signalées par le code car elles n'ont pas été fournies en tant que facteurs afin que is.factor
vont nous manquer. Dans cette extension, je vais rendre la réponse originale plus adaptative.
Soit dat
votre ensemble 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 regrouper dans un cadre de données. Ce qui suit n'est peut-être pas la meilleure solution, mais cela 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é l'argument subset
de lm
ou glm
, commencez 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: supprimez les cas incomplets
dat <- na.omit(dat)
Vous pouvez ignorer cette étape si vous avez franchi l'étape 0, car subset
élimine automatiquement les observations incomplètes .
Étape 2: vérification du mode et conversion
Une colonne de trame de données est généralement un vecteur atomique, avec un mode parmi: "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. Il peut soit être traité comme une variable muette (1
Pour TRUE
; 0
Pour FALSE
) et donc un "numérique", ou peut être forcé à un facteur à deux niveaux. Tout dépend de si model.matrix
Pense qu'une contrainte "à factoriser" est nécessaire à partir de la spécification de votre formule de modèle. Pour simplifier, nous pouvons le comprendre comme tel: il est toujours contraint de prendre en compte un facteur, mais le résultat de l’application de contrastes peut aboutir à la même matrice de modèle que si elle était traitée directement comme un mannequin.
Certaines personnes peuvent se demander pourquoi "entier" n'est pas inclus. Parce qu'un vecteur entier, comme 1:4
, A un mode "numérique" (try mode(1:4)
).
Une colonne de trame de données peut également être une matrice avec la classe "AsIs", mais une telle matrice doit avoir le mode "numérique".
Notre vérification est de produire une erreur lorsque
et procéder à la conversion de "logical" et "character" en "numérique" de la classe "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)
Notez que si une colonne de trame de données est déjà une variable de facteur, elle ne sera pas incluse dans ind1
, Car une variable de facteur a le mode "numérique" (try mode(factor(letters[1:4]))
).
étape 3: supprimez les niveaux de facteur non utilisés
Nous n’aurons pas de niveaux de facteur inutilisés pour les variables de facteur converties à partir de l’étape 2, c’est-à-dire celles indexées par ind1
. Cependant, les variables factorielles fournies avec dat
peuvent avoir des niveaux inutilisés (souvent à la suite des étapes 0 et 1). Nous devons supprimer tous les niveaux inutilisés possibles.
## 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écapitulatif des variables factorielles
Nous sommes maintenant 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 simplifier la vie, j'ai résumé ces étapes dans une fonction debug_contr_error
.
Entrée:
dat
est votre trame de données passée à lm
ou glm
via data
argument;subset_vec
Est le vecteur d'index transmis à l'argument lm
ou glm
via subset
.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 ni 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 petit 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 termine 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 fourni sous forme de variable de caractère et non 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 niveaux peut également causer une "erreur de contraste". Vous pouvez vous demander comment un facteur de niveau 0 est possible. Et bien c'est légitime: nlevels(factor(character(0)))
. Ici, vous vous retrouverez avec un facteur de niveau 0 si vous n'avez pas de cas complet.
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 quelques cas où 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 prédira une "erreur de contraste", mais cela arrivera-t-il vraiment?
debug_contr_error(dat)$nlevels
#f1 f2
# 2 1
Non, au moins celui-ci n'échoue pas ( le coefficient NA
est dû au manque 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 d'erreur, mais ce n'est pas nécessaire. En pratique, nous n'utilisons 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 de facteur incriminée.
Certains diront peut-être qu'une variable logique n'est pas différente d'un mannequin. Mais essayez l’exemple simple ci-dessous: cela dépend 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
"model.frame"
De lm
Il est également conseillé de passer R: comment déboguer l'erreur "facteur a de nouveaux niveaux" pour le modèle linéaire et la prédiction , ce qui explique ce que lm
et glm
font sous le capot sur votre jeu de données. Vous comprendrez que les étapes 0 à 4 énumérées ci-dessus ne font qu'essayer d'imiter un tel processus interne. N'oubliez pas que les données réellement utilisées pour l'ajustement du modèle peuvent être très différentes de celles que vous avez transmises .
Nos étapes ne sont pas complètement compatibles 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 petit exemple précédemment construit dat
où f2
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'exécutera que les étapes 0 et 1. Il supprimera également les variables fournies dans votre jeu de données mais pas dans votre formule de modèle. Ainsi, un cadre de modèle peut contenir à la fois moins de lignes et de colonnes que ce que vous avez alimenté lm
et glm
. La contrainte de type comme effectuée à l'étape 2 est effectuée par le dernier model.matrix
Où une "erreur de contraste" peut être générée.
Il existe quelques avantages à obtenir d’abord ce cadre de modèle interne, puis à le transmettre à debug_contr_error
(Afin qu’il effectue uniquement 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 gérer des variables transformées
Il est valide de transformer des variables dans la formule du modèle et model.frame
Enregistrera celles transformées au lieu de celles d'origine. Notez que, même si votre variable d'origine n'a pas de NA
, celle qui a été 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 qui résume model.frame
Et debug_contr_error
.
Entrée :
form
est votre formule modèle;dat
est l'ensemble de données passé à lm
ou glm
via data
argument;subset_vec
Est le vecteur d'index transmis à l'argument lm
ou glm
via subset
.Sortie: une liste avec
mf
(un cadre de données) donne le cadre du modèle (attribut "termes" supprimé);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 exemple de transformation 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
Essayez aussi 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
?split.data.frame
);debug_contr_error2
(la fonction lapply
peut être utile pour effectuer cette boucle).Certains m'ont aussi dit qu'ils ne peuvent pas utiliser na.omit
Sur leurs données, car il y aura trop de lignes pour faire quoi que ce soit de raisonnable. Cela peut être assoupli. En pratique, ce sont les NA_integer_
Et NA_real_
Qui doivent être omis, mais NA_character_
Peut être conservé: ajoutez simplement NA
en tant que niveau de facteur. Pour ce faire, vous devez parcourir les variables de votre bloc de données:
x
est déjà un facteur et que anyNA(x)
est TRUE
, faites x <- addNA(x)
. Le "et" est important. Si x
n'a pas de NA
, addNA(x)
ajoutera un niveau inutilisé <NA>
.x
est un caractère, faites x <- factor(x, exclude = NULL)
pour la contraindre à un facteur. exclude = NULL
Conservera <NA>
En tant que niveau.x
est "logique", "numérique", "brut" ou "complexe", rien ne doit être changé. NA
n'est que NA
.Le niveau de facteur <NA>
Ne sera pas supprimé de droplevels
ou na.omit
, Et il est valide pour la construction d'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 <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
Une fois que vous ajoutez NA
en tant que niveau dans un facteur/caractère, votre jeu de données peut soudainement comporter des observations plus complètes. Ensuite, vous pouvez 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 commodité, j’écris une fonction pour ce prétraitement NA
.
Entrée :
dat
est votre jeu de données complet .Sortie:
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
}
Les suivants sont spécialement sélectionnés pour des études de cas reproductibles, comme je viens de leur répondre avec les trois fonctions d'aide créées ici.
Il existe également quelques autres threads de bonne qualité résolus par d'autres utilisateurs de StackOverflow:
Cette réponse vise à déboguer "l'erreur de contraste" lors de l'ajustement du modèle. Cependant, cette erreur peut également apparaître lorsque vous utilisez predict
pour la prédiction. Un tel comportement ne se produit pas avec predict.lm
Ou predict.glm
, Mais avec les méthodes de prédiction de certains paquets. Voici quelques discussions sur StackOverflow.
Notez également que la philosophie de cette réponse est basée sur celle de lm
et glm
. Ces deux fonctions constituent une norme de codage pour de nombreuses routines d’ajustement de modèles , mais toutes les routines d’ajustement de modèles ne se comportent peut-être pas de la même manière. Par exemple, ce qui suit ne semble pas clair pour moi si les fonctions d'assistance seraient réellement utiles.
Bien qu'un peu hors sujet, il est toujours utile de savoir que parfois une "erreur de contraste" vient simplement de l'écriture d'un mauvais morceau de code. Dans les exemples suivants, OP a passé le nom de leurs variables plutôt que leurs valeurs à lm
. Dans la mesure où un nom est un caractère à valeur unique, il est ensuite contraint à un facteur à niveau unique et provoque l'erreur.
En pratique, les gens veulent savoir comment résoudre ce problème, que ce soit au niveau statistique ou au niveau de la programmation.
Si vous ajustez des modèles sur l'ensemble de vos données, il n'existe probablement aucune solution statistique, à moins que vous ne puissiez imputer des valeurs manquantes ou collecter davantage de données. Ainsi, vous pouvez simplement vous tourner vers une solution de codage pour supprimer la variable incriminée. debug_contr_error2
Renvoie nlevels
, ce qui vous aide à les localiser facilement. Si vous ne voulez pas les supprimer, 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 ou plusieurs niveaux"? ) et laissez lm
ou glm
traiter de la déficience de rang qui en résulte.
Si vous ajustez des modèles sur un sous-ensemble, il peut exister des solutions statistiques.
L'ajustement de modèles par groupe ne nécessite pas nécessairement la division de votre jeu de données par groupe et l'ajustement de modèles indépendants. Ce qui suit peut vous donner une idée approximative:
Si vous scindez vos données de manière explicite, vous pouvez facilement obtenir une "erreur de contraste". Vous devez donc ajuster votre formule de modèle par groupe (en d'autres termes, vous devez générer de manière dynamique des formules de modèle). Une solution plus simple consiste à ignorer la construction d'un modèle pour ce groupe.
Vous pouvez également partitionner votre jeu de données de manière aléatoire en un sous-ensemble d'apprentissage et un sous-ensemble de test afin de pouvoir effectuer une validation croisée. R: comment déboguer "le facteur a de nouveaux niveaux" erreur pour le modèle linéaire et la prédiction le mentionne brièvement, et vous feriez mieux de faire un échantillonnage stratifié pour assurer le succès de l’estimation du modèle pour prédiction sur la partie test.
Une première étape consiste peut-être à vérifier que vous avez bien au moins 2 facteurs. Le moyen rapide que j'ai trouvé était:
df %>% dplyr::mutate_all(as.factor) %>% str