Nota: è possibile utilizzare la table of content scorrevole sulla sinistra per andare alle specifiche sezioni.
In questo piccolo progetto andrò ad effettuare una semplice analisi descrittiva di un dataset .csv di prova che contiene report dei dati relativi alle vendite immobiliari in Texas.
Approfitterò anche per provare un po’ di differenti funzionalità e pacchetti aggiuntivi.
In uno studio formale sarebbe ovviamente più appropriato mantenere una certa omogeneità, sia per chiarezza sia per un più efficiente utilizzo di risorse.
~In questo scenario cercherò di sperimentare (come chiesto anche nei dettagli dell’esercitazione) nei limiti delle mie
(molto modeste) capacità di programmazione.~
Nota: tutti i pacchetti caricati con library() vanno ovviamente prima installati, nel caso l’utente non lo abbia già fatto, tramite il comando install.packages(“nomepacchetto”).
#Caricamento del dataset
data <- read.csv("realestate_texas.csv", sep = ",")
#Visualizzazione grezza della struttura del dataset:
str(data)
## 'data.frame': 240 obs. of 8 variables:
## $ city : chr "Beaumont" "Beaumont" "Beaumont" "Beaumont" ...
## $ year : int 2010 2010 2010 2010 2010 2010 2010 2010 2010 2010 ...
## $ month : int 1 2 3 4 5 6 7 8 9 10 ...
## $ sales : int 83 108 182 200 202 189 164 174 124 150 ...
## $ volume : num 14.2 17.7 28.7 26.8 28.8 ...
## $ median_price : num 163800 138200 122400 123200 123100 ...
## $ listings : int 1533 1586 1689 1708 1771 1803 1857 1830 1829 1779 ...
## $ months_inventory: num 9.5 10 10.6 10.6 10.9 11.1 11.7 11.6 11.7 11.5 ...
Il dataset contiene 240 “elementi” (osservazioni) e 8 diverse variabili. Esaminiamone la tipologia e le modalità (nel caso delle variabili non categoriche ne prenderemo solo alcune casualmente).
City: Formato stringa (chr), variabile qualitativa su scala nominale. Ricaviamo le possibili modalità uniche che essa può assumere nel dataset (tramite una semplice funzione della libreria dplyr):
#utilizzato l'argomento message=false per evitare di riportare l'output del caricamento della libreria, sarà lo stesso per gli altri blocchi di caricamento librerie
library(dplyr)
cities <- data %>% group_by(city)
cities %>% group_keys()
## # A tibble: 4 × 1
## city
## <chr>
## 1 Beaumont
## 2 Bryan-College Station
## 3 Tyler
## 4 Wichita Falls
Queste sono semplicemente le località nelle quali si trovano gli immobili venduti ovvero Beaumont, Bryan-College Station, Tyler e Wichita Falls.
Year: Formato numero intero (int). Quando si tratta di tempo, l’effettiva tipologia della variabile può essere ambigua. Visualizziamo le modalità:
years <- data %>% group_by(year)
#utilizzo di print.data.frame per rendere l'output più pulito
print.data.frame(years %>% group_keys())
## year
## 1 2010
## 2 2011
## 3 2012
## 4 2013
## 5 2014
Formalmente, saremmo in presenza di una variabile quantitativa discreta (solo nel senso che indichiamo gli anni interi, ma di per sé sarebbe continua) su scala ad intervalli. In questo scenario è probabilmente più utile e appropriato trattarla come una variabile qualitativa (o categorica). Sono registrate le vendite su un periodo di 5 anni consecutivi che va da inizio 2010 a fine 2014, è opportuno associare gli anni a “categorie”.
Month: Formato numero intero (int). Abbiamo un’unità normalmente espressa in parole (i nomi dei mesi) ma qui espressa sottoforma di numeri. Si può intuire che abbia la stessa funzione della precedente variabile. Osserviamo, per completezza, le modalità della variabile:
## month
## 1 1
## 2 2
## 3 3
## 4 4
## 5 5
## 6 6
## 7 7
## 8 8
## 9 9
## 10 10
## 11 11
## 12 12
I mesi dell’anno sono “codificati” con numeri da 1 a 12. La variabile può essere trattata come una variabile qualitativa ordinale, nello specifico ordinale ciclica in quanto tecnicamente non esiste una reale modalità iniziale (o finale), se non nel contesto di un’analisi annuale ordinata. Chiaramente sarebbe possibile trattarla come quantitativa nel caso si dovessero effettuare operazioni numeriche (come calcolare una distanza temporale e trarre eventuali altre informazioni correlate e coerenti nel dataset).
Sales: Formato numero intero (int). Vediamo qualche modalità:
sample(data$sales, 10)
## [1] 127 202 170 182 388 90 163 111 403 196
Una semplice variabile quantitativa discreta su scala di rapporti. Indicherà il numero di vendite effettuato nello specifico mese-anno di riferimento.
Questa variabile, come le altre successive, è legata alle 3
precedenti (nel senso che con opportuni strumenti possibile indicare le
prime 3 variabili come “input di ricerca” e ottenere le altre 5
variabili come informazioni).
Si può anche selezionare solo una o due di loro 3 ed ottenere un dataset
ridotto invece che una singola riga.
Un esempio grezzo per ottenere le informazioni per Beaumont, anno 2011 e
mese di maggio:
c <- "Beaumont"
y <- 2011
m <- 5
info <- data %>%
filter(city == c, year == y, month == m) %>%
select(sales, volume, median_price, listings, months_inventory)
#abbiamo "filtrato" per le 3 variabili indicate prima, e selezionato le altre per la visualizzazione
print(info)
## sales volume median_price listings months_inventory
## 1 143 19.684 120700 1832 12.4Volume: Formato numero decimale (num).
## [1] 25.487 63.759 28.833 29.117 35.395 19.684 16.908 40.306 23.029 49.914
Variabile quantitativa continua su scala di rapporti. Indica il valore delle vendite in milioni di dollari nel mese-anno di riferimento.
Median_price: Formato numero decimale (num).
## [1] 143100 118200 111100 126200 99300 155200 91700 134200 95000 163700
Variabile quantitativa continua* su scala di rapporti. Indica il prezzo mediano di vendita nel mese-anno di riferimento.
*Da notare comunque che si tratta numeri interi arrotondati alle centinaia, seppur inseriti nel dataset in formato decimale.
In un certo senso apparirebbe come una variabile discreta, ma sarebbe formalmente incorretto.
Listings: Formato numero intero (int).
## [1] 1041 3272 1562 3267 1016 2897 1486 1586 907 2938
Variabile quantitativa discreta su scala di rapporti. Indica il numero di annunci attivi nel periodo di riferimento.
Months_inventory: Formato numero decimale (num).
## [1] 13.8 9.3 7.9 9.0 8.1 8.5 8.6 4.8 9.0 11.6
Variabile quantitativa continua su scala di rapporti. Indica il tempo in mesi che sarebbe necessario, col rateo di vendite stimato nel periodo di riferimento, per vendere tutte le inserzioni ancora presenti.
Per le prime 3 variabili, essendo qualitative, calcoliamo le distribuzioni di frequenze. Per le altre calcoleremo i vari indici.
#Memorizziamo il numero di righe (oggetti) del dataset
n <- dim(data)[1]
#Calcoliamo frequenze assolute, relative, e le loro cumulate
#Tabuliamo i dati della variabile city con la funzione table
city_ni <- table(data$city) #freq assolute
city_fi <- table(data$city)/n #freq relative
city_Ni <- cumsum(city_ni) #somma cumulativa delle freq assolute
city_Fi <- city_Ni/n #somma cumulativa delle freq relative
#concateniamo i valori ottenuti in un vettore
cbind(city_ni, city_fi, city_Ni, city_Fi)
## city_ni city_fi city_Ni city_Fi
## Beaumont 60 0.25 60 0.25
## Bryan-College Station 60 0.25 120 0.50
## Tyler 60 0.25 180 0.75
## Wichita Falls 60 0.25 240 1.00
Notiamo che nel dataset le vendite sono equamente distibuite (city_ni, che rappresenta la frequenza assoluta, è 60 per tutte le modalità). Abbiamo una variabile quadrimodale.
(Ciò ha senso considerando che, come vedremo dopo, per tutte le città sono riportati i dati relativi a un periodo di 60 mesi consecutivi.)
Osserviamo meglio per mezzo di un basilare grafico:
city_distrib <- as.data.frame(cbind(city_ni, city_fi, city_Ni, city_Fi))
#Trasformare i vettori in un dataframe è consigliabile e utile in molti casi,
#ad esempio nel caso si vogliano esportare i risultati in file csv o simili,
#anche se in questo specifico scenario di presentazione non è necessario.
#Per tenere conto di questa operazione, è necessario specificare come primo
#argomento il dataframe in questione (e la colonna da usare, con la notazione $).
#In questo caso funzionerebbe anche indicando direttamente il vettore city_ni.
par(las=1) #per rendere i valori dell'asse y orizzontali
barplot(city_distrib$city_ni,
main = "Distribuzione di frequenza (città)",
xlab = "Città",
ylab = "Frequenza assoluta",
col = "gray",
names.arg = rownames(city_distrib))
Questa volta utilizzeremo la libreria qdap per eseguire le stesse operazioni ma più rapidamente.
library(qdap)
Con il comando dist_tab() di qdap è possibile ottenere direttamente una distribuzione di frequenza (specificando opportunamente la variabile del dataset). Le frequenze relative saranno indicate in formato percentuale.
year_distrib <- dist_tab(data$year)
year_distrib
## interval freq cum.freq percent cum.percent
## 1 2010 48 48 20 20
## 2 2011 48 96 20 40
## 3 2012 48 144 20 60
## 4 2013 48 192 20 80
## 5 2014 48 240 20 100
Noteremo che anche qui abbiamo una distribuzione equimodale, pentamodale in questo caso. La frequenza sarà di 48 per tutti e 5 gli anni.
Ciò ha senso se consideriamo che, come vedremo, sono riportati i dati per ogni mese di 12 mesi (per ogni anno) per 5 anni.
Se prendiamo un anno, per ogni città ci saranno 12 report (quindi 12 “oggetti” nel dataset).
Se prendiamo tutte e 4 le città, cumulativamente ci saranno 48 report annuali.
Utilizziamo la stessa tipologia di grafico basilare per visualizzare:
par(las=1)
barplot(year_distrib$freq,
main = "Distribuzione di frequenza (anni)",
xlab = "Anno",
ylab = "Frequenza assoluta",
ylim = c(0, 50), #impostiamo manualmente la cima dell'asse y
col = "gray",
names.arg = year_distrib$interval)
month_distrib <- dist_tab(data$month)
month_distrib
## interval freq cum.freq percent cum.percent
## 1 1 20 20 8.33 8.33
## 2 2 20 40 8.33 16.67
## 3 3 20 60 8.33 25.00
## 4 4 20 80 8.33 33.33
## 5 5 20 100 8.33 41.67
## 6 6 20 120 8.33 50.00
## 7 7 20 140 8.33 58.33
## 8 8 20 160 8.33 66.67
## 9 9 20 180 8.33 75.00
## 10 10 20 200 8.33 83.33
## 11 11 20 220 8.33 91.67
## 12 12 20 240 8.33 100.00
Anche in questo caso, distribuzione equimodale. La frequenza è di 20 per ogni mese.
Essendo il periodo di report di 5 anni, per ogni città ci saranno 5 report di mese corrispondente (ovviamente di anno diverso).
Abbiamo 4 città, dunque 5*4=20.
par(las=1)
barplot(month_distrib$freq,
main = "Distribuzione di frequenza (mesi)",
xlab = "Mese",
ylab = "Frequenza assoluta",
ylim = c(0, 20),
col = "gray",
names.arg = c("gen", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dic")) #sovrascriviamo la notazione in numeri con abbreviazioni dei mesi
Da questo momento, passeremo al calcolo degli indici (posizione, variabilità, forma) delle altre 5 variabili.
Partiamo dagli indici di posizione.
#QUARTILI (comprende minimo, massimo e mediana)
quantile(data$sales)
## 0% 25% 50% 75% 100%
## 79.0 127.0 175.5 247.0 423.0
0% e 100% corrisponderanno al minimo e al massimo, 50% corrisponderà alla mediana.
Per formalità, verifichiamoli manualmente:
#Utilizzo del comando paste per concatenare in modo semplice
#le stringhe e i risultati dei calcoli nelle funzioni richiamate
#Fun fact: venendo dal corso Python ero abituato ad eseguire
#operazioni di questo tipo con print, ma R apparentemente
#non permette la stessa sintassi e c'è voluta un'ora solo per
#capire ci volesse un altro comando
#Non so se ci siano modi migliori, ma questo è adeguato
paste("Il minimo è:", min(data$sales), " la mediana è:", median(data$sales), " il massimo è:", max(data$sales))
## [1] "Il minimo è: 79 la mediana è: 175.5 il massimo è: 423"
Calcoliamo la media (aritmetica):
paste("Media (aritmetica):", mean(data$sales))
## [1] "Media (aritmetica): 192.291666666667"
Non dimentichiamo la moda. Essendoci un numero non basso di osservazioni, utilizzare table non è molto comodo.
Si potrebbe utilizzare una piccola funzione creata manualmente, ad esempio:
mode <- function(x) {
u <- unique(x) #tutte le modalità uniche possibili dell'argomento
tab <- tabulate(match(x, u)) #confronto tra tutte le modalità della variabile e quelle uniche
u[tab == max(tab)]
}
#Ci ho messo più del dovuto a trovare questa combinazione
Questa funzione crea una tabella di frequenza e poi trova il valore (o i valori) che hanno la frequenza massima.
mode(data$sales)
## [1] 124
Ma visto che siamo sfaticati potremmo voler utilizzare un
pacchetto che offre una funzione dedicata, come DescTools.
library(DescTools)
Mode(data$sales)
## [1] 124
## attr(,"freq")
## [1] 5
#Il primo valore riportato in output è la moda, l'ultimo indica quante volte si ripete
Notiamo che la moda è inferiore della mediana, la quale è inferiore della media. Ci ritorneremo a breve.
Passiamo agli indici di variabilità.
IQR (range interquartile, ovvero il distacco tra terzo e primo quartile):
paste("IQR:", IQR(data$sales))
## [1] "IQR: 120"
Varianza (o momento secondo, ovvero il rapporto tra la sommatoria dagli scarti dalla media al quadrato e il numero totale di osservazioni):
var(data$sales)
## [1] 6344.3
#tengo conto del fatto che le funzioni dirette restituiscono un valore leggermente diverso
#da quelli calcolati manualmente, ma ai fini di questo studio non è rilevante
Deviazione standard (radice quadrata della varianza):
sales_sd <- sd(data$sales)
print(sales_sd)
## [1] 79.65111
Coefficiente di variazione (rapporto tra deviazione standard e media):
sales_CV <- sd(data$sales)/mean(data$sales)
#nota, facoltativamente si moltiplica per 100 nel caso si desideri
#utilizzare un valore percentuale
print(sales_CV)
## [1] 0.4142203
Ora passiamo agli indici di forma:
Utilizziamo il pacchetto moments per questa sezione.
library(moments)
(A)Simmetria (skewness) ovvero il rapporto del rapporto tra il cubo del momento terzo della distribuzione (rapporto tra sommatoria degli scarti dalla media al cubo e il numero di osservazioni) e il cubo della deviazione standard:
sales_skew <- skewness(data$sales)
print(sales_skew)
## [1] 0.718104
Essendo maggiore di 0, avremo una distribuzione asimmetrica positiva.
Curtosi (ovvero il rapporto tra momento quarto e deviazione standard alla quarta):
#ricordiamoci di sottrarre 3 al valore ottenuto dalla funzione dedicata,
#essendo essa centrata in 3 e non in 0
sales_kurt <- kurtosis(data$sales)-3
print(sales_kurt)
## [1] -0.3131764
Essendo minore di 0 avremo una distribuzione platicurtica, ovvero più “appiattita” (almeno mediamente) rispetto alla distribuzione normale teorica.
Per farci un’idea, vediamo una rappresentazione grezza della distribuzione (ci interessa principalmente la “forma” del grafico):
plot(density(data$sales), lwd=2)
abline(v=range(data$sales), col=2)
x <- seq(min(data$sales), max(data$sales), length = 200)
y <- dnorm(x, mean = mean(data$sales), sd = sd(data$sales))
lines(x, y, col = "cyan3", lwd = 1)
~(Le due linee rosse sono state inserite per delimitare minimo e massimo, le parti di curva che le superano possono essere ignorate.
La linea azzurrina rappresenta la distribuzione normale teorica, data stessa media e deviazione standard.)
~
Si può dire che il numero di vendite tipico di una città in un mese è
molto più vicino al minimo che al massimo.
Vediamo anche l’andamento cumulativo delle vendite (includendo tutte le città) nell’intero periodo d’analisi per mezzo della funzione aggregate:
#Calcoliamo le vendite cumulative per città e mese (di ogni anno)
cum_sales <- aggregate(data$sales ~ data$month + data$year, data = data, FUN = sum)
#l'output sarà ordinato per mesi ciclicamente, andando avanti di anno in anno
print(cum_sales)
## data$month data$year data$sales
## 1 1 2010 421
## 2 2 2010 487
## 3 3 2010 755
## 4 4 2010 916
## 5 5 2010 931
## 6 6 2010 866
## 7 7 2010 712
## 8 8 2010 738
## 9 9 2010 598
## 10 10 2010 565
## 11 11 2010 503
## 12 12 2010 604
## 13 1 2011 425
## 14 2 2011 469
## 15 3 2011 668
## 16 4 2011 716
## 17 5 2011 780
## 18 6 2011 885
## 19 7 2011 812
## 20 8 2011 786
## 21 9 2011 629
## 22 10 2011 594
## 23 11 2011 549
## 24 12 2011 565
## 25 1 2012 499
## 26 2 2012 574
## 27 3 2012 711
## 28 4 2012 747
## 29 5 2012 882
## 30 6 2012 898
## 31 7 2012 928
## 32 8 2012 954
## 33 9 2012 707
## 34 10 2012 742
## 35 11 2012 650
## 36 12 2012 643
## 37 1 2013 576
## 38 2 2013 593
## 39 3 2013 814
## 40 4 2013 878
## 41 5 2013 1057
## 42 6 2013 1045
## 43 7 2013 1127
## 44 8 2013 1107
## 45 9 2013 814
## 46 10 2013 738
## 47 11 2013 690
## 48 12 2013 733
## 49 1 2014 627
## 50 2 2014 694
## 51 3 2014 841
## 52 4 2014 977
## 53 5 2014 1127
## 54 6 2014 1177
## 55 7 2014 1136
## 56 8 2014 1044
## 57 9 2014 899
## 58 10 2014 959
## 59 11 2014 745
## 60 12 2014 843
par(las=1)
sales_month <- barplot(cum_sales$`data$sales`,
main = "Numero di vendite cumulate mese per mese (2010-2014)",
xlab = "Numero mese (1 anno = 12 mesi)",
ylab = "Vendite",
col = "white",
names.arg = seq(1, 60, 1),
cex.names = 0.7,
las = 2)
lines(sales_month, cum_sales$`data$sales`, col=2, lwd=1)
Pare che, complessivamente, ci siano picchi di vendite nei periodi estivi (comprensibile…) e che nel 2013 e 2014 le vendite in generale siano maggiori. Ritorneremo su tendenze come queste in una sezione successiva.
Quantifichiamo meglio annualmente:
cum_sales_y <- aggregate(data$sales ~ data$year, data = data, FUN = sum)
print.data.frame(cum_sales_y)
## data$year data$sales
## 1 2010 8096
## 2 2011 7878
## 3 2012 8935
## 4 2013 10172
## 5 2014 11069
Nel secondo anno si è registrata una leggera riduzione di vendite totali rispetto al primo, mentre negli anni successivi è notevolmente aumentata.
Questa volta proviamo ad utilizzare il pacchetto summarytools, che promette di calcolare pressoché tutti gli indici essenziali con un singolo comando. Eventuali mancanze le colmeremo manualmente.
library(summarytools)
stats_vol <- descr(data$volume, round.digits = 7, transpose = T)
stats_vol
## Descriptive Statistics
## data$volume
## N: 240
##
## Mean Std.Dev Min Q1 Median Q3
## ------------ ------------ ------------ ----------- ------------ ------------ ------------
## volume 31.0051875 16.6514472 8.1660000 17.6290000 27.0625000 40.9030000
##
## Table: Table continues below
##
##
##
## Max MAD IQR CV Skewness SE.Skewness
## ------------ ------------ ------------ ------------ ----------- ----------- -------------
## volume 83.5470000 16.1566335 23.2335000 0.5370536 0.8792182 0.1571376
##
## Table: Table continues below
##
##
##
## Kurtosis N.Valid Pct.Valid
## ------------ ----------- ------------- -------------
## volume 0.1505673 240.0000000 100.0000000
Per referenza,
~Mean = media (aritmetica)
Std.Dev = Deviazione standard
Min e Max = Minimo, massimo
Q1 e Q3 = primo e terzo quartile
Median = mediana
IQR = range interquartile
CV= coefficiente di variazione
Skewness = Asimmetria
Kurtosis = Curtosi~
Sembra esserci tutto a parte moda e varianza, che per certi versi son di minor rilievo in scenari come questo dato che avendo tantissimi dati molto precisi (e a volte non interi) e diversi difficilmente ci saranno dati che si ripeteranno più volte e se ci son più valori di moda questi possono essere molto diversi tra loro, mentre alla varianza viene pressoché sempre preferita la deviazione standard (sua radice quadrata). In ogni caso sono facilmente calcolabili.
paste("Moda:", Mode(data$volume))
## [1] "Moda: 14.003" "Moda: 35.335" "Moda: 42.553"
paste(" Varianza:", var(data$volume))
## [1] " Varianza: 277.270692404027"
Volendo, per curiosità scientifica, si potrebbe cercare la moda dei valori interi approssimati:
Mode(round(data$volume))
## [1] 14
## attr(,"freq")
## [1] 11
O anche dei valori approssimati alla prima cifra decimale, anche se a questo punto comincerebbe a crearsi una certa confusione:
Mode(round(data$volume, digits = 1))
## [1] 13.9 16.1 16.2 17.8 24.9 31.7 32.1 40.9 42.6
## attr(,"freq")
## [1] 3
In generale, spesso ha senso creare delle classi e determinare le classi modali, che è un’operazione molto semplice. Per questo studio non lo faremo (tralasciando successive richieste), ci limiteremo ad osservare le densità di distribuzione.
Nell’output abbiamo anche qualche “nuovo” indice come la MAD (Deviazione Mediana Assoluta) che è in realtà un indice piuttosto utile data la sua “robustezza” (bassa o negligibile sensibilità ad outliers o al peso di distribuzioni fortemente non-normali) ma che per questo studio non approfondiremo.
Abbiamo di nuovo un’asimmetria positiva, ma in questo caso una distribuzione leggermente leptocurtica (distribuzione mediamente più “appuntita” rispetto alla normale teorica).
Come per sales, vediamo qualche grafico:
plot(density(data$volume), lwd=2)
abline(v=range(data$volume), col=2)
x <- seq(min(data$volume), max(data$volume), length = 100)
y <- dnorm(x, mean = mean(data$volume), sd = sd(data$volume))
lines(x, y, col = "cyan3", lwd = 1)
#Guadagni cumulativi per città e mese
cum_volume <- aggregate(data$volume ~ data$month + data$year, data = data, FUN = sum)
print(cum_volume)
## data$month data$year data$volume
## 1 1 2010 63.751
## 2 2 2010 76.897
## 3 3 2010 111.876
## 4 4 2010 135.196
## 5 5 2010 143.689
## 6 6 2010 138.191
## 7 7 2010 106.764
## 8 8 2010 114.356
## 9 9 2010 88.826
## 10 10 2010 87.164
## 11 11 2010 73.663
## 12 12 2010 92.070
## 13 1 2011 60.658
## 14 2 2011 69.379
## 15 3 2011 101.566
## 16 4 2011 110.267
## 17 5 2011 122.036
## 18 6 2011 138.986
## 19 7 2011 130.855
## 20 8 2011 120.816
## 21 9 2011 95.103
## 22 10 2011 88.274
## 23 11 2011 86.288
## 24 12 2011 83.347
## 25 1 2012 69.791
## 26 2 2012 82.504
## 27 3 2012 109.129
## 28 4 2012 112.128
## 29 5 2012 147.526
## 30 6 2012 149.335
## 31 7 2012 152.463
## 32 8 2012 154.271
## 33 9 2012 112.759
## 34 10 2012 115.004
## 35 11 2012 102.547
## 36 12 2012 97.386
## 37 1 2013 91.739
## 38 2 2013 89.946
## 39 3 2013 125.502
## 40 4 2013 145.621
## 41 5 2013 184.474
## 42 6 2013 184.315
## 43 7 2013 188.551
## 44 8 2013 185.637
## 45 9 2013 136.781
## 46 10 2013 124.589
## 47 11 2013 110.195
## 48 12 2013 119.965
## 49 1 2014 94.076
## 50 2 2014 114.304
## 51 3 2014 139.621
## 52 4 2014 162.877
## 53 5 2014 196.317
## 54 6 2014 215.236
## 55 7 2014 203.805
## 56 8 2014 185.203
## 57 9 2014 158.514
## 58 10 2014 166.541
## 59 11 2014 123.450
## 60 12 2014 149.125
par(las=1)
volume_month <- barplot(cum_volume$`data$volume`,
main = "Guadagni cumulati mese per mese (2010-2014)",
xlab = "Numero mese (1 anno = 12 mesi)",
ylab = "Guadagni (milioni di dollari)",
col = "white",
names.arg = seq(1, 60, 1),
cex.names = 0.7,
las = 2)
lines(volume_month, cum_volume$`data$volume`, col=2, lwd=1)
Vediamo le somme cumulate annuali:
cum_volume_y <- aggregate(data$volume ~ data$year, data = data, FUN = sum)
print.data.frame(cum_volume_y)
## data$year data$volume
## 1 2010 1232.443
## 2 2011 1207.575
## 3 2012 1404.843
## 4 2013 1687.315
## 5 2014 1909.069
Stesse tendenze di quelle intraviste nella variabile sales (ha senso, essendo le due strettamente legate e direttamente proporzionali a parità di prezzo medio).
stats_median_p <- descr(data$median_price, round.digits = 7, transpose = T)
stats_median_p
## Descriptive Statistics
## data$median_price
## N: 240
##
## Mean Std.Dev Min Q1
## ------------------ ---------------- --------------- --------------- ----------------
## median_price 132665.4166667 22662.1486865 73800.0000000 117100.0000000
##
## Table: Table continues below
##
##
##
## Median Q3 Max MAD
## ------------------ ---------------- ---------------- ---------------- ---------------
## median_price 134500.0000000 150100.0000000 180000.0000000 24092.2500000
##
## Table: Table continues below
##
##
##
## IQR CV Skewness SE.Skewness Kurtosis N.Valid
## ------------------ --------------- ----------- ------------ ------------- ------------ -------------
## median_price 32750.0000000 0.1708218 -0.3622768 0.1571376 -0.6427292 240.0000000
##
## Table: Table continues below
##
##
##
## Pct.Valid
## ------------------ -------------
## median_price 100.0000000
paste("Moda:", Mode(data$median_price))
## [1] "Moda: 130000"
paste(" Varianza:", var(data$median_price))
## [1] " Varianza: 513572983.089261"
Asimmetria e curtosi sono entrambe negative, quindi avremo una distribuzione asimmetrica negativa e platicurtica.
Vediamo:
plot(density(data$median_price), lwd=2)
abline(v=range(data$median_price), col=2)
x <- seq(min(data$median_price), max(data$median_price), length = 100)
y <- dnorm(x, mean = mean(data$median_price), sd = sd(data$median_price))
lines(x, y, col = "cyan3", lwd = 1)
Proviamo a vedere un “andamento medio” del prezzo mediano (sempre tenendo conto di tutte le città in insieme, per avere un’idea generale).
#andamendo del prezzo mediano medio per città e mese
mean_median_p_m <- aggregate(data$median_price ~ data$month + data$year, data = data, FUN = mean)
print(mean_median_p_m)
## data$month data$year data$median_price
## 1 1 2010 135450
## 2 2 2010 131100
## 3 3 2010 122325
## 4 4 2010 128300
## 5 5 2010 130125
## 6 6 2010 133275
## 7 7 2010 127225
## 8 8 2010 131100
## 9 9 2010 125825
## 10 10 2010 134150
## 11 11 2010 131600
## 12 12 2010 131825
## 13 1 2011 122250
## 14 2 2011 121200
## 15 3 2011 126750
## 16 4 2011 132475
## 17 5 2011 128775
## 18 6 2011 130350
## 19 7 2011 130750
## 20 8 2011 135350
## 21 9 2011 129100
## 22 10 2011 124100
## 23 11 2011 127900
## 24 12 2011 125250
## 25 1 2012 114250
## 26 2 2012 127225
## 27 3 2012 129275
## 28 4 2012 126250
## 29 5 2012 130575
## 30 6 2012 136175
## 31 7 2012 137375
## 32 8 2012 135050
## 33 9 2012 135275
## 34 10 2012 125075
## 35 11 2012 136750
## 36 12 2012 127650
## 37 1 2013 128325
## 38 2 2013 130600
## 39 3 2013 128225
## 40 4 2013 131050
## 41 5 2013 141025
## 42 6 2013 139525
## 43 7 2013 136200
## 44 8 2013 139525
## 45 9 2013 141350
## 46 10 2013 140550
## 47 11 2013 134150
## 48 12 2013 138150
## 49 1 2014 120975
## 50 2 2014 140250
## 51 3 2014 130500
## 52 4 2014 139375
## 53 5 2014 141925
## 54 6 2014 148775
## 55 7 2014 142200
## 56 8 2014 142350
## 57 9 2014 138650
## 58 10 2014 143525
## 59 11 2014 141125
## 60 12 2014 144125
par(mar = c(5, 5, 4, 0) + 0.1, las=1, cex.axis= 0.95, mgp = c(4, 1, 0))
#qualche aggiustamento per fare spazio nel grafico
mean_median_p_month <- barplot(mean_median_p_m$`data$median_price`,
main = "Media del prezzo mediano mese per mese (2010-2014)",
xlab = "Numero mese (1 anno = 12 mesi)",
ylab = "Prezzo mediano medio",
col = "white",
names.arg = seq(1, 60, 1),
cex.names = 0.75,
las = 2)
lines(mean_median_p_month, mean_median_p_m$`data$median_price`, col=2, lwd=1)
Si potrebbe dire che il prezzo mediano è aumentato in modo timido e graduale dal 2010 a fine 2014. Non sembrano esserci evidenti variazioni stagionali.
Proviamo a raccogliere la media in anni:
mean_median_p_y <- aggregate(data$median_price ~ data$year, data = data, FUN = mean)
print.data.frame(mean_median_p_y)
## data$year data$median_price
## 1 2010 130191.7
## 2 2011 127854.2
## 3 2012 130077.1
## 4 2013 135722.9
## 5 2014 139481.2
Nel secondo anno del periodo di osservazione il prezzo mediano è generalmente diminuito, dopodiché è progressivamente aumentato, proprio come per le vendite (e guadagni).
stats_listings <- descr(data$listings, round.digits = 7, transpose = T)
stats_listings
## Descriptive Statistics
## data$listings
## N: 240
##
## Mean Std.Dev Min Q1 Median
## -------------- -------------- ------------- ------------- -------------- --------------
## listings 1738.0208333 752.7077561 743.0000000 1025.0000000 1618.5000000
##
## Table: Table continues below
##
##
##
## Q3 Max MAD IQR CV Skewness
## -------------- -------------- -------------- ------------- -------------- ----------- -----------
## listings 2128.0000000 3296.0000000 879.9231000 1029.5000000 0.4330833 0.6454431
##
## Table: Table continues below
##
##
##
## SE.Skewness Kurtosis N.Valid Pct.Valid
## -------------- ------------- ------------ ------------- -------------
## listings 0.1571376 -0.8101534 240.0000000 100.0000000
paste("Moda:", Mode(data$listings))
## [1] "Moda: 1581"
paste(" Varianza:", var(data$listings))
## [1] " Varianza: 566568.966091353"
Avremo una distribuzione asimmetrica positiva e platicurtica.
plot(density(data$listings), lwd=2)
abline(v=range(data$listings), col=2)
x <- seq(min(data$listings), max(data$listings), length = 100)
y <- dnorm(x, mean = mean(data$listings), sd = sd(data$listings))
lines(x, y, col = "cyan3", lwd = 1)
Fin’ora questa è sicuramente la distribuzione più irregolare.
Mostriamo l’andamento cumulativo degli annunci attivi:
#Quantità cumulata di annunci attivi ogni mese
cum_listings <- aggregate(data$listings ~ data$month + data$year, data = data, FUN = sum)
par(las=1)
listings_month <- barplot(cum_listings$`data$listings`,
main = "Totale degli annunci attivi mese per mese (2010-2014)",
xlab = "Numero mese (1 anno = 12 mesi)",
ylab = "Annunci attivi",
col = "white",
ylim = c(0, 8000),
names.arg = seq(1, 60, 1),
cex.names = 0.7,
las = 2)
lines(listings_month, cum_listings$`data$listings`, col=2, lwd=1)
Come per per il numero di vendite e i guadagni, il numero di annunci attivi pare essere maggiore in estate (soprattutto attorno giugno). La differenza relativa tra periodo invernale ed estivo però è considerevolmente minore in questo caso.
Proviamo a raggruppare per anni la somma degli annunci attivati all’anno:
cum_listings_y <- aggregate(data$listings/12 ~ data$year, data = data, FUN = sum)
print.data.frame(cum_listings_y)
## data$year data$listings/12
## 1 2010 7304.000
## 2 2011 7398.583
## 3 2012 7107.250
## 4 2013 6710.417
## 5 2014 6240.167
(Per formalità listings dovrebbe essere arrotondato, essendo una variabile intera discreta, ma qui ignoriamo essendo questo un semplice confronto immediato.)
Nel secondo anno il numero totale di annunci attivi ogni mese è stato generalmente maggiore del primo, ma negli anni successivi è costantemente diminuito.
Ad occhio, con i dati così raggruppati, sembrerebbe esserci una correlazione inversa con il prezzo mediano e le precedenti variabili, che avevamo visto diminuire nel secondo anno e aumentare successivamente. Proviamo a vedere quanto è forte la correlazione col prezzo mediano con alcune funzioni.
medianP_listings_correlation_y <- c(cor(mean_median_p_y$`data$median_price`, cum_listings_y$`data$listings`), cor(mean_median_p_y$`data$median_price`, cum_listings_y$`data$listings`, method = "kendall"), cor(mean_median_p_y$`data$median_price`, cum_listings_y$`data$listings`, method = "spearman"))
print(medianP_listings_correlation_y)
## [1] -0.9847591 -0.8000000 -0.9000000
Senza entrare particolarmente nei dettagli, il coefficiente di correlazione è un indice che può assumere un valore compreso nel range continuo -1 e 1. Un coefficiente pari a 1 indica una correlazione positiva perfetta (le due variabili aumentano assieme, un coefficiente di -1 indica una correlazione negativa perfetta (se una variabile aumenta, l’altra diminuisce), mentre 0 indicherebbe l’assenza di correlazione. I metodi utilizzati calcolano in modo diverso la correlazione, e hanno ognuno un proprio campo ideale di applicazione, ma per oggi non ci interessa.
Qui il coefficiente è abbastanza vicino a -1 con tutti i metodi, quindi c’è effettivamente una certa correlazione se si considerano le medie annuali.
Vediamo mese per mese:
medianP_listings_correlation_m <- c(cor(mean_median_p_m$`data$median_price`, cum_listings$`data$listings`), cor(mean_median_p_m$`data$median_price`, cum_listings$`data$listings`, method = "kendall"), cor(mean_median_p_m$`data$median_price`, cum_listings$`data$listings`, method = "spearman"))
print(medianP_listings_correlation_m)
## [1] -0.3937336 -0.2619520 -0.3880514
Mensilmente, la correlazione negativa sembra molto debole.
Facciamo la stessa cosa col numero di vendite.
sales_listings_correlation_y <- c(cor(cum_sales_y$`data$sales`, cum_listings_y$`data$listings`), cor(cum_sales_y$`data$sales`, cum_listings_y$`data$listings`, method = "kendall"), cor(cum_sales_y$`data$sales`, cum_listings_y$`data$listings`, method = "spearman"))
print(sales_listings_correlation_y)
## [1] -0.9902385 -1.0000000 -1.0000000
Apparentemente qui la correlazione inversa annuale è pressoché perfetta.
Vediamo mensilmente:
sales_listings_correlation_m <- c(cor(cum_sales$`data$sales`, cum_listings$`data$listings`), cor(cum_sales$`data$sales`, cum_listings$`data$listings`, method = "kendall"), cor(cum_sales$`data$sales`, cum_listings$`data$listings`, method = "spearman"))
print(sales_listings_correlation_m)
## [1] -0.040204981 0.001697313 -0.001111497
Mentre osservando a livello mensile la correlazione pare nulla.
~Immagino che gli andamenti mensili di alcune variabili potrebbero essere “sfasati” tra loro e ciò mascheri la presenza di un forte collegamento,
oppure osservare le medie annuali comporti una qualche “distorsione” o bias.
Purtroppo ora come ora non ho le competenze per analizzare la prima ipotesi.~
(Non sono sicuro dell’utilità e accuratezza di queste osservazioni.)
stats_months_inv <- descr(data$months_inventory, round.digits = 7, transpose = T)
stats_months_inv
## Descriptive Statistics
## data$months_inventory
## N: 240
##
## Mean Std.Dev Min Q1 Median Q3
## ---------------------- ----------- ----------- ----------- ----------- ----------- ------------
## months_inventory 9.1925000 2.3036686 3.4000000 7.8000000 8.9500000 11.0000000
##
## Table: Table continues below
##
##
##
## Max MAD IQR CV Skewness SE.Skewness
## ---------------------- ------------ ----------- ----------- ----------- ----------- -------------
## months_inventory 14.9000000 2.1497700 3.1500000 0.2506031 0.0407194 0.1571376
##
## Table: Table continues below
##
##
##
## Kurtosis N.Valid Pct.Valid
## ---------------------- ------------ ------------- -------------
## months_inventory -0.1979448 240.0000000 100.0000000
Distribuzione asimmetrica positiva (ma non troppo distante dalla distribuzione normale, data la bassa skewness) e leggermente platicurtica.
paste("Moda:", Mode(data$months_inventory))
## [1] "Moda: 8.1"
paste(" Varianza:", var(data$months_inventory))
## [1] " Varianza: 5.30688912133891"
plot(density(data$months_inventory), lwd=2)
abline(v=range(data$months_inventory), col=2)
x <- seq(min(data$months_inventory), max(data$months_inventory), length = 100)
y <- dnorm(x, mean = mean(data$months_inventory), sd = sd(data$months_inventory))
lines(x, y, col = "cyan3", lwd = 1)
Facciamo la media mensile (sempre considerando le 4 città contemporaneamente):
months_inventory_m <- aggregate(data$months_inventory ~ data$month + data$year, data = data, FUN = mean)
print(months_inventory_m)
## data$month data$year data$months_inventory
## 1 1 2010 8.750
## 2 2 2010 9.150
## 3 3 2010 9.475
## 4 4 2010 10.025
## 5 5 2010 9.700
## 6 6 2010 10.050
## 7 7 2010 10.550
## 8 8 2010 10.625
## 9 9 2010 10.650
## 10 10 2010 10.500
## 11 11 2010 10.400
## 12 12 2010 9.800
## 13 1 2011 9.950
## 14 2 2011 10.200
## 15 3 2011 10.900
## 16 4 2011 11.525
## 17 5 2011 12.075
## 18 6 2011 11.925
## 19 7 2011 11.575
## 20 8 2011 11.225
## 21 9 2011 11.025
## 22 10 2011 10.650
## 23 11 2011 10.175
## 24 12 2011 9.625
## 25 1 2012 9.950
## 26 2 2012 10.150
## 27 3 2012 10.475
## 28 4 2012 10.625
## 29 5 2012 10.525
## 30 6 2012 10.475
## 31 7 2012 10.350
## 32 8 2012 9.800
## 33 9 2012 9.625
## 34 10 2012 9.225
## 35 11 2012 8.975
## 36 12 2012 8.375
## 37 1 2013 8.550
## 38 2 2013 8.700
## 39 3 2013 8.900
## 40 4 2013 9.050
## 41 5 2013 8.700
## 42 6 2013 8.600
## 43 7 2013 8.225
## 44 8 2013 8.000
## 45 9 2013 7.650
## 46 10 2013 7.500
## 47 11 2013 7.225
## 48 12 2013 6.700
## 49 1 2014 7.000
## 50 2 2014 7.100
## 51 3 2014 7.275
## 52 4 2014 7.375
## 53 5 2014 7.375
## 54 6 2014 7.475
## 55 7 2014 7.375
## 56 8 2014 7.300
## 57 9 2014 6.975
## 58 10 2014 6.825
## 59 11 2014 6.525
## 60 12 2014 6.075
par(las=1)
months_inventory_month <- barplot(months_inventory_m$`data$months_inventory`,
main = "Andamento medio delle stime del tempo per la vendita degli annunci attivi (2010-2014)",
xlab = "Numero mese (1 anno = 12 mesi)",
ylab = "Tempo (mesi)",
col = "white",
names.arg = seq(1, 60, 1),
cex.names = 0.7,
las = 2)
lines(months_inventory_month, months_inventory_m$`data$months_inventory`, col=2, lwd=1)
months_inventory_y <- aggregate(data$months_inventory ~ data$year, data = data, FUN = mean)
print(months_inventory_y)
## data$year data$months_inventory
## 1 2010 9.972917
## 2 2011 10.904167
## 3 2012 9.879167
## 4 2013 8.150000
## 5 2014 7.056250
Come per Listings, c’è un aumento nel secondo anno e una diminuzione costante negli anni successivi. Intuitivamente ha senso: nel secondo anno, ad esempio, listings è maggiore che negli altri anni (più annunci attivi) e al contempo sales è al minimo (minor numero di vendite). Logicamente, la stima del tempo teoricamente necessario per vendere tutti gli annunci sarà più alta.
-
Per identificare la variabile con maggiore variabilità, può essere opportuno confrontare i coefficienti di variazione (CV).
(Le prime 3 variabili, quelle trattate come qualitative, possono essere tranquillamente escluse da questo “esercizio” in funzione della loro equimodalità e del basso numero di modalità.
CVs <- c(sales_CV, stats_vol$CV, stats_median_p$CV, stats_listings$CV, stats_months_inv$CV)
#per sales avevamo calcolato manualmente il CV
colors <- c("green2", "cyan3", "orange2", "pink3", "cyan4")
par(las=1)
barplot(CVs, names.arg = c("Sales","Volume","Median Price","Listings","Months Inv."), col = colors,
ylim = c(0, 0.6),
main = "Confronto della Variabilità Relativa (CV)",
xlab = "Variabile quantitativa", ylab = "Coefficiente di Variazione")
Appare evidente che Volume sia la variabile più… variabile.
In ordine decrescente dunque abbiamo:
Volume
Listings
Sales
Months_Inventory
Median_Price
Un’operazione analoga può essere eseguita per visualizzare la variabile più asimmetrica.
skew_vals <- c(sales_skew, stats_vol$Skewness, stats_median_p$Skewness, stats_listings$Skewness, stats_months_inv$Skewness)
par(las=1)
barplot(skew_vals, names.arg = c("Sales","Volume","Median Price","Listings","Months Inv."), col = colors,
ylim = c(-0.4, 1),
main = "Confronto dell'asimmetria (skewness)",
xlab = "Variabile quantitativa", ylab = "Asimmetria")
abline(h = 0, col = "black", lwd = 2) #un semplice asse orizzontale
Anche qui Volume è la più asimmetrica, quantitativamente. Abbiamo un’unica variabile asimmetrica negativamente ovvero median price, che in senso assoluto è la quarta più asimmetrica.
In ordine decrescente (assoluto):
Volume
Sales
Listings
Median_Price*
Months_Inventory
Proviamo con la variabile median price.
#Dividiamo in 9 classi di "larghezza" 12000
medianP_cl <- cut(data$median_price, c(72000, 84000, 96000, 108000, 120000, 132000, 144000, 156000, 168000, 180000), dig.lab = 6)
medianP_cl_dist <- as.data.frame(dist_tab(medianP_cl))
print(medianP_cl_dist)
## dataframe.interval dataframe.freq dataframe.cum.freq dataframe.percent dataframe.cum.percent
## 1 (72000,84000] 2 2 0.83 0.83
## 2 (84000,96000] 17 19 7.08 7.92
## 3 (96000,108000] 25 44 10.42 18.33
## 4 (108000,120000] 22 66 9.17 27.50
## 5 (120000,132000] 36 102 15.00 42.50
## 6 (132000,144000] 50 152 20.83 63.33
## 7 (144000,156000] 55 207 22.92 86.25
## 8 (156000,168000] 23 230 9.58 95.83
## 9 (168000,180000] 10 240 4.17 100.00
~(Nota: per convenzione, il limite superiore di una classe è escluso, il limite inferiore è incluso. In questo caso però è l’opposto.
Ai fini pratici non cambia nulla in questo specifico scenario, ma è giusto specificarlo.
Per creare classi con il criterio convenzionale è sufficiente aggiungere il parametro right = F nella funzione cut)~
par(mar = c(5, 3.7, 3.5, 0) + 0, las=1, cex.axis= 1, mgp = c(2.5, 1, 0))
barplot(medianP_cl_dist$dataframe.freq, names.arg = medianP_cl_dist$dataframe.interval,
main = "Distribuzione di frequenza prezzo mediano (classi)",
cex.names = 0.7,
ylim = c(0, 60),
xlab = "Classi", ylab = "Frequenza (assoluta)")
Passiamo all’indice di Gini, che ricordiamo di base misura la “propensione” di una variabile qualitativa ad assumere le sue diverse modalità, andando a considerare la sua distribuzione di frequenze.
Creiamo una semplice funzione per il calcolo dell’indice (compresa normalizzazione):
gini.index<- function(x){
ni=table(x)
fi=ni/length(x)
fi2=fi^2
J = length(table(x))
gini = 1-sum(fi2)
#normalizzazione
gini.norm = gini/((J-1)/J)
return(gini.norm)
}
gini.index(medianP_cl)
## [1] 0.9521094
Se i calcoli sono corretti, abbiamo un’eterogeneità del 95,2%.
Riguardiamo un momento la distribuzione per city:
print.data.frame(city_distrib)
## city_ni city_fi city_Ni city_Fi
## Beaumont 60 0.25 60 0.25
## Bryan-College Station 60 0.25 120 0.50
## Tyler 60 0.25 180 0.75
## Wichita Falls 60 0.25 240 1.00
Avevamo individuato una distribuzione equimodale (quadrimodale), dunque un’equidistribuzione, il ché significa massima eterogeneità, e quindi indice di Gini pari a 1.
Confermiamo, per formalità:
gini.index(data$city)
## [1] 1
Iniziamo dalla probabilità di prendere una riga che riporti “Beaumont” (città).
Già intutivamente potremmo dire che sarà del 25% perché sappiamo che la distribuzione di city è quadrimodale, e ci sono 60 righe per ogni città, per un totale di 240. 60/240 = 0.25 -> 25%.
Verifichiamo come se non lo sapessimo:
Beaum_freq <- sum(data$city == "Beaumont")
Beaum_prob <- Beaum_freq/nrow(data) #casi favorevoli/casi possibili
paste0(Beaum_prob*100,"%")
## [1] "25%"
Passiamo alla probabilità di avere “luglio” (che sarà indicato come 7 nel dataset per la variabile month).
Jul_freq <- sum(data$month == "7") #sarà 20 per ogni mese
Jul_prob <- Jul_freq/nrow(data)
paste0(Jul_prob*100,"%")
## [1] "8.33333333333333%"
(Una possibilità su 12)
Infine vediamo la probabilità di avere “dicembre 2012”:
Dec_2012_freq <- sum(data$month == "12" & data$year == "2012")
#4, uno per città
Dec_2012_prob <- Dec_2012_freq/nrow(data)
paste0(Dec_2012_prob*100, "%")
## [1] "1.66666666666667%"
(Una possibilità su 60)
In questo scenario, il prezzo medio è facilmente ricavabile dal
rapporto tra i guadagni (volume) e il numero di vendite (sales).
Lo ricaviamo per ogni riga e creeremo una colonna con i risultati.
data$mean_price <- (data$volume*1000000)/data$sales
Verifichiamo se il dataset è stato correttamente aggiornato:
head(data, 10)
## city year month sales volume median_price listings months_inventory mean_price
## 1 Beaumont 2010 1 83 14.162 163800 1533 9.5 170626.5
## 2 Beaumont 2010 2 108 17.690 138200 1586 10.0 163796.3
## 3 Beaumont 2010 3 182 28.701 122400 1689 10.6 157697.8
## 4 Beaumont 2010 4 200 26.819 123200 1708 10.6 134095.0
## 5 Beaumont 2010 5 202 28.833 123100 1771 10.9 142737.6
## 6 Beaumont 2010 6 189 27.219 122800 1803 11.1 144015.9
## 7 Beaumont 2010 7 164 22.706 124300 1857 11.7 138451.2
## 8 Beaumont 2010 8 174 25.237 136800 1830 11.6 145040.2
## 9 Beaumont 2010 9 124 17.233 121100 1829 11.7 138975.8
## 10 Beaumont 2010 10 150 23.904 138500 1779 11.5 159360.0
#***Questo blocco non verrà eseguito***
#Sarebbe possibile scegliere una posizione specifica per la colonna
#Ad esempio, se avessimo voluto metterla dopo median_price:
pos <- which(names(data) == "median_price") + 1
data <- cbind(dat[, 1:pos], mean_price = data$mean_price, data[, (pos+1):ncol(data)])
#***Fine blocco non eseguibile***
La quantità di dati disponibile è in realtà relativamente limitata e
c’è un certo margine di “soggettività” nella definizione di efficacia,
ma si possono comunque fare delle considerazioni.
La più ovvia di gran lunga sarebbe quella di osservare il rapporto tra
vendite e annunci attivi: un rapporto maggiore implicherebbe una
maggiore “efficacia”.
Un parametro minore da considerare potrebbe essere il rapporto tra prezzo medio e prezzo mediano. Un prezzo medio maggiore può essere interpretato come un maggior successo delle inserzioni e una maggiore capacità di generare profitto con gli annunci più di “fascia alta” o comunque di rendere “attraenti” gli immobili, spingendo a valutazioni maggiori.
La variazione stagionale (col picco estivo e il fondo invernale) è probabilmente dovuta al semplice fatto che le persone siano più invogliate/facilitate a fare questo tipo di grandi acquisti in determinati periodi dell’anno, soprattutto considerando che la tendenza è perfettamente ciclica. Difficile immaginare abbia a che fare con un’effettiva “efficacia” degli annunci, tantomeno con variazioni così ampie.
Altre metriche sarebbero in qualche modo variazioni delle precedenti. Ad esempio, potremmo considerare il tempo stimato di vendita degli annunci attivi (months_inventory), ma ciò sarebbe comunque correlato alle vendite e agli annunci attivi (anche se non calcolato in modo diretto da un rapporto vendite/annunci.)
I guadagni per annuncio attivo nemmeno fornirebbero informazioni che non sarebbero in qualche modo ridondanti (o persino fuorvianti).
Potremmo quindi ricavare una combinazione ponderata delle prime due metriche citate, dando alla seconda un “peso” minore, e riscalare secondo alcuni fattori. Faremo in modo che ad un indice di 1 corrisponda (idealmente) un rapporto vendite/annunci “tipico” (per esempio il rapporto tra la mediana di sales e la mediana di listings) e a un rapporto prezzo medio/prezzo mediano pari a 1.
sales_to_list_ratio <- (data$sales)/(data$listings)
mean_to_median_ratio <- (data$mean_price)/(data$median_price)
sales_to_list_typ <- median(data$sales)/median(data$listings)
#normalizziamo rispetto a 1
sales_to_list_norm <- (sales_to_list_ratio)/(sales_to_list_typ)
#Definiamo un indice AEI (Ad Efficacy Index) e creiamo la colonna
data$AEI <- (0.8*sales_to_list_norm)+(0.2*mean_to_median_ratio)
-
Vediamo rapidamente le caratteristiche di questa nuova variabile:
stats_AEI <- descr(data$AEI, round.digits = 7, transpose = T)
stats_AEI
## Descriptive Statistics
## data$AEI
## N: 240
##
## Mean Std.Dev Min Q1 Median Q3 Max
## --------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
## AEI 1.1090300 0.3473404 0.6077843 0.8905339 1.0454133 1.2350649 3.0963658
##
## Table: Table continues below
##
##
##
## MAD IQR CV Skewness SE.Skewness Kurtosis N.Valid
## --------- ----------- ----------- ----------- ----------- ------------- ----------- -------------
## AEI 0.2500839 0.3396027 0.3131930 2.0813694 0.1571376 6.8318433 240.0000000
##
## Table: Table continues below
##
##
##
## Pct.Valid
## --------- -------------
## AEI 100.0000000
plot(density(data$AEI), lwd=2)
abline(v=range(data$AEI), col=2)
x <- seq(min(data$AEI), max(data$AEI), length = 100)
y <- dnorm(x, mean = mean(data$AEI), sd = sd(data$AEI))
lines(x, y, col = "cyan3", lwd = 1)
-
Infine diamo un’occhiata all’andamento di questa nuova variabile per ogni città separatamente, in un unico grafico. Essendo un’operazione un po’ meno semplice ci serviremo della libreria ggplot2 (che servirà anche per i quesiti della prossima macro-sezione).
library(ggplot2)
ggplot(data, aes(x = (as.Date(paste(year, month, "01", sep = "-"))), y = AEI, color = city)) +
geom_line() +
geom_point() +
scale_color_brewer(palette = "Set1") +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
scale_y_continuous(limits = c(0, 3.5))+
labs(title = "Andamento dell'AEI per Città", x = "Data", y = "AEI", color = "Città") +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom")
Vediamo anche con 4 grafici separati:
ggplot(data, aes(x = (as.Date(paste(year, month, "01", sep = "-"))), y = AEI, color = city)) +
geom_line() +
geom_point() +
scale_color_brewer(palette = "Set1") +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
scale_y_continuous(limits = c(0, 3.5))+
labs(title = "Andamento dell'AEI per Città", x = "Data", y = "AEI", color = "Città") +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom") +
facet_wrap(~ city, ncol = 2)
Pare che l’andamento in Bryan-College Station sia alquanto irregolare
se confrontato con le altre città, con picchi estivi assai più evidenti.
Al contrario, sembra esserci la maggiore costanza per Tyler.
Probabilmente, una buona porzione degli andamenti stagionali che abbiamo
notato in precedenza per variabili come sales, volume, listings, etc
etc… è dovuta al mercato di Bryan-College Station, soprattutto negli
ultimi 2 anni.
Vediamo l’indice anche numericamente e graficamente con un confronto
medio:
per_city_AEI <- aggregate(data$AEI ~ data$city, data = data, FUN = mean)
print.data.frame(per_city_AEI)
## data$city data$AEI
## 1 Beaumont 1.0091317
## 2 Bryan-College Station 1.3199782
## 3 Tyler 0.9269012
## 4 Wichita Falls 1.1801089
col = c("red3", "blue3", "green4", "purple3" )
par(las=1)
barplot(per_city_AEI$`data$AEI`, names.arg = per_city_AEI$`data$city`, col = col,
ylim = c(0, 1.5),
main = "Indice di efficacia medio per città nel periodo di riferimento",
xlab = "Città", ylab = "AEI")
abline(h = 0, col = "black", lwd = 2)
Bryan-College è la città dove, in media, l’indice che abbiamo calcolato risulta più alto, Tyler quella dove risulta più basso.
-
(Commento personale secondario: il cheatsheet di dplyr è oggettivamente utile ma personalmente non sono riuscito a
trovarlo super intuitivo/esplicativo.
Mi sono trovato maggiormente ad utilizzare altri manuali/guide.)
Iniziamo creando un sommario di alcuni indici principali (in ordine: minimo, massimo, deviazione standard, media, mediana) di sales, volume, median price e listings, raggruppando città e anno:
summary_city_year <- data %>%
group_by(city, year) %>%
summarise(
Sales_min = min(sales),
Sales_max = max(sales),
Sales_sd = sd(sales),
Sales_mean = round(mean(sales)),
Sales_mdn = round(median(sales)),
Vol_min = min(volume),
Vol_max = max(volume),
Vol_sd = sd(volume),
Vol_mean = mean(volume),
Vol_mdn = median(volume),
MdnP_min = min(median_price),
MdnP_max = max(median_price),
MdnP_sd = sd(median_price),
MdnP_mean = round(mean(median_price)),
MdnP_mdn = round(median(median_price)),
List_min = min(listings),
List_max = max(listings),
List_sd = sd(listings),
List_mean = round(mean(listings)),
List_mdn = round(median(listings))
)
print.data.frame(summary_city_year, digits = 4)
## city year Sales_min Sales_max Sales_sd Sales_mean Sales_mdn Vol_min Vol_max Vol_sd Vol_mean Vol_mdn MdnP_min MdnP_max MdnP_sd MdnP_mean MdnP_mdn List_min List_max List_sd List_mean List_mdn
## 1 Beaumont 2010 83 202 36.92 156 157 14.162 28.83 4.954 22.65 23.30 121100 163800 13354 133117 128400 1533 1857 101.91 1731 1756
## 2 Beaumont 2011 108 177 22.66 144 148 15.489 28.48 4.301 21.10 21.38 111100 144600 9603 125642 126900 1596 1845 74.67 1748 1764
## 3 Beaumont 2012 110 218 28.39 172 176 13.496 31.15 4.922 24.47 25.32 110000 134700 7973 126533 128300 1570 1765 54.56 1691 1694
## 4 Beaumont 2013 140 273 37.73 201 202 20.342 42.03 6.437 30.31 30.13 121100 147000 7785 132400 132750 1534 1708 54.92 1640 1657
## 5 Beaumont 2014 148 262 36.49 214 210 18.077 41.19 7.050 32.13 33.26 106700 142400 9835 132250 134150 1500 1672 57.37 1587 1590
## 6 Bryan-College Station 2010 89 286 70.75 168 153 15.151 47.45 10.818 28.73 26.29 148300 165300 5474 153533 151800 1298 1984 165.16 1562 1563
## 7 Bryan-College Station 2011 94 284 62.19 167 148 15.242 47.78 10.313 28.93 25.88 146700 157300 3709 151417 150300 1362 1840 156.09 1606 1572
## 8 Bryan-College Station 2012 115 296 74.28 197 161 19.789 55.45 13.490 35.36 30.07 140700 170000 7096 153567 153700 1442 1834 152.98 1610 1576
## 9 Bryan-College Station 2013 125 402 95.85 238 188 18.977 76.12 19.541 45.12 35.19 146900 167300 5429 159392 159900 1057 1750 228.36 1406 1424
## 10 Bryan-College Station 2014 152 403 86.69 260 246 29.457 83.55 17.971 52.81 48.44 155300 180000 7776 169533 170900 882 1271 127.44 1106 1096
## 11 Tyler 2010 155 316 48.98 228 229 24.411 49.91 8.394 36.35 34.27 129400 143100 4782 135175 134450 2727 3296 226.43 3051 3099
## 12 Tyler 2011 143 313 49.62 239 247 21.050 52.32 9.406 38.55 39.77 120600 152600 8505 136217 134650 2720 3266 184.60 3070 3098
## 13 Tyler 2012 169 322 46.40 264 276 25.386 57.39 10.230 44.01 45.36 124200 152100 7983 139250 138800 2633 3072 122.95 2910 2918
## 14 Tyler 2013 197 369 53.05 287 288 32.083 63.05 10.326 50.32 51.35 132400 155600 6726 146100 146100 2500 2998 159.72 2824 2884
## 15 Tyler 2014 238 423 56.85 332 340 36.916 80.81 12.761 59.60 63.17 130700 161600 8543 150467 151900 2272 2875 172.17 2670 2716
## 16 Wichita Falls 2010 89 167 26.62 123 123 8.951 20.88 4.066 14.97 15.01 86400 119200 10361 98942 98350 904 1028 45.17 959 957
## 17 Wichita Falls 2011 79 135 19.76 106 111 8.166 15.28 2.515 12.05 13.21 73800 113600 10632 98142 101500 844 1052 58.59 975 982
## 18 Wichita Falls 2012 90 132 14.25 112 116 9.695 17.79 2.662 13.23 12.78 82100 118800 12347 100958 99900 801 941 41.22 896 907
## 19 Wichita Falls 2013 79 159 26.00 121 122 9.666 19.06 3.106 14.85 15.01 85900 121300 10383 105000 103600 743 923 52.91 841 847
## 20 Wichita Falls 2014 89 150 21.09 117 111 9.626 18.67 3.131 14.54 13.90 90000 135300 12444 105675 104250 746 973 73.05 877 884
-
A questo punto potremmo visualizzare ogni elemento del sommario in un grafico (in questo caso usiamo quello a barre), come ad esempio la media delle vendite:
ggplot(summary_city_year, aes(x=year, y=Sales_mean, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media vendite per città ogni anno", x= "Anno", y="Vendite medie", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_y_continuous(limits = c(0, 350)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")
-
O la mediana del numero di annunci attivi:
ggplot(summary_city_year, aes(x=year, y=List_mdn, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Mediana del numero di annunci attivi per città ogni anno", x= "Anno", y="Mediana annunci attivi", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_y_continuous(limits = c(0, 3100)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")
-
O la media del prezzo mediano:
ggplot(summary_city_year, aes(x=year, y=MdnP_mean, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media del prezzo mediano per città ogni anno", x= "Anno", y="Media prezzo mediano", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_y_continuous(limits = c(0, 200000)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")
-
O la deviazione standard degli incassi:
ggplot(summary_city_year, aes(x=year, y=Vol_sd, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Deviazione standard degli incassi per città ogni anno", x= "Anno", y="Deviazione standard incassi", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")
-
Possiamo provare a fare la stessa cosa ma raggruppando città e mese.
summary_city_month <- data %>%
group_by(city, month) %>%
summarise(
Sales_min = min(sales),
Sales_max = max(sales),
Sales_sd = sd(sales),
Sales_mean = round(mean(sales)),
Sales_mdn = round(median(sales)),
Vol_min = min(volume),
Vol_max = max(volume),
Vol_sd = sd(volume),
Vol_mean = mean(volume),
Vol_mdn = median(volume),
MdnP_min = min(median_price),
MdnP_max = max(median_price),
MdnP_sd = sd(median_price),
MdnP_mean = round(mean(median_price)),
MdnP_mdn = round(median(median_price)),
List_min = min(listings),
List_max = max(listings),
List_sd = sd(listings),
List_mean = round(mean(listings)),
List_mdn = round(median(listings))
)
-
Vediamo di nuovo qualche grafico. Media delle vendite:
ggplot(summary_city_month, aes(x=month, y=Sales_mean, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media vendite per città nei singoli mesi", x= "Mese", y="Vendite medie", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12, labels = month.name) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")
Proviamo anche a visualizzare separatamente per le 4 città:
ggplot(summary_city_month, aes(x=month, y=Sales_mean, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media vendite per città nei singoli mesi", x= "Mese", y="Vendite medie", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")+
facet_wrap(~ city, ncol=2)
-
Vediamo anche la media degli annunci per mese, direttamente separata per città:
ggplot(summary_city_month, aes(x=month, y=List_mean, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media del numero di annunci attivi per città nei singoli mesi", x= "Anno", y="Media annunci attivi", fill="Città") +
scale_fill_brewer(palette = "Set1") +
theme(legend.position = "none") +
scale_x_continuous(breaks = 1:12) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")+
facet_wrap(~ city, ncol=2)
Da notare che per Bryan-College Station sembra sia la primavara il periodo con più annunci attivi, in controtendenza con le altre città dove è l’estate ad avere il picco.
Vediamo infine con la media del prezzo mediano, sempre già separata per città:
ggplot(summary_city_month, aes(x=month, y=MdnP_mean, fill=city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media del prezzo mediano per città nei singoli mesi", x= "Mese", y="Media prezzo mediano", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 12),
legend.position = "bottom")+
facet_wrap(~ city, ncol=2)
Potremmo continuare per eternità con tante combinazioni, ma fermiamoci qui.
-
Creiamo subito i boxplot con ggplot:
ggplot(data, aes(x=city, y=median_price))+
geom_boxplot(fill="lightblue1",
outlier.color = "red",
outlier.size = 3)+
geom_jitter(width = 0.27, size = 1.1,
aes(color = median_price))+
scale_y_continuous(limits = c(0, NA))+
stat_summary(fun = mean, geom = "point", shape = 16,
size = 2.5, colour = "orange")+
labs(x= "Città", y="Prezzo mediano")
Nota: outlier(s) indicati con puntino rosso, jitter points in gradazioni di blu aggiunti per avere ulteriore rappresentazione della distribuzione, punto di media indicato con puntino arancione.
Beaumont, Bryan-College Station e Wichita Falls presentano un singolo report con valore considerato outlier, con un dato del prezzo mediano significativamente superiore a tutti gli altri report. Tyler non sembra presentare outliers.
Appare evidente che Bryan-College Station abbia i prezzi più alti di tutte le città, seguita da Tyler, Beaumont e infine Wichita Falls con i prezzi di gran lunga più bassi, come avevamo già intravisto nelle sezioni precedenti.
Wichita Falls in generale presenta relativamente poche vendite, pochi
annunci attivi e prezzi bassi.
Abbiamo però visto un AEI in seconda posizione (sezione 8 del “capitolo”
precedente), dunque non c’è necessariamente motivo di immaginare la
presenza di una qualche “crisi”, più realisticamente si tratta
semplicemente di un mercato più stazionario e in “piccola scala”.
Bryan-College Station sembra avere il mercato “più attraente”, in
ogni caso. Pur non presentando il maggior numero di annunci attivi o di
vendite assolute, le vendite percentuali sono le maggiori (Tyler ad
esempio porta molti più annunci a fronte di un numero di vendite non di
molto superiore) e i prezzi sono i più alti. L’indice AEI che avevamo
calcolato era il più alto.
Buona parte di questi risultati però è probabilmente dovuta agli ultimi
2 anni di misurazione, con un rapporto vendite/annunci attivi
notevolmente elevato rispetto agli altri anni e in generale alle altre
città.
In particolare si è potuto notare che è il numero di annunci attivi ad
essere calato in modo più evidente specificamente per questa città, a
differenza del numero di vendite che è aumentato in modo abbastanza
coerente tra le città (escludendo Whichita Falls, che come già accennato
è rimasta prossoché stazionaria).
In termini di guadagno assoluto, comunque, Tyler è al primo posto. Lo vedremo meglio nella prossima sezione.
-
Proviamo a rappresentare con qualche grafico “ibrido”. In particolare, utilizziamo una combinazione di boxplot azzurro e violinplot dimezzato rosa, con l’aggiunta di jitter points arancioni e un trattino di media viola. Per il momento limitiamoci alle città:
library(PupillometryR)
#utilizziamo questo pacchetto per aggiungere una piccola funzione specifica
#geom_flat_violin
ggplot(data, aes(x=city, y=volume))+
geom_flat_violin(position = position_nudge(x = .2, y = 0),
adjust = .5, trim = T, fill="pink2") +
geom_jitter(width = 0.18, size = 1.15, aes(color = volume)) +
scale_color_gradient(low = "orange4", high = "orange")+
geom_boxplot(width = .15, outlier.color = "red", fill="lightblue2")+
stat_summary(fun = mean, geom = "crossbar",
width=0.06, colour = "purple")+
labs(title = "Distribuzione dei guadagni per città",
x= "Città", y="Guadagni (milioni di dollari)")+
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 13))+
scale_y_continuous(limits = c(0, NA))
## Warning: Using the `size` aesthetic with geom_polygon was deprecated in ggplot2 3.4.0.
## ℹ Please use the `linewidth` aesthetic instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Proviamo ora ad integrare gli anni.
ggplot(data, aes(x=city, y=volume))+
geom_flat_violin(position = position_nudge(x = .2, y = 0),
adjust = .5, trim = T, fill="pink2") +
geom_jitter(width = 0.18, size = 1.15, aes(color = volume)) +
scale_color_gradient(low = "orange4", high = "orange")+
geom_boxplot(width = .15, outlier.color = "red", fill="lightblue2")+
stat_summary(fun = mean, geom = "crossbar",
width=0.06, colour = "purple")+
labs(title = "Distribuzione dei guadagni per città negli anni",
x= "Città", y="Guadagni (milioni di dollari)")+
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 13))+
scale_y_continuous(limits = c(0, NA))+
facet_wrap(~ year, ncol=2, scales = "free")
—
library(ggpubr) #utilizzo della funzione ggarrange per unire grafici
year_boxplot_vol <- function(data_subset, year) {
ggplot(data_subset, aes(x = city, y = volume)) +
geom_flat_violin(position = position_nudge(x = .2, y = 0),
adjust = .5, trim = TRUE, fill = "pink2") +
geom_jitter(width = 0.18, size = 1.15, aes(color = volume)) +
scale_color_gradient(low = "orange4", high = "orange") +
geom_boxplot(width = .15, outlier.color = "red", fill = "lightblue2") +
stat_summary(fun = mean, geom = "crossbar",
width = 0.06, colour = "purple") +
labs(title = paste("Distribuzione dei guadagni per città -", year),
x = "Città", y = "Guadagni (milioni di dollari)") +
theme(plot.title = element_text(hjust = 0.5, size = 10, face = "bold"),
axis.title = element_text(size = 10)) +
scale_y_continuous(limits = c(0, NA))
}
#Per chiarezza, la funzione accetta un subset dei dati (in questo caso
#relativo a un anno) e restituisce il grafico
#Sarà richiamata per tutti gli anni fino ad ottenere i differenti
#grafici tutti in un blocco
p1 <- year_boxplot_vol(subset(data, year == 2010), "2010")
p2 <- year_boxplot_vol(subset(data, year == 2011), "2011")
p3 <- year_boxplot_vol(subset(data, year == 2012), "2012")
p4 <- year_boxplot_vol(subset(data, year == 2013), "2013")
p5 <- year_boxplot_vol(subset(data, year == 2014), "2014")
#applicazione della funzione con ogni subset
ggarrange(p1, p2, p3, p4, p5, ncol = 2, nrow = 3)
#combinazione dei 5 grafici
In questo scenario però il risultato non è necessariamente migliore.
—
In ogni caso, si nota che Tyler abbia in genere i guadagni maggiori. Bryan-College Station ha dei guadagni elevati ma più “instabili”, con una fascia di distribuzione che si estende generalmente di più, con un massimo superiore a quello di Tyler in alcuni anni. Questo è coerente con la forte variabilità stagionale in termini di vendite che abbiamo visto in sezioni precedenti, superiore per B-C.S. rispetto alle altre città.
Wichita Falls ha invece una fascia molto ristretta in termini di guadagni, anche questo è coerente con il mercato “stazionario” e in piccola scala visto in precedenza, con prezzi modesti e numeri di vendite poco variabili tra stagioni e anni.
Non c’è molto da segnalare per Beaumont, se non nel 2012 dove la fascia è estremamente concentrata (alcuni valori vengono considerati addirittura outliers).
C’è da dire, comunque, che questo tipo di dataset presenta un numero di “osservazioni” relativamente limitato.
-
#Arrivato a questo punto mi accorgo che in precedenza ho inconsciamente utilizzato geom_bar impostando manualmente l'argomento stat="identity" invece che usare direttamente geom_col che lo usa di default.
#Pur funzionando correttamente, capisco che formalmente sia un po' improprio.
ggplot(data, aes(x=month, y=sales, fill=city)) +
geom_col(position = "stack") +
labs(title = "Totale delle vendite per città nei singoli mesi", x= "Mese", y="Vendite (unità)", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12, labels = month.name) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 17, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom")
-
Ricreiamo lo stesso grafico ma normalizzato (sufficiente cambiare l’argomento di geom_col):
ggplot(data, aes(x=month, y=sales, fill=city)) +
geom_col(position = "fill") +
labs(title = "Totale delle vendite per città nei singoli mesi", x= "Mese", y="Vendite (frazione relativa)", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12, labels = month.name) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 17, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom")
-
Adesso invece proviamo ad inserire anche l’anno come variabile di rappresentazione, usando nuovamente ggarrange.
year_barplot_mSal <- function(data_subset, year) {
ggplot(data_subset, aes(x=month, y=sales, fill=city)) +
geom_col(position = "stack") +
labs(title = paste("Totale delle vendite per città nei singoli mesi -",
year),
x= "Mese",
y="Vendite (unità)", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12, labels = month.name) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.title = element_text(size = 13),
legend.position = "bottom")
}
bar_p1 <- year_barplot_mSal(subset(data, year == 2010), "2010")
bar_p2 <- year_barplot_mSal(subset(data, year == 2011), "2011")
bar_p3 <- year_barplot_mSal(subset(data, year == 2012), "2012")
bar_p4 <- year_barplot_mSal(subset(data, year == 2013), "2013")
bar_p5 <- year_barplot_mSal(subset(data, year == 2014), "2014")
ggarrange(bar_p1, bar_p2, bar_p3, bar_p4, bar_p5, ncol = 1, nrow = 5)
-
Proviamo infine a rifare lo stesso ma con normalizzazione. Questa volta proviamo più semplicemente con facet_grid:
ggplot(data, aes(x=month, y=sales, fill=city)) +
geom_col(position = "fill") +
labs(title = "Totale delle vendite per città nei singoli mesi", x= "Mese", y="Vendite (frazione relativa)", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12, labels = month.name) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 17, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom")+
facet_grid(year ~ .)
Per curiosità, proviamo a fare lo stesso con gli anni come colonne:
ggplot(data, aes(x=month, y=sales, fill=city)) +
geom_col(position = "fill") +
labs(title = "Totale delle vendite per città nei singoli mesi", x= "Mese", y="Vendite (frazione relativa)", fill="Città") +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(breaks = 1:12) +
scale_y_continuous(limits = c(0, NA)) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 11),
legend.position = "bottom")+
facet_grid(~ year)
Non è grandiosamente elegante… ma ce lo faremo bastare.
-
Per questa operazione ci concentreremo sulla variabile sales. Oltre
alla linechart aggiungeremo delle linee tratteggiate che definiscano
vagamente la “tendenza”.
Osserviamo prima tutto assieme (anche se sarà molto confusionario):
ggplot(data, aes(x = (as.Date(paste(year, month, "01", sep = "-"))),
y = sales, color = city))+
geom_line()+
geom_point(size=1.5)+
geom_text(aes(label = sales), vjust = -0.7, size = 2.5)+
geom_smooth(method = "loess", span = 0.3, se = F,
linetype = "dotdash", linewidth = 0.5)+
labs(title = "Vendite nel tempo tra le città",
x = "Anno",
y = "Vendite (unità)",
color = "Città") +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
scale_y_continuous(limits= c(0, NA))+
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom",
panel.grid.major = element_line(linewidth=0.30, linetype = 'twodash',
color = "grey60"),
panel.grid.minor = element_line(linewidth = 0.4, linetype = 'dotted',
color = "grey70"))+
scale_color_brewer(palette = "Set1")
-
Focus sulle linee di tendenza, di cui un set che segue con maggiore precisione l’andamento dei singoli dati e un altro meno visibile che lo definisce più genericamente e linearmente:
ggplot(data, aes(x = (as.Date(paste(year, month, "01", sep = "-"))),
y = sales, color = city))+
geom_smooth(method = "loess", span = 0.15, se = F,
linetype = "twodash", linewidth = 1)+
geom_smooth(method = "loess", span = 0.8, se = F,
linetype = "dotted", linewidth = 0.8)+
labs(title = "Vendite nel tempo tra le città",
x = "Anno",
y = "Vendite (unità)",
color = "Città") +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
scale_y_continuous(limits= c(0, NA))+
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom",
panel.grid.major = element_line(linewidth=0.30, linetype = 'twodash',
color = "grey60"),
panel.grid.minor = element_line(linewidth = 0.4, linetype = 'dotted',
color = "grey70"))+
scale_color_brewer(palette = "Set1")
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
Si nota come Tyler si confermi la città con le maggiori vendite
(superata solo per brevi periodi da Bryan-College Station).
Bryan-College Station ha un andamento particolarmente variabile,
portandolo talvolta al di sotto di Beaumont.
Whichita Falls è la più stabile e l’unica che non sembra avere una
crescita sostanziale negli anni.
Si nota anche che i picchi (sia massimi che minimi) non sono sempre
perfettamente coincidenti tra città e anni.
-
Aggiungiamo la funzione geom_text_repel del pacchetto ggrepel per mitigare il problema della sovrapposizione delle etichette numeriche nella visualizzazione dell’intero periodo.
library(ggrepel)
Vediamo anche i grafici separatamente, limitandoci ad usare facet_wrap:
ggplot(data, aes(x = (as.Date(paste(year, month, "01", sep = "-"))),
y = sales, color = city))+
geom_line(linewidth=0.35)+
geom_point(size=1.2)+
geom_text_repel(aes(label = sales), size = 2.0, nudge_y = 2,
max.overlaps = 30, direction = "both",
max.iter = 50000, max.time = 1.5)+
geom_smooth(method = "loess", span = 0.3, se = F,
linetype = "dotted", linewidth = 0.3)+
labs(title = "Vendite nel tempo tra le città",
x = "Anno",
y = "Vendite (unità)",
color = "Città") +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
scale_y_continuous(limits= c(0, NA))+
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title = element_text(size = 14),
legend.position = "bottom",
panel.grid.major = element_line(linewidth=0.30, linetype = 'twodash',
color = "grey60"),
panel.grid.minor = element_line(linewidth = 0.4, linetype = 'dotted',
color = "grey70"))+
scale_color_brewer(palette = "Set1")+
facet_wrap(~ city)
-
Creiamo grafici differenti per i 5 anni, per visualizzare con maggiore dettaglio:
year_lineplot_mSal <- function(data_subset, year) {
ggplot(data_subset, aes(x=month, y=sales, color=city)) +
geom_line(linewidth = 0.5)+
geom_point(size=1.3)+
geom_text_repel(aes(label = sales), size = 2.5,
max.overlaps = 20, direction = "y",
max.iter = 30000, max.time = 1.2)+
geom_smooth(method = "loess", span = 0.7, se = F,
linetype = "dotted", linewidth = 0.55)+
labs(title = paste("Andamento delle vendite -",
year),
x = "Mese",
y = "Vendite (unità)",
color = "Città") +
scale_x_continuous(breaks = 1:12, labels = month.abb) +
scale_y_continuous(limits = c(0, NA))+
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 10),
legend.title = element_text(size = 9),
legend.text = element_text(size = 8),
legend.key.size = unit(0.4, 'cm'),
legend.key.height = unit(0.4, 'cm'),
legend.key.width = unit(0.4, 'cm'),
legend.position = "bottom",
panel.grid.major = element_line(linewidth=0.30, linetype = 'twodash',
color = "grey60"),
panel.grid.minor = element_line(linewidth = 0.4, linetype = 'dotted',
color = "grey70"))+
scale_color_brewer(palette = "Set1")
}
line_p1 <- year_lineplot_mSal(subset(data, year == 2010), "2010")
line_p2 <- year_lineplot_mSal(subset(data, year == 2011), "2011")
line_p3 <- year_lineplot_mSal(subset(data, year == 2012), "2012")
line_p4 <- year_lineplot_mSal(subset(data, year == 2013), "2013")
line_p5 <- year_lineplot_mSal(subset(data, year == 2014), "2014")
-
-
Visualizziamo, a due a due, confronti tra un anno e il successivo con ggarrange:
-
library(RColorBrewer)
-
Concludiamo visualizzando grafici per città invece che per anno, facendo anche in modo di indicare i valori massimi per ogni anno (nota: idea iniziale suggerita dall’AI, poi opportunamente personalizzata):
city_lineplot_mSal <- function(data_subset, city) {
max_points <- data_subset %>%
group_by(year) %>%
summarize(max_sales = max(sales), .groups = 'drop') %>%
left_join(data_subset, by = c("year", "max_sales" = "sales"))
ggplot(data_subset, aes(x=month, y=sales, color=factor(year))) +
geom_line(linewidth = 0.45)+
geom_point(size=1.3)+
geom_point(data = max_points, aes(x = month, y = max_sales),
shape = 17, size = 3) +
geom_text_repel(data = max_points,
aes(x = month, y = max_sales,
label = max_sales),
max.overlaps = 20, direction = "both",
max.iter = 30000, max.time = 1.2,
size = 3.8, fontface= "bold")+
geom_smooth(method = "loess", span = 0.7, se = F,
linetype = "dotted", linewidth = 0.3)+
labs(title = paste("Andamento delle vendite -",
city),
x = "Mese",
y = "Vendite (unità)",
color = "Anno") +
scale_x_continuous(breaks = 1:12, labels = month.abb) +
scale_y_continuous(limits = c(0, NA))+
theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 10),
legend.title = element_text(size = 9),
legend.text = element_text(size = 8),
legend.key.size = unit(0.4, 'cm'),
legend.key.height = unit(0.4, 'cm'),
legend.key.width = unit(0.4, 'cm'),
legend.position = "bottom",
panel.grid.major = element_line(linewidth=0.30, linetype = 'twodash',
color = "grey60"),
panel.grid.minor = element_line(linewidth = 0.4, linetype = 'dotted',
color = "grey70"))+
scale_color_manual(values = brewer.pal(7, "YlOrRd")[3:7])
}
c_line_p1 <- city_lineplot_mSal(subset(data, city == "Beaumont"), "Beaumont")
c_line_p2 <- city_lineplot_mSal(subset(data, city == "Bryan-College Station"), "Bryan-College Station")
c_line_p3 <- city_lineplot_mSal(subset(data, city == "Tyler"), "Tyler")
c_line_p4 <- city_lineplot_mSal(subset(data, city == "Wichita Falls"), "Wichita Falls")
E con questo siamo giunti al termine di quest’analisi descrittiva.
Tra i punti salienti abbiamo:
-Mercato in leggera regressione nel 2011, ma in discreta crescita negli anni successivi.
-Tyler è la città col maggior mercato, maggiori vendite e guadagni ma anche tantissimi annunci attivi, Whichita Falls quella col mercato più stabile e in piccola scala.
-Bryan-College Station è la città col maggior rapporto percentuale di vendite/annunci, i prezzi generalmente più alti (di poco più di Tyler) e un mercato assai variabile.
-I prezzi mediani sono sensibilmente aumentati negli anni, generalmente.
-Il numero di annunci attivi ha avuto picco nel 2011 ed è sceso
rapidamente negli anni successivi,
mentre le vendite sono aumentate.
-R Markdown è un po’ buggato (ok questo non è rilevante).
The end.