Introduzione

In questa analisi abbiamo esplorato un dataset relativo al mercato immobiliare in Texas, esaminando le caratteristiche delle vendite,il volume delle transazioni e i prezzi mediani delle proprietà. L’analisi è stata effettuata attraverso statistiche descrittive, misure di dispersione e distribuzione e visualizzazioni grafiche con ggplot2. Abbiamo utilizzato la libreria tidyverse per la manipolazione dei dati e moments per calcolare l’asimmetria (skewness) e la curtosi (kurtosis). Il dataset in oggetto contiene le seguenti variabili:

  • city: città di riferimento
  • year: anno di riferimento
  • month: mese di riferimento
  • sales: numero totale di vendite
  • volume: valore totale delle vendite (in milioni di dollari)
  • median_price: prezzo mediano di vendita (in dollari)
  • listings: numero totale di annunci attivi
  • months_inventory: quantità di tempo necessaria per vendere tutte le inserzioni correnti, espresso in mesi

Dopo il caricamento, abbiamo eseguito un’esplorazione iniziale con glimpse() e summary(), ottenendo una visione generale delle variabili presenti.

1. Caricamento delle librerie e del dataset

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
library(dplyr)
library(moments)  # Per skewness e kurtosis
library(ineq)     # Per indice di Gini
library(conflicted)

conflict_prefer("filter", "dplyr")
## [conflicted] Will prefer dplyr::filter over any other package.
conflict_prefer("lag", "dplyr")
## [conflicted] Will prefer dplyr::lag over any other package.
# Caricamento dataset
dataset <- read.csv("realestate_texas.csv", header = TRUE, stringsAsFactors = FALSE)

sum(is.na(dataset))  # Per vedere il numero di valori mancanti nel dataset 
## [1] 0
# Esplorazione iniziale del dataset
glimpse(dataset)
## Rows: 240
## Columns: 8
## $ city             <chr> "Beaumont", "Beaumont", "Beaumont", "Beaumont", "Beau…
## $ year             <int> 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010,…
## $ month            <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5,…
## $ sales            <int> 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         <int> 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, …
summary(dataset)
##      city                year          month           sales      
##  Length:240         Min.   :2010   Min.   : 1.00   Min.   : 79.0  
##  Class :character   1st Qu.:2011   1st Qu.: 3.75   1st Qu.:127.0  
##  Mode  :character   Median :2012   Median : 6.50   Median :175.5  
##                     Mean   :2012   Mean   : 6.50   Mean   :192.3  
##                     3rd Qu.:2013   3rd Qu.: 9.25   3rd Qu.:247.0  
##                     Max.   :2014   Max.   :12.00   Max.   :423.0  
##      volume        median_price       listings    months_inventory
##  Min.   : 8.166   Min.   : 73800   Min.   : 743   Min.   : 3.400  
##  1st Qu.:17.660   1st Qu.:117300   1st Qu.:1026   1st Qu.: 7.800  
##  Median :27.062   Median :134500   Median :1618   Median : 8.950  
##  Mean   :31.005   Mean   :132665   Mean   :1738   Mean   : 9.193  
##  3rd Qu.:40.893   3rd Qu.:150050   3rd Qu.:2056   3rd Qu.:10.950  
##  Max.   :83.547   Max.   :180000   Max.   :3296   Max.   :14.900

2. Analisi delle variabili

Abbiamo identificato le variabili numeriche e categoriche nel dataset, determinando il loro tipo con sapply(dataset, class). Questo passaggio è cruciale per comprendere quali operazioni statistiche possiamo applicare.

# Identificazione delle variabili numeriche e categoriali
sapply(dataset, class)
##             city             year            month            sales 
##      "character"        "integer"        "integer"        "integer" 
##           volume     median_price         listings months_inventory 
##        "numeric"        "numeric"        "integer"        "numeric"

3. Statistiche descrittive

Abbiamo calcolato le seguenti statistiche per le variabili “sales”, “volume”, “median_price”, “listings” e “months_inventory”:

  • Media e mediana
  • Deviazione standard
  • Asimmetria (skewness)
  • Curtosi (kurtosis)

Questi valori ci permettono di capire la distribuzione dei dati e la presenza di eventuali skewness o outlier.

# Seleziono solo le colonne numeriche

numeric_cols <- c("sales", "volume", "median_price", "listings", "months_inventory")

stats <- data.frame (
  Media = sapply(dataset[numeric_cols], mean, na.rm = TRUE),
  Mediana = sapply(dataset[numeric_cols], median, na.rm = TRUE),
  Moda = sapply(dataset[numeric_cols], function(x) as.numeric(names(sort(table(x), decreasing=TRUE)[1]))),
  Range = sapply(dataset[numeric_cols], function(x) max(x, na.rm = TRUE) - min(x, na.rm = TRUE)),
  Varianza = sapply(dataset[numeric_cols], var, na.rm = TRUE),
  Dev_Standard = sapply(dataset[numeric_cols], sd, na.rm = TRUE),
  Coeff_Variazione = sapply(dataset[numeric_cols], function(x) sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)),
  Asimmetria = sapply(dataset[numeric_cols], skewness, na.rm = TRUE),
  Curtosi = sapply(dataset[numeric_cols], kurtosis, na.rm = TRUE)
)

stats
##                         Media     Mediana       Moda      Range     Varianza
## sales               192.29167    175.5000    124.000    344.000 6.344300e+03
## volume               31.00519     27.0625     14.003     75.381 2.772707e+02
## median_price     132665.41667 134500.0000 130000.000 106200.000 5.135730e+08
## listings           1738.02083   1618.5000   1581.000   2553.000 5.665690e+05
## months_inventory      9.19250      8.9500      8.100     11.500 5.306889e+00
##                  Dev_Standard Coeff_Variazione  Asimmetria  Curtosi
## sales               79.651111        0.4142203  0.71810402 2.686824
## volume              16.651447        0.5370536  0.88474203 3.176987
## median_price     22662.148687        0.1708218 -0.36455288 2.377038
## listings           752.707756        0.4330833  0.64949823 2.208210
## months_inventory     2.303669        0.2506031  0.04097527 2.825552

Risultati:

Le vendite (sales) mostrano una distribuzione asimmetrica, con un valore medio superiore alla mediana, indicando la presenza di pochi valori molto elevati.

Il volume delle transazioni (volume) segue una distribuzione simile, evidenziando una forte variabilità.

Il prezzo mediano (median_price) ha una minore dispersione rispetto agli altri parametri.

4. Analisi della variabilità e asimmetria

Successivamente, abbiamo calcolato: il Coefficiente di variazione (CV) per determinare quale variabile ha la maggiore variabilità relativa e Skewness per identificare la variabile con la maggiore asimmetria

cv_values <- sapply(dataset[, c("sales", "volume", "median_price")], 
                    function(x) sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE))
var_max_cv <- names(cv_values)[which.max(cv_values)]

skewness_values <- sapply(dataset[, c("sales", "volume", "median_price")], 
                          skewness, na.rm = TRUE)
var_max_skew <- names(skewness_values)[which.max(abs(skewness_values))]



# Risultati della variabilità e asimmetria
cat("La variabile con la più alta variabilità è:", var_max_cv, "\n")
## La variabile con la più alta variabilità è: volume
cat("La variabile con la distribuzione più asimmetrica è:", var_max_skew, "\n")
## La variabile con la distribuzione più asimmetrica è: volume

L’alta variabilità del volume delle vendite indica che il valore totale delle transazioni varia notevolmente tra le città e i mesi. L’alta asimmetria suggerisce che ci sono alcuni periodi in cui il volume delle vendite è eccezionalmente alto rispetto alla norma. Questi risultati indicano che il mercato immobiliare è soggetto a grandi fluttuazioni, probabilmente legate a stagionalità o fattori economici.

5. Distribuzione di frequenza per la variabile quantitativa

A questo punto, abbiamo selezionato la variabile quantitativa “sales” e l’abbiamo suddivisa in classi, creato una distribuzione di frequenze e rappresentato i dati con un grafico a barre.

#Visualizzazione dell'analisi della distribuzione delle vendite

ggplot(dataset, aes(x = sales)) + 
  geom_histogram(binwidth = 10, fill = "skyblue", color = "black", alpha = 0.7) +
  facet_wrap(~ city) +
  labs(title = "Distribuzione delle Vendite per Città")

#Creazione di classi per la variabile "sales" e distribuzione di frequenza

breaks <- seq(min(dataset$sales, na.rm = TRUE), max(dataset$sales, na.rm = TRUE), length.out = 6) 
dataset$sales_class <- cut(dataset$sales, breaks = breaks, include.lowest = TRUE)

freq_table <- table(dataset$sales_class)

print(freq_table)
## 
##  [79,148] (148,217] (217,285] (285,354] (354,423] 
##        84        77        41        27        11
Mostrate nel seguente istogramma:
# Istogramma
col_palette <- terrain.colors(length(freq_table))

barplot(freq_table, 
        main = "Distribuzione delle Vendite (Sales)", 
        col =  col_palette,  
        xlab = "Classi di Sales", 
        ylab = "Frequenza",
        border = "black") 

Indice di Gini per le vendite

Abbiamo calcolato l’indice di Gini per la variabile sales e il risultato vicino a 0 indica che le vendite sono distribuite in modo abbastanza equo tra le città. Tuttavia, c’è ancora un certo livello di concentrazione.

#Calcolo dell'indice di Gini per le vendite

gini_sales <- Gini(dataset$sales, na.rm = TRUE)
cat("Indice di Gini per le vendite:", gini_sales, "\n")
## Indice di Gini per le vendite: 0.2310975

6. Probabilità di città e mesi

Abbiamo calcolato le probabilità condizionate:

-Probabilità che una transazione avvenga a Beaumont. -Probabilità che una vendita avvenga nel mese di luglio. -Probabilità che una vendita avvenga nel dicembre 2012.

prob_beaumont <- sum(dataset$city == "Beaumont", na.rm=TRUE) / nrow(dataset)
prob_july <- sum(dataset$month == 7, na.rm=TRUE) / nrow(dataset)
prob_december_2012 <- sum(dataset$month == 12 & dataset$year == 2012, na.rm=TRUE) / nrow(dataset)

cat("Probabilità di Beaumont:", prob_beaumont, "\n")
## Probabilità di Beaumont: 0.25
cat("Probabilità di Luglio:", prob_july, "\n")
## Probabilità di Luglio: 0.08333333
cat("Probabilità di Dicembre 2012:", prob_december_2012, "\n")
## Probabilità di Dicembre 2012: 0.01666667

7. Creazione nuove variabili

Abbiamo creato due nuove variabili:

  • avg_price: il prezzo medio per transazione (volume/sales).

  • marketing_efficiency: l’efficienza di mercato, data dal rapporto tra vendite e inserzioni (sales/listings).

dataset$avg_price <- dataset$volume / dataset$sales
dataset$marketing_efficiency <- ifelse(dataset$listings > 0, dataset$sales / dataset$listings, NA)

# Visualizzazione delle nuove variabili
head(dataset[c("volume", "sales", "avg_price", "marketing_efficiency")])
##   volume sales avg_price marketing_efficiency
## 1 14.162    83 0.1706265           0.05414220
## 2 17.690   108 0.1637963           0.06809584
## 3 28.701   182 0.1576978           0.10775607
## 4 26.819   200 0.1340950           0.11709602
## 5 28.833   202 0.1427376           0.11405985
## 6 27.219   189 0.1440159           0.10482529

Risultati:

L’analisi dell’efficienza di mercato aiuta a capire quanto il mercato sia reattivo rispetto alle nuove offerte. Nel caso specifico, l’efficacia degli annunci (vendite/annunci) è molto bassa (<10%), il che significa che ci sono troppi immobili disponibili rispetto alla domanda

8. Analisi condizionata

Abbiamo calcolato la media e la deviazione standard delle vendite per ciascuna città.

# Analisi condizionata per città

summary_by_city <- dataset %>%
  group_by(city) %>%
  summarise(avg_sales = mean(sales, na.rm=TRUE),
            sd_sales = sd(sales, na.rm=TRUE))

print(summary_by_city)
## # A tibble: 4 × 3
##   city                  avg_sales sd_sales
##   <chr>                     <dbl>    <dbl>
## 1 Beaumont                   177.     41.5
## 2 Bryan-College Station      206.     85.0
## 3 Tyler                      270.     62.0
## 4 Wichita Falls              116.     22.2
# Calcolo della media e della deviazione standard per prezzo medio e vendite per città, anno e mese

summary_stats <- dataset %>%
  group_by(city, year, month) %>%
  summarise(
    mean_price = mean(avg_price, na.rm = TRUE),
    sd_price = sd(avg_price, na.rm = TRUE),
    mean_sales = mean(sales, na.rm = TRUE),
    sd_sales = sd(sales, na.rm = TRUE),
    .groups = "drop"
  )
head(summary_stats)
## # A tibble: 6 × 7
##   city      year month mean_price sd_price mean_sales sd_sales
##   <chr>    <int> <int>      <dbl>    <dbl>      <dbl>    <dbl>
## 1 Beaumont  2010     1      0.171       NA         83       NA
## 2 Beaumont  2010     2      0.164       NA        108       NA
## 3 Beaumont  2010     3      0.158       NA        182       NA
## 4 Beaumont  2010     4      0.134       NA        200       NA
## 5 Beaumont  2010     5      0.143       NA        202       NA
## 6 Beaumont  2010     6      0.144       NA        189       NA

Risultati:

Alcune città mostrano un volume di vendite più elevato e una maggiore variabilità rispetto ad altre.

9. Visualizzazioni con ggplot2

Infine, abbiamo utilizzato diversi tipi di grafici per esplorare le distribuzioni e le relazioni tra le variabili:

#  Visualizzazione delle medie per prezzo medio per città

ggplot(summary_stats, aes(x = city, y = mean_price, fill = city)) +
  geom_bar(stat = "identity", show.legend = FALSE) +
  labs(title = "Media del Prezzo Medio per Città", x = "Città", y = "Prezzo Medio") +
  theme_minimal()

  • Linechart del prezzo mediano nel tempo
#  Line chart del prezzo medio nel tempo

ggplot(summary_stats, aes(x = as.Date(paste(year, month, "1", sep = "-")), 
                          y = mean_price, group = city, color = city)) +
  geom_line() +
  labs(title = "Andamento del Prezzo Medio nel Tempo", x = "Data", y = "Prezzo Medio") +
  theme_minimal()

  • Boxplot del prezzo mediano tra città

Evidenzia le differenze nei prezzi mediani.

# Boxplot per il prezzo mediano tra città

ggplot(dataset, aes(x = city, y = median_price)) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Distribuzione del Prezzo Mediano per Città")

Risultati:

Bryan-College Station ha i prezzi mediani più alti rispetto alle altre città, con una mediana intorno ai 150.000 dolllari, mentre alcune osservazioni superano i 175.000 dollari. Le città di Beaumont e Tyler presentano prezzi mediani simili, intorno ai 125.000-135.000 dollari, con una dispersione moderata. Wichita Falls ha i prezzi mediani più bassi, con una mediana inferiore ai 100.000 dollari e una distribuzione che mostra una maggiore variabilità con alcuni outlier al ribasso.

  • Boxplot del volume di vendita per città e anno

Mostra l’andamento delle transazioni immobiliari.

# Boxplot per il valore totale delle vendite per città e anno
ggplot(dataset, aes(x = city, y = volume, fill = factor(year))) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(title = "Distribuzione del Valore Totale delle Vendite per Città e Anno")

Risultati:

Bryan-College Station e Tyler hanno il volume di vendite più alto, con una tendenza alla crescita negli anni 2013 e 2014. Ciò suggerisce un mercato più attivo e dinamico. Beaumont mostra un volume inferiore ma comunque in crescita nel tempo, anche se con maggiore variabilità nelle transazioni tra gli anni. Wichita Falls ha il volume di vendita più basso rispetto alle altre città e una distribuzione più compatta, suggerendo un mercato più stabile ma meno attivo.

  • Grafico a barre sovrapposte e normalizzate per vendite mensili per città

Permette di confrontare l’andamento delle vendite tra città.

# Grafico a barre sovrapposte per vendite mensili per città
ggplot(dataset, aes(x = factor(month), y = sales, fill = city)) +
  geom_col(position = "stack") +
  ggtitle("Vendite Mensili per Città - Grafico Sovrapposto")

Risultati:

Si nota una stagionalità nelle vendite, con un picco massimo intorno ai mesi maggio, giugno e luglio, suggerendo che il mercato immobiliare è più attivo durante la tarda primavera e l’inizio dell’estate. Le città di Tyler (azzurro) e Beaumont (rosso) sembrano contribuire maggiormente al volume totale delle vendite rispetto a Bryan-College Station (verde) e Wichita Falls (viola).

# Grafico a barre normalizzato per vendite mensili per città
ggplot(dataset, aes(x = factor(month), y = sales, fill = city)) +
  geom_bar(stat = "identity", position = "fill") +
  labs(title = "Distribuzione Normalizzata delle Vendite Mensili per Città", x = "Mese", y = "Proporzione delle Vendite")

Risultati:

La proporzione delle vendite tra le città rimane relativamente costante nel tempo, indicando che la stagionalità influisce in modo simile su tutte le città. Tyler e Beaumont dominano la maggior parte delle vendite ogni mese, mentre Wichita Falls rappresenta sempre una quota minore delle transazioni.

# Grafico a barre delle vendite mensili per città
ggplot(dataset, aes(x = factor(month), y = sales, fill = city)) +
  geom_bar(stat = "identity", position = "dodge") +
  ggtitle("Vendite Mensili per Città")

  • Grafico dell’andamento delle vendite negli anni

Mostra la stagionalità e le variazioni nel tempo.

#Andamento delle vendite negli anni
ggplot(dataset, aes(x = factor(month), y = sales, fill = city)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~year) +
  ggtitle("Vendite Mensili per Città con Anno") +
  labs(x = "Mese", y = "Numero di Vendite") +
  theme_minimal()

  • Linechart per il confronto storico delle vendite

Permette di osservare l’andamento del mercato immobiliare nel tempo.

# Line chart per il confronto storico delle vendite
ggplot(dataset, aes(x = as.Date(paste(year, month, "1", sep = "-")), y = sales, color = city)) +
  geom_line(linewidth = 1) +
  geom_point() +
  labs(title = "Andamento delle Vendite nel Tempo",
       x = "Data",
       y = "Numero di Vendite") +
  theme_minimal()

CONCLUSIONI:

Dalle analisi svolte, possiamo affermare che, in generale, il mercato immobiliare in Texas è in crescita, con un aumento generale delle vendite tra il 2010 e il 2014. Abbiamo dimostrato che alcune città (Tyler, Bryan-College Station) mostrano maggiore dinamicità, mentre altre (Wichita Falls) rimangono più stabili. Il volume totale delle vendite, ha mostrato una forte asimmetria, suggerendo che in alcuni periodi o città si verificano picchi di transazioni molto elevate rispetto alla media. La stagionalità, infatti, gioca un ruolo importante, con vendite più alte nei mesi primaverili ed estivi e più basse in inverno.

L’analisi storica delle vendite ha permesso di individuare alcune tendenze chiave:

  • in alcuni anni si è registrato un calo delle vendite, spesso riconducibile a fattori economici, come crisi finanziarie o variazioni nei tassi di interesse sui mutui.

  • una ciclicità del mercato: in diverse città emergono pattern ripetitivi nel tempo, segnalando un comportamento ciclico del settore immobiliare.

  • dinamiche territoriali: mentre alcune città mostrano una crescita costante delle vendite, altre evidenziano oscillazioni più marcate.

L’indice di Gini indica che alcune città dominano il mercato, mentre altre hanno un minor numero di vendite. Per quel che riguarda l’efficacia degli annunci, essenziale per prevedere ed indirizzare le vendite, seppur evidenziando dei miglioramenti rispetto al passato, è ancora relativamente bassa, suggerendo un mercato con troppa offerta rispetto alla domanda.