R è un linguaggio interpretato orientato agli oggetti, che semplicemente organizza i dati in liste con vari attributi sui tipi. Il sistema è particolarmente semplice, perché di norma il risultato di un comando è un oggetto che può essere trasferito al comando successivo, senza dover modificarne la struttura, o manipolarlo con puntatori, riferimenti o altri meccanismi complicati dei linguaggi di programmazione; R è pertanto particolarmente indicato all’uso interattivo. IMPORTANTE: R è case-sensitive!
Vediamo un brevissimo esempio: dopo aver avviato l’interprete (detto R Console), compare un breve messaggio ed il prompt “>”, che accetta input dall’utente. Io digito l’espressione cat("Hello, world!\n"), che produce l’ormai famoso messaggio ed è disponibile anche un esempio scritto (da me) in Wikipedia (poi spostato in Wikibooks).
![Immagine](https://i.postimg.cc/vTfWWjPS/01.jpg)
Il comando save.image salva l’attuale ambiente di lavoro in un file dall’estensione RData: questo contiene variabili, funzioni, dati ecc. Il comando load carica un file precedentemente salvato con save.image. Il comando q() comporta la chiusura dell’interprete, e se tu provi a chiudere la finestra, allora viene automaticamente invocato tale comando; se eseguito senza argomenti allora mostra una finestra di dialogo in cui chiede se salvare i propri dati con save.image; tu puoi anche chiudere senza tale messaggio con questo comando: q("no"). Questi comandi sono funzioni.
Altri comandi particolarmente interessanti sono ls(), che mostra tutti gli oggetti definiti (variabili e funzioni), e la variabile .Last.value, che memorizza il risultato dell’ultimo comando. Le variabili che iniziano con un punto, come quest’ultima, di norma non sono mostrate da ls(), e per visualizzarle occorre invocare ls(all.names = TRUE). Quest’ultimo esempio serve per introdurre un concetto importante: le funzioni disponibili in R sono ormai numerose e davvero ricche di argomenti; molti di questi argomenti non sono obbligatori, e possono essere specificati usando la formula NOME = VALORE, senza necessità di ricorrere all’elenco di tutti gli argomenti possibili; questo modo di utilizzarli è davvero comodo, perché spesso si vuole specificare solo il decimo argomento, per esempio, senza specificare i nove che lo precedono. Per finire, bisogna sapere che esistono due operatori di assegnazione: tu puoi utilizzarli entrambi indifferentemente.
… comandi …
NewName = .Last.vaue
NewName <- .Last.value
Elementi del linguaggio
Abbiamo detto che R è un linguaggio orientato agli oggetti; questi oggetti possono avere la struttura di funzione oppure di vettore, con degli attributi collegati. I vettori sono composti da uno o più elementi di tipo numerico, intero, binario (detto “Boolean”) e testo (detto “Character”).
> x = 1 # definisce un vettore di un vettore di un solo elemento di classe “numeric”
> x = 1:9 # definisce un vettore di nove elementi di classe “numeric”, contenente i numeri da 1 a 9
> x = (1:9) - c(3,1,7)
> x
[1] -2 1 -4 1 4 -1 4 7 2
> c("ABC", "g", "Maryland") [1] "ABC" "g" "Maryland"
> y = ( (1:9) - c(3,1,7) > 0 )
> y
[1] F T F T T F T T T
> as.numeric(y)
[1] 0 1 0 1 1 0 1 1 1
> as.character(x)
[1] "-2" "1" "-4" "1" "4" "-1" "4" "7" "2"
> as.numeric(.Last.value)
[1] -2 1 -4 1 4 -1 4 7 2
Procediamo con ordine: la funzione c() produce un vettore con i suoi argomenti, ed è comodissima come si può notare. Notare che l’espressione (1:9) - c(3,1,7) prevede di sottrarre un vettore di tre elementi da uno di nove: questo va bene, perché il vettore più piccolo viene ripetuto fino a raggiungere la lunghezza di quello più lungo. L’espressione (1:9) - c(3,1,7) > 0 fa un confronto con “>” per verificare se gli elementi del vettore sono maggiori di zero: il risultato è un vettore, in cui ciascun elemento assume il valore TRUE (abbreviato con T) o FALSE (abbreviato con F), e di nuovo lo zero (ricorda: è un vettore di un solo elemento!) viene “prolungato” fino ad assumere la dimensione necessaria per l’altro vettore. Le funzioni as.___ convertono gli elementi di un vettore da un tipo ad un altro. Se il risultato non viene memorizzato in una variabile, allora è mostrato come riga di output, ed è memorizzato in .Last.value. Questo tipo di output è una forma contratta dell’uso della funzione print: se tu digiti “x” al prompt e premi INVIO, con lo scopo di visualizzare il contenuto del vettore, questa forma è del tutto equivalente ad eseguire print(x). La funzione scan() è il suo speculare, ossia accetta input dall’utente:
> z = scan()
1: 1 2 3 28
5:
Read 4 items
> print(z)
[1] 1 2 3 28
scan è la classica funzione che prevede numerosissimi argomenti, ed è possibile usarla per leggere file, interpretare file .CSV, con tutte le opzioni del caso. Una delle funzionalità belle di R è che memorizza le funzioni come testi, ed è possibile digitare il nome della funzione scan senza le parentesi rotonde (che indicano l’invocazione della funzione), per mostrare il testo della funzione; è anche possibile usare print(scan) per questo scopo, ossia passare il testo di una funzione ad un’altra: è più o meno simile al passaggio di puntatori, e rende le cose davvero interessanti quando si combinano gli oggetti fra loro! Perché mai stampare il testo di una funzione? Semplice: perché si può vedere l’elenco degli argomenti!
![Immagine](https://i.postimg.cc/T3YDwpXs/02.png)
Come tu puoi notare, gli argomenti sono davvero tanti. Esiste anche un’altra strada: digitare il punto interrogativo seguito dal nome della funzione.
![Immagine](https://i.postimg.cc/1XQgZzHJ/03.png)
Questa funzionalità attiva un server http sul computer locale, che mostra una pagina di aiuto sulla funzione indicata.
Attenzione: l’avvio di un server http sul proprio computer locale non è uno scherzo per le problematiche della sicurezza! L’esecuzione del comando netstat.exe in questo caso è utile per vedere quali programmi sono in ascolto ed eventuali connessioni esterne.
Altri tipi di dati interessanti
Ci sono alcuni tipi di dato fondamentali che sono utilissimi. Il più semplice è matrix: questo rappresenta un array con più dimensioni, come nei linguaggi di programmazione. R fornisce una matrix d’esempio di nome volcano. L’accesso agli elementi si fa in modo simile ai vettori:
volcano[1,4] # elemento della prima riga, quarta colonna
Volcano[100] # centesimo elemento della matrice, considerandola come un vettore
Per creare una matrice, è sufficiente richiamare la funzione matrix().
Il tipo data.frame è la classica tabella: gli elementi sono divisi in righe e colonne, con ciascuna colonna che può essere di tipo diverso ed identificata da un nome. R fornisce un data.frame d’esempio per fare esperimenti di nome warpbreaks. Tu puoi creare un data.frame invocando al funzione omonima data.frame().
Infine, il tipo table fornisce una tabella di contingenza, ossia di conteggi di altri valori: per esempio table(volcano) crea una tabella che conta le occorrenze di ciascun valore, come mostra la seguente immagine.
![Immagine](https://i.postimg.cc/t4MY4kk1/04.png)
Questa tabella dice che il valore 94 compare 51 volte nella matrice, il valore 95 compare 49 volte, e così via, fino al valore 194 che compare 2 volte e al valore 195 che compare una sola volta.
Grafici
R può produrre bellissimi grafici, che possono essere salvati come immagini o come documenti PDF. Tu puoi provare a digitare il comando plot(volcano): la funzione plot è la base per la produzione dei grafici; invocandola, R apre una finestra contenente il grafico. Esistono vari tipi di grafico: istogramma, box & whisker plot, visualizzatori di densità, ecc. ma esistono anche grafici molto avanzati.
Ecco un esempio che spiega il nome “volcano”: la funzione filled.contour richiama plot dopo aver opportunamente elaborato i dati (che rappresentano una griglia di circa 10 x 10 metri del vulcano Maunga Whau o Monte Eden, nelle Auckland, Nuova Zelanda).
require(grDevices); require(graphics)
filled.contour(volcano, color.palette = terrain.colors, asp = 1)
title(main = "volcano data: filled contour map")
![Immagine](https://i.postimg.cc/rFhpfjvh/05.png)
Infine, questo piccolo esempio ma che dimostra le potenzialità del sistema:
d <- outer(0:9, 0:9)
fr <- table(outer(d, d, "-"))
plot(names(fr), fr, type="h", xlab="Determinant", ylab="Frequency")
La funzione outer esegue il prodotto vettoriale esterno di due vettori, in questo caso i vettori dei numeri da zero a dieci; d è quindi una matrice di dimensione 2 con questi valori.
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 0 0 0
[2,] 0 1 2 3 4 5 6 7 8 9
[3,] 0 2 4 6 8 10 12 14 16 18
[4,] 0 3 6 9 12 15 18 21 24 27
[5,] 0 4 8 12 16 20 24 28 32 36
[6,] 0 5 10 15 20 25 30 35 40 45
[7,] 0 6 12 18 24 30 36 42 48 54
[8,] 0 7 14 21 28 35 42 49 56 63
[9,] 0 8 16 24 32 40 48 56 64 72
[10,] 0 9 18 27 36 45 54 63 72 81
Successivamente si usa nuovamente outer, ma questa volta si specifica il terzo parametro “-“: questo rappresenta l’operazione da eseguire, in questo caso la sottrazione (in assenza di questo parametro la funzione esegue la moltiplicazione), e si crea una tabella di contingenza dei risultati. Che cosa rappresenta quindi fr? Analizziamo brevemente questa matrice: il suo determinante è -22 (ricorda: il determinante di una matrice quadrata di ordine 2 è dato dalla differenza del prodotto dei due elementi nella diagonale principale 7 x 2 con il prodotto dei due elementi nella diagonale secondaria 9 x 4: 7 x 2 – 9 x 4 = -22).
7 4
9 2
Ora, d[8,3] è esattamente il prodotto 7 x 2 = 14, come puoi notare nella tabellina sopra; la prima riga e la prima colonna di d sono zero, perché i vettori 0:9 iniziano con il numero zero. Allo stesso modo, d[10,5] è il prodotto 9 x 4 = 36. Di conseguenza, la funzione outer(d, d, "-") produce una matrix di dimensione 4 (dimensione nel senso di indici) di ordine 4 (ossia 4 elementi per ciascuna dimensione): il suo elemento con indice [8, 3, 10, 5] è proprio la differenza fra d[8, 3] e d[10, 5], ossia il determinante della matrice vista sopra. In sostanza, ciascun elemento di outer(d, d, "-") è il determinante di una possibile matrice con gli elementi di una sola cifra!
La funzione table produce una tabella di contingenza di questi determinanti, e la funzione plot ne mostra il grafico: il grafico predefinito di una tabella di contingenza è un istogramma. Come puoi notare, la maggior parte delle matrici quadrate di ordine 2 con elementi di una sola cifra ha valore zero, e gli altri sono sparsi fra -81 e +81 secondo queste frequenze mostrate sotto.
![Immagine](https://i.postimg.cc/vmnHHsDj/06.png)
Conclusione
Per concludere, allego il file Statistical Computing with R.pdf che rappresenta l’inizio del corso di programmazione in R: contiene moltissime nozioni di base, perciò suggerisco di provarle prima di passare all’azione vera e propria con R.