Caricamento librerie e dataset

# Installazione (solo se necessario)
# install.packages(c("readr", "psych", "ggplot2", "dplyr"))

library(readr)
library(psych)
library(ggplot2)
library(dplyr)

# Importazione del dataset
realestate <- read_csv("realestate_texas.csv")

Step 1 - Analisi delle variabili

Classificazione delle variabili e possibili analisi:

Variabile Tipo Possibili Analisi
city Qualitativa nominale Confronti tra città, analisi geografiche
year/month/date Quantitativa continua Analisi temporali, stagionalità
sales Quantitativa discreta Statistiche descrittive, confronto tra città, trend nel tempo
volume Quantitativa continua Analisi di distribuzione, rapporto volume/vendite
median_price Quantitativa continua Analisi prezzi, variazione nel tempo, confronto tra città
listings Quantitativa discreta Analisi dell’offerta, relazione con vendite o inventario
months_inventory Quantitativa continua Analisi della saturazione del mercato, indicatore domanda-offerta

Le variabili year e month definiscono il tempo in anni e mesi, ritengo sia oppurtono creare una variabile date combinata per poter ordinare i dati cronologicamente ed effettuare in seguito analisi temporali (es. trend o stagionalità).

realestate$date <- as.Date(paste(realestate$year, realestate$month, 1, sep = "-"))

Per facilitare la comprensione dei dati e semplificarne i calcoli, ho inoltre convertito la variabile median_price da dollari a milioni di dollari, così da uniformarla all’unità di misura utilizzata per volume

realestate$median_price <- realestate$median_price / 1000000

Step 2 - Indici di posizione, variabilità e forma

# Funzione per calcolare indici di posizione, variabilità e forma
calculate_indices <- function(x) {
  stats <- c(
    mean = mean(x),
    median = median(x),
    max = max(x),
    min = min(x),
    range = max(x) - min(x),
    Q1 = quantile(x)[2],
    Q3 = quantile(x)[4],
    IQR = IQR(x),
    variance = var(x),
    std_dev = sd(x),
    CV = sd(x) / mean(x) * 100,
    skewness = skew(x),
    kurtosis = kurtosi(x)
  )
  return(round(stats, 2))
}

numeric_columns <- c("sales", "volume", "median_price", "listings", "months_inventory")
indices_list <- lapply(realestate[numeric_columns], calculate_indices)
total_indices <- do.call(rbind, indices_list)
rownames(total_indices) <- c("Sales", "Volume", "Median_price", "Listings", "Months_Inventory")

total_indices
                    mean  median     max    min   range  Q1.25%  Q3.75%     IQR
Sales             192.29  175.50  423.00  79.00  344.00  127.00  247.00  120.00
Volume             31.01   27.06   83.55   8.17   75.38   17.66   40.89   23.23
Median_price        0.13    0.13    0.18   0.07    0.11    0.12    0.15    0.03
Listings         1738.02 1618.50 3296.00 743.00 2553.00 1026.50 2056.00 1029.50
Months_Inventory    9.19    8.95   14.90   3.40   11.50    7.80   10.95    3.15
                  variance std_dev    CV skewness kurtosis
Sales              6344.30   79.65 41.42     0.71    -0.34
Volume              277.27   16.65 53.71     0.88     0.15
Median_price          0.00    0.02 17.08    -0.36    -0.64
Listings         566568.97  752.71 43.31     0.65    -0.81
Months_Inventory      5.31    2.30 25.06     0.04    -0.20

Analizzando i risultati ottenuti da total_indices possiamo già notare come:

A primo impatto, il mercato immobiliare texano si presenta abbastanza stabile nei prezzi, ma con elevate fluttuazioni nei volumi e negli annunci attivi, riflettendo probabilmente la diversa dinamicità tra le città e i periodi dell’anno.

Step 2.1 - Distribuzione di frequenza

# Per city
city_frequence <- realestate %>%
  group_by(city) %>%
  summarise(
    freq_abs = n(),
    freq_rel = n() / nrow(realestate),
    freq_percent = (n() / nrow(realestate)) * 100
  ) %>%
  bind_rows(summarise(., city="Total", freq_abs=sum(freq_abs), freq_rel=sum(freq_rel), freq_percent=sum(freq_percent)))

as.data.frame(city_frequence)
                   city freq_abs freq_rel freq_percent
1              Beaumont       60     0.25           25
2 Bryan-College Station       60     0.25           25
3                 Tyler       60     0.25           25
4         Wichita Falls       60     0.25           25
5                 Total      240     1.00          100
# Per year
year_frequence <- realestate %>%
  mutate(year = as.character(year)) %>%
  group_by(year) %>%
  summarise(
    freq_abs = n(),
    freq_rel = n() / nrow(realestate),
    freq_percent = (n() / nrow(realestate)) * 100
  ) %>%
  bind_rows(summarise(., year="Total", freq_abs=sum(freq_abs), freq_rel=sum(freq_rel), freq_percent=sum(freq_percent)))

as.data.frame(year_frequence)
   year freq_abs freq_rel freq_percent
1  2010       48      0.2           20
2  2011       48      0.2           20
3  2012       48      0.2           20
4  2013       48      0.2           20
5  2014       48      0.2           20
6 Total      240      1.0          100
# Per month
month_frequence <- realestate %>%
  mutate(month = factor(month, levels = 1:12, 
                        labels = c("Gen","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dic"))) %>%
  group_by(month) %>%
  summarise(
    freq_abs = n(),
    freq_rel = n() / nrow(realestate),
    freq_percent = (n() / nrow(realestate)) * 100
  ) %>%
  bind_rows(summarise(., month="Total", freq_abs=sum(freq_abs), freq_rel=sum(freq_rel), freq_percent=sum(freq_percent)))

as.data.frame(month_frequence)
   month freq_abs   freq_rel freq_percent
1    Gen       20 0.08333333     8.333333
2    Feb       20 0.08333333     8.333333
3    Mar       20 0.08333333     8.333333
4    Apr       20 0.08333333     8.333333
5    May       20 0.08333333     8.333333
6    Jun       20 0.08333333     8.333333
7    Jul       20 0.08333333     8.333333
8    Aug       20 0.08333333     8.333333
9    Sep       20 0.08333333     8.333333
10   Oct       20 0.08333333     8.333333
11   Nov       20 0.08333333     8.333333
12   Dic       20 0.08333333     8.333333
13 Total      240 1.00000000   100.000000

Il dataset contiene informazioni relative a quattro città del Texas: Beaumont, Bryan-College Station, Tyler e Wichita Falls. Ciascuna rappresentata da 60 osservazioni, ovvero il 25% del totale, per un numero complessivo di 240 record. L’arco temporale coperto dai dati va dal 2010 al 2014, con 48 osservazioni per ciascun anno, pari al 20% del totale. Inoltre, tutti i 12 mesi dell’anno sono rappresentati in modo uniforme, con 20 osservazioni ciascuno, corrispondenti a circa l’8,33% del totale. Questo consente di poter effettuare analisi stagionali affidabili e comparabili, evitando il rischio di sovra- o sotto-rappresentazione di determinati periodi.

Il dataset è quindi costruito in maniera estremamente bilanciata e strutturata, con una distribuzione equa rispetto a città, anni e mesi. Tale impostazione garantisce delle condizioni ideali per analisi comparative tra classi, valutazioni su tendenze temporali e confronto tra le città, senza necessità di correzioni per squilibri nei dati.

Step 3 - Identificazione delle variabili con maggiore variabilità e asimmetria

var_data <- data.frame(
  name = rownames(total_indices),
  sd = total_indices[, "std_dev"],
  row.names = NULL
)

max_sd_row <- var_data[var_data$sd == max(var_data$sd), ]

skew_data <- data.frame(
  name = rownames(total_indices),
  skew = total_indices[, "skewness"],
  row.names = NULL
)

max_skew_row <- skew_data[abs(skew_data$skew) == max(abs(skew_data$skew)), ]

max_sd_row
      name     sd
4 Listings 752.71
max_skew_row
    name skew
2 Volume 0.88

Per identificare la variabile con maggiore variabilità ho creato un data frame var_data che associa a ciascuna variabile la sua deviazione standard (st_dev), recuperata da total_indices, per poi estrapolare il valore massimo. Il risultato ottenuto è stato: Listings, con una deviazione standard di 752.71. Questo risultato mi ha suggerito una forte fluttuazione del mercato in termini di offerta immobiliare.

Ho utilizzato lo stesso metodo anche per identificare la variabile con maggiore asimmetria, andando però ad estrarre la skewness di ciascuna variabile. Il risultato ottenuto è stato: Volume, con una skewness pari a 0.88. Avendo un’asimmetria positiva, la distribuzione risulterà asimmetrica verso destra: il chè significa che la maggior parte dei valori sarà concentrata verso il basso, presentando inoltre alcuni outlier.

Step 4 - Creazione di classi per una variabile quantitativa

variable <- realestate$sales
breaks <- seq(min(variable), max(variable), by = 50)
variable_cut <- cut(variable, breaks = breaks, include.lowest = TRUE, right = FALSE)

freq_table <- table(variable_cut)

barplot(freq_table,
        main = "Distribuzione di frequenza di sales",
        xlab = "Intervalli di sales",
        ylab = "Frequenza", 
        col = "skyblue", 
        border = NA,
        las = 1,  
        cex.names = 0.8,
        ylim = c(0, max(freq_table) + 10))

# Funzione per calcolare l'indice di Gini normalizzato
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(round(gini.norm, 2))
}

gini.index(variable_cut)
[1] 0.96

L’indice di Gini normalizzato calcolato sulla variabile Sales, suddivisa in classi di ampiezza 50, risulta pari a 0,96, indicando un elevato livello di disuguaglianza nella distribuzione delle frequenze tra gli intervalli. Questo valore riflette una forte eterogeneità delle osservazioni in poche classi, principalmente quelle corrispondenti ai livelli di vendita più bassi. Come possiamo notare anche dal grafico delle frequenze, le fasce iniziali raccolgono la maggior parte dei casi, mentre le classi relative a vendite più elevate risultano scarsamente rappresentate.

Step 5 - Calcolo della probabilità

probability <- function(variable, condition) {
  sum(realestate[[variable]] == condition) / nrow(realestate)
}

prob_beaumont <- (probability("city", "Beaumont"))
prob_july <- (probability("month", 7))
prob_december_2012 <- (sum(realestate$year==2012 & realestate$month==12)/nrow(realestate))

prob_beaumont
[1] 0.25
prob_july
[1] 0.08333333
prob_december_2012
[1] 0.01666667

Step 6 - Creazione di nuove variabili

realestate$average_price <- round((realestate$volume / realestate$sales), 4)
head(realestate$average_price)
[1] 0.1706 0.1638 0.1577 0.1341 0.1427 0.1440
realestate$ad_effectiveness <- round((realestate$sales / realestate$listings), 2)
head(realestate$ad_effectiveness)
[1] 0.05 0.07 0.11 0.12 0.11 0.10
calculate_indices(realestate$ad_effectiveness)
    mean   median      max      min    range   Q1.25%   Q3.75%      IQR 
    0.12     0.11     0.39     0.05     0.34     0.09     0.13     0.04 
variance  std_dev       CV skewness kurtosis 
    0.00     0.05    39.77     2.09     6.93 

L’efficacia degli annunci nel mercato immobiliare texano risulta essere molto bassa, con una media di appena 0.12 e una mediana di 0.11. Questo significa che, in media, solamente il 12% degli annunci pubblicati portano effettivamente ad una vendita.

Come possiamo inoltre notare, la distribuzione dei valori è piuttosto concentrata: il 1° quartile è 0.09, il 3° quartile è 0.13 e l’IQR è solo 0.04, questo ci indica che la maggior parte dei dati si concentra in una fascia molto ristretta.

La skewness è pari a 2.09 e indica una forte asimmetria a destra, mentre la kurtosis di 6.93 suggerisce una concentrazione centrale elevata ma con code lunghe, ovvero molte situazioni con efficacia molto bassa e alcune eccezioni con valori ben più alti.

Per concludere, il coefficiente di variazione (CV) risulta altissimo, pari a 39.77, e mette in evidenza una forte variabilità: l’efficacia degli annunci varia moltissimo da città a città e da mese a mese, segnalando un mercato non uniforme e con dinamiche locali molto differenziate.

Step 7 - Analisi condizionata

monthly_summary <- realestate %>%
  group_by(month) %>%
  summarise(mean_price = mean(median_price))

monthly_summary$month <- factor(monthly_summary$month,
                           levels = 1:12,
                           labels = c("Gen","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dic"))

ggplot(monthly_summary, aes(x = factor(month), y = mean_price)) +
  geom_bar(stat = "identity", fill = "#FF7F50") +
  geom_text(aes(label = round(mean_price, 2)), vjust = -0.5, color = "black", size = 3.5) +
  labs(title = "Prezzo medio mensile per mese", x = "Mese", y = "Prezzo medio") +
  scale_y_continuous(limits = c(0, 0.15))+
  theme_minimal(base_size = 10)+ 
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5))

sales_by_city <- realestate %>%
  group_by(city) %>%
  summarise(total_sales = sum(sales)) 

ggplot(sales_by_city, aes(x = reorder(city, -total_sales), y = total_sales)) +
  geom_bar(stat = "identity", fill = "#90EE90") +
  geom_text(aes(label = scales::comma(total_sales)), vjust = -0.5, color = "black", size = 3.5) +
  labs(title = "Vendite per città", x = "Città", y = "Vendite") +
  theme_minimal(base_size = 10) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5))

Step 7b - Analisi per città, anno e mese

city_year_month_summary <- realestate %>%
  group_by(city, year, month) %>%
  summarise(
    mean_price = mean(median_price),
    sd_price = sd(median_price),
    mean_sales = mean(sales),
    sd_sales = sd(sales)
  ) %>%
  mutate(date = as.Date(paste(year, month, "01", sep = "-")))

ggplot(city_year_month_summary, aes(x = date, y = mean_sales, fill = city)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ city, ncol = 1) +
  labs(title = "Media delle vendite per città e mese", x = "Date", y = "Vendite medie") +
  theme_minimal(base_size = 20) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        legend.position = "none")

Step 8 - Creazione di visualizzazioni con ggplot2

ggplot(realestate, aes(x = city, y = median_price, fill = city)) +
  geom_boxplot() +
  labs(title = "Distribuzione del prezzo mediano per città", x = "Città", y = "Prezzo Mediano") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1), 
        legend.position = "none")

Con questo grafico andiamo ad analizzare la distribuzione del prezzo mediano (in milioni di dollari) per: Beaumont, Bryan-College Station, Tyler e Wichita Falls. Possiamo notare che Bryan-College Station presenta il prezzo mediano più elevato, con valori centrali attorno a 0.16 milioni di dollari, mentre Wichita Falls risulta essere la città più economica, con una mediana vicina a 0.10 milioni. Le città di Beaumont e Tyler si collocano invece in una posizione intermedia, con prezzi mediani rispettivamente attorno a 0.13 e 0.145 milioni.

È interessante notare che Wichita Falls presenta una maggiore variabilità nei dati, con diversi valori anomali verso il basso, mentre Bryan-College Station mostra una distribuzione più concentrata, sebbene con alcuni outlier verso l’alto.

realestate$month <- factor(realestate$month,
                           levels = 1:12,
                           labels = c("Gen","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dic"))

ggplot(realestate, aes(x = month, y = sales, fill = city)) +
  geom_bar(stat = "identity", position = "stack", width = 0.8) +
  labs(title = "Totale delle vendite per mese e città", x = "Mese", y = "Totale Vendite", fill = "Città") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, size = 8), 
        legend.position = "top") +
  scale_fill_brewer(palette = "Set3")

Il grafico evidenzia chiaramente una stagionalità marcata nelle vendite, con un aumento progressivo a partire da marzo e un picco nei mesi compresi tra maggio e agosto. Questo andamento suggerisce che le vendite immobiliari tendano a concentrarsi nel periodo primaverile/estivo, probabilmente per ragioni legate ad un clima più favorevole e magari ad un aumento del turismo. Da settembre in poi si osserva invece un graduale calo delle vendite, con i livelli più bassi concentrati tra novembre e gennaio.

realestate_normalized <- realestate %>%
  group_by(month) %>%
  mutate(percent = sales / sum(sales))

ggplot(realestate_normalized, aes(x = month, y = percent, fill = city)) +
  geom_col(position = "dodge", width = 0.8) +
  labs(title = "Proporzione delle vendite per mese e città", x = "Mese", y = "Percentuale", fill = "Città") +
  scale_y_continuous(labels = scales::label_percent(accuracy = 1)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, size = 8), 
        legend.position = "top") +
  scale_fill_brewer(palette = "Set3")

ggplot(realestate, aes(x = date, y = sales, color = city)) +
  geom_line(size = 1) +
  geom_point(size = 1.5) +
  labs(title = "Andamento delle vendite nel tempo per città", x = "Data", y = "Vendite", color = "Città") +
  theme_minimal(base_size = 12) +
  theme(legend.position = "top", axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_x_date(date_labels = "%b %Y", date_breaks = "6 months")

Grazie a questo grafico possiamo osservare diverse tendenze interessanti:

In generale, il grafico mette in evidenzia come alcune città (Tyler e Bryan-College Station) abbiano un mercato delle vendite più attivo e soggetto a variazioni stagionali, mentre altre (Beaumont e Wichita Falls) mostrano un andamento più costante nel tempo.

Step 9 - Conclusione

L’analisi condotta del mercato immobiliare in Texas, focalizzata sulle città di Beaumont, Bryan-College Station, Tyler e Wichita Falls, ha messo in luce una serie di elementi fondamentali per comprendere le dinamiche storiche delle vendite e l’efficacia delle strategie di marketing attuate tramite le inserzioni.

Da un punto di vista temporale, i dati mostrano una stagionalità ben definita, con picchi durante i mesi primaverili ed estivi e un calo costante nei mesi invernali. Questo trend è particolarmente marcato a Tyler e Bryan-College Station, mentre risulta più attenuato a Wichita Falls, città in cui il mercato è meno dinamico. In generale, i prezzi medi delle abitazioni si mantengono stabili nel tempo, con differenze moderate tra le città: Bryan-College Station risulta l’area più cara tra quelle analizzate, mentre Wichita Falls presenta i prezzi medi più contenuti.

La seconda parte dell’indagine si è concentrata sull’efficacia delle inserzioni immobiliari, valutata attraverso il rapporto tra vendite e numero di annunci attivi. Questo indicatore mostra valori mediamente bassi (attorno al 12%), suggerendo che solo una piccola parte degli immobili pubblicizzati viene effettivamente venduta. Inoltre, la distribuzione dei dati evidenzia una forte variabilità e asimmetria, con alcune inserzioni molto performanti e altre del tutto inefficaci.

Nel complesso, il mercato analizzato si presenta solido ma disomogeneo, seppur con ampi margini di miglioramento sul fronte del marketing immobiliare. Per incrementare l’efficienza delle vendite, sarà fondamentale puntare su strategie di comunicazione più professionali, calibrate sulle caratteristiche specifiche di ciascun mercato locale. L’adozione di best practices, la valorizzazione dei dati storici e l’utilizzo di strumenti digitali avanzati rappresentano leve strategiche per potenziare la competitività e la redditività del settore immobiliare in Texas.