Le matrici sono collezioni di dati dello stesso tipo, individuate da due indici di riga e colonna. Gli array sono estensioni del concetto di matrice a più di due dimensioni.
Riassumiamo le funzioni disponibili per il trattamento di matrici. Si rimanda alla dispansa di Muggeo e Ferrara per le spiegazioni:
matrix()
, cbind()
, rbind()
e diag()
[]
, funzioni diag()
e dim()
.drop
e argomento drop
per l'operatore []
.t()
e solve()
, operatori +/-
e %*%
.dimnames()
, rownames()
e colnames()
.is.matrix()
, as.vector()
Per quanto riguarda gli array, si usano esattamente le stesse operazioni previste per le matrici (quando applicabili). In più abbiamo la funzione array
per la creazione di un nuovo array.
apply
Quando si possiede una struttura dati complessa, è spesso necessario eseguire una determinata operazione per ognuno dei suoi elementi. Nei linguaggi di programmazione standard questo si fa tipicamente con dei cicli for
, ma nei linguaggi funzionali, come R, si sfruttano le capacità di ordine superiore del linguaggio.
apply(x,margin,fun)
: x è una matrice e margin è un intero pari a 1 oppure 2. La funzione apply spezzetta la matrice x
in righe (se margin=1) o in colonne (se margin=2) e applica ad ogni riga (o colonna) la funzione fun
. Poi, incolla i risulati tra di loro, ottenendo un vettore (se il risultato di fun
è un numero) o un'altra matrice (se il risultato di fun
è un vettore).
> m <- matrix(1:10,ncol=5)
> m
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 5 7 9
[2,] 2 4 6 8 10
> apply(m,1,sum)
[1] 25 30
> apply(m,2,sum)
[1] 3 7 11 15 19
Scrivere una espressione che calcoli la tabella pitagorica di lato 12, ovvero una matrice 12x12 nella quale, nella posizione (i,j), via sia il prodotto i * j. Utilizzare la funzione apply
(esistono altri metodi più furbi basati sul calcolo matriciale).
Scrivere una funzione tavolapitagorica
che prende in input un interno n
e restituisce la tavola pitagorica di dimensione n * n.
Un ulteriore tipo di dato supportato da R è il tipo lista. La lista è una sequenza di elementi che, a differenza di vettori, matrici ed array, possono avere tutti tipo diverso.
list(...)
: la funzione list prende una sequenza di parametri, ogni parametro diventa un elemento della lista.
> list(c(1,2,4),"prova",matrix(1:10,ncol=5))
[[1]]
[1] 1 2 4
[[2]]
[1] "prova"
[[3]]
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 5 7 9
[2,] 2 4 6 8 10
È possibile dare dei nomi agli elementi della lista specificando ogni parametro con la sintassi <nome-elemento>=<valore>
.
> list(c(1,2,4),testo="prova")
[[1]]
[1] 1 2 4
$testo
[1] "prova"
l[[i]]
: seleziona l'elemento in posizione i-esima della lista x. Se i è un a stringa, seleziona l'elemento che ha quel nome. Può essere usato anche per modificare un elemento della lista o per aggiungere un nuovo elemento.
> l <- list(c(1,2,4),testo="prova") # definisco una lista
> l[[1]]] # estraggo l'elemento n. 1
[1] 1 2 4
> l[["testo"]]<-"pippo" # modifico l'elemento n. 2
> l[[4]]<-TRUE # aggiungo l'elemento n. 4
> l # visualizzo la lista così modificata
[[1]]
[1] 1 2 4
$testo
[1] "pippo"
[[3]]
NULL
[[4]]
[1] TRUE
l$name
: equivalente a l[["name"]]
.
l[v]
: se v è un vettore di interi, restituisce la sottolista composta dagli elementi indicati in v; se v è un vettore di booleani lungo quanto la lista x, restituisce la sottolista composta dagli elementi corrispondenti ai valori TRUE.
> l[c(1,2)]
[[1]]
[1] 1 2 4
$testo
[1] "pippo"
> l[c(TRUE,FALSE,FALSE,TRUE)]
[[1]]
[1] 1 2 4
[[2]]
[1] TRUE
c(l1,...ln)
: concatena gli elementi l1, ... ln. Se almeno uno di questi elementi è una lista, il risultato finale è una lista.
> c(list(1,"prova"),c(1,2,3),list("fine")
[[1]]
[1] 1
[[2]]
[1] "prova"
[[3]]
[1] 1
[[4]]
[1] "fine"
names(l)
: restituisce un vettore con i nomi degli elementi della lista l. In corrispondenza degli elementi senza nome restituisce una stringa vuota. Se la lista non ha nessun nome, allora names(l)
è NULL
.
> names(l) [1] "" "testo" "" "" > names(list(c(1,2),"abc")) NULL
attach(l)
: rende visibili i componenti di una lista come se fossero variabili definite indipendentemente. In pratica, ciò che prima era visibile come l$nome
adesso è semplicemente visible come nome
. Si noti, tuttavia, che la modifica di nome
non modifica la lista originaria.
> l < list(elem1=c(1,2,3), elem2=c("a","b")) > attach(l) > elem1 [1] 1 2 3 > elem1 <- c(1) > elem1 [1] 1 > l$elem1 [1] 1 2 3
detach(l)
: rende invisibili i componenti di una lista precedentemente sottoposta al comando attach(l)
. Tuttavia, se sono state fatte modifiche alle variabili globali generate con attach, le variabili modificate rimangono.
> detach(l) > elem1 [1] 1 > elem2 Error: Object "elem2" not found
is.list()
e as.list()
L'ultima struttura dati che vediamo è il data frame. Questo è un tipo particolare di lista che viene utilizzata per rappresentare degli insiemi di dati. Gli elementi di un data-frame sono vettori (numerici o stringhe) tutti della stessa lunghezza: ogni vettore rappresenta un attributo dell'insieme di dati. Il data frame ha anche varie caratteristiche in comune con le matrici.
data.frame(...)
: simile a list(...)
ma il risultato è un data frame. Notare che un data frame ha sempre dei nomi per gli elementi: se non vengono forniti verranno generati automaticamente.
> data.frame(x=c(1,2,3),c("a","b","d")) x c..a....b....d.. 1 1 a 2 2 b 3 3 dNotare la visualizzazione di tipo matriciale che viene utilizzata per i data frame. Se gli elementi forniti come parametri non sono vettori, la funzione data.frame tenterà di fare la cosa più ovvia. Ad esempio, se un parametro è una matrice, ogni colonna della matrice costituirà un nuovo attributo dell'insieme.
> data.frame(a=matrix(1:10,ncol=5),label=c("a","b")) a.1 a.2 a.3 a.4 a.5 label 1 1 3 5 7 9 a 2 2 4 6 8 10 b
df[[i]]
, df$name
, df[v]
, names(df)
: come le analoghe operazioni per le liste, con la differenza che i risultati sono di tipo data frame.
df[i,j]
, dimnames(df)
, rownames(df)
, colnames(df)
: come le analoghe operazioni per le matrici, con la differenza che i risultati sono di tipo data frame. Inoltre nè rownames(df)
nè colnames(df)
possono essere impostati a NULL, e colnames(df)
è equivalente names(df)
.
La funzione apply
ha degli analoghi che si applicano alle liste (e quindi ai data frame) invece che agli array.
lapply(l,fun)
: applica la funzione fun a ogni elemento della lista l. Ad esempio
> l <- data.frame(a=c(1,1,2), b=c("outlook","sunny","sunny"))
> apply(l,table)
$a
1 2
2 1
$b
outlook sunny
1 2
sapply(l,fun)
: applica la funzione fun a ogni elemento della lista. Se possibile, trasforma il risultato in una matrice o in un vettore.
> l <- data.frame(a=c(1,1,2), b=c(10,20,30))
> lapply(l,rev)
$a
[1] 2 1 1
$b
[1] 30 20 10
> sapply(l,rev)
a b
[1,] 2 30
[2,] 1 20
[3,] 1 10
La funzione fun che si passa ad apply (e simili) può essere una funzione predefinita, ma non necessariamente. Ad esempio, nell'esempio che segue definiamo una nuova operazione meansd
che, dato un vettore, restituisce una nuovo vettore con la media e lo scarto quadratico medio dell'input. Utilizziamo poi questa nuova funzione con apply.
> l <- data.frame(a=c(1,1,2), b=c(10,20,30)) > myfun <- function (v) c(mean(v),sd(v)) > myfun(l$a) [1] 20 10 > sapply(l,myfun) a b [1,] 1.3333333 20 [2,] 0.5773503 10
Al solito, non è necessario definire una nuova funzione esplicitamente, come è stato fatto sopra. Invece di passare a sapply
la funzione myfun
, potevamo passare direttamente il codice R che definisce tale funzione. Ad esempio
> l <- data.frame(a=c(1,1,2), b=c(10,20,30)) > sapply(l,function (v) c(mean(v),sd(v))) > sapply(l,myfun) a b [1,] 1.3333333 20 [2,] 0.5773503 10
A partire dal set di dati sull'iris, calcolare una matrice che ha tante colonne quante sono le variabili del data frame e due sole righe. Ogni colonna contiene media e scarto quadratico medio della variabile corrispondente (se la variabile è numerica), o i valori di entropia assoluta e relativa se la variabile è categoriale.
In pratica, il risultato deve essere la seguente matrice:
sepallength sepalwidth petallength petalwidth class [1,] 5.8433333 3.0540000 3.758667 1.1986667 1.584963 [2,] 0.8280661 0.4335943 1.764420 0.7631607 1.000000
È possibile leggere dei dati provenienti da file esterni e salvare i risultati delle elaborazioni. I comandi più utili a tale scopo sono read.table
e write.table
.
read.table(nomefile)
: legge il file indicato come parametro e lo restituisce come risultato sotto forma di data frame. Normalmente, il file ha una prima riga composta dai nomi degli attributi, mentre le righe successive contengono i dati veri e propri, con una colonna in più iniziale che contiene il nome della riga:
x y
01 1 a
02 2 b
xx 3 c
In alternativa, si può usare un formato senza intestazioni di riga o di colonna, come in
1 a
2 b
3 c
Se si hanno invece solo le intestazioni di colonna, ma non di riga, si può utilizzare l'opzione header=TRUE
. In questo caso, il set di dati è fornito in questo modo:
x y
1 a
2 b
3 c
write.table(df,nomefile)
: salva il data frame df con il nome del file indicato, nel formato con intestazioni di riga e colonna.
Un caso particolare di file di testo contenente dati in formato tabellare è un file in formato CSV (Comma Separated Values). In un file CSV la prima riga contiene i nomi dei campi, e i campi sono separati da virgole. Per leggere e scrivere file CSV è possibile usare read.table
/write.table
con i parametri opportuni, oppure usare le funzioni specifiche read.csv
e write.csv
.
Importare in R i set di dati weather.csv e weather.nomincal.csv.