Come primo passo per il nostro progetto andiamo a caricare il dataset contenente i dati relativi alla vendita di immobili in Texas.
head(df)
## city year month sales volume median_price listings months_inventory
## 1 Beaumont 2010 1 83 14.162 163800 1533 9.5
## 2 Beaumont 2010 2 108 17.690 138200 1586 10.0
## 3 Beaumont 2010 3 182 28.701 122400 1689 10.6
## 4 Beaumont 2010 4 200 26.819 123200 1708 10.6
## 5 Beaumont 2010 5 202 28.833 123100 1771 10.9
## 6 Beaumont 2010 6 189 27.219 122800 1803 11.1
str(df)
## '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 ...
Come possiamo notare il dataset è composto da :
City : variabile qualitativa su scala nominale, analisi : distribuzione di frequenze
year : variabile quantitativa discreta, analisi : serie temporali,distribuzione di frequenze
month : variabile quantitativa discreta , analisi : serie temporali,distribuzioni di frequenze
sales : variabile quantitativa discreta, analisi: statistiche descrittive (indici di posizione, variabilità , forma)
volume : variabile quantitativa continua, analisi: statistiche descrittive (indici di posizione, variabilità , forma)
median_price variabile quantitativa continua, analisi : statistiche descrittive (indici di posizione, variabilità , forma)
listings variavile quantitativa discreta, analisi : statistiche descrittive(indici di posizione,variabilità ,forma)
months_inventory : variabile quantitativa continua, analisi : statistiche descrittive(indici di posizione, variabilità , forma)
Le variabili year e month sono indicative del tempo, potremmo cambiarle in variabili qualitative su scala ordinale, ma visto che sono variabile quantitative discrete e sono già ordinate, possiamo usarle direttamente per le nostre serie storiche, essendo anche che sono entrambe variabili quantitative discrete non ha molto senso andare a calcolare le statistiche descrittive per queste due variabili, ma ha più senso invece andare a calcolare la distribuzione di frequenze.
Andiamo adesso a calcolare gli indici di posizione, variabilità e forma per tutte le variabili numeriche.
Sales :
# Statistiche descrittive per la variabile sales :
summary(df$sales)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 79.0 127.0 175.5 192.3 247.0 423.0
# Deviazione standard :
sd(df$sales)
## [1] 79.65111
# Range :
max(df$sales) - min(df$sales)
## [1] 344
# Range interquartile :
IQR(df$sales)
## [1] 120
# Coefficiente di variazione :
cv = function(x){
return(sd(x)/mean(x)*100)
}
cv(df$sales)
## [1] 41.42203
# Asimmetria :
skewness(df$sales)
## [1] 0.718104
# Curtosi :
kurtosis(df$sales) - 3
## [1] -0.3131764
Volume :
# Statistiche descrittive per la variabile volume :
summary(df$volume)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 8.166 17.660 27.062 31.005 40.893 83.547
# Deviazione standard :
sd(df$volume)
## [1] 16.65145
# Range :
max(df$volume) - min(df$volume)
## [1] 75.381
# Range interquartile :
IQR(df$volume)
## [1] 23.2335
# Coefficiente di variazione :
cv = function(x){
return(sd(x)/mean(x)*100)
}
cv(df$volume)
## [1] 53.70536
# Asimmetria :
skewness(df$volume)
## [1] 0.884742
# Curtosi :
kurtosis(df$volume) - 3
## [1] 0.176987
Median_price :
# Statistiche descrittive per la variabile median_price :
summary(df$median_price)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 73800 117300 134500 132665 150050 180000
# Deviazione standard :
sd(df$median_price)
## [1] 22662.15
# Range :
max(df$median_price) - min(df$median_price)
## [1] 106200
# Range interquartile :
IQR(df$median_price)
## [1] 32750
# Coefficiente di variazione :
cv = function(x){
return(sd(x)/mean(x)*100)
}
cv(df$median_price)
## [1] 17.08218
# Asimmetria :
skewness(df$median_price)
## [1] -0.3645529
# Curtosi :
kurtosis(df$median_price) - 3
## [1] -0.6229618
Listings :
# Statistiche descrittive per la variabile listings :
summary(df$listings)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 743 1026 1618 1738 2056 3296
# Deviazione standard :
sd(df$listings)
## [1] 752.7078
# Range :
max(df$listings) - min(df$listings)
## [1] 2553
# Range interquartile :
IQR(df$listings)
## [1] 1029.5
# Coefficiente di variazione :
cv = function(x){
return(sd(x)/mean(x)*100)
}
cv(df$listings)
## [1] 43.30833
# Asimmetria :
skewness(df$listings)
## [1] 0.6494982
# Curtosi :
kurtosis(df$listings) - 3
## [1] -0.79179
Andiamo a vedere le distribuzioni di frequenze per le variabili city,year e month :
City :
N = dim(df)[1]
ni = table(df$city)
fi = table(df$city)/N
Ni = cumsum(ni)
Fi = Ni/N
cbind(ni,fi,Ni,Fi)
## ni fi Ni 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
Year :
N = dim(df)[1]
ni = table(df$year)
fi = table(df$year)/N
Ni = cumsum(ni)
Fi = Ni/N
cbind(ni,fi,Ni,Fi)
## ni fi Ni Fi
## 2010 48 0.2 48 0.2
## 2011 48 0.2 96 0.4
## 2012 48 0.2 144 0.6
## 2013 48 0.2 192 0.8
## 2014 48 0.2 240 1.0
Month :
N = dim(df)[1]
ni = table(df$month)
fi = table(df$month)/N
Ni = cumsum(ni)
Fi = Ni/N
cbind(ni,fi,Ni,Fi)
## ni fi Ni Fi
## 1 20 0.08333333 20 0.08333333
## 2 20 0.08333333 40 0.16666667
## 3 20 0.08333333 60 0.25000000
## 4 20 0.08333333 80 0.33333333
## 5 20 0.08333333 100 0.41666667
## 6 20 0.08333333 120 0.50000000
## 7 20 0.08333333 140 0.58333333
## 8 20 0.08333333 160 0.66666667
## 9 20 0.08333333 180 0.75000000
## 10 20 0.08333333 200 0.83333333
## 11 20 0.08333333 220 0.91666667
## 12 20 0.08333333 240 1.00000000
Sales : Per le vendite notiamo una media di 192.3 , e una mediana di 175.5 , questo indica che la distribuzione dei dati tende a concentrarsi verso il basso ovvero valori minori di 175.5, ma non si discosta troppo dalla media. Per la variabilità notiamo che è di 79.65 che suggerisce una discreta dispersione dei dati attorno alla media, il range è 344 mentre il range interquartile è di 120, mostrando una certa variabilità ma non eccessiva Forma: La skewness è 0.72, il che indica una lieve asimmetria positiva (distribuzione con una coda più lunga a destra). La curtosi è -0.31 , il che suggerisce che la distribuzione è leggermente più piatta rispetto alla distribuzione normale, con meno valori estremi.
Volume: Indici di posizione: La media è 31.005 e la mediana è 27.062, il che indica che il volume delle vendite tende a concentrarsi intorno ai valori medio-bassi. Variabilità : La deviazione standard è 16.65, che suggerisce una buona variabilità dei dati. Il range delle vendite è 75.38, mentre l’IQR è 23.23, indicando una discreta dispersione dei valori attorno alla mediana. Forma: La skewness è 0.88, che segnala una forte asimmetria positiva. La curtosi è 0.18, il che indica una distribuzione leptocurtica (più appuntita), suggerendo una maggiore concentrazione dei dati attorno alla media rispetto a una distribuzione normale.
Median_price : Indici di posizione: La media del median_price è 132665 e la mediana è 134500, mostrando una distribuzione abbastanza simmetrica attorno alla media, con una leggera differenza tra media e mediana. Variabilità : La deviazione standard di 22662.15 mostra una discreta variabilità del median_price. Il range è 106200, mentre l’IQR è 32750, indicando che il median_price può variare in modo significativo tra le diverse osservazioni. Forma: La skewness è -0.36, che indica una leggera asimmetria negativa (distribuzione con una coda più lunga a sinistra). La curtosi è -0.62, suggerendo che la distribuzione è platicurtica (più piatta rispetto alla normale).
Listings : Indici di posizione: La media è 1738 e la mediana è 1618, con una leggera differenza tra media e mediana, che potrebbe indicare una distribuzione leggermente asimmetrica. Variabilità : La deviazione standard di 752.71 suggerisce una variabilità significativa, con un range di 2553 e un IQR di 1029.5, indicando che il numero di inserzioni può variare in modo abbastanza ampio. Forma: La skewness è 0.65, suggerendo un’asimmetria positiva (una coda più lunga a destra). La curtosi è -0.79 , indicando una distribuzione più piatta rispetto alla normale.
Per ognuna di queste variabili è possibile osservere delle distribuzioni equamente distribuite per ogni modalità :
City : i dati sono equamente distribuiti in ognuna delle 4 città , ognuna con il 25% delle osservazioni
Year : anche la distribuzione degli anni è uniforme con un numero di 48 osservazioni per ogni anno
Month : ogni mese ha una distribuzione di 20 osservazioni, anche qui questo indica che la distribuzione è uniforme con un 8.33% delle osservazioni per ogni mese.
Dalle statistiche descrittive viste in precedenza possiamo determinare qual’è la variabile con la più alta variabilità , ed anche la variabile più asimmetrica.
Per identificare la variabile che ha la variabilità più alta non dobbiamo considerare le deviazioni standard di ogni variabile, bensì il coefficiente di variazione, perchè esso ci fornisce un indice relativo, ovvero non influenzato dalla scala di valori della variabile, anche perchè le variabili del nostro dataset differiscono molto nelle loro medie, perchè su scale di valori differenti.
Mentre per identificare la variabile più asimmetrica dobbiamo guardare al valore assoluto degli indici di assimmetria di ogni variabile e determinare qual’è il maggiore.
La variabile con la più alta variabilità è la variabile volume che presenta un coefficiente di variazione di 53.7.
La variabile con l’indice di asimmetria più alto è sempre la variabile volume con una skewness di 0.88 che indica una forte asimmetria positiva, ovvero con una distribuzione che tende maggiormente a valori bassi, creando così una lunga coda a destra della distribuzione.
Prendiamo in considerazione la variabile sales e dividiamola in classi, successivamente andiamo a calcolare la distribuzione di frequenze e osserviamo il tutto tramite un grafico a barre, infine andiamo a calcolare l’indice di eterogeneità di Gini.
# dividiamo in classi la variabile sales
sales_cl = cut(df$sales,breaks = 8)
# costruiamo la distribuzione di frequenze
N = dim(df)[1]
ni = table(sales_cl)
fi = table(sales_cl)/N
Ni = cumsum(ni)
Fi = Ni/N
cbind(ni,fi,Ni,Fi)
## ni fi Ni Fi
## (78.7,122] 50 0.20833333 50 0.2083333
## (122,165] 60 0.25000000 110 0.4583333
## (165,208] 46 0.19166667 156 0.6500000
## (208,251] 25 0.10416667 181 0.7541667
## (251,294] 29 0.12083333 210 0.8750000
## (294,337] 16 0.06666667 226 0.9416667
## (337,380] 10 0.04166667 236 0.9833333
## (380,423] 4 0.01666667 240 1.0000000
# Grafico a barre della distribuzione di frequenze della variabile sales_cl :
ggplot(df,aes(x=sales_cl))+
geom_bar(fill="lightblue",color = "black")+
labs(title = "Distribuzione delle vendite in classi di vendita",
x = "Classi di vendite",
y = "Frequenza")
# Calcolo dell'indice di eterogeneità di Gini :
gini.index = function(x){
x = sort(x)
N = length(x)
ni = table(x)
fi = ni/N
fi2 = fi^2
J = length(table(x))
gini = 1-sum(fi2)
gini.normalizzato = gini/((J-1)/J)
return(gini.normalizzato)
}
gini.index(sales_cl)
## [1] 0.943373
Grazie al grafico a barre possiamo vedere come il numero totale di vendite ha una distribuzione maggiore nella classe (122,165), con ben 60 osservazioni per quella classe che occupa il 25% della distribuzione , indicando che le vendite annue si aggirano tra le 122 alle 164 vendite effetuate. La classe con il numero minore di frequenze è la classe di vendite (380,423) per un totale di 4 osservazioni per questa classe la quale occupa il 1.7% della distribuzione.
L’indice di eterogeneità di Gini ha un valore di 0.94 indicando che la distribuzione è fortemente eterogenea, l’indice di eterogeneità di Gini infatti calcola l’eterogeneità di una distribuzione per variabili qualitative su scala ordinale, può assumere valori tra 0 e 1, con 0 abbiamo totale omogeneità dei della distribuzione, ovvero tutti i valori osservati appartengono ad una sola modalità della variabile, mentre è 1 quando i valori sono eterogenei , ovvero tutti i valori osservati appartengono in egual misura a tutte le modalità della variabile.
Nel nostro caso abbiamo una forte eterogeneità in quanto i valori della distribuzione appartengono in buone quantità tutti a classi diversi della variabile.
# Calcolo della probabilità per la città Beaumont :
mean(df$city == "Beaumont")
## [1] 0.25
# Calcolo della probabilità per il mese di luglio :
mean(df$month == 7)
## [1] 0.08333333
# Calcolo della probabilità mese dicembre anno 2012 :
mean(df$month == 12 & df$year == 2012)
## [1] 0.01666667
Per i risultati ottenuti per città e mese potevamo aspettarcelo, in quanto essi corrispondono alle frequenze relative delle due modalità , questo perchè come abbiamo avuto modo di osservare in precedenza le due variabili sono equi distribuite ed hanno lo stesso numero di frequenze per ognuna delle loro modalità .
Per quanto riguarda la prob. che il mese sia dicembre e l’anno 2012, le cose cambiano perché stiamo effetuando un calcolo di un evento doppiamente condizionato che è dato dal prodotto delle due prob. 0.083 x 0.20 che torna come risultato 0.0167 che è proprio il risultato che abbiamo ottenuto.
Creaiamo due nuove colonne per il dataframe , la prima colonna avg_price conterrà il prezzo medio per ogni immobile, la seconda ad_efficiency conterrà un valore che esprime l’efficacia degli annunci di immobili.
# Calcoliamo la colonna avg_price come rapporto tra volume e sales
# in quanto abbiamo bisogno del numero totale di dollari guadagnati e del numero totale di vendite
df$avg_price <- df$volume * 1e6 / df$sales
head(df)
## city year month sales volume median_price listings months_inventory
## 1 Beaumont 2010 1 83 14.162 163800 1533 9.5
## 2 Beaumont 2010 2 108 17.690 138200 1586 10.0
## 3 Beaumont 2010 3 182 28.701 122400 1689 10.6
## 4 Beaumont 2010 4 200 26.819 123200 1708 10.6
## 5 Beaumont 2010 5 202 28.833 123100 1771 10.9
## 6 Beaumont 2010 6 189 27.219 122800 1803 11.1
## avg_price
## 1 170626.5
## 2 163796.3
## 3 157697.8
## 4 134095.0
## 5 142737.6
## 6 144015.9
# Calcoliamo la colonna ad_efficiency come rapporto tra il numero di vendite e il numero di annunci :
df$ad_efficiency <- df$sales / df$listings
head(df)
## city year month sales volume median_price listings months_inventory
## 1 Beaumont 2010 1 83 14.162 163800 1533 9.5
## 2 Beaumont 2010 2 108 17.690 138200 1586 10.0
## 3 Beaumont 2010 3 182 28.701 122400 1689 10.6
## 4 Beaumont 2010 4 200 26.819 123200 1708 10.6
## 5 Beaumont 2010 5 202 28.833 123100 1771 10.9
## 6 Beaumont 2010 6 189 27.219 122800 1803 11.1
## avg_price ad_efficiency
## 1 170626.5 0.05414220
## 2 163796.3 0.06809584
## 3 157697.8 0.10775607
## 4 134095.0 0.11709602
## 5 142737.6 0.11405985
## 6 144015.9 0.10482529
Possiamo dire grazie a questi risultati che in generale un miglior rapporto tra vendite e annunci porta ad un maggior numero di vendite, ma anche di guadagno, come possiamo già notare analizzando le prime righe del dataset, per anni in cui il rapporto vendite/annunci è maggiore si registra anche un volume in milioni di dollari maggiore.
Andiamo ad usare il pacchetto dplyr per una analisi condizionata per città ,mese e anno :
# Summary delle vendite media e deviazione standard per città :
df %>%
group_by(city) %>%
summarise(
mean_sales = mean(sales),
sd_sales = sd(sales)
) %>%
ggplot(aes(x = city, y = mean_sales, fill = city)) +
geom_bar(stat = "identity") +
labs(title = "Vendite Medie per Città ", y = "Vendite Medie", x = "Città ")
# Media delle vendite per città e per anno :
df %>%
group_by(city,year) %>%
summarise(
mean_sales = mean(sales),
sd_sales = sd(sales)) %>%
ggplot(aes(x = factor(year), y = mean_sales, fill = city)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Media delle Vendite per Città e Anno", y = "Vendite Medie", x = "Anno") +
theme_minimal()
## `summarise()` has grouped output by 'city'. You can override using the
## `.groups` argument.
# Prezzo mediano per città e mese :
df %>%
group_by(city,month) %>%
summarise(
mean_median_price = mean(median_price),
sd_median_price = sd(median_price)
) %>%
ggplot(aes(x = month, y = mean_median_price, color = city, group = city)) +
geom_line(size = 1) +
labs(title = "Prezzo Mediano per Città e Mese", y = "Prezzo Mediano", x = "Mese") +
scale_x_continuous(breaks = 1:12, labels = 1:12)+
theme_minimal()
## `summarise()` has grouped output by 'city'. You can override using the
## `.groups` argument.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Media delle vendite per mese e anno :
df %>%
group_by(year, month) %>%
summarise(mean_sales = mean(sales)) %>%
ggplot(aes(x = month, y = mean_sales, color = factor(year), group = year)) +
geom_line(size = 1) +
labs(title = "Media delle Vendite per Mese e Anno", y = "Media delle Vendite", x = "Mese") +
scale_x_continuous(breaks = 1:12, labels = 1:12) + # Mesi da 1 a 12
theme_minimal()
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
# Boxplot per la distribuzione del prezzo mediano tra le città :
ggplot(df,aes(x = city, y = median_price))+
geom_boxplot(fill = "lightblue")+
labs(title = "Distribuzione del prezzo mediano tra le città ",
x = "Città ",
y = "Prezzo mediano"
)
Possiamo notare dal grafico come i prezzi mediani di ogni città varini molto,infatti le posizioni dei box lungo l’asse delle ordinate evidenzia come ogni città abbia dei prezzi mediani ben diversi dalle altre, possiamo anche vedere come in città come Tyler e Wichita Falls i box risultino più grandi rispetto a quelli delle altre città , ovvero il IQR è più ampio, quindi i valori spaziono molto fra di loro. Possiamo anche già vedere l’asimmetria della distribuzione guardando alla mediana, essa infatti per nessuna delle città è esattamente al centro del box, questo indica una asimmetria negativa o positiva a seconda dei casi.
# Boxplot per la distribuzione del valore totale delle vendite tra città e anni :
ggplot(df,aes(x = city, y = volume, fill = factor(year)))+
geom_boxplot()+
labs(
title = "Distribuzione del volume totale delle vendite per città e anno",
x = "Città ",
y = "Volume (milioni di $)"
)
Le differenze nei volumi di vendita tra le città possono indicare che alcune città hanno avuto dei picchiin determinati anni, causati da diversi fattori, come abbiamo visto può essere il numero di vendite, l’efficacia degli annunci ed altri fattori. Oltre a mostrare dei picchi ci sono anche città che presentano un trend costante nel tempo come la città di Tyler.
# Grafico a barre sovrapposte per il totale delle vendite nei vari mesi per città :
ggplot(df,aes(x=factor(month),y=sales,fill = city))+
geom_col(position = "stack")+
labs(
title = "Totale vendite mensili per città ",
x = "Mese",
y = "Totale vendite"
)
# Grafico a barre normalizzato :
ggplot(df,aes(x=factor(month),y=sales,fill = city))+
geom_col(position = "fill")+
labs(
title = "Totale vendite mensili per città (Normalizzato)",
x = "Mese",
y = "Proporzione delle vendite"
)
Nel grafico normale possiamo vedere i mesi con i picchi di vendite per ciascuna città ,per la maggior parte delle città i picchi di vendita vengono raggiunti nei mesi che vanno da maggio ad agosto,tranne per città come Wichita Falls che mantengono picchì costanti senza troppe variazioni durante i mesi dell’anno.
Nel grafico normalizzato invece, si osservano proporzioni stabili o cambiamenti significativi nel mix di vendite tra le città durane ogni mese.
# Line chart per confrontare le vendite nel tempo :
ggplot(df, aes(
x = as.Date(paste(year, month, "01", sep = "-")),
y = sales,
color = city)) +
geom_line() +
labs(
title = "Andamento delle vendite nel tempo per città ",
x = "Data",
y = "Vendite") +
scale_x_date(
date_labels = "%b %Y",
date_breaks = "3 month"
) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1)
)
Le linee mostrano chiaramente la stagionalità dei trend, alcune città potrebbero avere dei picchi che si ripetono in certi mesi, o un trend crescente o decrescente.