Al fine di poter svolgere l’analisi statistica commissionata da “Texas Realty Insights” sarà prima di tutto necessario caricare i dataset e le librerie necessarie per la corretta implementazione dello studio.

library(moments)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(tidyr)
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
texas_house <- read.csv("realestate_texas.csv", sep = ",")

1) ANALISI DELLE VARIABILI

Una volta caricato il dataset la prima cosa da fare sarà analizzare ed identificare le variabili che lo compongono, capirne la tipologia e, di conseguenza, le analisi e gli studi che possono essere sviluppati sulla base delle singole informazioni. Per prima cosa elenchiamo le variabili presenti:

str(texas_house)
## '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 ...

Siamo in presenza di differenti tipologie di variabili, nello specifico:

Le variabili temporali (year, month) sono cruciali per l’analisi delle tendenze. Possono essere gestite in diversi modi:

Tipi di analisi per ciascuna variabile

2.1) INDICI DI POSIZIONE,VARIABILITA’ E FORMA

Per avere una panoramica completa dei dati è fondamentale analizzare indici di posizione, variabilità e forma. Gli indici di posizione (come la media) identificano la tendenza centrale, gli indici di variabilità (come la deviazione standard) misurano la dispersione dei dati, e gli indici di forma (come asimmetria e curtosi) rivelano la distribuzione e eventuali anomalie. Insieme, permettono di comprendere meglio il comportamento dei dati e prendere decisioni più informate.

Definisco una funzione per il calcolo dei vari indici e la applico alle variabili per cui ha senso fare ciò:

summary_stats <- function(x) {
  c(
    Media = round(mean(x, na.rm = TRUE), 2),
    Mediana = round(median(x, na.rm = TRUE), 2),
    Varianza = round(var(x, na.rm = TRUE), 2),
    Deviazione_Std = round(sd(x, na.rm = TRUE), 2),
    Coeff_Variazione = round((sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)) * 100, 2),
    Asimmetria = round(skewness(x, na.rm = TRUE), 2),
    Curtosi = round(kurtosis(x, na.rm = TRUE), 2)
  )
}
# Seleziono le variabili numeriche 
numerical_vars <- texas_house[, c("sales", "volume", "median_price", "listings", "months_inventory")]
stats <- apply(numerical_vars, 2, summary_stats)
stats
##                    sales volume median_price  listings months_inventory
## Media             192.29  31.01    132665.42   1738.02             9.19
## Mediana           175.50  27.06    134500.00   1618.50             8.95
## Varianza         6344.30 277.27 513572983.09 566568.97             5.31
## Deviazione_Std     79.65  16.65     22662.15    752.71             2.30
## Coeff_Variazione   41.42  53.71        17.08     43.31            25.06
## Asimmetria          0.72   0.88        -0.36      0.65             0.04
## Curtosi             2.69   3.18         2.38      2.21             2.83

I dati cosi ricavati possono già fornire importanti informazioni, nello specifico:

2.2) DISTRIBUZIONI DI FREQUENZA PER CITY, YEAR E MONTH

Per le variabili per le quali non ha senso l’applicazione dei calcoli esplicitata sopra andra definita una distribuzione di frequenza; variabili che ricadono in questa casistica sono city, year e month.

Di seguito la definizione degli intervalli di frequenza per le tre variabili

CITY

# Frequenza assoluta
city_ni <- table(texas_house$city)
n_city <- sum(city_ni)

# Calcoli
city_fi <- city_ni / n_city
city_ni_cum <- cumsum(city_ni)
city_fi_cum <- cumsum(city_fi)

# Creazione tabella con cbind
freq_table_city <- cbind(
  Frequenza_Assoluta = as.vector(city_ni),
  Frequenza_Relativa = round(as.vector(city_fi), 4),
  Frequenza_Cumulata = as.vector(city_ni_cum),
  Freq_Relativa_Cumulata= round(as.vector(city_fi_cum), 4)
)

rownames(freq_table_city) <- names(city_ni)

print(freq_table_city)
##                       Frequenza_Assoluta Frequenza_Relativa Frequenza_Cumulata
## Beaumont                              60               0.25                 60
## Bryan-College Station                 60               0.25                120
## Tyler                                 60               0.25                180
## Wichita Falls                         60               0.25                240
##                       Freq_Relativa_Cumulata
## Beaumont                                0.25
## Bryan-College Station                   0.50
## Tyler                                   0.75
## Wichita Falls                           1.00

YEAR

year_ni <- table(texas_house$year)
n_year <- sum(year_ni)
year_fi <- year_ni / n_year
year_ni_cum <- cumsum(year_ni)
year_fi_cum <- cumsum(year_fi)

freq_table_year <- cbind(
  Frequenza_Assoluta = as.vector(year_ni),
  Frequenza_Relativa = round(as.vector(year_fi), 4),
  Frequenza_Cumulata = as.vector(year_ni_cum),
  Frequenza_Relativa_Cumulata = round(as.vector(year_fi_cum), 4)
)

rownames(freq_table_year) <- names(year_ni)

print(freq_table_year)
##      Frequenza_Assoluta Frequenza_Relativa Frequenza_Cumulata
## 2010                 48                0.2                 48
## 2011                 48                0.2                 96
## 2012                 48                0.2                144
## 2013                 48                0.2                192
## 2014                 48                0.2                240
##      Frequenza_Relativa_Cumulata
## 2010                         0.2
## 2011                         0.4
## 2012                         0.6
## 2013                         0.8
## 2014                         1.0

MONTH

month_ni <- table(texas_house$month)
n_month <- sum(month_ni)
month_fi <- month_ni / n_month
month_ni_cum <- cumsum(month_ni)
month_fi_cum <- cumsum(month_fi)

freq_table_month<- cbind(
  Frequenza_Assoluta = as.vector(month_ni),
  Frequenza_Relativa = round(as.vector(month_fi), 4),
  Frequenza_Cumulata = as.vector(month_ni_cum),
  Frequenza_Relativa_Cumulata = round(as.vector(month_fi_cum), 4)
)

rownames(freq_table_month) <- names(month_ni)

print(freq_table_month)
##    Frequenza_Assoluta Frequenza_Relativa Frequenza_Cumulata
## 1                  20             0.0833                 20
## 2                  20             0.0833                 40
## 3                  20             0.0833                 60
## 4                  20             0.0833                 80
## 5                  20             0.0833                100
## 6                  20             0.0833                120
## 7                  20             0.0833                140
## 8                  20             0.0833                160
## 9                  20             0.0833                180
## 10                 20             0.0833                200
## 11                 20             0.0833                220
## 12                 20             0.0833                240
##    Frequenza_Relativa_Cumulata
## 1                       0.0833
## 2                       0.1667
## 3                       0.2500
## 4                       0.3333
## 5                       0.4167
## 6                       0.5000
## 7                       0.5833
## 8                       0.6667
## 9                       0.7500
## 10                      0.8333
## 11                      0.9167
## 12                      1.0000

Osservando i risultati possiamo affermare che, per tutte le 3 distribuzioni di frequenza calcolate, i dati sono distribuiti uniformemente e in modo bilanciato all’interno delle variabili analizzate.

3) IDENTIFICAZIONE DELLE VARIABILI CON MAGGIOR VARIABILITA’ E ASIMMETRIA

Variabilità La variabile con la maggiore variabilità è Volume, che presenta un coefficiente di variazione pari a 53.71. In questo contesto di studio è fondamentale scegliere gli strumenti giusti per analizzare la variabilità e, in questo caso, il coefficiente di variazione si dimostra particolarmente adatto. La sua caratteristica principale è che non è influenzato dall’ordine di grandezza della variabile, il che consente di fare confronti tra variabili con scale di misura e ordini di grandezza molto diversi. Ad esempio, è utile per confrontare variabili come median_price e months_inventory, che appartengono a scale completamente differenti. Il coefficiente di variazione è dato dal rapporto tra deviazione standard e media di una variabile; da qui si può capire il perchè non risulti influenzato dalla scala di misura della variabile.

Asimmetria La variabile maggiormente asimmetrica è volume con un valore di 0.88 ottenuto tramite il calcolo dell’indice di asimmetria, sintetizzabile tramite il comando skewness in RStudio. Un valore di questo tipo suggerisce che ci siano alcuni valori estremamente alti che influenzano la distribuzione. Sebbene l’asimmetria sia inferiore a 1, il che implica che non è una distorsione eccessiva, la distribuzione non è perfettamente simmetrica.

4) CREAZIONE DI CLASSI PER UNA VARIABILE QUANTITATIVA

Creazione classi e distribuzione di frequenze

Per la suddivisione in classi è stata scelta la variabile sales. Di seguito il codice per la suddivisione e il calcolo della relativa distribuzione di frequenza:

#Calcolo max e min per capire meglio l'ordine di grandezza della varibile
min(texas_house$sales)
## [1] 79
max(texas_house$sales)
## [1] 423
#Suddivisione in classi tramite il comando cut; è stato inserito 439 così da riuscire a considerare anche il max pari a 423
sales_class<-cut(texas_house$sales,seq(79,439,30))

#Costruisco una distribuzinoe di frequenza
salesclass_ni <- table(sales_class)
n_salesclass <- sum(salesclass_ni)
salesclass_fi <- salesclass_ni / n_salesclass
salesclass_ni_cum <- cumsum(salesclass_ni)
salesclass_fi_cum <- cumsum(salesclass_fi)

freq_table_salesclass<- cbind(
  Frequenza_Assoluta = as.vector(salesclass_ni),
  Frequenza_Relativa = round(as.vector(salesclass_fi), 4),
  Frequenza_Cumulata = as.vector(salesclass_ni_cum),
  Frequenza_Relativa_Cumulata = round(as.vector(salesclass_fi_cum), 4)
)

rownames(freq_table_salesclass) <- names(salesclass_ni)

print(freq_table_salesclass)
##           Frequenza_Assoluta Frequenza_Relativa Frequenza_Cumulata
## (79,109]                  31             0.1303                 31
## (109,139]                 42             0.1765                 73
## (139,169]                 40             0.1681                113
## (169,199]                 32             0.1345                145
## (199,229]                 21             0.0882                166
## (229,259]                 19             0.0798                185
## (259,289]                 20             0.0840                205
## (289,319]                 13             0.0546                218
## (319,349]                  8             0.0336                226
## (349,379]                  8             0.0336                234
## (379,409]                  3             0.0126                237
## (409,439]                  1             0.0042                238
##           Frequenza_Relativa_Cumulata
## (79,109]                       0.1303
## (109,139]                      0.3067
## (139,169]                      0.4748
## (169,199]                      0.6092
## (199,229]                      0.6975
## (229,259]                      0.7773
## (259,289]                      0.8613
## (289,319]                      0.9160
## (319,349]                      0.9496
## (349,379]                      0.9832
## (379,409]                      0.9958
## (409,439]                      1.0000
freq_table_salesclass_df <- as.data.frame(salesclass_ni)

colnames(freq_table_salesclass_df) <- c("Classi", "Frequenza_Assoluta")

# Creo un grafico a barre con ggplot
ggplot(freq_table_salesclass_df, aes(x = Classi, y = Frequenza_Assoluta)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "black") +
  theme_minimal() +
  labs(title = "Distribuzione di Frequenza delle Vendite", x = "Classi di Sales", y = "Frequenza Assoluta") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Calcolo Indice di GINI

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(gini_norm)
}
gini.index(sales_class)
## [1] 0.9610227

Un indice di Gini pari a 0.96 è molto vicino al massimo possibile (1), il che significa che c’è una grande disuguaglianza tra le classi di vendite. Nello specifico, questo indice di Gini ci suggerisce che una parte significativa delle vendite è concentrata in un numero ristretto di classi (109-139 e 139-169), mentre molte altre classi hanno poche o nessuna vendita.

5) CALCOLO DELLE PROBABILITA’

# Calcolo del numero totale di righe
totale_righe <- nrow(texas_house)

# 1. Probabilità che la riga riporti la città "Beaumont"
prob_beaumont <- sum(texas_house$city == "Beaumont") / totale_righe

# 2. Probabilità che la riga riporti il mese di Luglio (indipendentemente dall'anno)
prob_luglio <- sum(texas_house$month == "7") / totale_righe

# 3. Probabilità che la riga riporti Dicembre 2012
prob_dicembre_2012 <- sum(texas_house$month == "12" & texas_house$year == 2012) / totale_righe

print(paste("Probabilità che la riga riporti Beaumont:", prob_beaumont))
## [1] "Probabilità che la riga riporti Beaumont: 0.25"
print(paste("Probabilità che la riga riporti il mese di Luglio:", prob_luglio))
## [1] "Probabilità che la riga riporti il mese di Luglio: 0.0833333333333333"
print(paste("Probabilità che la riga riporti Dicembre 2012:", prob_dicembre_2012))
## [1] "Probabilità che la riga riporti Dicembre 2012: 0.0166666666666667"

6) CREAZIONE DI NUOVE VARIABILI

Prezzo medio immobili

texas_house$prezzo_medio <- (texas_house$volume * 1e6) / texas_house$sales

In questo caso si moltiplica per ‘1e6’ dal momento che la variaile “Volume” è espressa in milioni di euro.

Efficacia degli annunci di vendita

In questo caso potrebbe essere utile rapportare il numero di vendite e annunci, più il numero si avvicinerà ad 1 e maggiore sarà l’efficacia delle politiche di marketing / annunci

texas_house$efficacia_annunci <- texas_house$sales / texas_house$listings

Volendo esplorare maggiormente i dati calcolati possiamo fare cosi:

summary(texas_house[, c("prezzo_medio", "efficacia_annunci")])
##   prezzo_medio    efficacia_annunci
##  Min.   : 97010   Min.   :0.05014  
##  1st Qu.:132939   1st Qu.:0.08980  
##  Median :156588   Median :0.10963  
##  Mean   :154320   Mean   :0.11874  
##  3rd Qu.:173915   3rd Qu.:0.13492  
##  Max.   :213234   Max.   :0.38713

7) ANALISI CONDIZIONATA

Utiliziamo la libreria dplyr per analisi condizionate

Calcolo

summary_stats <- texas_house %>%
  mutate(quarter = paste(year, ceiling(month / 3), sep = "-Q")) %>%
  group_by(city, quarter) %>%
  summarise(
    avg_sales = mean(sales, na.rm = TRUE),
    sd_sales = sd(sales, na.rm = TRUE),
    avg_volume = mean(volume, na.rm = TRUE),
    sd_volume = sd(volume, na.rm = TRUE),
    avg_median_price = mean(median_price, na.rm = TRUE),
    sd_median_price = sd(median_price, na.rm = TRUE),
    avg_listings = mean(listings, na.rm = TRUE),
    sd_listings = sd(listings, na.rm = TRUE),
    avg_months_inventory = mean(months_inventory, na.rm = TRUE),
    sd_months_inventory = sd(months_inventory, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(city, quarter)  # Ordinamento per città e trimestre

# Visualizzazione dei risultati in un grafico con trimestre (anno-Q) sull'asse X
ggplot(summary_stats, aes(x = quarter, y = avg_sales, color = city, group = city)) +
  geom_line() +
  labs(title = "Vendite medie per città nel tempo (Anno-Trimestre)", x = "Periodo (Anno-Trimestre)", y = "Vendite Medie") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

BOXPLOT

#Boxplot
ggplot(texas_house, aes(x = city, y = median_price, fill = city)) +
  geom_boxplot() +
  labs(
    title = "Distribuzione del Prezzo Mediano di Vendita per Città",
    x = "Città",
    y = "Prezzo Mediano di Vendita (USD)"
  ) +
  theme_minimal() +
  theme(legend.position = "none")

L’analisi della distribuzione del prezzo mediano di vendita mostra che Bryan-College Station ha i prezzi più elevati, con una variabilità contenuta ma alcuni outlier al rialzo. Tyler ha una distribuzione più ampia, segnalando una maggiore fluttuazione dei prezzi. Beaumont, con prezzi mediamente più bassi, ha una distribuzione stabile, pur con alcuni outlier occasionali. Wichita Falls presenta i prezzi più bassi e una distribuzione compatta, riflettendo un mercato più accessibile. In generale, le città con prezzi più alti mostrano una maggiore pressione sulla domanda, mentre quelle con prezzi più bassi sono più stabili e meno competitive.

LINE CHART

#Linechart
texas_house$date <- as.Date(paste(texas_house$year, texas_house$month, "01", sep = "-"))
ggplot(texas_house, aes(x = date, y = sales, color = city)) +
  geom_line() +
  labs(
    title = "Andamento Storico delle Vendite per Città",
    x = "Data",
    y = "Vendite"
  ) +
  theme_minimal() +
  scale_x_date(date_breaks = "3 months", date_labels = "%b %Y") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Dal grafico emerge chiaramente che la città di Tyler registra, in media, vendite più elevate rispetto alle altre città analizzate. Al contrario, Wichita Falls si distingue per avere i valori medi più bassi. Le linee di tendenza suggeriscono la presenza di una stagionalità generale,che appare particolarmente marcata a Tyler e Bryan-College Station, dove i picchi e i cali sono più evidenti. Al contrario, a Wichita Falls, la stagionalità è meno pronunciata, con un andamento più stabile nel tempo.

GRAFICO A BARRE

 #Barplot
ggplot(texas_house, aes(x = factor(month), y = sales, fill = city)) +
  geom_bar(stat = "summary", fun = "sum", position = "dodge") +
  labs(
    title = "Totale Vendite per Mese e Città",
    x = "Mese",
    y = "Numero Totale di Vendite"
  ) +
  theme_minimal()

# Prepara i dati aggregati: somma delle vendite per città e mese
df_sales <- texas_house %>%
  group_by(city, month) %>%
  summarise(total_sales = sum(sales), .groups = 'drop')

# Grafico a barre sovrapposte
ggplot(df_sales, aes(x = factor(month), y = total_sales, fill = city)) +
  geom_col(position = "stack") +
  labs(title = "Confronto delle vendite totali per città e mese",
       x = "Mese", y = "Totale vendite",
       fill = "Città") +
  theme_minimal()

# Grafico a barre normalizzato
df_sales_normalized <- df_sales %>%
  group_by(month) %>%
  mutate(total_sales_month = sum(total_sales),
         normalized_sales = total_sales / total_sales_month)

ggplot(df_sales_normalized, aes(x = factor(month), y = normalized_sales, fill = city)) +
  geom_col(position = "stack") +
  labs(title = "Vendite normalizzate per città e mese",
       x = "Mese", y = "Vendite normalizzate",
       fill = "Città") +
  theme_minimal()

Dal grafico a barre emergono due principali osservazioni:

df_sales <- texas_house %>%
  group_by(city, year, month) %>%
  summarise(total_sales = sum(sales), .groups = 'drop')

df_sales$month <- factor(df_sales$month, levels = 1:12, labels = month.name)

ggplot(df_sales, aes(x = month, y = total_sales, fill = city)) +
  geom_col(position = "stack") +
  facet_wrap(~ year, scales = "free_y", ncol = 2) +  # Modifica il numero di colonne nel faceting
  labs(title = "Confronto delle vendite totali per città e mese",
       x = "Mese", y = "Totale vendite",
       fill = "Città") +
  theme_minimal() +
  theme(
    axis.text.x = element_blank(),
    strip.text = element_text(size = 12),  
    panel.spacing = unit(1, "lines"), 
    axis.title.x = element_text(size = 12), 
    axis.title.y = element_text(size = 12) 
  )

È evidente che le vendite mostrano una crescita costante anno dopo anno (ad esempio, confrontando il 2010 con il 2014). Anche in questo caso, Tyler continua a detenere una quota significativa del totale delle vendite, mentre Wichita Falls rimane la città con il volume di vendite più basso.

df_time <- texas_house %>%
  mutate(date = make_date(year, month, 1)) %>%
  group_by(city, date) %>%
  summarise(median_price = mean(median_price, na.rm = TRUE), .groups = 'drop')

# Line chart del prezzo mediano nel tempo per città
ggplot(df_time, aes(x = date, y = median_price, color = city)) +
  geom_line(linewidth = 1) +
  labs(title = "Andamento del prezzo mediano di vendita nel tempo",
       x = "Data", y = "Prezzo mediano (USD)",
       color = "Città") +
  theme_minimal() +
  theme(
    legend.position = "bottom",
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(size = 14, face = "bold")
  )

Il grafico mostra una crescita generale del prezzo mediano di vendita dal 2010 al 2014 in tutte le città analizzate, indicando un mercato immobiliare in espansione. Si notano differenze tra le città: Tyler presenta i prezzi più alti e una crescita costante, mentre città come Bryan-College Station seguono con fluttuazioni moderate. Wichita Falls registra invece i prezzi più bassi e una forte variabilità, segnale di un mercato più instabile o sensibile a fattori locali.

CONCLUSIONI

L’analisi condotta sui dati immobiliari tra il 2010 e il 2014 mette in luce alcune tendenze significative. In primo luogo, emerge una crescita costante del mercato: le vendite aumentano progressivamente anno dopo anno, accompagnate da un incremento generale dei prezzi mediani, più marcato in alcune città.

La stagionalità è un tratto distintivo delle dinamiche di vendita, con picchi ricorrenti nei mesi estivi. Questo fenomeno è particolarmente evidente a Tyler e Bryan-College Station, mentre Wichita Falls mostra un andamento più stabile e meno influenzato dalle stagioni.

Le analisi statistiche descrittive rivelano che sales e listing sono le variabili più volatili e sensibili ai cambiamenti, mentre median_price e months_inventory risultano più stabili. L’indice di asimmetria più alto si riscontra sulla variabile volume, indicando la presenza di valori estremi che influenzano la distribuzione.

Il coefficiente di variazione conferma che la variabilità non dipende dall’ordine di grandezza delle variabili, permettendo confronti coerenti tra misure molto diverse tra loro. In questo contesto, volume è la variabile con la maggiore dispersione relativa.

Un ulteriore aspetto critico è la disuguaglianza nella distribuzione delle vendite: un indice di Gini pari a 0.96 suggerisce una forte concentrazione in poche fasce di prezzo, con implicazioni rilevanti in termini di accessibilità e segmentazione del mercato.

Infine, il confronto tra città evidenzia Tyler come leader per volumi di vendita, Bryan-College Station come città con i prezzi più elevati e Wichita Falls come area con mercato più accessibile ma anche più contenuto. In sintesi, il mercato mostra dinamiche in espansione, differenziate per area geografica, ma nel complesso caratterizzate da una domanda crescente e da segnali di consolidamento.