Introduzione a R
Introduzione
Prima di iniziare: Scaricate la cartella compressa data dentro cui si trovano tutti i dataset che sono usati in questo file.
R è un software open source per il calcolo statistico, la grafica e molto altro
Installazione
È necessario installare
R(disponibile https://cran.r-project.org/bin/windows/base/)
RStudio è l’IDE perfetto per R \(\rightarrow\)consente un utilizzo di R migliore e più semplice
RStudiosi può installare dopo aver installatoR(disponibile https://posit.co/download/rstudio-desktop/)
R funziona su Windows, MacOs, Unix
RStudio IDE
La schermata di RStudio è suddivisa in 4 pannelli:
- Alto sx: è l’area dedicata agli script di codice
- Alto dx: l’area dove è possibile vedere quello che è presente all’interno dell’ambiente di R (i.e., tutti gli oggetti, le variabili, i dataframe che sono stati caricati/creati per le analisi)
- Basso sx: la console di R, dove vengono mostrati gli output delle operazioni e i codici eseguiti
- basso dx: l’area dei grafici
Console
I comandi nella console vengono eseguiti ma il codice non viene salvato e scompare sequenzialmente.
Possono essere recuperati utilizzando la freccia verso l’alto.
Per eseguire qualsiasi comando nella console \(\rightarrow\) Invio (o Enter)
L’output appare immediatamente nella console.
Script
Si possono salvare gli script con tutte e righe di codice che servono per le analisi
Per far “correre” qualsiasi comando nello script \(\rightarrow\) Ctrl + Invio (or ctrl + Enter or cmd + Enter) oppure utilizza semplicemente il pulsante “Run” in cima allo script.
L’output appare nella console.
Si possono inserire commenti (i.e., righe di codice che non vengono eseguite) inserendo # \(\rightarrow\) tutto il testo dopo # non viene eseguito
Per fare lo switch alla console \(\rightarrow\) ctrl + 2
Per fare lo switch allo script \(\rightarrow\) ctr + 1
Working directories
Bisogna conoscere le working directories (da qui in avanti indicate come wd), ovvero le cartelle dove si lavor, dove si salvano i dati ecc.
getwd() # per sapere in quale wd si sta lavorando
dir() # lista degli oggetti all'interno della wd Per cambiare la wd:
setwd("C:/Users/huawei/OneDrive/Documenti/GitHub/RcouRse")Basics
CalcolatRice
3 + 2 # più
3 - 2 # meno
3 * 2 # per
3 / 2 # diviso
sqrt(4) # radice quadrata
log(3) # logaritmo naturale
exp(3) # esponenzialeParentesi e simili:
()[]{}"":;,
L’uso delle parantesi funziona come se si stesse scrivendo un’equazione:
(3 * 2)/ sqrt(25 + 4) # Look at me!R ignora tutto quello dopo # (è un commento)
Assign
I risultati delle operazioni possono essere “memorizzati” in oggetti (variabili, in senso informatico) con nomi specifici definiti dagli utenti
Per assegnare un valore a un oggetto, si possono utilizzare due operatori:
x = exp(2^2)X <- log(2^2)
Gli elementi a destra del simbolo vengono assegnati all’oggetto a sinistra
Attenzione! R e case sensitive: x e X sono due oggetti diversi!!!
Nomi delle variabili
Nomi di variabili validi sono lettere, numeri, punti, underscore (e.g., variable_name)
I nomi delle variabili non possono iniziare con numeri
L’idea per i nomi delle variabili è di dare indicazione di cosa contengono senza stare a scrivere divine commedie, ad esempio
Buoni nomi per le variabili:
mean_rtsum_ageprop_item
Pessimi nomi per variabili:
xyciccio
Chiedere aiuto
R community is the best feature of R
Basta copiare & incollare qualsiasi messaggio di errore o avviso in Google o chiedere a Google “come fare [qualcosa] in R”.
Chiedete a R di aiutarvi! Basta scrivere ? nella console seguito dal nome della funzione che si vuole conoscere:
?mean()
Mostra la help page della funzione mean()
Ordine, ordine e ancora ordine
R projects for the win
Gestire i directory di lavoro è un vero e proprio incubo. Per evitare di doverle gestire, si possono usare i porgetti di R
Un progetto R crea automaticamente una sorta di isola sul vostro pc con le sue sottoisole (che sono le sottocartelle che create voi)
Sono davvero comodi almeno per due motivi:
Permettono di avere diverse istanze di R aperte allo stesso tempo \(\rightarrow\) Potete lavorare a progetti diversi contemporaneamente
Tutti i file sono sempre a disposizione, senza preoccuparsi delle wd
Creare un nuovo R project
File \(\rightarrow\) New project e scegliete quello che fa al caso vostro (a meno che non abbiate già inizializzato un directory per il tuo progetto, selezionate una nuova directory):
Rproject “basic”RpackageShinyproject
e tanto altro
Creare un nuovo R project
File \(\rightarrow\) New project:
Selezionare il tipo di progetto
Assegnargli una directory e dargli un nome
Buttare via la spazzatura
L’ambiente R dovrebbe essere sempre ordinato
Per pulirlo:
ls() # lista tutti gli oggetti nell'ambiente
rm(A) # rimuove l'oggetto A dalla directory
rm(list=ls()) # rimuove tutto dall'ambienteSalvare l’ambiente
Per salvare tutti i risultati delle analisi in formato ambiente di R:
save.image("my-computations.RData")Per ricaricare l’ambiente in R:
load("my-computations.RData")I codici di prima, dove viene salvato e ricaricato l’ambiente, presuppone che l’ambiente di R venga salvato nella stessa wd dove è presente lo script
Quando salvare l’ambiente?
I calcoli sono lenti e hai bisogno che siano sempre e facilmente accessibili
La pratica migliore è salvare lo script e documentarlo in un file RMarkdown \(\rightarrow\) Riproducibilità!
Your turn!
Crea un progetto R per questo corso nella tua cartella “documenti” (scegli un nome carino :)
Crea un nuovo script R (
shift + ctrl + n)Calcolatrice utilizzando lo script:
- \(\sqrt{15} \times 14 - \frac{22}{4}\) [48.72177]
- \(\frac{\sqrt{7-\pi}}{3 \times (45-34)}\) [0.05952372]
Salva lo script
Assegna i risultati del primo calcolo a una variabile chiamata
my_results
See solution
# Calculator
sqrt(15) *14 - 22/4[1] 48.72177
# Calculator
sqrt(7 - pi)/(3*(45 -34))[1] 0.05952372
# assign
my_results = sqrt(15) *14 - 22/4Strutture in R
Funzioni e loro argomenti
Quasi tutto si fa attraverso le funzioni, composte da:
Un nome:
meanun paio di parentesi:
()degli argomenti:
na.rm = TRUE
mean(1:5, trim = 0, na.rm = TRUE)[1] 3
Gli argomenti possono essere impostati con valori predefiniti; la funzione di ogni argomento è spiegata esaustivamente nell’help della funzione stessa, e.g., ?mean()
Gli argomenti possono essere specificati:
senza un nome (seguendo il loro ordine di default)
con il loro nome (in qualsiasi ordine) \(\rightarrow\) keyword matching
mean(x, trim = 0.3, na.rm = TRUE)Alcune funzioni funzionano anche senza argomenti:
ls(), dir(), getwd()
Per vedere il codice di una funzione, basta scrivere il suo nome senza parentesi nella console e premere invio
chisq.test
concatenate, c()
Una delle funzioni più usate di R.
Concatena diverse variabili insieme per creare una nuova unica variabile
x = c(1, 2, 3) # crea un vettore concatenando 1,2,3
x[1] 1 2 3
X = 1:3 # crea lo stesso identico vettore
X[1] 1 2 3
x == X[1] TRUE TRUE TRUE
Vettori
Tipi di variabili diverse \(\rightarrow\) Tipi di vettori diversi:
int: Numeri interi(1,2, 0, -5)num: Numeri(-0.56, 3, 4.5)logi: Logico (TRUEvsFALSE)chr: characters("a", "b", "c")factor: factor con diversi livelli
int and num
int: numeri interi: -3, -2, -1, 0, 1, 2, 3
months = c(5, 6, 8, 10, 12, 16)[1] 5 6 8 10 12 16
num: tutti i numeri da \(-\infty\) a \(\infty\): 1.1335688, -0.6872158, -0.9923375, 0.5400471, 1.211567, 0.4292457
weight = seq(3, 11, by = 1.5) [1] 3.0 4.5 6.0 7.5 9.0 10.5
logi
Valori loigici, TRUE (T) o FALSE (F)
v_logi = c(TRUE, TRUE, FALSE, FALSE, TRUE)[1] TRUE TRUE FALSE FALSE TRUE
I vettori logici si possono ottenere attraverso il confronto dei valori in vettori di altro tipo, ad esempio:
months > 12[1] FALSE FALSE FALSE FALSE FALSE TRUE
restituisce un vettore logico dove FALSE indica tutti i valori all’interno di months che sono inferiori o uguali a 12, TRUE indica tutti i valori maggiori di 12
chr and factor
chr: characters: a, b, c, D, E, F
v_chr = c(letters[1:3], LETTERS[4:6])[1] "a" "b" "c" "D" "E" "F"
factor: usa u numeri o i characters per identificare i livelli della variabile
ses = factor(rep(c("low", "medium", "high"), each = 2))[1] low low medium medium high high
Levels: high low medium
Si può cambiare l’ordine dei livelli:
ses1 = factor(ses, levels = c("medium", "high", "low")) [1] low low medium medium high high
Levels: medium high low
Creare i vettori
I vettori si possono creare (anche) concatenando insieme variabili diverse
Si possono creare con la funzione c()
Tutti gli elementi all’interno di c() devono essere separati da una virgola
Concantenare gli oggetti con la funzione c(): vec = c(1, 2, 3, 4, 5)
Utilizzare le sequenze:
-5:5 # vettore di numeri da -5 a 5 [1] -5 -4 -3 -2 -1 0 1 2 3 4 5
seq(-2.5, 2.5, by = 0.5) # sequenza di numeri da -2.5 a 2.5 in step da 0.5 [1] -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5
Ripetere gli elementi:
# ripete gli elementi da 1a 3 per 4 volte
rep(1:3, 4) [1] 1 2 3 1 2 3 1 2 3 1 2 3
rep(c("condA", "condB"), each = 3)[1] "condA" "condA" "condA" "condB" "condB" "condB"
rep(c("on", "off"), c(3, 2))[1] "on" "on" "on" "off" "off"
paste0("item", 1:4)[1] "item1" "item2" "item3" "item4"
Non mischiare tra loro i vettori
int + num \(\rightarrow\) num
int/num + logi \(\rightarrow\) int/num
int/num + factor \(\rightarrow\) int/num
int/num + chr \(\rightarrow\) chr
chr + logi \(\rightarrow\) chr
Vettori e operazioni
I vettori possono essere sommati/sottratti/divisi e moltiplicati tra loro.
a = c(1:8)
a[1] 1 2 3 4 5 6 7 8
b = c(4:1)
b [1] 4 3 2 1
a - b[1] -3 -1 1 3 1 3 5 7
Se non hanno la stessa lunghezza e un vettore non ha una lunghezza che è un multiplo del’altro vettore si ottiene un messaggio di avvertimento
Se si applica una funzione, questa viene applicata a tutti gli elementi all’interno del vettore
sqrt(a)[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427
Allo stesso modo, la stessa operazione può essere applicata a tutti gli oggetti all’interno del vettore
(a - mean(a))^2 # deviazione dalla media al quadrato[1] 12.25 6.25 2.25 0.25 0.25 2.25 6.25 12.25
Your turn!
Crea un nuovo script e salvalo all’interno del progetto di R
Assegna i seguenti valori a una variabile chiamata
my_var(i valori devono essere concatenati tra loro)
23, 24, 25, 27, 28, 29, 30
Calcola la media di
my_varusando la funzionemean()Calcola la media di
my_varusando le funzionisum()elength()Trova il valore minimo (
min()) e massimo (max()) dimy_var
See solution
# crea il vettore e assegnalo a my_var
my_var = c(23, 24, 25, 27, 28, 29, 30)
# calcola la media
mean(my_var)[1] 26.57143
# calcola la media usando altre funzioni
sum(my_var)/length(my_var)[1] 26.57143
# trova il minimo e il masssimo
min(my_var); max(my_var)[1] 23
[1] 30
Indicizzare gli oggetti all’interno di un vettore
In questo ambito, indicizzare è vuol dire aver accesso a un particolare elemento all’interno del vettore, con una sua particolare posizione
names = c("Pasquale", "Egidio", "Giulia", "Livio", "Andrea")| Pasquale | Egidio | Giulia | Livio | Andrea |
|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 |
I vettori si indicizzano usando il numero corrispondente alla posizione dell’oggetto che si vuole estrarre all’interno di parentesi quadre: vector_name[index]
names[1] \(\rightarrow\) Pasquale
names[3] \(\rightarrow\) Giulia
names[seq(2, 5, by = 2)] \(\rightarrow\) Egidio, Livio
Esempi di indicizzazione di vettori
weight[1] 3.0 4.5 6.0 7.5 9.0 10.5
weight[2] # secondo elemento di weight[1] 4.5
(weight[6] = 15.2) # sostituisce il sesto elemento di weight[1] 15.2
weight[seq(1, 6, by = 2)] # elementi 1, 3, 5[1] 3 6 9
weight[2:6] # elementi dal 2 al 6 (incluso)[1] 4.5 6.0 7.5 9.0 15.2
weight[-2] # rimuove il secondo elemento[1] 3.0 6.0 7.5 9.0 15.2
Indicizzare usando la logica
weight[1] 3.0 4.5 6.0 7.5 9.0 15.2
Quali sono i valori \(> 7\) all’interno di weight?
weight > 7[1] FALSE FALSE FALSE TRUE TRUE TRUE
“Filtra” il vettore con la logica
weight[weight > 7] # solo i valori > di 7[1] 7.5 9.0 15.2
weight[weight >= 4.5 & weight < 8] # valori compresi tra 4.5 e 8[1] 4.5 6.0 7.5
Your turn!
- Considerando
my_var- Estrarre il terzo elemento
- Estrai tutti gli elementi dispari e assegnali a una nuova variabile
my_vector1 - Estrai tutti gli element \(> 25\) da
my_vector1
See solution
# Terzo elemento di my_var
my_var[3][1] 25
# Estrai tutti gli elementi dispari da my_var e assegnali a my_vector1
my_vector1 = my_var[seq(1, length(my_var), by = 2)]
# Estrai tutti gli elementi maggiori di 25 da my_vector1
my_vector1[my_vector1 > 25][1] 28 30
Matrici ed arrays
Per creare una matrice:
matrix(data, nrow, ncol, byrow = TRUE)
Crea una matrice \(3 \times 4\):
A = matrix(1:12, nrow=3, ncol = 4, byrow = TRUE)Etichette:
rownames(A) = paste("a",
1:nrow(A), sep ="_")
colnames(A) = paste("b",
1:ncol(A), sep ="_")
A b_1 b_2 b_3 b_4
a_1 1 2 3 4
a_2 5 6 7 8
a_3 9 10 11 12
Trasposizione:
t(A) a_1 a_2 a_3
b_1 1 5 9
b_2 2 6 10
b_3 3 7 11
b_4 4 8 12
Le matrici si possono creare attraverso la concatenazione di colonne e righe:
cbind(a1 = 1:4, a2 = 5:8, a3 = 9:12) # concatenazione per colonna a1 a2 a3
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
rbind(a1 = 1:4, a2 = 5.8, a3 = 9:12) # concatenazione per riga [,1] [,2] [,3] [,4]
a1 1.0 2.0 3.0 4.0
a2 5.8 5.8 5.8 5.8
a3 9.0 10.0 11.0 12.0
Arrays
Per cerare un array:
array(data, c(nrow, ncol, ntab))
my_array = array(1:30, c(2, 5, 3)) # 2 x 5 x 3 array, , 1
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 5 7 9
[2,] 2 4 6 8 10
, , 2
[,1] [,2] [,3] [,4] [,5]
[1,] 11 13 15 17 19
[2,] 12 14 16 18 20
, , 3
....
Indicizzare gli elementi in una matrice:
| [,1] | [,2] | [,3] | |
|---|---|---|---|
| [1,] | \([1, 1]\) | \([1, 2]\) | \([1, 3]\) |
| [2,] | \([2, 1]\) | \([2, 2]\) | \([2, 3]\) |
| [3,] | \([3, 1]\) | \([3, 2]\) | \([3, 3]\) |
Stessa logica usata per i vettori ma aggiungendo la seconda dimensione:
matrix_name[row, column]
A[2, 3] # cella della seconda riga nella terza colonna[1] 7
A[2, ] # seconda rigab_1 b_2 b_3 b_4
5 6 7 8
A[, 3] # terza colonnaa_1 a_2 a_3
3 7 11
Indicizzare gli elementi in un array
Oltre alle righe e alle colonne, va considerata anche la terza dinmensione, il tab
array_name[row, col, tab]
Codice
my_array[2, 1, 3] # cella nella seconda riga della prima colonna della terza tab[1] 22
Codice
my_array[, , 3] # terza tab [,1] [,2] [,3] [,4] [,5]
[1,] 21 23 25 27 29
[2,] 22 24 26 28 30
Codice
my_array[1, ,2] # prima riga della seconda tab[1] 11 13 15 17 19
Your turn!
- Crea una matrice \(3 \times 3\) con la tabella del 3 fino a 24
- Assegna la matrice alla variabile
my_mat - Dai un nome alle righe come “row” e alle colonne come “column”
- Trasponi my_mat e assegnala alla variabile
my_t - Indice da
my_t:- La prima riga
- La seconda colonna
- La cella \([1,2]\)
See solution
# Crea una matrice 3x3 con la tabella del 3 fino a 24
my_mat = matrix(seq(0, 24, by = 3), nrow = 3, ncol = 3)
# Assegna la matrice alla variabile `my_mat`
my_mat [,1] [,2] [,3]
[1,] 0 9 18
[2,] 3 12 21
[3,] 6 15 24
# Dai un nome alle righe come "row" e alle colonne come "column"
rownames(my_mat) = paste("row", 1:nrow(my_mat), sep = "_")
colnames(my_mat) = paste("column", 1:ncol(my_mat), sep = "_")
# Trasponi `my_mat` e assegna la trasposta alla variabile `my_t`
my_t = t(my_mat)
# Indice da `my_t`:
# - La prima riga
my_t[1, ]row_1 row_2 row_3
0 3 6
# - La seconda colonna
my_t[,2]column_1 column_2 column_3
3 12 21
# - La cella [1,2]
my_t[1,2][1] 3
Liste
Possono contenere diversi tipi di oggetti (e.g., vettori, data frames, altre liste):
my_list = list(w = weight, m = months, s = ses1, a = A)Gli elementi nelle liste possono essere indicizzati con $ oppure [[]] e il nome o la posizione dell’elemento che si vuole estrarre:
Indicizzare months:
my_list[["m"]] # my_list$m[1] 5 6 8 10 12 16
Indicizzare weight:
my_list[[1]] # my_list$weight oppure my_list[["w"]][1] 3.0 4.5 6.0 7.5 9.0 15.2
Your turn!
- Creare una lista con i seguenti elementi
my_matmy_t- I valori \(> 25\) di
my_var - Assegnare la lista all’oggetto
my_list
See solution
# - Creare una lista con i seguenti elementi
# - `my_mat`
# - `my_t`
# - I valori $> 25$ di `my_var`
# - Assegnare la lista all'oggetto `my_list`
my_list = list(my_mat, my_t, my_var[my_var > 25])Data frames
I data frames sono liste che contengono vettori anche di tipo diverso ma di stessa lunghezza:
id = paste0("sbj", 1:6)
babies = data.frame(id, months, weight)babies id months weight
1 sbj1 5 3.0
2 sbj2 6 4.5
3 sbj3 8 6.0
4 sbj4 10 7.5
5 sbj5 12 9.0
6 sbj6 16 15.2
Working with data frames
Gli elementi nei data frame si possono indicizzare in modo diverso (e simile a quanto visto per le matrici):
babies$months # colonna "months"[1] 5 6 8 10 12 16
babies$months[2] # secondo elemento della colonna months[1] 6
babies[, "id"] # colunna id[1] "sbj1" "sbj2" "sbj3" "sbj4" "sbj5" "sbj6"
babies[2, ] # seconda riga id months weight
2 sbj2 6 4.5
Si può usare la logica per indicizzare gli elementi ed estrarre (od escludere) le osservazioni che soddisfano un determinato criterio:
Codice
babies[babies$weight > 7, ] # tutte le osservazioni che mostrano weight > 7 id months weight
4 sbj4 10 7.5
5 sbj5 12 9.0
6 sbj6 16 15.2
Codice
babies[babies$id %in% c("sbj1", "sbj6"), ] # ossrevazioni dei sbj1 e sbj6 id months weight
1 sbj1 5 3.0
6 sbj6 16 15.2
Alcune funzioni utili da applicare ai dataframe
dim(babies) # mostra le dimensioni (numero righe e colonne) del data frame[1] 6 3
names(babies) # nomi delle variabili[1] "id" "months" "weight"
str(babies) # mostra le caratteristiche delle variabili nel data frame'data.frame': 6 obs. of 3 variables:
$ id : chr "sbj1" "sbj2" "sbj3" "sbj4" ...
$ months: num 5 6 8 10 12 16
$ weight: num 3 4.5 6 7.5 9 15.2
View(babies) # apre il data viewer
plot(babies) # pariwise plotsummary(babies) # calcola le statistiche descrittive id months weight
Length:6 Min. : 5.0 Min. : 3.000
Class :character 1st Qu.: 6.5 1st Qu.: 4.875
Mode :character Median : 9.0 Median : 6.750
Mean : 9.5 Mean : 7.533
3rd Qu.:11.5 3rd Qu.: 8.625
Max. :16.0 Max. :15.200
Riordinare i valori (e il data frame)
order():
babies[order(babies$weight), ] # riordina le righe del dataframe in ordine id months weight
1 sbj1 5 3.0
2 sbj2 6 4.5
3 sbj3 8 6.0
4 sbj4 10 7.5
5 sbj5 12 9.0
6 sbj6 16 15.2
# crescente di weightbabies[order(babies$weight, # riordina le righe in ordine decrescente
decreasing = T), ] Aggregate
Aggrega e applica una funzione su una variabile di confronto a seconda di una o più variabili di raggruppamento (per esempio è utile per calcolare le medie dei tempi di risposta in diverse condizioni sperimentali):
# Una variabile di confronto e una variabile di raggruppamento
aggregate(y ~ x, data = data, FUN, ...)
# Più variabili di confronto e di raggruppamento
aggregate(cbind(y1, y2) ~ x1 + x2, data = data, FUN, ...)Un esempio
L’esempio è basato su un dataset interno a R, ToothGrowth, dove si investiga la crescita dei denti delle cavie in base alla somministrazione di vitamina C
ToothGrowth # Crescita dei denti nelle cavie con la somministrazione di Vitamina C len supp dose
1 4.2 VC 0.5
2 11.5 VC 0.5
3 7.3 VC 0.5
4 5.8 VC 0.5
5 6.4 VC 0.5
6 10.0 VC 0.5
7 11.2 VC 0.5
8 11.2 VC 0.5
9 5.2 VC 0.5
10 7.0 VC 0.5
11 16.5 VC 1.0
12 16.5 VC 1.0
13 15.2 VC 1.0
14 17.3 VC 1.0
15 22.5 VC 1.0
16 17.3 VC 1.0
17 13.6 VC 1.0
18 14.5 VC 1.0
19 18.8 VC 1.0
20 15.5 VC 1.0
21 23.6 VC 2.0
22 18.5 VC 2.0
23 33.9 VC 2.0
24 25.5 VC 2.0
25 26.4 VC 2.0
26 32.5 VC 2.0
27 26.7 VC 2.0
28 21.5 VC 2.0
29 23.3 VC 2.0
30 29.5 VC 2.0
31 15.2 OJ 0.5
32 21.5 OJ 0.5
33 17.6 OJ 0.5
34 9.7 OJ 0.5
35 14.5 OJ 0.5
36 10.0 OJ 0.5
37 8.2 OJ 0.5
38 9.4 OJ 0.5
39 16.5 OJ 0.5
40 9.7 OJ 0.5
41 19.7 OJ 1.0
42 23.3 OJ 1.0
43 23.6 OJ 1.0
44 26.4 OJ 1.0
45 20.0 OJ 1.0
46 25.2 OJ 1.0
47 25.8 OJ 1.0
48 21.2 OJ 1.0
49 14.5 OJ 1.0
50 27.3 OJ 1.0
51 25.5 OJ 2.0
52 26.4 OJ 2.0
53 22.4 OJ 2.0
54 24.5 OJ 2.0
55 24.8 OJ 2.0
56 30.9 OJ 2.0
57 26.4 OJ 2.0
58 27.3 OJ 2.0
59 29.4 OJ 2.0
60 23.0 OJ 2.0
Per vedere la lunghezza media len (variabile di confronto) a seconda di due variabili di raggruppamento (supp + dose)
aggregate(len ~ supp + dose, data = ToothGrowth, mean) supp dose len
1 OJ 0.5 13.23
2 VC 0.5 7.98
3 OJ 1.0 22.70
4 VC 1.0 16.77
5 OJ 2.0 26.06
6 VC 2.0 26.14
Reshaping: Long to wide
Solitamente i dati sono organizzati o in wide format (i.e., una riga corrisponde a una singola unità statistica) o in long format (i.e., una riga corrisponde a una singola osservazione).
head(Indometh) # Long format Subject time conc
1 1 0.25 1.50
2 1 0.50 0.94
3 1 0.75 0.78
4 1 1.00 0.48
5 1 1.25 0.37
6 1 2.00 0.19
Per passare da long format a wide forma (e viceversa) si utilizza la funzione reshape()
Long to wide
# From long to wide
df.w <- reshape(Indometh, v.names="conc",
timevar="time",
idvar="Subject", direction="wide")
df.w Subject conc.0.25 conc.0.5 conc.0.75 conc.1 conc.1.25 conc.2 conc.3 conc.4
1 1 1.50 0.94 0.78 0.48 0.37 0.19 0.12 0.11
12 2 2.03 1.63 0.71 0.70 0.64 0.36 0.32 0.20
....
Reshaping: Wide to long
# From wide to long
df.l <- reshape(df.w, varying=list(2:12),
v.names="conc",
idvar="Subject", direction="long",
times=c(.25, .5, .75, 1, 1.25, 2, 3, 4, 5, 6, 8)) Subject time conc
1.0.25 1 0.25 1.50
2.0.25 2 0.25 2.03
3.0.25 3 0.25 2.72
....
df.l[order(df.l$Subject), ] # rirodina il data farme per soggetti Subject time conc
1.0.25 1 0.25 1.50
1.0.5 1 0.50 0.94
1.0.75 1 0.75 0.78
....
Your turn!
Crea un data frame con 10 osservazioni e le seguenti colonne:
id:character, ID dei soggettises:factor, livello socio economico con tre livelli,low,medium,high(3low, 5medium, 2high)income:numeric(e.g.,runif(10, min = 1110, max = 5430))
Applicare i seguenti filtri:
- Soggetti con
ses == high - Soggetti con
income> 2000
- Soggetti con
See solution
set.seed(999) # imposta un seed in modo che tutti si abbia gli stessi risultati
ids = paste("sbj", 1:10, sep = "_")
ses = factor(c(rep("low", 3), rep("medium", 5), rep("high", 2)),
levels = c("low", "medium", "high"))
income = runif(10, min = 1110, max = 5430)
income = income[order(income)]
dati = data.frame(ids, ses, income)
dati[dati$ses == "high", ] ids ses income
9 sbj_9 high 4508.746
10 sbj_10 high 4793.367
dati[dati$income > 2000, ] ids ses income
4 sbj_4 medium 2790.788
5 sbj_5 medium 2798.137
6 sbj_6 medium 3628.822
7 sbj_7 medium 3729.851
8 sbj_8 medium 3786.121
9 sbj_9 high 4508.746
10 sbj_10 high 4793.367
Data input and output
Leggere file esterni
File di testo ASCII o spread sheet possono essere imporati con la funzione read.table()
data = read.table("C:/RcouRse/file.txt", header = TRUE)Argomenti utili:
header: Se la rpima riga è occupata dai nomi delle variabili, va messoTRUEsep: separatore delle colonne (e.g., virgola,\t)dec: Se i deciminali sono nel formato1.2oppure1,2?
data = read.csv("C:/RcouRse/file.csv",
header = TRUE, sep = ";",
dec = ",")In alternativa alla funzione read.csv(), si può utilizzare la funzione read.csv2(), dove il default per il separatore di colonna è “;”. Per vedere se i dati sono stati importati correttamente:
data = read.csv("data/benessere.csv")
head(data) # mostra le prime 6 righe del dataset benessere stipendio genere
1 5 1461.0983 m
2 7 1132.3637 f
3 7 1675.9004 m
4 2 328.9587 f
5 6 1370.0146 m
6 5 954.3540 f
I dati sono importati correttamente! Si hanno le variabili nelle rispettive colonne.
Se i dati non vengono importati correttamente, si ha una situazione del genere:
data = read.csv2("data/benessere.csv")
head(data) # mostra le prime 6 righe del dataset benessere.stipendio.genere
1 5,1461.09828023079,m
2 7,1132.36368361099,f
3 7,1675.90040479853,m
4 2,328.958701913838,f
5 6,1370.01460952768,m
6 5,954.354030915469,f
Dove tutte le variabili vengono “schiacciate” in un’unica colonna
Da SPSS (file con estensione .sav):
install.packages("foreign")
library(foreign)
data = read.spss("data.sav", to.data.frame = TRUE)Combinare data frame diversi
Se hanno lo stesso numero di colonne o lo stesso numero di righe:
all_data = rbind(data, data1, data2) # stesso numero di colonne
all_data = cbind(data, data1, data2) # stesso numero di righeSe hanno numeri di righe/colonne diverse ma condividono almeno una carattertistica contenuta in una colonna (e.g., ID):
all_data = merge(data1, data2,
by = "ID")Se ci sono IDs differenti nei due dataset, la funzione aggiunge automaticamente delle righe, mettendo NA nelle colonne del dataframe in cui l’osservazione non era presente
all_data contiene tutte le colonne che erano presenti in data1 e data2.
Esportare i dati
Per esportare i dati in formato txt o csv:
write.table(data, # il dataset che si vuole salvare
file = "data/mydata.txt", # directory/nome_del_file.estensione
header = TRUE, # La prima riga contiene i nomi delle variabili?
sep = "\t", # separatore di colonna
....) # altri argomenti (leggere la documentazione)Si può esportare direttamente tutto l’ambiente:
save(dat, file = "exp1_data.rda") # Salva solo un oggetto specifico
save(file = "the_earth.rda") # salva tutto l'ambiente
load("the_earth.rda") # importa l'mabiente salvatoBasics of Programming
Bisogna essere pronti a sbagliare, TANTO
Coding is hard art
L’ideale è suddividere il problema in sotto-problemi
Da tenere a mente: You’re not alone! \(\rightarrow\) stackoverflow (o Google o CHATGPT) sono degli ottimi aiuti
ifelse()
Esecuzione condizionata:
ifelse(test, if true, if false)
ifelse(weight > 7, "big boy", "small boy")[1] "small boy" "small boy" "small boy" "big boy" "big boy" "big boy"
Pros:
è davvero facile da usare
Si possono concatenare diversi
ifelse()
Cons:
- Funziona bene finché la condizione da testare è semplice
if () {} else {}
Nel caso si abbiano condizioni più complesse
if (test_1) {
command_1
} else {
command_2
}if () {} else {}
Si possono mettere più condizioni all’interno dello stesso ciclo
if (test_1) {
command_1
} else if (test_2) {
command_2
} else {
command_3
}test_1 (e test_2) devono risultare in TRUE o FALSE, altrimenti non funziona. Se ci sono valori particolari tipo i missing, codifictai come NA, va specificato
if(!is.na(x)) y <- x^2 else stop("x is missing")Loops
for() e while()
Ripete un comando per un numero specifico di iterazioni
# Don't do this at home
x <- rnorm(10)
y <- numeric(10) # crea un contenitore vuoto per mettere i risultati
for(i in seq_along(x)) { # itera tante volte quanti sono gli elementi inx
y[i] <- x[i] - mean(x)
}In realtà quel codice poteva essere scritto in maniera molto più semplice:
y = x - mean(x)Sempre meglio evitare i loop
I loop possono essere evitati attraverso l’useo della funzione apply()!
apply(X, margin, fun)
X: l’elemento su cui vi vuole ripetere la funzione
margin: può essere 1 (la funzione viene ripetuta attraverso le colonne, si ottengono i valori marginali di riga) oppure 2 (la funzione viene ripetuta attraverso le righe, si ottengono i valori marginali di colonna)
X <- matrix(rnorm(20),
nrow = 5, ncol = 4)
apply(X, 2, max) # si ottiene ik valore massimo per ogni colonna (vettore di lunghezza 4)[1] -0.5660237 1.3254637 0.9013448 0.6430443
apply(X, 1, max) # si ottiene ik valore massimo per ogni riga (vettore di lunghezza 5)[1] 1.3254637 0.6430443 0.9387494 0.9013448 0.9576504
Alternativa (sconsigliata) utilizzando un ciclo for()
y = NULL
for (i in 1:ncol(X)) {
y[i] = max(X[, i])
}
y[1] -0.5660237 1.3254637 0.9013448 0.6430443
Importare i dati
Import csv
Attraverso la funzione read.csv()
data = read.csv("data/benessere.csv", # directory/nome_file.csv
header = TRUE, # prima riga occupata dai nomi delle variabili
sep =",", # separatore di colonne
dec = ".") # separatore di decimali
head(data) benessere stipendio genere
1 5 1461.0983 m
2 7 1132.3637 f
3 7 1675.9004 m
4 2 328.9587 f
5 6 1370.0146 m
6 5 954.3540 f
Calcolo dei punteggi somma
L’assunto è che il data set sia in wide format, ovvero che ogni riga corrisponda a una unità statistica
- Somma attraverso le colonne \(\rightarrow\) Punteggi somma marginali di riga
rowSums()(rowMeans()se si vuole calcolare la media)
- Somma attraverso le righe \(\rightarrow\) Punteggi somma marginali di colonna
colSums()(colMeans()se si vuole calcolare la media)
Dati del benessere
# va installato il pacchetto readxl attraverso il codice install.packages("readxl")
# da far correre solo una volta in console
library(readxl)
benessere = read_xlsx("data/datiBenessere.xlsx")
head(benessere)# A tibble: 6 × 19
ID età genere frat item1 item2 item3 item4 item5 au1 au2 au3 au4
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 16 1 0 1 2 4 3 4 5 4 5 2
2 2 21 1 1 2 2 3 4 3 5 4 5 2
3 3 28 2 4 2 3 5 1 2 4 4 4 4
4 4 15 2 2 3 4 2 2 2 4 5 5 3
5 5 23 1 3 4 5 1 5 2 3 2 3 2
6 6 31 1 2 2 3 2 1 2 5 1 5 1
# ℹ 6 more variables: au5 <dbl>, au6 <dbl>, au7 <dbl>, au8 <dbl>, au9 <dbl>,
# au10 <dbl>
Se si volessero calcolare i punteggi marginali di riga, si sarebbe tentati di fare:
rowSums(benessere) [1] 65 79 88 74 75 83 78 79 85 87 80 88 83 84 92 103 88 101
[19] 79 83 112 105 98 86 94 94 120 108 98 100 107 111 113 111 112 104
[37] 100 108 107 126 126 116 116 110 117 117 106 111 122 123 129 146 134 118
[55] 121 120 121 148 141 129 130 128 132 148 145 152 136 137 138 145 135 136
[73] 140 140 143 151 148 158 156 143 149 157 165 162 159 170 163 167 163 144
[91] 149 168 173 176 162 171 174 169 176 172 175 175 172 182 191 186 175 187
[109] 178 190 189 186 184 174 187 189 190 187 199 192 191 194 199 188 206 198
[127] 207 214 208 213 214 205 205 207 227 212 213 208 209 209 216 211 211 216
[145] 208 216 215 219 222 210
Il problema è che ha poco senso fare una cosa del genere! Nei punteggi somma sono inclusi anche le età, gli id, il genere eccetera
Condizioni basate sulle etichette delle variabili
Deve essere presente una regular expression, una sorta di radice comune delle etichette del dataframe:
colnames(benessere)[1] "ID" "età" "genere" "frat" "item1" "item2"
"item3" "item4"
[9] "item5" "au1" "au2" "au3" "au4" "au5" "au6"
"au7"
[17] "au8" "au9" "au10"
I nomi delle variabili che contengono la stringa item \(\rightarrow\) sono gli item del benessere
I nomi delle variabili che contengono la stringa au \(\rightarrow\) sono gli item che valutano l’autostima
grep()e grepl(): sono le funzioni che permettono di filtrare i dati a seconda delle regular expression (regex)
grep("regex", vector)
"regex": regular expression che si vuole trovare nel vettore
vector: vettore entro cui si vuole trovare la regex desiderata
La funzione grepl("regex", vector) funziona in modo analogo
Dall’esempio:
# vettore con i nomi delle variabili del dataset
(my_vector = colnames(benessere)) [1] "ID" "età" "genere" "frat" "item1" "item2" "item3" "item4"
[9] "item5" "au1" "au2" "au3" "au4" "au5" "au6" "au7"
[17] "au8" "au9" "au10"
grep()
# ottiene le posizioni delle colonne che hanno la stringa au
grep("au", my_vector) [1] 10 11 12 13 14 15 16 17 18 19
grepl()
# testa quali colonne contengono la stringa au
grepl("au", my_vector) [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE
[13] TRUE TRUE TRUE TRUE TRUE TRUE TRUE
Per calcolare i punteggi somma per i soggetti (riportati sulle righe), va usata la funzione rowSums() condizionata su specifiche colonne:
rowSums(benessere[, grep("item",
colnames(benessere))]) [1] 14 14 13 13 17 10 16 16 9 13 14 16 17 12 9 20 14 15 13 10 17 16 18 11 14
[26] 17 20 13 13 18 12 11 14 17 14 17 11 14 11 14 18 9 11 11 16 18 12 12 18 14
[51] 19 19 13 9 13 10 11 18 17 17 18 12 12 16 14 16 15 17 18 12 11 11 10 11 16
[76] 19 10 9 12 12 14 14 20 13 12 20 14 20 16 11 10 17 19 26 15 12 20 16 16 14
[101] 12 11 11 16 18 15 10 16 11 18 17 9 11 13 16 19 13 11 15 14 21 22 14 9 16
[126] 13 16 19 13 14 16 12 11 18 18 17 12 12 14 14 18 14 17 15 10 12 15 19 18 9
Per assegnare i punteggi somma a una nuova variabile all’interno del dataframe
benessere$score_ben = rowSums(benessere[, grep("item", colnames(benessere))])summary()
Permette di ottenere delle prime statistiche descrittive di una variabile o di una dataframe
babies <- read.table("data/babies.tab")
summary(babies$peso) Min. 1st Qu. Median Mean 3rd Qu. Max.
5.411 7.809 9.429 9.970 11.268 17.343
data set
summary(babies) id genere peso altezza
Length:10 Length:10 Min. : 5.411 Min. :46.80
Class :character Class :character 1st Qu.: 7.809 1st Qu.:62.90
Mode :character Mode :character Median : 9.429 Median :78.73
Mean : 9.970 Mean :74.88
3rd Qu.:11.268 3rd Qu.:84.98
Max. :17.343 Max. :99.22
table()
Permette di ottenere delle tavole di frequenze per le variabili categoriali
table(babies$genere)
f m
6 4
Tabelle di frequenza a doppia entrata
benessere = read.csv("data/benessere.csv", header = T)
# viene creata una nuova variabile dicotomica di benessere basso (se sotto la media)
# o alto (se sopra la media)
benessere$new_benessere = with(benessere,
ifelse(benessere > mean(benessere),
"low",
"high"))
with(benessere, table(genere, new_benessere)) new_benessere
genere high low
f 32 22
m 28 18
table() e percentuali
Nel caso di una singola variabile, basta dividere le frequenze di ogni categoria per il numero totale di osservazioni e moltiplicare per 100
(table(babies$genere)/nrow(babies))*100
f m
60 40
Nel caso di tabelle a doppia entrata, vanno calcolati prima i valori marginali di riga
my_perc = table(benessere$genere, benessere$new_benessere)
# calcola i valori marinali di riga per il genere
(my_perc = cbind(my_perc, rowSums(my_perc))) high low
f 32 22 54
m 28 18 46
(my_perc[,1]/my_perc[,3])*100 # percentuale di maschi e femmine con benessere alto f m
59.25926 60.86957
(my_perc[,2]/my_perc[,3])*100 # percentuale di maschi e femmine con benessere basso f m
40.74074 39.13043
aggregate()
Aggrega una variabile (o più variabili) di confronto a seconda di una (o più) variabile di raggruppamento, permettendo di applicare delle funzioni sui singoli gruppi. Ad esempio, è utile per ottenere la media dei tempi di risposta in diverse condizioni sperimentali:
# una variabile di confronto y e una variabile di raggruppamento x
aggregate(y ~ x, data = data, FUN, ...)
# più variabili di confronto y1 e y2 e più variabile di confronto x1 e x2
aggregate(cbind(y1, y2) ~ x1 + x2, data = data, FUN, ...)Un esempio sui punteggi del benessere
benessere = read.csv("data/benessereScores.csv",
header = T, sep =",")
head(benessere) ID età genere frat item1 item2 item3 item4 item5 au1 au2 au3 au4 au5 au6 au7
1 1 16 1 0 1 2 4 3 4 5 4 5 2 3 3 1
2 2 21 1 1 2 2 3 4 3 5 4 5 2 4 4 5
3 3 28 2 4 2 3 5 1 2 4 4 4 4 4 4 4
4 4 15 2 2 3 4 2 2 2 4 5 5 3 5 4 2
5 5 23 1 3 4 5 1 5 2 3 2 3 2 4 3 1
6 6 31 1 2 2 3 2 1 2 5 1 5 1 4 5 1
au8 au9 au10 score_ben score_au
1 4 2 4 14 33
2 5 1 5 14 40
3 4 2 4 13 38
4 4 2 4 13 38
5 4 1 3 17 26
6 5 1 5 10 33
Si può calcolare la media del punteggio di autostima (variabile di confronto) a seconda del genere (variabile di raggruppamento a due livelli):
aggregate(score_au ~ genere, data = benessere, mean) genere score_au
1 1 33.08750
2 2 32.62857
Oppure si può calcolare la media sia del punteggio di benessere che di autostima contemporaneamente a seconda del genere
aggregate(cbind(score_ben, score_au) ~ genere,
data = benessere, mean) genere score_ben score_au
1 1 14.46250 33.08750
2 2 14.41429 32.62857
Your turn!
Utilizzano il data set “benessereScores.csv”
Ricodificare la variabile
fratin modo che diventi una variabile dicotomica a due livelli:- livello
siblingssefrat == 0 - livello
no_siblingssefrat != 0Suggerimento: funzioneifelse()
- livello
calcolare la media di
score_bena seconda della variabilesiblingscalcolare la media di
score_benescore_aua seconda della variabilesibilingsegender(assegnare il risultato alla variabilemean_dep)calcolare la deviazione standard di
score_benescore_aua seconda della variabilesibilingsegender(assegnare il risultato alla variabilesd_dep)Unire (con la funzione
merge())mean_depesd_depe assegnare il risultato alla variabiledescr
Quando si utilizza la funzione merge() i nomi delle variabili devono essere diversi
See solution
# ricodifica la variabile frat in modo che dica "no" quando la persona ha 0 fratelli
# e assegnala a una nuova colonna nel dataframe chiamata "siblings"
benessere$siblings = ifelse(benessere$frat == 0,
"no", "yes")
# calcola la media per score_ben e score_au in base a siblings e genere
mean_dep = aggregate(cbind(score_ben, score_au) ~ siblings + genere,
data = benessere, mean)
# rinomina le colonne in modo che abbiano l'etichetta "mean"
colnames(mean_dep)[3:4] = paste("mean", colnames(mean_dep)[3:4], sep = "_")
# calcola la deviazione standard per score_ben e score_au in base a siblings e genere
sd_dep = aggregate(cbind(score_ben, score_au) ~ siblings + genere,
data = benessere, sd)
# rinomina le colonne in modo che abbiano l'etichetta "sd"
colnames(sd_dep)[3:4] = paste("sd", colnames(sd_dep)[3:4], sep = "_")
# unisci i due dataframe
descr = merge(mean_dep, sd_dep)
descr siblings genere mean_score_ben mean_score_au sd_score_ben sd_score_au
1 no 1 13.90909 33.27273 3.250042 4.452734
2 no 2 14.51852 32.11111 3.309315 3.004270
3 yes 1 14.67241 33.01724 3.347625 3.743961
4 yes 2 14.34884 32.95349 3.228472 4.396717
tidyverse()
tidyverse() è un pacchetto specifico pensato per il data science che permette un utilizzo più agevole e forse inutitivo delle funzioni di base di R.
install.packages("tidyverse") # installa il pacchetto ( va fatto solo una volta)
library(tidyverse) # carica la libreria e la rende disponibileTutto il suo funzionamento si base sull’operatore %>% (Pipe)
Lo si ottiene con la combinazione di tasti shift + ctrl + M
La logica del suo funzionamento è molto semplice:
object %>% # oggetto su cui si vogliono svolgere le operazioni
grouping %>% # eventuali variabili di ragguppamento
function # la o le funzioni che si vogliono applicareStatistice descrittive con tidyverse
Per ottenere le statistiche descrittive (media e ds) del punteggio di benessere e autostima considerando le variabili di raggruppamento siblings e genere
benessere %>% # oggetto (dataframe) su cui si vuole lavorare
group_by(siblings, genere) %>% # variabili di raggruppamento
summarise(m_benessere = mean(score_ben), # funzioni che si vogliono applicare e variabili di confronto
sd_benessere = sd(score_ben),
m_au = mean(score_au),
sd_au = sd(score_au))# A tibble: 4 × 6
# Groups: siblings [2]
siblings genere m_benessere sd_benessere m_au sd_au
<chr> <int> <dbl> <dbl> <dbl> <dbl>
1 no 1 13.9 3.25 33.3 4.45
2 no 2 14.5 3.31 32.1 3.00
3 yes 1 14.7 3.35 33.0 3.74
4 yes 2 14.3 3.23 33.0 4.40
Your turn!
Calcolare minimo, massimo e mediana di
score_auescore_bencontidyverseImportare il dataset
babiese calcolare media e deviazione standard delle variabilipesoealtezzacontidyverse
See solution
benessere %>%
summarise(min_ben = min(score_ben),
max_ben = max(score_ben),
median_ben = median(score_ben),
min_au = min(score_au),
max_au = max(score_au),
median_au = median(score_au)) min_ben max_ben median_ben min_au max_au median_au
1 9 26 14 25 48 32
# importa il dataset babies
babies = read.table("data/babies.tab")
# calcola le statistiche descrittive
babies %>%
summarise(mean_weight = mean(peso),
sd_weight = sd(peso),
mean_height = mean(altezza),
sd_height = sd(altezza)) mean_weight sd_weight mean_height sd_height
1 9.969853 3.274042 74.87698 16.31357
Grafici
Si hanno a disposizione i grafici “tradizionali” per cui non è necessario scaricare ulteriori pacchetti e i grafici prodotti con ggplot, per cui è necessario scaricare il pacchetto ggplot2
Per entrambe le tipologie:
- Funzioni di alto livello \(\rightarrow\) Quelle che effettivamente producono il grafico
- funzioni di basso livello \(\rightarrow\) Quelle che lo rendono più carino
Grafici tradizionali
Funzioni di alto livello:
plot() # scatter plot, specialized plot methods
boxplot()
hist() # histogram
qqnorm() # quantile-quantile plot
barplot()
pie() # pie chart
pairs() # scatter plot matrix
persp() # 3d plot
contour() # contour plot
coplot() # conditional plot
interaction.plot()demo(graphics) produce un tour guidato di tutte le funzioanalità grafiche!
Low level functions
points() # add points
lines() # add lines
rect()
polygon()
abline() # add line with intercept a, slope b
arrows()
text() # add text in plotting region
mtext() # add text in margins region
axis() # customize axes
box() # box around plot
legend()Il layout dei plot è molto importante ed è costituito da due regioni principali:
- La regione del plot (quella che effettivamente contiene il plot)
- La regione dei margini (dove sono contenuti i margini e le etichette degli assi)
Ad esempio, per ottenere uno scatterplot:
x <- runif(50, 0, 2) # genera 50 valori casuali da una distribuzione uniforme
y <- runif(50, 0, 2) # genera 50 valori casuali da una distribuzione uniforme
# produce il plot
plot(x, y, # variabili sull'asse x e y, rispettivamente
main="Title", # titolo del grafico
sub="Subtitle", # sottotitolo
xlab="x-label", # etichetta asse x
ylab="y-label") # etichetta asse y
text(0.6, 0.6, "Text at (0.6, 0.6)") # aggiunge del testo al grafico
# aggiunge linee orizzontal e verticali
abline(h=.6, v=.6, lty=2) plot()
plot(x) # una variabile
plot(x, y) # scatter plot
plot(y ~ x) # scatter plot (codice alternativo)Si uò cambiare il colore degli elementi del grafico anche condizionando su specifiche variabili di raggruppamento:
with(benessere,
plot(score_au,
col = ifelse(genere == 1, "blue", "pink"), # condiziona il colore tale per cui se è maschio è blu
pch = ifelse(genere == 1, 3, 16))) # condiziona la forma del puntino
# inserisce la legenda
legend(x = 100, y = 48, # coordinate specifiche per la legenda
c("Male", "Female"), # etichette
pch = c(3, 16), # mappe dei puntini
col =c("blue", "pink"), # mappe del colore
cex = 2) # grandezza del testoEsempio di scatterplot: plot(x, y)
with(benessere,
plot(score_au, score_ben))Esempio di scatterplot: plot(y ~ x)
with(benessere,
plot(score_ben ~ score_au))Agli scatterplot si può aggiungere anche la linea che indica il modello di regressione
with(benessere,
plot(score_ben ~ score_au))
# aggiunge la linea
abline(lm(score_ben ~ score_au, data = benessere),# specifica il modello di regressione per le coordinate della linea
col = "blue",
lty = 3, # tipo di linea
lwd = 3) # spessore della lineaEsempio di scatterplot quando x è una variabile categorica: plot(y ~ x)
benessere$genere <- factor(ifelse(benessere$genere == 1,
"maschio", "femmina"))
plot(score_au ~ genere, data = benessere)equivale ad usare la funzione boxplot(y ~ x)
boxplot(score_au ~ genere, data = benessere)hist()
Serve per produrre l’istogramma delle frequenze per variabili continue
hist(benessere$score_au, # la variabile che si vuole plottare
breaks = 20) # il numero di binsSi può plottare la funzione di densità della variabile plottat in istogramma (solo per variabili conitnue)
Codice
hist(benessere$score_au,
density=20, # va specificato che si vuole aggiungere l'istogramma
breaks=20,
prob=TRUE, # va specificato che sull'asse delle y dovranno esserci valori di probabilità
col = "darkblue")
curve(dnorm(x, mean=mean(benessere$score_au), # si utilizza la funzione dnorm() per avere la densità
sd=sd(benessere$score_au)),
col="darkblue", lwd=4,
add=TRUE, # fa in modo che la funzione di densità venga aggiunta al grafico
yaxt="n")Modificare il layout
Si possono plottare più grafici in un’unica finesra modifcando il layout
par(mfrow=c(nrows, ncolumns)) # i pannelli vengono riempiti per riga
par(mfcol=c(nrows, ncolumns)) # i pannelli vengono riempiti per colonnaMulti plot: Righe
Codice
par(mfrow=c(1, 2)) # specifica che si vuole una riga e due colonne
hist(benessere$score_au,density=50, breaks=20, prob=TRUE,
main = "Score au")
curve(dnorm(x, mean=mean(benessere$score_au),
sd=sd(benessere$score_au)),
col="springgreen4", lwd=2, add=TRUE, yaxt="n")
hist(benessere$score_ben,density=50, breaks=20, prob=TRUE,
main = "Score benessere")
curve(dnorm(x, mean=mean(benessere$score_ben),
sd=sd(benessere$score_ben)),
col="royalblue", lwd=2, add=TRUE, yaxt="n")Multi plot: colonne
Codice
par(mfcol= c(2,2))
# primo pannello in alto a sinistra
hist(benessere$score_au,density=50, breaks=20, prob=TRUE,
main = "Score au")
curve(dnorm(x, mean=mean(benessere$score_au),
sd=sd(benessere$score_au)),
col="springgreen4", lwd=2, add=TRUE, yaxt="n")
# secondo pannello in basso a sinistra
hist(benessere$score_ben,density=50, breaks=20, prob=TRUE,
main = "Score benessere")
curve(dnorm(x, mean=mean(benessere$score_ben),
sd=sd(benessere$score_ben)),
col="royalblue", lwd=2, add=TRUE, yaxt="n")
# terzo pannello in alto a destra
with(benessere,
plot(score_au, score_ben, frame = FALSE))
# quarto pannello in basso a adestra
with(benessere,
plot(score_au, score_ben, frame = FALSE))
abline(lm(score_ben ~ score_au, data = benessere), col = "blue", lwd = 2)barplot()
Serve per ottenere il plot delle frequeunze per le categorie di una variabile categoriale
freq_item1 = table(benessere$item1)
barplot(freq_item1,
main = "Item 1 - Frequencies")barplot(): Frequenze relative
è necessario calcolare prima le proporzioni sul totale per ogni categoria di risposta
Codice
perc_item1 = freq_item1/sum(freq_item1)
barplot(perc_item1,
ylim = c(0, 1), # setta l'asse delle y ad essere compreso tra 0 e 1
main = "Item 1 - Realtive frequencies")Nel caso si voglia vedere la frequenza relativa in vase a una variabile di raggruppamento, vanno prima calcolate le frequenze relative per ognuno dei gruppi considerati
Codice
# tabella a doppia entrata delle frequeunze di risposta all'item 1 in base al genere
perc_item1_gender = table(benessere$genere, benessere$item1)
# calcola le frequeunze relative sul totale delle risposte per il livello 1
perc_item1_gender[1,] = perc_item1_gender[1,]/table(benessere$item1)
# calcola le frequeunze relative sul totale delle risposte per il livello 2
perc_item1_gender[2,] = perc_item1_gender[2,]/table(benessere$item1)
# produce il plot, automaticamente suddiviso nei due gruppi
barplot(perc_item1_gender, ylim=c(0,1),
legend = rownames(perc_item1_gender))
abline(h = .5, lty = 2, col = "red")Multi plot: Esempio su tutti gli item
Si può ottenere un grafico come quello illustrato in precedenza per tutti gli item, ripetendo il procedimento per oguno degli item attraverso l’applicazione di un ciclo for
Codice
# isola le etichette degli item di interesse
item_ben = benessere[, grep("item", colnames(benessere))]
# predispone il layout per ospitare tutti i grafici
par(mfrow = c(2, round(ncol(item_ben)/2 + 0.2)))
temp = NULL # crea una variabile temporanea dove mettere le tabelle delle frequeunze necessarie per il plot
for (i in 1:ncol(item_ben)) { # il ciclo itera per ognuno degli item
temp = table(benessere$genere, item_ben[,i]) # calcola le frequeunze per ogni item a sceonda del genere
for (j in 1:nrow(temp)) {
temp[j,] = temp[j,]/table(item_ben[,i]) # calcola le frequeunze percentuali
}
barplot(temp, ylim=c(0,1), legend = rownames(temp), # produce il plot
main = colnames(item_ben)[i])
abline(h = .5, lty = 2, col = "red")
}interaction.plot()
è possibile anche ottenere un grafico che mostra la relazione tra la variabile \(x\) e \(y\) dato l’effetto della variabile \(z\)
interaction.plot(x, z, y)
Riprendendo l’esempio dei bambini, è possibile che la relazione tra peso e altezza vari in base al genere?
Codice
babies = read.table("data/babies.tab")
# plot base relazione peso e altezza
plot(altezza ~ peso, data = babies,
col = ifelse(genere == "f", "pink", "blue"),
pch = 16, main = "Weight - height",
cex = 3)Codice
babies$cat_weight = with(babies,
ifelse(
peso <= quantile(babies$peso)[2],
"light", ifelse(peso > quantile(babies$peso)[2] &
peso > quantile(babies$peso)[4], "medium",
"heavy")))
babies$cat_weight = factor(babies$cat_weight,
levels = c("light",
"medium",
"heavy"))
babies id genere peso altezza cat_weight
1 baby1 f 7.424646 62.07722 light
2 baby2 m 7.442727 58.18877 light
3 baby3 f 9.512598 84.52737 heavy
4 baby4 f 11.306349 85.13573 medium
....
with(babies,
interaction.plot(cat_weight, genere, altezza))ggplot2
ggplot2 (Grammar of Graphics plot, Wickman, 2016) è uno dei pacchetti migliori per ottenere dei grafici d’effetto con (relativamente) poco sforzo
install.packages("ggplot2") # installa il pacchetto (da far correre solo la priuma volta)
library(ggplot2) # carica il pacchetto La funzione ggplot() è las base del funzionamento del pacchetto. Il suo funzionamento prevede fondamentalmente 3 elementi principali:
ggplot(data, # il dataframe da cui si vogliono prendere le variabili da plottare
aes(x = x, y = y)) + # aes sta per aestetichs, definisce le variabili da plottare sulla x, y, i colori, le forme eccetera
geom_graph.type() # definisce il tipo di grafico specifico che si vuole ottenere (alcuni) Argomenti di aes():
x= variabile asse xy= variabile asse ycol= variabile per distinguere i colori delle linee/contorni (factor o charcater)fill= variabile per distinguere il riempimento delle figure (factor o charcater)shape= variabile per distinguere la forma delle figure (factor o charcater)size= variabile per distinguere la grandezza delle figure (numeric o integer)- ….
Alcuni tipi di grafici:
geom_point()= plotta punti, se vengono date le variabili x e y è uno scatterplotgeom_line()= plotta le lineegeom_boxplot()= per ottenere i boxplotgeom_violin()= per ottenere i violinplotgeom_hist(): istogrammageom_bar(): barplot- ….
Un esempio di scatterplot basato sul dataset rock (è un dataset interno a R)
ggplot(rock,
aes(y=peri,
x=shape,
color =shape, # il colore viene definito sulla base della forma delle rocce
size = peri)) + # la grandezza dei punti è definita dalla lunghezza del perimentro
geom_point() + # produce lo scatterplot
theme_bw() + # cambia lo sfondo del grafico
theme(legend.position = "none") # toglie la legendaUn altro esempio, dove si crea un data frame dove si hanno due a disposizione i tempi medi di risposta di due gruppi (variabile gruppo, livelli a1 e a2) in tre wave temporali (variabile tempo, tre livelli, b1, b2,b3`):
dat <- read.table(header=TRUE, text="
gruppo tempo rt
a1 b1 825
a1 b2 792
a1 b3 840
a2 b1 997
a2 b2 902
a2 b3 786
")
# le variabili gruppo e tempo vengono forzate ad essere factor
dat[,1:2] = lapply(dat[,1:2], as.factor)A livello grafico, si può investigare se la relazione tra i tempi di risposta e la wave temporale cambia in base all’appartenenza di gruppo (interaction plot):
Codice
ggplot(dat,
aes(x = tempo,
y = rt,
group = gruppo,
color= gruppo,
shape = gruppo,
linetype = gruppo)) +
geom_point(size = 5) +
geom_line(size=1) + theme_classic() +
ylab("RT") + scale_linetype_manual("Task", values =c(3,4),
labels = c("A1", "A2")) +
scale_x_discrete(labels = c("B1", "B2", "B3")) Scatter plot con ggplot2
Codice
states = data.frame(state.x77)
ggplot(states, # raw data
aes(x = Illiteracy, y = Murder)) +
geom_point(size =3, pch=3) + theme_classic() + theme(legend.position = "none")Per aggiungere la linea del modello di regressione con tanto di banda che indica l’errore standard della stima, si utilizza la funzione geom_smooth() (si possono specificare vari modelli oltre al modello lineare).
Codice
ggplot(states, # raw data
aes(x = Illiteracy, y = Murder)) +
geom_point(size =3, pch=10) + theme_classic() +
geom_smooth(method="lm", color="red", aes(fill="red")) Multi plot
Per ottenere i multiplot combinando plot di analisi differenti (e.g, grafico delle statistiche descrittive e grafico dei risultati), è necessario installare il pacchetto gridExtra
install.packages("gridExtra") # installa il pacchetto, da usare solo una volta
library(gridExtra) # carica il pacchetto e lo rende usabileI multi plot si ottengono con la funzione grid.arrange()
Codice
murder_raw = ggplot(states, # scatterplot
aes(x = Illiteracy, y = Murder)) + geom_point(size =3, pch=3) + theme_classic() + theme(legend.position = "none")
murder_lm = ggplot(states, # scatterplot con modello di regerssione
aes(x = Illiteracy, y = Murder)) + geom_point(size =3, pch=3) + theme_classic() +
geom_smooth(method="lm", color="red", aes(fill="red")) + theme(legend.position = "none")
grid.arrange(murder_raw, # primo plot
murder_lm, # secondo plot
nrow=1) # numero di righeMulti plot II
states = data.frame(state.x77, state.name = state.name,
state.region = state.region)Nel caso in cui si voglia vedere la relazione tra due variabili nei vari livelli definiti da una terza variabile presente sempre nello stesso dataframe, si può usare la funzione facet_wrap() inclusa nel pacchetto ggplot2.
Ad esempio, prendendo come riferimento il dataset disponibile all’interno di R state.x77 si vuole vedere se la relazione tra illiteracy e numero di omicidi varia al variare della macro area geografica considerata (Northeast, South, North Central, West)
Codice
# si crea il dataframe corretto a partire da state.77
states = data.frame(state.x77, state.name = state.name,
state.region = state.region)
ggplot(states,
aes(x = Illiteracy, y = Murder )) + geom_point() +
facet_wrap(~state.region) # imposta automatiacamente di suddividere i grafici a seconda della state regionboxplot() e violinplot()
Per ottenere i boxplot e i violin plot, i dati devono essere riorganizzati in formato long, come nell’esempio qui sotto:
id condition mean_time
1 sbj1 A 3.860907
2 sbj1 B 2.131674
3 sbj2 A 3.423382
4 sbj2 B 3.919431
5 sbj3 A 4.241790
6 sbj3 B 4.002556
Prendiamo ad esempio i dati del benessere, facendo esclusivamente riferimento ai punteggi di scala dell’autostima e del benessere:
# seleziona solo le variabili di interesse
small = benessere[, c("ID", "score_au", "score_ben")]
# trasforma il dataframe in fromato long
score_long = reshape(small,
idvar = "ID",
times =names(small)[-1],
timevar = "score", v.names = "value",
varying = list(names(small)[-1]),
direction = "long")
head(score_long) ID score value
1.score_au 1 score_au 33
2.score_au 2 score_au 40
3.score_au 3 score_au 38
4.score_au 4 score_au 38
5.score_au 5 score_au 26
6.score_au 6 score_au 33
Boxplot:
Codice
ggplot(score_long,
aes(x = score, y = value,
fill = score)) +
geom_boxplot()Violinplot:
Codice
ggplot(score_long,
aes(x = score, y = value,
fill = score)) +
geom_violin() score_long = merge(score_long, benessere[, c("ID", "genere")])
ggplot(score_long,
aes(x = score, y = value,
fill = genere)) + geom_violin(trim = FALSE) Grafici a barre ed istogrammi
geom_hist(): Istograma per variabili continue
geom_bar(): Grafico a barre per variabili categoriali
Argomenti
geom_bar(stat = "count"): conta automaticamente le frequeunze per ogni categoria, per cui non c’è bisogno di specificare una variabile y inaes()geom_bar(stat = "identity"): associa un valore presente nel dataframe alla variabile y che deve essere specificata in aes()`
geom_bar(stat = "count"):
Ad esempio, per ottenere le frequenze di risposta di un item:
ggplot(benessere,
aes(x = item5)) + geom_bar(stat = "count") # conta automaticamente le frequeunzegeom_bar(stat = "identity")
Per lo stesso item, si vuole plottare la proporzione di risposte epr ogni categoria \(\rightarrow\) si vuole specificare il valore delll’asse y
item_5 = data.frame(table(benessere$item5)/nrow(benessere))
ggplot(item_5,
aes(x = Var1,
y = Freq)) + # viene specificata la variabile da plottare sulla y
geom_bar(stat = "identity") # va specificato che si ha una variabile yUn esempio completo
Si possono mettere insieme le funzioni da tidyverse per ottenere più facilmente e velocemente grafici informativi sulle proprie variabili.
Ad esempio mettiamo di volere vedere la proporzione di risposte in ogni categoria di ogni item per la misurazione del benessere:
Codice
# seleziona solo le colonne con gli id dei soggetti e gli item di benessere
new_item = benessere[, c(grep("ID", colnames(benessere)),
grep("item", colnames(benessere)))]
new_item = pivot_longer(new_item, # funzione di tidyverse per girare i dati
cols = !ID,
names_to = "item",
values_to = "value")Vanno calcolate le proporzioni per ogni categoria per ogni item
Codice
proporzione = new_item %>%
group_by(item, value) %>%
summarise(prop = n()/nrow(benessere))
proporzione# A tibble: 26 × 3
# Groups: item [5]
item value prop
<chr> <int> <dbl>
1 item1 1 0.193
2 item1 2 0.293
3 item1 3 0.227
4 item1 4 0.133
5 item1 5 0.153
6 item2 1 0.187
7 item2 2 0.313
8 item2 3 0.187
9 item2 4 0.18
10 item2 5 0.133
# ℹ 16 more rows
e infine si può otttenere il grafico:
ggplot(proporzione,
aes(x = value, y = prop)) +
geom_bar(stat = "identity") +
facet_wrap(~item) Esportare i grafici
Automaticamente:
pdf("nome_grafico.pdf")
png("nome_grafico.png")
tiff("nome_grafico.tiff")
jpeg("nome_grafico.jpeg")
bmp("nome_grafico.bmp")Ma poi bisogna ricordarsi di chiudere il device grafico con:
dev.off()Ad esempio, se si vuole salvare il violin plot ottenuto dai punteggi di benessere e autostima:
pdf("il_mio_violin.pdf")
ggplot(score_long,
aes(x = score, y = value,
fill = score)) + geom_violin(trim = FALSE)
dev.off()