R è un linguaggio adatto all'analisi statistica. In statistica si trattano continuamente vettori di elementi omogenei (tutti dello stesso tipo), e infatti R gestisce i vettori come un tipo primitivo, al pari dei numeri, dei booleani e delle stringhe. Anzi, in realtà il vettore è il tipo di R più semplice possibile: un numero per R non è altro che un vettore di numeri di lunghezza 1.
Ecco alcune funzioni (e operatori) per la costruzione di vettori:
c(v1, ...., vn) restituisce un vettore i cui elementi, nell'ordine, sono v1, ... vn.
> c(1,10,4,2)
[1] 1 10 4 2
from:to restituisce il vettore di tutti i numeri compresi tra from e to.
> 4:10
[1] 4 5 6 7 8 9 10
> 4.2:10
[1] 4.2 5.2 6.2 7.2 8.2 9.2
seq(from=1,to=1,by=1) restituisce il vettore dei numeri compresi tra from e to, a passi di lunghezza by. Notare che seq(from,to), senza il parametro by, è esattamente uguale all'operatore : (due punti).
> seq(4,10,3)
[1] 4 7 10
rep(x,times) genera un vettore ottenuto replicando il vettore x per il numero di volte times.
> x=1:5
> rep(x,2)
[1] 1 2 3 4 5 1 2 3 4 5
Se il vettore generato è troppo lungo per essere visualizzato su una sola riga, sarà spezzato in righe consecutive. All'inizio di ogni riga, il simbolo [i] (che per ora abbiamo sempre visto come [1]) ci dice a che posizione del vettore corrisponde il primo elemento visualizzato in quella riga.
> 1:70 [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [26] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 [51] 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
Le espressioni sui vettori posso anche essere composte da più operazioni annidate, e vengono eseguite proprio come le espressioni sui numeri: prima si eseguono le operazioni all'interno delle parentesi più interne, poi si prosegue ad eseguire le operazioni più esterne. Ad esempio:
> rep(1:5,2) [1] 1 2 3 4 5 1 2 3 4 5
In seguito, per far vedere come viene eseguita una espressione complessa, si userà la seguente notazione (chiamata derivazione):
rep(1:5,2) => rep({1 2 3 4 5},2) => {1 2 3 4 5 1 2 3 4 5}
Questo vuol dire che prima 1:5 viene eseguita e genera il vettore {1 2 3 4 5}, poi la rep esterna viene eseguita e genera il vettore {1 2 3 4 5 1 2 4 5 5}. Attenzione che rep({1 2 3 4 5},2) non è un codice R valido. Le parentesi graffe sono eclusivamente una notazione che useremo all'interno delle derivazioni.
Tutti gli operatori e le funzioni aritmetiche si estendono direttamente ai vettori, sui quali vengono applicati elemento per elemento. Ad esempio, se x e y sono vettori numerici della stessa lunghezza, x+y è il vettore in cui l'elemento i-esimo è la somma dell'elemento i-esimo di x e dell'elemento i-esimo di y. Analogamente sqrt(x) è il vettore il cui elemento i-esimo è la radice quadrata dell'elemento i-esimo di x.
> x=c(2,4,6) > x>5 [1] FALSE FALSE TRUE > x+1:3 [1] 3 6 9 > sqrt(x+1:3) [1] 1.732051 2.449490 3.000000
Notare che se una operazione coinvolge un vettore x e un numero, allora il numero viene trasformato automaticamente in un vettore della stessa lunghezza di x, e poi l'operazione ha luogo.
> x=c(2,4,6) > x+1 [1] 3 5 7
In realtà, questa procedura è più generale, e si applica ogni volta che si vuole operare con due vettori di lunghezza diversa. Quello più piccolo viene duplicato tante volte, fino ad avere la stessa lunghezza di quello più grande.
> c(1,2,3,4,5,6)+c(1,2) [1] 2 4 4 6 6 8
Se la lunghezza del vettore grande non è un multiplo esatto della lunghezza del vettore piccolo, l'operazione prosegue comunque come detto sopra, ma si genera un messaggio di avvertimento.
> c(1,2,3,4,5,6,7)+c(1,2) [1] 2 4 4 6 6 8 8 Warning message: In c(1, 2, 3, 4, 5, 6, 7) + c(1, 2) : longer object length is not a multiple of shorter object length
A parte quanto visto sopra, ovvero operazioni che agiscono elemento per elemento, esisono altre funzioni utili per la manipolazione di vettori che operano globalmente.
max(x) restituisce il valore massimo del vettore x;min(x) restituisce il valore minimo del vettore x;sum(x) restituisce la somma di tutti gli elementi del vettore x;prod(x) restituisce il prodotto di tutti gli elementi del vettore x;length(x) restituisce la lunghezza del vettore x;c(v1, ..., vn) concatena i vettori v1.... vn.Ad esempio, la seguente espressione calcola la somma di tutti i numeri da 1 a 100.
> sum(1:100) [1] 5050
Notare che la funzione c serve a due scopi: costruire un vettore elencandone gli elementi e concatenare i vettori. In realtà si tratta sempre della stessa cosa. Come abbiamo detto, in realtà R gestisce i numeri come vettori di lunghezza uno. Scrivere c(2,4,-1) vuol dire quindi concatenare i vettore {2}, {4}, {-1}.
Dato il vettore p contenente una distribuzione di probabilità, che espressione R si può usare per calcolare il valore dell'entropia relativa a p (ovvero la somma, per i che va da 1 alla lunghezza di p, di p[i] * log p[i])? Se p=c(0.5,0.3,0.2), il risultato di questa espressione dovrebbe essere 1.485475. Si assuma che tutti gli elementi di p siano diversi da zero.
Altri operatori si occupano di estrarre da un vettore un qualunque elemento o un suo sottovettore.
x[i] estrae dal vettore x l'elemento in posizione i. A differenza che in Java (o in PHP), la prima posizione è la numero uno.
> x=c(1,10,4,5,20,2)
> x[2]
[1] 10
> c(9,8,7,6)[3]
[1] 7
> rep(1:5,2)[7]
[1] 2
Notare che è possibile applicare l'operatore di estrazione, così come qualunque operatore sui vettori, sia ad una variabile di tipo vettore, che direttamente ad una qualunque espressione. Questa, ad esempio, è la derivazione per l'ultima riga di cui sopra:
rep(1:5,2)[7] => rep({1 2 3 4 5},2)[7] => {1 2 3 4 5 1 2 3 4 5}[7] => 2
x[v] dove v è un vettore numerico, estrae da x il sottovettore i cui indici sono presenti in v.
> x=seq(1,20,by=2)
> x
[1] 1 3 5 7 9 11 13 15 17 19
> x[2:4]
[1] 3 5 7
> seq(1,20,by=2)[2:4]
[1] 3 5 7
È possibile usare valori negativi per v e in questo caso si estraggono da x tutti gli elementi tranne quelli di indice contenuti in v (cambiato di segno). Ad esempio, x[-1] estrare da x tutti gli elementi tranne il primo. Non si possono mischiare in v numeri positivi e negativi (il tutto sarebbe molto ambiguo).
> x[-2]
[1] 1 5 7 9 11 13 15 17 19
x[b] dove b è un vettore di booleani della stessa lunghezza di x, estra da x gli elementi posizione i per cui b[i] è TRUE. Se b non ha la stessa lunghezza di x allora b viene duplicato più volte, come visto per le operazioni aritmetiche.
> c(10,4,5,1)[c(TRUE,FALSE,FALSE,TRUE)]
[1] 10 1
> seq(1,20,by=2)[c(TRUE,FALSE)]
[1] 1 5 9 13 17
La prima espressione estrae dal vettore c(10,4,5,1) gli elementi in prima e quarta posizione, mentre la seconda espressione estrare da seq(1,20,by=2) che è {1 3 5 7 9 11 13 15 17 19}, gli elementi in posizione dispari.
Utilizzando queste operazioni di estrazione si possono realizzare facilmente operazioni di selezione molto complesse. Ad esempio, l'espressione x[x>0] estrae da x il sottovettore di tutti i suoi elementi positivi.
> x=c(-1,2,4,-3,0) > x[x>0] [1] 2 4
Questa è la derivazione corrispondente:
x[x>0] => {-1 2 4 -3, 0}[{-1 2 4 -3 0}>0] => {-1 2 4 -3 0}[{FALSE TRUE TRUE FALSE FALSE}] => {2 4}
ifelseUna operazione simile alla selezione è quella fornita dalla funzione ifelse(test,yes,no). test è un vettore di booleani, mentre yes e no devono essere vettori dello stesso tipo. Quello che fa questa funzione è creare un vettore delle stessa lunghezza di test: per ogni i, da 1 alla lunghezza di test, l'elemento i-esimo del vettore risultato sarà preso da yes[i] se test[i] è TRUE, da no[i] altrimenti.
Normalmente i tre vettori devono essere tutti della stessa lunghezza, ma al solito R estende automaticamente i vettori più corti quando è necessario
> x=c(1,4,0,3,2,0) > ifelse(x==0,NaN,x+1) [1] 2 5 NaN 4 3 NaN
Questa è la derivazione corrispondente:
ifelse(x==0,NaN,x+1) => ifelse({1 4 0 3 2 0}==0,{NaN},{2 5 1 4 3 1}) =>
=> ifelse({FALSE FALSE TRUE FALSE FALSE TRUE},{NaN NaN NaN NaN NaN NaN}{2 5 1 4 3 1})} => {2 5 NaN 4 3 NaN}
Migliorare l'esercizio precedente in modo che l'espressione funzioni anche quando p contiene valori nulli. Ad esempio, se p=c(0,0.5,0.5), l'espressione deve restituire 1 (non altri valori e, in particolare, non deve restituire NaN).