knitr::opts_chunk$set(
  echo = TRUE,
  eval = TRUE,
  include = TRUE,
  message = FALSE,
  warning = FALSE
)

0) Caricamento dati

library(dplyr)
library(ggplot2)

dati <- read.csv("realestate_texas.csv", sep = ",")
head(dati)
##       city year month sales volume median_price listings months_inventory
## 1 Beaumont 2010     1    83 14.162       163800     1533              9.5
## 2 Beaumont 2010     2   108 17.690       138200     1586             10.0
## 3 Beaumont 2010     3   182 28.701       122400     1689             10.6
## 4 Beaumont 2010     4   200 26.819       123200     1708             10.6
## 5 Beaumont 2010     5   202 28.833       123100     1771             10.9
## 6 Beaumont 2010     6   189 27.219       122800     1803             11.1
str(dati)
## 'data.frame':    240 obs. of  8 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 ...
summary(dati)
##      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

Il dataset contiene informazioni sul mercato immobiliare in diverse città del Texas, con dati mensili relativi a vendite, volume, prezzi e offerta.

1) Analisi delle variabili

In questa sezione le variabili del dataset vengono classificate in base alla loro tipologia statistica e viene illustrato il tipo di analisi appropriata per ciascuna di esse.

Le variabili presenti nel dataset sono di diversa natura:

Le variabili temporali permettono analisi di trend e stagionalità, mentre le variabili quantitative consentono il calcolo di indici descrittivi e confronti tra città.

2) Indici di posizione, variabilità e forma

In questa sezione vengono calcolati gli indici descrittivi per le variabili quantitative del dataset.

In particolare: - Media e mediana (indici di posizione) - Deviazione standard e IQR (indici di variabilità) - Skewness (indice di forma) - Kurtosis Per le variabili qualitative verranno invece calcolate distribuzioni di fre-quenza.

library(moments)

indici <- data.frame(
  variabile = c("sales","volume","median_price","listings","months_inventory"),
  media = c(mean(dati$sales),
            mean(dati$volume),
            mean(dati$median_price),
            mean(dati$listings),
            mean(dati$months_inventory)),
  mediana = c(median(dati$sales),
              median(dati$volume),
              median(dati$median_price),
              median(dati$listings),
              median(dati$months_inventory)),
  sd = c(sd(dati$sales),
         sd(dati$volume),
         sd(dati$median_price),
         sd(dati$listings),
         sd(dati$months_inventory)),
  IQR = c(IQR(dati$sales),
          IQR(dati$volume),
          IQR(dati$median_price),
          IQR(dati$listings),
          IQR(dati$months_inventory)),
  skewness = c(skewness(dati$sales),
               skewness(dati$volume),
               skewness(dati$median_price),
               skewness(dati$listings),
               skewness(dati$months_inventory)),
  kurtosis = c(kurtosis(dati$sales),
             kurtosis(dati$volume),
             kurtosis(dati$median_price),
             kurtosis(dati$listings),
             kurtosis(dati$months_inventory))
)

indici$CV <- indici$sd / indici$media

indici
##          variabile        media     mediana           sd        IQR    skewness
## 1            sales    192.29167    175.5000    79.651111   120.0000  0.71810402
## 2           volume     31.00519     27.0625    16.651447    23.2335  0.88474203
## 3     median_price 132665.41667 134500.0000 22662.148687 32750.0000 -0.36455288
## 4         listings   1738.02083   1618.5000   752.707756  1029.5000  0.64949823
## 5 months_inventory      9.19250      8.9500     2.303669     3.1500  0.04097527
##   kurtosis        CV
## 1 2.686824 0.4142203
## 2 3.176987 0.5370536
## 3 2.377038 0.1708218
## 4 2.208210 0.4330833
## 5 2.825552 0.2506031
table(dati$city)
## 
##              Beaumont Bryan-College Station                 Tyler 
##                    60                    60                    60 
##         Wichita Falls 
##                    60
table(dati$year)
## 
## 2010 2011 2012 2013 2014 
##   48   48   48   48   48
table(dati$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
ggplot(dati, aes(x = city)) +
  geom_bar(fill = "lightblue") +
  theme_classic() +
  labs(title = "Distribuzione delle osservazioni per città",
       x = "Città",
       y = "Frequenza") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Dalla tabella si osserva che:

La combinazione tra CV, skewness e kurtosis consente di valutare contemporaneamente stabilità, asimmetria e presenza di valori anomali.

Le distribuzioni di frequenza mostrano che:

Ogni città è rappresentata con lo stesso numero di osservazioni (60), indicando un dataset bilanciato rispetto alla variabile city.

Anche gli anni risultano equamente distribuiti (48 osservazioni per anno).

I mesi sono distribuiti uniformemente (20 osservazioni per ciascun mese), garantendo una copertura temporale completa e omogenea.

Il grafico per city conferma che il dataset è bilanciato rispetto alla variabile city, con lo stesso numero di osservazioni per ciascuna città.

3) Identificazione delle variabili con maggiore variabilità e asimmetria

In questa sezione si analizza: - la variabile con maggiore variabilità relativa (CV massimo) - la variabile con distribuzione più asimmetrica (|skewness| massimo)

var_max_cv <- indici$variabile[which.max(indici$CV)]
var_max_sk <- indici$variabile[which.max(abs(indici$skewness))]

var_max_cv
## [1] "volume"
var_max_sk
## [1] "volume"

La variabile con coefficiente di variazione (CV) massimo presenta la maggiore variabilità relativa, ossia una dispersione più elevata in rapporto al proprio valore medio.

La variabile con valore assoluto della skewness più elevato è quella che mostra il maggiore grado di asimmetria, indicando una distribuzione caratterizzata da una coda più pronunciata e dalla presenza di valori relativamente più distanti dalla media.

4) Creazione di classi per una variabile quantitativa

Si seleziona la variabile sales e si suddivide in 5 classi. Viene costruita la distribuzione di frequenza e viene rappresentata con un grafico a barre. Si calcola infine l’indice di eterogeneità di Gini sulle classi ottenute.

# Creazione classi per sales
dati$classi_sales <- cut(dati$sales, breaks = 5)

# Distribuzione di frequenza
frequenze_sales <- table(dati$classi_sales)

frequenze_sales
## 
## (78.7,148]  (148,217]  (217,285]  (285,354]  (354,423] 
##         84         77         41         27         11
prop.table(frequenze_sales)
## 
## (78.7,148]  (148,217]  (217,285]  (285,354]  (354,423] 
## 0.35000000 0.32083333 0.17083333 0.11250000 0.04583333
barplot(frequenze_sales,
        main = "Distribuzione di sales in 5 classi",
        xlab = "Classi di sales",
        ylab = "Frequenza",
        col = "lightblue",
        las = 2)

## Gini

gini.index <- function(x){
  ni <- table(x)
  fi <- ni / length(x)
  J <- length(ni)
  gini <- 1 - sum(fi^2)
  gini.normalizzato <- gini / ((J-1)/J)
  return(gini.normalizzato)
}

gini_sales <- gini.index(dati$classi_sales)
gini_sales
## [1] 0.9132812

L’indice di Gini normalizzato misura il grado di eterogeneità della distribuzione tra le classi.

Un valore vicino a 1 indica forte concentrazione delle osservazioni in alcune classi, mentre un valore vicino a 0 indica distribuzione più uniforme.

Il valore ottenuto suggerisce il livello di concentrazione delle vendite all’interno delle classi individuate.

5) Calcolo della probabilità

Si calcolano le probabilità richieste come frequenze relative nel dataset:

prob_beaumont <- mean(dati$city == "Beaumont")
prob_luglio   <- mean(dati$month == 7)
prob_dic2012  <- mean(dati$year == 2012 & dati$month == 12)

prob_beaumont
## [1] 0.25
prob_luglio
## [1] 0.08333333
prob_dic2012
## [1] 0.01666667

Queste probabilità rappresentano la quota di osservazioni del dataset che soddisfa ciascuna condizione (interpretazione frequentista).

6) Creazione di nuove variabili

6.1 Stima del prezzo medio per transazione

Il dataset non contiene i prezzi delle singole abitazioni, ma solo: - il volume totale delle vendite (in milioni di dollari) - il numero di vendite effettuate nel periodo

Non è quindi possibile calcolare la vera media dei prezzi individuali. È tuttavia possibile stimare un prezzo medio per transazione come rapporto tra volume totale e numero di vendite.

dati$price_estimate <- ifelse(dati$sales > 0,
                              (dati$volume * 1e6) / dati$sales,
                              NA_real_)
summary(dati$price_estimate)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   97010  132939  156588  154320  173915  213234

6.2 Efficacia degli annunci (sales/listings)

L’efficacia degli annunci è definita come il rapporto tra vendite concluse e annunci attivi:

dati$efficacia_annunci <- ifelse(dati$listings > 0,
                                 dati$sales / dati$listings,
                                 NA_real_)
summary(dati$efficacia_annunci)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.05014 0.08980 0.10963 0.11874 0.13492 0.38713

Si misura l’efficacia degli annunci come rapporto tra vendite concluse e annunci attivi. Valori più alti indicano un mercato dinamico e una maggiore capacità di assorbire l’offerta, mentre valori più bassi suggeriscono eccesso di offerta o maggiore difficoltà di vendita.

7) Analisi condizionata (città e anno)

Vengono calcolate statistiche condizionate per città e anno (media e deviazione standard) su variabili chiave.

summary_city_year <- dati %>%
  group_by(city, year) %>%
  summarise(
    mean_sales = mean(sales),
    sd_sales   = sd(sales),
    mean_volume = mean(volume),
    sd_volume   = sd(volume),
    mean_price  = mean(median_price),
    sd_price    = sd(median_price),
    mean_inventory = mean(months_inventory),
    sd_inventory   = sd(months_inventory),
    .groups = "drop"
  )

summary_city_year
## # A tibble: 20 × 10
##    city       year mean_sales sd_sales mean_volume sd_volume mean_price sd_price
##    <chr>     <int>      <dbl>    <dbl>       <dbl>     <dbl>      <dbl>    <dbl>
##  1 Beaumont   2010       156.     36.9        22.7      4.95    133117.   13354.
##  2 Beaumont   2011       144      22.7        21.1      4.30    125642.    9603.
##  3 Beaumont   2012       172.     28.4        24.5      4.92    126533.    7973.
##  4 Beaumont   2013       201.     37.7        30.3      6.44    132400     7785.
##  5 Beaumont   2014       214.     36.5        32.1      7.05    132250     9835.
##  6 Bryan-Co…  2010       168.     70.8        28.7     10.8     153533.    5474.
##  7 Bryan-Co…  2011       167.     62.2        28.9     10.3     151417.    3709.
##  8 Bryan-Co…  2012       197.     74.3        35.4     13.5     153567.    7096.
##  9 Bryan-Co…  2013       238.     95.8        45.1     19.5     159392.    5429.
## 10 Bryan-Co…  2014       260.     86.7        52.8     18.0     169533.    7776.
## 11 Tyler      2010       228.     49.0        36.3      8.39    135175     4782.
## 12 Tyler      2011       239.     49.6        38.6      9.41    136217.    8505.
## 13 Tyler      2012       264.     46.4        44.0     10.2     139250     7983.
## 14 Tyler      2013       287.     53.0        50.3     10.3     146100     6726.
## 15 Tyler      2014       332.     56.9        59.6     12.8     150467.    8543.
## 16 Wichita …  2010       123.     26.6        15.0      4.07     98942.   10361.
## 17 Wichita …  2011       106.     19.8        12.1      2.52     98142.   10632.
## 18 Wichita …  2012       112.     14.2        13.2      2.66    100958.   12347.
## 19 Wichita …  2013       121.     26.0        14.9      3.11    105000    10383.
## 20 Wichita …  2014       117      21.1        14.5      3.13    105675    12444.
## # ℹ 2 more variables: mean_inventory <dbl>, sd_inventory <dbl>
summary_city_month <- dati %>%
  group_by(city, month) %>%
  summarise(
    mean_sales = mean(sales),
    sd_sales   = sd(sales),
    .groups = "drop"
  )

summary_city_month
## # A tibble: 48 × 4
##    city     month mean_sales sd_sales
##    <chr>    <int>      <dbl>    <dbl>
##  1 Beaumont     1       122.     31.2
##  2 Beaumont     2       135.     31.9
##  3 Beaumont     3       171      14.9
##  4 Beaumont     4       190.     17.7
##  5 Beaumont     5       207.     42.6
##  6 Beaumont     6       205      36.0
##  7 Beaumont     7       185.     22.9
##  8 Beaumont     8       217.     50.6
##  9 Beaumont     9       174      46.9
## 10 Beaumont    10       189.     44.0
## # ℹ 38 more rows
ggplot(summary_city_month, aes(x = factor(month), y = mean_sales, color = city, group = city)) +
  geom_line() +
  geom_point() +
  theme_classic() +
  labs(title = "Vendite medie per mese (media su tutti gli anni) per città",
       x = "Mese", y = "Sales medio", color = "Città")

Questa tabella consente di confrontare i mercati (città) e i periodi (anni) evidenziando livelli medi e stabilità: deviazioni standard più alte indicano maggiore variabilità nel tempo.

Analisi condizionata per città, anno e mese

summary_city_year_month <- dati %>%
  group_by(city, year, month) %>%
  summarise(
    mean_sales = mean(sales),
    sd_sales   = sd(sales),
    mean_volume = mean(volume),
    .groups = "drop"
  )

summary_city_year_month
## # A tibble: 240 × 6
##    city      year month mean_sales sd_sales mean_volume
##    <chr>    <int> <int>      <dbl>    <dbl>       <dbl>
##  1 Beaumont  2010     1         83       NA        14.2
##  2 Beaumont  2010     2        108       NA        17.7
##  3 Beaumont  2010     3        182       NA        28.7
##  4 Beaumont  2010     4        200       NA        26.8
##  5 Beaumont  2010     5        202       NA        28.8
##  6 Beaumont  2010     6        189       NA        27.2
##  7 Beaumont  2010     7        164       NA        22.7
##  8 Beaumont  2010     8        174       NA        25.2
##  9 Beaumont  2010     9        124       NA        17.2
## 10 Beaumont  2010    10        150       NA        23.9
## # ℹ 230 more rows

Per la combinazione (city, year, month) è presente una sola osservazione per gruppo; di conseguenza la deviazione standard non è calcolabile e risulta NA.

8) Visualizzazioni con ggplot2

8.1 Boxplot: confronto del prezzo mediano tra città

ggplot(dati, aes(x = city, y = median_price)) +
  geom_boxplot() +
  theme_classic() +
  labs(title = "Distribuzione del prezzo mediano (median_price) per città",
       x = "Città", y = "Prezzo mediano ($)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

### 8.2 Boxplot: confronto del volume tra città e anni

ggplot(dati, aes(x = city, y = volume)) +
  geom_boxplot() +
  facet_wrap(~ year) +
  theme_classic() +
  labs(title = "Distribuzione del volume (milioni $) per città e anno",
       x = "Città", y = "Volume (milioni $)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

### 8.3 Barre stacked e normalizzate: totale vendite per mese e città (con Year nello stesso blocco)

agg_sales_mese <- dati %>%
  group_by(year, month, city) %>%
  summarise(sales_tot = sum(sales), .groups = "drop")

Stacked (valori assoluti) e Normalizzato

ggplot(agg_sales_mese, aes(x = factor(month), y = sales_tot, fill = city)) +
  geom_col() +
  facet_wrap(~ year) +
  theme_classic() +
  labs(title = "Totale vendite (sales) per mese e città (stacked), separato per anno",
       x = "Mese", y = "Vendite totali", fill = "Città")

### Stacked Normalizzato

ggplot(agg_sales_mese, aes(x = factor(month), y = sales_tot, fill = city)) +
  geom_col(position = "fill") +
  facet_wrap(~ year) +
  theme_classic() +
  labs(title = "Totale vendite per mese (normalizzato), separato per anno",
       x = "Mese", y = "Proporzione", fill = "Città")

Viene usato geom_col() perché vengono rappresentati valori già aggregati (sales_tot). geom_bar() invece calcola conteggi automaticamente.

8.4 Line chart: confronto tra città e periodi storici (vendite nel tempo)

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

agg_sales_time <- dati %>%
  group_by(date, city) %>%
  summarise(sales_tot = sum(sales), .groups = "drop") %>%
  arrange(city, date)

ggplot(agg_sales_time, aes(x = date, y = sales_tot, color = city)) +
  geom_line() +
  theme_classic() +
  labs(title = "Andamento delle vendite (sales) nel tempo per città",
       x = "Tempo", y = "Vendite totali", color = "Città")

8.5 Interpretazione rapida dei grafici

8.1 Boxplot – Prezzo mediano per città

Il boxplot evidenzia differenze strutturali tra i mercati locali. Bryan–College Station presenta una mediana più elevata rispetto alle altre città, mentre Wichita Falls mostra livelli sistematicamente inferiori. Tyler e Beaumont si collocano in posizione intermedia.

La diversa ampiezza dell’IQR indica una variabilità differente dei prezzi tra città. La presenza di alcuni outlier segnala mesi o anni caratterizzati da condizioni di mercato particolarmente favorevoli o atipiche.

8.2 Boxplot – Volume per città e anno

Il confronto per anno mostra un’evoluzione temporale del volume totale delle vendite. In diversi casi si osserva un aumento del livello centrale negli anni più recenti, in particolare per Tyler e Bryan–College Station.

L’ampiezza delle scatole e delle whiskers suggerisce una maggiore dispersione negli anni finali, coerente con il valore elevato del coefficiente di variazione del volume. Questo indica che l’intensità economica del mercato diventa progressivamente più variabile nel tempo.

8.3 Barre stacked – Totale vendite per mese e città

Il grafico stacked in valori assoluti evidenzia chiaramente la stagionalità delle vendite: i mesi primaverili ed estivi presentano livelli complessivi più elevati rispetto ai mesi autunnali e invernali.

Si osserva inoltre che Tyler e Bryan–College Station contribuiscono in misura maggiore al totale nei mesi di picco, confermando il loro ruolo più dinamico nel mercato complessivo.

8.3 Barre normalizzate (position = “fill”)

La versione normalizzata consente di confrontare le quote percentuali delle città, indipendentemente dal livello totale delle vendite.

La struttura percentuale appare nel complesso relativamente stabile nel tempo, pur con alcune variazioni nei singoli anni. Questo suggerisce che le differenze tra città sono in parte strutturali e non esclusivamente legate alla stagionalità.

8.4 Line chart – Andamento vendite nel tempo

Il grafico temporale evidenzia una chiara componente ciclica con picchi ricorrenti nei mesi centrali dell’anno.

Tyler mostra un trend complessivamente crescente nel periodo 2010–2014, con oscillazioni più ampie rispetto alle altre città. Bryan–College Station segue un andamento simile, mentre Wichita Falls presenta livelli più contenuti e una dinamica relativamente più stabile.

Le differenze persistenti tra le linee indicano mercati locali con intensità strutturalmente differenti.

9) Conclusioni

L’analisi del dataset nel periodo 2010–2014 evidenzia un mercato immobiliare eterogeneo tra città e caratterizzato da una chiara componente stagionale.

Dal punto di vista descrittivo, la variabile volume risulta la più instabile in termini relativi (CV = 0.537) e anche la più asimmetrica (skewness = 0.885). Ciò indica che l’intensità economica del mercato oscilla proporzionalmente più del numero di transazioni e presenta periodi con valori particolarmente elevati, coerenti con i picchi osservati nei grafici temporali.

La distribuzione delle vendite in classi mostra una forte disuguaglianza tra frequenze (indice di Gini normalizzato = 0.913), con concentrazione nelle fasce medio-basse e una presenza meno frequente di livelli molto elevati. Questo suggerisce che i picchi di attività rappresentano eventi relativamente rari ma economicamente rilevanti.

L’analisi grafica evidenzia:

una stagionalità ricorrente, con livelli di vendita più elevati nei mesi primaverili ed estivi;

differenze strutturali tra città: Tyler e Bryan–College Station mostrano livelli medi più alti e maggiore dinamismo, mentre Wichita Falls presenta valori più contenuti e maggiore stabilità; Beaumont si colloca in posizione intermedia.

Gli indicatori costruiti (stima del prezzo medio per transazione e rapporto vendite/annunci) forniscono una lettura operativa del mercato, permettendo di valutare sia l’intensità economica sia la capacità di assorbimento dell’offerta.

Nel complesso, il mercato analizzato non risulta omogeneo, ma caratterizzato da dinamiche territoriali differenziate e da una componente ciclica significativa. Nei contesti più dinamici appare opportuno un monitoraggio frequente dell’andamento delle vendite; nei mercati più stabili possono essere adottate strategie più conservative e orientate al medio periodo.