L'analisi descrittiva
Finalità
Come è stato detto nel capitolo di introduzione metodologica, le statistiche descrittive sono finalizzate a:
- avere una prima visione, qualitativa, delle variabili raccolte;
- controllare la presenza di errori, ad esempio di data-entry;
- far emergere outliers e anomalie;
- valutare qualitativamente ipotesi e assunti, determinare qualitativamente le relazioni fra le variabili;
- identificare l’entità e la direzione delle relazioni fra le variabili;
- selezionare i modelli statistici appropriati;
Sappiamo inoltre che si usano indicatori e strumenti diversi in base alla tipologia delle variabili (categoriali, ordinali, quantitative) ed in base al numero di variabili prese in considerazione (univariate, bivariate, multivariate).
Analizziamo ora le funzioni più comuni nell'analisi descrittiva.
Variabili categoriali
Tabelle di contingenza
Le tabelle di contingenza permettono di rappresentare la distribuzione di frequenza di variabili categoriali e di fattori.
In R, si usa la funzione table(variabile)
per la rappresentazione univariata, e table(variabile1, variabile2)
per la rappresentazione bivariata.
Barplot
Un metodo grafico per visualizzare la distribuzione di frequenza di una variabile categoriale o ordinale è il barplot, usando la forma barplot(table(variabile))
.
La moda
In R non esiste una funzione per calcolare la moda, ovvero la categoria (o il valore, in caso di variabili numeriche) con la frequenza più alta. Per calcolarla, si identifica il valore più alto della tabella delle frequenze.
# creiamo una variabile nominale con 3 livelli
nominale <- factor(
sample (c("rosso","bianco","verde"), 100,replace = TRUE))
# la tabella delle frequenze
(frequenze<-table(nominale))
## nominale
## bianco rosso verde
## 31 28 41
# calcoliamo la moda
moda<-which(frequenze == max(frequenze))
names(frequenze)[moda]
## [1] "verde"
# il grafico a barre
barplot(frequenze)
Variabili ordinali
Gli indici che si possono calcolare con le variabili ordinali sono, oltre al numero di livelli e la moda, anche il minimo, il massimo, la mediana, i quartili ed il range interquantile.
Come abbiamo visto nel capitolo dedicato alle tipologie di dati, in R le variabili ordinali vanno rappresentate come fattori ordinati.
La funzione summary()
però, se applicata ad un fattore, non restituisce questi dati, perché non vede il fattore come variabile numerica. Per aggirare l'ostacolo, è necessario utilizzare il vettore numerico sottostante, attraverso la chiamata alla funzione as.integer()
.
# creiamo una variabile ordinale con 5 livelli
cat_ord <- c("A","B","C","D", "E")
ordinale <- factor(
sample (cat_ord, 100,replace = TRUE),
levels = cat_ord, ordered = TRUE )
# summary su ordinale
summary(ordinale)
## A B C D E
## 23 25 17 11 24
# summary mi da anche la media, che non ha senso
summary(as.integer(ordinale))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.00 2.00 3.00 2.88 4.00 5.00
# meglio usare quantile
# calcolare i quartili sul vettore sottostante
quantile(as.integer(ordinale))
## 0% 25% 50% 75% 100%
## 1 2 3 4 5
Variabili a intervalli
Oltre a moda, mediana, minimo, massimo e quartili, per le variabili ad intervalli si calcolano la media e la varianza / deviazione standard.
Dal punto di vista grafico, oltre a boxplot
, si usa hist
per rappresentare l'istogramma delle frequenze
# generiamo una variabile numerica con distribuzione normale
# rnorm = numeri Random con distribuzione NORMale
# 100 osservazioni, media=10, ds=2
intervalli <- rnorm(100, mean=10, sd=2)
summary(intervalli)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 4.512 8.839 10.334 10.223 11.611 13.965
sd(intervalli)
## [1] 1.876995
boxplot(intervalli)
hist(intervalli)
Due variabili categoriali
Il rapporto fra due variabili categoriali (nominali o ordinali) può essere rappresentato attraverso la tabella di contingenza a due vie. Graficamente, il rapporto può essere rappresentato attraverso il grafico mosaicplot
.
(contingenza <- table(nominale, ordinale))
## ordinale
## nominale A B C D E
## bianco 6 7 7 4 7
## rosso 6 9 3 4 6
## verde 11 9 7 3 11
mosaicplot(contingenza)
Una variabile categoriale, una numerica
By
by()
è una funzione della famiglia di apply
. La logica di by
è di separare le righe di un data frame, una matrice o un vettore in base ad un fattore. Ad esempio, la funzione by(intervalli,ordinale,mean)
divide la variabile intervalli
in cinque gruppi, in base al fattore ordinale
, e per ogni gruppo applica la media.
by(intervalli,ordinale,mean)
## ordinale: A
## [1] 10.32556
## ---------------------------------------------------------------------------------------
## ordinale: B
## [1] 10.11703
## ---------------------------------------------------------------------------------------
## ordinale: C
## [1] 10.02585
## ---------------------------------------------------------------------------------------
## ordinale: D
## [1] 9.597189
## ---------------------------------------------------------------------------------------
## ordinale: E
## [1] 10.66142
Boxplot
Boxplot può essere usato per confrontare una variabile ad intervalli su una variabile categoriale (nominale o ordinale).
boxplot(intervalli ~ ordinale)
Due variabili numeriche
In caso di due variabili numeriche, la rappresentazione grafica è il grafico di dispersione. Se dal grafico appare che vi sia una correlazione, può essere utile calcolare la regressione lineare, con la funzione lm(y ~ x)
e disegnare la retta di regressione, con la funzione abline()
.
# creo una seconda variabile ad intervalli
# che *correla* con la prima
intervalli2 <- intervalli + rnorm(100, mean=2, sd=3)
# calcolo la regressione lineare
lineare <- lm(intervalli2 ~ intervalli)
# il grafico di dispersione
plot(intervalli,intervalli2)
# disegno la retta di regressione
abline(lineare)
Risorse
- Exploratory Data Analysis Using R – Datazar Blog
- The Personality Project's Guide to R
- Exploratory Data Analysis with R
- visualising data - psyr.org
Esercizio
In questo esercizio caricheremo un file di dati, in formato tsv (valori separati da tab), puliremo il data.frame, e faremo alcune analisi descrittive sui dati.
partecipanti <- read.delim("https://s3.eu-central-1.amazonaws.com/bussolon/dati/parole_nonparole.tsv")
Controllo
- la dimensione della tabella (righe e colonne)
- i nomi delle righe
Ci si aspetta che le righe corrispondano alle osservazioni (in questo caso, i partecipanti), mentre le colonne sono le variabili.
dim(partecipanti)
## [1] 768 7
names(partecipanti)
## [1] "Data" "sex" "age" "scol" "professione" "risposte" "corrette"
str(partecipanti)
## 'data.frame': 768 obs. of 7 variables:
## $ Data : chr "2003/06/25 18:45:44" "2003/06/26 10:34:00" "2003/06/26 11:10:21" "2003/06/26 12:03:05" ...
## $ sex : chr "femmina" "maschio" "femmina" "femmina" ...
## $ age : int 25 40 45 31 50 31 NA 26 50 18 ...
## $ scol : int 13 13 13 13 18 13 NA 13 16 8 ...
## $ professione: chr "libero professionista" "agente di viaggio" "XX" "impiegata" ...
## $ risposte : int 48 49 44 47 49 49 47 49 49 49 ...
## $ corrette : int 30 39 37 24 34 30 29 35 38 30 ...
Se non altrimenti specificato, le funzioni read.[table|csv|delim]
tendono a considerare le colonne stringa come fattori (stringsAsFactors = TRUE
). Nel caso della variabile sex
questo è corretto. Nel caso di professione
è formalmente corretto, ma in pratica i dati sono talmente sporchi (perché raccolti via internet con una domanda a risposta aperta) che ci ritroviamo con 0 livelli
. Nel caso della variabile Data
la trasformazione in fattore è del tutto sbagliata.
summary(partecipanti)
## Data sex age scol professione risposte
## Length:768 Length:768 Min. : 2.00 Min. : 5.00 Length:768 Min. : 0.00
## Class :character Class :character 1st Qu.:24.00 1st Qu.:13.00 Class :character 1st Qu.:48.00
## Mode :character Mode :character Median :29.00 Median :13.00 Mode :character Median :49.00
## Mean :32.33 Mean :14.22 Mean :46.22
## 3rd Qu.:40.00 3rd Qu.:18.00 3rd Qu.:49.00
## Max. :99.00 Max. :18.00 Max. :49.00
## NA's :37 NA's :38
## corrette
## Min. : 0.00
## 1st Qu.:30.00
## Median :34.00
## Mean :32.43
## 3rd Qu.:37.00
## Max. :49.00
##
I dati erano stati raccolti su internet, attraverso una servlet java. Nel caso della variabile sex
, i dati mancanti erano stati contrassegnati con la stringa null
. Pertanto, è necessario trasformare i sex == null
in NA
.
partecipanti$sex[partecipanti$sex=='null'] <- NA
levels(partecipanti$sex)
## NULL
## per togliere il livello "null", che ora è vuoto
## [r - Drop factor levels in a subsetted data frame - Stack Overflow](https://stackoverflow.com/questions/1195826/drop-factor-levels-in-a-subsetted-data-frame)
partecipanti$sex <- factor(partecipanti$sex)
levels(partecipanti$sex)
## [1] "femmina" "maschio"
# in alternativa https://stackoverflow.com/a/17218028/1042167
# y <- droplevels(y)
Nonostante la trasformazione dei null
in NA
, il fattore mantiene il livello null
, anche se con 0 elementi. Per pulire i livelli, è necessario ri-applicare la funzione factor()
alla colonna da pulire.
Scolarità
La scolarità dei partecipanti era stata codificata con un numero che corrispondeva agli anni:
- 5: elementari
- 8: scuole dell'obbligo
- 13: diploma
- 16: laurea triennale
- 18: laurea magistrale
Questa variabile, però, non è propriamente numerica, e può costituire un fattore su cui fare alcune analisi. Più in particolare può essere interessante capire se vi è un rapporto fra scolarità e numero di risposte corrette.
Trasformare in fattore
Per poter fare questo tipo di analisi, è opportuno trasformare questa variabile da numerica (int) ad un fattore. La funzione per operare questa trasformazione è as.factor()
.
partecipanti$scol <- as.factor(partecipanti$scol)
levels(partecipanti$scol)
## [1] "5" "8" "13" "16" "18"
levels(partecipanti$scol) <- c("elementari", "medie","diploma","triennale","magistrale")
as.integer(partecipanti$scol)[1:20] # i primi 20 elementi
## [1] 3 3 3 3 5 3 NA 3 4 2 3 5 3 3 5 5 5 5 3 3
summary(partecipanti$scol)
## elementari medie diploma triennale magistrale NA's
## 10 71 360 60 229 38
(quant_scol <- quantile(as.integer(partecipanti$scol), na.rm = TRUE))
## 0% 25% 50% 75% 100%
## 1 3 3 5 5
levels(partecipanti$scol)[quant_scol]
## [1] "elementari" "diploma" "diploma" "magistrale" "magistrale"
Le date
Attraverso la funzione strptime(stringa, formato)
possiamo fare il parsing della colonna data, rendendo esplicito il formato da utilizzare, ed otterremo un vettore di date.
partecipanti$Data <- strptime(partecipanti$Data, format = "%Y/%m/%d %H:%M:%S")
summary(partecipanti$Data)
## Min. 1st Qu. Median Mean 3rd Qu.
## "2003-06-25 18:45:44" "2003-07-22 19:00:15" "2003-08-21 12:19:56" "2003-08-21 00:29:27" "2003-09-18 02:39:42"
## Max.
## "2003-10-21 00:19:14"
partecipanti$professione[partecipanti$professione=="XX"] <- NA
str(partecipanti$professione)
## chr [1:768] "libero professionista" "agente di viaggio" NA "impiegata" "insegnante" "impiegata" NA "studente" ...
partecipanti$professione <- as.character(tolower(partecipanti$professione))
Nella colonna professione
, i dati mancanti erano contrassegnati con i caratteri XX
. Attraverso il filtro, abbiamo trasformato XX
in NA
.
Essendo questo un campo a testo libero, alcune persone scrivevano tutto minuscolo, altre maiuscolo. Per semplificare le cose, abbiao trasformato i valori in minuscolo, con la funzione tolower()
.
Boxplot
Una modalità efficace per valutare visivamente se ci sono delle differenze fra diversi gruppi è utilizzando i grafici Box plot.
Ad esempio, potremmo chiederci se vi è una relazione fra scolarità e numero di risposte corrette.
boxplot(partecipanti$corrette ~ partecipanti$scol)
Il grafico boxplot permette di visualizzare la mediana e la distanza interquartilica (ovvero fra il primo ed il terzo quartile). Permette inoltre di stimare la varianza (attraverso i baffi) e gli eventuali outlier, ovvero delle osservazioni che si discostano in maniera evidente dalla distribuzione. Dal grafico si può osservare che vi sono numerosi dati anomali. Nel test i partecipanti dovevano fare un numero di scelte vero o falso su 50 item. Chi rispondeva a caso aveva il 50% di probabilità di rispondere correttamente. Tutti i punteggi inferiori a 20 risultano dunque sospetti. L'ipotesi più plausibile è che quei punteggi rappresentino dei soggetti che avevano fatto molte omissioni (ovvero risposto soltanto ad alcuni dei 50 item).
Per valutare questa ipotesi, possiamo verificare la colonna risposte.
hist(partecipanti$risposte)
L'istogramma della variabile risposte
lascia intendere che sebbene la maggior parte dei partecipanti abbia risposto ad almeno 40 delle 50 domande, alcuni hanno fatto un numero di omissioni più alto. Per avere una misura dei partecipanti che hanno risposto a meno di 40 domande, possiamo usare un filtro (partecipanti$risposte>40
) e la funzione table()
table(partecipanti$risposte>=40)
##
## FALSE TRUE
## 61 707
Su 768 partecipanti, 64 hanno risposto a meno di 40 domande.
plot(partecipanti$risposte, partecipanti$corrette, col = as.factor(partecipanti$risposte<40))
Il grafico conferma la nostra ipotesi: i punteggi più bassi corrispondono a quei partecipanti che hanno risposto a meno domande.
Attraverso il parametro col = as.factor(partecipanti$risposte<40)
abbiamo colorato i punti del plot in base alla soglia delle 40 risposte.
Filtrare i partecipanti
A questo punto, possiamo decidere di considerare validi solo i partecipanti che hanno risposto ad almeno 40 domande.
partecipanti_validi <- partecipanti[partecipanti$risposte>=40,]
dim(partecipanti_validi)
## [1] 707 7
Il nuovo data frame ha ancora 7 colonne, e 707 righe.
Per verificare la distribuzione di risposte date e risposte corrette, possiamo fare il plot sul nuovo data frame.
plot(partecipanti_validi$risposte, partecipanti_validi$corrette)
Come prevedibile, nel nuovo data frame il numero minimo di risposte corrette è 19.
Creiamo il boxplot corrette ~ scol sul nuovo data frame.
boxplot(partecipanti_validi$corrette ~ partecipanti_validi$scol)
Attach
Per evitare di ripetere ogni volta il nome del data frame, possiamo utilizzare la funzione attach ()
, che ci permette di richiamare direttamente i nomi delle variabili del data frame.
## summary(scol) ---> Error in summary(scol) : oggetto "scol" non trovato
attach(partecipanti_validi)
## I seguenti oggetti sono mascherati da partecipanti_validi (pos = 3):
##
## age, corrette, Data, professione, risposte, scol, sex
## I seguenti oggetti sono mascherati da partecipanti_validi (pos = 4):
##
## age, corrette, Data, professione, risposte, scol, sex
## I seguenti oggetti sono mascherati da partecipanti_validi (pos = 7):
##
## age, corrette, Data, professione, risposte, scol, sex
summary(scol)
## elementari medie diploma triennale magistrale NA's
## 10 65 334 59 212 27
Analisi delle variabili nominali
Possiamo calcolare la frequenza attraverso la funzione table().
(freq_sex <- table (sex))
## sex
## femmina maschio
## 431 265
freq_sex / sum(freq_sex) # frequenza
## sex
## femmina maschio
## 0.6192529 0.3807471
prop.table(freq_sex) # = freq_sex / sum(freq_sex)
## sex
## femmina maschio
## 0.6192529 0.3807471
Grafici su variabili nominali
La funzione barplot mi permette di fare un grafico a barre.
barplot (freq_sex)
pie
è una funzione che permette di generare dei grafici a torta. I grafici a torta sono piuttosto popolari, ma funzionano soltanto con un gruppo molto limitato di livelli (non più di 4). Nel caso di un fattore con due livelli, come in questo caso, il grafico a torta può dare un'idea visiva della frequenza nei due generi.
pie(freq_sex)
Calcolo della moda
t_sex<-table(sex)
(mode_sex<-which(t_sex == max(t_sex)))
## femmina
## 1
sex[mode_sex]
## [1] femmina
## Levels: femmina maschio
t_sex[mode_sex]
## femmina
## 431
Scolarità
freq_scol <- table (scol)
freq_scol
## scol
## elementari medie diploma triennale magistrale
## 10 65 334 59 212
prop.table(freq_scol)
## scol
## elementari medie diploma triennale magistrale
## 0.01470588 0.09558824 0.49117647 0.08676471 0.31176471
barplot (freq_scol)
barplot (freq_scol, beside = TRUE)
Calcolo della moda
t_scol<-tabulate(scol)
mode_scol<-which(t_scol == max(t_scol))
mode_scol
## [1] 3
scol[mode_scol]
## [1] diploma
## Levels: elementari medie diploma triennale magistrale
t_scol[mode_scol]
## [1] 334
Distribuzione delle risposte corrette
attraverso hist()
possiamo visualizzare la distribuzione di variabili a intervalli o a rapporti. Usiamola per visualizzare la distribuzione di risposte corrette.
hist(partecipanti_validi$corrette)
Una alternativa, per le variabili numeriche, è utilizzare la funzione plot(density())
.
plot(density(partecipanti_validi$corrette))
fivenum()
è la versione sintetica di summary()
: restituisce il minimo, il primo quartile, la mediana, il terzo quartile e il massimo. In caso di variabili ordinali fivenum
è più corretto di summary
, in quanto la media è un valore che non ha senso in quel tipo di misura.
fivenum(partecipanti_validi$corrette)
## [1] 19 31 34 37 49
summary(partecipanti_validi$corrette)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 19.00 31.00 34.00 33.88 37.00 49.00
Conclusioni
L'analisi descrittiva è finalizzata a comprendere i dati: la dispersione (i livelli, il range interquantilico, il minimo, il massimo, la deviazione standard) e le tendenze centrali (moda, mediana, media) delle variabili e le eventuali relazioni fra variabili. Vengono utilizzati metodi statistici e strumenti grafici. Variabili diverse vengono rappresentate con grafici e statistiche diverse. L'anali descrittiva ci permette di cogliere molti aspetti dei dati che stiamo analizzando e di intuire la presenza di relazioni, la cui significatività andrà valutata attraverso la stastistica inferenziale.
Esercizio
- Visualizzare graficamente la relazione fra sesso e risposte corrette
- Creare la tabella di contingenza sesso * scolarità
- Visualizzare graficamente la relazione fra sesso e scolarità
- Visualizzare graficamente la relazione fra età e risposte corrette 4.1 Valutare la possibilità che ci siano dei dati sporchi in riferimento all'età riferita
- Creare un grafico a torta della variabile scolarità