INTRODUZIONE A R Lezione 2 Silvia Bacci∗e Silvia Pandolfi† 1 Importare e esportare i dati Quando la mole di dati è sostanziosa l’immissione da tastiera non è agevole. La situazione più comune è che i dati vengano inseriti a partire da file esterni. R è in grado di leggere dati in praticamente qualsiasi formato1 . Per leggere dati da file R fornisce numerose funzioni, tra le quali read.table() e read.csv() che permettono di caricare file di testo .txt e .csv (comma separated value). Questi file possono utilizzare differenti separatori di colonna; i principali sono la virgola, il punto e virgola e la tabulazione. Nel caricare i dati in R occorre prestare attenzione a quale separatore viene utilizzato nel file. > read.table("percorso/file.txt", header = TRUE, sep = " \t", dec = ".") > read.table(file.choose()) Andiamo ad analizzarne gli argomenti: • file.choose(): permette di aprire una finestra di navigazione in cui selezionare il file d’interesse; • header = TRUE: identifica la prima riga della matrice dei dati come quella contenente i nomi delle variabili; • sep = "," specifica il tipo di separatore di colonna. Tra virgolette potremmo perciò inserire la virgola, il punto e virgola e per la tabulazione \t . > data = read.table("var_qualitative.txt",header=TRUE) >#Visualizza in maniera compatta la struttura di un oggetto > str(data) ’data.frame’: 500 obs. of 5 variables: $ eta : int 78 26 57 30 23 32 80 60 75 55 ... $ sesso : Factor w/ 2 levels "F","M": 1 1 1 1 2 1 2 1 1 2 ... $ titolo_studio: Factor w/ 4 levels "Diploma","Laurea",..: 3 3 1 4 1 1 2 2 4 3 ... $ reddito : num 66.7 29.5 50.6 39.6 30.2 ... ∗ [email protected] [email protected] 1 Tramite il pacchetto foreign, R è in grado di leggere e salvare dati in formati provenienti da altri programmi di analisi statistica, come Stata, SPSS e SAS. † 1 $ anni_lavoro : int 15 6 15 10 3 11 19 14 17 15 ... > #Mostra solo le prime sei righe della matrice dei dati > head(data) >#Restituisce il nome delle variabili della matrice > names(data) > sesso Errore: oggetto "sesso" non trovato > attach(data) > sesso > data$sesso > data[ ,2] I file con estensione .csv sono particolari tipi di documenti di testo che possono essere letti e salvati con Excel. > #Caricare il dataset DATI.csv > data2 = read.csv(file.choose(), header = TRUE, sep = ",") > head(data2) Sesso Diploma Regione Libri Cellulare Fumatore Parrucchiere Caffe Famiglia Altezza 1 F LS Umbria 3 Da 0 a 10 Si 0 8 5 165 2 M LS Umbria 1 Da 20 a 50 No 20 1 3 178 3 F COM Umbria 0 Da 0 a 10 No 40 0 5 165 4 M LC Centro 2 Da 10 a 20 Si 20 3 3 183 5 M LS Umbria 0 Da 20 a 50 Si 27 1 4 170 6 F COM Umbria 2 Da 10 a 20 Si 30 4 4 170 > str(data2) ’data.frame’: 138 obs. of 10 variables: $ Sesso : Factor w/ 2 levels "F","M": 1 2 1 2 2 1 1 2 1 1 ... $ Diploma : Factor w/ 4 levels "Altro","COM",..: 4 4 2 3 4 2 4 2 2 1 ... $ Regione : Factor w/ 3 levels "Centro","Sud",..: 3 3 3 1 3 3 3 1 3 3 ... $ Libri : int 3 1 0 2 0 2 1 2 1 1 ... $ Cellulare : Factor w/ 5 levels "0","Da 0 a 10",..: 2 4 2 3 4 3 2 2 2 2 ... $ Fumatore : Factor w/ 2 levels "No","Si": 2 1 1 2 2 2 2 2 2 1 ... $ Parrucchiere: int 0 20 40 20 27 30 15 18 50 0 ... $ Caffe : int 8 1 0 3 1 4 0 3 2 2 ... $ Famiglia : int 5 3 5 3 4 4 4 4 4 4 ... $ Altezza : int 165 178 165 183 170 170 160 172 155 160 ... Per esportare un data frame o una matrice su un file in R è disponibile la funzione > write.table(data2, file="prova.xls",sep="\t",dec=",", row.names = FALSE) 1.1 I pacchetti in R Le funzioni di R sono organizzate in pacchetti, i più importanti dei quali sono già disponibili quando si accede al programma. Alcuni pacchetti, pur essendo presenti nella release di base di R, devono essere caricati durante la sessione di lavoro, mediante il comando: 2 > require(nomepacchetto) > library(nomepacchetto) Per sapere quali sono i pacchetti già presenti nella release di R con cui si sta lavorando, basta scrivere: > library() In alternativa, dal menu “Pacchetti” si sceglie l’opzione “Carica pacchetto”. In tal caso apparirà una finestra con la lista dei pacchetti già installati. Sarà quindi sufficiente selezionare il nome del pacchetto da caricare con il mouse e poi cliccare su ”OK”. Per avere informazioni sul pacchetto e sulle funzioni in esso contenute: > help(package = "nomepacchetto") Alcuni pacchetti non sono invece presenti nella release di base di R. Per installare un pacchetto non presente, è sufficiente scrivere: > install.packages("nomepacchetto") La prima volta che si usa questa funzione durante una sessione di lavoro si dovrà anche selezionare da una lista il sito mirror da cui scaricare il pacchetto. In alternativa, nel menu “Pacchetti” si seleziona “Installa pacchetti” e, successivamente, si seleziona il pacchetto dalla lista di tutti i pacchetti. 1.2 > > > > Importare i dati da un file di Excel install.packages("gdata") library(gdata) # carica il pacchetto gdata help(read.xls) mydata = read.xls("sweat.xls", sheet=1) # legge dal primo foglio di lavoro 1.3 Esercizi Dopo aver caricato il dataset DATI.csv: • Estrarre l’altezza degli studenti umbri; qual è l’altezza dello studente più basso e quella dello studente più alto? • Calcolare la spesa complessiva per il parrucchiere degli studenti maschi provenienti dal Sud Italia. • Estrarre l’altezza degli studenti in base al sesso • Estrarre il numero di libri letti dagli studenti dei licei 3 2 Introduzione all’analisi dei dati Quando si svolge un’analisi statistica dei dati è necessario procedere prima con un’analisi di tipo esplorativo per capire la struttura generale delle variabili che compongono l’insieme di dati e successivamente con una serie di analisi di tipo descrittivo per la comprensione dei fenomeni studiati. Solo dopo queste prime due fasi ha senso procedere ad analisi più approfondite di natura inferenziale. 2.1 Manipolazione dei dati (etichette, ricodifiche, creazione nuove variabili) Per prima cosa carico i dati: > # importo i dati > dati <- read.table(famiglia2.txt, header = TRUE) Come introdotto in precedenza, con la funzione dim ottengo la dimensione del mio dataset, con il numero di righe corrispondente al numero di individui e il numero di colonne corrispondente al numero di variabili: > dim(dati) [1] 100 5 Successivamente, posso utilizzare le due funzioni str e head per ottenere una descrizione delle variabili contenute in dati: > # visualizzo le prime righe (= individui) del dataset ># e tutte le colonne (= variabili) > head(dati) X1 X2 X3 X4 X5 1 1 17.695 3 1 9.549 2 2 21.510 3 1 10.062 3 3 27.602 1 1 5.089 4 4 36.846 3 0 10.137 5 5 23.252 1 0 4.343 6 6 20.496 1 0 3.991 > # ottengo informazioni sulla natura delle variabili > str(dati) ’data.frame’: 100 obs. of 5 variables: $ X1: int 1 2 3 4 5 6 7 8 9 10 ... $ X2: num 17.7 21.5 27.6 36.8 23.3 ... $ X3: int 3 3 1 3 1 1 3 1 3 2 ... $ X4: int 1 1 1 0 0 0 0 1 0 0 ... $ X5: num 9.55 10.06 5.09 10.14 4.34 ... I nomi delle variabili sono visualizzabili tramite: > names(dati) [1] "X1" "X2" "X3" "X4" "X5" 4 Spesso è utile modificare i nomi originari delle variabili: > # cambio i nomi delle variabili > names(dati) <- c("id","reddito","numcomp","sesso","spesaal") > head(dati) > # oppure, in fase di lettura dei dati: > dati2 <- read.table(file.choose(), header = TRUE, + col.names=c("id","reddito","numcomp","sesso","spesaal")) > attach(dati2) > str(dati2) ’data.frame’: 100 obs. of 5 variables: $ id : int 1 2 3 4 5 6 7 8 9 10 ... $ reddito: num 17.7 21.5 27.6 36.8 23.3 ... $ numcomp: int 3 3 1 3 1 1 3 1 3 2 ... $ sesso : int 1 1 1 0 0 0 0 1 0 0 ... $ spesaal: num 9.55 10.06 5.09 10.14 4.34 ... Non sempre le variabili originarie presenti nel dataset sono utili ai fini delle analisi che vogliamo svolgere. Può essere necessario ricorrere a delle manipolazioni particolari, quali ridefinizione della natura della variabile, creazione di nuove etichette per le modalità della variabile, definizione di nuove variabili a partire da variabili già esistenti. > ## Trasformo la variabile sesso, vista come intero, in un fattore > is.factor(sesso) [1] FALSE > levels(sesso) NULL > sesso<-as.factor(sesso) > levels(sesso) [1] "0" "1" > # creare le etichette per i livelli di sesso (0=maschio, 1=femmina) > levels(sesso) <- c("maschio","femmina") > is.numeric(reddito) > # Indago sulla presenza di eventuali risposte mancanti: > is.na(reddito) > is.na(dati2) > > > > > # Creo una variabile aggregata da numcomp # (max 1 componente oltre al capofamiglia, 2-3, almeno 4) sort(numcomp) numcomp2 = 0 + (numcomp>1) + (numcomp>3) table(numcomp,numcomp2) > > > > # Creo tre dummy dalla variabile numcomp2 n = nrow(dati2) numcomp3 = matrix(0,n,3) numcomp3[cbind(1:n, numcomp2+1)]=1 5 > cbind(numcomp2, numcomp2+1, numcomp3) > # Alternativa (modo meno parsimonioso!!) > numcomp4 = matrix(0,n,3) > for (i in 1:n){ if (numcomp2[i]==0) { numcomp4[i,1] = 1 } if (numcomp2[i]==1) { numcomp4[i,2] = 1 } if (numcomp2[i]==2) { numcomp4[i,3] = 1 } } > # Aggiungo le due nuove variabili ai dati > dati2 = cbind(dati2, numcomp2, numcomp3) > names(dati2)[7:9] <- c("0-1 comp", "2-3 comp", "almeno 4 comp") > # ordinare i dati in senso ascendente per la variabile numcomp > ?order > dati2[order(numcomp), ] 2.2 Analisi descrittiva dei dati In R ci sono diverse funzioni per l’analisi descrittiva dei dati. Tra le più utilizzate, alcune sono state introdotte nelle precedenti sezioni (mean(), median(), sd(), length(), sum()). Altre funzioni utili sono: • var(): calcola la varianza di un vettore di dati, la covarianza tra due vettori, o la matrice di varianze e covarianze di una matrice di dati • cor(): calcola la correlazione tra due vettori, o la matrice di correlazione di una matrice di dati • summary(): riporta le principali statistiche descrittive di un vettore o di una matrice di dati • prod(): restituisce il prodotto tra gli elementi di un vettore o di una matrice • cumsum(): restituisce la somma cumulata degli elementi di un vettore • cumprod(): restituisce il prodotto cumulato degli elementi di un vettore • quantile(): calcola i quantili di una distribuzione • colMeans() - rowMeans(): calcola le medie di diverse variabili all’interno di un dataset (per colonna o per riga) 6 Inoltre, la funzione sample() genera un vettore estraendo da un altro oggetto gli elementi in ordine casuale. > y=1:10 > sample(y) [1] 5 9 2 7 3 1 10 8 6 4 > sample(1:90,size=1) [1] 53 > sample(y,size=20,replace=TRUE) [1] 8 1 6 1 6 8 9 4 2 6 5 2 7 9 10 10 7 3 7 5 Con riferimento alla variabile reddito dei dati famiglia2.txt, vediamo di seguito alcune analisi descrittive base: > summary(dati2) id reddito numcomp sesso Min. : 1.00 Min. :10.89 Min. :0.00 Min. :0.00 1st Qu.: 25.75 1st Qu.:19.87 1st Qu.:1.00 1st Qu.:0.00 Median : 50.50 Median :25.59 Median :2.00 Median :0.00 Mean : 50.50 Mean :27.28 Mean :1.97 Mean :0.25 3rd Qu.: 75.25 3rd Qu.:34.43 3rd Qu.:3.00 3rd Qu.:0.25 Max. :100.00 Max. :54.78 Max. :5.00 Max. :1.00 > summary(reddito) Min. 1st Qu. Median Mean 3rd Qu. Max. 10.89 19.87 25.60 27.28 34.43 54.78 > range(reddito) [1] 10.890 54.781 > mean(reddito) [1] 27.28431 > median(reddito) [1] 25.595 > quantile(reddito, probs=c(0.10,0.25,0.75, 0.90)) 10% 25% 75% 90% 14.47210 19.86625 34.43275 41.55800 > var(reddito) [1] 101.4713 2.3 spesaal Min. : 1.366 1st Qu.: 5.680 Median : 7.915 Mean : 8.016 3rd Qu.:10.113 Max. :15.995 Distribuzioni di frequenza semplici e doppie Uno strumento molto utile della statistica descrittiva è rappresentato dalle tabelle di frequenza semplici e doppie. Nel caso delle frequenze assolute la funzione principale che si utilizza è table. > # Tabelle delle frequenze di una sola variabile > table(sesso) sesso maschio femmina 75 25 > table(numcomp2) 7 numcomp2 0 1 2 37 57 6 > table(numcomp2)/nrow(dati2) numcomp2 0 1 2 0.37 0.57 0.06 > # Tabelle doppie di frequenza > tab1 <- table(sesso,numcomp2) > tab1 numcomp2 sesso 0 1 2 maschio 25 45 5 femmina 12 12 1 # tabella delle frequenze assolute > addmargins(tab1, FUN=sum) # aggiungo i totali di riga e colonna Margins computed over dimensions in the following order: 1: sesso 2: numcomp2 numcomp2 sesso 0 1 2 sum maschio 25 45 5 75 femmina 12 12 1 25 sum 37 57 6 100 Le corrispondenti tabelle delle frequenze relative o proporzioni si ottengono tramite la funzione prop.table. > # tabelle delle frequenze relative congiunte > tab2 <- prop.table(tab1, margin=NULL) > addmargins(tab2, FUN=sum) Margins computed over dimensions in the following order: 1: sesso 2: numcomp2 numcomp2 sesso 0 1 2 sum maschio 0.25 0.45 0.05 0.75 femmina 0.12 0.12 0.01 0.25 sum 0.37 0.57 0.06 1.00 > prop.table(tab1, margin=NULL)*100 numcomp2 sesso 0 1 2 maschio 25 45 5 femmina 12 12 1 # tabella delle frequenze percentuali 8 Per le tabelle a doppia entrate si possono definire anche le distribuzioni di frequenza condizionate rispetto ad una variabile attraverso l’argomento margin. Se margin =1 si ottiene la distribuzione condizionata rispetta alla variabile sulle righe, se margin=2 si ottiene la condizionata rispetto alla variabile sulle colonne. > # tabelle delle frequenze condizionate rispetto al sesso > # (o frequenze relative di riga) > prop.table(tab1, margin=1) numcomp2 sesso 0 1 2 maschio 0.33333333 0.60000000 0.06666667 femmina 0.48000000 0.48000000 0.04000000 > # tabella delle frequenze condizionate rispetto al numero di > #†componenti (o frequenze relative di colonna) > prop.table(tab1, margin=2) numcomp2 sesso 0 1 2 maschio 0.6756757 0.7894737 0.8333333 femmina 0.3243243 0.2105263 0.1666667 > # test di indipendenza Chi-quadrato (attenzione al Warning!) > chisq.test(sesso,numcomp2) Pearson’s Chi-squared test data: sesso and numcomp2 X-squared = 1.786, df = 2, p-value = 0.4094 Warning message: In chisq.test(sesso, numcomp2) : Chi-squared approximation may be incorrect 2.4 Le funzioni apply(), tapply(), e aggregate() Nell’analisi descrittiva dei dati è spesso utile calcolare una o più misure di sintesi (media, mediana, varianza, ecc.) in presenza di variabili classificazioni e organizzare i risultati in una tabella doppia. A questo proposito non esiste un metodo intuitivo in R per questo tipo di analisi, contrariamente ad altri software di analisi statistica dei dati. Le funzioni maggiormente adatte a tale scopo sono le seguenti: • apply(X,dim,FUN): permette di calcolare una generica funzione sulle righe o sulle colonne di una matrice di dati (o su una combinazione di dimensioni di un array multidimensionale) • tapply(x,INDEX,FUN), aggregate(X,by,FUN) e by(X,INDICES,FUN): se nel data frame sono presenti variabili di classificazione, permettono di calcolare una funzione separatamente su ciascuna classe di unità. tapply si applica ad un singolo vettore, aggregate ad una matrice di dati. In aggregate, by è una lista, anche composta 9 da un solo oggetto, di variabili di classificazione. Simile ad aggregate è la funzione by. > # Mediane di reddito e spesa alimentare > valori=cbind(reddito,spesaal) > apply(valori,2,median) reddito spesaal 25.59500 -0.10055 > # Reddito medio per sesso del capofamiglia > tapply(reddito,sesso,mean) maschio femmina 27.85677 25.56692 > # oppure > aggregate(reddito, by=list(sesso), FUN=mean) Group.1 x 1 maschio 27.85677 2 femmina 25.56692 > # Reddito medio per sesso del capofamiglia e numero componenti famiglia > aggregate(reddito, by=list(sesso, numcomp2), FUN=mean) Group.1 Group.2 x 1 maschio 0 28.17836 2 femmina 0 26.02675 3 maschio 1 27.52622 4 femmina 1 23.79192 5 maschio 2 29.22380 6 femmina 2 41.34900 > # oppure > tab3 <- by(reddito, INDICES=list(sesso, numcomp2), FUN=mean) > > > > > # Dispongo i dati di tab3 in una tabella tab3_new<- cbind(tab3[1:2],tab3[3:4],tab3[5:6]) colnames(tab3_new)=c(0, 1, 2) rownames(tab3_new)=c(’maschio’,’femmina’) tab3_new 0 1 2 maschio 28.17836 27.52622 29.2238 femmina 26.02675 23.79192 41.3490 > # Mediana di reddito e spesa alimentare per sesso e numero di componenti > aggregate(valori,by=list(sesso, numcomp2), FUN=median) Group.1 Group.2 reddito spesaal 1 maschio 0 23.8090 -3.41655 2 femmina 0 25.8390 -2.92555 3 maschio 1 26.1780 1.37645 4 femmina 1 22.3325 1.57745 5 maschio 2 35.6590 6.70645 10 6 femmina 2.5 2 41.3490 7.11045 Variabili in classi Caricare il file DATI.csv e nominarlo dati.studenti. Vogliamo costruire una variabile contenente l’altezza degli studenti suddivisa in classi. Il comando da utilizzare è cut(), che suddivide l’intero range della variabile quantitativa in base a dei breaking-point specificati dall’utente. Per tale motivo è utile conoscere il campo di variazione della variabile (comando range()). > dati.studenti=read.csv(file.choose(),header=T,sep=",") > head(dati.studenti) Sesso Diploma Regione Libri Cellulare Fumatore Parrucchiere Caffe Famiglia Altezza 1 F LS Umbria 3 Da 0 a 10 Si 0 8 5 165 2 M LS Umbria 1 Da 20 a 50 No 20 1 3 178 3 F COM Umbria 0 Da 0 a 10 No 40 0 5 165 4 M LC Centro 2 Da 10 a 20 Si 20 3 3 183 5 M LS Umbria 0 Da 20 a 50 Si 27 1 4 170 6 F COM Umbria 2 Da 10 a 20 Si 30 4 4 170 > alt.class=cut(dati.studenti$Altezza,breaks=c(150,165,170,175,180,190)) > alt.class [1] (150,165] (175,180] (150,165] (180,190] (165,170] (165,170] (150,165] [11] (165,170] (180,190] (150,165] (150,165] (165,170] (165,170] (165,170] [21] (150,165] (175,180] (150,165] (150,165] (165,170] (165,170] (150,165] [31] (180,190] (170,175] (175,180] (150,165] (150,165] (165,170] (150,165] [41] (180,190] (150,165] (170,175] (180,190] (150,165] (170,175] (170,175] [51] (150,165] (150,165] (150,165] (170,175] (170,175] (150,165] (180,190] [61] (150,165] (150,165] (150,165] (170,175] (175,180] (165,170] (180,190] [71] (165,170] (165,170] (150,165] (175,180] (170,175] (170,175] (180,190] [81] (170,175] (150,165] (170,175] (175,180] (175,180] (180,190] (165,170] [91] (170,175] (150,165] (150,165] (170,175] (150,165] (170,175] (165,170] [101] (165,170] (165,170] (150,165] (150,165] (175,180] (170,175] (165,170] [111] (170,175] (150,165] (150,165] (150,165] (150,165] (150,165] (180,190] [121] (150,165] (165,170] (180,190] (175,180] (180,190] (165,170] (175,180] [131] (165,170] (165,170] (165,170] (165,170] (150,165] (180,190] (150,165] Levels: (150,165] (165,170] (170,175] (175,180] (180,190] > table(dati.studenti$Sesso,alt.class) Nell’argomento break si possono specificare gli estremi delle classi come sopra, (notare che le classi non hanno ampiezze uguali), oppure il numero di classi desiderate. In questo caso R produce classi equidistanti > #5 classi di ampiezza uguale > alt.class=cut(dati.studenti$Altezza,breaks=5) 11 ... ... ... ... ... ... ... ... ... ... ... ... ... ... Per default il comando cut() chiude le classi a destra; con right=FALSE si possono chiudere le classi a sinistra. 2.6 Esercizi Da dataset DATI.csv si costruisca la tabella di contingenza delle variabili Sesso e Parrucchiere, dividendo la variabile Parrucchiere in classi [0 − 15), [15 − 20), [20 − 35), [35 − 150) (chiusura a sinistra). Fra gli studenti che spendono [20 − 35) euro dal parrucchiere, quant’è la percentuale dei maschi? Fra le femmine, qual’è la percentuale di quelle che spendono più di 35 euro? Quant’è la proporzione di studenti che prendono più di 4 caffè al giorno? 12