L’azienda Texas Realty Insights vuole analizzare le tendenze del mercato immobiliare in Texas per supportare decisioni strategiche di vendita e ottimizzazione delle inserzioni. Il dataset contiene 240 osservazioni mensili relative a 4 città del Texas nel periodo 2010–2014, con informazioni su vendite, volumi, prezzi mediani e annunci attivi.
Il dataset si compone di 8 variabili, classificate come segue:
city — qualitativa nominale (4
categorie)year,
month — numeriche con valenza temporale,
da trattare come variabili di stratificazionesales,
listings — quantitative discretevolume,
median_price,
months_inventory — quantitative
continueVerifichiamo la struttura del dataset:
glimpse(realestate)
## Rows: 240
## Columns: 8
## $ city <chr> "Beaumont", "Beaumont", "Beaumont", "Beaumont", "Beau…
## $ year <dbl> 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010,…
## $ month <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5,…
## $ sales <dbl> 83, 108, 182, 200, 202, 189, 164, 174, 124, 150, 150,…
## $ volume <dbl> 14.162, 17.690, 28.701, 26.819, 28.833, 27.219, 22.70…
## $ median_price <dbl> 163800, 138200, 122400, 123200, 123100, 122800, 12430…
## $ listings <dbl> 1533, 1586, 1689, 1708, 1771, 1803, 1857, 1830, 1829,…
## $ months_inventory <dbl> 9.5, 10.0, 10.6, 10.6, 10.9, 11.1, 11.7, 11.6, 11.7, …
Controlliamo che il panel sia bilanciato:
table(realestate$city, realestate$year)
##
## 2010 2011 2012 2013 2014
## Beaumont 12 12 12 12 12
## Bryan-College Station 12 12 12 12 12
## Tyler 12 12 12 12 12
## Wichita Falls 12 12 12 12 12
Tutte le celle contengono 12 osservazioni (i 12 mesi), confermando che il dataset è perfettamente bilanciato.
Calcoliamo gli indici sintetici per le cinque variabili numeriche
significative attraverso la funzione describe() del
pacchetto psych:
describe(realestate[, c("sales", "volume", "median_price", "listings", "months_inventory")])
## vars n mean sd median trimmed mad
## sales 1 240 192.29 79.65 175.50 184.97 82.28
## volume 2 240 31.01 16.65 27.06 29.17 16.16
## median_price 3 240 132665.42 22662.15 134500.00 133607.81 24092.25
## listings 4 240 1738.02 752.71 1618.50 1677.36 879.92
## months_inventory 5 240 9.19 2.30 8.95 9.20 2.15
## min max range skew kurtosis se
## sales 79.00 423.00 344.00 0.71 -0.34 5.14
## volume 8.17 83.55 75.38 0.88 0.15 1.07
## median_price 73800.00 180000.00 106200.00 -0.36 -0.64 1462.84
## listings 743.00 3296.00 2553.00 0.65 -0.81 48.59
## months_inventory 3.40 14.90 11.50 0.04 -0.20 0.15
Per ottenere quartili e IQR , non inclusi in describe()
, utilizziamo summary() e sapply():
summary(realestate[, c("sales", "volume", "median_price", "listings", "months_inventory")])
## sales volume median_price listings
## Min. : 79.0 Min. : 8.166 Min. : 73800 Min. : 743
## 1st Qu.:127.0 1st Qu.:17.660 1st Qu.:117300 1st Qu.:1026
## Median :175.5 Median :27.062 Median :134500 Median :1618
## Mean :192.3 Mean :31.005 Mean :132665 Mean :1738
## 3rd Qu.:247.0 3rd Qu.:40.893 3rd Qu.:150050 3rd Qu.:2056
## Max. :423.0 Max. :83.547 Max. :180000 Max. :3296
## months_inventory
## Min. : 3.400
## 1st Qu.: 7.800
## Median : 8.950
## Mean : 9.193
## 3rd Qu.:10.950
## Max. :14.900
sapply(realestate[, c("sales", "volume", "median_price", "listings", "months_inventory")], IQR)
## sales volume median_price listings
## 120.0000 23.2335 32750.0000 1029.5000
## months_inventory
## 3.1500
Calcoliamo infine il coefficiente di variazione (CV = sd/media), che useremo per confrontare la variabilità tra variabili con scale diverse essendo una misura adimensionale:
sapply(realestate[, c("sales", "volume", "median_price", "listings", "months_inventory")],
function(x) sd(x) / mean(x))
## sales volume median_price listings
## 0.4142203 0.5370536 0.1708218 0.4330833
## months_inventory
## 0.2506031
Per city, year e month non ha
senso calcolare media o varianza: ricostruiamo invece le distribuzioni
di frequenza.
table(realestate$city)
##
## Beaumont Bryan-College Station Tyler
## 60 60 60
## Wichita Falls
## 60
table(realestate$year)
##
## 2010 2011 2012 2013 2014
## 48 48 48 48 48
table(realestate$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
Tutte le distribuzioni risultano uniformi, 60 osservazioni per ognuna delle quattro città, 48 osservazioni per ognuno dei 5 anni coperti dal dataset e 20 osservazioni per ognuno dei dodici mesi dell’anno, coerentemente con la natura di panel bilanciato del dataset.
median_price mostra media
(132.665 $) e mediana (134.500 $) molto vicine: la lieve prevalenza
della mediana sulla media e la skewness negativa (−0.36) indicano una
distribuzione quasi simmetrica, leggermente asimmetrica a sinistra.sales,
volume e
listings presentano media maggiore della
mediana e skewness positiva, segnalando code destre dovute alla presenza
nel dataset di città di dimensione molto diversa.months_inventory è la variabile più
simmetrica (skewness ≈ 0.04), coerentemente con la sua natura di
indicatore già “normalizzato” (un rapporto, non un aggregato).volume è la
variabile con maggiore variabilità relativa (53.7%), mentre
median_price la più stabile (17.1%).Come già detto confrontare le deviazioni standard delle variabili avrebbe poco senso, poiché esse hanno unità di misura e ordini di grandezza diversi (dollari, mesi, milioni…). Utilizziamo quindi il coefficiente di variazione, adimensionale, già calcolato nello Step 2:
sapply(realestate[, c("sales", "volume", "median_price", "listings", "months_inventory")],
function(x) sd(x) / mean(x))
## sales volume median_price listings
## 0.4142203 0.5370536 0.1708218 0.4330833
## months_inventory
## 0.2506031
La variabile con maggiore variabilità relativa è
volume con CV = 0.537, seguita da
listings (0.43) e sales (0.41). All’estremo
opposto, median_price è la più stabile (0.17).
Questo risultato è coerente con la natura dei dati: il volume di vendita, dato dal prodotto tra numero di transazioni e prezzi, vista la coesistenza nel dataset di città di scala molto diversa (da Beaumont a Bryan-College Station), “raccoglie” al suo interno la variabilità di entrambi i fattori che lo compongono che possono variare in sincronia spostandosi da una città all’altra.
Per confrontare l’asimmetria utilizziamo il valore assoluto della skewness:
sapply(realestate[, c("sales", "volume", "median_price", "listings", "months_inventory")],
function(x) abs(skewness(x)))
## sales volume median_price listings
## 0.71810402 0.88474203 0.36455288 0.64949823
## months_inventory
## 0.04097527
La variabile più asimmetrica è nuovamente
volume (|skew| = 0.88), con asimmetria
positiva (coda destra): la media (31.0) supera la
mediana (27.1), confermando il pattern.
In base alle soglie convenzionali:
volume, sales e listings
ricadono nella fascia di moderata asimmetria positiva,
mentre median_price e months_inventory
risultano quasi simmetriche.
volume risulta prima in entrambe le classifiche, sia per
variabilità che per asimmetria. Non è una coincidenza: la coda destra
(pochi valori molto alti, dovuti alle città grandi) inflaziona sia la
deviazione standard sia la skewness. Le due caratteristiche sono
manifestazioni dello stesso fenomeno : la coesistenza nel dataset di
realtà urbane di scala economica eterogenea.
Dati i valori trovati nel punto precedente scelgo come variabile target da suddividere il ‘median price’, la sua skewness è tra le più basse, indice di maggiore simmetria quindi la divisione in classi permette una ripartizione abbastanza uniforme delle osservazioni. Inoltre racconta una utilità maggiore dal punto di vista del mercato immobiliare per capire il posizionamento delle inserzioni.
Il range di “median_price” è [73.800-180.000] , volendo dividere in 5 classi ognuna misurerebbe (180.000-73.800)/5 = 21.240. Per comodità arrotondo a 25.000 l’ampiezza di ogni classe.
realestate <- realestate %>%
mutate(price_class = cut(median_price,
breaks = c(60000, 85000, 110000, 135000, 160000, 185000),
include.lowest = TRUE,
labels = c("60-85k", "85-110k", "110-135k", "135-160k", "160-185k")))
freq_ass <- table(realestate$price_class)
freq_ass
##
## 60-85k 85-110k 110-135k 135-160k 160-185k
## 2 47 74 96 21
freq_rel <- prop.table(freq_ass)
round(freq_rel, 3)
##
## 60-85k 85-110k 110-135k 135-160k 160-185k
## 0.008 0.196 0.308 0.400 0.088
Il grafico più adatto a rappresentare le varie classi è quello a barre:
ggplot(realestate,aes(x=price_class))+
geom_bar(fill="steelblue",color= "white")+
labs(title="Distribuzione del prezzo mediano in classi",
x="Classe di prezzo (USD)",
y="Frequenza assoluta") +
theme_minimal()
L’indice di eterogeneità di Gini misura quanto le osservazioni sono distribuite uniformemente tra le classi:
\[G = 1 - \sum_{i=1}^{k} f_i^2\] L’indice può variare tra 0 (caso di massima omogeneità quindi osservazioni tutte in una classe) e 1-1/k.
k<-length(freq_rel)
G<- 1-sum(freq_rel^2)
G_max<-(k-1)/k
G_norm<-G/G_max
cat("Numero di classi (k):", k, "\n")
## Numero di classi (k): 5
cat("Indice di Gini (G):", round(G, 4), "\n")
## Indice di Gini (G): 0.6989
cat("Gini massimo (G_max):", round(G_max, 4), "\n")
## Gini massimo (G_max): 0.8
cat("Gini normalizzato (G'):", round(G_norm, 4), "\n")
## Gini normalizzato (G'): 0.8736
La divisione in classi della variabile ‘median_price’ permette di osservare la prevalenza delle inserzioni nella classe [135k-160k] che coprono il 40% del dataset e più in generale una concentrazione negli intervalli centrali che lascia agli estremi poco meno del 10% delle inserzioni. La skewness calcolata nello step 2 anticipava questo risultato raccontando una distribuzione quasi simmetrica con una coda leggermente più lunga sul lato sinistro( skewness -0,36).
Nel nostro dataset la probabilità di estrarre una riga riferita alla città di Beaumont è data da:
sum(realestate$city == "Beaumont") / nrow(realestate)
## [1] 0.25
il valore ottenuto di 0,25 conferma quanto visto nello step 1, le quattro città sono equamente rappresentate con 60 osservazioni per una, pertanto equiprobabili (25% l’una).
La probabilità di estrarre un’inserzione del mese di luglio è data da:
sum(realestate$month == 7) / nrow(realestate)
## [1] 0.08333333
il valore trovato di 0,083 equivale a 1/12 a conferma del fatto che tutti i mesi dell’anno come visto in precedenza sono equamente presenti nel dataset.
La probabilità di estrarre inserzioni relative a dicembre 2012 è data invece da:
sum(realestate$month==12 & realestate$year==2012)/nrow(realestate)
## [1] 0.01666667
il valore di 0,0167 corrisponde a 4/240 , quindi 1/60.
Nello Step 2 abbiamo visto che le inserzioni sono equamente distribuite
per anno e per mese quindi,essendo indipendenti i singoli eventi,
P(dicembre ∩ 2012)= P(dicembre) * P(2012),
con P(dicembre)=1/12 e P(2012)=1/5
P(dicembre ∩ 2012)= 1/12 * 1/5 = 1/60 come appunto risulta.
Con le variabili a disposizione vado a fare feature engineering creando due variabili utili alla mia analisi del mercato immobiliare texano: il prezzo medio per immobile e un indicatore adatto a verificare l’efficacia degli annunci.
Per calcolare il prezzo medio per immobile divido il valore totale delle vendite per il numero effettivo di vendite, visto che il primo valore è espresso in milioni di dollari dovrò moltiplicarlo adeguatamente per rendere la misura corretta.
realestate$mean_price <- (realestate$volume * 1000000) / realestate$sales
summary(realestate$mean_price)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 97010 132939 156588 154320 173915 213234
per cogliere eventuali asimmetrie scorro riga per riga per vedere su quante di queste il prezzo medio appena trovato supera il prezzo mediano.
prop_mean_greater<-mean(realestate$mean_price>realestate$median_price)
round(prop_mean_greater*100,2)
## [1] 99.58
il risultato trovato ci dice che nel 99,58 percento dei casi (239 mesi su 240 osservati) il prezzo medio delle transazioni supera il prezzo mediano, dinamica del mercato immobiliare nota dove poche transazioni di valore alto(case di lusso,fondi commerciali ecc.) possono trascinare verso l’alto la media ovviamente senza intaccare la mediana, indicatore robusto agli outlier.
Per determinare l’efficacia degli annunci le misure da utilizzare sono due: il totale delle vendite ‘sales’ e il numero di annunci attivi ‘listings’:
realestate$effectiveness <- realestate$sales/realestate$listings
summary(realestate$effectiveness)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.05014 0.08980 0.10963 0.11874 0.13492 0.38713
Visto che sarebbe approssimativo basare l’analisi su un valore univoco divido l’analisi per città:
realestate %>%
group_by (city)%>%
summarise(efficacia_media= mean(effectiveness),
efficacia_sd= sd(effectiveness)) %>%
arrange(desc(efficacia_media))
## # A tibble: 4 × 3
## city efficacia_media efficacia_sd
## <chr> <dbl> <dbl>
## 1 Bryan-College Station 0.147 0.0729
## 2 Wichita Falls 0.128 0.0247
## 3 Beaumont 0.106 0.0267
## 4 Tyler 0.0935 0.0235
Bryan-College Station risulta la città dove le campagne di marketing funzionano meglio seppur la deviazione standard dell’indice di efficacia sia 3 volte maggiore delle altre.
Le due features calcolate ci permettono rispettivamente di confermare: -la natura asimmetrica del mercato immobiliare, con outlier di valori(transazioni su case di lusso, complessi residenziali, edifici commerciali..) che trascinano le medie della transazioni verso l’alto in praticamente ogni mese osservato -in quali città le campagne di marketing vadano rafforzate per aumentare il tasso di vendita sulla base degli annunci pubblicati.
Usando il pacchetto dplyr vado ad analizzare il dataset
suddividendolo rispettivamente per città, anno e mese e per ciascuno di
questi gruppi calcolo le statistiche di sintesi
realestate %>%
group_by(city) %>%
summarise(
media_vendite = mean(sales),
sd_vendite = sd(sales),
media_volume = mean(volume),
sd_volume = sd(volume),
media_prezzo = mean(mean_price),
sd_prezzo = sd(mean_price),
media_efficacia = mean(effectiveness),
sd_efficacia = sd(effectiveness),
media_inventory = mean(months_inventory),
sd_inventory = sd(months_inventory)
) %>%
kable(digits = 2, caption = "Statistiche di sintesi per città") %>%
kable_styling(bootstrap_options = c("striped", "hover")) %>%
scroll_box(width = "100%")
| city | media_vendite | sd_vendite | media_volume | sd_volume | media_prezzo | sd_prezzo | media_efficacia | sd_efficacia | media_inventory | sd_inventory |
|---|---|---|---|---|---|---|---|---|---|---|
| Beaumont | 177.38 | 41.48 | 26.13 | 6.97 | 146640.4 | 11232.13 | 0.11 | 0.03 | 9.97 | 1.65 |
| Bryan-College Station | 205.97 | 84.98 | 38.19 | 17.25 | 183534.3 | 15149.35 | 0.15 | 0.07 | 7.66 | 2.25 |
| Tyler | 269.75 | 61.96 | 45.77 | 13.11 | 167676.8 | 12350.51 | 0.09 | 0.02 | 11.32 | 1.89 |
| Wichita Falls | 116.07 | 22.15 | 13.93 | 3.24 | 119430.0 | 11398.48 | 0.13 | 0.02 | 7.82 | 0.78 |
realestate %>%
group_by(year) %>%
summarise(
media_vendite = mean(sales),
sd_vendite = sd(sales),
media_volume = mean(volume),
sd_volume = sd(volume),
media_prezzo = mean(mean_price),
sd_prezzo = sd(mean_price),
media_efficacia = mean(effectiveness),
sd_efficacia = sd(effectiveness),
media_inventory = mean(months_inventory),
sd_inventory = sd(months_inventory)
) %>%
kable(digits = 2, caption = "Statistiche di sintesi per anno") %>%
kable_styling(bootstrap_options = c("striped", "hover")) %>%
scroll_box(width = "100%")
| year | media_vendite | sd_vendite | media_volume | sd_volume | media_prezzo | sd_prezzo | media_efficacia | sd_efficacia | media_inventory | sd_inventory |
|---|---|---|---|---|---|---|---|---|---|---|
| 2010 | 168.67 | 60.54 | 25.68 | 10.80 | 150188.6 | 23279.55 | 0.10 | 0.03 | 9.97 | 2.08 |
| 2011 | 164.12 | 63.87 | 25.16 | 12.20 | 148250.6 | 24938.38 | 0.09 | 0.02 | 10.90 | 2.07 |
| 2012 | 186.15 | 70.91 | 29.27 | 14.52 | 150898.7 | 26438.50 | 0.11 | 0.03 | 9.88 | 1.61 |
| 2013 | 211.92 | 84.00 | 35.15 | 17.93 | 158705.2 | 26523.81 | 0.13 | 0.04 | 8.15 | 1.69 |
| 2014 | 230.60 | 95.51 | 39.77 | 21.19 | 163558.7 | 31740.53 | 0.16 | 0.06 | 7.06 | 1.75 |
realestate %>%
group_by(month) %>%
summarise(
media_vendite = mean(sales),
sd_vendite = sd(sales),
media_volume = mean(volume),
sd_volume = sd(volume),
media_prezzo = mean(mean_price),
sd_prezzo = sd(mean_price),
media_efficacia = mean(effectiveness),
sd_efficacia = sd(effectiveness),
media_inventory = mean(months_inventory),
sd_inventory = sd(months_inventory)
) %>%
kable(digits = 2, caption = "Statistiche di sintesi per mese") %>%
kable_styling(bootstrap_options = c("striped", "hover")) %>%
scroll_box(width = "100%")
| month | media_vendite | sd_vendite | media_volume | sd_volume | media_prezzo | sd_prezzo | media_efficacia | sd_efficacia | media_inventory | sd_inventory |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 127.40 | 43.38 | 19.00 | 8.37 | 145640.4 | 29819.11 | 0.08 | 0.02 | 8.84 | 1.97 |
| 2 | 140.85 | 51.07 | 21.65 | 10.09 | 148840.5 | 25120.42 | 0.09 | 0.02 | 9.06 | 1.98 |
| 3 | 189.45 | 59.18 | 29.38 | 12.02 | 151136.5 | 23237.92 | 0.12 | 0.03 | 9.40 | 2.06 |
| 4 | 211.70 | 65.40 | 33.30 | 14.52 | 151461.3 | 26174.30 | 0.13 | 0.04 | 9.72 | 2.24 |
| 5 | 238.85 | 83.12 | 39.70 | 19.02 | 158235.0 | 25787.19 | 0.14 | 0.05 | 9.68 | 2.38 |
| 6 | 243.55 | 95.00 | 41.30 | 21.08 | 161545.8 | 23470.46 | 0.14 | 0.06 | 9.70 | 2.41 |
| 7 | 235.75 | 96.27 | 39.12 | 21.41 | 156881.0 | 27220.12 | 0.14 | 0.07 | 9.62 | 2.50 |
| 8 | 231.45 | 79.23 | 38.01 | 18.05 | 156455.6 | 28253.21 | 0.14 | 0.05 | 9.39 | 2.45 |
| 9 | 182.35 | 72.52 | 29.60 | 15.22 | 156522.3 | 29669.41 | 0.11 | 0.03 | 9.19 | 2.52 |
| 10 | 179.90 | 74.95 | 29.08 | 15.13 | 155897.4 | 32527.29 | 0.11 | 0.04 | 8.94 | 2.44 |
| 11 | 156.85 | 55.47 | 24.81 | 11.15 | 154233.0 | 29684.87 | 0.10 | 0.03 | 8.66 | 2.37 |
| 12 | 169.40 | 60.75 | 27.09 | 12.57 | 154995.5 | 27008.87 | 0.12 | 0.04 | 8.12 | 2.27 |
Le tre suddivisioni servono rispettivamente ad analizzare eventuali differenze tra città con strutture economiche diverse (come già accennato nello step 6), i cambiamenti dovuti alla evoluzione temporale nel mercato immobiliare di anno in anno e i pattern stagionali dovuti ai movimenti di ogni mese nel corso degli anni. Tuttavia visto che suddividere per una sola variabile lascia una porzione delle informazioni nascosta procedo ad una analisi incrociata tra variabili per cogliere sfumature dei dati non visibili.
realestate %>%
group_by(city, year) %>%
summarise(
media_vendite = mean(sales),
media_volume = mean(volume),
media_prezzo = mean(mean_price),
media_efficacia = mean(effectiveness),
media_inventory = mean(months_inventory),
.groups = "drop"
) %>%
kable(digits = 2, caption = "Statistiche di sintesi per città × anno") %>%
kable_styling(bootstrap_options = c("striped", "hover")) %>%
scroll_box(width = "100%", height = "400px")
| city | year | media_vendite | media_volume | media_prezzo | media_efficacia | media_inventory |
|---|---|---|---|---|---|---|
| Beaumont | 2010 | 156.17 | 22.65 | 146582.5 | 0.09 | 10.91 |
| Beaumont | 2011 | 144.00 | 21.10 | 145922.0 | 0.08 | 11.73 |
| Beaumont | 2012 | 171.92 | 24.47 | 141475.9 | 0.10 | 10.78 |
| Beaumont | 2013 | 201.17 | 30.31 | 150079.0 | 0.12 | 8.78 |
| Beaumont | 2014 | 213.67 | 32.13 | 149142.7 | 0.13 | 7.64 |
| Bryan-College Station | 2010 | 167.58 | 28.73 | 174601.8 | 0.11 | 8.67 |
| Bryan-College Station | 2011 | 167.42 | 28.93 | 173689.0 | 0.10 | 9.80 |
| Bryan-College Station | 2012 | 196.75 | 35.36 | 179360.6 | 0.12 | 8.94 |
| Bryan-College Station | 2013 | 237.83 | 45.12 | 187315.8 | 0.17 | 6.50 |
| Bryan-College Station | 2014 | 260.25 | 52.81 | 202704.3 | 0.24 | 4.38 |
| Tyler | 2010 | 227.50 | 36.35 | 159537.5 | 0.07 | 12.63 |
| Tyler | 2011 | 238.83 | 38.55 | 160248.0 | 0.08 | 13.47 |
| Tyler | 2012 | 263.50 | 44.01 | 165533.0 | 0.09 | 11.58 |
| Tyler | 2013 | 287.42 | 50.32 | 174501.8 | 0.10 | 10.18 |
| Tyler | 2014 | 331.50 | 59.60 | 178563.5 | 0.12 | 8.76 |
| Wichita Falls | 2010 | 123.42 | 14.97 | 120032.5 | 0.13 | 7.68 |
| Wichita Falls | 2011 | 106.25 | 12.05 | 113143.6 | 0.11 | 8.62 |
| Wichita Falls | 2012 | 112.42 | 13.23 | 117225.3 | 0.13 | 8.21 |
| Wichita Falls | 2013 | 121.25 | 14.85 | 122924.3 | 0.14 | 7.13 |
| Wichita Falls | 2014 | 117.00 | 14.54 | 123824.4 | 0.13 | 7.44 |
realestate %>%
group_by(city, year) %>%
summarise(media_prezzo = mean(mean_price),
sd_prezzo = sd(mean_price),
.groups = "drop") %>%
ggplot(aes(x = year, y = media_prezzo, color = city, fill = city)) +
geom_ribbon(aes(ymin = media_prezzo - sd_prezzo,
ymax = media_prezzo + sd_prezzo),
alpha = 0.2, color = NA) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
labs(title = "Andamento del prezzo medio per città (2010-2014)",
subtitle = "Linea = media annuale; bande di Bollinger = ± 1 sd",
x = "Anno",
y = "Prezzo medio (USD)",
color = "Città",
fill = "Città") +
theme_minimal()
Dal grafico si nota come delle 4 città del dataset nel corso dei cinque anni analizzati solo in due il prezzo medio cresce nel corso degli anni,Tyler e Bryan-College Station. Beaumont e Wichita Falls a livello di prezzi restano pressochè stagnanti al netto di oscillazioni lievi nel corso del quinquennio.
realestate %>%
group_by(city, month) %>%
summarise(media_vendite = mean(sales), .groups = "drop") %>%
ggplot(aes(x = month, y = media_vendite, color = city)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_x_continuous(breaks = 1:12) +
labs(title = "Stagionalità delle vendite per città",
x = "Mese",
y = "Vendite medie",
color = "Città") +
theme_minimal()
Anche nella analisi di come variano le vendite nel corso dell’anno Tyler e Bryan-College Station mostrano dei trend molto più netti rispetto alle altre due città.Si può vedere chiaramente dal grafico come in entrambe la distribuzione delle vendite assuma una forma “gaussiana” toccando il picco nel mese di Giugno con valori molto piu bassi nei primi e negli ultimi mesi dell’anno. Lo stesso trend è a malapena visibile a Beaumont e Wichita Falls dove la “campana” delle vendite ha una durata più ampia ma molto meno netta.
Per completare l’analisi della stagionalità, verifico se anche i prezzi mostrano oscillazioni mensili:
realestate %>%
group_by(city, month) %>%
summarise(media_prezzo = mean(median_price),
sd_prezzo = sd(median_price),
.groups = "drop") %>%
ggplot(aes(x = month, y = media_prezzo, color = city, fill = city)) +
geom_ribbon(aes(ymin = media_prezzo - sd_prezzo,
ymax = media_prezzo + sd_prezzo),
alpha = 0.15, color = NA) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_x_continuous(breaks = 1:12) +
labs(title = "Stagionalità del prezzo mediano per città",
subtitle = "Linea = media mensile su 2010-2014; fascia = ± 1 sd",
x = "Mese",
y = "Prezzo mediano medio (USD)",
color = "Città",
fill = "Città") +
theme_minimal()
Il grafico mostra l’evoluzione dei prezzi medi nel corso dei mesi
dell’anno, l’ampiezza del ‘ribbon’ attorno a ogni linea spiega quanto
questi prezzi cambino nel corso degli anni analizzati.
In merito ai dati osservati tutte le città nel corso dei dodici mesi vedono una crescita del prezzo mediano, tuttavia con dinamiche eterogenee: la comune “campana estiva” dei volumi lascia spazio a tratti distintivi di ognuna. Tyler è l’unica con distribuzione dei prezzi a campana con picco nella stagione calda, Wichita ha un unico picco a Giugno,Beaumont ad agosto mentre Bryan-College Station ha un trend di crescita costante lungo l’anno solare.
Dopo aver analizzato i dati vado a mostrarne , grazie a ggplot2, i risultati più utili e esplicativi per i fruitori di questo report.
Il boxplot è la rappresentazione più utile per mostrare come si distribuisce una variabile quantitativa tra gruppi, ogni “scatola” ha una ampiezza data dalla differenza tra terzo e primo quartile, al suo interno si trova la mediana dei valori assunti dalla variabile per quel gruppo, i baffi si estendono solitamente per una misura convenzionale di 1,5 IQR (range interquartile) oltre gli estremi della box, i valori al di fuori vengono considerati outlier.
iqr_text <- realestate %>%
group_by(city) %>%
summarise(IQR_val = IQR(median_price)) %>%
mutate(riga = paste0(city, ": IQR = ", IQR_val)) %>%
pull(riga) %>%
paste(collapse = "\n")
ggplot(realestate, aes(x = city, y = median_price, fill = city)) +
geom_boxplot(alpha = 0.7) +
annotate("label", x = 4.5, y = 180000, label = iqr_text,
hjust = 1, vjust = 1, size = 3, fill = "white") +
labs(title = "Distribuzione del prezzo mediano per città",
x = "Città",
y = "Prezzo mediano (USD)") +
theme_minimal() +
theme(legend.position = "none")
Dal grafico risulta ben chiaro che il mercato della città di Bryan-
College Station abbia prezzi più alti , mentre il valore IQR più basso
si traduce in una maggior concentrazione dei valori attorno al ‘median
price’, viceversa Wichita Falls ha i prezzi mediamente più bassi ma un
range interquartile ben più ampio delle altre città, coprendo prezzi
sotto i 100 mila dollari che altrove non si trovano.
ggplot(realestate, aes(x = city, y = volume, fill = factor(year))) +
geom_boxplot(alpha = 0.7) +
labs(title = "Distribuzione del volume per città e anno",
x = "Città",
y = "Volume (milioni USD)",
fill = "Anno") +
theme_minimal()
Da questo secondo grafico la cosa che balza subito all’occhio è che il
mercato immobiliare nel corso del quinquennio è cresciuto ovunque tranne
a Wichita, anzi dal 2010 al 2014 il valore mediano del volume di affari
è persino diminuito, questo conferma che la città rappresenta una
nicchia che esula dalle dinamiche di mercato classiche, cosa già
appurata dal box precedente sul prezzo mediano.
I grafici a barre sovrapposte serviranno a mostrare le vendite per mese, suddivise per città. Avendo già la somma come valore da rappresentare sull’asse y uso ‘geom_col()’.
realestate %>%
group_by(month, city) %>%
summarise(vendite_totali = sum(sales), .groups = "drop") %>%
ggplot(aes(x = factor(month), y = vendite_totali, fill = city)) +
geom_col() +
labs(title = "Vendite totali per mese e città",
x = "Mese",
y = "Vendite totali",
fill = "Città") +
theme_minimal()
Il grafico così fatto non restituisce informazioni importanti se non il solito mercato stantio di Wichita che tuttavia tende a notarsi più per la collocazione alla base delle barre. Per dare un senso maggiore al grafico aggiungo la versione normalizzata:
realestate %>%
group_by(month, city) %>%
summarise(vendite_totali = sum(sales), .groups = "drop") %>%
group_by(month) %>%
mutate(percentuale = vendite_totali / sum(vendite_totali)) %>%
ungroup() %>%
ggplot(aes(x = factor(month), y = percentuale, fill = city)) +
geom_col() +
geom_text(aes(label = scales::percent(percentuale, accuracy = 1)),
position = position_stack(vjust = 0.5),
size = 3, color = "white", fontface = "bold") +
scale_y_continuous(labels = scales::percent) +
labs(title = "Quota di vendite per città in ogni mese",
x = "Mese",
y = "Quota di vendite (%)",
fill = "Città") +
theme_minimal()
Con questa visualizzazione il grafico offre informazioni più utili: -la città di Tyler si conferma quella che traina il mercato a prescindere dal periodo dell’anno considerato, seppur con una lieve contrazione nel periodo estivo -Bryan-College risulta quella che gode maggiormente del periodo estivo per accrescere la propria fetta di mercato (dal 23% invernale al 33% di giugno) - le tre città restanti seppur con caratteristiche di mercato differenti pagano la contrazione estiva a favore di Bryan-College.
Per cogliere ulteriori informazioni legate al periodo storico può servire l’aggiunta della variabile ‘year’ per cogliere il cambiamento nel corso degli anni. Allo scopo di non appesantire il grafico utilizzo il faceting, andando a generare un sotto-grafico per ogni anno:
realestate %>%
group_by(year, month, city) %>%
summarise(vendite_totali = sum(sales), .groups = "drop") %>%
ggplot(aes(x = factor(month), y = vendite_totali, fill = city)) +
geom_col() +
facet_wrap(~ year, nrow = 2, scales = "free_x") +
labs(
title = "Vendite totali per mese e città, suddivise per anno",
x = "Mese",
y = "Vendite totali",
fill = "Città"
) +
theme_minimal() +
theme(panel.spacing = unit(1, "lines"))
Il grafico ora permette di notare la crescita del mercato immobiliare
complessivo. All’interno dell’anno persiste un calo complessivo nel mese
di settembre. Per quanto riguarda le singole città è importante notare
come il picco delle vendite si sia lentamente spostato dal secondo al
terzo trimestre nel corso degli anni, frutto di una crescita costante
delle vendite nel periodo estivo riscontrabile specialmente a Tyler e
Bryan-College Station.
Per cogliere la stagionalità nelle città del dataset costruisco una line chart analizzando il volume di vendita nel corso degli anni:
realestate %>%
group_by(city, year, month) %>%
summarise(volume_medio = mean(volume), .groups = "drop") %>%
mutate(periodo = year + (month - 1)/12) %>%
ggplot(aes(x = periodo, y = volume_medio, color = city)) +
geom_line(linewidth = 0.8) +
labs(title = "Evoluzione mensile del volume per città (2010-2014)",
x = "Periodo (anno + frazione mese)",
y = "Volume medio (milioni USD)",
color = "Città") +
theme_minimal()
A fronte di una trend-line crescente nel corso degli anni ad eccezione di Wichita, è facilmente visibile come i mercati di Tyler e Bryan abbiano una forte componente stagionale ripetendo nel corso del tempo gli stessi picchi nel corso del periodo estivo. Diversamente Beaumont e Wichita presentano volumi frastagliati e meno legati al periodo dell’anno.
La misura sull’efficacia degli annunci calcolata negli step precedenti può essere un buon indicatore per orientare le campagne di marketing nelle varie città:
realestate %>%
group_by(month, city) %>%
summarise(eff_media = mean(effectiveness), .groups = "drop") %>%
ggplot(aes(x = factor(month), y = eff_media, color = city, group = city)) +
annotate("rect", xmin = 4, xmax = 9, ymin = -Inf, ymax = Inf,
alpha = 0.1, fill = "orange") +
geom_line(size = 1.2) +
geom_point(size = 2.5) +
scale_color_brewer(palette = "Set1") +
labs(
title = "Stagionalità dell'Efficacia degli Annunci (Effectiveness)",
subtitle = "Media mensile storica (2010-2014) - Analisi per la pianificazione del marketing",
x = "Mese",
y = "Tasso di Conversione (Vendite / Annunci Attivi)",
color = "Città"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 14),
panel.grid.minor = element_blank()
)
Il grafico mostra un chiaro trend a campana nel corso dell’anno
solare per tutte le città analizzate , dove nel periodo estivo si
assiste e maggiori spostamenti di famiglie e studenti per vacanze e
visite col favore del clima favorevole. Bryan tuttavia è quella che
mostra il picco più netto nei mesi caldi dell’anno ,evidenziati nel
grafico, raddoppiando la propria efficacia negli annunci pubblicati fino
a oltre il 22%: più di un annuncio su 5 si traduce in una vendita! La
presenza della Texas A&M University spiega perchè il mercato estivo
sia così attivo rispetto al resto dell’anno,in concomitanza con l’inizio
dell’anno accademico. Questo trend è quindi da considerare strutturale e
non dovuto al caso, il che suggerisce di rafforzare gli investimenti in
pubblicità in un periodo con un così alto tasso di conversioni
Delle restanti città vale la pena analizzare come la seconda in ordine
di efficacia resti per tutti i periodi dell’anno mediamente Wichita
Falls, che abbiamo visto avere logiche di mercato differenti dalle altre
e prezzi molto più bassi, proprio questo fattore può essere alla base
della “velocità di conversione” degli annunci in compravendite
effettuate. A esclusione di Bryan la stagionalità dell’indice di
efficacia ha un trend più “dolce” il che suggerisce una ripartizione
delle risorse di marketing più uniforme nel corso dell’anno.
L’analisi del dataset Texas Real Estate (240 osservazioni mensili, 4 città, 2010-2014) ha portato alla luce pattern strutturali di natura geografica , temporale e stagionale che vado a riepilogare:
Eterogeneità tra città. Le quattro città del dataset rappresentano quattro mercati strutturalmente diversi. Bryan-College Station e Tyler sono i mercati più dinamici, con prezzi medi elevati e un trend di crescita lungo il quinquennio. Beaumont e Wichita invece nel corso del quinquennio mantengono mediamente gli stessi prezzi attraversando anche fasi di contrazione, Wichita stessa rappresenta una nicchia di mercato a sè stante con prezzi molto più bassi rispetto alle altre. Questa eterogeneità giustifica statisticamente le analisi stratificate condotte negli Step 7 e 8: aggregare l’intero dataset nasconderebbe pattern molto diversi.
Trend temporale di crescita. Anche parlando di volumi BCT e Tyler guidano la classifica dei tassi di crescita, Beaumont ha un trend più morbido mentre Wichita conferma il suo ruolo di nicchia a parte rimanendo stagnante nel corso del quinquennio considerato.
Stagionalità sui volumi: marcata e uniforme. Nell’analisi dei volumi nel corso dell’anno tutte le città presentano la tipica crescita estiva del mercato immobiliare americano, tuttavia i grafici potrebbero sovrapporsi a coppie: BCT e Tyler con un picco unico e netto che copre i mesi caldi, Beaumont e Wichita con forme più frastagliate. Il tratto comune del dataset è un mercato invernale molto più moderato
Stagionalità sui prezzi: più debole e differenziata. A differenza dei volumi, la stagionalità sui prezzi mediani è più sfumata e segue pattern diversi per ogni città. Tyler mostra il pattern classico a campana estiva (+14% tra gennaio e giugno); Wichita Falls presenta un picco isolato a giugno (+25% rispetto a gennaio) seguito da un rapido ritorno ai valori di base; Bryan-College Station rimane più stabile con una lieve crescita autunnale; Beaumont non mostra pattern stagionali identificabili.
Efficacia degli annunci e ruolo dell’università. Bryan-College Station si distingue come la città con la maggiore efficacia commerciale, con un picco a luglio che supera il 22% di conversione da annunci a vendite. Questa anomalia è strutturale e non casuale: la presenza della Texas A&M University concentra la domanda nel periodo di inizio dell’anno accademico, generando una stagionalità più estrema e un’efficienza di mercato eccezionale ma confinata ai mesi estivi. Wichita Falls, pur essendo il mercato meno dinamico in termini di volumi, mostra una buona efficacia di conversione, attribuibile probabilmente ai prezzi più bassi che riducono i tempi di vendita.
Asimmetria interna ai prezzi. Il confronto tra
prezzo medio (mean_price) e prezzo mediano
(median_price) ha rivelato che nel 99.6% delle osservazioni
la media supera la mediana. Questo conferma quantitativamente
l’asimmetria positiva tipica del real estate: poche transazioni di
valore elevato (immobili di lusso, fondi commerciali) trascinano la
media verso l’alto, mentre la mediana resta più stabile. La mediana è
quindi l’indicatore più robusto del “prezzo della casa tipica”.
In virtù di queste considerazioni le raccomandazioni operative sul mercato immobiliare texano devono seguire linee guida ben definite:
1. Differenziare le strategie per città. I quattro mercati non possono essere gestiti con un approccio uniforme. Bryan e Tyler richiedono un approccio di marketing aggressivo sugli immobili di valore medio-alto; Beaumont richiede strategie per consolidare la posizione intermedia e stabile di mercato; Wichita Falls va trattata come nicchia , con riguardo ad acquirenti di reddito più basso con offerte ad hoc.
2. Calibrare il marketing sulla stagionalità di Bryan-College Station. Il picco estivo di Bryan esige la convergenza delle risorse di marketing in quel periodo per massimizzare il ritorno economico, anche a discapito del periodo invernale che vede un mercato molto meno attivo.
3. Distribuire risorse uniformemente nelle altre città. A Beaumont, Tyler e Wichita Falls la stagionalità dei volumi è più dolce. Una distribuzione equilibrata delle risorse di marketing lungo l’anno è più efficiente di concentrazioni stagionali.
4. Sfruttare la stagionalità sui prezzi per immobili di valore. Wichita e Tyler , pur facendo parte di realtà differenti come visto hanno un mercato estivo con prezzo mediano più alto rispetto ai mesi invernali, il che offre la possibilità di mettere sul mercato fondi o case di valore maggiore che nel corso dell’anno hanno un mercato difficile.
5. Monitorare l’efficienza commerciale con KPI
dedicati. L’indicatore effectiveness
(sales/listings) costruito si è rivelato efficace alla comprensione
delle dinamiche di vendita delle città del dataset. Pur rappresentando
concettualmente l’inverso della variabile months_inventory
le due se integrate possono fornire una lettura completa della velocità
di rotazione dello stock.
6. Posizionamento sul segmento di prezzo dominante.
L’analisi delle classi di median_price allo Step 4 ha
mostrato che la fascia 135-160k dollari raccoglie il 40% delle
osservazioni. Le inserzioni in questa fascia sono quindi quelle che
incontrano una domanda maggiore pertanto dovrebbero essere il target
principale.
Il dataset non è esaustivo pertanto porta con sè diversi limiti:
effectiveness assume implicitamente che
sales e listings siano misurate nello stesso
istante; eventuali disallineamenti temporali nei dati originari
potrebbero introdurre distorsioni e invalidare parzialmente le
considerazioni fatte.Queste limitazioni suggeriscono quindi di estendere l’analisi ampliando l’area geografica, il periodo considerato e valutando le caratteristiche specifiche degli immobili, così da rendere l’analisi più completa.