setwd("C:/Users/barba/Downloads/RFile")
# Caricamento pacchetti
library(dplyr)
## 
## Caricamento pacchetto: 'dplyr'
## I seguenti oggetti sono mascherati da 'package:stats':
## 
##     filter, lag
## I seguenti oggetti sono mascherati da 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(moments)  # per skewness e kurtosis

# Funzione per etichette percentuali 
percent_label <- function(x) paste0(round(x * 100, 1), "%")

# Import del dataset
df <- read.csv("realestate_texas.csv")
str(df)
## '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 ...

Introduzione

L’obiettivo di questo lavoro è analizzare in modo descrittivo il mercato immobiliare residenziale in alcune città del Texas (Beaumont, Bryan‑College Station, Tyler, Wichita Falls) nel periodo 2010–2014, utilizzando il dataset “Real Estate Texas”.
L’analisi mira a identificare trend storici nelle vendite, differenze tra città, pattern stagionali e indicatori di efficacia delle inserzioni, affiancando ai risultati numerici una serie di visualizzazioni grafiche (boxplot, grafici a barre, line chart) utili per supportare decisioni strategiche di business.


1. Analisi delle variabili

# Distribuzioni di frequenza per le variabili qualitative
table(df$city)
## 
##              Beaumont Bryan-College Station                 Tyler 
##                    60                    60                    60 
##         Wichita Falls 
##                    60
table(df$year)
## 
## 2010 2011 2012 2013 2014 
##   48   48   48   48   48
table(df$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
# Riassunti descrittivi iniziali per le variabili quantitative
summary(df[, 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

Il dataset contiene, per ogni città, anno e mese, informazioni relative alle vendite immobiliari. Le variabili sono le seguenti:

Le variabili temporali year e month non vengono analizzate con indici di posizione tradizionali, ma come dimensioni lungo le quali osservare l’evoluzione delle variabili quantitative (serie temporali, trend e stagionalità).


2. Indici di posizione, variabilità e forma

# Indici descrittivi globali
desc_global <- df %>%
  summarise(
    mean_sales  = mean(sales),  sd_sales  = sd(sales),
    mean_vol    = mean(volume), sd_vol    = sd(volume),
    mean_price  = mean(median_price), sd_price = sd(median_price),
    mean_list   = mean(listings), sd_list = sd(listings),
    mean_mi     = mean(months_inventory), sd_mi = sd(months_inventory)
  )
desc_global
##   mean_sales sd_sales mean_vol   sd_vol mean_price sd_price mean_list  sd_list
## 1   192.2917 79.65111 31.00519 16.65145   132665.4 22662.15  1738.021 752.7078
##   mean_mi    sd_mi
## 1  9.1925 2.303669
# Coefficienti di variazione
cv <- df %>%
  summarise(
    cv_sales  = sd(sales)  / mean(sales),
    cv_vol    = sd(volume) / mean(volume),
    cv_price  = sd(median_price) / mean(median_price),
    cv_list   = sd(listings) / mean(listings),
    cv_mi     = sd(months_inventory) / mean(months_inventory)
  )
cv
##    cv_sales    cv_vol  cv_price   cv_list     cv_mi
## 1 0.4142203 0.5370536 0.1708218 0.4330833 0.2506031
# Indici di forma (asimmetria e curtosi) 
shape <- data.frame(
  var = c("sales","volume","median_price","listings","months_inventory"),
  skew = c(skewness(df$sales),
           skewness(df$volume),
           skewness(df$median_price),
           skewness(df$listings),
           skewness(df$months_inventory)),
  kurt = c(kurtosis(df$sales),
           kurtosis(df$volume),
           kurtosis(df$median_price),
           kurtosis(df$listings),
           kurtosis(df$months_inventory))
)
shape
##                var        skew     kurt
## 1            sales  0.71810402 2.686824
## 2           volume  0.88474203 3.176987
## 3     median_price -0.36455288 2.377038
## 4         listings  0.64949823 2.208210
## 5 months_inventory  0.04097527 2.825552

A livello complessivo, le variabili sales e volume mostrano valori medi differenti tra città, con Tyler e Bryan‑College Station generalmente caratterizzate da livelli di attività più elevati rispetto a Wichita Falls. La variabilità delle due variabili è significativa, segnalando oscillazioni notevoli da un mese all’altro.

La variabile median_price presenta una dispersione inferiore a quella del volume, ma evidenzia differenze di livello tra città (mercati più o meno “ricchi”).
Le variabili listings e months_inventory risultano più elevate all’inizio del periodo e in progressivo calo verso il 2013–2014, suggerendo una maggiore efficienza e liquidità del mercato nel tempo.

Per le variabili qualitative (city, year, month) le distribuzioni di frequenza confermano un buon bilanciamento delle osservazioni tra città, anni e mesi, consentendo confronti temporali e spaziali affidabili.


3. Variabili con maggiore variabilità e asimmetria

# Individuazione della variabile con CV più alto
cv_long <- tidyr::pivot_longer(cv, everything(),
                               names_to = "var", values_to = "cv")
cv_long
## # A tibble: 5 × 2
##   var         cv
##   <chr>    <dbl>
## 1 cv_sales 0.414
## 2 cv_vol   0.537
## 3 cv_price 0.171
## 4 cv_list  0.433
## 5 cv_mi    0.251
# Individuazione della variabile con skewness più alto in valore assoluto
shape$abs_skew <- abs(shape$skew)
shape[order(-shape$abs_skew), ]
##                var        skew     kurt   abs_skew
## 2           volume  0.88474203 3.176987 0.88474203
## 1            sales  0.71810402 2.686824 0.71810402
## 4         listings  0.64949823 2.208210 0.64949823
## 3     median_price -0.36455288 2.377038 0.36455288
## 5 months_inventory  0.04097527 2.825552 0.04097527

Per confrontare la variabilità relativa si è utilizzato il coefficiente di variazione. Dai calcoli emerge che il volume delle vendite è la variabile con il CV più elevato, quindi quella con maggiore dispersione relativa rispetto alla media.

Per l’asimmetria, la variabile volume mostra la skewness in valore assoluto più alta, con asimmetria positiva marcata: la distribuzione è concentrata su valori medi/bassi, ma presenta alcuni mesi con volumi molto elevati che generano una coda a destra.
Si conclude dunque che:

  • la variabile con maggiore variabilità relativa è il volume delle vendite;
  • la variabile con distribuzione più asimmetrica (positivamente) è ancora il volume.

4. Classi per una variabile quantitativa e indice di Gini

# Scelta della variabile quantitativa: median_price
min_p <- min(df$median_price)
max_p <- max(df$median_price)

# Creazione di 6 classi di uguale ampiezza
breaks <- seq(min_p, max_p, length.out = 7)
df$price_class <- cut(df$median_price, breaks = breaks, include.lowest = TRUE)

# Distribuzione di frequenza per classi
freq_price <- table(df$price_class)
rel_freq_price <- prop.table(freq_price)
freq_price
## 
## [7.38e+04,9.15e+04] (9.15e+04,1.09e+05] (1.09e+05,1.27e+05] (1.27e+05,1.45e+05] 
##                  13                  33                  37                  73 
## (1.45e+05,1.62e+05]  (1.62e+05,1.8e+05] 
##                  68                  16
rel_freq_price
## 
## [7.38e+04,9.15e+04] (9.15e+04,1.09e+05] (1.09e+05,1.27e+05] (1.27e+05,1.45e+05] 
##          0.05416667          0.13750000          0.15416667          0.30416667 
## (1.45e+05,1.62e+05]  (1.62e+05,1.8e+05] 
##          0.28333333          0.06666667
# Grafico a barre delle classi di prezzo
ggplot(df, aes(x = price_class)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(x = "Classi di prezzo mediano", y = "Frequenza")

# Indice di eterogeneità di Gini
p <- as.numeric(rel_freq_price)
gini_eterogeneita <- 1 - sum(p^2)
gini_eterogeneita
## [1] 0.7771528

La distribuzione per classi del median_price mostra una maggiore concentrazione nelle fasce centrali, con un numero più limitato di osservazioni nelle classi corrispondenti ai prezzi più elevati. Questo conferma la presenza di una certa asimmetria positiva.

L’indice di Gini di eterogeneità assume un valore intermedio‑alto, segnalando che le osservazioni sono distribuite su più classi, pur con una densità maggiore in un intervallo centrale. Esiste quindi una discreta varietà di segmenti di prezzo, utile per la segmentazione della clientela e delle strategie di listing.


5. Calcolo della probabilità

n_tot <- nrow(df)

# Probabilità che la riga sia relativa a Beaumont
p_beaumont <- sum(df$city == "Beaumont") / n_tot
p_beaumont
## [1] 0.25
# Probabilità che la riga sia relativa al mese di luglio
p_july <- sum(df$month == 7) / n_tot
p_july
## [1] 0.08333333
# Probabilità che la riga sia relativa a Beaumont, dicembre 2012
p_dec2012_beaumont <- sum(df$city == "Beaumont" &
                          df$year == 2012 &
                          df$month == 12) / n_tot
p_dec2012_beaumont
## [1] 0.004166667

La probabilità empirica che una riga del dataset si riferisca a Beaumont è data dal rapporto tra il numero di osservazioni per questa città e il numero totale di osservazioni.
Analogamente, la probabilità che una riga si riferisca al mese di luglio si ottiene come quota di righe con month = 7 sul totale.

Per l’evento congiunto “dicembre 2012 a Beaumont”, la probabilità corrisponde alla proporzione di righe con city = Beaumont, year = 2012 e month = 12 sul totale: essendo presente una sola combinazione per ogni città‑anno‑mese, tale probabilità è pari a 1 diviso il numero totale di righe.


6. Creazione di nuove variabili

6.1 Prezzo medio per transazione

# Creazione della variabile avg_price
df <- df %>%
  mutate(avg_price = (volume * 1e6) / sales)

summary(df$avg_price)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   97010  132939  156588  154320  173915  213234
# Confronto con median_price
cor(df$median_price, df$avg_price)
## [1] 0.9464941
ggplot(df, aes(x = median_price, y = avg_price, color = city)) +
  geom_point() +
  labs(x = "Prezzo mediano", y = "Prezzo medio per transazione")

La nuova variabile avg_price rappresenta il prezzo medio di ogni transazione e, confrontata con median_price, fornisce indicazioni sulla forma della distribuzione dei prezzi.
Dove media e mediana sono simili, la distribuzione dei prezzi è abbastanza simmetrica; dove la media risulta più elevata, si osserva una coda a destra, dovuta a poche transazioni di valore particolarmente alto.

6.2 Misura dell’efficacia degli annunci

# Indicatori di efficacia
df <- df %>%
  mutate(
    eff_ratio = sales / listings,
    eff_sales_per_mi = sales / months_inventory
  )

summary(df$eff_ratio)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.05014 0.08980 0.10963 0.11874 0.13492 0.38713
summary(df$eff_sales_per_mi)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   8.737  14.165  18.477  22.457  26.424  98.293
# Confronto per città
df %>%
  group_by(city) %>%
  summarise(
    mean_eff_ratio = mean(eff_ratio),
    mean_eff_sales_per_mi = mean(eff_sales_per_mi)
  )
## # A tibble: 4 × 3
##   city                  mean_eff_ratio mean_eff_sales_per_mi
##   <chr>                          <dbl>                 <dbl>
## 1 Beaumont                      0.106                   18.7
## 2 Bryan-College Station         0.147                   31.2
## 3 Tyler                         0.0935                  24.9
## 4 Wichita Falls                 0.128                   15.0

L’indicatore eff_ratio misura la quota di annunci che si trasformano in vendite, mentre eff_sales_per_mi interpreta le vendite in rapporto al tempo di assorbimento dell’inventario.
In molte città tali indicatori mostrano una tendenza alla crescita negli anni, coerente con il calo di months_inventory, e suggerendo un mercato via via più efficiente.


7. Analisi condizionata per città, anno e mese

# Summary per città
summary_city <- df %>%
  group_by(city) %>%
  summarise(
    mean_sales  = mean(sales),  sd_sales  = sd(sales),
    mean_vol    = mean(volume), sd_vol   = sd(volume),
    mean_price  = mean(median_price), sd_price = sd(median_price),
    mean_list   = mean(listings), sd_list = sd(listings),
    mean_mi     = mean(months_inventory), sd_mi = sd(months_inventory),
    mean_eff_ratio = mean(eff_ratio),
    mean_eff_sales_per_mi = mean(eff_sales_per_mi)
  )
summary_city
## # A tibble: 4 × 13
##   city         mean_sales sd_sales mean_vol sd_vol mean_price sd_price mean_list
##   <chr>             <dbl>    <dbl>    <dbl>  <dbl>      <dbl>    <dbl>     <dbl>
## 1 Beaumont           177.     41.5     26.1   6.97    129988.   10105.     1679.
## 2 Bryan-Colle…       206.     85.0     38.2  17.2     157488.    8852.     1458.
## 3 Tyler              270.     62.0     45.8  13.1     141442.    9337.     2905.
## 4 Wichita Fal…       116.     22.2     13.9   3.24    101743.   11320.      910.
## # ℹ 5 more variables: sd_list <dbl>, mean_mi <dbl>, sd_mi <dbl>,
## #   mean_eff_ratio <dbl>, mean_eff_sales_per_mi <dbl>
# Summary per città e anno
summary_city_year <- df %>%
  group_by(city, year) %>%
  summarise(
    mean_sales = mean(sales),
    mean_vol = mean(volume),
    mean_price = mean(median_price),
    mean_eff_ratio = mean(eff_ratio),
    .groups = "drop"
  )
summary_city_year
## # A tibble: 20 × 6
##    city                   year mean_sales mean_vol mean_price mean_eff_ratio
##    <chr>                 <int>      <dbl>    <dbl>      <dbl>          <dbl>
##  1 Beaumont               2010       156.     22.7    133117.         0.0898
##  2 Beaumont               2011       144      21.1    125642.         0.0823
##  3 Beaumont               2012       172.     24.5    126533.         0.102 
##  4 Beaumont               2013       201.     30.3    132400          0.123 
##  5 Beaumont               2014       214.     32.1    132250          0.135 
##  6 Bryan-College Station  2010       168.     28.7    153533.         0.106 
##  7 Bryan-College Station  2011       167.     28.9    151417.         0.103 
##  8 Bryan-College Station  2012       197.     35.4    153567.         0.122 
##  9 Bryan-College Station  2013       238.     45.1    159392.         0.171 
## 10 Bryan-College Station  2014       260.     52.8    169533.         0.236 
## 11 Tyler                  2010       228.     36.3    135175          0.0745
## 12 Tyler                  2011       239.     38.6    136217.         0.0773
## 13 Tyler                  2012       264.     44.0    139250          0.0902
## 14 Tyler                  2013       287.     50.3    146100          0.101 
## 15 Tyler                  2014       332.     59.6    150467.         0.124 
## 16 Wichita Falls          2010       123.     15.0     98942.         0.129 
## 17 Wichita Falls          2011       106.     12.1     98142.         0.108 
## 18 Wichita Falls          2012       112.     13.2    100958.         0.126 
## 19 Wichita Falls          2013       121.     14.9    105000          0.144 
## 20 Wichita Falls          2014       117      14.5    105675          0.133
# Summary per città e mese (pattern stagionali)
summary_city_month <- df %>%
  group_by(city, month) %>%
  summarise(
    mean_sales = mean(sales),
    mean_price = mean(median_price),
    .groups = "drop"
  )
summary_city_month
## # A tibble: 48 × 4
##    city     month mean_sales mean_price
##    <chr>    <int>      <dbl>      <dbl>
##  1 Beaumont     1       122.     127460
##  2 Beaumont     2       135.     126980
##  3 Beaumont     3       171      124460
##  4 Beaumont     4       190.     129820
##  5 Beaumont     5       207.     129320
##  6 Beaumont     6       205      130680
##  7 Beaumont     7       185.     130900
##  8 Beaumont     8       217.     138480
##  9 Beaumont     9       174      127760
## 10 Beaumont    10       189.     130820
## # ℹ 38 more rows

L’analisi condizionata per città evidenzia differenze di livello nei principali indicatori: Tyler e Bryan‑College Station presentano vendite e volumi medi più elevati, Wichita Falls valori inferiori e Beaumont posizioni intermedie.

L’analisi per anno mostra un aumento di vendite, volume e prezzi medi nella maggior parte delle città, segno di un mercato in consolidamento.
L’analisi per mese conferma un pattern stagionale, con mesi primaverili‑estivi più attivi rispetto ai mesi invernali.


8. Visualizzazioni con ggplot2

8.1 Boxplot del prezzo mediano per città

ggplot(df, aes(x = city, y = median_price)) +
  geom_boxplot() +
  labs(x = "Città", y = "Prezzo mediano ($)")

I boxplot di median_price per città mostrano chiaramente differenze nelle mediane e nella dispersione dei prezzi. Alcune città hanno livelli di prezzo tipicamente più elevati, altre risultano più economiche. Questo influisce direttamente sul posizionamento commerciale degli immobili nelle diverse aree.

8.2 Boxplot del volume per città e per anno

# Boxplot per città
ggplot(df, aes(x = city, y = volume)) +
  geom_boxplot() +
  labs(x = "Città", y = "Volume vendite (milioni $)")

# Boxplot per anno
ggplot(df, aes(x = factor(year), y = volume)) +
  geom_boxplot() +
  labs(x = "Anno", y = "Volume vendite (milioni $)")

I boxplot del volume per città evidenziano mercati strutturalmente più grandi (Tyler, Bryan‑College Station) rispetto a quelli più piccoli (Wichita Falls).
A livello di anno, i boxplot mostrano un progressivo aumento dei volumi nel periodo, con un incremento non solo dei valori centrali ma anche della dispersione negli anni più recenti.

8.3 Grafici a barre sovrapposte per vendite mensili per città

# Totali delle vendite per mese e città
df_month_city <- df %>%
  group_by(city, month) %>%
  summarise(total_sales = sum(sales), .groups = "drop")

# Barre sovrapposte (stacked)
ggplot(df_month_city, aes(x = factor(month), y = total_sales, fill = city)) +
  geom_col(position = "stack") +
  labs(x = "Mese", y = "Totale vendite", fill = "Città")

# Barre normalizzate (percentuali) SENZA scales
ggplot(df_month_city, aes(x = factor(month), y = total_sales, fill = city)) +
  geom_col(position = "fill") +
  scale_y_continuous(labels = percent_label) +
  labs(x = "Mese", y = "Quota sul totale del mese", fill = "Città")

# PRO LEVEL: includere anche l'anno con facet
df_my <- df %>%
  group_by(city, year, month) %>%
  summarise(total_sales = sum(sales), .groups = "drop")

ggplot(df_my, aes(x = factor(month), y = total_sales, fill = city)) +
  geom_col(position = "stack") +
  facet_wrap(~ year, nrow = 2) +
  labs(x = "Mese", y = "Totale vendite", fill = "Città")

I grafici a barre mettono in luce la stagionalità delle vendite e la ripartizione dei volumi tra le diverse città nei vari mesi e anni. I mesi centrali dell’anno risultano generalmente più dinamici, con variazioni interessanti nella quota relativa di ciascuna città.

8.4 Line chart per confronti tra città e periodi storici

# Costruzione di un indice temporale continuo
df <- df %>%
  mutate(time = year + (month - 0.5) / 12)

# Line chart mensile del prezzo mediano
ggplot(df, aes(x = time, y = median_price, color = city)) +
  geom_line() +
  labs(x = "Anno", y = "Prezzo mediano ($)", color = "Città")

# Aggregazione annuale per linee più lisce
df_year <- df %>%
  group_by(city, year) %>%
  summarise(mean_price = mean(median_price), .groups = "drop")

ggplot(df_year, aes(x = year, y = mean_price, color = city)) +
  geom_line() +
  geom_point() +
  labs(x = "Anno", y = "Prezzo mediano medio annuale ($)", color = "Città")

Le linee mostrano una tendenza generalmente crescente del prezzo mediano nel periodo 2010–2014, con velocità diverse tra città. Analoghi grafici per le vendite e i volumi confermano il rafforzamento del mercato nel tempo.


9. Conclusioni e raccomandazioni

L’analisi descrittiva del dataset “Real Estate Texas” permette di concludere che:

  • il mercato immobiliare nelle città considerate ha mostrato un rafforzamento nel periodo 2010–2014, con crescita di vendite, volumi e prezzi;
  • esistono differenze territoriali significative: alcune città (Tyler, Bryan‑College Station) sono mercati più grandi e dinamici, altre (Wichita Falls) più contenute ma potenzialmente interessanti per strategie specifiche;
  • la stagionalità è evidente, con picchi di attività nei mesi primaverili‑estivi;
  • i nuovi indicatori di efficacia (sales/listings e sales/months_inventory) mostrano un miglioramento nel tempo, suggerendo un mercato più efficiente e/o strategie di marketing più mirate.

Per Texas Realty Insights, questi risultati suggeriscono di:

  • monitorare regolarmente gli indicatori chiave per città e periodo;
  • adattare le strategie di pricing e promozione alle caratteristiche specifiche di ciascun mercato locale;
  • pianificare le campagne commerciali sfruttando i periodi dell’anno storicamente più favorevoli alle transazioni.