Analisi dei Dati ed Estrazione della Conoscenza

Il sistema R: i vettori

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.

Costruzione di vettori

Ecco alcune funzioni (e operatori) per la costruzione di vettori:

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.

Operazioni sui vettori

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.

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}.

Esercizio

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.

Estrazioni di elementi da un vettore

Altri operatori si occupano di estrarre da un vettore un qualunque elemento o un suo sottovettore.

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}

La funzione ifelse

Una 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}

Esercizio

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).

Valid XHTML 1.1 Valid CSS!