Questo documento è stato redatto seguendo il libro R for Data Science.
Ci sono diversi pacchetti al di fuori del tidyverse e in questo libro ci si occupa di:
Quando si cerca aiuto si pensi sempre a fare un reprex con il codice interessanto e soprattutto un dput con le variabili interessate:
dput(mtcars)## structure(list(mpg = c(21, 21, 22.8, 21.4, 18.7, 18.1, 14.3,
## 24.4, 22.8, 19.2, 17.8, 16.4, 17.3, 15.2, 10.4, 10.4, 14.7, 32.4,
## 30.4, 33.9, 21.5, 15.5, 15.2, 13.3, 19.2, 27.3, 26, 30.4, 15.8,
## 19.7, 15, 21.4), cyl = c(6, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8,
## 8, 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 8, 6, 8, 4),
## disp = c(160, 160, 108, 258, 360, 225, 360, 146.7, 140.8,
## 167.6, 167.6, 275.8, 275.8, 275.8, 472, 460, 440, 78.7, 75.7,
## 71.1, 120.1, 318, 304, 350, 400, 79, 120.3, 95.1, 351, 145,
## 301, 121), hp = c(110, 110, 93, 110, 175, 105, 245, 62, 95,
## 123, 123, 180, 180, 180, 205, 215, 230, 66, 52, 65, 97, 150,
## 150, 245, 175, 66, 91, 113, 264, 175, 335, 109), drat = c(3.9,
## 3.9, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92, 3.92, 3.92,
## 3.07, 3.07, 3.07, 2.93, 3, 3.23, 4.08, 4.93, 4.22, 3.7, 2.76,
## 3.15, 3.73, 3.08, 4.08, 4.43, 3.77, 4.22, 3.62, 3.54, 4.11
## ), wt = c(2.62, 2.875, 2.32, 3.215, 3.44, 3.46, 3.57, 3.19,
## 3.15, 3.44, 3.44, 4.07, 3.73, 3.78, 5.25, 5.424, 5.345, 2.2,
## 1.615, 1.835, 2.465, 3.52, 3.435, 3.84, 3.845, 1.935, 2.14,
## 1.513, 3.17, 2.77, 3.57, 2.78), qsec = c(16.46, 17.02, 18.61,
## 19.44, 17.02, 20.22, 15.84, 20, 22.9, 18.3, 18.9, 17.4, 17.6,
## 18, 17.98, 17.82, 17.42, 19.47, 18.52, 19.9, 20.01, 16.87,
## 17.3, 15.41, 17.05, 18.9, 16.7, 16.9, 14.5, 15.5, 14.6, 18.6
## ), vs = c(0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0,
## 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1), am = c(1,
## 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
## 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), gear = c(4, 4, 4, 3,
## 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3,
## 3, 3, 4, 5, 5, 5, 5, 5, 4), carb = c(4, 4, 1, 1, 2, 1, 4,
## 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1, 2, 1, 1, 2, 2, 4, 2, 1,
## 2, 2, 4, 6, 8, 2)), row.names = c("Mazda RX4", "Mazda RX4 Wag",
## "Datsun 710", "Hornet 4 Drive", "Hornet Sportabout", "Valiant",
## "Duster 360", "Merc 240D", "Merc 230", "Merc 280", "Merc 280C",
## "Merc 450SE", "Merc 450SL", "Merc 450SLC", "Cadillac Fleetwood",
## "Lincoln Continental", "Chrysler Imperial", "Fiat 128", "Honda Civic",
## "Toyota Corolla", "Toyota Corona", "Dodge Challenger", "AMC Javelin",
## "Camaro Z28", "Pontiac Firebird", "Fiat X1-9", "Porsche 914-2",
## "Lotus Europa", "Ford Pantera L", "Ferrari Dino", "Maserati Bora",
## "Volvo 142E"), class = "data.frame")
Attenzione ci sono un sacco di addins per ggplot in questa pagina web
Prima di tutto facciamo qualche grafico con ggplot. Ci
chiediamo con il data set di mtcars se le auto con un
motore più grande consumano più benzina. Tutta via non vogliamo un si o
no bensì che tipo di relazione c’è al crescere del motore sul consumo di
benzina.
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
labs(x="Car engine in liters", y="Miles x gallon in high")Con il pacchetto plotly posso fare dei grafici
interattivi molto interessanti:
baseDati <- mtcars %>% group_by(cyl) %>% summarise(horsePower = mean(hp))
plotly::ggplotly(
baseDati %>% ggplot(
aes(x=cyl,y=horsePower))+
geom_col(fill="#ba0c2f"))The greatest value of a picture is when it forces us to notice what we never expected to see. — John Tukey
Supponiamo un grafico che metta in relazione il consumo con il motore dell’auto come quello di sopra. Si vedono dei punti nell’angolo a destra in alto che escono fuori dalla linearità. Si può analizzare cosa siano aggiungendo una terza variabile:
ggplot(data = mpg) +
geom_point(
mapping = aes(
x = displ,
y = hwy,
#color = class, # Posso usare i colori
#size = class # oppure le dimensioni dei punti
#alpha = class # oppure la tonalità
shape = class # oppure una forma (ce ne sono solo 6!!)
)
)+
labs(x="Litri motore",y="Consumo autostradale")+
scale_color_paletteer_d("RColorBrewer::RdGy")## Warning: The shape palette can deal with a maximum of 6 discrete values because
## more than 6 becomes difficult to discriminate; you have 7. Consider
## specifying shapes manually if you must have them.
## Warning: Removed 62 rows containing missing values (geom_point).
Un modo differente per aggiungere una terza variabile, oltre ai colori, le dimensioni, gli alfa e le forme, potrebbe essere il facet. Sono sottografici che mi servono per mostrare un subset per ogni grafico.
Una variabile:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_wrap(~ class, nrow = 2)Due variabili:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_grid( drv ~ cyl)Si possono fare un facet o per riga o per colonna in questo modo:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_grid(drv ~ .)ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy)) +
facet_grid(. ~ cyl)Non tutti i geom accettano gli stessi argomenti. Per esempio gli shape funzionano con i punti ma non con le linee. D’altro canto puoi usare, con le linee, il linetype.
geom_smooth serve per fare una banda di accettazione intorno alla linea principale.
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv))## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
e poi
library(ggpubr)
#> Loading required package: ggplot2
#> Loading required package: magrittr
# Create some data format
# :::::::::::::::::::::::::::::::::::::::::::::::::::
set.seed(1234)
wdata = data.frame(
sex = factor(rep(c("F", "M"), each=200)),
weight = c(rnorm(200, 55), rnorm(200, 58)))
head(wdata, 4)## sex weight
## 1 F 53.79293
## 2 F 55.27743
## 3 F 56.08444
## 4 F 52.65430
#> sex weight
#> 1 F 53.79293
#> 2 F 55.27743
#> 3 F 56.08444
#> 4 F 52.65430
# Density plot with mean lines and marginal rug
# :::::::::::::::::::::::::::::::::::::::::::::::::::
# Change outline and fill colors by groups ("sex")
# Use custom palette
ggdensity(wdata, x = "weight",
add = "mean", rug = TRUE,
color = "sex", fill = "sex",
palette = c("#00AFBB", "#E7B800"))data(diamonds)
ggpie3D(data = diamonds, group_key = "cut", count_type = "full", tilt_degrees = -10, label_size=2) +
ggtitle("tilt_degrees = -10") +
theme(plot.title = element_text(hjust = 0.5))Prendiamo in considerazione la base dati diamonds che comprende 53940.
Se noi facciamo un grafico come il seguente:
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut))vediamo che alcuni grafici come i bar generano una variabile count mentre altri come scatterplot vogliono sia una variabile x sia una y.
I grafici che non richiedono esplicitamente una y fanno implicitamente un conteggio. Per questo motivo posso modificare il grafico di sopra in questo modo:
ggplot(data = diamonds) +
stat_count(mapping = aes(x = cut))Ci sono tre ragioni per le quali posso fare un override della funzione di default:
stat_summary che fa il summarise il valore y per ciascun
x.Caso 1:
demo <- tribble(
~cut, ~freq,
"Fair", 1610,
"Good", 4906,
"Very Good", 12082,
"Premium", 13791,
"Ideal", 21551
)
ggplot(data = demo) +
geom_bar(mapping = aes(x = cut, y = freq), stat = "identity")Caso 2:
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, y = stat(prop), group = 1))Caso 3:
ggplot(data = diamonds) +
stat_summary(
mapping = aes(x = cut, y = depth),
fun.min = min,
fun.max = max,
fun = median
)Ci sono molte altre cose che posso fare con il bar chart. Per esempio puoi colorare o riempire le barre.
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, colour = cut))ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = cut))ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity))Se non si vuole un chart impilato classico si possono usare una di queste tre opzioni:
identity
dodge
fill
Identity: posiziona ogni oggetto esattamente dove cade
ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) +
geom_bar(alpha = 1/5, position = "identity")ggplot(data = diamonds, mapping = aes(x = cut, colour = clarity)) +
geom_bar(fill = NA, position = "identity")fill: funziona come l’impilata ma crea barre tutte della medesima altezza
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity), position = "fill")dodge: crea più barre una vicina all’altra
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut, fill = clarity), position = "dodge")C’è un altro aggiustamento utile non tanto per i barchart bensì per gli scatter plot che si chiama jitter e serve per ridurre il overplotting. Questo fenomeno viene fuori quando ho tanti punti nella stessa posizione. In questo caso, jitter aggiunge pochissimo rumore sui vari punti in modo da rendere maggiormente visibile la situazione. Si vedano i due grafici sottostanti:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy), position = "jitter")Per saperne di più: ?position_dodge,
?position_fill, ?position_identity,
?position_jitter, and ?position_stack
E’ la parte più difficile ma qui introduciamo:
coord_flip: per invertire x con y
coord_quickmap: per impostare il ratio alle mappe
coord_polar: per creare coordinate polari
# Flip
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot()ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot() +
coord_flip()# Quickmap
nz <- map_data("nz")
ggplot(nz, aes(long, lat, group = group)) +
geom_polygon(fill = "white", colour = "black")ggplot(nz, aes(long, lat, group = group)) +
geom_polygon(fill = "white", colour = "black") +
coord_quickmap()# Polari
bar <- ggplot(data = diamonds) +
geom_bar(
mapping = aes(x = cut, fill = cut),
show.legend = FALSE,
width = 1
) +
theme(aspect.ratio = 1) +
labs(x = NULL, y = NULL)
bar + coord_flip()bar + coord_polar()La visualizzazione dei dati è importante ma è raro averli nel formato desiderato. Per modificare i dati, il pacchetto principe è dyplr.
Per far ciò utilizzeremo il dataset denominato flights.
head(flights)## # A tibble: 6 × 19
## year month day dep_time sched_dep…¹ dep_d…² arr_t…³ sched…⁴ arr_d…⁵ carrier
## <int> <int> <int> <int> <int> <dbl> <int> <int> <dbl> <chr>
## 1 2013 1 1 517 515 2 830 819 11 UA
## 2 2013 1 1 533 529 4 850 830 20 UA
## 3 2013 1 1 542 540 2 923 850 33 AA
## 4 2013 1 1 544 545 -1 1004 1022 -18 B6
## 5 2013 1 1 554 600 -6 812 837 -25 DL
## 6 2013 1 1 554 558 -4 740 728 12 UA
## # … with 9 more variables: flight <int>, tailnum <chr>, origin <chr>,
## # dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## # time_hour <dttm>, and abbreviated variable names ¹sched_dep_time,
## # ²dep_delay, ³arr_time, ⁴sched_arr_time, ⁵arr_delay
flights è un dataframe ma di tipo tibble che è un formato particolarmente utile nel tidyverse. In particolare mi fa vedere solo poche righe e tutte le colonne. Inoltre ogni colonna ha, sotto il nome, il tipo di dato.
In questo capitolo andremo a vedere le 5 più utili funzioni del tidyverse che servono per risolvere la maggior parte dei problemi di manipolazione dati:
Se voglio salvare il risultato in una variabile e contemporaneamente visualizzarne l’output posso racchiudere tutta l’istruzione all’iterno di una parentesi.
(feb3 <- flights %>% filter(
month==1, day==1
))## # A tibble: 842 × 19
## year month day dep_time sched_de…¹ dep_d…² arr_t…³ sched…⁴ arr_d…⁵ carrier
## <int> <int> <int> <int> <int> <dbl> <int> <int> <dbl> <chr>
## 1 2013 1 1 517 515 2 830 819 11 UA
## 2 2013 1 1 533 529 4 850 830 20 UA
## 3 2013 1 1 542 540 2 923 850 33 AA
## 4 2013 1 1 544 545 -1 1004 1022 -18 B6
## 5 2013 1 1 554 600 -6 812 837 -25 DL
## 6 2013 1 1 554 558 -4 740 728 12 UA
## 7 2013 1 1 555 600 -5 913 854 19 B6
## 8 2013 1 1 557 600 -3 709 723 -14 EV
## 9 2013 1 1 557 600 -3 838 846 -8 B6
## 10 2013 1 1 558 600 -2 753 745 8 AA
## # … with 832 more rows, 9 more variables: flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>, and abbreviated variable names
## # ¹sched_dep_time, ²dep_delay, ³arr_time, ⁴sched_arr_time, ⁵arr_delay
Gli operatori logici sono estremamente utili:
Una cosa particolarmente imporante sono gli NA. Questi vengono esclusi di default e se li voglio, devo includerli esplicitamente come nel seguente caso:
df <- tibble(x = c(1, NA, 3))
filter(df, x > 1)## # A tibble: 1 × 1
## x
## <dbl>
## 1 3
#> # A tibble: 1 x 1
#> x
#> <dbl>
#> 1 3
filter(df, is.na(x) | x > 1)## # A tibble: 2 × 1
## x
## <dbl>
## 1 NA
## 2 3
#> # A tibble: 2 x 1
#> x
#> <dbl>
#> 1 NA
#> 2 3Arrange, non seleziona le righe ma, piuttosto le riordina. Per ordinarle dal maggiore al minore si utilizza desc(). I valori NA sono sempre messi al fondo.
arrange(flights, desc(dep_time), year, day)## # A tibble: 336,776 × 19
## year month day dep_time sched_de…¹ dep_d…² arr_t…³ sched…⁴ arr_d…⁵ carrier
## <int> <int> <int> <int> <int> <dbl> <int> <int> <dbl> <chr>
## 1 2013 4 2 2400 2359 1 339 343 -4 B6
## 2 2013 9 2 2400 2359 1 411 340 31 B6
## 3 2013 4 4 2400 2355 5 347 345 2 B6
## 4 2013 12 5 2400 2359 1 427 440 -13 B6
## 5 2013 2 7 2400 2359 1 432 436 -4 B6
## 6 2013 2 7 2400 2359 1 443 444 -1 B6
## 7 2013 7 7 2400 1950 250 107 2130 217 AA
## 8 2013 12 9 2400 2359 1 432 440 -8 B6
## 9 2013 12 9 2400 2250 70 59 2356 63 B6
## 10 2013 8 10 2400 2245 75 110 1 69 B6
## # … with 336,766 more rows, 9 more variables: flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>, and abbreviated variable names
## # ¹sched_dep_time, ²dep_delay, ³arr_time, ⁴sched_arr_time, ⁵arr_delay
Come in SQL serve per selezionare le colonne.
# select(flights, year, month, day) #sel 4 colonne
# select(year:day) #seleziona da year a day (month incluso)
# select(-(year:day)) #seleziona tutto a parte da year a day
# Posso usare anche diverse funzioni quali:
# starts_with("abc"): matches names that begin with “abc”.
# ends_with("xyz"): matches names that end with “xyz”.
# contains("ijk"): matches names that contain “ijk”.
# matches("(.)\\1")ATTENZIONE Select può anche essere utilizzato per rinominare le variabili. Tuttavia ha l’inconveniente di droppare le variabili non esplicitamente nominate. Al suo posto si può usare rename()
rename(flights, tail_num = tailnum)## # A tibble: 336,776 × 19
## year month day dep_time sched_de…¹ dep_d…² arr_t…³ sched…⁴ arr_d…⁵ carrier
## <int> <int> <int> <int> <int> <dbl> <int> <int> <dbl> <chr>
## 1 2013 1 1 517 515 2 830 819 11 UA
## 2 2013 1 1 533 529 4 850 830 20 UA
## 3 2013 1 1 542 540 2 923 850 33 AA
## 4 2013 1 1 544 545 -1 1004 1022 -18 B6
## 5 2013 1 1 554 600 -6 812 837 -25 DL
## 6 2013 1 1 554 558 -4 740 728 12 UA
## 7 2013 1 1 555 600 -5 913 854 19 B6
## 8 2013 1 1 557 600 -3 709 723 -14 EV
## 9 2013 1 1 557 600 -3 838 846 -8 B6
## 10 2013 1 1 558 600 -2 753 745 8 AA
## # … with 336,766 more rows, 9 more variables: flight <int>, tail_num <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>, and abbreviated variable names
## # ¹sched_dep_time, ²dep_delay, ³arr_time, ⁴sched_arr_time, ⁵arr_delay
Molto utile anche la funzione everything:
select(flights, time_hour, air_time, everything())## # A tibble: 336,776 × 19
## time_hour air_t…¹ year month day dep_t…² sched…³ dep_d…⁴ arr_t…⁵
## <dttm> <dbl> <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013-01-01 05:00:00 227 2013 1 1 517 515 2 830
## 2 2013-01-01 05:00:00 227 2013 1 1 533 529 4 850
## 3 2013-01-01 05:00:00 160 2013 1 1 542 540 2 923
## 4 2013-01-01 05:00:00 183 2013 1 1 544 545 -1 1004
## 5 2013-01-01 06:00:00 116 2013 1 1 554 600 -6 812
## 6 2013-01-01 05:00:00 150 2013 1 1 554 558 -4 740
## 7 2013-01-01 06:00:00 158 2013 1 1 555 600 -5 913
## 8 2013-01-01 06:00:00 53 2013 1 1 557 600 -3 709
## 9 2013-01-01 06:00:00 140 2013 1 1 557 600 -3 838
## 10 2013-01-01 06:00:00 138 2013 1 1 558 600 -2 753
## # … with 336,766 more rows, 10 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>, origin <chr>,
## # dest <chr>, distance <dbl>, hour <dbl>, minute <dbl>, and abbreviated
## # variable names ¹air_time, ²dep_time, ³sched_dep_time, ⁴dep_delay, ⁵arr_time
Serve per aggiungere nuove variabili alla fine del dataset. Iniziamo con un dataset e l’aggiunta di una nuova colonna:
flights_sml <- select(flights,
year:day,
ends_with("delay"),
distance,
air_time,
hour
)
#puoi riferirti a var appena create
mutate(flights_sml,
gain = dep_delay - arr_delay,
speed = distance / air_time * 60,
gain_per_hour = gain / hour
)## # A tibble: 336,776 × 11
## year month day dep_delay arr_d…¹ dista…² air_t…³ hour gain speed gain_…⁴
## <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2013 1 1 2 11 1400 227 5 -9 370. -1.8
## 2 2013 1 1 4 20 1416 227 5 -16 374. -3.2
## 3 2013 1 1 2 33 1089 160 5 -31 408. -6.2
## 4 2013 1 1 -1 -18 1576 183 5 17 517. 3.4
## 5 2013 1 1 -6 -25 762 116 6 19 394. 3.17
## 6 2013 1 1 -4 12 719 150 5 -16 288. -3.2
## 7 2013 1 1 -5 19 1065 158 6 -24 404. -4
## 8 2013 1 1 -3 -14 229 53 6 11 259. 1.83
## 9 2013 1 1 -3 -8 944 140 6 5 405. 0.833
## 10 2013 1 1 -2 8 733 138 6 -10 319. -1.67
## # … with 336,766 more rows, and abbreviated variable names ¹arr_delay,
## # ²distance, ³air_time, ⁴gain_per_hour
Se al contrario vuoi cestinare tutti gli altri campi e modificarne solo alcuni puoi usare transmutate():
flights_trmt <- transmute(flights,
gain = dep_delay - arr_delay,
hours = air_time / 60,
gain_per_hour = gain / hours
)Ci sono molteplici funzioni che possono essere utili in questa funzione e ne vediamo alcune.
+ - * / ^: sono vettorizzati con
la regola che se finisce un vettore, ricomincia il calcolo%/% (la parte intera di una
divisione) %% (resto di una divisione – non ho capito
bene!!)log() log2() log10()lead() lag() permettono di fare riferimento
alla parte iniziale o terminale. Utile per trovare le differenze
incrementali (x-lag(x)) oppure quando cambia il valore
(x!=lag(x))cumsum cumprod cummax cummeanmin_rank() row_number(), dense_rank(), percent_rank(), cume_dist(), ntile()Esempio offset:
vettore <- c(1,12,33,44,11,67)
lag(vettore)## [1] NA 1 12 33 44 11
lead(vettore)## [1] 12 33 44 11 67 NA
Serve per collassare un data.frame in un’unica riga:
summarise(flights, media = mean(dep_delay, na.rm=TRUE))## # A tibble: 1 × 1
## media
## <dbl>
## 1 12.6
flights %>%
group_by(origin) %>%
summarise( media = mean(dep_delay, na.rm=TRUE))## # A tibble: 3 × 2
## origin media
## <chr> <dbl>
## 1 EWR 15.1
## 2 JFK 12.1
## 3 LGA 10.3
Tutti questi comandi possono essere messi in pipe.
flights %>%
group_by(dest) %>%
summarise(
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
) %>%
filter(count > 20, dest != "HNL") %>%
ggplot(mapping = aes(x = dist, y = delay)) +
geom_point(aes(size = count), alpha = 1/3) +
geom_smooth(se = FALSE)## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
oppure così
# Molto interessante mettere in ascisse il count
# ed in ordinate il valore tipo il numero di rate
flights %>%
filter(!is.na(dep_delay), !is.na(arr_delay)) %>%
group_by(tailnum) %>%
summarise(n=n(), delay = mean(arr_delay)) %>%
ggplot(mapping = aes(x = n, y = delay)) +
geom_point(alpha = 1/10)Vediamo un esempio con il dataset Lahman:
batting <- tibble(Lahman::Batting)
batters <- batting %>%
group_by(playerID) %>%
summarise(
ba = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
ab = sum(AB, na.rm = TRUE)
) %>% filter(ab > 100)
batters %>%
ggplot(mapping = aes(x = ab, y = ba)) +
geom_point() +
geom_smooth(se = FALSE)## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
#> `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'Ci sono diverse istruzioni molto interessanti che viaggiano insieme al summarise:
mean(), median(), sum(), first(x), nth(x, 2), last(x), range()
(range ti dà il minimo e massimo valore del vettore)sd(), IQR(), mad() (IQR è
interquartile)min(), quantile(x,0.25), max()n(), n_distinct()sum(x<10) oppure
mean(y==0).flights %>%
group_by(year, month) %>%
summarise(numero =n(), n_early = sum(dep_time < 1000))## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
## # A tibble: 12 × 4
## # Groups: year [1]
## year month numero n_early
## <int> <int> <int> <int>
## 1 2013 1 27004 NA
## 2 2013 2 24951 NA
## 3 2013 3 28834 NA
## 4 2013 4 28330 NA
## 5 2013 5 28796 NA
## 6 2013 6 28243 NA
## 7 2013 7 29425 NA
## 8 2013 8 29327 NA
## 9 2013 9 27574 NA
## 10 2013 10 28889 NA
## 11 2013 11 27268 NA
## 12 2013 12 28135 NA
Utile se necessiti di togliere il raggruppamento e ritornare alla situazione precedente.
In questo capitolo vediamo come utilizzare la Data Visualisation e la Data transformation per esplorare i dati in maniera sistematica, un’attività che gli statistici chiamano Exploring Data Analysis o EDA.
Non si tratta di un processo formale ma casuale e sempre più raffinato. Quando ti fai una domanda, vai ad esplorare un determinato ambito del dataset.
Le domande che devono sempre guidare sono:
E’ ineteressante fare un taglio della base dati per cluster tipo.
Altra cosa geom_freqpoly è come geom_histogram
ma al posto degli istogrammi fa apparire delle rette:
diamonds %>%
group_by(cut_width(carat, 0.5)) %>%
summarise(numero = n())## # A tibble: 11 × 2
## `cut_width(carat, 0.5)` numero
## <fct> <int>
## 1 [-0.25,0.25] 785
## 2 (0.25,0.75] 29498
## 3 (0.75,1.25] 15977
## 4 (1.25,1.75] 5313
## 5 (1.75,2.25] 2002
## 6 (2.25,2.75] 322
## 7 (2.75,3.25] 32
## 8 (3.25,3.75] 5
## 9 (3.75,4.25] 4
## 10 (4.25,4.75] 1
## 11 (4.75,5.25] 1
smaller <- diamonds %>%
filter(carat < 3)
ggplot(data = smaller, mapping = aes(x = carat)) +
geom_histogram(binwidth = 0.1)ggplot(data = smaller, mapping = aes(x = carat, colour = cut)) +
geom_freqpoly(binwidth = 0.1)Nel caso si ottengano risultati inaspettati è possibile sostituire le righe con valori strani con degli NA e un ifelse.
diamonds2 <- diamonds %>%
mutate(y = ifelse(y<3 | y>20, NA, y))Esiste anche il case_when() particolarmente utile
all’interno di mutate().
Se si creano dei grafici con degli NA, ggplot non li visualizza nel grafico ma fornisce un warning che può essere eliminato con la clausola na.rm = TRUE.
Se la varianza indica la variazione all’interno di una variabile, la covarianza indica le variazioni tra variabili ovvero la tendenza di 2 o più variabili di cambiare in una direzione.
E’ importante capire che è molto differente una variabile categorica
e una continua. E’ molto comune voler analizzare una variabile continua
differenziata da una categorica: in tal caso geom_freqpoly
non è così utile perchè sulle ascisse c’è il conteggio. Per questo
motivo se un gruppo è molto più piccolo di un altro è difficile vederne
la sagoma. Per esempio:
ggplot(data = diamonds, mapping = aes(x = price)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)è difficile vedere le differenze perchè Fair ha un numero piccolo di osservazioni:
ggplot(diamonds) +
geom_bar(mapping = aes(x = cut))In questi casi possiamo cambiare l’asse y ed invece del count, possiamo mettere in questo caso, la densità che è il conteggio a somma 100. IMPORTANTE:
ggplot(
data = diamonds,
mapping = aes(x = price, y = ..density..)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)
Una visualizzazione alternativa sono i boxplot:
Spiegazione grafica dei tre punti sopra
Co questo grafico si veda la situazione:
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_boxplot()Questo grafico dà meno informazioni sulla distribuzione ma è compatto e più facile da comparare avvallando la testi che il prezzo medio dei diamanti più pregiati è mediamente più basso di quelli meno pregiati.
ATTENZIONE: il cut è un fattore ma in
altri casi potrei voler ordinare la mia variabile con
`reorder``.
Per esempio questo grafico:
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot()
posso voler riordinare la
classe per la mediana del
hwy che sarebbero le ordinate:
ggplot(data = mpg) +
geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))Nel caso di nomi di variabili lunghi posso invertire gli assi con
coord_flip.
Per visualizzare la correlazione tra due categoriche puoi contare il
numero di osservazioni per ogni combinazione. Per esempio puoi usare
geom_count.
ggplot(data = diamonds) +
geom_count(mapping = aes(x = cut, y = color))Un altro approccio può essere una semplice tabella:
diamonds %>%
count(color, cut)## # A tibble: 35 × 3
## color cut n
## <ord> <ord> <int>
## 1 D Fair 163
## 2 D Good 662
## 3 D Very Good 1513
## 4 D Premium 1603
## 5 D Ideal 2834
## 6 E Fair 224
## 7 E Good 933
## 8 E Very Good 2400
## 9 E Premium 2337
## 10 E Ideal 3903
## # … with 25 more rows
Oppure un geom_tile
diamonds %>%
count(color, cut) %>%
ggplot(mapping = aes(x = color, y = cut)) +
geom_tile(mapping = aes(fill = n))Uno dei migliori grafici è lo scatterplot. Il problema è che quando il numero di punti aumenta tanto, inizia a diventare confusionario: in tal caso posso usare la trasparenza (con alpha=1/100) oppure bin.
geom_bin2() e geom_hex dividono il piano in
due contenitori e poi li usano un colore per visualizzare quanti punti
appaiono in ciascun contenitore: il primo con i rettangoli, il secondo
con gli esagoni.
ggplot(data = smaller) +
geom_bin2d(mapping = aes(x = carat, y = price))library("hexbin")## Warning: il pacchetto 'hexbin' è stato creato con R versione 4.2.1
ggplot(data = smaller) +
geom_hex(mapping = aes(x = carat, y = price))
Un’altra tecnica è far diventare una variabile continua come se fosse
una variabile categorica e poi sfruttare una delle visualizzazioni viste
sopra:
ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))
IMPORTANTE:
cut_width(x, width) divide x
in contenitori di larghezza width.
Un altro approccio è visualizzare lo stesso numero di osservazioni
per contenitore. Anzichè usare cut_width si userà
cut_number:
ggplot(data = smaller, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_number(carat, 20)))Se esiste un pattern ovvero una relazione fra variabili bisogna chiedersi:
Se si pensa la variazione di una variabile come fonte di incertezza,
la covarianza è un fenomeno che riduce tale incertezza. Posso fare delle
previsioni sulla seconda variabile al cambiare della prima. I modelli
sono strumenti per estrarre pattern dai dati. Per esempio: nel dataset
diamonds è difficile trovare una relazione tra
cut, price perchè cut, carat e
price, carat sono molto forti. Si può usare un modello per
rimuovere la forte relazione price, carat e quindi
esplorare ciò che rimane.
Posso quindi impostare un modello che preveda il prezzo dai carati e calcoli i residui (differenza tra il valore predetto ed attuale). I residui mi danno una vista del prezzo del diamante una volta che l’effetto dei carati è stato rimosso:
library(modelr)## Warning: il pacchetto 'modelr' è stato creato con R versione 4.2.1
mod <- lm(log(price) ~ log(carat), data = diamonds)
diamonds2 <- diamonds %>%
add_residuals(mod) %>%
mutate(resid = exp(resid))
ggplot(data = diamonds2) +
geom_point(mapping = aes(x = carat, y = resid))Una volta rimossa la forte relazione tra prezzo e carati, si può vedere la relazione tra taglio e prezzo: in relazione alla dimensione, migliore qualità è più caro.
ggplot(data = diamonds2) +
geom_boxplot(mapping = aes(x = cut, y = resid))Data wrangling sarebbe l’arte di rendere i dati facilmente utilizzabili per la visualizzazione e la modellazione. E’ una parte estremamente importante senza la quale non si riuscirebbe a lavorare con i propri dati e si divide in tre parti:
Ci orienteremo in particolare in alcuni tipologie di dati: Dati relazionali, stringhe, date, fattori.