Corso
Se ci pensi, molte funzioni in R fanno uso delle formule: pacchetti come ggplot2, stats, lattice e dplyr le utilizzano tutti! Esempi comuni di funzioni in cui userai questi oggetti R sono glm(), lm(), facet_wrap(), ecc. Ma che cosa sono esattamente queste formule e perché dovresti usarle?
Strutture dati in R
Dal momento che le formule sono una classe speciale nel linguaggio di programmazione R, è una buona idea rivedere brevemente i tipi di dati e le strutture dati che hai a disposizione in questo linguaggio.
Ricorda R è un linguaggio di programmazione orientato agli oggetti: è organizzato attorno agli oggetti. In R tutto è un oggetto.
Partiamo dall'inizio: nella programmazione, lavori con strutture dati che memorizzano i tuoi dati e funzioni che li elaborano. Una struttura dati è l'interfaccia ai dati organizzati nella memoria del computer. Come afferma la Definizione del linguaggio R, R non fornisce accesso diretto alla memoria del computer, ma mette a disposizione una serie di strutture dati specializzate a cui puoi fare riferimento come "oggetti". Le strutture dati sono ognuna progettata per ottimizzare qualche aspetto dell'archiviazione, dell'accesso o dell'elaborazione.
Le cinque principali strutture dati in R sono:
- Vettore atomico,
- Lista,
- Matrice,
- Data frame e
- Array
# Create variables
a <- c(1,2,3,4,5,6,7,8,9)
b <- list(x = LifeCycleSavings[,1], y = LifeCycleSavings[,2])
Suggerimento: puoi usare la funzione typeof() per restituire il tipo di un oggetto R. Il tipo di un oggetto ti dice di più sul tipo (interno a R) o sulla modalità di archiviazione di qualsiasi oggetto:
# Retrieve the types of `a` and `b`
typeof(a)
typeof(b)
'double'
'list'
Nell'esempio sopra in cui hai definito le variabili a e b, puoi vedere che le strutture dati contengono sequenze di elementi. Questi elementi possono essere dello stesso o di diverso tipo di dato. In R puoi trovare i seguenti 6 tipi di dato atomici:
- numeric, come
100,5,4, include gli interi; - character, come
"Hello","True"o"23.4", consiste in stringhe di caratteri da tastiera; - logical, come
TRUEoFALSE, consiste in "valori di verità"; - raw, come
48 65 6c 6c 6f, consiste in bit; - complex, come
2+5i, include numeri complessi; e, infine, - double, come
3.14, include numeri decimali.
Quasi tutti gli oggetti in R hanno attributi associati. Ad esempio, potresti già sapere che matrici e array sono semplicemente vettori con l'attributo dim e opzionalmente dimnames associati al vettore. Gli attributi sono usati per implementare la struttura a classi utilizzata in R. Come linguaggio orientato agli oggetti, il concetto di classi, insieme ai metodi, è centrale. Una classe è una definizione di un oggetto. Definisce quali informazioni contiene l'oggetto e come quell'oggetto può essere usato.
Guarda il seguente esempio:
# Retrieve the classes of `a` and `b`
class(a)
class(b)
'numeric'
'list'
Nota che se un oggetto non ha un attributo class, ha una classe implicita, "matrix", "array" o il risultato della funzione mode().
Alcune delle classi speciali che puoi incontrare sono Date e Formule; ed è proprio quest'ultima l'argomento del tutorial di oggi!
Cos'è una formula in R?
Come hai letto nell'introduzione di questo tutorial, potresti aver già visto comparire le formule lavorando con pacchetti come ggplot2 o in funzioni come lm(). Poiché di solito usi le formule all'interno di queste chiamate di funzione per esprimere l'idea di un modello statistico, è logico che spesso utilizzi questi oggetti R nelle funzioni di modellazione così come in alcune funzioni grafiche.
Giusto?
Tuttavia, le formule non sono limitate ai modelli. Sono uno strumento potente e generico che ti permette di catturare due cose:
- Un'espressione non valutata, e
- Il contesto o ambiente in cui l'espressione è stata creata.
Questo spiega perché le formule vengono usate all'interno delle chiamate di funzione per generare un "comportamento speciale": ti permettono di catturare i valori delle variabili senza valutarli, così che possano essere interpretati dalla funzione.
Con le strutture dati ben presenti in mente, puoi descrivere questi oggetti R come oggetti di “linguaggio” o espressioni non valutate che hanno classe “formula” e un attributo che memorizza l'ambiente.
Nella sezione precedente hai visto che gli oggetti hanno determinati tipi (interni a R) che indicano come l'oggetto è stato memorizzato. In questo caso, una formula è un oggetto di tipo "language".
Ma cosa significa esattamente?
Beh, di solito ti imbatti in questo tipo di oggetti quando stai elaborando il linguaggio R stesso. Dai un'occhiata al seguente esempio per capirlo meglio:
# Retrieve the object type
typeof(quote(x * 10))
# Retrieve the class
class(quote(x * 10))
'language'
'call'
Nell'esempio sopra chiedi a R di restituire il tipo e la classe di quote(x*10). Di conseguenza, vedi che il tipo di quote(x*10) è 'language' e la class è 'call'.
Questo non è di certo una formula, perché dovresti vedere che class() restituisce 'formula'!
Ma allora cosa lo è?
Qualcosa che caratterizza le formule in R è l'operatore tilde ~. Con questo operatore puoi dire: "cattura il significato di questo codice, senza valutarlo subito". Questo spiega anche perché puoi pensare a una formula in R come a un operatore di "quoting".
Ma come appare esattamente una formula? Guarda più da vicino la seguente riga di codice:
# A formula
c <- y ~ x
d <- y ~ x + b
# Double check the class of `c`
class(c)
'formula'
La variabile a sinistra della tilde (~) è chiamata "variabile dipendente", mentre le variabili a destra sono chiamate "variabili indipendenti" e sono unite dal segno più +.
È utile sapere che i nomi di queste variabili cambiano a seconda del contesto. Potresti aver già visto le variabili indipendenti come "predictor (variable)", "controlled variable", "feature", ecc. Allo stesso modo, potresti incontrare le variabili dipendenti come "response variable", "outcome variable" o "label".
Nota che, anche se la formula d che hai definito nel blocco di codice sopra contiene alcune variabili, la struttura di base di una formula è in realtà solo il simbolo tilde ~ e almeno una variabile indipendente o di destra.
Ricorda che le formule sono in realtà oggetti di linguaggio con attributi che memorizzano l'ambiente:
# Return the type of `d`
typeof(d)
# Retrieve the attributes of `d`
attributes(d)
'language'
$class
[1] "formula"
$.Environment
<environment: R_GlobalEnv>
Come hai visto negli esempi sopra, le variabili incluse in una formula possono essere, ad esempio, vettori. Tuttavia, spesso vedrai che le variabili incluse nelle formule provengono da un data frame, proprio come nel seguente esempio:
Sepal.Width ~ Petal.Width + log(Petal.Length) + Species
Nota che eventuali valori assegnati ai simboli nella formula non vengono acceduti quando la formula stessa viene creata.
Ora che sai come appaiono le formule e cosa sono in R, è bene menzionare che l'oggetto formula sottostante varia a seconda che tu abbia una formula a un solo lato o a due lati. Puoi riconoscere la prima guardando la variabile sul lato sinistro. Se non c'è, come in ~ x, hai una formula a un solo lato.
Questo significa anche che una formula a un solo lato avrà lunghezza 2, mentre una formula a due lati avrà lunghezza 3.
Non del tutto convinto? Guarda il seguente blocco di codice. Puoi accedere agli elementi di una formula con l'aiuto delle parentesi quadre: [[ e ]].
e <- ~ x + y + z
f <- y ~ x + b
# Return the length of `g`
length(e)
length(f)
# Retrieve the elements at index 1 and 2
e[[1]]
e[[2]]
f[[3]]
2
3
`~`
x + y + z
x + b
Perché usare le formule in R?
Come hai visto, le formule sono strumenti potenti e generici che ti permettono di catturare i valori delle variabili senza valutarli, in modo che possano essere interpretati dalla funzione. Questa è già una parte della risposta sul perché dovresti usare le formule in R.
Inoltre, usi questi oggetti R per esprimere una relazione tra variabili.
Ad esempio, nella prima riga di codice del blocco qui sotto, dici "y è una funzione di x, a e b"; Ovviamente, puoi incontrare formule più complesse, come nella seconda riga di codice, dove intendi dire "la larghezza del sepalo è una funzione della larghezza del petalo, condizionata dalla specie".
y ~ x + a + b
Sepal.Width ~ Petal.Width | Species
y ~ x + a + b
Sepal.Width ~ Petal.Width | Species
Uso delle formule in R
Ora che sai di più sul "cosa" e sul "perché" di questi oggetti speciali in R, è il momento di imparare come puoi utilizzare formule di base e più complesse! In questa sezione, non solo vedrai come creare e concatenare formule di base, ma scoprirai anche come costruirne di più complesse con l'aiuto degli operatori.
Come creare una formula in R
Questo già lo sai fare! Hai già visto alcuni esempi in questo tutorial, ma ricapitoliamo:
y ~ x
~ x + y + z
g <- y ~ x + b
Esatto: puoi semplicemente digitare la formula!
Tuttavia, probabilmente ti capiterà una situazione in cui devi o vuoi creare una formula a partire da un oggetto R, come una stringa. In tali casi, puoi usare la funzione formula o as.formula():
"y ~ x1 + x2"
h <- as.formula("y ~ x1 + x2")
h <- formula("y ~ x1 + x2")
Facile!
Come concatenare formule
Per unire o mettere insieme più formule, hai due opzioni. Per prima cosa, puoi creare variabili separate per ciascuna formula e poi usare list():
# Create variables
i <- y ~ x
j <- y ~ x + x1
k <- y ~ x + x1 + x2
# Concatentate
formulae <- list(as.formula(i),as.formula(j),as.formula(k))
# Double check the class of the list elements
class(formulae[[1]])
'formula'
In alternativa, puoi anche usare la funzione lapply(), passando come primo argomento un vettore con tutte le tue formule e as.formula come funzione da applicare a ciascun elemento di quel vettore:
# Join all with `c()`
l <- c(i, j, k)
# Apply `as.formula` to all elements of `f`
lapply(l, as.formula)
[[1]]
y ~ x
[[2]]
y ~ x + x1
[[3]]
y ~ x + x1 + x2
Operatori per le formule
Con queste basi, sei pronto per approfondire formule più complesse! In precedenza hai già visto che ciò che caratterizza le formule è il simbolo tilde ~. Oltre a quel simbolo, hai visto che servono anche variabili dipendenti e indipendenti e che queste possono essere unite con il segno più +.
Ma c'è dell'altro!
Oltre al simbolo +, ci sono anche altri simboli che possono aggiungere un significato speciale alle tue formule:
-per rimuovere termini;:per l'interazione;*per il crossing;%in%per il nesting; e^per limitare il crossing al grado specificato.
Vedrai esempi di tutti questi operatori nel resto di questa sezione! Iniziamo dagli operatori + e -:
# Use multiple independent variables
y ~ x1 + x2
# Ignore objects in an analysis
y ~ x1 - x2
Nota che più spesso ti serviranno i simboli : e * in un contesto di regressione, quando devi specificare termini di interazione. Come hai letto sopra, il primo simbolo è usato per l'interazione, il che significa che vuoi solo l'interazione delle variabili, e non le variabili stesse. Questo è in contrasto con il secondo simbolo, che è usato per il crossing: lo usi per includere due variabili e la loro interazione.
Attenzione! Usando questi operatori, alcune formule possono sembrare diverse ma saranno in realtà equivalenti. Considera i seguenti esempi, che produrranno la stessa regressione:
y ~ x1 * x2
y ~ x1 + x2 + x1:x2
Non ti è chiaro come possano essere uguali? Considera i seguenti blocchi di codice R:
# Set seed
set.seed(123)
# Data
x = rnorm(5)
x2 = rnorm(5)
y = rnorm(5)
# Model frame
model.frame(y ~ x * x2, data = data.frame(x = x, y = y, x2=x2))
| y | x | x2 |
|---|---|---|
| 1.7150650 | -0.56047565 | 1.2240818 |
| 0.4609162 | -0.23017749 | 0.3598138 |
| -1.2650612 | 1.55870831 | 0.4007715 |
| -0.6868529 | 0.07050839 | 0.1106827 |
| -0.4456620 | 0.12928774 | -0.5558411 |
model.frame(y ~ x + x2 + x:x2, data = data.frame(x = x, y = y, x2))
| y | x | x2 |
|---|---|---|
| 1.7150650 | -0.56047565 | 1.2240818 |
| 0.4609162 | -0.23017749 | 0.3598138 |
| -1.2650612 | 1.55870831 | 0.4007715 |
| -0.6868529 | 0.07050839 | 0.1106827 |
| -0.4456620 | 0.12928774 | -0.5558411 |
Non preoccuparti se non conosci ancora la funzione model.frame(); Ne vedrai di più più avanti in questo tutorial!
Inoltre, ecco un esempio di nesting, che puoi espandere a y ~ a + a:b:
y ~ a + b %in% a
Tutti questi operatori sono davvero utili, ma cosa succede se vuoi eseguire un'operazione aritmetica? Diciamo che vuoi includere x e x^2 nel tuo modello. In tali casi, potresti essere tentato di scrivere la seguente formula: y ~ x + x^2.
Ma il risultato sarà esattamente quello che vuoi? Guarda un po'!
model.frame( y ~ x + x^2, data = data.frame(x = rnorm(5), y = rnorm(5)))
y ~ x + x^2
| y | x |
|---|---|
| -0.2053091 | 1.18231565 |
| -0.3030972 | 0.04779636 |
| -0.7621604 | 0.86382418 |
| -0.1377784 | -1.18333097 |
| -0.3813125 | -1.25247842 |
Non è il risultato che ti aspettavi!
Nell'esempio sopra, non proteggi l'espressione aritmetica e, di conseguenza, R scarterebbe il termine x^2, in quanto considerato un duplicato di x.
Perché?
Beh, x ti darebbe l'effetto principale di x, e x^2 ti darebbe l'effetto principale e l'interazione di secondo ordine di x. Alla fine, finirai per includere x nel model frame perché l'effetto principale di x è già incluso dal termine x nella formula, e non c'è nulla con cui incrociare x per ottenere le interazioni di secondo ordine nel termine x^2.
Per evitarlo, hai un paio di soluzioni:
- Puoi calcolare e memorizzare in anticipo tutte le variabili
- Usi l'operatore
I()o "as-is":y ~ x + I(x^2)
Dai un'occhiata al seguente esempio per capire le conseguenze di aggiungere la funzione I() al tuo codice:
model.frame( y ~ x + I(x^2), data = data.frame(x = rnorm(5), y = rnorm(5)))
y ~ x + I(x^2)
| y | x | I(x^2) |
|---|---|---|
| 1.414090 | -0.1996230 | 0.039849.... |
| 1.777646 | -1.0675904 | 1.139749.... |
| 1.710137 | -1.4071841 | 1.980167.... |
| 1.259111 | -1.3747289 | 1.889879.... |
| -1.490866 | 0.8323668 | 0.692834.... |
Questa ultima riga di codice dice a R di calcolare i valori di x^2 prima di usare la formula. Nota anche che puoi usare l'operatore "as-is" per scalare una variabile per un modello; basta racchiudere il nome della variabile rilevante in I():
y ~ I(2 * x)
Tutto questo può sembrare piuttosto astratto vedendo gli esempi sopra, quindi copriamo altri casi; Ad esempio, considera la regressione polinomiale. In tali casi, dovrai lavorare con la funzione I(). Per un modello ANOVA fattoriale limitato a interazioni di depth=2, invece, non ti servirà questa funzione perché vuoi espandere a una formula che contenga gli effetti principali per a, b e c insieme alle loro interazioni di secondo ordine:
# Polynomial Regression
y ~ x + I(x^2) + I(x^3)
# Factorial ANOVA
y ~ (a*b*c)^2
Infine, c'è un'altra funzionalità utile quando lavori con più variabili: l'operatore .. Quando lo usi all'interno di una formula, ti riferisci a tutte le altre variabili nella matrice che non sono ancora state incluse nel modello. Questo è comodo, per esempio, quando vuoi eseguire una regressione su una matrice o un dataframe e non vuoi digitare tutte le variabili:
y ~ .
Come ispezionare le formule in R
Quando hai creato una formula, potresti anche volerla ispezionare. In questa sezione, ti presenteremo alcuni strumenti che puoi usare per esplorare ulteriormente gli oggetti speciali di R che hai creato!
Nota che hai già visto alcuni modi per esaminare le formule nelle sezioni precedenti: hai visto funzioni come attributes(), typeof(), class(), ecc.
terms() function
Per esaminare e confrontare diverse formule, puoi usare la funzione terms():
m <- formula("y ~ x1 + x2")
terms(m)
y ~ x1 + x2
attr(,"variables")
list(y, x1, x2)
attr(,"factors")
x1 x2
y 0 0
x1 1 0
x2 0 1
attr(,"term.labels")
[1] "x1" "x2"
attr(,"order")
[1] 1 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
all.vars
Se vuoi conoscere i nomi delle variabili nel modello, puoi usare all.vars. Con questa funzione, restituisci un vettore di caratteri che contiene tutti i nomi che compaiono in una formula:
print(all.vars(m))
[1] "y" "x1" "x2"
update() function
Per modificare le formule senza convertirle in caratteri, puoi usare la funzione update():
update(y ~ x1 + x2, ~. + x3)
y ~ x1 + x2 + x3
Nota che avresti potuto anche aggiornare la formula convertendola in carattere con as.character(); poi puoi costruire formule molto rapidamente usando paste(). Ad esempio, se vuoi aggiungere un'altra variabile sul lato destro della tua formula, puoi semplicemente concatenarla:
as.formula(paste("y ~ x1 + x2", "x3", sep = "+"))
factors <- c("x2", "x3")
as.formula(paste("y~", paste(factors, collapse="+")))
y ~ x1 + x2 + x3
y ~ x2 + x3
Nel blocco di codice sopra, puoi vedere che puoi utilizzare gli argomenti sep o collapse per indicare una stringa di caratteri con cui separare i termini all'interno della tua formula.
Tuttavia, usare paste() non è certo l'unico modo per apportare modifiche alla tua formula: puoi anche usare reformulate():
reformulate(termlabels = factors, response = 'y')
y ~ x2 + x3
is.formula()
Controlla che la tua variabile sia una formula passandola alla funzione is.formula(). Tieni presente che questa funzione fa parte della libreria plyr, quindi dovrai renderla disponibile nel tuo workspace prima di chiamare is.formula()!
# Load `plyr`
library(plyr)
# Check `m`
is.formula(m)
TRUE
Quando usare le formule
Finora hai già letto che le formule in R sono strumenti generici che non sono limitati alla modellazione e hai visto alcuni esempi di dove puoi usarle. In questa sezione, approfondirai quest'ultimo argomento: vedrai alcuni casi in cui puoi usare questi strumenti a tuo vantaggio. Ovviamente, tratterai funzioni di modellazione e grafiche di pacchetti come lattice e stats, ma coprirai anche la valutazione non standard in dplyr.
Funzioni di modellazione
R è ottimo quando devi fare modellazione statistica. Come già sai, la modellazione statistica è un modo semplificato e formalizzato matematicamente per approssimare la realtà e, opzionalmente, fare previsioni a partire da questa approssimazione. Un modello statistico spesso rappresenta il processo di generazione dei dati in una forma idealizzata. Per fare modellazione statistica, ti servono funzioni di modellazione.
Le funzioni di modellazione in R sono un tipico esempio in cui hai bisogno di un oggetto formula come argomento. Altri argomenti che potresti trovare in queste funzioni sono data, che ti permette di specificare un data frame da associare per la durata del modello, subset per selezionare i dati che vuoi usare, ... In generale, se vuoi sapere quali argomenti passare a una funzione specifica, non esitare a usare la funzione help() o ? nella tua console R.
Le funzioni di modellazione restituiscono un oggetto modello che contiene tutte le informazioni sull'adattamento. Funzioni generiche di R come print(), summary(), plot(), anova(), ecc. avranno metodi definiti per classi di oggetti specifiche per restituire informazioni appropriate a quel tipo di oggetto.
Probabilmente una delle funzioni di modellazione più note è lm(), che usa tutti gli argomenti descritti sopra. Usi lm() per adattare modelli lineari. Puoi usarla per eseguire regressione, analisi della varianza a singolo strato e analisi della covarianza. Diamo un'occhiata a un esempio in cui usi lm() e ispezioni il modello con l'aiuto di print():
lm.m <- lm(Sepal.Width ~ Petal.Width + log(Petal.Length) + Species,
data = iris,
subset = Sepal.Length > 4.6)
print(lm.m)
Call:
lm(formula = Sepal.Width ~ Petal.Width + log(Petal.Length) +
Species, data = iris, subset = Sepal.Length > 4.6)
Coefficients:
(Intercept) Petal.Width log(Petal.Length) Speciesversicolor
3.1531 0.6620 0.4612 -1.9265
Speciesvirginica
-2.3088
lm() utilizza inizialmente la formula e l'ambiente appropriato per tradurre le relazioni tra variabili nella creazione di un data frame contenente i dati.
Esistono anche i metodi model.frame(), di cui hai già visto un esempio in questo tutorial, che sono più spesso utilizzati per recuperare o ricreare il model frame dall'oggetto adattato, senza altri argomenti. Questo ti permette di recuperare colonne del data frame che corrispondono ad argomenti della chiamata originale diversi da formula, subset e weights: ad esempio, il metodo glm() gestisce offset, etastart e mustart.
Nel seguente blocco di codice, vedi che usi la funzione model.frame() per ottenere un data frame dall'oggetto adattato; Nota che il codice è leggermente diverso dal blocco che hai visto sopra: l'argomento subset è stato leggermente modificato.
stats::model.frame(formula = Sepal.Width ~ Petal.Width + log(Petal.Length) + Species,
data = iris,
subset = Sepal.Length > 6.9,
drop.unused.levels = TRUE)
| Sepal.Width | Petal.Width | log(Petal.Length) | Species | |
|---|---|---|---|---|
| 51 | 3.2 | 1.4 | 1.547563 | versicolor |
| 103 | 3.0 | 2.1 | 1.774952 | virginica |
| 106 | 3.0 | 2.1 | 1.887070 | virginica |
| 108 | 2.9 | 1.8 | 1.840550 | virginica |
| 110 | 3.6 | 2.5 | 1.808289 | virginica |
| 118 | 3.8 | 2.2 | 1.902108 | virginica |
| 119 | 2.6 | 2.3 | 1.931521 | virginica |
| 123 | 2.8 | 2.0 | 1.902108 | virginica |
| 126 | 3.2 | 1.8 | 1.791759 | virginica |
| 130 | 3.0 | 1.6 | 1.757858 | virginica |
| 131 | 2.8 | 1.9 | 1.808289 | virginica |
| 132 | 3.8 | 2.0 | 1.856298 | virginica |
| 136 | 3.0 | 2.3 | 1.808289 | virginica |
Suggerimento: ci sono ancora più funzioni nel pacchetto stats che ti permettono di usare le formule, come aggregate().
Per i modelli ad effetti misti lineari, che ti permettono di modellare effetti casuali per tenere conto di variazioni dovute a fattori come differenze tra osservatori, puoi usare il pacchetto nlme con la funzione lme(). Anche qui, vedi che formula è il primo argomento che devi fornire alla funzione di modellazione, e c'è anche un argomento data!
# Load packages
library(MASS)
library(nlme)
# Get some data
data(oats)
# Adjust the data names and columns
names(oats) = c('block', 'variety', 'nitrogen', 'yield')
oats$mainplot = oats$variety
oats$subplot = oats$nitrogen
# Fit a non-linear mixed-effects model
nlme.m = lme(yield ~ variety*nitrogen,
random = ~ 1|block/mainplot,
data = oats)
# Retrieve a summary
summary(nlme.m)
Linear mixed-effects model fit by REML
Data: oats
AIC BIC logLik
559.0285 590.4437 -264.5143
Random effects:
Formula: ~1 | block
(Intercept)
StdDev: 14.64496
Formula: ~1 | mainplot %in% block
(Intercept) Residual
StdDev: 10.29863 13.30727
Fixed effects: yield ~ variety * nitrogen
Value Std.Error DF t-value p-value
(Intercept) 80.00000 9.106958 45 8.784492 0.0000
varietyMarvellous 6.66667 9.715028 10 0.686222 0.5082
varietyVictory -8.50000 9.715028 10 -0.874933 0.4021
nitrogen0.2cwt 18.50000 7.682957 45 2.407927 0.0202
nitrogen0.4cwt 34.66667 7.682957 45 4.512152 0.0000
nitrogen0.6cwt 44.83333 7.682957 45 5.835427 0.0000
varietyMarvellous:nitrogen0.2cwt 3.33333 10.865342 45 0.306786 0.7604
varietyVictory:nitrogen0.2cwt -0.33333 10.865342 45 -0.030679 0.9757
varietyMarvellous:nitrogen0.4cwt -4.16667 10.865342 45 -0.383482 0.7032
varietyVictory:nitrogen0.4cwt 4.66667 10.865342 45 0.429500 0.6696
varietyMarvellous:nitrogen0.6cwt -4.66667 10.865342 45 -0.429500 0.6696
varietyVictory:nitrogen0.6cwt 2.16667 10.865342 45 0.199411 0.8428
Correlation:
(Intr) vrtyMr vrtyVc ntr0.2 ntr0.4 ntr0.6
varietyMarvellous -0.533
varietyVictory -0.533 0.500
nitrogen0.2cwt -0.422 0.395 0.395
nitrogen0.4cwt -0.422 0.395 0.395 0.500
nitrogen0.6cwt -0.422 0.395 0.395 0.500 0.500
varietyMarvellous:nitrogen0.2cwt 0.298 -0.559 -0.280 -0.707 -0.354 -0.354
varietyVictory:nitrogen0.2cwt 0.298 -0.280 -0.559 -0.707 -0.354 -0.354
varietyMarvellous:nitrogen0.4cwt 0.298 -0.559 -0.280 -0.354 -0.707 -0.354
varietyVictory:nitrogen0.4cwt 0.298 -0.280 -0.559 -0.354 -0.707 -0.354
varietyMarvellous:nitrogen0.6cwt 0.298 -0.559 -0.280 -0.354 -0.354 -0.707
varietyVictory:nitrogen0.6cwt 0.298 -0.280 -0.559 -0.354 -0.354 -0.707
vM:0.2 vV:0.2 vM:0.4 vV:0.4 vM:0.6
varietyMarvellous
varietyVictory
nitrogen0.2cwt
nitrogen0.4cwt
nitrogen0.6cwt
varietyMarvellous:nitrogen0.2cwt
varietyVictory:nitrogen0.2cwt 0.500
varietyMarvellous:nitrogen0.4cwt 0.500 0.250
varietyVictory:nitrogen0.4cwt 0.250 0.500 0.500
varietyMarvellous:nitrogen0.6cwt 0.500 0.250 0.500 0.250
varietyVictory:nitrogen0.6cwt 0.250 0.500 0.250 0.500 0.500
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-1.81300898 -0.56144838 0.01758044 0.63864476 1.57034166
Number of Observations: 72
Number of Groups:
block mainplot %in% block
6 18
Nota che oltre a nlme ci sono anche altri pacchetti, come lme4, anch'esso dedicato all'adattamento di modelli lineari e generalizzati ad effetti misti.
Un altro esempio di funzioni di modellazione e della presenza di formule è nls(), che useresti per creare modelli non lineari:
# Set seed
set.seed(20160227)
# Data
x <- seq(0,50,1)
y <- ((runif(1,10,20)*x)/(runif(1,0,10)+x))+rnorm(51,0,1)
# Non-linear model
nls.m <- nls(y ~ a*x/(b+x),
start=c(a=4, b=1))
Un ultimo esempio sono le funzioni che puoi usare per costruire modelli lineari generalizzati (GLM). In R, puoi usare la funzione glm() per farlo. Starà diventando ripetitivo, ma anche qui fai uso degli argomenti formula e data:
# Load package
library(MPDiR)
# Get the data
data(Chromatic)
# Model
glm.m <- glm(Thresh ~ Axis:(I(Age^-1) + Age),
family = Gamma(link = "identity"),
data = Chromatic)
# Get back a summary
summary(glm.m)
Call:
glm(formula = Thresh ~ Axis:(I(Age^-1) + Age), family = Gamma(link = "identity"),
data = Chromatic)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.2160 -0.3728 -0.0805 0.2311 1.2932
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.282e-04 9.965e-05 3.294 0.00106 **
AxisDeutan:I(Age^-1) 7.803e-03 3.686e-04 21.172 < 2e-16 ***
AxisProtan:I(Age^-1) 8.271e-03 3.863e-04 21.410 < 2e-16 ***
AxisTritan:I(Age^-1) 1.166e-02 5.284e-04 22.065 < 2e-16 ***
AxisDeutan:Age 1.521e-05 3.418e-06 4.450 1.06e-05 ***
AxisProtan:Age 1.540e-05 3.434e-06 4.484 9.10e-06 ***
AxisTritan:Age 4.812e-05 5.838e-06 8.241 1.48e-15 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for Gamma family taken to be 0.2054848)
Null deviance: 543.35 on 510 degrees of freedom
Residual deviance: 100.40 on 504 degrees of freedom
AIC: -4777.6
Number of Fisher Scoring iterations: 6
Prima di passare alle funzioni grafiche, c'è un'altra cosa utile da sapere: quando usi le formule nelle funzioni di modellazione come lm(), avviene una conversione standard da formula a funzioni. Per spiegare che tipo di conversione avviene esattamente, dovresti tornare all'esempio visto in precedenza in questo tutorial:
lm.m <- lm(Sepal.Width ~ Petal.Width + log(Petal.Length) + Species,
data = iris,
subset = Sepal.Length > 4.6)
Mentre lo scopo di questo blocco è adattare un modello di regressione lineare, la formula è usata per specificare il modello simbolico e anche per generare la matrice del disegno prevista. Una matrice del disegno è la rappresentazione bidimensionale dell'insieme dei predittori o delle variabili indipendenti, in cui le istanze dei dati sono in righe e gli attributi delle variabili sono in colonne. La matrice del disegno è anche nota come matrice X.
Detto ciò, il metodo formula definisce anche le colonne che dovrebbero essere incluse nella matrice del disegno.
Ma cosa significa esattamente tutto questo? Guardiamo le seguenti righe di codice:
# A formula
y ~ x
# A converted formula
y = a_1 + a_2 * x
Questo è un esempio di una conversione semplice: y ~ x viene tradotto in y = a_1 + a_2 * x.
Per vedere e capire cosa fa effettivamente R, puoi usare la funzione model_matrix(). Questa funzione crea una matrice del disegno o del modello, ad esempio espandendo i fattori in un insieme di variabili dummy, a seconda dei contrasti, ed espandendo le interazioni in modo simile.
Non dimenticare di passare il data frame df e la formula per ottenere una tibble che definisce l'equazione del modello:
# Load packages
library(tidyverse)
library(modelr)
# A data frame
df <- tribble(
~y, ~x1, ~x2,
4, 2, 5,
5, 1, 6
)
# Model matrix
model_matrix(df, y ~ x1)
| (Intercept) | x1 |
|---|---|
| 1 | 2 |
| 1 | 1 |
Compare una colonna extra (Intercept). Questo è il comportamento predefinito da parte di R. Tuttavia, il modo in cui R aggiunge l'intercetta al modello è semplicemente avere una colonna piena di uni. Se non lo vuoi, devi rimuoverla esplicitamente aggiungendo -1 alla formula, proprio così:
model_matrix(df, y ~ x1-1)
| x1 |
|---|
| 2 |
| 1 |
Nota anche che la matrice del modello cresce in modo prevedibile quando aggiungi più variabili al modello.
model_matrix(df, y ~ x1 + x2)
| (Intercept) | x1 | x2 |
|---|---|---|
| 1 | 2 | 5 |
| 1 | 1 | 6 |
Funzioni grafiche in R
Un altro luogo importante in cui troverai le formule in R sono le funzioni grafiche. Ci sono moltissimi pacchetti, quindi questo tutorial si concentrerà solo su graphics, lattice, ggplot2 e ggformula.
graphics
Il pacchetto base di R graphics ti permette di specificare uno scatterplot o aggiungere punti, linee o testo usando una formula. Guarda il seguente esempio:
# Get data
data(airquality)
# Plot
plot(Ozone ~ Wind, data = airquality, pch = as.character(Month))

Se vuoi saperne di più, non esitare a consultare questa pagina.
lattice
lattice è un pacchetto costruito sulla grafica grid. È un pacchetto grafico generico che fornisce implementazioni alternative di molte funzioni di plotting disponibili nella grafica base. Esempi specifici includono scatterplot con la funzione xyplot(), grafici a barre con la funzione barchart() e boxplot con la funzione bwplot().
Suggerimento: vuoi saperne di più su lattice? Valuta il corso di DataCamp Data Visualization in R con lattice.
Ciò che rende speciale questo pacchetto è che utilizza la notazione a formule dei modelli statistici per descrivere il grafico desiderato e, più specificamente, le variabili da rappresentare. Aggiunge anche il carattere pipe o barra verticale | per specificare la variabile di condizionamento:
# Load package
library(lattice)
# Plot histogram
histogram(~ Ozone | factor(Month),
data = airquality,
layout = c(2, 3),
xlab = "Ozone (ppb)")

Nota che, proprio come le funzioni di modellazione, le funzioni della libreria lattice hanno, oltre a un argomento formula, anche un argomento data, come ci si aspetterebbe!
Ricorda che potresti anche omettere l'argomento data nel blocco di codice sopra, ma in quel caso dovresti attaccare i dati:
# Load package
library(lattice)
# Attach data
attach(airquality)
# Plot
histogram(~ Ozone | factor(Month),
layout = c(2, 3),
xlab = "Ozone (ppb)")
ggplot2
Puoi usare le formule in varie funzioni di ggplot2:
geom_smooth()ostats_smooth(), per specificare la formula da usare nella funzione di smoothing; questo influenzerà la forma dell'adattamento.facet_wrap(), per specificare i pannelli per il plotting.facet_grid(), per specificare le righe e le colonne da plottare, con o senza faceting.
# Load package
library(ggplot2)
# Plot
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth(method = "lm",
formula = y ~ splines::bs(x, 3),
se = FALSE)

Nota che, per creare questo grafico, la formula usa le lettere x e y, non i nomi delle variabili. Non è così quando usi facet_wrap():
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth(span = 0.8) +
facet_wrap(~drv)

Vuoi saperne di più su ggplot2? Dai un'occhiata a questo corso o consulta la documentazione.
ggformula
Il pacchetto ggformula attualmente si basa su ggplot2, ma fornisce un'interfaccia basata sulle formule. Questo è simile a ciò che trovi nell'interfaccia di lattice. Troverai anche che l'operatore pipe è usato in questo pacchetto per costruire grafici più complessi a partire da componenti più semplici.
Suggerimento: se vuoi sapere di più sull'operatore pipe in R, guarda questo tutorial.
Il modo di base per creare un grafico con ggformula è
gf_plottype(formula, data = mydata)
Nota che la funzione gf_plottype() inizia in realtà con gf, per ricordarti che stai lavorando con funzioni che sono interfacce basate su formule a ggplot2; La g sta per ggplot2 e la f per “formula”.
Guarda il seguente esempio per vedere come appare questo in codice R:
# Load package
library(ggformula)
# Plot
gf_point(mpg ~ hp, data = mtcars)

Ovviamente, questo è solo un grafico di base; Con questo pacchetto di visualizzazione puoi fare molto di più! Puoi selezionare un tipo diverso di glifo e attributi specifici per quei glifi, puoi creare grafici a 1 o 2 variabili, regolare le posizioni dei glifi e così via. Questo tutorial non entrerà più nel dettaglio su questo pacchetto, ma il punto principale è che questo pacchetto ha reso le formule l'ingrediente principale per creare grafici!
Se vuoi saperne di più di quanto trattato in questo tutorial, leggi del pacchetto ggformula qui o consulta la pagina RDocumentation del pacchetto.
dplyr
dplyr è un esempio di pacchetto che lavora con la valutazione non standard (Non-Standard Evaluation). Altri esempi sono library(magrittr) rispetto a library("magrittr"), che funzionano entrambi perfettamente, anche se in una riga ci sono le virgolette e nell'altra no! Confronta questi esempi di library() con install.packages(), per esempio:
# This will work
install.packages("magrittr")
# This won't work
install.packages(magrittr)
In R, di solito devi usare le virgolette ogni volta che stai nominando una parte di un oggetto, ma in alcune funzioni - come library() - non è necessario. Queste funzioni sono progettate per funzionare in modo non standard. Inoltre, alcune di queste funzioni non hanno nemmeno un modo standard!
Per quanto riguarda dplyr, scoprirai che la maggior parte delle funzioni di questo pacchetto funzionano proprio come le altre: tutte le funzioni usano la Valutazione Standard (SE). Tuttavia, per l'uso interattivo, le funzioni hanno anche una Valutazione Non Standard (NSE), che ti fa risparmiare un po' di digitazione.
(Diciamocelo, digitare tutte quelle virgolette può essere snervante!)
Ecco perché la maggior parte delle funzioni di dplyr usa la valutazione non standard. Non seguono le solite regole di valutazione di R. Invece, catturano l'espressione che hai digitato e la valutano in un modo personalizzato.
Questo, però, non significa che queste funzioni non abbiano una variante a valutazione standard. Ogni funzione che usa la valutazione non standard ha (e dovrebbe avere) una via di fuga a valutazione standard che fa il calcolo effettivo. La funzione a valutazione standard dovrebbe terminare con _. Questo significa che ci sono più verbi nel pacchetto dplyr: select(), select_(), mutate(), mutate_(), ecc.
Quando usate in modo interattivo, queste funzioni saranno prima valutate con il pacchetto lazyeval prima di essere inviate alla versione a valutazione standard della funzione. Ciò significa che, sotto il cofano, select() è valutata con il pacchetto lazyeval e inviata a select_().
Detto questo, ci sono 3 modi per quotare le variabili nelle funzioni a valutazione standard che dplyr e lazyeval comprendono:
- Formule,
quote(), e- Stringhe
# Load `dplyr`
library(dplyr)
# NSE evaluation
select(iris, Sepal.Length, Petal.Length)
# standard evaluation
select_(iris, ~Sepal.Length)
select_(iris, ~Sepal.Length, ~Petal.Length) #works
select_(iris, quote(Sepal.Length), quote(Petal.Length)) # yes!
select_(iris, "Sepal.Length", "Petal.Length", "Species")
Suggerimento: se vuoi leggere di più sulla valutazione non standard, considera la lettura del capitolo su questo argomento nel libro Advanced R di Hadley Wickham.
Pacchetti sulle formule in R
In precedenza, hai visto che puoi creare e ispezionare le tue formule usando funzioni come as.formula, update(), all.vars, ... Queste erano tutte operazioni e manipolazioni semplici, ma che ne dici di manipolazioni avanzate con le formule? Forse i seguenti pacchetti saranno di tuo interesse!
Formula Package
Recentemente, questo pacchetto è stato pubblicato su CRAN. Questo pacchetto è ideale per chi vuole portare le formule al livello successivo. Estende la classe base formula.
Più specificamente, gli oggetti Formula estendono gli oggetti formula di base: con questo pacchetto, puoi effettivamente definire formule che accettano un operatore di formula aggiuntivo | che separa più parti o che possono potenzialmente contenere tutti gli operatori di formula (incluso il carattere pipe) sul lato sinistro per supportare risposte multiple.
Esempi di formule che sarai in grado di creare sono:
- Formule multi-parte, come
y ~ x1 + x2 | u1 + u2 + u3 | v1 + v2 - Formule a risposte multiple, come
y1 + y2 ~ x1 + x2 + x3 - Risposte multi-parte, come
y1 | y2 + y3 ~ x, e - Combinazioni dei tre casi sopra.
# Load package
library(Formula)
# Create formulas
f1 <- y ~ x1 + x2 | z1 + z2 + z3
F1 <- Formula(f1)
# Retrieve the class of `F1`
class(F1)
- 'Formula'
- 'formula'
Nota che le funzioni as.formula() e is.formula() sono state aggiornate anche in questo pacchetto: userai is.Formula() e as.Formula() al loro posto!
Leggi di più qui.
formula.tools
Questo pacchetto è stato rilasciato di recente e ti fornisce "utility programmatiche per manipolare formule, espressioni, chiamate, assegnazioni e altri oggetti R". È un bel po', ma in sostanza, tutto si riduce a questo: puoi usare questo pacchetto per accedere e modificare le strutture delle formule così come per estrarre e sostituire nomi e simboli di quegli oggetti R. Questo pacchetto è stato scritto da Christopher Brown.
Alcune cose che potresti trovare utili lavorando con questo pacchetto sono le seguenti:
get.vars(): invece diall.vars(), questa funzione estrarrà i nomi delle variabili da vari oggetti R, ma tutti i simboli, ecc. saranno interpolati in nomi di variabili.invert(): puoi usare questa funzione per invertire gli operatori in un oggetto, come una formula.is.one.sided(): questa funzione è molto utile per determinare se una formula è a uno o due lati.
Ricorda che una formula è a un lato se appare così: ~x; sarà a due lati quando formulata come x~y.
- ...
C'è altro da scoprire!
Evviva! Sei arrivato alla fine di questo tutorial sulle formule in R. Se vuoi leggere di più, dai sicuramente un'occhiata al libro di Hadley Wickham R for Data Science, che ha un capitolo interamente dedicato alle formule e alle famiglie di modelli in R.
Dai un'occhiata al tutorial introduttivo al Tidyverse di DataCamp.
Ti vengono in mente altri casi in cui puoi trovare formule o altri pacchetti che puoi usare per manipolarle? Fammi sapere su Twitter: @willems_karlijn.


