Comment écrire succinctement une formule avec de nombreuses variables à partir d'un bloc de données?

127

Supposons que j'ai une variable de réponse et une donnée contenant trois covariables (comme exemple de jouet):

y = c(1,4,6)
d = data.frame(x1 = c(4,-1,3), x2 = c(3,9,8), x3 = c(4,-4,-2))

Je souhaite ajuster une régression linéaire aux données:

fit = lm(y ~ d$x1 + d$x2 + d$y2)

Existe-t-il un moyen d'écrire la formule, de sorte que je n'ai pas à écrire chaque covariable individuelle? Par exemple, quelque chose comme

fit = lm(y ~ d)

(Je veux que chaque variable de la trame de données soit une covariable.) Je demande parce que j'ai en fait 50 variables dans ma trame de données, donc je veux éviter d'écrire x1 + x2 + x3 + etc.

grautur
la source

Réponses:

202

Il existe un identifiant spécial que l'on peut utiliser dans une formule pour désigner toutes les variables, c'est l' .identifiant.

y <- c(1,4,6)
d <- data.frame(y = y, x1 = c(4,-1,3), x2 = c(3,9,8), x3 = c(4,-4,-2))
mod <- lm(y ~ ., data = d)

Vous pouvez également faire des choses comme ça, pour utiliser toutes les variables sauf une (dans ce cas, x3 est exclu):

mod <- lm(y ~ . - x3, data = d)

Techniquement, cela .signifie toutes les variables non déjà mentionnées dans la formule . Par exemple

lm(y ~ x1 * x2 + ., data = d)

.ferait seulement référence x3comme x1et x2sont déjà dans la formule.

Gavin Simpson
la source
La trame de données «d» comporte 4 colonnes (y, x1, x2 et x3). Donc, si la formule est "y ~", est-ce que le côté droit signifie "toutes les colonnes" sauf celles listées sur le côté gauche?
stackoverflowuser2010
1
@ stackoverflowuser2010 Oui, cela .signifie techniquement que toutes les variables data ne sont pas déjà dans la formule .
Gavin Simpson
1
@theforestecologist si vous voulez dire dataest une liste à partir de laquelle les variables de la formule sont recherchées dans cette liste, alors oui. Un bloc de données, une liste ou un environnement sont des options acceptables pour l' dataargument. Si ce n'est pas ce que vous voulez dire, vous devrez vous développer un peu plus.
Gavin Simpson
@Gavin. C'est ce que je voulais dire. Merci. Comment procéder pour cette méthode en utilisant les données [[x]] comme variable répertoriée par rapport au nom réel de la variable (par exemple, «x3»)? Par exemple, comment pourrais-je faire le travail suivant?:lm(d[[1]] ~ d[[3]] + ., data = d)
theforestecologist
Cela fonctionne hors namesde la liste; que vous avez ll <- list(y = rnorm(10), x = rnorm(10), z = rnorm(10), zz = runif(10)), puis les travaux suivants: lm(y ~ x + ., data = ll). Il n'y a donc pas beaucoup de raisons d'avoir vos données comme ça à moins qu'il ne s'agisse déjà d'une liste, mais cela fonctionne. L'exigence que les éléments de la formule soient de la même longueur impose certaines restrictions à ce que vous avez dans une liste. Les objets plus complexes ont probablement besoin de code pour extraire les éléments souhaités; s'il d[[1]]s'agissait d'une trame / matrice de données, vous avez besoin de code pour que cela fonctionne
Gavin Simpson
66

Une approche légèrement différente consiste à créer votre formule à partir d'une chaîne. Dans la formulapage d'aide, vous trouverez l'exemple suivant:

## Create a formula for a model with a large number of variables:
xnam <- paste("x", 1:25, sep="")
fmla <- as.formula(paste("y ~ ", paste(xnam, collapse= "+")))

Ensuite, si vous regardez la formule générée, vous obtiendrez:

R> fmla
y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + 
    x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + 
    x22 + x23 + x24 + x25
Juba
la source
1
Cela fonctionne très bien pour lire ces valeurs à partir d'un fichier. Merci!
Ben Sidhom
Notez que la partie as.formula est un must
Jinhua Wang
7

Oui bien sûr, ajoutez simplement la réponse en ytant que première colonne dans le dataframe et appelez lm()-la:

d2<-data.frame(y,d)
> d2
  y x1 x2 x3
1 1  4  3  4
2 4 -1  9 -4
3 6  3  8 -2
> lm(d2)

Call:
lm(formula = d2)

Coefficients:
(Intercept)           x1           x2           x3  
    -5.6316       0.7895       1.1579           NA  

En outre, mes informations sur R indiquent que l'affectation avec <-est recommandée =.

Bernd Elkemann
la source
Merci! Oui, je sais que tout le monde dit toujours d'utiliser <-, mais personne ne dit jamais pourquoi et = est plus facile à taper =).
grautur
2
@gratur Une des raisons est que des choses comme foo(bar <- 1:10)fonctionnent (et barsont créées) mais foo(bar = 1:10)échoueraient soit parce que ce barn'est pas un argument de fooet ne créera pas non barplus.
Gavin Simpson
2
Pourquoi le coefficient est-il de x3 NA?
ziyuang
6

Une extension de la méthode de juba est d'utiliser reformulate, une fonction qui est explicitement conçue pour une telle tâche.

## Create a formula for a model with a large number of variables:
xnam <- paste("x", 1:25, sep="")

reformulate(xnam, "y")
y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + 
    x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + 
    x22 + x23 + x24 + x25

Pour l'exemple dans l'OP, la solution la plus simple ici serait

# add y variable to data.frame d
d <- cbind(y, d)
reformulate(names(d)[-1], names(d[1]))
y ~ x1 + x2 + x3

ou

mod <- lm(reformulate(names(d)[-1], names(d[1])), data=d)

Notez que l'ajout de la variable dépendante au data.frame dans d <- cbind(y, d)est préférable non seulement parce qu'il permet l'utilisation de reformulate, mais aussi parce qu'il permet une utilisation future de l' lmobjet dans des fonctions telles que predict.

lmo
la source
2

Je construis cette solution, reformulatene fait pas attention si les noms de variables ont des espaces blancs.

add_backticks = function(x) {
    paste0("`", x, "`")
}

x_lm_formula = function(x) {
    paste(add_backticks(x), collapse = " + ")
}

build_lm_formula = function(x, y){
    if (length(y)>1){
        stop("y needs to be just one variable")
    }
    as.formula(        
        paste0("`",y,"`", " ~ ", x_lm_formula(x))
    )
}

# Example
df <- data.frame(
    y = c(1,4,6), 
    x1 = c(4,-1,3), 
    x2 = c(3,9,8), 
    x3 = c(4,-4,-2)
    )

# Model Specification
columns = colnames(df)
y_cols = columns[1]
x_cols = columns[2:length(columns)]
formula = build_lm_formula(x_cols, y_cols)
formula
# output
# "`y` ~ `x1` + `x2` + `x3`"

# Run Model
lm(formula = formula, data = df)
# output
Call:
    lm(formula = formula, data = df)

Coefficients:
    (Intercept)           x1           x2           x3  
        -5.6316       0.7895       1.1579           NA  

''

Christian Torrez
la source
0

Vous pouvez vérifier le package leapset en particulier les fonctions de regsubsets() fonction pour la sélection du modèle. Comme indiqué dans la documentation:

Sélection du modèle par recherche exhaustive, en avant ou en arrière pas à pas, ou remplacement séquentiel

un moine
la source