#librerie
library(ggplot2)
library(dplyr)
## 
## Caricamento pacchetto: 'dplyr'
## I seguenti oggetti sono mascherati da 'package:stats':
## 
##     filter, lag
## I seguenti oggetti sono mascherati da 'package:base':
## 
##     intersect, setdiff, setequal, union
library(scales)
## Warning: il pacchetto 'scales' è stato creato con R versione 4.4.3
#dataset
data_ret <- read.csv("realestate_texas.csv")

#cose utili
n <- nrow(data_ret)
months <- c("Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic")

#funzioni

#indice di gini
gini.index <- function(x){
  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(gini.norm)
}

#calcolo indici
calculate_indices <- function(x) {
  indici <- list(
    min = min(x),
    Q1 = quantile(x, 0.25),
    mediana = median(x),
    media = mean(x),
    Q3 = quantile(x, 0.75),
    max = max(x),
    range = max(x) - min(x),
    IQR = IQR(x),
    varianza = var(x),
    ds = sd(x),
    CV = sd(x) / mean(x) * 100,
    skewness = skewness(x),
    kurtosis = kurtosis(x)
  )
  return(indici)
}

Analisi delle variabili

Nel dataset sono presenti 240 osservazioni per 8 variabili.

La seguente tabella illustra i tipi di variabili statistiche presenti nel dataset.

Variabile Tipo varia Descrizione
city qualitativa su scala nominale città di riferimento per i dati
year qualitativa su scala nominale anno di riferimento per i dati
month qualitativa su scala nominale mese di riferimento per i dati
sales quantitativa discreta numero totale delle vendite
volume quantitativa continua valore totale delle vendite espresso in milioni di dollari
median_price quantitativa continua valore mediano del prezzo di vendita, espresso in dollari
listings quantitativa discreta numero totale di annunci attivi
month_inventory quantitativa continua quantità di tempo necessaria per vendere tutte le inserzioni correnti al ritmo attuale di vendita, espresso in mesi

Ho deciso di trattare le variabili che esprimono una dimensione tempo come variabili qualitative.

Ho creato una variabile date_var in formato date combinando i dati delle variabili temporali monthyear. Ho convenzionalmente impostato il valore day con il primo giorno del mese.

data_ret$date_var <- as.Date(
  paste(data_ret$year, data_ret$month, "01", sep = "-"),
  format = "%Y-%m-%d"
  )

Per le variabili qualitative calcolerò la distribuzione di frequenza, per le variabili quantitative calcolerò gli indici di posizione, variabilità e forma.

Ho analizzato distribuzione di frequenza per le variabili city, year e month calcolando l’indice di Gini.

L’indice di Gini esprime massima eterogeneità per tutte le variabili analizzate. Pertanto, essendo i valori equamente distribuiti, si può dedurre che dal 2010 al 2014, per ogni mese e per ogni città di riferimento sono state rilevate le variabili quantitative descritte nella tabella precedente.

Questa deduzione è confermata dalla verifica delle categorie per ciascuna variabile, che risultano perfettamente equidistribuite.

table(data_ret$city)
## 
##              Beaumont Bryan-College Station                 Tyler 
##                    60                    60                    60 
##         Wichita Falls 
##                    60
table(data_ret$year)
## 
## 2010 2011 2012 2013 2014 
##   48   48   48   48   48
table(data_ret$month)
## 
##  1  2  3  4  5  6  7  8  9 10 11 12 
## 20 20 20 20 20 20 20 20 20 20 20 20

Calcolo della probabilità

Procedo con il calcolo delle probabilità richieste nel punto 5.

Probabilità che una riga riporti la città di Beaumont: 25%

Probabilità che una riga riporti il mese di luglio: 8.34%

Probabilità che una riga riporti il mese di dicembre 2012: 1.67%

sum((data_ret$city=="Beaumont")/n)*100
## [1] 25
sum((data_ret$month==07)/n)*100
## [1] 8.333333
sum((data_ret$date=="2012-12-01")/n)*100
## [1] 1.666667

Creazione di nuove variabili

Ho deciso di anticipare questo anche il punto 6 per poter poi confrontare il prezzo medio al prezzo mediano.

Prezzo medio degli immobili

Ho calcolato il prezzo medio degli immobili utilizzando la seguente formula:

\[ mean\_price = \frac{volume}{sales} \]

Nel dataset, il totale delle vendite è espresso in milioni di dollari. Per poter poi confrontare avg_price e median_price ho convertito anche mean_price in dollari.

data_ret$mean_price <- (data_ret$volume/data_ret$sales)*1000000

Efficacia degli annunci

Ho calcolato l’efficacia degli annunci utilizzando la seguente formula:

\[ listing\_eff = \frac{sales}{listings} \times{100} \] Il rapporto tra il numero di vendite (sales) e il numero di annunci attivi (listings) indica quante vendite sono generate per ogni annuncio attivo; listing_eff è espresso in termini percentuali. Valori alti Indicano molte vendite per annuncio, quindi che gli annunci sono molto efficaci. Valori bassi indicano poche vendite per annuncio, quindi che gli annunci sono poco efficaci.

data_ret$listing_eff <- (data_ret$sales/data_ret$listings)*100

Per considerare anche il valore economico delle vendite, ho calcolato una variabile che esprime in dollari il valore generato per ciascun annuncio.

\[ listing\_value = \frac{{mean\_price}\times{sales}}{listings} \]

data_ret$listing_value <- ((data_ret$sales)*(data_ret$mean_price))/data_ret$listings

Analisi degli indici

Procedo con il calcolo degli indici di posizione, variabilità e forma per le variabili listings, sales, volume, median_price, mean_price che riepilogo nella seguente tabella.

listings sales volume median_price mean_price
min 743.00 79.00 8.17 73800.00 97010.20
Q1 1026.50 127.00 17.66 117300.00 132938.94
mediana 1618.50 175.50 27.06 134500.00 156588.48
media 1738.02 192.29 31.01 132665.42 154320.37
Q3 2056.00 247.00 40.89 150050.00 173915.15
max 3296.00 423.00 83.55 180000.00 213233.94
range 2553.00 344.00 75.38 106200.00 116223.74
IQR 1029.50 120.00 23.23 32750.00 40976.22
varianza 566568.97 6344.30 277.27 513572983.09 736984385.27
ds 752.71 79.65 16.65 22662.15 27147.46
CV 43.31 41.42 53.71 17.08 17.59
skewness 0.65 0.72 0.88 -0.36 -0.07
kurtosis 2.21 2.69 3.18 2.38 2.22

Commento sul volume delle vendite

ggplot(data_ret, aes(x = volume)) +
  geom_histogram(
    bins = 21,
    fill = "lightgrey",
    color = "black"
  ) +
  geom_density(
    aes(y = after_stat(count)*((max(data_ret$volume)-min(data_ret$volume))/21)),
    color = "red",
    size = 1
  ) +
  labs(
    x = NULL,
    y = "Frequenza assoluta",
    title = "Distribuzione del volume totale delle vendite") +
  scale_x_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M"),
    breaks = seq(10, 90, by = 10)
  )+
  theme_light()

Il volume totale delle vendite esprime la più alta variabilità (CV = 53.71%) tra le variabili analizzate, i valori sono quindi molto dispersi intorno alla media. Ciò potrebbe essere dovuto a importanti fluttuazioni nel valore totale delle vendite nel corso del tempo, o a significative differenze tra una città e l’altra. Un alto CV potrebbe quindi indicare un mercato immobiliare piuttosto volatile.

Il volume totale delle vendite esprime anche la variabile con la distribuzione più asimmetrica, con una coda lunga a destra (skewness = 0.88), la maggior parte dei volumi di vendita registrati si concentra sui valori più bassi. Questo aspetto potrebbe essere causato da diversi fattori, come, ad esempio, minor rendimento di alcune città o un basso volume di vendite per periodi prolungati.

Come si può vedere dal grafico, la curva di densità (linea rossa) mostra che la maggior parte dei volumi di vendita registrati si concentra tra i 10 e i 35 milioni di dollari. L’istogramma, che rappresenza le frequenze assolute suddivise in 21 classi, mostra una distribuzione dei valori piuttosto varia con più picchi significativi.

Confronto tra prezzo medio e prezzo mediano

ggplot(data_ret, aes(x = median_price)) +
  geom_histogram(
    bins = 21,
    fill = "lightgrey",
    color = "black"
    ) +
  geom_density(
    aes(y = after_stat(count)*((max(data_ret$median_price)-min(data_ret$median_price))/21)),    
    color = "red",
    size = 1
  ) +
  labs(
    x = "Prezzo mediano",
    y = "Frequenza assoluta",
    title = "Distribuzione del prezzo mediano"
  ) +
  scale_x_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "),
    breaks = seq(70000, 180000, by = 20000)
    )+
  theme_light()

ggplot(data_ret, aes(x = mean_price)) +
  geom_histogram(
    bins = 21,
    fill = "lightgrey",
    color = "black"
  ) +
  geom_density(
    aes(y = after_stat(count)*((max(data_ret$mean_price)-min(data_ret$mean_price))/21)),    
    color = "red",
    size = 1
  ) +
  labs(
    x = "Prezzo medio",
    y = "Frequenza assoluta",
    title = "Distribuzione del prezzo medio"
  ) +
  scale_x_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "),
    breaks = seq(90000, 220000, by = 20000)
  )+
  theme_light()

Il prezzo medio ha un range più ampio rispetto al prezzo mediano (116224$ cfr. 106200$), indicando la presenza di alcuni valori estremi che influenzano la media. Potrebbe trattarsi di alcuni immobili di alto valore, essendo il prezzo medio generalmente più alto del prezzo mediano. Inoltre, il prezzo medio minimo è più alto del prezzo mediano minimo, ciò suggerisce che non ci sono molti immobili a prezzi molto bassi.

La varianza e la deviazione standard del prezzo medio sono leggermente più elevate rispetto a quelle del prezzo mediano, confermando una maggiore dispersione dei prezzi medi.

Sia nel prezzo medio sia nel prezzo mediano si può osservare una leggera asimmetria negativa e la curtosi leggermente elevata. Questi dati suggeriscono che la distribuzione dei prezzi ha una coda sinistra più lunga e alcuni valori estremi, anche se non particolarmente influenti.

Analisi della distribuzione in classi del totale delle vendite

Ho suddiviso la variabile quantitativa sales in 3 classi.

sales_cl_3 <- cut(data_ret$sales, breaks = 3)

distr_freq_sales_3 <- as.data.frame(
  cbind(
    ni=table(sales_cl_3),
    fi=round(table(sales_cl_3)/n, 3),
    Ni=cumsum(table(sales_cl_3)),
    Fi=round(cumsum(table(sales_cl_3))/n, 3)
  )
)
ni fi Ni Fi
(78.7,194] 141 0.5875 141 0.5875
(194,308] 76 0.3167 217 0.9042
(308,423] 23 0.0958 240 1.0000
ggplot(as.data.frame(sales_cl_3))+
  geom_bar(aes(x = sales_cl_3),
           fill = "lightgrey",
           color = "black"
  )+
  labs(
    x = "Classi",
    y = "Frequenza assoluta",
    title = "Distribuzione in 3 classi del totale delle vendite"
  )+
  theme_light()

La maggior parte dei valori (58.6%) è concentrata nella prima classe, che rappresenta un numero di vendite basso compreso tra 79 e 194. Pochi valori (9.6%) sono registrati nella terza classe, che rappresenta un numero alto di vendite alto compreso tra 308 e 423. L’indice di Gini è di 0.82, e presenta una modesta eterogeneità.

Ho voluto analizzare più nel dettaglio la distribuzione del numero di vendite, con una suddivisione in 9 classi.

sales_cl_9 <- cut(data_ret$sales, breaks = 9)

distr_freq_sales_9 <- as.data.frame(
  cbind(
    ni=table(sales_cl_9),
    fi=round(table(sales_cl_9)/n, 3),
    Ni=cumsum(table(sales_cl_9)),
    Fi=round(cumsum(table(sales_cl_9))/n, 3)
  )
)
ni fi Ni Fi
(78.7,117] 46 0.1916667 46 0.1916667
(117,155] 51 0.2125000 97 0.4041667
(155,194] 44 0.1833333 141 0.5875000
(194,232] 27 0.1125000 168 0.7000000
(232,270] 23 0.0958333 191 0.7958333
(270,308] 26 0.1083333 217 0.9041667
(308,347] 10 0.0416667 227 0.9458333
(347,385] 9 0.0375000 236 0.9833333
(385,423] 4 0.0166667 240 1.0000000
ggplot(as.data.frame(sales_cl_9))+
  geom_bar(aes(x = sales_cl_9),
           fill = "lightgray",
           color = "black"
           )+
  labs(
    x = "Classi",
    y = "Frequenza assoluta",
    title = "Distribuzione in 3 classi del totale delle vendite"
    )+
  theme_light()

ggplot(data_ret, aes(x = sales)) +
  geom_histogram(
    bins = 21,
    fill = "lightgrey",
    color = "black"
  ) +
  geom_density(
    aes(y = after_stat(count)*((max(data_ret$sales)-min(data_ret$sales))/21)),    
    color = "red",
    size = 1
  ) +
  labs(
    x = "Vendite",
    y = "Frequenza assoluta",
    title = "Distribuzione del numero di vendite"
    ) +
  theme_light()

La doppia analisi (9 classi e 21 bin) conferma un mercato polarizzato, dove la maggior parte dei periodi/città registra vendite modeste, mentre pochi casi eccezionali contribuiscono in modo sproporzionato al volume totale. Inoltre, la similitudine tra le distribuzioni di sales e volume conferma che il valore totale delle vendite è trainato principalmente dal numero di transazioni.

Analisi condizionata

Ho deciso di condurre l’analisi condizionata sulla variabili che esprimono il prezzo medio (avg_price) e il numero di vendite (sales), al fine di analizzare più nel dettaglio la composizione del volume totale delle vendite.

Dati

Analisi condizionata per anno

Prezzo medio

summary_year_mean_price <- data_ret %>%
  group_by(year) %>%
  summarise(
    mean_mean_price = round(mean(mean_price),0),
    sd_mean_price = round(sd(mean_price),0)
  )
anno media prezzo medio sd prezzo medio
2010 150189 23280
2011 148251 24938
2012 150899 26438
2013 158705 26524
2014 163559 31741

Vendite

summary_year_sales <- data_ret %>%
  group_by(year) %>%
  summarise(
    mean_sales = round(mean(sales),0),
    sd_sales = round(sd(sales),0)
  )
anno media vendite sd vendite
2010 169 61
2011 164 64
2012 186 71
2013 212 84
2014 231 96

Analisi condizionata per mese

Prezzo medio

summary_month_mean_price <- data_ret %>%
  group_by(month) %>%
  summarise(
    mean_mean_price = round(mean(mean_price),0),
    sd_mean_price = round(sd(mean_price),0)
  )
mese media prezzo medio sd prezzo medio
1 145640 29819
2 148840 25120
3 151137 23238
4 151461 26174
5 158235 25787
6 161546 23470
7 156881 27220
8 156456 28253
9 156522 29669
10 155897 32527
11 154233 29685
12 154996 27009

Vendite

summary_month_sales <- data_ret %>%
  group_by(month) %>%
  summarise(
    mean_sales = round(mean(sales),0),
    sd_sales = round(sd(sales),0)
  )
mese media vendite sd prezzo medio
1 127 43
2 141 51
3 189 59
4 212 65
5 239 83
6 244 95
7 236 96
8 231 79
9 182 73
10 180 75
11 157 55
12 169 61

Analisi condizionata per città

Prezzo medio

summary_city_mean_price <- data_ret %>%
  group_by(city) %>%
  summarise(
    mean_mean_price = round(mean(mean_price),0),
    sd_mean_price = round(sd(mean_price),0)
  )
città media prezzo medio sd prezzo medio
Beaumont 146640 11232
Bryan-College Station 183534 15149
Tyler 167677 12351
Wichita Falls 119430 11398

Vendite

summary_city_sales <- data_ret %>%
  group_by(city) %>%
  summarise(
    mean_sales = round(mean(sales),0),
    sd_sales = round(sd(sales),0)
  )
città media vendite sd vendite
Beaumont 177 41
Bryan-College Station 206 85
Tyler 270 62
Wichita Falls 116 22

Grafici

ggplot(summary_year_mean_price, aes(x = as.integer(year), y = mean_mean_price)) +
  geom_line(color = "darkblue", size = 1) +
  geom_point(color = "darkblue", size = 3) +
  labs(
    x = NULL,
    y = "Prezzo medio",
    title = "Andamento della media del prezzo medio per anno"
  ) +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "))+
  theme_light()

ggplot(summary_year_sales, aes(x = as.integer(year), y = mean_sales)) +
  geom_line(color = "darkorange", size = 1) +
  geom_point(color = "darkorange", size = 3) +
  labs(
    x = NULL,
    y = "Vendite",
    title = "Andamento della media del numero vendite per anno"
  ) +
  theme_light()

ggplot(summary_month_mean_price,
       aes(x = month, y = mean_mean_price)) +
  geom_bar(
    aes(fill = mean_mean_price),
    stat = "identity",
    color = "black",
    show.legend = FALSE 
  ) +
  scale_fill_gradient(
    low = "lightgray",        
    high = "darkblue"         
  ) +
  labs(
    x = NULL,     
    y = "Prezzo medio",
    title = "Valore medio del prezzo medio per mese"
  ) +
  scale_x_continuous(
    breaks = seq(1, 12, by = 1),
    labels = months) +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "))+
  theme_light()

ggplot(summary_month_sales,
       aes(x = month, y = mean_sales)) +
  geom_bar(
    aes(fill = mean_sales),
    stat = "identity",
    color = "black",
    show.legend = FALSE 
  ) +
  scale_fill_gradient(
    low = "lightgray",        
    high = "darkorange"         
  ) +
  labs(
    x = NULL,     
    y = "Vendite",
    title = "Valore medio del numero di vendite per mese"
  ) +
  scale_x_continuous(
    breaks = seq(1, 12, by = 1),
    labels = months) +
  theme_light()

ggplot(summary_city_mean_price,
       aes(x = city, y = mean_mean_price)) +
  geom_bar(
    aes(fill=mean_mean_price),
    stat = "identity",
    color = "black",
    show.legend = FALSE
    ) +
  scale_fill_gradient(
    low = "lightgray",        
    high = "darkblue"         
  ) +
  labs(
    x = NULL,
    y = "Prezzo medio",
    title = "Media del prezzo medio per città"
  ) +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "))+
  theme_light()

Commento

L’analisi condizionata per anno, mese e città rivela marcate differenze geografiche e temporali.

Negli anni (2010-2014), si osserva una crescita sia del numero medio di vendite (+36.7%), sia del prezzo di vendita medio (+8.9%). La crescita è costante ad eccezione del 2011 che registra il valore medio più basso sia di prezzo medio (148251$) sia di numero di vendite (164). Negli anni cresce anche la variabilità, soprattutto nel numero medio di vendite (2010 ±61, 2014 ±96).

A livello stagionale, prezzi e vendite raggiungono il picco nel mese di giugno, con una media di 161546$ e 244 vendite. I mesi da ottobre a gennaio sono caratterizzati da una progressiva contrazione e da una maggiore variabilità dei prezzi medi. Gennaio è il mese con il prezzo medio più basso (145640$) e con in media meno vendite (127).

La città di Bryan-College Station ha il prezzo medio più alto, con una media 183534$. Questo primato potrebbe essere viziato dalla presenza alcuni immobili o quartieri di lusso. Inoltre, Bryan-College Station mostra la maggiore variabilità sia nel prezzo medio (±15149$) sia nel numero di vendite (±85). La città di Tyler domina in numero di vendite medio (270). Wichita Falls è la città con il prezzo medio più basso (119430$) e con in media meno vendite (116).

Ho voluto indagare un possibile trade-off tra prezzo medio e numero di vendite, elaborando un grafico a punti. Bryan-College Station (prezzi alti, vendite medie) e Tyler (prezzi medi, vendite alte) mostrano infatti una relazione inversa, mentre Wichita Falls (prezzi bassi, vendite basse) e Beaumont (prezzi medi, vendite medie) sfuggono a questo schema.

ggplot(data_ret, aes(x = mean_price, y = sales, label = city)) +
  geom_point(aes(color=city),
             size = 2) +
  labs(x = "Prezzo medio",
       y = "Vendite",
       title = "Vendite/prezzo medio per città",
       color = "Città")+
  scale_color_brewer(palette = "Set2")+
  scale_x_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "))+
  theme_light()

Visualizzazioni con ggplot2

Boxplot per confrontare la distribuzione del prezzo mediano e medio tra le città

Per proseguire l’analisi ho preferito considerare in parallelo anche la distribuzione del prezzo medio.

Prezzo mediano
città Beaumont Bryan-College Station Tyler Wichita Falls
min 106700 140700 120600 73800
q1 123025 150800 133975 92800
median 130750 155400 142200 102300
mean 129988 157488 141442 101743
q3 134550 161975 147675 109175
max 163800 180000 161600 135300
range 57100 39300 41000 61500
IQR 11525 11175 13700 16375
Prezzo medio
city Beaumont Bryan-College Station Tyler Wichita Falls
min 120713 151816 143464 97010
q1 141631 170685 158750 111597
median 145809 183185 167224 119785
mean 146640 183534 167677 119430
q3 153598 194513 175050 125446
max 174724 213234 193787 148775
range 54011 61418 50323 51765
IQR 11967 23828 16300 13849

Il confronto tra prezzo mediano e medio nelle quattro città rivela un mercato immobiliare stratificato, in cui è presente una differenza sistematica tra media e mediana in tutte le città (in media +22088$).

Bryan-College Station si conferma come località più prestigiosa, con il prezzo mediano (mediana 155400$) e medio (mediana 183185$) nettamente superiori alle altre città; la marcata differenza tra le due metriche (+27785$) può essere considerata un segno dell’influenza di immobili di lusso.

Wichita Falls resta la città più economica, con il prezzo mediano (mediana 102300$) e medio (mediana 119430$) significativamente inferiori. La modesta differenza media-mediana (+17130$) suggerisce una distribuzione più equilibrata.

Come confermato anche dal seguente grafico a dispersione non sono presenti significativi outliers, ad eccezione di un valore per Beaumont. Viene mostrata una evidente correlazione positiva, quasi lineare, tra prezzo medio e mediano.

ggplot(data_ret, aes(x = mean_price, y = median_price, label = city)) +
  geom_point(aes(color=city),
             size = 1.5) +
  labs(x = "Prezzo medio",
       y = "Prezzo mediano",
       title = "Prezzo mediano/prezzo medio per città",
       color = "Città")+
  scale_x_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "),
    limits = c(73000, 220000), 
    breaks = seq(75000, 250000, by = 50000)) +  
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", big.mark = " "),
    limits = c(73000, 220000), 
    breaks = seq(75000, 250000, by = 50000)) + 
  coord_equal() + 
  scale_color_brewer(palette = "Set2")+
  theme_light()

Grafici a barre sovrapposte per confrontare il totale delle vendite nei vari mesi, considerando le città (2010-2014)

data_perc_moth_city_sales <- data_ret %>%
  group_by(month, city) %>%
  summarise(total_sales = sum(sales), .groups = 'drop') %>%
  group_by(month) %>%
  mutate(percent = total_sales/sum(total_sales)) %>%
  ungroup()

ggplot(data_perc_moth_city_sales, aes(x = as.integer(month), y = percent, fill = city)) +
  geom_col(position = "fill") +
  geom_text(aes(label = percent(percent, accuracy = 1)),
            position = position_fill(vjust = 0.5),
            size = 4, color = "white",
            fontface = "bold") +
  scale_x_continuous(
    breaks = 1:12,
    labels = months
  ) +
  scale_y_continuous(labels = percent_format()) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    x = NULL,
    y = NULL,
    title = "Distribuzione aggregata delle vendite (2010-2014)",
    fill = "Città"
  ) +
  theme_light()+
  theme(legend.position = "top")

Commento

L’analisi integrata dei tre grafici conferma un mercato immobiliare caratterizzato da forti disparità geografiche e una marcata stagionalità, evidenziando un picco estivo generalizzato, sostenuto soprattutto dalle città di Tyler e Bryan-College Station.

Tyler emerge come il motore principale, con una quota di mercato costantemente superiore al 34%. Segue Bryan-College Station, dai prezzi più elevati, che mantiene livelli meno costanti con un’escursione del 12% (21-33%). Beaumont (20-26%) e Wichita Falls (13-16%) mostrano un mercato piuttosto stabile, con un leggero aumento percentuale in autunno e inverno.

Line charts del valore totale delle vendite

data_ret_aggr_volume <- data_ret %>%
  group_by(date_var) %>%
  summarise(total_volume = sum(volume, na.rm = TRUE), .groups = 'drop')

ggplot(data = data_ret_aggr_volume) +
  geom_line(aes(
    x=date_var,
    y=total_volume)
    ) +
  scale_x_date(
    breaks = "1 year",
    labels = scales::date_format("%b %Y"),
    ) +
   scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M", big.mark = ".", decimal.mark = ","),
    n.breaks = 8
  ) +
  geom_smooth(aes(
    x = date_var,
    y = total_volume),
    method = "lm",
    color = "red",
    se = FALSE) +
  labs(
    y = NULL,
    x = NULL,
    title = "Andamento del volume delle vendite")+
  theme_light()

data_aggr_volume_year <- data_ret %>%
  group_by(year, month) %>%
  summarise(total_volume = sum(volume), .groups = 'drop')

ggplot(data_aggr_volume_year) +
  geom_line(aes(
    x = month,
    y = total_volume,
    color = as.character(year)),
    linewidth = 1
  ) +
  geom_point(aes(
    x = month,
    y = total_volume,
    color = as.character(year)),
    size = 2
  ) +
  scale_x_continuous(
    breaks = 1:12,
    labels = months
  ) +
 scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M", big.mark = ".", decimal.mark = ","),
    n.breaks = 8
  ) +
  labs(
    x = NULL,
    y = NULL,
    title = "Confronto tra annate del volume delle vendite"
  ) +
  scale_color_manual(
    values = colorRampPalette(c("lightgreen", "darkgreen"))(5),
    name = "Anno"
  )+
  theme_light() +
  theme(
    legend.position = "top",
  )

ggplot(data = data_ret) +
  geom_line(aes(x = date_var, y = volume, color = city)) +
  geom_smooth(aes(x = date_var, y = volume, color = city), method = "lm", se = FALSE) +
  labs(
    x = NULL,
    y = NULL,
    color = "Città",
    title = "Andamento del volume delle vendite per città (2010-2014)"
  ) +
   scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M", big.mark = ".", decimal.mark = ",")
  ) +
scale_x_date(
    breaks = "1 year",
    labels = scales::date_format("%b\n%Y"),
    ) +
  scale_color_brewer(palette = "Set2")+
  theme_light()+
  theme(legend.position = "top")

ggplot(data = data_ret) +
  geom_line(aes(x = date_var, y = volume, color = city)) +
  geom_smooth(aes(x = date_var, y = volume, color = city), method = "lm", se = FALSE) +
  facet_wrap(~ city,
             labeller = labeller(city = NULL)) +
  labs(
    x = NULL,
    y = NULL,
    color = "Città",
    title = "Andamento del volume delle vendite nel tempo per città (2010-2014)"
  ) +
   scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M", big.mark = ".", decimal.mark = ",")
  ) +
  scale_x_date(
    breaks = "1 year",
    labels = scales::date_format("%b\n%Y"),
    )+
  scale_color_brewer(palette = "Set2")+
  theme_light()+
  theme(legend.position = "top",
        strip.text = element_blank())

I grafici rivelano una crescita complessiva del volume delle vendite, e le città di Tyler e di Bryan-College Station si confermano come traino principale del mercato. Bryan-College Station mostra picchi elevati, ma anche una maggiore volatilità stagionale. Tyler, invece, mantiene volumi medi più consistenti e una tendenza stabile, indicando un mercato solido e meno sensibile alle fluttuazioni. Beaumont registra una crescita costante ma moderata, con minore variabilità rispetto alle città leader, riflettendo probabilmente un’espansione più graduale. Wichita Falls contribuisce in misura significativamente inferiore al totale, con una trascurabile crescita che riflette il suo ruolo marginale nel panorama analizzato.

Boxplot o per confrontare la distribuzione del valore totale delle vendite tra le varie città e tra i vari anni

Ho deciso di confrontare questi valori con il valore generato dal singolo annuncio, per comprendere in modo più approfondito il rendimento di ciascuna città e valutare eventuali strategie.

ggplot(data_ret, aes(x = city, y = volume, fill = city)) +
  geom_boxplot(
    alpha = 0.8,
    outlier.color = "#E41A1C",
    outlier.shape = 19
  ) +
  stat_summary(
    fun = mean,
    geom = "point",
    shape = 18,
    size = 3,
    color = "black"
  ) +
  scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M")) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    x = NULL,
    y = NULL,
    title = "Distribuzione per città del volume delle vendite",
    subtitle = "Punto nero: media, punti rossi: outlier"
  ) +
  theme_light() +
  theme(
    legend.position = "none",
    plot.subtitle = element_text(size = 8))

ggplot(data_ret, aes(x = city, y = listing_value, fill = city)) +
  geom_boxplot(
    alpha = 0.8,
    outlier.color = "#E41A1C",
    outlier.shape = 19
  ) +
  stat_summary(
    fun = mean,
    geom = "point",
    shape = 18,
    size = 3,
    color = "black"
  ) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    x = NULL,
    y = NULL,
    title = "Distribuzione per città del valore generato da ogni annuncio attivo",
    subtitle = "Punto nero: media, Punti rossi: outlier"
  ) +
  scale_y_continuous(
  labels = scales::dollar_format(prefix = "$", big.mark = " "))+
  theme_light() +
  theme(
    legend.position = "none",
    plot.subtitle = element_text(size = 8))

ggplot(data_ret, aes(x = as.factor(year), y = volume, fill = year)) +
  geom_boxplot(
    alpha = 0.7,
    outlier.color = "#E41A1C",
    outlier.shape = 19,
    show.legend = FALSE
  ) +
  scale_fill_gradient(
    low = "lightgreen",
    high = "darkgreen",
    guide = "none"
  ) +
  stat_summary(
    fun = mean,
    geom = "point",
    shape = 18,
    size = 3,
    color = "black"
  ) +
    scale_y_continuous(
    labels = scales::dollar_format(prefix = "$", suffix = "M")) +
  labs(
    x = NULL,
    y = NULL,
    title = "Evoluzione annuale della distribuzione del volume delle vendite",
    subtitle = "Punto nero: media, punti rossi: outlier"
  ) +
  theme_light() +
  theme(
    plot.subtitle = element_text(size = 8))

ggplot(data_ret, aes(x = as.factor(year), y = listing_value, fill = year)) +
  geom_boxplot(
    alpha = 0.7,
    outlier.color = "#E41A1C",
    outlier.shape = 19,
    show.legend = FALSE
  ) +
  scale_fill_gradient(
    low = "lightgreen",
    high = "darkgreen",
    guide = "none"
  ) +
  stat_summary(
    fun = mean,
    geom = "point",
    shape = 18,
    size = 3,
    color = "black"
  ) +
  labs(
    x = NULL,
    y = NULL,
    title = "Evoluzione annuale della distribuzione del valore generato da ogni annuncio attivo",
    subtitle = "Punto nero: media, punti rossi: outlier"
  ) +
  scale_y_continuous(
  labels = scales::dollar_format(prefix = "$", big.mark = " "))+
  theme_light() +
  theme(
    plot.title = element_text(size = 12),
    plot.subtitle = element_text(size = 8))

I boxplot evidenziano un andamento parallelo tra il volume complessivo delle vendite e il valore generato per annuncio attivo, sebbene con dinamiche differenziate a livello territoriale.

L’incremento progressivo degli outlier nel valore generato per annuncio attivo segnala una disuguaglianza crescente nella performance degli annunci alimentata da transazioni eccezionali che superano di molto i valori medi.

Questo fenomeno sottolinea una marcata polarizzazione del mercato, ed è riconducibile a due fattori principali: la stagionalità delle transazioni e la presenza di un segmento premium, particolarmente pronunciato a Bryan-College Station, dove si concentrano immobili di lusso con valutazioni elevate.

Un aspetto interessante riguarda Wichita Falls: nonostante volumi di vendita sensibilmente inferiori rispetto alle altre città, il valore per annuncio non presenta scostamenti significativi da Tyler e Beaumont. Questo suggerisce che, pur in un contesto di minore liquidità, il mercato locale mantiene una discreta capacità di generare valore per singola transazione, seppur senza picchi particolarmente rilevanti. Il dato è confermato da un buona media del tasso di conversione tra annunci attivi e vendite.

Città % di conversione annunci / vendite commento
Beaumont 10.61 Andamento medio senza picchi
Bryan-College Station 14.73 Performance migliore, domanda alta per immobili premium
Tyler 9.35 Più vendite, ma minore efficacia per annuncio (volume compensa)
Wichita Falls 12.80 Buona efficienza, mercato piccolo ma reattivo

Conclusioni

Per concludere vorrei fare qualche commento all’efficacia degli annunci, al fine di suggerire delle strategie per migliorare il mercato.

data_agg_listing_eff_city_month <- data_ret %>%
  group_by(city, month) %>%
  summarise(total_listing_eff = round(mean(listing_eff), 2), .groups = 'drop')

ggplot(data_agg_listing_eff_city_month, aes(x=city, y=month, fill=total_listing_eff)) +
  geom_tile(color="white") +
  geom_text(aes(label=paste0(round(total_listing_eff,1),"%")), color="black") +
  labs(
    title = "Efficacia degli annunci immobiliari per città e mese",
    x = NULL,
    y = NULL,
    ) +
  scale_fill_gradient(low="lightgrey", high="purple")+
  scale_y_continuous(
    breaks = 1:12,
    labels = months
  ) +
  theme_light()+
  theme(legend.position = "null")

L’analisi evidenzia come l’efficacia degli annunci vari significativamente tra città e stagioni. Bryan-College Station domina nei mesi estivi (picchi del 22% a luglio), confermando l’attrattiva del suo segmento premium, mentre Wichita Falls mantiene performance costanti (10-15% tutto l’anno) grazie a un mercato stabile. Tyler, nonostante l’alto volume di vendite, mostra un’efficacia inferiore (9.4%), suggerendo margini di miglioramento nel potenziale degli annunci.

Per ottimizzare i risultati si potrebbero realizzare campagne stagionali mirate e un pricing dinamico per capitalizzare i periodi di alta domanda.

Alcuni esempi: