Analisi del mercato immobiliare del Texas

Su richiesta di Texas Realty Insights, è stato analizzato un insieme di dati sulle vendite immobiliari in quattro città del Texas a partire da una tabella fornita dall’azienda stessa. I risultati di questa analisi saranno esposti in questo documento.

Dopo aver importato i dati e fornito delle indicazioni sul contesto in cui sono stati raccolti, saranno analizzate nel dettaglio le variabili coinvolte e si individueranno le caratteristiche più rilevanti in termini di variabilità e asimmetria. Dall’analisi delle singole variabili ci sposteremo poi sull’analisi condizionata di più variabili contemporaneamente anche tramite il supporto di simulazioni e rappresentazioni grafiche.

Importazione dei dati e analisi delle variabili

Questa sezione sarà dedicata all’importazione e a un’analisi di carattere generale dei dati.

I dati sono stati importati in un Dataframe R da un file CSV fornito dall’azienda committente.

df <- read.csv("realestate_texas.csv",
               sep = ",")

Nella cella seguente saranno mostrate le dimensioni del dataset.

dim(df)
## [1] 240   8

La tabella è costituita da 240 osservazioni di cui sono state misurate 8 variabili. La cella seguente mostra le prime cinque righe del dataset.

head(df,
     5)
##       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

La prima colonna, city, indica le città del Texas in cui sono state effettuate le rilevazioni. La cella seguente mostra i valori unici di questa colonna.

unique(df$city)
## [1] "Beaumont"              "Bryan-College Station" "Tyler"                
## [4] "Wichita Falls"

La colonna year indica l’anno a cui fa riferimento l’osservazione e, per questa analisi, sarà trattata come una variabile categorica e non come una variabile numerica. Di seguito si mostreranno i valori unici.

unique(df$year)
## [1] 2010 2011 2012 2013 2014

In modo analogo tratteremo come variabile categorica la colonna month, che indica il mese di riferimento dell’osservazione in formato numerico. Anche di questa colonna si mostreranno i valori unici.

unique(df$month)
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12

Nella cella seguente abbiamo verificato che il numero di righe della tabella è dato dal prodotto tra il numero di valori unici delle colonne city, year e month. Questo ci porta a confermare che i dati forniti rappresentino un riepilogo delle vendite sulla base di questi tre parametri.

length(unique(df$city)) * 
  length(unique(df$year)) *
  length(unique(df$month)) == dim(df)[1]
## [1] TRUE

Passando alle variabili numeriche, i dati sulle vendite immobiliari sono indicati nelle colonne sales, volume e median_price, che indicano rispettivamente il numero di vendite registrate, il volume di vendite in milioni di dollari e il prezzo mediano, ossia la soglia di prezzo sotto cui sono stati venduti il 50% degli immobili. Di queste tre colonne sarà mostrato il range di valori nella cella seguente.

#Sales
range(df$sales)
## [1]  79 423
#Volume
range(df$volume) *
  1000000
## [1]  8166000 83547000
#Median price
range(df$median_price)
## [1]  73800 180000

I dati sugli annunci immobiliari sono invece indicati nelle colonne listings e months_inventory, che riportano rispettivamente il numero di annunci attivi e i mesi previsti per vendere tutti gli annunci correnti. Anche in questo caso saranno mostrati i range di valori di queste colonne.

#Listings
range(df$listings)
## [1]  743 3296
#Months inventory
range(df$months_inventory)
## [1]  3.4 14.9

Riassumendo quanto esposto finora, possiamo dedurre quanto segue:

  • i dati riguardano le vendite di immobili nelle città di Beaumont, “Bryan-College Station, Tyler e Wichita Falls tra il 2010 e il 2014
  • sono state registrate tra le 79 e le 423 vendite immobiliari che hanno comportato un volume di vendita tra gli 8,1 e i 83,6 milioni di dollari. Per ogni osservazione, inoltre, metà delle vendite si sono concluse a un prezzo mediano compreso tra i 73000 e i 180000 dollari per ciascun immobile;
  • gli annunci attivi pubblicati sono compresi tra i 743 e i 3296 e, secondo le stime riportate, sarebbero occorsi tra i tre e i quindici mesi per venderli tutti.

Il dettaglio riguardo ogni singola variabile sarà esplorato nella prossima sezione.

Indici di posizione, variabilità e forma

In questa sezione analizzeremo le variabili dal punto di vista della loro distribuzione.

Partendo dalle variabili categoriche, la cella seguente mostra le frequenze assolute e relative della colonna city. Non trattandosi di una variabile ordinale, non sono state calcolate le frequenze cumulate.

ni <- table(df$city)
fi <- ni/
  dim(df)[1]

cbind(ni,
      fi)
##                       ni   fi
## Beaumont              60 0.25
## Bryan-College Station 60 0.25
## Tyler                 60 0.25
## Wichita Falls         60 0.25

Come già anticipato nella precedente sezione, si osserva una distribuzione uniforme delle osservazioni tra le quattro città riportate nella tabella, a ulteriore conferma che i dati riguardano le vendite nell’arco di 60 mesi per ogni città.

In modo analogo, le due celle seguenti mostrano le distribuzioni di frequenze nelle colonne year e month che, ricordiamo, sono trattate come variabili qualitative di tipo ordinale per gli scopi di questa analisi.

ni <- table(df$year)
fi <- ni/
  dim(df)[1]
Ni <- cumsum(ni)
Fi <- cumsum(fi)

cbind(ni,
      fi,
      Ni,
      Fi)
##      ni  fi  Ni  Fi
## 2010 48 0.2  48 0.2
## 2011 48 0.2  96 0.4
## 2012 48 0.2 144 0.6
## 2013 48 0.2 192 0.8
## 2014 48 0.2 240 1.0
ni <- table(df$month)
fi <- ni/
  dim(df)[1]
Ni <- cumsum(ni)
Fi <- cumsum(fi)

cbind(ni,
      fi,
      Ni,
      Fi)
##    ni         fi  Ni         Fi
## 1  20 0.08333333  20 0.08333333
## 2  20 0.08333333  40 0.16666667
## 3  20 0.08333333  60 0.25000000
## 4  20 0.08333333  80 0.33333333
## 5  20 0.08333333 100 0.41666667
## 6  20 0.08333333 120 0.50000000
## 7  20 0.08333333 140 0.58333333
## 8  20 0.08333333 160 0.66666667
## 9  20 0.08333333 180 0.75000000
## 10 20 0.08333333 200 0.83333333
## 11 20 0.08333333 220 0.91666667
## 12 20 0.08333333 240 1.00000000

Come anticipato in precedenza, la distribuzione dei dati è uniforme sia riguardo year che riguardo month. Osserviamo inoltre che metà delle osservazioni sono antecedenti al 2012 e riguardano il primo semestre di ogni anno.

Per quanto riguarda le variabili numeriche, come anche parzialmente evidenziato nella sezione precedente, le colonne presentano dati continui. Anche per agevolare la lettura di questi dati, è stata definita la funzione plot_distribution che, a partire da una colonna data in ingresso:

  • Mostra in un grafico sia la distribuzione dei dati (in nero) che la normale avente la stessa media e la stessa deviazione standard (linea tratteggiata);
  • Traccia come linee verticali la media (in verde), la mediana (in blu) e il range interquartile (in rosso);
  • Stampa i valori calcolati nel punto precedente nella legenda, approssimandoli a una cifra dopo la virgola per migliorare la loro leggibilità.

La funzione così definita è riportata nella cella seguente.

plot_distribution <- function(x){
  mean_x <- mean(x)
  sd_x <- sd(x)
  quartiles_x <- quantile(x)
  
  plot(density(x),
       xlim = c(min(x),
                max(x)),
       main = "",
       xlab = "",
       ylab = "Freq. relativa")
  lines(density(rnorm(1000000,
                      mean = mean_x,
                      sd = sd_x)),
        lty = 2)
  abline(v = quartiles_x[3],
         col = "blue")
  abline(v = quartiles_x[c(2,
                           4)],
         col = "red")
  abline(v = mean_x,
         col = "green")
  legend("bottom",
         legend = c(paste("Mediana:",
                          round(quartiles_x[3],
                                1)),
                    paste("IQR:",
                          round(quartiles_x[2],1),
                          "-",
                          round(quartiles_x[4],1)),
                    paste("Media:",
                          round(mean_x,
                                1))),
         col = c("blue",
                 "red",
                 "green"),
         lty = c(1,
                 1,
                 1))
}

Testiamo la funzione plot_function sulla colonna sales del dataset.

plot_distribution(df$sales)

Osserviamo una distribuzione delle vendite asimmetrica rispetto alla normale con la stessa media e la stessa deviazione standard. Più nello specifico si evidenzia una tendenza a un basso numero di vendite per il 75% dei dati e una tendenza, seppur minore, a un numero di vendite comprese tra le 275 e le 300.

La cella seguente mostra la distribuzione della colonna volume.

plot_distribution(df$volume)

Il grafico è simile nella forma alla distribuzione vista con il numero di vendite, ma la seconda “gobba” di dati (intorno ai 50 milioni) è meno evidente rispetto al grafico precedente.

La cella seguente mostra la distribuzione della colonna median_price.

plot_distribution(df$median_price)

Il prezzo mediano è sì asimmetrico, ma in misura minore e soprattutto tende a mostrare come più frequenti i valori più alti, pur mostrando una tendenza minore intorno ai 100000 dollari.

La cella seguente mostra la distribuzione della colonna listings.

plot_distribution(df$listings)

Decisamente asimmetrica rispetto alla distribuzione normale, la colonna listings concentra il 50% dei dati tra i 1000 e i 2000 annunci attivi. La distribuzione mostra inoltre due tendenze minori, una nel primo 25% dei dati e l’altra tra i 2750 e i 3000 annunci attivi.

Infine, si mostra nella cella seguente la distribuzione della colonna months_inventory.

plot_distribution(df$months_inventory)

Come in median_price, i mesi di inventario tendono ad approssimare la distribuzione normale con la stessa media e la stessa deviazione standard, pur presentando una tendenza ai valori più bassi. Una seconda tendenza, di minore intensità, si osserva tra gli 11 e i 12 mesi.

Sulla base di quanto osservato in questa sezione, possiamo dedurre quanto segue:

  • i dati sono uniformemente distribuiti tra quattro città del Texas nei sessanta mesi tra gennaio 2010 e dicembre 2014;
  • le variabili numeriche presentano diversi ordini di grandezza, motivo per cui non è stato possibile confrontare direttamente la loro variabilità e la loro asimmetria. A questo confronto sarà dedicata la prossima sezione;
  • sia in termini di numero di vendite che di volume, la tendenza principale favorisce i valori più bassi, mentre il prezzo mediano mostra frequenze più alte a destra della distribuzione normale, seppur non in modo evidente. Questo fa supporre che, nel quinquennio osservato, il mercato si è mantenuto stabile nel prezzo mediano ma cauto nel numero e nel volume di vendite. Il rapporto tra vendite e volume sarà esplorato più nel dettaglio nelle prossime sezioni;
  • la cautela e la stabilità del mercato sembrano confermarsi rispettivamente negli annunci attivi osservati, in cui spicca la tendenza ai numeri più bassi, e nei mesi di inventario, dove la tendenza si concentra intorno ai valori centrali. Anche il rapporto tra numero di annunci e mesi di inventario sarà esplorato nelle prossime sezioni.

Variabilità e asimmetria

In questa sezione ci concentreremo sulla variabilità e sull’asimmetria delle variabili viste in precedenza.

Come emerso nella sezione precedente, il diverso ordine di grandezza tra le variabili spinge a optare per il coefficiente di variazione come misura della variabilità dei dati, mentre per l’asimmetria sarà utilizzato l’indice di Fisher. Nella cella seguente è stata definita la funzione per il coefficiente di variazione ed è stata importata la libreria moments per il calcolo dell’asimmetria.

cv <- function(x){
  return (sd(x)/
            mean(x))*
    100
}

library(moments)

Dalle colonne quantitative otterremo una tabella riassuntiva scores che calcola i valori di media, deviazione standard, coefficiente di variazione e indice di Fisher. Questa tabella è calcolata e stampata nella cella seguente.

columns <- c()
means <- c()
stdevs <- c()
cvs <- c()
fishers <- c()

for(i in 4:8){
  columns <- append(columns,
                    colnames(df[i]))
  means <- append(means,
                  mean(df[, i]))
  stdevs <- append(stdevs,
                   sd(df[, i]))
  cvs <- append(cvs,
                cv(df[, i]))
  fishers <- append(fishers,
                    skewness(df[, i]))
}

scores <- data.frame(
  cols <- columns,
  mean <- means,
  sd <- stdevs,
  cv <- cvs,
  skew <- fishers
)

scores
##    cols....columns mean....means sd....stdevs cv....cvs skew....fishers
## 1            sales     192.29167    79.651111 0.4142203      0.71810402
## 2           volume      31.00519    16.651447 0.5370536      0.88474203
## 3     median_price  132665.41667 22662.148687 0.1708218     -0.36455288
## 4         listings    1738.02083   752.707756 0.4330833      0.64949823
## 5 months_inventory       9.19250     2.303669 0.2506031      0.04097527

Nelle due celle seguenti, la tabella scores è stata ordinata in base ai valori del coefficiente di variazione e di asimmetria.

sort_by(scores[c(1,
                 4,
                 5)],
        scores$cv....cvs)
##    cols....columns cv....cvs skew....fishers
## 3     median_price 0.1708218     -0.36455288
## 5 months_inventory 0.2506031      0.04097527
## 1            sales 0.4142203      0.71810402
## 4         listings 0.4330833      0.64949823
## 2           volume 0.5370536      0.88474203
sort_by(scores[c(1,
                 4,
                 5)],
        scores$skew....fishers)
##    cols....columns cv....cvs skew....fishers
## 3     median_price 0.1708218     -0.36455288
## 5 months_inventory 0.2506031      0.04097527
## 4         listings 0.4330833      0.64949823
## 1            sales 0.4142203      0.71810402
## 2           volume 0.5370536      0.88474203

Notiamo che la colonna volume presenta sia la massima variabilità che la massima asimmetria. Visualizziamo nuovamente la sua distribuzione tramite la funzione plot_distribution.

plot_distribution(df$volume)

La colonna median_price, invece, presenta sia la minima variabilità che la minima asimmetria.

plot_distribution(df$median_price)

Dai risultati appena ottenuti possiamo dedurre quanto segue:

  • unendo la tendenza ai valori più bassi alla maggiore variabilità dei dati, la cautela del volume di vendite ipotizzata nella precedente sezione può essere dovuta a periodi in cui il mercato immobiliare era più instabile;
  • la minima variabilità e asimmetria del prezzo mediano confermano la sua stabilità nei riferimenti osservati, il che ci porta a ipotizzare una stabilità dei prezzi degli immobili.

Il prezzo mediano sarà approfondito nella prossima sezione.

La distribuzione delle classi di prezzo mediano

In questa sezione approfondiremo la distribuzione del prezzo mediano nel dataset suddividendolo in fasce di prezzo.

Allo scopo di interpretare più facilmente i dati, è stata aggiunta la colonna median_price_K che riscala la colonna median_price dividendone i valori per mille.

df$median_price_K <- df$median_price/
  1000

Questa colonna è stata divisa in fasce da 10.000 dollari a partire da 70.000 dollari fino al massimo registrato di 180.000 dollari. Le fasce di prezzo sono state calcolate nella colonna median_price_CL.

df$median_price_CL <- cut(df$median_price_K,
                          breaks = seq(70,
                                       180,
                                       10))

Nella prima delle due celle che seguono sarà calcolata la distribuzione di frequenze delle classi di prezzo. La divisione in quartili del prezzo mediano, calcolata nella seconda cella, servirà come riferimento.

ni <- table(df$median_price_CL)
fi <- ni/
  dim(df)[1]
Ni <- cumsum(ni)
Fi <- cumsum(fi)

cbind(ni,
      fi,
      Ni,
      Fi)
##           ni          fi  Ni          Fi
## (70,80]    1 0.004166667   1 0.004166667
## (80,90]   10 0.041666667  11 0.045833333
## (90,100]  15 0.062500000  26 0.108333333
## (100,110] 23 0.095833333  49 0.204166667
## (110,120] 17 0.070833333  66 0.275000000
## (120,130] 29 0.120833333  95 0.395833333
## (130,140] 46 0.191666667 141 0.587500000
## (140,150] 39 0.162500000 180 0.750000000
## (150,160] 39 0.162500000 219 0.912500000
## (160,170] 15 0.062500000 234 0.975000000
## (170,180]  6 0.025000000 240 1.000000000
quantile(df$median_price_K)
##     0%    25%    50%    75%   100% 
##  73.80 117.30 134.50 150.05 180.00

Nella cella seguente calcoleremo la media ponderata della suddivisione in classi e il rapporto percentuale con la media effettiva dei valori.

val_centrali <- seq(75,
                    175,
                    10)
weight_mean <- sum(val_centrali*ni)/
  sum(ni)

weight_mean; round((weight_mean/
                      mean(df$median_price_K))*
                     100,
                   3)
## [1] 132.4167
## [1] 99.812

La cella che segue riepiloga in un grafico a barre la suddivisione nelle fasce di prezzo.

barplot(ni,
        main = "Distribuzione del prezzo mediano (in migliaia di dollari)",
        ylim = c(0,
                 50),
        las = 2)

Infine, nella cella che segue calcoleremo l’indice di Gini della suddivisione in classi, che misura la tendenza del prezzo mediano a distribuirsi equamente tra le fasce indicate.

G <- 1 - 
  sum(fi^
        2)
J <- length(fi)
gini <- G/
  ((J-1)/
     J)
gini
## [1] 0.9586042

Da questi risultati osserviamo quanto segue:

  • la media ponderata della suddivisione in classi approssima al 99% la media effettiva dei valori ottenuti, il che implica che la perdita di informazione sia stata minima;
  • la moda è rappresentata dalla fascia di prezzo tra i 130.000 e i 140.000 dollari, come anche evidenziabile nella distribuzione dei valori effettivi di prezzo mediano;
  • in una scala da 0 (massima concentrazione intorno a un’unico valore) a 1 (massima eterogeneità tra i valori), i dati si distribuiscono in modo molto eterogeneo tra le fasce di prezzo indicate. Questo significa che, pur mostrando la minima asimmetria rispetto alla distribuzione normale, rimane la tendenza a distribuirsi uniformemente tra tutte le fasce di prezzo.

Simulazioni di probabilità sulle città, sul mese e sull’anno

In questa sezione ci occuperemo di effettuare delle simulazioni di probabilità sulle colonne city, month e year. In particolare, esamineremo tre eventi:

  • L’estrazione di un record riferito alla città di Beaumont indipendentemente dal mese e dall’anno di riferimento;
  • L’estrazione di un record risalente al mese di luglio, indipendentemente dalla città e dall’anno;
  • L’estrazione di un record risalente a dicembre 2012, indipendentemente dalla città.

Ricordiamo brevemente che le tre colonne presentano ciascuna una distribuzione uniforme dei dati all’interno del dataset, perciò i risultati che si otterranno dalle simulazioni non cambieranno al variare dei parametri negli eventi da esaminare. Se poi noi utilizziamo un approccio classico alla probabilità, essa coinciderà con le frequenze relative dei valori in esame.

Nella cella seguente, calcoliamo quindi le probabilità degli eventi sopra citati.

p_beaumont <- sum(df$city == "Beaumont")/
  dim(df)[1]
p_july <- sum(df$month == 7)/
  dim(df)[1]
p_dec_12 <- sum(df$month == 12 & 
                  df$year == 2012)/
  dim(df)[1]

p_beaumont; p_july; p_dec_12
## [1] 0.25
## [1] 0.08333333
## [1] 0.01666667

Verifichiamo ora con un approccio frequentista questi risultati. L’idea è di simulare l’estrazione di un campione di questi dati e calcolare la frequenza relativa delle estrazioni in cui si verificano gli eventi descritti.

Per fare questo, definiamo la funzione simulate_extraction che, dato il numero N di simulazioni, estrae casualmente N record dal dataset.

simulate_extraction <- function(N){
  rows <- sample(1:dim(df)[1],
                 N,
                 replace = TRUE)
  return(df[rows, ])
}

Nella cella seguente costruiamo la tabella simulations che effettua fino a 100.000 simulazioni e calcola le frequenze relative degli eventi in esame (per semplicità, per un numero di simulazioni nullo, le frequenze saranno nulle a loro volta). All’interno della stessa cella, simulations sarà convertito in un dataframe per facilitare la visualizzazione della tabella.

simulations <- c()

for(N in seq(0,
             100000,
             1000)){
  if(N == 0){
    row_simulation <- c(N,
                        0,
                        0,
                        0)
  }else{
    df_simulation <- simulate_extraction(N)
    row_simulation <- c(N,
                        sum(df_simulation$city == "Beaumont")/
                          N,
                        sum(df_simulation$month == 7)/
                          N,
                        sum(df_simulation$month == 12 & 
                              df_simulation$year == 2012)/
                          N)
  }
  
  simulations <- rbind(simulations,
                       row_simulation)
}

simulations <- as.data.frame(simulations)
names(simulations) <- c("N. simulations",
                        "City = Beaumont",
                        "Month = July",
                        "Month-Year = December-2012")

Le tre celle seguenti riepilogano graficamente i risultati ottenuti. Il grafico a linee rappresenta le frequenze relative dei successi per il numero di simulazioni, mentre la linea tratteggiata rossa rappresenta la probabilità calcolata con l’approccio classico.

plot(simulations[c("N. simulations",
                   "City = Beaumont")],
     main = "Estrazione della città di Beaumont",
     xlab = "N. simulazioni",
     ylab = "Freq. relative",
     type = "l")
abline(h = p_beaumont,
       col = "red",
       lty = 2)

plot(simulations[c("N. simulations",
                   "Month = July")],
     main = "Estrazione del mese di luglio",
     xlab = "N. simulazioni",
     ylab = "Freq. relative",
     type = "l")
abline(h = p_july, 
       col = "red", 
       lty=2)

plot(simulations[c("N. simulations",
                   "Month-Year = December-2012")],
     main = "Estrazione del mese di dicembre 2012",
     xlab = "N. simulazioni",
     ylab = "Freq. relative",
     type = "l")
abline(h = p_dec_12, 
       col = "red", 
       lty=2)

Osserviamo che le frequenze relative dei tre eventi tenderanno alla probabilità calcolata all’aumentare del numero di prove. La fluttuazione di questi valori intorno alla probabilità è dovuta perlopiù alla casualità con cui sono stati estratti i campioni dalla funzione extract_simulations.

Il calcolo delle nuove variabili

Questa sezione è dedicata alla creazione di nuove variabili a partire dalle colonne del dataset. Più nello specifico, saranno calcolati i seguenti indicatori:

  • Il prezzo medio di ciascun immobile venduto nella città di riferimento nel mese e nell’anno specificati;
  • Il tempo di inventario medio, ossia il tempo medio in ore che occorre per evadere un annuncio attivo.

Entrambi gli indicatori sono stati calcolati nella cella seguente. Allo scopo di facilitare la lettura e l’interpretazione dei dati, la colonna volume è stata moltiplicata per 1000000 (ricordiamo che il volume di vendite è espresso in milioni di dollari), mentre la colonna months_inventory è stata moltiplicata per 30 giorni e per 24 ore.

df$mean_price <- (df$volume*
                    1000000)/
  df$sales
df$mean_inventory <- (df$months_inventory*
                        30*
                        24)/
  df$listings

La cella seguente mostra le prime cinque righe delle colonne city, month e year insieme alle due colonne appena calcolate.

head(df[c("city",
          "month",
          "year",
          "mean_price",
          "mean_inventory")],
     5)
##       city month year mean_price mean_inventory
## 1 Beaumont     1 2010   170626.5       4.461840
## 2 Beaumont     2 2010   163796.3       4.539723
## 3 Beaumont     3 2010   157697.8       4.518650
## 4 Beaumont     4 2010   134095.0       4.468384
## 5 Beaumont     5 2010   142737.6       4.431395

Le due celle seguenti mostrano tramite la funzione plot_distribution le distribuzioni delle due nuove variabili.

plot_distribution(df$mean_price)

plot_distribution(df$mean_inventory)

Da questi risultati osserviamo quanto segue:

  • Il grafico di mean_price mostra delle analogie con median_price in termini di forma della distribuzione e concentrazione intorno alla media. Questo ci porta a confermare la stabilità ipotizzata in precedenza a proposito dei prezzi degli immobili;
  • La forte variabilità del numero di annunci influisce sulla forma della distribuzione della colonna mean_inventory, che però è attenuata dalla distribuzione più stabile del tempo totale di inventario. Il risultato è una divisione nella distribuzione in cui la tendenza maggiore è data dagli annunci evasi in tempi più brevi (indicatore di un periodo di mercato favorevole), mentre si evidenzia una seconda tendenza di minore intensità per tempi più lunghi (che suppone periodi in cui il mercato immobiliare era più sfavorevole).

Analisi condizionata

Questa sezione approfondirà il numero e il volume di vendita in base al tempo e alla città di riferimento.

La cella seguente mostrerà il numero di vendite nel tempo in base alla città di riferimento.

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
plot(filter(df,
            city == "Beaumont")$sales,
     main = "Vendite per città",
     ylab = "Vendite",
     ylim = c(min(df$sales),
              max(df$sales)),
     type = "l",
     col = "blue",
     xaxt = "n")
lines(filter(df, 
             city == "Bryan-College Station")$sales, 
      col="red")
lines(filter(df, 
             city=="Tyler")$sales, 
      col="darkgreen")
lines(filter(df, city == "Wichita Falls")$sales,
      col="black")
legend("topleft",
       legend = unique(df$city),
       col = c("blue", 
               "red",
               "darkgreen",
               "black"),
       lty=1)
axis(side = 1,
     at = 1:60,
     labels = unique(paste(df$month,
                           df$year,
                           sep = "/")),
     las = 2)

Analogamente, la cella seguente mostra lo stesso tipo di grafico ma per i volumi di vendita.

plot(filter(df, 
            city == "Beaumont")$volume,
     main = "Volume di vendite per città",
     ylab = "Volume (in mln)",
     ylim = c(min(df$volume),
              max(df$volume)),
     type = "l",
     col = "blue",
     xaxt = "n")
lines(filter(df,
             city == "Bryan-College Station")$volume, 
      col="red")
lines(filter(df, 
             city == "Tyler")$volume, 
      col="darkgreen")
lines(filter(df, 
             city == "Wichita Falls")$volume, 
      col="black")
legend("topleft",
       legend = unique(df$city),
       col = c("blue", 
               "red",
               "darkgreen",
               "black"),
       lty=1)
axis(side = 1,
     at = 1:60,
     labels =unique(paste(df$month,
                          df$year,
                          sep = "/")),
     las = 2)

Si evidenzia quanto segue:

  • I due grafici sono visivamente molto simili e presentano un andamento ciclico in cui ogni anno a un primo semestre tendenzialmente in salita segue il secondo semestre in forte discesa;
  • In entrambi i grafici, le maggiori vendite sia in numero che in volume si sono registrate nella città di Tyler, anche se nel secondo trimestre del 2013 e del 2014 si è verificata un’inversione di tendenza con la città di Bryan-College Station;
  • Le minori vendite sia come numero che come volume si sono registrate nella città di Wichita Falls, che tende ad assumere un comportamento meno spiccato rispetto alle altre tre città osservate.

Grafici in ggplot

In questa sezione utilizzeremo la libreria ggplot per effettuare ulteriori analisi condizionate sul dataset.

Riprendendo dalla conclusione della precedente sezione, il grafico a barre nella cella seguente riepiloga le vendite in base alla città di riferimento.

library(ggplot2)

df_sales <- df %>%
  group_by(city) %>%
  summarise(
    total_sales = sum(sales),
    .groups = "drop"
  )

ggplot(df_sales)+
  geom_bar(aes(y = total_sales,
               x = city),
           stat = "identity")+
  labs(title = "Vendite per città",
       y = "Vendite",
       x="Città")

Sempre condizionati dalla città di riferimento, i boxplot della cella seguente mostrano la distribuzione dei prezzi mediani.

ggplot(df)+
  geom_boxplot(aes(y = median_price_K,
                   colour = city))+
  scale_color_manual(values = c("blue",
                                "red",
                                "darkgreen",
                                "black"))+
  labs(title = "Prezzo mediano per città",
       y = "Prezzo mediano (in k)")

Infine, nella cella seguente i dati di vendita sono stati raggruppati in base all’anno e al mese di riferimento per poi essere mostrati in un grafico a linee.

df_time <- df %>%
  group_by(year,
           month) %>%
  summarise(
    total_sales = sum(sales),
    .groups = "drop"
  )

ggplot(df_time) +
  geom_line(aes(x = paste(year,
                          month,
                          sep = "/"), 
                y = total_sales, 
                group = 1)) +
  labs(title = "Vendite nel tempo",
       x = "Mese e Anno",
       y = "Vendite") +
  scale_x_discrete(labels = paste(df_time$month,
                                  df_time$year,
                                  sep = "/"))+
  theme(axis.text.x = element_text(angle = 90, 
                                   hjust = 1))

Da questi grafici osserviamo quanto segue:

  • La città di Wichita Falls ha registrato sia il minimo numero di vendite in totale sia la fascia di prezzi mediani più bassa. Questo si accorda con i risultati della sezione precedente, dove il volume di vendite di questa città era il più basso nel tempo.
  • Il più alto numero di vendite in totale si è registrato a Tyler, mentre a Bryan-College Station risultano le fasce di prezzo mediano più elevate;
  • L’andamento delle vendite complessive registra lo stesso comportamento ciclico visto nella sezione precedente, dove a un semestre di aumento delle vendite segue un semestre in discesa. Inoltre, si osserva una tendenza a crescere in modo lineare nel corso dei sessanta mesi di riferimento.

Conclusioni

L’ultima sezione del progetto è dedicata alle conclusioni che si sono potute trarre dai risultati delle precedenti sezioni.

Ricordiamo che il dataset fornito da Texas Realty Insights mostra i dati di vendita degli immobili nelle città di Beaumont, Bryan-College Station, Tyler e Wichita Falls nel quinquennio tra il 2010 e il 2014. Dalle simulazioni di calcolo delle probabilità sappiamo che la città, il mese e l’anno di riferimento dei record sono distribuiti nel dataset in modo uniforme.

Dall’analisi delle variabili e dagli indici di posizione, variabilità e forma è stata osservata una tendenza ai valori più bassi di vendite sia dal punto di vista del numero che da quello del volume. Ciò ci ha portato a dedurre un comportamento cauto nell’investimento immobiliare, mentre la minima variabilità e asimmetria registrate dal prezzo mediano hanno evidenziato una stabilità dei prezzi degli immobili, anche se la suddivisione in fasce di prezzo ci ha portato a concludere la tendenza a non concentrarsi su un unico valore, ma piuttosto a distribuirsi in modo quasi eterogeneo tra le fascie di prezzo.

Dal punto di vista degli annunci, l’esplorazione delle variabili ha contrapposto la stabilità dei mesi di inventario con la variabilità e l’asimmetria del numero di annunci attivi. Queste caratteristiche sono emerse anche nel calcolo del tempo medio di inventario, in cui si sono evidenziate sia la tendenza principale a evadere un annuncio in tempi molto brevi che una tendenza, seppur minore, ad avere tempi di vendita più lunghi.

Analizzando le vendite dal punto di vista delle città di riferimento, è emerso un comportamento ciclico del mercato immobiliare confermato anche dall’analisi delle vendite nel tempo, dove è emersa inoltre una crescita lineare delle vendite. Dall’ultima sezione, poi, è emerso che il maggiore numero di vendite si è registrato nella città di Tyler, mentre la fascia di prezzo mediano più elevata si è registrata nella città di Bryan-College Station.