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.


Thomas Neitmann

716 Parole

2021-01-27 00:00 +0700

860d4e8 @ 2021-10-03