L’azienda Texas Realty Insights desidera analizzare le tendenze del mercato immobiliare nello stato del Texas, sfruttando i dati storici relativi alle vendite di immobili. L’obiettivo è fornire insight statistici e visivi che supportino le decisioni strategiche di vendita e ottimizzazione delle inserzioni immobiliari.
Obiettivi del progetto
Caricamento del dataset da analizzare e descrivere, e delle librerie necessarie per condurre l’analisi. A seguire, una preview delle prime 5 righe del dataset.
library(ggplot2)
library(moments)
library(patchwork)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ lubridate 1.9.4 ✔ tibble 3.3.0
## ✔ purrr 1.1.0 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
dati <- read.csv("realestate_texas.csv",sep = ",")
knitr::kable(head(dati,5))
city | year | month | sales | volume | median_price | listings | months_inventory |
---|---|---|---|---|---|---|---|
Beaumont | 2010 | 1 | 83 | 14.162 | 163800 | 1533 | 9.5 |
Beaumont | 2010 | 2 | 108 | 17.690 | 138200 | 1586 | 10.0 |
Beaumont | 2010 | 3 | 182 | 28.701 | 122400 | 1689 | 10.6 |
Beaumont | 2010 | 4 | 200 | 26.819 | 123200 | 1708 | 10.6 |
Beaumont | 2010 | 5 | 202 | 28.833 | 123100 | 1771 | 10.9 |
attach(dati)
Il dataset contiene le seguenti variabili:
Identifica e descrivi il tipo di variabili statistiche presenti nel dataset. Valuta come gestire le variabili che sottintendono una dimensione tempo e commenta sul tipo di analisi che può essere condotta su ciascuna variabile.
Iniziamo con un po’ di analisi ad alto livello: numerosità del dataset, check sulla presenza di duplicati e funzione str() per avere una prima overview delle variabili del dataset.
N <- dim(dati) [1]
cat("Il dataset contiene",N,"osservazioni\n")
## Il dataset contiene 240 osservazioni
duplicati <- dati %>% group_by_all() %>% filter(n() > 1) %>% ungroup()
cat("Il dataset contiene",dim(duplicati) [1] ,"duplicati\n") #non ci sono duplicati
## Il dataset contiene 0 duplicati
Non sono presenti duplicati altrimenti avremmo dovuto identificare, interpretare e gestire tali dati.
str(dati)
## '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 ...
Analizzando il tipo di variabile abbiamo:
Risulta una sola variabile qualitatita/categorica (su scala nominale) che è ‘city’
Ci son due variabili, ossia ‘year’ e ‘month’, che tracciano nel tempo le vendite e le altre caratterstiche associate ad ogni osservazione. Per gestire al meglio la variabile tempo creaiamo una ulteriore variabile temporale che chiameremo ‘date’ e che prenderà 1 come giorno per convenzione.
dati$date <- as.Date(paste(year, month, 1, sep = "-"))
knitr::kable(head(dati, 5))
city | year | month | sales | volume | median_price | listings | months_inventory | date |
---|---|---|---|---|---|---|---|---|
Beaumont | 2010 | 1 | 83 | 14.162 | 163800 | 1533 | 9.5 | 2010-01-01 |
Beaumont | 2010 | 2 | 108 | 17.690 | 138200 | 1586 | 10.0 | 2010-02-01 |
Beaumont | 2010 | 3 | 182 | 28.701 | 122400 | 1689 | 10.6 | 2010-03-01 |
Beaumont | 2010 | 4 | 200 | 26.819 | 123200 | 1708 | 10.6 | 2010-04-01 |
Beaumont | 2010 | 5 | 202 | 28.833 | 123100 | 1771 | 10.9 | 2010-05-01 |
str(dati)
## 'data.frame': 240 obs. of 9 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 ...
## $ date : Date, format: "2010-01-01" "2010-02-01" ...
attach(dati)
## The following objects are masked from dati (pos = 3):
##
## city, listings, median_price, month, months_inventory, sales,
## volume, year
Calcola Indici di posizione, variabilità e forma per tutte le variabili per le quali ha senso farlo, per le altre crea una distribuzione di frequenza. Infine, commenta tutto brevemente.
Per ‘city’, ‘year’ e ‘month’ calcoliamo le frequenze assolute dal momento che le variabili in analisi hanno realizzazioni che appartengono ad un insieme finito di valori.
distr_freq_city<-as.data.frame(
cbind(
ni=table(city),
fi=table(city)/N))
knitr::kable(distr_freq_city)
ni | fi | |
---|---|---|
Beaumont | 60 | 0.25 |
Bryan-College Station | 60 | 0.25 |
Tyler | 60 | 0.25 |
Wichita Falls | 60 | 0.25 |
distr_freq_year<-as.data.frame(
cbind(
ni=table(year),
fi=table(year)/N))
knitr::kable(distr_freq_year)
ni | fi | |
---|---|---|
2010 | 48 | 0.2 |
2011 | 48 | 0.2 |
2012 | 48 | 0.2 |
2013 | 48 | 0.2 |
2014 | 48 | 0.2 |
distr_freq_month<-as.data.frame(
cbind(
ni=table(month),
fi=round(table(month)/N,2)))
knitr::kable(distr_freq_month)
ni | fi |
---|---|
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
20 | 0.08 |
Abbiamo una equa distribuzione di osservazioni per ogni città, anno e mese.
Per le restanti variabili si può utilizzare la funzione summary() per avere una prima overview (in realtà facciamo su tutte le variabili per avere anche gli indici di posizione di ‘year’ e ‘month’)
knitr::kable(summary(dati))
city | year | month | sales | volume | median_price | listings | months_inventory | date | |
---|---|---|---|---|---|---|---|---|---|
Length:240 | Min. :2010 | Min. : 1.00 | Min. : 79.0 | Min. : 8.166 | Min. : 73800 | Min. : 743 | Min. : 3.400 | Min. :2010-01-01 | |
Class :character | 1st Qu.:2011 | 1st Qu.: 3.75 | 1st Qu.:127.0 | 1st Qu.:17.660 | 1st Qu.:117300 | 1st Qu.:1026 | 1st Qu.: 7.800 | 1st Qu.:2011-03-24 | |
Mode :character | Median :2012 | Median : 6.50 | Median :175.5 | Median :27.062 | Median :134500 | Median :1618 | Median : 8.950 | Median :2012-06-16 | |
NA | Mean :2012 | Mean : 6.50 | Mean :192.3 | Mean :31.005 | Mean :132665 | Mean :1738 | Mean : 9.193 | Mean :2012-06-16 | |
NA | 3rd Qu.:2013 | 3rd Qu.: 9.25 | 3rd Qu.:247.0 | 3rd Qu.:40.893 | 3rd Qu.:150050 | 3rd Qu.:2056 | 3rd Qu.:10.950 | 3rd Qu.:2013-09-08 | |
NA | Max. :2014 | Max. :12.00 | Max. :423.0 | Max. :83.547 | Max. :180000 | Max. :3296 | Max. :14.900 | Max. :2014-12-01 |
Il summary() ci fornisce in maniera compatta la media, mediana, 1° e 3° quartile, minimo e massimo. Per analizzare al meglio questi indici e per valutare anche gli indici di variabilità e forma, possiamo farne una rappresentazione grafica.
indici_vis <- function(data, feature){
#Gestione dati e label
feature_name <- deparse(substitute(feature)) # nome come stringa
values <- pull(data, {{feature}}) # estrae il vettore
# Calcoli statistici
mu <- mean(values, na.rm = TRUE)
sigma <- sd(values, na.rm = TRUE)
cv <- sigma / mu
sk <- skewness(values, na.rm = TRUE)
ku <- kurtosis(values, na.rm = TRUE) - 3 # kurtosi centrata
# Crea df per la label informativa
info_label <- data.frame(x = mu, y = 0,
label = paste0("μ = ", round(mu,2),
"\nσ = ", round(sigma,2),
"\nCV = ", round(cv,2),
"\nSkew = ", round(sk,2),
"\nKurt = ", round(ku,2)))
# Quantili
q_vals <- quantile(values, probs = c(0.25, 0.5, 0.75), na.rm = TRUE)
mean_val <- mean(values, na.rm = TRUE)
# Plot dei record ordinati
record_plot <- ggplot() +
geom_point(aes(x = seq_along(values), y = sort(values))) +
geom_hline(yintercept = mu, color = "blue", linetype = "dashed", linewidth = 0.7) +
geom_hline(yintercept = q_vals, color = "red", linetype = "dotted" , linewidth = 0.7) +
geom_label(aes(x = length(values)*0.9, y = mu),
label = paste0("Media: ", round(mu, 1)),
color = "blue", size = 3) +
geom_label(data = data.frame(y = q_vals, label = names(q_vals)),
aes(x = length(values)*0.9, y = y, label = paste0(label, ": ", round(y, 1))),
color = "red", size = 3) +
labs(title = "Distribuzione osservazioni ordinate",
x = "n. osservazione", y = "Valore osservazione") +
theme_minimal()
# Boxplot
box_plot <- ggplot(data, aes(y = {{feature}})) +
geom_boxplot(fill = "tan1") +
geom_hline(yintercept = mu, color = "blue", linetype = "dashed", linewidth = 0.7) +
geom_hline(yintercept = q_vals, color = "red", linetype = "dotted", linewidth = 0.7) +
labs(title = "Boxplot",
y = "Valore osservazione") +
theme_minimal()+
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
# Grafico della densità con box dei valori
density_plot <- ggplot(data, aes(x = {{feature}})) +
geom_density(fill = "lightblue", color = "black", alpha = 0.6) +
geom_vline(xintercept = mu, color = "blue", linetype = "dashed", linewidth = 0.7) +
geom_label(data = info_label, aes(x = x, y = y, label = label),
hjust = -0.1, vjust = -0.1, size = 3, fill = "white", label.size = 0.3) +
labs(title = "Stima della densità di probabilità",
x = "Valore osservazione", y = "Densità osservazione") +
theme_minimal()
print((record_plot + box_plot) +
plot_annotation(title = paste("Analisi di:", feature_name,"\n"),
theme = theme(plot.title = element_text(size = 16))))
print(density_plot)
}
indici_vis(dati,sales)
indici_vis(dati,volume)
indici_vis(dati,median_price)
indici_vis(dati,listings)
indici_vis(dati,months_inventory)
Analizzando i grafici possiamo fare le seguenti considerazioni:
Prima di andare al punto successivo, scriviamoci una funzione che calcola e restituisci alcuni indici degli indici analizzati e che ci serviranno nel corso dell’analisi.
indici_calc <- function(data, feature){
#Gestione dati e label
feature_name <- deparse(substitute(feature)) # nome come stringa
values <- pull(data, {{feature}}) # estrae il vettore
# Calcoli statistici
mu <- mean(values, na.rm = TRUE)
sigma <- sd(values, na.rm = TRUE)
cv <- sigma / mu
sk <- skewness(values, na.rm = TRUE)
ku <- kurtosis(values, na.rm = TRUE) - 3 # kurtosi centrata
return(c(mu = mu, sigma = sigma, cv = cv, skewness = sk, kurtosis = ku))
}
Determina: 1) Qual è la variabile con la più alta variabilità; 2) Qual è la variabile con la distribuzione più asimmetrica Spiega come sei giunto a queste conclusioni e fornisci considerazioni statistiche.
Per la variabilità consideriamo il coefficiente di variazione per avere un valore normalizzato che tenga conto della media e della varianza. Tale coefficiente rende confrontabile la variabilità tra due variabili di uno stesso campione o la variabilità di due campioni relativamente alla stessa variabile.
Si fa notare che è possibile utilizzare il coefficiente di variazione poiché le variabili da confrontare non hanno media nulla.
Per l’asimmetria utilizzeremo la skewness, indicatore di asimmetria per eccellenza.
# Calcolo gli indici per ogni feature
sales_stats <- indici_calc(dati, sales)
volume_stats <- indici_calc(dati, volume)
median_price_stats <- indici_calc(dati, median_price)
listings_stats <- indici_calc(dati, listings)
months_inventory_stats <- indici_calc(dati, months_inventory)
# Inserisco i valori in un df
comparazione_df <- rbind(
sales = sales_stats,
volume = volume_stats,
median_price = median_price_stats,
listings = listings_stats,
months_inventory = months_inventory_stats
)
comparazione_df <- as.data.frame(comparazione_df)
# Mostro i risultati
knitr::kable(round(comparazione_df, 2))
mu | sigma | cv | skewness | kurtosis | |
---|---|---|---|---|---|
sales | 192.29 | 79.65 | 0.41 | 0.72 | -0.31 |
volume | 31.01 | 16.65 | 0.54 | 0.88 | 0.18 |
median_price | 132665.42 | 22662.15 | 0.17 | -0.36 | -0.62 |
listings | 1738.02 | 752.71 | 0.43 | 0.65 | -0.79 |
months_inventory | 9.19 | 2.30 | 0.25 | 0.04 | -0.17 |
# Individua massimi
max_variabilita <- rownames(comparazione_df)[which.max(comparazione_df$cv)]
cat("Feature con massima variabilità (CV):", max_variabilita, "\n")
## Feature con massima variabilità (CV): volume
max_asimmetria <- rownames(comparazione_df)[which.max(abs(comparazione_df$skewness))]
cat("Feature con massima asimmetria (Skewness):", max_asimmetria, "\n")
## Feature con massima asimmetria (Skewness): volume
Il volume è la variabile che risulta avere la maggiormente variabilità in termini di coefficiente di variazione (0.54) e la maggiore asimmetria in termini di skewness (0.88). Tale risultato, se riprendiamo i risultati del punto 2, è dovuto anche alla presenza di outliers.
outliers <- volume[volume > quantile(volume, 0.99)]
N_outliers <- length(outliers)
cat("Ci sono", N_outliers, "outliers (soglia 99%) che sono:", outliers)
## Ci sono 3 outliers (soglia 99%) che sono: 77.983 83.547 80.814
Seleziona una variabile quantitativa (es. sales o median_price) e suddividila in classi. Crea una distribuzione di frequenze e rappresenta i dati con un grafico a barre. Calcola l’indice di eterogeneità Gini e discuti i risultati.
Sarà analizzata la variabile ‘sales’. Il primo step prevede la scelta del numero di intervalli da cui dispende la deinizione delle classi e di conseguenza l’indice di eterogenicità. Da notare che un numero di intervalli troppo elevato potrebbe definire delle classi con pochissime osservazioni, condizione non desiderata per questa analisi.
# Suddivisione in classi
num_bin = 4
dati$sales_cl <- cut(dati$sales, breaks = seq(from = min(sales),
to = max(sales),
by = (max(sales)-min(sales))/num_bin))
# Distribuzione di frequenze
ni = table(dati$sales_cl)
fi = round(ni/N,2)
Ni <- round(cumsum(ni),2)
Fi <- round(Ni/N,2)
F2 <- round(table(dati$sales_cl)/length(dati$sales_cl),2)
distr_frequenze <- as.data.frame(cbind(ni,fi,Ni,Fi,F2))
knitr::kable(distr_frequenze)
ni | fi | Ni | Fi | F2 | |
---|---|---|---|---|---|
(79,165] | 108 | 0.45 | 108 | 0.45 | 0.45 |
(165,251] | 71 | 0.30 | 179 | 0.75 | 0.30 |
(251,337] | 45 | 0.19 | 224 | 0.93 | 0.19 |
(337,423] | 14 | 0.06 | 238 | 0.99 | 0.06 |
#Grafico a barre
barplot(distr_frequenze$ni,
xlab = "Classi di sales",
ylab = "Frequenze assolute",
names.arg = rownames(distr_frequenze),
ylim = c(0,120),
col="blue")
# Indice di Gini
gini.index <- function(x){ #x: var qualitativa
ni = table(x)
fi = ni/length(x)
fi2 = fi^2
J = length(table(x))
gini = 1-sum(fi2)
gini.norm = gini/((J-1)/J)
return(round(gini.norm,2))
}
gini.index(dati$sales_cl)
## [1] 0.9
Per un numero di classi pari a 4 abbiamo un num di Gini pari a 0.9, indice di un’alta eterogenicità e propensione ad assumere le diverse modalità che può assumere la variabile.
Il valore dipende anche dal numero di classi:
Alla luce di questi ultimi test con diversi numeri di classi, si può affermare che l’indice di Gini per la variabile ‘sales’ dimostra un’alta eterogenicità (anche se lontani da una equidistribuzione (indice di Gini pari a 1) come si evince anche dal grafico precedente).
Qual è la probabilità che, presa una riga a caso di questo dataset, essa riporti la città “Beaumont”?
prop_beaumont <- mean(city == "Beaumont")
prop_beaumont
## [1] 0.25
In accordo alla definizione di probabilità classica (num. casi favorevoli/num. casi possibili totali), la probabiità di prescare Beaumont è di 1/4
E la probabilità che riporti il mese di Luglio?
prop_luglio <- round(mean(month == 7),2)
prop_luglio
## [1] 0.08
In accordo alla definizione di probabilità classica (num. casi favorevoli/num. casi possibili totali), la probabiità di prescare il mesi di leglio è di 1/12
E la probabilità che riporti il mese di dicembre 2012?
prop_dic2012 <- round(mean(format(date, "%Y-%m") == "2012-12"),2)
prop_dic2012
## [1] 0.02
In accordo alla definizione di probabilità classica (num. casi favorevoli/num. casi possibili totali) e sotto l’ipotesi di indipendenza dei due eventi, la probabiità di prescare il mese di dicembre 2012 è (1/12)*(1/5), ossia la moltiplicazione della probabilità di pescare il mese di dicembre e l’anno 2012. Il tutto si basa sull’assunto che l’estrazione dal dataset del mese e dell’anno siano indipendenti, ma arriveremmo allo stesso risultato anche se li considerassimo accoppiati: in questo caso si hanno 5x12 combinazioni e una probabilità di 1/60 di estrarre un certo mese di un certo anno.
Crea una nuova colonna che calcoli il prezzo medio degli immobili utilizzando le variabili disponibili. Prova a creare una colonna che misuri l’efficacia degli annunci di vendita. Commenta e discuti i risultati
Prezzo medio calcolato come valore totale delle vendite su num di vendite totali. Quindi usando le variabili a disposizione: volume su sales.
dati$mean_price <- dati$volume*10e6 / dati$sales
knitr::kable(head(dati,5))
city | year | month | sales | volume | median_price | listings | months_inventory | date | sales_cl | mean_price |
---|---|---|---|---|---|---|---|---|---|---|
Beaumont | 2010 | 1 | 83 | 14.162 | 163800 | 1533 | 9.5 | 2010-01-01 | (79,165] | 1706265 |
Beaumont | 2010 | 2 | 108 | 17.690 | 138200 | 1586 | 10.0 | 2010-02-01 | (79,165] | 1637963 |
Beaumont | 2010 | 3 | 182 | 28.701 | 122400 | 1689 | 10.6 | 2010-03-01 | (165,251] | 1576978 |
Beaumont | 2010 | 4 | 200 | 26.819 | 123200 | 1708 | 10.6 | 2010-04-01 | (165,251] | 1340950 |
Beaumont | 2010 | 5 | 202 | 28.833 | 123100 | 1771 | 10.9 | 2010-05-01 | (165,251] | 1427376 |
Per l’efficacia occorre ragionare sulle variabili in gioco:
ggplot(dati, aes(x = date, y = listings, color = city)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Annunci attivi nel tempo",
x = "Tempo",
y = "Annunci attivi, n.",
col = "Città") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
# Gli annunci attivi mostrano un trend decrescente e periodico
ggplot(dati, aes(x = date, y = months_inventory, color = city)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Mesi necessari per vendere annunci nel tempo",
x = "Tempo",
y = "Mesi di giacenza annuncio",
col = "Città") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
# anche i tempi di giacenza diminuiscono sensibilmente negli ultimi anni suggerendo un aumento delle vendite
ggplot(dati, aes(x = date, y = sales, color = city)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Vendite nel tempo",
x = "Tempo",
y = "Vendite, n.",
col = "Città") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
# 3/4 città vedono un incremento sostanziale delle vendite
# quindi leghiamo sales e annunci
ggplot(dati, aes(x = date, y = sales*100/listings, color = city)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Efficacia annunci nel tempo",
x = "Tempo",
y = "Vendite/Annunci attivi, %",
col = "Città") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
# sembra che nel tempo ci sia un aumento di rapporto tra numero di vendite e annunci nel tempo
L’analisi potrebbe continuare con un approfondimento sulla correlazione delle variabili per cercare di capire quali sono le variabili che concorrono a far aumentare l’efficienza degli annunci e come si legano listings e sales.
Per ora, ci limitiamo a definire l’efficacia degli annunci come sales/listings e dire che aumenta nel tempo.
dati$listing_efficiency <- dati$sales / dati$listings
knitr::kable(head(dati,5))
city | year | month | sales | volume | median_price | listings | months_inventory | date | sales_cl | mean_price | listing_efficiency |
---|---|---|---|---|---|---|---|---|---|---|---|
Beaumont | 2010 | 1 | 83 | 14.162 | 163800 | 1533 | 9.5 | 2010-01-01 | (79,165] | 1706265 | 0.0541422 |
Beaumont | 2010 | 2 | 108 | 17.690 | 138200 | 1586 | 10.0 | 2010-02-01 | (79,165] | 1637963 | 0.0680958 |
Beaumont | 2010 | 3 | 182 | 28.701 | 122400 | 1689 | 10.6 | 2010-03-01 | (165,251] | 1576978 | 0.1077561 |
Beaumont | 2010 | 4 | 200 | 26.819 | 123200 | 1708 | 10.6 | 2010-04-01 | (165,251] | 1340950 | 0.1170960 |
Beaumont | 2010 | 5 | 202 | 28.833 | 123100 | 1771 | 10.9 | 2010-05-01 | (165,251] | 1427376 | 0.1140599 |
indici_vis(dati, listing_efficiency)
A questo punto riassiumiamo il trend dell’efficacia usando la media.
stats_efficiecy <- dati %>%
group_by(year) %>%
summarise(
mean_eff = round(mean(listing_efficiency, na.rm = TRUE),3),
sd_eff = round(sd(listing_efficiency, na.rm = TRUE),3),
.groups = "drop"
)
knitr::kable(stats_efficiecy)
year | mean_eff | sd_eff |
---|---|---|
2010 | 0.100 | 0.034 |
2011 | 0.093 | 0.023 |
2012 | 0.110 | 0.028 |
2013 | 0.135 | 0.045 |
2014 | 0.157 | 0.062 |
ggplot(stats_efficiecy, aes(x = year, y = mean_eff)) +
geom_line() +
geom_point() +
geom_errorbar(aes(ymin = mean_eff - sd_eff, ymax = mean_eff + sd_eff),
width = 0.2) +
labs(title = "Efficacia annunci media per anno", y = "Media efficacia", x = "Anno") +
theme_minimal()
Interessante vedere come la media dell’efficacia aumenti rapidamente nel tempo passando da un valore prossimo al 10% fino a circa il 16%. Aumenta anche la deviazione standard e questa è dovuta agli outlier introdotti da Bryan-College Station.
Usa il pacchetto dplyr o il linguaggio base di R per effettuare analisi statistiche condizionate per città, anno e mese. Genera dei summary (media, deviazione standard) e rappresenta graficamente i risultati.
L’analisi condotta in questo paragrafo si concetrerà sulle vendite ma potrà essere reiterata sulle altre variabili.
1) Analisi per città
stats_city <- dati %>%
group_by(city) %>%
summarise(
mean_sales = round(mean(sales, na.rm = TRUE),3),
sd_sales = round(sd(sales, na.rm = TRUE),3),
cv_sales = round(sd_sales / mean_sales,3),
.groups = "drop"
)
knitr::kable(stats_city)
city | mean_sales | sd_sales | cv_sales |
---|---|---|---|
Beaumont | 177.383 | 41.484 | 0.234 |
Bryan-College Station | 205.967 | 84.984 | 0.413 |
Tyler | 269.750 | 61.964 | 0.230 |
Wichita Falls | 116.067 | 22.152 | 0.191 |
ggplot(stats_city, aes(x = city, y = mean_sales, fill = city)) +
geom_col() +
geom_errorbar(aes(ymin = mean_sales - sd_sales, ymax = mean_sales + sd_sales),
width = 0.2) +
labs(title = "Vendite medie per città", y = "Media vendite", x = "Città", fill = "Città") +
theme_minimal()
In media, le vendite in città differenti variano molto. A causa di una deviazione standard non omogenea, il confronto tra le città usando soltanto questo grafico non è esaustivo. Tuttavia, si può assermare che in media Tyler è la città che ha venduto di più mentre, al contrario, Wichita Falls quella dove ci son state meno vendite. Da notare anche che Bryan-college station è il luogo in cui vi è maggiore variabilità nelle vendite (cv maggiore di tutti).
2) Analisi per anno
stats_year <- dati %>%
group_by(year) %>%
summarise(
mean_sales = round(mean(sales, na.rm = TRUE),3),
sd_sales = round(sd(sales, na.rm = TRUE),3),
.groups = "drop"
)
knitr::kable(stats_year)
year | mean_sales | sd_sales |
---|---|---|
2010 | 168.667 | 60.537 |
2011 | 164.125 | 63.870 |
2012 | 186.146 | 70.905 |
2013 | 211.917 | 83.996 |
2014 | 230.604 | 95.515 |
ggplot(stats_year, aes(x = year, y = mean_sales)) +
geom_line() +
geom_point() +
geom_errorbar(aes(ymin = mean_sales - sd_sales, ymax = mean_sales + sd_sales),
width = 0.2) +
labs(title = "Vendite medie per anno", y = "Media vendite", x = "Anno") +
theme_minimal()
Il grafico mostra un aumento costante della media delle vendite globale dal 2011 in poi. Anche la variabilità segue lo stesso trend negli anni.
3) Analisi per mese
stats_month <- dati %>%
group_by(month) %>%
summarise(
mean_sales = round(mean(sales, na.rm = TRUE),3),
sd_sales = round(sd(sales, na.rm = TRUE),3),
.groups = "drop"
)
ggplot(stats_month, aes(x = factor(month), y = mean_sales)) +
geom_col(fill = "steelblue") +
geom_errorbar(aes(ymin = mean_sales - sd_sales, ymax = mean_sales + sd_sales),
width = 0.2) +
labs(title = "Vendite medie per mese", x = "Mese", y = "Media vendite") +
theme_minimal()
Analizzando la stagionalità, dall’analisi emerge che si preferisce comprare in primavera-estate: la media delle vendite risulta più alta tra maggio e settembre.
Infine utilizziamo i boxplot o qualche variante per confrontare la distribuzione del valore totale delle vendite tra le varie città ma anche tra i vari anni
# Opzione 1
ggplot(dati, aes(x = city, y = sales, fill = city)) +
geom_boxplot() +
facet_wrap(~ year) +
labs(title = "Boxplot vendite per anno, suddivise per città",y= "Vendite", x = "Anno", fill = "Città") +
theme_minimal() +
theme(axis.text.x = element_blank())
Il grafico riassume un po’ le viste fornite dai grafici precedenti ma ci permette di osservare meglio l’andamento nel tempo degli indici di posizione e variabilità riferiti alle vendite per ogni città. Si fa notare che la forte variabilità delle vendite di Bryan-college notata in precedenza, viene amplificata fortemente dal 2012 in poi e con dei range interquartili che raggiungono i massimi valori nel 2012 e 2013.
# Opzione 2
stats_city_year <- dati %>%
group_by(city, year) %>%
summarise(
mean_sales = mean(sales, na.rm = TRUE),
sd_sales = sd(sales, na.rm = TRUE),
.groups = "drop"
)
ggplot(stats_city_year, aes(x = city, y = mean_sales, fill = factor(year))) +
geom_col(position = "dodge") +
geom_errorbar(aes(ymin = mean_sales - sd_sales, ymax = mean_sales + sd_sales),
position = position_dodge(width = 0.9), width = 0.2) +
labs(title = "Barplot vendite medie per città, suddivise per anno",
y = "Media vendite", x = "Città", fill = "Anno") +
theme_minimal()
Gli ultimi grafici non fanno altro che confermare quanto osservato in precedenza. Le medie delle vendite crescono nel tempo e in quasi tutte le città (a meno di Witchita Falls).
Utilizza ggplot2 per creare grafici personalizzati. Assicurati di esplorare:
ggplot(dati, aes(x = city, y = median_price, fill = city)) +
geom_boxplot() +
labs(title = "Boxplot prezzo mediano vendite", y = "Prezzo mediano, $", x = "Città", fill = "Città") +
theme_minimal()
stats_medianprice <- dati %>%
group_by(city) %>%
summarise(
mean_sales = round(mean(median_price, na.rm = TRUE),3),
sd_sales = round(sd(median_price, na.rm = TRUE),3),
.groups = "drop"
)
knitr::kable(stats_medianprice)
city | mean_sales | sd_sales |
---|---|---|
Beaumont | 129988.3 | 10104.993 |
Bryan-College Station | 157488.3 | 8852.235 |
Tyler | 141441.7 | 9336.538 |
Wichita Falls | 101743.3 | 11320.034 |
Il prezzo mediano differisca molto in termini di media e mediana da città a città. Sulla base di questi indici, Bryan-College Station è la città con il prezzo mediano più alto (e la deviazione standard minone) mentre Wichita Falls quella con il più basso (e la deviazione standard maggiore).
Vediamo cosa succede se aggiungiamo l’informazione tempo.
ggplot(dati, aes(x = factor(year), y = median_price, fill = city)) +
geom_boxplot() +
labs(title = "Distribuzione del prezzo mediano per anno, suddiviso per città",
x = "Anno", y = "Prezzo mediano, $" , fill = "Città") +
theme_minimal()
Viene riconfermata la posizione di Bryan-College Station in cima alla classifica delle città con prezzo mediano più elevato e questa volta anche negli anni. A questo si aggiunge un trend di crescita del prezzo mediano nel tempo anche per Tyler.
vendite_mensili <- dati %>%
group_by(city, month, year) %>%
summarise(tot_sales = sum(sales, na.rm = TRUE), .groups = "drop")
ggplot(vendite_mensili, aes(x = factor(month), y = tot_sales, fill = city)) +
geom_col(position = "dodge") +
labs(title = "Totale vendite per mese (e città su barre affiancate)",
x = "Mese", y = "Totale vendite", fill = "Città") +
theme_minimal()
ggplot(vendite_mensili, aes(x = factor(month), y = tot_sales, fill = city)) +
geom_col(position = "stack") +
labs(title = "Totale vendite per mese (e città su barre sovrapposte)",
x = "Mese", y = "Totale vendite", fill = "Città") +
theme_minimal()
vendite_mensili_norm <- vendite_mensili %>%
group_by(month) %>%
mutate(perc_sales = tot_sales / sum(tot_sales))
ggplot(vendite_mensili_norm, aes(x = factor(month), y = perc_sales, fill = city)) +
geom_col(position = "fill") +
labs(title = "Distribuzione percentuale delle vendite per mese (e città su barre normalizzate)",
x = "Mese", y = "Percentuale vendite", fill = "Città") +
scale_y_continuous(labels = scales::percent) +
theme_minimal()
C’è una certa tendenza a vendere maggiormente tra i mesi di maggio e settembre. La tendenza risulta molto marcata nelle città di Bryan-College Station e Tyler (come evidenziato anche dalla distribuzione delle percentuali delle vendite). E negli anni?
ggplot(vendite_mensili, aes(x = factor(month), y = tot_sales, fill = city)) +
geom_col(position = "dodge") +
facet_wrap(~ year) +
labs(title = "Totale vendite per mese visto negli anni",
x = "Mese", y = "Totale vendite", fill = "Città") +
theme_minimal()+
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
Negli anni si vede che tale comportamento si ripete ad eccezione del 2010 in cui la maggior parte delle vendite si son concentrate tra marzo e maggio.
andamento_vendite <- dati %>%
group_by(city, date) %>%
summarise(tot_sales = sum(sales, na.rm = TRUE), .groups = "drop")
ggplot(andamento_vendite, aes(x = date, y = tot_sales, color = city)) +
geom_line(linewidth = 1) +
geom_smooth(method = "lm", se = FALSE, linetype = "dotted") +
labs(title = "Andamento delle vendite totali nel tempo",
x = "Tempo", y = "Totale vendite", col = "Città") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
Oltre ad una certa stagionalità delle vendite, dall’analisi del grafico emerge un aumento delle vendite negli anni. In particolare per le città di Bryan-College Station e Tyler, ma anche in Beaumont.
Combinando le osservazioni tratte dai precedenti studi, ci si chiede se un elevato prezzo mediano sia collegato in qualche modo ad un maggiore volume di vendite. Vedi i casi di Bryan-College Station e Tyler.
Fornisci una sintesi dei risultati ottenuti, facendo riferimento alle principali tendenze emerse e fornendo raccomandazioni basate sull’analisi. Questo non è un progetto di programmazione, ma di statistica, e ci si aspetta di leggere commenti e considerazioni statistiche per i vari passaggi e risultati.
L’analisi presentata nel seguente progetto ha come obiettivo quello di supportare l’azienda Texas Realty Insights nell’analisi delle tendenze del mercato immobiliare nello stato del Texas tramite insight statistici e visivi.
In tale paragrafo conclusivo verrà fornita una sintesi dei risultati orientata agli obiettivi principali del progetto.
A) Identificare e interpretare i trend storici delle vendite immobiliari in Texas.
Dal 2011 si vede un aumento costante delle vendite nel tempo nonostante un aumento della variabilità delle vendite stesse: la media delle vendite subisce un incremento costante così come la deviazione standard fino a raggiungere i loro relativi massimi nel 2014.
In merito alle 4 città analizzate, emergono dei pattern di vendita positivi a meno di Wichita Falls, unica città ad avere un trend piatto: le altre 3 città, ossia Beaumont, Bryan-College Station e Tyler, hanno una media di vendite maggiore e, sopratutto, una media che aumenta nel tempo dal 2011 in poi.
Tra queste Tyler è la città con il maggior numero di vendite (presenta i più alti valori di vendite in media e mediana negli ultimi 5 anni) mentre Bryan-College Station è la città con il prezzo mediano di vendite più alto (accompagnato anche dall’avere la deviazione standard del prezzo mediano minore rispetto alle altre città).
I dati analizzati ci dicono quindi che Bryan-College Station e Tyler son due città in cui si ha un mercato immobiliare con delle tendenze molto positive sia in termini di numero di vendite che di prezzo mediano delle vendite. Tale primato viene confermato anche dall’aumento costante del prezzo mediano dal 2011 in poi nelle due città.
B) Valutare l’efficacia delle strategie di marketing delle inserzioni immobiliari.
Per quanto riguarda le inserzioni immobiliari, l’analisi rivela un impatto positivo delle strategie di marketing adottare negli anni. Si può osservare una riduzione degli annunci e della quantità di tempo necessaria per vendere le inserzioni correnti (il tutto sempre accompagnato da una crescita delle vendite)
Se si considera l’efficacia delle strategie di marketing come il rapporto tra il numero di vendite e il numero di annunci attivi, è possibili osservare un trend crescente nel tempo (a meno di Wichita Falls, unica città a mostrare un trend piatto). Nel 2014 l’efficacia raggiunge il suo valore massimo (la media passa dal 10% al 16%). Sarebbe interessante, supportati da dati relativi al marketing, capire a cosa è correlato questo trend.
Inoltre, osservando meglio il trend si notano delle fluttuazioni periodiche. Tali periodicità sono legate alla stagionalità delle vendite. Si preferisce comprare in primavera-estate, tra maggio è settembre. La distribuzione delle vendite rispetto ai 12 mesi risulta essere centrata.
Questo è particolarmente vero per Bryan-College Station e Tyler e negli anni dal 2011 in poi.