In questo documento analizziamo il dataset delle vendite immobiliari. Dopo aver caricato i dati, verifichiamo la struttura per assicurarci che le variabili siano lette correttamente.
url <- "https://drive.google.com/uc?export=download&id=1O4If8876MTwstkrZX0BqpQ_BxcsIMEko"
data <- read.csv(url)
# FIX IMPORTANTE:
# Il mese è spesso numerico nel CSV originale. Creiamo subito una colonna 'date'
# e una versione fattore del mese per evitare problemi con i grafici e le probabilità.
data$month_num <- data$month
data$month_name <- factor(data$month, levels = 1:12, labels = month.name)
data$year_factor <- as.factor(data$year)
# Anteprima struttura
str(data)
## 'data.frame': 240 obs. of 11 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 ...
## $ month_num : int 1 2 3 4 5 6 7 8 9 10 ...
## $ month_name : Factor w/ 12 levels "January","February",..: 1 2 3 4 5 6 7 8 9 10 ...
## $ year_factor : Factor w/ 5 levels "2010","2011",..: 1 1 1 1 1 1 1 1 1 1 ...
Prima di calcolare gli indici, definiamo le variabili statistiche utilizzate:
quant_vars <- c("sales", "volume", "median_price", "listings", "months_inventory")
# Creiamo un data frame riassuntivo per una lettura più pulita rispetto ai print()
stats_summary <- data.frame()
for (var in quant_vars) {
vec <- data[[var]]
temp <- data.frame(
Variabile = var,
Media = mean(vec, na.rm = TRUE),
SD = sd(vec, na.rm = TRUE),
Skewness = skewness(vec, na.rm = TRUE),
Kurtosis = kurtosis(vec, na.rm = TRUE)
)
stats_summary <- rbind(stats_summary, temp)
}
knitr::kable(stats_summary, digits = 2, caption = "Statistiche Descrittive Variabili Quantitative")
| Variabile | Media | SD | Skewness | Kurtosis |
|---|---|---|---|---|
| sales | 192.29 | 79.65 | 0.72 | 2.69 |
| volume | 31.01 | 16.65 | 0.88 | 3.18 |
| median_price | 132665.42 | 22662.15 | -0.36 | 2.38 |
| listings | 1738.02 | 752.71 | 0.65 | 2.21 |
| months_inventory | 9.19 | 2.30 | 0.04 | 2.83 |
# Analisi delle Distribuzioni
## 1. Variabili Quantitative (Istogrammi)
# Lista delle variabili quantitative
quant_vars <- c("sales", "volume", "median_price", "listings", "months_inventory")
# Ciclo per stampare un grafico per ogni variabile
for (var in quant_vars) {
# Calcolo la media per tracciare una linea di riferimento
mean_val <- mean(data[[var]], na.rm = TRUE)
p <- ggplot(data, aes_string(x = var)) +
# Istogramma: uso 'y = ..density..' per sovrapporre la curva
geom_histogram(aes(y = ..density..), bins = 30, fill = "lightblue", color = "white") +
# Curva di densità
geom_density(alpha = 0.2, fill = "blue") +
# Linea della media
geom_vline(aes(xintercept = mean_val), color = "red", linetype = "dashed", size = 1) +
labs(title = paste("Distribuzione di:", var),
subtitle = paste("Linea rossa = Media (", round(mean_val, 2), ")"),
x = var,
y = "Densità") +
theme_minimal()
# In un ciclo for, bisogna usare print() esplicitamente per mostrare il grafico
print(p)
}
Notiamo come le variabili Sales, Volume e Listing siano distribuite in
modo asimmetrico positivo, perciò motle più osservazioni per valori più
bassi. Mentre Months_inventory e e Median_price si distribuiscono più
come una normale (infatti hanno indici di asimmetria più vicini allo 0
rispetto alle altre), perciò in questo caso usare la media come indice
di posizione è ottimale.
Analizziamo il bilanciamento del dataset osservando le frequenze per città.
# City
table(data$city)
##
## Beaumont Bryan-College Station Tyler
## 60 60 60
## Wichita Falls
## 60
ggplot(data, aes(x = reorder(city, city, function(x)-length(x)))) +
geom_bar(fill = "steelblue") +
labs(title = "Conteggio Osservazioni per Città",
x = "Città",
y = "Osservazioni") +
coord_flip() + # Ruota il grafico per leggere meglio le etichette
theme_minimal()
Possiamo vedere come le città abbiano lo stesso numero di osservazioni,
questo ci permette di avere maggiore precisione durante l’analisi e di
non incorrere magari al rischio di “avere meno informazioni” perché si è
osservato meno il fenomeno nelle varie città.
Per confrontare la variabilità tra grandezze con unità di misura diverse (es. Dollari vs Numero di case), utilizziamo il Coefficiente di Variazione (CV): \(CV = \frac{\sigma}{\mu} \times 100\).
# Calcolo CV
cv_results <- stats_summary %>%
mutate(CV = (SD / Media) * 100,
Abs_Skewness = abs(Skewness)) %>%
select(Variabile, CV, Abs_Skewness) %>%
arrange(desc(CV))
knitr::kable(cv_results, digits = 2, caption = "Confronto Variabilità (CV) e Asimmetria")
| Variabile | CV | Abs_Skewness |
|---|---|---|
| volume | 53.71 | 0.88 |
| listings | 43.31 | 0.65 |
| sales | 41.42 | 0.72 |
| months_inventory | 25.06 | 0.04 |
| median_price | 17.08 | 0.36 |
# Estrazione risultati
max_cv_var <- cv_results$Variabile[1]
max_skew_var <- cv_results$Variabile[which.max(cv_results$Abs_Skewness)]
Considerazioni Statistiche: * La variabile con la maggiore variabilità è volume. Questo indica che questa metrica fluttua molto di più rispetto alla sua media rispetto alle altre, rendendola meno prevedibile. * La variabile con la distribuzione più asimmetrica è volume. Un’alta asimmetria suggerisce che la media non è un buon rappresentante centrale e che ci sono forti outlier in una direzione.
Selezioniamo la variabile sales, la dividiamo in classi
e calcoliamo l’eterogeneità.
Cos’è l’Indice di Gini? È una misura di eterogeneità. Nel contesto delle classi di frequenza: * 0: Massima concentrazione (tutti i dati sono in una sola classe). * ~1: Massima eterogeneità (i dati sono sparsi equamente tra tutte le classi).
# Creazione classi
num_classes <- 5
data$sales_class <- cut(data$sales, breaks = num_classes, include.lowest = TRUE)
freq_table <- table(data$sales_class)
prop_table <- prop.table(freq_table)
# Calcolo Gini
gini_index <- 1 - sum(prop_table^2)
# Grafico a barre
barplot(freq_table,
main = "Distribuzione frequenza sales per classi",
col = "skyblue",
xlab = "Classi di Vendita", ylab = "Frequenza")
Risultato Gini: L’indice calcolato è 0.731. Questo valore ci dice come le vendite si distribuiscono tra le fasce. Il risultato ci fa capire che le vendite non sono distribuite uniformemente tra le classi, ma che ci sono alcuni mesi con più vendite rispetto ad altri come dimostrato successivamente dal grafico a barre sovrapposto dove si aggregano le vendite per ogni mese di ogni anno.
# 1. Definizione delle classi (se non già definite nel blocco precedente)
num_classes <- 5
# Usiamo il range globale per rendere le città comparabili tra loro
data$volume_class <- cut(data$volume, breaks = num_classes, include.lowest = TRUE)
# 2. Calcolo dell'Indice di Gini per ogni Città
gini_per_city <- data %>%
group_by(city) %>%
summarise(
# Calcolo Gini interno: 1 - somma delle proporzioni al quadrato
gini_index = 1 - sum(prop.table(table(volume_class))^2)
) %>%
arrange(desc(gini_index)) # Ordiniamo dalla città più eterogenea
# 3. Visualizzazione della tabella con i valori calcolati
knitr::kable(gini_per_city,
col.names = c("Città", "Indice di Gini (Volume)"),
digits = 3,
caption = "Indice di Eterogeneità del Volume per Città")
| Città | Indice di Gini (Volume) |
|---|---|
| Bryan-College Station | 0.732 |
| Tyler | 0.679 |
| Beaumont | 0.506 |
| Wichita Falls | 0.000 |
# 4. Visualizzazione Grafica
ggplot(data, aes(x = volume_class, fill = city)) +
geom_bar() +
facet_wrap(~ city, scales = "free_x") +
labs(title = "Distribuzione delle Classi di Volume per Città",
subtitle = "Confronto della concentrazione del fatturato",
x = "Classi di Volume ($)",
y = "Frequenza (Mesi)") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none")
In questa sezione analizziamo come si distribuisce il volume d’affari
all’interno di ogni città. Utilizziamo le classi di volume globali
calcolate precedentemente per vedere se le città si posizionano su fasce
diverse o se variano tra fasce diverse.
Nota importante: come mostrato Wichita Falls ha indice 0 ciò non significa che è il volume di vendita è equidistribuito all’interno delle classi, ma è dato dal fatto che questa città ha un range così poco ampio di volume rispetto alle altre che viene tutto rappresentato da una classe singola che racchiude tutto il volume, perciò per forza di cose risulterà indice = 0, ma non è da considerare.
Correzione Feedback: Nel dataset originale i mesi sono numeri. Usiamo i numeri o la colonna fattore creata all’inizio per evitare probabilità zero.
n_total <- nrow(data)
# Probabilità Città = Beaumont
p_beaumont <- sum(data$city == "Beaumont") / n_total
# Probabilità Mese = Luglio (Luglio è il mese 7)
p_july <- sum(data$month_num == 7) / n_total
prob_df <- data.frame(
Evento = c("Città è Beaumont", "Mese è Luglio"),
Probabilità = c(p_beaumont, p_july)
)
prob_d <- data.frame(Evento = "Città è Beaumont", Prob = p_beaumont)
knitr::kable(prob_d, digits = 4)
| Evento | Prob |
|---|---|
| Città è Beaumont | 0.25 |
knitr::kable(prob_df, digits = 4)
| Evento | Probabilità |
|---|---|
| Città è Beaumont | 0.2500 |
| Mese è Luglio | 0.0833 |
Beaumont ha il 25% di probabilità di essere ossere osservato nel dataset. Mentre la probabilitò di avere un osservazione nel mese di luglio è dell’8,3%.
Creiamo avg_price_per_sale e
ad_effectiveness (vendite per annuncio).
# Calcolo delle nuove variabili e ordinamento immediato del dataset
data <- data %>%
mutate(
avg_price_per_sale = ifelse(sales > 0, volume / sales, NA),
ad_effectiveness = ifelse(listings > 0, sales / listings, NA)
) %>%
# Ordino il dataset in base all'efficacia decrescente
arrange(desc(ad_effectiveness))
# Visualizziamo l'anteprima delle prime 100 righe ordinate
# Nota: Dato che 'data' è già stato ordinato sopra, qui basta selezionare e stampare
head(data %>% select(city, sales, listings, ad_effectiveness), 100)
## city sales listings ad_effectiveness
## 1 Bryan-College Station 403 1041 0.3871278
## 2 Bryan-College Station 377 1152 0.3272569
## 3 Bryan-College Station 298 1016 0.2933071
## 4 Bryan-College Station 353 1212 0.2912541
## 5 Bryan-College Station 402 1385 0.2902527
## 6 Bryan-College Station 357 1462 0.2441860
## 7 Bryan-College Station 303 1271 0.2383950
## 8 Bryan-College Station 328 1385 0.2368231
## 9 Bryan-College Station 200 882 0.2267574
## 10 Bryan-College Station 275 1261 0.2180809
## 11 Bryan-College Station 341 1581 0.2156863
## 12 Bryan-College Station 218 1031 0.2114452
## 13 Bryan-College Station 204 1022 0.1996086
## 14 Bryan-College Station 296 1518 0.1949934
## 15 Wichita Falls 167 904 0.1847345
## 16 Wichita Falls 159 868 0.1831797
## 17 Wichita Falls 165 914 0.1805252
## 18 Bryan-College Station 286 1588 0.1801008
## 19 Bryan-College Station 190 1057 0.1797540
## 20 Wichita Falls 149 830 0.1795181
## 21 Wichita Falls 150 844 0.1777251
## 22 Bryan-College Station 292 1669 0.1749551
## 23 Bryan-College Station 282 1613 0.1748295
## 24 Bryan-College Station 169 973 0.1736896
## 25 Bryan-College Station 293 1734 0.1689735
## 26 Wichita Falls 143 852 0.1678404
## 27 Beaumont 260 1575 0.1650794
## 28 Bryan-College Station 294 1793 0.1639710
## 29 Beaumont 273 1675 0.1629851
## 30 Beaumont 262 1617 0.1620284
## 31 Bryan-College Station 284 1758 0.1615472
## 32 Wichita Falls 144 900 0.1600000
## 33 Wichita Falls 150 941 0.1594049
## 34 Bryan-College Station 267 1680 0.1589286
## 35 Wichita Falls 128 812 0.1576355
## 36 Wichita Falls 140 899 0.1557286
## 37 Wichita Falls 147 946 0.1553911
## 38 Bryan-College Station 186 1201 0.1548709
## 39 Beaumont 254 1672 0.1519139
## 40 Beaumont 246 1620 0.1518519
## 41 Beaumont 224 1501 0.1492338
## 42 Beaumont 246 1659 0.1482821
## 43 Tyler 423 2855 0.1481611
## 44 Bryan-College Station 244 1662 0.1468111
## 45 Bryan-College Station 166 1132 0.1466431
## 46 Tyler 332 2272 0.1461268
## 47 Wichita Falls 115 801 0.1435705
## 48 Wichita Falls 114 796 0.1432161
## 49 Bryan-College Station 164 1155 0.1419913
## 50 Tyler 369 2602 0.1418140
## 51 Tyler 388 2744 0.1413994
## 52 Wichita Falls 137 973 0.1408016
## 53 Bryan-College Station 171 1218 0.1403941
## 54 Wichita Falls 132 941 0.1402763
## 55 Wichita Falls 130 934 0.1391863
## 56 Wichita Falls 124 895 0.1385475
## 57 Beaumont 232 1675 0.1385075
## 58 Beaumont 212 1534 0.1382008
## 59 Wichita Falls 125 907 0.1378170
## 60 Wichita Falls 119 877 0.1356899
## 61 Beaumont 202 1500 0.1346667
## 62 Wichita Falls 135 1004 0.1344622
## 63 Tyler 361 2696 0.1339021
## 64 Wichita Falls 109 821 0.1327649
## 65 Wichita Falls 129 972 0.1327160
## 66 Wichita Falls 123 933 0.1318328
## 67 Wichita Falls 121 923 0.1310943
## 68 Wichita Falls 116 887 0.1307779
## 69 Beaumont 208 1604 0.1296758
## 70 Beaumont 218 1683 0.1295306
## 71 Bryan-College Station 238 1840 0.1293478
## 72 Tyler 371 2875 0.1290435
## 73 Wichita Falls 132 1028 0.1284047
## 74 Wichita Falls 123 961 0.1279917
## 75 Beaumont 212 1657 0.1279421
## 76 Wichita Falls 130 1022 0.1272016
## 77 Bryan-College Station 152 1199 0.1267723
## 78 Beaumont 213 1681 0.1267103
## 79 Wichita Falls 117 938 0.1247335
## 80 Tyler 347 2791 0.1243282
## 81 Bryan-College Station 196 1581 0.1239722
## 82 Wichita Falls 92 743 0.1238223
## 83 Wichita Falls 112 905 0.1237569
## 84 Wichita Falls 127 1029 0.1234208
## 85 Tyler 369 2998 0.1230821
## 86 Wichita Falls 119 968 0.1229339
## 87 Bryan-College Station 196 1599 0.1225766
## 88 Wichita Falls 105 859 0.1222352
## 89 Tyler 300 2460 0.1219512
## 90 Wichita Falls 102 838 0.1217184
## 91 Wichita Falls 128 1052 0.1216730
## 92 Wichita Falls 94 777 0.1209781
## 93 Tyler 357 2953 0.1208940
## 94 Beaumont 206 1708 0.1206089
## 95 Wichita Falls 93 774 0.1201550
## 96 Wichita Falls 89 746 0.1193029
## 97 Wichita Falls 101 850 0.1188235
## 98 Beaumont 182 1539 0.1182586
## 99 Bryan-College Station 233 1984 0.1174395
## 100 Beaumont 200 1708 0.1170960
Possiamo dedurre che gli annunci pubblicitari nella città di Bryan-College Station sono stati più efficaci in termini di vendita. Rapporto vendite/annunci. Ciò viene tradotto come: 10 annunci che in alcuni casi hanno portato a quasi 4 vendite nella città di Bryan-College Station.
# Aggregazione totale per Città
city_aggregated_stats <- data %>%
group_by(city) %>%
summarise(
total_sales = sum(sales, na.rm = TRUE), # Somma totale vendite
total_listings = sum(listings, na.rm = TRUE) # Somma totale annunci
) %>%
# Calcolo dell'efficacia aggregata (Totale Vendite / Totale Listing)
mutate(aggregated_ad_effectiveness = total_sales / total_listings) %>%
# Ordino dalla città più efficace a quella meno efficace
arrange(desc(aggregated_ad_effectiveness))
# Visualizzazione della tabella formattata
knitr::kable(city_aggregated_stats,
digits = 4,
caption = "Performance Aggregata per Città (Totale Storico)")
| city | total_sales | total_listings | aggregated_ad_effectiveness |
|---|---|---|---|
| Bryan-College Station | 12358 | 87488 | 0.1413 |
| Wichita Falls | 6964 | 54575 | 0.1276 |
| Beaumont | 10643 | 100759 | 0.1056 |
| Tyler | 16185 | 174303 | 0.0929 |
Mentre qui possiamo notare come a netto del periodo di tempo, la città Bryan-College Station si dimostra avere ancora un’efficacia maggiore rispetto alle altre. Mentre Wichita Falls si posiziona in seconda posizione, ma comunque come riportato durante questa analisi si dimostra come sia la meno performante in termini di vendita.
ggplot(data, aes(x = reorder(city, ad_effectiveness, FUN = median, na.rm = TRUE),
y = ad_effectiveness,
fill = city)) +
# Boxplot per statistiche (mediana e quartili)
geom_boxplot(alpha = 0.7, outlier.shape = NA) +
# Aggiungo i punti reali (jitter) per vedere la densità dei dati ed eventuali casi particolari
geom_jitter(width = 0.2, alpha = 0.3, size = 1) +
labs(title = "Distribuzione Efficacia Annunci per Città",
subtitle = "Boxplot ordinato per mediana crescente",
x = "Città",
y = "Efficacia (Vendite / Annunci)") +
theme_minimal() +
theme(legend.position = "none") # Rimuovo la legenda perché ridondante
Infatti come citato sopra possiamo vedere tramite boxplot come la
distribuzione dell’efficacia pubblicitaria nelle varie città abbia media
e range interquartile più alta e ampio rispetto le altre. Ciò significa
che è stato possibile registrare un’efficacia pubblicitaria molto più
alta e quindi più vendite.
Analizziamo come variano le vendite medie per città.
city_summary <- data %>%
group_by(city) %>%
summarise(median_sales = median(sales, na.rm = TRUE))
ggplot(city_summary, aes(x = reorder(city, -median_sales), y = median_sales)) +
geom_col(fill = "steelblue") +
labs(title = "Vendite medie per città", x = "Città", y = "Mediana Vendite") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Il grafico mostra chiaramente quali sono i mercati più grandi in termini di volume assoluto di transazioni. Tyler ha la media in vendita maggiore rispetto le altre, ma dato che è una variabile molto asimmetrica non possiamo dire con certezza che sia la città migliore per fare affari.
Utilizziamo i boxplot per visualizzare la mediana e la dispersione (IQR).
ggplot(data, aes(x = city, y = median_price)) +
geom_boxplot(fill = "lightblue", outlier.colour = "red") +
labs(title = "Distribuzione prezzo mediano per città",
y = "Prezzo Mediano ($)", x = "Città") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Interpretazione: I boxplot permettono di vedere non solo il prezzo centrale (linea nera), ma anche la variabilità del mercato. Città con box più “alti” hanno prezzi molto variabili. I punti rossi indicano valori anomali (outlier).
Possiamo dedurre dai boxplot che i range interquartili del prezzo mediano non cambia molto da città a città, quindi variano allo stesso modo ma con medie differenti.
In conclusione Wichita Falls è la città che si discosta di più in termine di prezzo mediano di vendita tra le altre città e fluttua leggermente di più rispetto gli altri, deduco quindi che Wichita Falls è il posto in cui si vende a prezzo inferiore rispetto ad altri in termini mediani.
Risposta al feedback: Uniamo le dimensioni Anno e Città in un unico grafico per vedere l’evoluzione.
ggplot(data, aes(x = year_factor, y = volume, fill = city)) +
geom_boxplot() +
labs(title = "Distribuzione volume vendite: confronto anno vs città",
x = "Anno", y = "Volume ($)", fill = "Città") +
theme_minimal()
Questo grafico è denso ma informativo: ci permette di vedere se, anno dopo anno, la distribuzione del volume di affari è cambiata specificamente per alcune città.
Possiamo vedere come in Tyler e Bryan-College Station le vendite siano salite durante gli anni in modo positivo, anche se nel 2013 a Tyler si sono rilevate in alcuni casi volumi di vendite ($) che hanno raggiunto lo stesso livello dell’anno 2014.
Mentre per le altre due città notiamo come a Beaurmont non sia cambiato molto si oscilla da 25 a 35 milioni in vendite media negli anni e a Wichita Falls si nota un trend decrescente di vendite.
Confrontiamo le vendite mensili totali, evidenziando il contributo di ogni città.
monthly_sales <- data %>%
group_by(month_name, city) %>%
summarise(total_sales = sum(sales), .groups = 'drop')
ggplot(monthly_sales, aes(x = month_name, y = total_sales, fill = city)) +
geom_col() +
labs(title = "Vendite totali per mese (contributo per città)",
x = "Mese", y = "Totale Vendite") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Notiamo la stagionalità: i mesi estivi (Maggio-Giugno-Luglio) tendono ad avere barre più alte, indicando un mercato più attivo in tutte le città, la città in cui fluttua meno durante i mesi e Wichita Falls in cui si ha un periodo da Marzo ad Agosto in cui si ha più o meno lo stesso totale di vendite, ma è anche vero che il totale per ogni mese è molto più basso comparato alle le altre città.
Costruiamo una data corretta per visualizzare il trend temporale continuo.
# Creazione colonna Date corretta (giorno 1 di ogni mese/anno)
data$date_obj <- as.Date(paste(data$year, data$month_num, "01", sep="-"))
# Aggregazione
time_series <- data %>%
group_by(date_obj, city) %>%
summarise(median_price = mean(median_price), .groups = 'drop')
ggplot(time_series, aes(x = date_obj, y = median_price, color = city)) +
geom_line(size = 1) +
labs(title = "Trend temporale del prezzo mediano",
x = "Data", y = "Prezzo Mediano ($)") +
scale_x_date(date_labels = "%Y", date_breaks = "1 year") +
theme_minimal()
library(dplyr)
library(zoo)
library(ggplot2)
# 1. Preparazione Data
data$date_obj <- as.Date(paste(data$year, data$month_num, "01", sep="-"))
# 2. Aggregazione e Calcolo Media Mobile per Città
time_series <- data %>%
group_by(date_obj, city) %>%
summarise(median_price = mean(median_price), .groups = 'drop') %>%
arrange(city, date_obj) %>% # Fondamentale ordinare per data
group_by(city) %>% # Raggruppo per città così il calcolo è separato
mutate(moving_avg = rollmean(median_price, k = 12, fill = NA, align = "right"))
# 3. Creazione del Grafico con Facet Wrap
ggplot(time_series, aes(x = date_obj, y = median_price)) +
# A. La linea originale dei dati reali (grigia o colorata tenue per sfondo)
geom_line(aes(color = city), alpha = 0.4, size = 0.6) +
# B. La Media Mobile (Viola):
# Specificando 'group = city', R disegna una linea viola distinta per ogni città
geom_line(aes(y = moving_avg, group = city), color = "purple", size = 1.2) +
# C. Suddivisione in 4 riquadri (uno per città)
facet_wrap(~ city, scales = "free_y") +
# D. Etichette e Stile
labs(title = "Trend del prezzo mediano per città",
subtitle = "Dati reali (colore) vs Media Mobile a 12 mesi (viola)",
x = "Data",
y = "Prezzo Mediano ($)") +
scale_x_date(date_labels = "%Y", date_breaks = "1 year") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") # Rimuovo la legenda perché i nomi sono già sopra i grafici
Il grafico mostra chiaramente come alcune città abbiano trend di crescita dei prezzi molto più marcati rispetto ad altre che rimangono stabili.
Possiamo dire che a Bryan-College Station si sono rilevati prezzi mediani maggiori di vendita e quindi gli immobili sono stati venduti ad un prezzo più alto rispetto alle altre città con un trend sempre crescente durante gli anni, lo stesso per la città di Tyler ma con prezzi leggermente inferiori.
Notiamo come a Beaumont ci sia stato una forte discesa del prezzo di vendita mediano tra inizio 2010 e metà 2010 che si è ripetuto a fine 2013. Ma comunque sia a Beaumont che a Wichita Falls non si notano trend crescenti, vediamo come la media mobile
La linea viola rappresenta la media mobile di ogni serie temporale, dimostrando i trend sopra citati.
# Analisi Distribuzione Months Inventory per Città
# 1. Creazione del Grafico a Boxplot
ggplot(data, aes(x = reorder(city, months_inventory, FUN = median, na.rm = TRUE),
y = months_inventory,
fill = city)) +
# Boxplot per mostrare mediana e quartili
geom_boxplot(alpha = 0.6, outlier.shape = NA) +
# Aggiungo i punti (jitter) per vedere la densità reale delle osservazioni
geom_jitter(width = 0.2, alpha = 0.3, size = 1) +
# Linea di riferimento (opzionale): 6 mesi è spesso considerato un mercato "bilanciato"
geom_hline(yintercept = 6, linetype = "dashed", color = "red", size = 0.8) +
labs(title = "Distribuzione dei Mesi di Inventario per Città",
subtitle = "La linea rossa tratteggiata (6 mesi) indica un mercato bilanciato.\nSotto = Mercato del venditore; Sopra = Mercato del compratore.",
x = "Città",
y = "Mesi di Inventario (Months Inventory)") +
theme_minimal() +
theme(legend.position = "none") # Rimuovo legenda ridondante
# 2. Tabella di sintesi (Statistiche aggregate)
inventory_stats <- data %>%
group_by(city) %>%
summarise(
Mean_Inventory = mean(months_inventory, na.rm = TRUE),
Median_Inventory = median(months_inventory, na.rm = TRUE),
Min_Inventory = min(months_inventory, na.rm = TRUE),
Max_Inventory = max(months_inventory, na.rm = TRUE)
) %>%
arrange(Median_Inventory)
knitr::kable(inventory_stats, digits = 2, caption = "Statistiche di Inventario per Città")
| city | Mean_Inventory | Median_Inventory | Min_Inventory | Max_Inventory |
|---|---|---|---|---|
| Wichita Falls | 7.82 | 7.90 | 6.1 | 9.4 |
| Bryan-College Station | 7.66 | 8.10 | 3.4 | 11.6 |
| Beaumont | 9.97 | 10.40 | 7.0 | 12.6 |
| Tyler | 11.32 | 11.45 | 6.9 | 14.9 |
# Aggregazione: Somma dei listings per Anno e Città
total_listings_year_city <- data %>%
group_by(year, city) %>%
summarise(total_listings = sum(listings, na.rm = TRUE), .groups = 'drop')
# Visualizzazione
ggplot(total_listings_year_city, aes(x = as.factor(year), y = total_listings, fill = city)) +
# position = "dodge" serve a mettere le barre affiancate e non una sopra l'altra
geom_col(position = "dodge", alpha = 0.9) +
# Aggiungo le etichette con i numeri sopra ogni barra per precisione
geom_text(aes(label = total_listings),
position = position_dodge(width = 0.9),
vjust = -0.5,
size = 3) +
labs(title = "Totale Listings (Inventario) per Anno e Città",
subtitle = "Confronto diretto del volume di offerta immobiliare",
x = "Anno",
y = "Totale Listings",
fill = "Città") +
theme_minimal() +
# Espando leggermente l'asse Y per far entrare le etichette numeriche
scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
theme(legend.position = "top")
I boxplot sopra ci aiutano a capire come si distribuisce la variabile
month_inventory, quindi la variabile che determina il tempo in mesi che
separa l’avvio dell’annuncio e la vendita effettiva dell’immobile.
Riprendendo la tabella dove viene mostrata l’efficacia degli annunci
aggregata per città è curioso come Wichita Falls si posiziona seconda
per efficacia migliore, anche se le vendite sono minori in totale
rispetto le altre, quindi posso dedurre che in Wichita Falls ci sia un
mercato molto più liquido, con meno annunci ma gli immobili vengono
venduti prima in termini di tempo.
Esploriamo se le case più costose sono più difficili da vendere (efficacia annunci inferiore).
ggplot(data, aes(x = median_price, y = ad_effectiveness)) +
geom_point(alpha = 0.5, color = "darkgreen") +
geom_smooth(method = "lm", color = "red", se = FALSE) + # Aggiunta linea di tendenza
facet_wrap(~ city, scales = "free") +
labs(title = "Relazione: prezzo mediano vs efficacia annunci",
subtitle = "Suddiviso per città",
x = "Prezzo Mediano ($)", y = "Efficacia (vendite / annunci)") +
theme_bw()
# 1. Aggregazione dei dati
# Sommiamo i listings di tutte le città per ogni mese/anno
listings_comparison <- data %>%
group_by(year, month_num) %>%
summarise(total_listings = sum(listings, na.rm = TRUE), .groups = 'drop') %>%
# Creiamo la label del mese ordinata
mutate(month_label = factor(month.name[month_num], levels = month.name))
# 2. Creazione del Grafico (Line Chart Multiplo)
ggplot(listings_comparison, aes(x = month_label, y = total_listings, group = factor(year), color = factor(year))) +
# Linee di tendenza
geom_line(size = 1.2) +
# Punti per evidenziare il valore esatto di ogni mese
geom_point(size = 3, alpha = 0.8) +
# Etichette e Titoli
labs(title = "Confronto stagionale dei listings (annunci attivi)",
subtitle = "Ogni linea rappresenta un anno diverso",
x = "Mese",
y = "Totale Listings",
color = "Anno") +
# Scala Y formattata con virgole per leggibilità (richiede library scales)
scale_y_continuous(labels = comma) +
# Tema pulito
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1), # Ruota i mesi per leggerli meglio
legend.position = "top") # Legenda in alto per risparmiare spazio laterale
Commento Finale: L’uso del facet_wrap è
fondamentale qui. In alcune città potremmo notare che all’aumentare del
prezzo, l’efficacia (punti sull’asse Y) diminuisce, suggerendo che
immobili di lusso rimangono sul mercato più a lungo o richiedono più
listing per essere venduti.
Mentre il periodo estivo (maggio-giugno-luglio-agosto) è molto più proficuo in termini di portare a casa vendite, come mostra il grafico sopra questo fenomeno è anche dovuto al fatto che tutti gli anni prima del periodo estivo, quindi inizio Maggio, ci siano molti più annunci in circolazione.
Tyler e Bryan-College Station sono le città in cui si è rilevato un trend di crescita in termini di volumi di vendita e prezzo mediano di vendita, mentre le altre due sono rimaste stabili sotto questo aspetto con Wichita Falls che rimane per certo la città con le prestazioni peggiori negli ultimi anni in termini di vendite e volumi.
Per quanto riguarda l’efficacia degli annunci, Bryan-College Station è la città in cui gli annunci hanno avuto efficacia maggiore rapportato alle vendite effettuate. Ma Wichita Falls è la città con mercato più liquido di tutti, anche se con meno vendite e meno annunci, ma gli immobili sponsorizzati vengono venduti con tempo minore.