Analisi dei Dati ed Estrazione della Conoscenza

Il sistema R: Modi, classi e funzioni generiche

Abbiamo visto che R dispone di vari tipi di dato: vettori numerici, vettori booleani, vettori di stringhe, matrici, array (anch'essi divisi in numerici, booleani e stringhe), liste, data frames, funzioni, etc... In realtà, in R vi sono due punti di vista per quanto riguarda i tipi degli oggetti: uno a basso livello ed uno ad alto livello. L'elenco appena fatto corrisponde ad una visione ad alto livello dei tipi di dato. In realtà, questa ricchezza è ottenuta a partire da un sistema di tipi a basso di livello molto ristretto, con l'uso degli attributi.

Modo

Dal punto di vista a più basso livello, in R esistono fondamentalmente i seguenti tipi di dato:

Per sapere il tipo di dato di un oggetto si può usare la funzione mode. Ad esempio:

> ? mode
> x=c(1,2,3,4)
> mode(x)
[1] "numeric"
> mode(log)
[1] "function"
> m=matrix(1:20,nrow=10)
> mode(m)
[1] "numeric"
> l=list(a=c(1,2),b=c(1,3))
> mode(l)
[1] "list"
> df=data.frame(a=c(1,2),b=c(1,3))
> mode(df)
[1] "list"

Notare come il modo di una matrice di numeri è numeric, esattamente come un vettore numerico. Inoltre, il modo per un data frame è esattamente lo stesso del modo per una lista. Tutto questo avviene perché, internamente, data frame e liste (e anche vettori e matrici numeriche) sono memorizzati e trattati esattamente con le stesse strutture dati. Quello che differenzia due tipi diversi che hanno lo stesso modo sono i loro attributi.

Attributo

Ad ogni oggetto in R sono associati uno o più attributi. Per manipolare gli attributi abbiamo le seguenti funzioni:

Normalmente un vettore (di qualunque modo) non ha attributi. Continuando l'esempio precedente:

> attributes(x)
NULL

La cosa cambia per matrici e array:

> attributes(m)
$dim
[1] 10  2

> a=array(1:20, dim=c(2,5,2))
> attributes(a)
$dim
[1] 2 5 2

Entrambi questi tipi hanno un attributo dim, un vettore numerico con la dimensione dell'array (da cui si capisce che m è un verrore 10x2 e a un array 2x5x2). La presenza di questo attributo è l'unica cosa che distingue i vettori dagli array o dalle matrici, tant'è che è possibile passare da un tipo all'altro semplicemente eliminando o aggiungendo un tale attributo.

> x
[1] 1 2 3 4
> attr(x,"dim") = c(2,2)
> x
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> attr(x,"dim") = NULL
> x
[1] 1 2 3 4

In generale, esistono alcuni attributi che hanno un significato speciale per R, e altri che invece non hanno alcun significato particolare e possiamo usare per i nostri scopi. Oltre a dim, un altro attributo che ha un significato per R è names, un vettore destinato a contenere i nomi delle posizioni.

> attr(x,"names")=c("A","B","C","D")
> x
A B C D 
1 2 3 4
> attr(x,"giuseppe")=12
> x
A B C D 
1 2 3 4 
attr(,"giuseppe")
[1] 12

Gli attributi più utilizzati, come dim e names, hanno di solito delle funzioni associate che consentono di accedervi direttamente. Così, attr(x,"dim") è esattamente equivalente a dim(x) e attr(x,"names") è esattamente equivalente a names(x) (quest'ultimo, tra l'altro, l'avevamo già incontrato nella lezione sui vettori).

Classe

Così come mode restituisce il tipo a basso livello di un oggetto, class restituisce il tipo ad alto livello. Ad esempio:

> class(x)
[1] "numeric"
> class(m)
[1] "matrix"
> class(log)
[1] "function"
> class(l)
[1] "list"
> class(df)
[1] "data.frame"
  

Notare che adesso matrici e data.frame hanno un valore di classe diverso da vettori e liste. Che cosa determina la classe di un oggetto? Se un oggetto ha uno specifico attributo class, la classe sarà il valore di quell'attributo. Infatti:

> attributes(l)
$names
[1] "a" "b"

> attributes(df)
$names
[1] "a" "b"

$row.names
[1] 1 2

$class
[1] "data.frame"

Se invece non è presente l'attributo class, il valore restituito dalla funzione class corrisponde in generale con il modo, tranne nel caso il modo sia uno tra numeric, logical e character ed è presente l'attributo dim. In quest'ultimo caso la classe sarà matrix se dim ha lunghezza 2, array altrimenti.

Il valore di classe di un oggetto determina come le varie funzioni di R si comportano su quell'oggetto. Ad esempio, la funzione print è utilizzata per visualizzare un oggetto, ed è quella chiamata direttamente da R alla fine della valutazione di una espressione. Liste e data frame vengono visualizzati da print in maniera completamente differente.

> l
$a
[1] 1 2

$b
[1] 1 3

> df
  a b
1 1 1
2 2 3
> 

La funzione unclass restituisce l'oggetto passato come input dopo aver rimosso l'attributo class. Questo è usato talvolta per vedere la struttura di lista di un oggetto di una classe complessa. Ad esempio:

> unclass(df)
$a
[1] 1 2

$b
[1] 1 3

attr(,"row.names")
[1] 1 2

Vedremo nelle prossime lezioni dei casi in cui può tornare utile.

Funzioni generiche

Alcune funzioni di R sono funzioni "generiche", nel senso che si applicano in maniera different a oggeti di classi differenti. Ad esempio, la funzione print che visualizza un oggetto sullo schermo, è una di esse. Sebbene un data frame sia solo un tipo particolare di lista, la visualizzazione di un data frame avviene in forma tabellare, una lista no.

Queste funzioni generiche sono normalmente definite utilizzando la funzione ausiliare UseMethod:

> print
function (x, ...) 
UseMethod("print")
<environment: namespace:base>

La chiamata UseMethod("print") cerca se esiste una funzione dal nome print.class dove class è la classe di x. Se la trova la esegue, passando x come parametro. Altrimenti esegue la funzione print.default.

> print(df)
  a b
1 1 1
2 2 3
> print.data.frame(df)
  a b
1 1 1
2 2 3
> print.default(df)
$a
[1] 1 2

$b
[1] 1 3

attr(,"class")
[1] "data.frame"

Notare l'uso di print.default per utilizzare la routine di stampa di default, invece di quella specifica per i data frame.

Per conoscere tutte le classi per cui esiste una implementazione specifica di una funzione generica si può usare la funzione methods:

> methods(print)
  [1] print.acf*                               
  [2] print.anova                              
  [3] print.aov*                               
  [4] print.aovlist*                           
   omissis 
  [42] print.data.frame
  [43] print.Date
  [44] print.default
   omissis 

Alcune funzioni specifiche sono nascoste, ovvero non possono essere chiamate esplicitamente ma solo passando dalla funzione generica. In tal caso sono asteriscate nell'output di methods. Altri esempi di funzioni generiche sono plot e summary.

Valid XHTML 1.1 Valid CSS!