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

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"
## [6] "risposte"    "corrette"
str(partecipanti)
## 'data.frame':    768 obs. of  7 variables:
##  $ Data       : Factor w/ 767 levels "2003/06/25 18:45:44",..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ sex        : Factor w/ 3 levels "femmina","maschio",..: 1 2 1 1 2 1 3 1 1 1 ...
##  $ 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: Factor w/ 195 levels "?","/","///",..: 102 6 194 71 92 71 194 160 117 170 ...
##  $ 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 195 livelli. Nel caso della variabile Data la trasformazione in fattore è del tutto sbagliata.

summary(partecipanti)
##                   Data          sex           age             scol      
##  2003/08/29 15:50:24:  2   femmina:458   Min.   : 2.00   Min.   : 5.00  
##  2003/06/25 18:45:44:  1   maschio:287   1st Qu.:24.00   1st Qu.:13.00  
##  2003/06/26 10:34:00:  1   null   : 23   Median :29.00   Median :13.00  
##  2003/06/26 11:10:21:  1                 Mean   :32.33   Mean   :14.22  
##  2003/06/26 12:03:05:  1                 3rd Qu.:40.00   3rd Qu.:18.00  
##  2003/06/26 12:48:03:  1                 Max.   :99.00   Max.   :18.00  
##  (Other)            :761                 NA's   :37      NA's   :38     
##       professione     risposte        corrette    
##  XX         :242   Min.   : 0.00   Min.   : 0.00  
##  studente   : 87   1st Qu.:48.00   1st Qu.:30.00  
##  impiegata  : 50   Median :49.00   Median :34.00  
##  studentessa: 40   Mean   :46.22   Mean   :32.43  
##  impiegato  : 31   3rd Qu.:49.00   3rd Qu.:37.00  
##  medico     : 20   Max.   :49.00   Max.   :49.00  
##  (Other)    :298

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)
## [1] "femmina" "maschio" "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:

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 
## "2003-06-25 18:45:44" "2003-07-22 19:00:15" "2003-08-21 12:19:56" 
##                  Mean               3rd Qu.                  Max. 
## "2003-08-21 00:29:27" "2003-09-18 02:39:42" "2003-10-21 00:19:14"
partecipanti$professione[partecipanti$professione=="XX"] <- NA
str(partecipanti$professione)
##  Factor w/ 195 levels "?","/","///",..: 102 6 NA 71 92 71 NA 160 117 170 ...
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)
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. Qualsiasi guida di data visualization considera i grafici a torta come una pessima modalità di visualizzazione. Dunque il consiglio è: usateli con molta, molta parsimonia. 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. È comunque sconsigliabile utilizzarlo in una pubblicazione accademica.

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)

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

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

Esercizio

  1. Visualizzare graficamente la relazione fra sesso e risposte corrette
  2. Creare la tabella di contingenza sesso * scolarità
  3. Visualizzare graficamente la relazione fra sesso e scolarità
  4. 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
  5. Creare un grafico a torta della variabile scolarità