Analisi Esplorativa del Mercato Immobiliare del Texas

Questo documento contiene il progetto finale di Luigi Carlucci del corso “Statistica Descrittiva” per il Master in Data Science di ProfessionAI.

Viene caricata la libreria dplyr e il dataset realestate_texas.csv.

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
texas_data = read.csv("realestate_texas.csv")
attach(texas_data)
head(texas_data)
##       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

1. Analisi delle variabili

Nel dataset sono presenti le seguenti variabili:

  • city: variabile qualitativa su scala nominale; contiene il nome della città di riferimento; può essere usata per effettuare analisi di confronto tra le diverse città

  • year: variabile quantitativa discreta; rappresenta una dimensione temporale, nello specifico l’anno di riferimento; può essere usata per effettuare analisi di andamento nel tempo

  • month: variabile quantitativa discreta; rappresenta una dimensione temporale, nello specifico il mese di riferimento per ogni anno; può essere usata per effettuare analisi di confronto tra i diversi mesi

  • sales: variabile quantitativa discreta; riporta il numero totale di vendite di immobili per ogni città, anno e mese; è possibile effettuare analisi con indici di posizione, variabilità e forma, suddividere la variabile in classi e creare una distribuzione di frequenze, effettuare analisi condizionate per altre variabili

  • volume: variabile quantitativa continua; riporta il valore totale delle vendite di immobili (in milioni di dollari) per ogni città, anno e mese; è possibile effettuare analisi con indici di posizione, variabilità e forma, suddividere la variabile in classi e creare una distribuzione di frequenze, effettuare analisi condizionate per altre variabili

  • median_price: variabile quantitativa continua; riporta il prezzo mediano di vendita di immobili (in dollari) per ogni città, anno e mese; è possibile effettuare analisi con indici di posizione, variabilità e forma, suddividere la variabile in classi e creare una distribuzione di frequenze, effettuare analisi condizionate per altre variabili

  • listings: variabile quantitativa discreta; riporta il numero totale di annunci attivi per ogni città, anno e mese; è possibile effettuare analisi con indici di posizione, variabilità e forma, suddividere la variabile in classi e creare una distribuzione di frequenze, effettuare analisi condizionate per altre variabili

  • months_inventory: variabile quantitativa continua; riporta il tempo necessario (in mesi) per vendere tutte le inserzioni correnti per ogni città, anno e mese; è possibile effettuare analisi con indici di posizione, variabilità e forma, suddividere la variabile in classi e creare una distribuzione di frequenze, effettuare analisi condizionate per altre variabili

2. Indici di posizione, variabilità e forma

Per le variabili quantitative (escludendo quelle che rappresentano una dimensione temporale) si possono calcolare:

  • Indici di posizione: minimo, massimo, mediana, primo e terzo quartile, media

  • Indici di variabilità: range, range interquartile, varianza, deviazione standard, coefficiente di variazione

  • Indici di forma: asimmetria e curtosi

# Caricamento della libreria per skewness e kurtosis
library(moments)
# Creazione di una funzione per il calcolo degli indici
calcola_indici = function(x) {
  c(
    Min = min(x),
    Max = max(x),
    Mediana = median(x),
    Primo_Quartile = unname(quantile(texas_data$sales, 0.25)),
    Terzo_Quartile = unname(quantile(texas_data$sales, 0.75)),
    Media = mean(x),
    Range = max(x) - min(x),
    Range_Interquartile = IQR(x),
    Varianza = var(x),
    Deviazione_Standard = sd(x),
    Coeff_Variazione = sd(x)/mean(x) * 100,
    Asimmetria = skewness(x),
    Curtosi = kurtosis(x)
  )
}

Come esempio, vengono prima calcolati gli indici per la variabile sales:

# Applicazione della funzione e approssimazione dei risultati a due cifre decimali
indici_sales = round(calcola_indici(sales),2)
# Trasformazione in data frame per una migliore visualizzazione
indici_sales_df = as.data.frame(indici_sales)
indici_sales_df
##                     indici_sales
## Min                        79.00
## Max                       423.00
## Mediana                   175.50
## Primo_Quartile            127.00
## Terzo_Quartile            247.00
## Media                     192.29
## Range                     344.00
## Range_Interquartile       120.00
## Varianza                 6344.30
## Deviazione_Standard        79.65
## Coeff_Variazione           41.42
## Asimmetria                  0.72
## Curtosi                     2.69

In seguito, vengono calcolati gli indici per tutte le variabili opportune:

# Selezione delle variabili dal dataset intero
variables_subs = texas_data %>% select(sales, volume, median_price, listings, months_inventory)
# Applicazione della funzione a tutte le variabili selezionate e approssimazione
indici_vars = round(sapply(variables_subs, calcola_indici),2)
# Trasformazione in data frame e visualizzazione
indici_vars_df = as.data.frame(indici_vars)
indici_vars_df
##                       sales volume median_price  listings months_inventory
## Min                   79.00   8.17     73800.00    743.00             3.40
## Max                  423.00  83.55    180000.00   3296.00            14.90
## Mediana              175.50  27.06    134500.00   1618.50             8.95
## Primo_Quartile       127.00 127.00       127.00    127.00           127.00
## Terzo_Quartile       247.00 247.00       247.00    247.00           247.00
## Media                192.29  31.01    132665.42   1738.02             9.19
## Range                344.00  75.38    106200.00   2553.00            11.50
## Range_Interquartile  120.00  23.23     32750.00   1029.50             3.15
## Varianza            6344.30 277.27 513572983.09 566568.97             5.31
## Deviazione_Standard   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
write.csv(indici_vars_df, file='indici_stats.csv')

Da una prima osservazione si notano molte differenze tra le diverse variabili in termini di distribuzione, che verranno analizzate e discusse più nel dettaglio nelle sezioni seguenti.

Per le variabili city, year e month viene creata una distribuzione di frequenza:

N=dim(texas_data)[1]

# Distribuzione di frequenza della variabile "city"
city_freq_ass = table(city)
city_freq_rel = table(city)/N
city_freq = as.data.frame(cbind(city = rownames(city_freq_ass), city_freq_ass, city_freq_rel), row.names = FALSE)
print(city_freq)
##                    city city_freq_ass city_freq_rel
## 1              Beaumont            60          0.25
## 2 Bryan-College Station            60          0.25
## 3                 Tyler            60          0.25
## 4         Wichita Falls            60          0.25
# Distribuzione di frequenza della variabile "year"
year_freq_ass = table(year)
year_freq_rel = table(year)/N
year_freq = as.data.frame(cbind(year = rownames(year_freq_ass), year_freq_ass, year_freq_rel), row.names = FALSE)
print(year_freq)
##   year year_freq_ass year_freq_rel
## 1 2010            48           0.2
## 2 2011            48           0.2
## 3 2012            48           0.2
## 4 2013            48           0.2
## 5 2014            48           0.2
# Distribuzione di frequenza della variabile "month"
month_freq_ass = table(month)
month_freq_rel = table(month)/N
month_freq = as.data.frame(cbind(month = rownames(month_freq_ass), month_freq_ass, month_freq_rel), row.names = FALSE)
print(month_freq)
##    month month_freq_ass     month_freq_rel
## 1      1             20 0.0833333333333333
## 2      2             20 0.0833333333333333
## 3      3             20 0.0833333333333333
## 4      4             20 0.0833333333333333
## 5      5             20 0.0833333333333333
## 6      6             20 0.0833333333333333
## 7      7             20 0.0833333333333333
## 8      8             20 0.0833333333333333
## 9      9             20 0.0833333333333333
## 10    10             20 0.0833333333333333
## 11    11             20 0.0833333333333333
## 12    12             20 0.0833333333333333

Si nota chiramente come tutte le tre variabili hanno le stesse frequenze per ogni valore. Nello specifico ogni città ha lo stesso numero (n=60) di dati, distribuiti ugualamente per ogni anno e mese.

3. Identificazione delle variabili con maggiore variabilità e asimmetria

Viene ora determinato:

  • qual è la variabile con la maggiore variabilità, valutando il coefficiente di variazione (CV)

  • qual è variabile con la distribuzione più asimmetrica, valutando il valore dell’asimmetria

# Identificazione della variabile con la maggiore variabilità (CV più alto)
var_max_cv = colnames(indici_vars_df)[which.max(indici_vars_df["Coeff_Variazione",])]
cv_max_value = max(indici_vars_df["Coeff_Variazione",])
var_max_cv
## [1] "volume"
cv_max_value
## [1] 53.71

In questo caso, la variabile volume presenta il valore più alto (53.71) di coefficiente di variazione (CV), che misura la dispersione relativa rispetto alla media, ed è quindi la variabile con la maggiore variabiltà tra tutte quelle presenti nel dataset.

# Identificazione della variabile più asimmerica (asimmetria più alta in valore assoluto)
var_max_skewness = colnames(indici_vars_df)[which.max(abs(indici_vars_df["Asimmetria",]))]
skew_max_value = max(abs(indici_vars_df["Asimmetria",]))
var_max_skewness
## [1] "volume"
skew_max_value
## [1] 0.88

In questo caso, la variabile volume presenta il valore più alto (0.88) di asimmetria in valore assoluto ed è quindi la variabile con la distribuzione più asimmetrica tra tutte quelle presenti nel dataset.

# Identificazione del valore orginale di asimmetria per la variabile "volume"
indici_vars_df["Asimmetria",]$volume
## [1] 0.88

In particolare, poichè il valore dell’indice di asimmetria è positivo, la variabile volume ha una distribuzione asimmetrica positiva, cioè con una coda verso destra, come si può osservara anche da un grafico della densità.

library(ggplot2)
ggplot(texas_data)+
  geom_density(aes(x=volume), col="black", fill="steelblue") +
  labs(title = "Densità del volume delle vendite",
       x = "Volume delle vendite (milioni di $)", y = "Densità")

4. Creazione di classi per una variabile quantitativa

La variabile quantitativa median_price viene suddivisa in 10 classi.

# Suddivisione della variabile median_price in classi
median_price_cl = cut(texas_data$median_price, breaks = 10, dig.lab=10)

Vengono create delle distribuzione di frequenze, che includono la frequenza assoluta, la frequenza relativa, la frequenza assoluta cumulata e la frequenza relativa cumulata.

# Creazione delle distribuzioni di frequenze
N=dim(texas_data)[1]

ni = table(median_price_cl)
fi = ni/N
Ni = cumsum(ni)
Fi = Ni/N

distr_freq_median_price_cl = as.data.frame(cbind(ni,fi,Ni,Fi))
distr_freq_median_price_cl
##                   ni          fi  Ni          Fi
## (73693.8,84420]    2 0.008333333   2 0.008333333
## (84420,95040]     16 0.066666667  18 0.075000000
## (95040,105660]    23 0.095833333  41 0.170833333
## (105660,116280]   17 0.070833333  58 0.241666667
## (116280,126900]   25 0.104166667  83 0.345833333
## (126900,137520]   48 0.200000000 131 0.545833333
## (137520,148140]   38 0.158333333 169 0.704166667
## (148140,158760]   46 0.191666667 215 0.895833333
## (158760,169380]   16 0.066666667 231 0.962500000
## (169380,180106.2]  9 0.037500000 240 1.000000000

Viene utilizzata la funzione barplot per rappresentare i dati della frequenza assoluta della variabile median_price suddivisa in classi.

barplot(distr_freq_median_price_cl$ni,
        xlab = "Classi di prezzo ($)",
        ylab = "Frequenza assolute",
        names.arg = rownames(distr_freq_median_price_cl),
        col="steelblue")

Viene utilizzata la libreria ggplot2 per creare i gafici a barre relativi ai quattro tipi di distribuzione di frequenze (assoluta, relativa, assoluta cumulata e relativa cumulata).

ggplot(distr_freq_median_price_cl) +
  geom_bar(aes(x=row.names(distr_freq_median_price_cl), y=ni), stat = "identity", fill = "steelblue") +
  scale_x_discrete(limits=row.names(distr_freq_median_price_cl)) +
  labs(title = "Distribuzione del prezzo mediano",
       x = "Classi di prezzo ($)",
       y = "Frequenza assoluta") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(distr_freq_median_price_cl) +
  geom_bar(aes(x=row.names(distr_freq_median_price_cl), y=fi), stat = "identity", fill = "steelblue") +
  scale_x_discrete(limits=row.names(distr_freq_median_price_cl)) +
  labs(title = "Distribuzione del prezzo mediano",
       x = "Classi di prezzo ($)",
       y = "Frequenza relativa") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(distr_freq_median_price_cl) +
  geom_bar(aes(x=row.names(distr_freq_median_price_cl), y=Ni), stat = "identity", fill = "steelblue") +
  scale_x_discrete(limits=row.names(distr_freq_median_price_cl)) +
  labs(title = "Distribuzione del prezzo mediano",
       x = "Classi di prezzo ($)",
       y = "Frequenza assoluta cumulata") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplot(distr_freq_median_price_cl) +
  geom_bar(aes(x=row.names(distr_freq_median_price_cl), y=Fi), stat = "identity", fill = "steelblue") +
  scale_x_discrete(limits=row.names(distr_freq_median_price_cl)) +
  labs(title = "Distribuzione del prezzo mediano",
       x = "Classi di prezzo ($)",
       y = "Frequenza relativa cumulata") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Infine, viene calcolato l’indice di eterogeneità Gini:

# Creazione di una funzione per calcolare l'indice di eterogeneità 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)
}
#Applicazione della funzione alla variabile "median_price", precedentemente suddivisa in classi
gini.index(median_price_cl)
## [1] 0.958642

L’indice di eterogeneità Gini rappresenta una misura della diseguaglianza (o eterogeneità) di una distribuzione e assume un valore compreso tra 0 e 1. Valori bassi (vicini allo 0) indicano che la distribuzione è abbastanza omogenea, mentre valori alti (vicino ad 1) indicano una distribuione più eterogenea. Nel caso preso in esempio del prezzo medio di vendita degli immobili, l’indice calcolato di 0.95 indica una elevata eterogeneità nei prezzi.

5. Calcolo della probabilità

In questa sezione, viene utilizzata la probabilità classica, cioè il rapporto tra il numero di casi favorevoli e il numero totale di osservazioni, per calcolare la probabilità che, presa una riga a caso del dataset originario, si verfichino le seguenti condizioni:

  • la riga riporti la città “Beaumont”

  • la riga riporti il mese di Luglio

  • la riga riporti il mese di dicembre 2012

# Numero totale di righe nel dataset
total_row = nrow(texas_data)

# Probabilità che la città sia "Beaumont"
p_beaumont = sum(texas_data$city == "Beaumont") / total_row
cat("P(Beaumont) =", round(p_beaumont,2), "\n")
## P(Beaumont) = 0.25
# Probabilità che il mese sia Luglio
p_luglio = sum(texas_data$month == 7) / total_row
cat("P(Luglio) =", round(p_luglio,2), "\n")
## P(Luglio) = 0.08
# Probabilità che sia Dicembre 2012
p_dicembre_2012 = sum(texas_data$year == 2012 & texas_data$month == 12) / total_row
cat("P(Dicembre 2012) =", round(p_dicembre_2012,2), "\n")
## P(Dicembre 2012) = 0.02

Dal calcolo delle probabilità è risultato che la probabilità che una riga presa a caso:

  • riporti la città “Beaumont” è di 0.25 (25%)

  • riporti il mese di Luglio è di 0.08 (8%)

  • porti il mese di dicembre 2012 è di 0.02 (2%)

6. Creazione di nuove variabili

Vengono aggiunte due nuove colonne al dataset, create partendo da altre variabili già presenti, che indicano:

  • il prezzo medio degli immobili

  • l’efficacia degli annunci di vendita

Il prezzo medio degli immobili venduti viene calcolato come il valore totale delle vendite (variabile volume) diviso per il numero totale di vendite (variabile sales). La variabile volume viene moltiplicata per \(10^6\) per ottenere il valore in dollari.

# Creazione della colonna "mean_price"
texas_data$mean_price = round(((texas_data$volume * 10^6) / texas_data$sales),0)
head(texas_data)
##       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
##   mean_price
## 1     170627
## 2     163796
## 3     157698
## 4     134095
## 5     142738
## 6     144016

I valori della variabile calcolata mean_price differiscono dalla variabile median_price poichè la media è influenzata dai valori estremi. Una maggiore differenza tra mean_price e median_price indica una maggiore asimmetria nei prezzi.

Una possibile misura dell’efficacia degli annunci di vendita è data dal rapporto tra le vendite effettive per ogni mese (variabile sales) e le vendite attese determinato dalle variabili listings (numero di annunci attivi) e months_inventory (numero di mesi necessari per vendere tutte le inserzioni correnti). In particolare, il rapporto tra le variabili listings e months_inventory determina il numero atteso di vendite in un singolo mese.

# Creazione della colonna "listing_effectiveness"
texas_data$listing_effectiveness = texas_data$sales / (texas_data$listings / texas_data$months_inventory)
head(texas_data)
##       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
##   mean_price listing_effectiveness
## 1     170627             0.5143509
## 2     163796             0.6809584
## 3     157698             1.1422143
## 4     134095             1.2412178
## 5     142738             1.2432524
## 6     144016             1.1635607

In questo caso, se la variabile listing_effectiveness è > 1 le vendite nel mese corrente sono andate più velocemente di quanto atteso, se è < 1 le vendite sono andate più lentamente, mentre se sono vicine ad 1 le vendite sono andate esattamente al ritmo atteso.

7. Analisi condizionata

In questa sezione vengono eseguite delle analisi statistiche condizionate, attraverso la creazione di summary statistici in base a tre variabili:

  • città (city)

  • anno (year)

  • mese (month)

Nello specifico, vengono calcolate la media e la deviazione standard per le principali variabili quantitative e mostrate delle rappresentazioni grafiche dei risultati.

# Summary statistico per città
summary_city = texas_data %>%
  group_by(city) %>%
  summarise(
    sales_mean = mean(sales),
    sales_sd = sd(sales),
    volume_mean = mean(volume),
    volume_sd = sd(volume),
    median_price_mean = mean(median_price),
    median_price_sd = sd(median_price),
    listings_mean = mean(listings),
    listings_sd = sd(listings)
  ) %>% 
  mutate(across(2:9, round, 2))
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `across(2:9, round, 2)`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))
summary_city
## # A tibble: 4 × 9
##   city               sales_mean sales_sd volume_mean volume_sd median_price_mean
##   <chr>                   <dbl>    <dbl>       <dbl>     <dbl>             <dbl>
## 1 Beaumont                 177.     41.5        26.1      6.97           129988.
## 2 Bryan-College Sta…       206.     85.0        38.2     17.2            157488.
## 3 Tyler                    270.     62.0        45.8     13.1            141442.
## 4 Wichita Falls            116.     22.2        13.9      3.24           101743.
## # ℹ 3 more variables: median_price_sd <dbl>, listings_mean <dbl>,
## #   listings_sd <dbl>

Nei seguenti garfici a barre vengono mostrati i valori medi delle vendite e dei prezzi mediani nelle diverse città.

ggplot(summary_city, aes(x = city, y = sales_mean)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  labs(title = "Media delle vendite per città", x = "Città", y = "Vendite")

ggplot(summary_city, aes(x = city, y = median_price_mean)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  labs(title = "Media del prezzo mediano per città", x = "Città", y = "Prezzo ($)") +
  scale_y_continuous(labels = scales::comma)

# Summary statistico per anno
summary_year = texas_data %>%
  group_by(year) %>%
  summarise(
    sales_mean = mean(sales),
    sales_sd = sd(sales),
    volume_mean = mean(volume),
    volume_sd = sd(volume),
    median_price_mean = mean(median_price),
    median_price_sd = sd(median_price),
    listings_mean = mean(listings),
    listings_sd = sd(listings)
  ) %>% 
  mutate(across(2:9, round, 2))

summary_year
## # A tibble: 5 × 9
##    year sales_mean sales_sd volume_mean volume_sd median_price_mean
##   <int>      <dbl>    <dbl>       <dbl>     <dbl>             <dbl>
## 1  2010       169.     60.5        25.7      10.8           130192.
## 2  2011       164.     63.9        25.2      12.2           127854.
## 3  2012       186.     70.9        29.3      14.5           130077.
## 4  2013       212.     84          35.2      17.9           135723.
## 5  2014       231.     95.5        39.8      21.2           139481.
## # ℹ 3 more variables: median_price_sd <dbl>, listings_mean <dbl>,
## #   listings_sd <dbl>

Nei seguenti garfici a linee vengono mostrati gli andamenti dei valori medi delle vendite e dei prezzi mediani nel corso degli anni.

ggplot(summary_year, aes(x = year, y = sales_mean)) +
  geom_line(color = "red") +
  geom_point(color = "red") +
  labs(title = "Evoluzione della media delle vendite", x = "Anno", y = "Vendite")

ggplot(summary_year, aes(x = year, y = median_price_mean)) +
  geom_line(color = "red") +
  geom_point(color = "red") +
  labs(title = "Evoluzione della media del prezzo mediano", x = "Anno", y = "Prezzo ($)") +
  scale_y_continuous(labels = scales::comma)

# Summary statistico per mese
summary_month = texas_data %>%
  group_by(month) %>%
  summarise(
    sales_mean = mean(sales),
    sales_sd = sd(sales),
    volume_mean = mean(volume),
    volume_sd = sd(volume),
    median_price_mean = mean(median_price),
    median_price_sd = sd(median_price),
    listings_mean = mean(listings),
    listings_sd = sd(listings)
  ) %>% 
  mutate(across(2:9, round, 2))

summary_month
## # A tibble: 12 × 9
##    month sales_mean sales_sd volume_mean volume_sd median_price_mean
##    <int>      <dbl>    <dbl>       <dbl>     <dbl>             <dbl>
##  1     1       127.     43.4        19        8.37            124250
##  2     2       141.     51.1        21.6     10.1             130075
##  3     3       189.     59.2        29.4     12.0             127415
##  4     4       212.     65.4        33.3     14.5             131490
##  5     5       239.     83.1        39.7     19.0             134485
##  6     6       244.     95          41.3     21.1             137620
##  7     7       236.     96.3        39.1     21.4             134750
##  8     8       231.     79.2        38.0     18.0             136675
##  9     9       182.     72.5        29.6     15.2             134040
## 10    10       180.     75.0        29.1     15.1             133480
## 11    11       157.     55.5        24.8     11.2             134305
## 12    12       169.     60.8        27.1     12.6             133400
## # ℹ 3 more variables: median_price_sd <dbl>, listings_mean <dbl>,
## #   listings_sd <dbl>

Nei seguenti garfici a barre vengono mostrati i valori medi delle vendite e dei prezzi mediani in ogni mese.

ggplot(summary_month, aes(x = factor(month), y = sales_mean)) +
  geom_bar(stat = "identity", fill = "forestgreen") +
  labs(title = "Media delle vendite per mese", x = "Mese", y = "Vendite")

ggplot(summary_month, aes(x = factor(month), y = median_price_mean)) +
  geom_bar(stat = "identity", fill = "forestgreen") +
  labs(title = "Media del prezzo mediano per mese", x = "Mese", y = "Prezzo ($)") +
  scale_y_continuous(labels = scales::comma)

8. Creazione di visualizzazioni con ggplot2

In questa sezione viene usata la libreria ggplot2 per realizzare dei grafici personalizzati ed evidenziare alcuni aspetti che emergono dai dati.

Vengono creati dei boxplot per confrontare la distrubuzione del prezzo mediano tra le città. Questo tipo di grafico permette di visualizzare il valore mediano. il range interquartile, i valori massimi e minimi e gli outliers per la variabile median_price nelle diverse città. Sono stati aggiunti anche dei violin plot per mostrare anche la distribuzione della densità dei valori. Si può chiaramente osservare come i prezzi mediani a “Wichita Falls” sono tendenzialmente più bassi rispetto alle altre tre città, ma hanno una maggiore variabilità. Si nota anche come a “Bryan-College Station” vi sono i prezzi più alti, indicati sia dalla mediana che dai massimi.

ggplot(texas_data, aes(x = city, y = median_price, fill = city)) +
  geom_violin(show.legend = FALSE) +
  geom_boxplot(width=0.5 , show.legend = FALSE) +
  labs(title = "Distribuzione del prezzo mediano tra le città",
       x = "Città", y = "Prezzo mediano ($)") +
  scale_y_continuous(labels = scales::comma)

Vengono poi creati dei grafici a barre per confrontare il totale delle vendite per mese e città. In questo caso il dataset viene raggruppato considerando le variabili city e month e viene eseguito un summary calcolando le vendite totali per tutti gli anni. Nel primo grafico vengono mostrati i mesi sull’asse x e le barre colorate diversamente indicano le città. Nel secondo grafico vengono mostrate le città sull’asse x e le diverse barre sono colorate in base al mese. In entrabi i casi si nota come vi sia un maggior numero di vendite nei mesi primaverili e estivi nelle città di “Bryan-College Station” e “Tyler”, mentre nelle città di “Beaumont” e soprattutto “Wichita Falls” le vendite sono più costanti nel corso dell’anno.

texas_data %>%
  group_by(city, month) %>%
  summarise(total_sales = sum(sales)) %>%
  ggplot(aes(x = factor(month), y = total_sales, fill = city)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "Totale vendite per mese e città",
       x = "Mese", y = "Totale vendite", fill = "Città")
## `summarise()` has grouped output by 'city'. You can override using the
## `.groups` argument.

texas_data %>%
  group_by(city, month) %>%
  summarise(total_sales = sum(sales)) %>%
  ggplot(aes(x = factor(city), y = total_sales, fill = as.factor(month))) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "Totale vendite per mese e città",
       x = "Città", y = "Totale vendite", fill = "Mese")
## `summarise()` has grouped output by 'city'. You can override using the
## `.groups` argument.

Costruendo un grafico a barre sovrapposte si osservano più chiaramente le differenza nel totale delle vendite. “Wichita Falls”, oltre ad avere la minore variabilità annuale, ha anche un numero di vendite totali minore rispetto alle altre città. “Tyler”, invece, è la città con il maggior numero di vendite.

texas_data %>%
  group_by(city, month) %>%
  summarise(total_sales = sum(sales)) %>%
  ggplot(aes(x = factor(city), y = total_sales, fill = as.factor(month))) +
  geom_bar(stat = "identity", position = "stack") +
  labs(title = "Totale vendite per mese e città (sovrapposte)",
       x = "Città", y = "Totale vendite", fill = "Mese")
## `summarise()` has grouped output by 'city'. You can override using the
## `.groups` argument.

Costruendo lo stesso grafico a barre normalizzato, che rappresenta le percentuali sul totale delle vendite per ogni mese, si osserva meglio la differenza nelle vendite nei vari mesi ma si perde l’informazione sul totale delle vendite nelle diverse città.

texas_data %>%
  group_by(city, month) %>%
  summarise(total_sales = sum(sales)) %>%
  group_by(month) %>%
  mutate(perc_sales = total_sales / sum(total_sales)) %>%
  ggplot(aes(x = factor(city), y = perc_sales, fill = as.factor(month))) +
  geom_bar(stat = "identity", position = "fill") +
  scale_y_continuous(labels = scales::percent) +
  labs(title = "Totale vendite per mese e città (normalizzato)",
       x = "Città", y = "Percentuale sul totale", fill = "Mese")
## `summarise()` has grouped output by 'city'. You can override using the
## `.groups` argument.

Utilizzano la funzione facet_wrap di ggplot2 è possibile suddivire i grafici anche per la variabile year. In questo modo si può per esempio visualizzare il totale delle vendite per mese e città nei diversi anni attraverso grafici a barre affiancate o sovrapposte.

texas_data %>%
  group_by(year, city, month) %>%
  summarise(total_sales = sum(sales)) %>%
  ggplot(aes(x = factor(month), y = total_sales, fill = city)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~ year) +  # Suddivisione dei grafici per anno
  labs(title = "Totale vendite per mese, città e anno",
       x = "Mese", y = "Totale vendite", fill = "Città") + 
  theme(legend.position = c(0.85, 0.2))
## `summarise()` has grouped output by 'year', 'city'. You can override using the
## `.groups` argument.
## Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2
## 3.5.0.
## ℹ Please use the `legend.position.inside` argument of `theme()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

texas_data %>%
  group_by(year, city, month) %>%
  summarise(total_sales = sum(sales)) %>%
  ggplot(aes(x = factor(city), y = total_sales, fill = as.factor(month))) +
  geom_bar(stat = "identity", position = "stack") +
  facet_wrap(~ year) +  # Suddivisione dei grafici per anno
  labs(title = "Totale vendite per mese, città e anno",
       x = "Città", y = "Totale vendite", fill = "Mese") + 
  theme(legend.position = c(0.85, 0),
        axis.text.x = element_text(size=7, angle = 45, hjust = 1)) +
  guides(fill=guide_legend(ncol=3))
## `summarise()` has grouped output by 'year', 'city'. You can override using the
## `.groups` argument.

In seguito vengono creati dei boxplot che permettono di confrontare la distribuzione del valore totale delle vendite tra le varie città e nei diversi anni. Dal grafico si può osservare come il valore delle vendite a “Wichita Falls” rimane basso nel corso degli anni, mentre cresce molto a “Bryan-College Station” e “Tyler”.

ggplot(texas_data, aes(x = city, y = volume, fill = as.factor(year))) +
  geom_boxplot() +
  labs(title = "Distribuzione del valore totale delle vendite tra le città",
       x = "Città", y = "Volume delle vendite (milioni di $)", fill = "Anno")

Infine, vengono creati dei grafici a linee per visualizzare l’andamento delle vendite nel corso del tempo. Per questa operazione viene creata una nuova variabile date che combina le variabili year e month e indica la data di riferimento.

# Creazione di una varianile "date" che include mese e anno 
texas_data = texas_data %>%
  mutate(date = as.Date(paste(year, month, "01", sep = "-")))

I due garfici seguenti mostrano l’andamento delle vendite totali e del loro valore totale per tutte le città.

texas_data %>%
  group_by(date) %>%
  summarise(total_sales = sum(sales)) %>%
  ggplot(aes(x = date, y = total_sales)) +
  geom_line(color = "red") +
  geom_point(color = "red") +
  labs(title = "Andamento delle vendite totali",
       x = "Data", y = "Totale vendite") + 
  scale_x_date(date_breaks = "years", date_labels = "%Y")

texas_data %>%
  group_by(date) %>%
  summarise(total_volume = sum(volume)) %>%
  ggplot(aes(x = date, y = total_volume)) +
  geom_line(color = "limegreen") +
  geom_point(color = "limegreen") +
  labs(title = "Andamento del valore totale delle vendite",
       x = "Data", y = "Volume delle vendite (milioni di $)") + 
  scale_x_date(date_breaks = "years", date_labels = "%Y")

Il grafico seguente permette di confrontare l’andamento delle vendite nelle varie città.

ggplot(texas_data, aes(x = date, y = sales, color = city, group = city)) +
  geom_line() +
  geom_point() +
  labs(title = "Andamento delle vendite nelle varie città",
       x = "Data", y = "Vendite", color = "Città") + 
  scale_x_date(date_breaks = "years", date_labels = "%Y") + 
  guides(color = guide_legend(position = "inside")) +
  theme(legend.position.inside = c(0.2, 0.8), 
        legend.background = element_rect(fill = "#FFFFFF85", colour = NA))

Mentre con il grafico precedente è possibile osservare le oscillazioni delle vendite nel corso dei mesi e degli anni, è difficile distinguere delle tendenze tra le varie città. Questo è facilitato utilizzando la funzione geom_smooth di ggplot2, che applica una funzione di smoothing alle serie di dati relative alle diverse città, producendo una linea di tendenza e un intervallo di confidenza.

ggplot(texas_data, aes(x = date, y = sales, color = city, group = city)) +
  geom_point() +
  geom_smooth(aes(color = city, fill = city)) +
  labs(title = "Andamento delle vendite nelle varie città",
       x = "Data", y = "Vendite", color = "Città", fill = "Città") + 
  scale_x_date(date_breaks = "years", date_labels = "%Y") + 
  guides(color = guide_legend(position = "inside"),
         fill = guide_legend(position = "inside")) +
  theme(legend.position.inside = c(0.2, 0.8), 
        legend.background = element_rect(fill = "#FFFFFF85", colour = NA))
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Dal grafico è possibile osservare come le vendite nella città di “Tyler” abbiano valori più alti e una tendenza marcatamente crescente nel corso del tempo. Al contrario, nella città di “Wichita Falls” le vendite hanno valori più bassi e una tendenza costante. Le città di “Beaumont” e “Bryan-College Station” hanno dei valori intermedi e in buona parte sovrapposti, con delle tendeze leggermente in crescita.

Creando lo stesso grafico per i prezzi mediani si osserva un andamento leggermente crescente in tutte le città, con l’esclusione di “Beaumont”. Come notato anche in precedenza, i prezzi nella città di “Bryan-College Station” risultano essere i più alti.

ggplot(texas_data, aes(x = date, y = median_price, color = city, group = city)) +
  geom_point() +
  geom_smooth(aes(color = city, fill = city)) +
  labs(title = "Andamento dei prezzi mediani nelle varie città",
       x = "Data", y = "Prezzi mediani ($)", color = "Città", fill = "Città") + 
  scale_x_date(date_breaks = "years", date_labels = "%Y") +
  scale_y_continuous(labels = scales::comma) +
  guides(color = guide_legend(position = "inside"),
         fill = guide_legend(position = "inside")) +
  theme(legend.position.inside = c(0.2, 0.8), 
        legend.background = element_rect(fill = "#FFFFFF85", colour = NA))
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

9. Conclusioni

Questa analisi esplorativa del mercato immobiliare del Texas ha permesso di rivelare alcune caratteristiche e tendenze relative alla vendita di immobili in 4 diverse città.

Come indicato dall’indice di eterogeneità di Gini, calcolato per il prezzo mediano di vendita degli immobili, pari a 0.95, è presente un’elevata eterogeneità nei prezzi. In particolare, osservando le analisi condizionate per le diverse città, questo sembra dovuto al fatto che i prezzi della città di “Wichita Falls” sono tendenzialmente più bassi rispetto alle altre tre città. Appare interessante notare che i prezzi nella città di “Bryan-College Station” sono i più alti, mentre le vendite nella stessa città sono inferiori rispetto alla città di “Tyler”. Questo potrebbe suggerire uno squilibrio nei prezzi nella città di “Bryan-College Station”, le cui cause andrebbero approfondite analizzando altre varibili.

Un’altra caratteristica emersa da questa analisi è la stagionalità nelle vendite. Questa è evidente principalmente nelle città di “Bryan-College Station” e “Tyler”, che presentano un maggior numero di vendite nei mesi primaverili e estivi, mentre le città di “Beaumont” e soprattutto “Wichita Falls” presentano vendite più costanti nel corso dell’anno. Le cause di questa stagionalità potrebbero essere legate a una maggiore attrattività turistica di “Bryan-College Station” e “Tyler”, ma servirebbero ulteriori dati per confermare questa ipotesi. Da questo punto di vista, inoltre, è interessante notare, confrontando le città di “Beaumont” e “Bryan-College Station” attraverso i grafici a barre che mostrano le vendite suddivise per mese e il grafico a linee dell’andamento temporale, che le vendite nelle due città nei mesi autunnali e invernali sono sempre a livelli simili, mentre nei mesi primaverili ed estivi le vendite a “Bryan-College Station” sono molto maggiori.

Infine, occorre notare una generale tendenza crescente delle vedite nel corso degli anni, emersa in tutte le città analizzate, ad esclusione di “Wichita Falls”, dove le vendite sono rimaste sostanzialmente costanti. Questo suggerisce che “Wichita Falls” potrebbe presentare una scarsa attrattività generale, non legata esclusivamente al settore turistico.