Capire la Non-Standard Evaluation. Parte Prima: Le Basi
La non-standard evaluation (letteralmente valutazione non-standard)—abbreviata in NSE—è uno di quei termini tecnici che i maghi di R adorano sventolare nelle discussioni sul linguaggio di programmazione. Ma cosa significa esattamente NSE? Per rispondere a questa domanda e demistificare il concetto, bisogna cominciare a parlare del suo opposto, ovvero la standard evaluation (no, non si chiama non-NSE 😉).
Prendiamo per esempio il selezionare una singola colonna da un data frame. In R base lo si può fare sia usando [[
sia con $
. Il primo usa la semantica della standard evaluation, il secondo usa la NSE.
Quando si usa [[
, bisogna passare una stringa tra le parentesi. Nel caso più semplice si usa una stringa letterale.
data(iris)
head(iris[["Species"]])
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
Ma è anche possibile passare un simbolo all’interno di [[
. Questo simbolo verrà valutato ad un valore (che farà meglio ad essere una stringa o un numero, altrimenti darà un errore).
var <- "Species"
head(iris[[var]])
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
Nessuna sorpresa fino a qui. Ora vediamo come si comporta $
. Proprio come con [[
, puoi passare una stringa letterale a $
.
head(iris$"Species")
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
Tuttavia, difficilmente lo si fa nella pratica perchè con $
non è necessario. Piuttosto puoi mettere un simbolo nel lato destro di $
.
head(iris$Species)
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
Tutto ciò è molto comodo quando scrivi il codice direttamente nella console perchè richiede meno battute sulla tastiera. Ora, cosa succede se passiamo var
, che abbiamo definito poco fa, nel lato destro di $
?
head(iris$var)
## NULL
Non è quello che ti aspettavi? Non sei il solo! Questo è il momento in cui noto molti programmatori di R alle prime armi avere delle difficoltà. Ricorda, il simbolo var
contiene il valore "Species"
. Usando la valutazione standard, R valuterebbe var
al suo valore. Tuttavia quando si usa $
questo non succede, poichè $
usa la NSE. Invece $
cerca una colonna con il nome var
all’interno del data frame iris
. Dal momento che questa colonna non esiste, si ottiene NULL
come risultato (personalmente preferirei un errore, ma le cose stanno così).
Oltre a Species
, il data frame iris
contiene anche una colonna chiamata Sepal.Length
. In base a quanto abbiamo appena detto, si può selezionare quella colonna sia usando iris[["Sepal.Length"]]
sia usando iris$Sepal.Length
. Ma cosa succede quando abbiamo una variabile chiamata Sepal.Length
nel global environment?
Sepal.Length <- "Species"
Che cosa restituiranno rispettivamente iris[[Sepal.Length]]
e iris$Sepal.Length
? Prima di proseguire con la lettura, fermati e pensaci. In base a quanto abbiamo detto finora, dovresti essere in grado di rispondere correttamente a quella domanda. Se sei nel dubbio, torna indietro e leggi un’altra volta i paragrafi precedenti.
Cominciamo da iris[[Sepal.Length]]
. Quando usiamo [[
il simbolo Sepal.Length
viene valutato al suo valore "Species"
. Quindi in questo caso iris[[Sepal.Length]]
equivale a iris[["Species"]]
.
head(iris[[Sepal.Length]])
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
Al contrario quando si usa iris$Sepal.Length
, a R non interessa dell’esistenza di una variabile chiamata Sepal.Length
nel global environment. Invece, la prima cosa che fa è cercare una variabile chiamata Sepal.Length
all’interno del data frame iris
, e infatti ne trova una.
head(iris$Sepal.Length)
## [1] 5.1 4.9 4.7 4.6 5.0 5.4
Quindi, anche se esegui il comando iris$Sepal.Length
nel global environment, e in quello stesso ambiente esiste un simbolo con il nome Sepal.Length
associato ad un valore, R lo ignora. Tratta invece il data frame stesso come se fosse un ambiente, e se valuti lì Sepal.Length
, ottieni il contenuto di quella colonna. Questo non segue per nulla le regole di R sulla valutazione standard, dunque questo processo prende il nome di valutazione non-standard.
Se hai capito quello che abbiamo visto finora, hai appena fatto un grande passo in avanti nel tuo viaggio per padroneggiare R. Ma aspetta, non è finita! Nella seconda parte di questo post ti mostrerò come implementare a tua volta una funzione che usa la NSE. Così facendo approfondirai ancora di più la tua comprensione e imparerai alcuni meccanismi interni di R che ti daranno il super potere di scrivere pacchetti come {dplyr}. Rimani sintonizzato!
Questo post è stato tradotto dall’inglese da Stefano Anzani.