1. Analisi delle variabili

# Caricamento dataset
data <- read.csv("realestate_texas.csv")

# Creazione nuova colonna per facilitare analisi temporali 
data <- data %>%
  mutate(date = as.Date(sprintf("%d-%02d-01", year, month)))

# Raggruppa per year e month e calcola la somma totale vendite per ogni mese/anno
andamento_anni <- data %>%
  group_by(year, month) %>%
  summarise(sales = sum(sales, na.rm = TRUE)) %>%
  ungroup() %>%
  mutate(sales = round(sales, 2))

variables <- data.frame(
  name = c("city",
           "year",
           "month",
           "sales",
           "volume",
           "median_price",
           "listings",
           "months_inventory",
           "average_price_per_sale",
           "listings_effectiveness"),
  type = c("Qualitativa Nominale", 
           "Quantitativa Discreta",
           "Quantitativa Discreta",
           "Quantitativa Discreta",
           "Quantitativa Continua",
           "Quantitativa Continua",
           "Quantitativa Discreta",
           "Quantitativa Continua",
           "Quantitativa Continua",
           "Quantitativa Continua"),
  description = c("Città del Texas in cui sono stati raccolti i dati",
                  "Anno in cui sono stati raccolti i dati (2010-2014)",
                  "Mese (espresso numericamente 1-12) in cui sono stati raccolti i dati",
                  "Numero totale di vendite immobiliari concluse nel mese",
                  "Valore complessivo delle vendite immobiliari concluse (in milioni di dollari)",
                  "Prezzo mediano degli immobili venduti (in dollari)", 
                  "Numero totale di annunci immobiliari attivi nel mese", 
                  "Numero medio di mesi necessari per vendere tutti gli immobili disponibili sul mercato",
                  "Prezzo medio per singola transazione",
                  "Efficacia annunci (rapporto tra vendite ed annunci)")
)

kable(variables, caption = "Analisi Variabili")
Analisi Variabili
name type description
city Qualitativa Nominale Città del Texas in cui sono stati raccolti i dati
year Quantitativa Discreta Anno in cui sono stati raccolti i dati (2010-2014)
month Quantitativa Discreta Mese (espresso numericamente 1-12) in cui sono stati raccolti i dati
sales Quantitativa Discreta Numero totale di vendite immobiliari concluse nel mese
volume Quantitativa Continua Valore complessivo delle vendite immobiliari concluse (in milioni di dollari)
median_price Quantitativa Continua Prezzo mediano degli immobili venduti (in dollari)
listings Quantitativa Discreta Numero totale di annunci immobiliari attivi nel mese
months_inventory Quantitativa Continua Numero medio di mesi necessari per vendere tutti gli immobili disponibili sul mercato
average_price_per_sale Quantitativa Continua Prezzo medio per singola transazione
listings_effectiveness Quantitativa Continua Efficacia annunci (rapporto tra vendite ed annunci)

2. Indici di posizione, variabilità e forma

Distribuzioni di frequenza per city, month e year

table_freq <- function(x) {
  freq_abs <- table(x)
  
  table_res <- data.frame(
    Categoria = names(freq_abs), # Categoria nome temporaneo
    Frequenza_Assoluta = as.integer(freq_abs),
    Frequenza_Relativa_In_Percentuale = as.numeric(prop.table(freq_abs)) * 100
  )
  
  return(table_res)
}

# colonne da analizzare
columns <- c("city", "month", "year")

for (column in columns) {
  caption_table <- paste("Distribuzione di Frequenza per", column)
  
  table_frame <- table_freq(data[[column]])
  
  names(table_frame)[1] <- column # nome relativa colonna (city, month, year)
  
  # stampa tabelle
  print(
    kable(
      table_frame,
      caption = caption_table,
      digits = 2
    )
  )
  
  cat("\n\n") # per separare tabelle
}
Distribuzione di Frequenza per city
city Frequenza_Assoluta Frequenza_Relativa_In_Percentuale
Beaumont 60 25
Bryan-College Station 60 25
Tyler 60 25
Wichita Falls 60 25
Distribuzione di Frequenza per month
month Frequenza_Assoluta Frequenza_Relativa_In_Percentuale
1 20 8.33
2 20 8.33
3 20 8.33
4 20 8.33
5 20 8.33
6 20 8.33
7 20 8.33
8 20 8.33
9 20 8.33
10 20 8.33
11 20 8.33
12 20 8.33
Distribuzione di Frequenza per year
year Frequenza_Assoluta Frequenza_Relativa_In_Percentuale
2010 48 20
2011 48 20
2012 48 20
2013 48 20
2014 48 20

Commenti

  • Ogni città, mese ed anno presentano lo stesso numero di osservazioni

Calcolo indici per restanti variabili

vars_index_selected <- data %>% select(sales, volume, median_price, listings, months_inventory)

summary_vars_index <- data.frame(
  Mean                    = sapply(vars_index_selected, mean),
  Median                  = sapply(vars_index_selected, median), 
  Dev.Std                 = sapply(vars_index_selected, sd),
  Var                     = sapply(vars_index_selected, var),
  Min                     = sapply(vars_index_selected, min),
  Max                     = sapply(vars_index_selected, max),
  Q1_25_percentile        = sapply(vars_index_selected, quantile, probs = 0.25),
  Q3_75_percentile        = sapply(vars_index_selected, quantile, probs = 0.75),
  Range_Interquartile_IQR = sapply(vars_index_selected, IQR),
  Skewness                = sapply(vars_index_selected, skewness),
  Kurtosis                = sapply(vars_index_selected, kurtosis)
)

kable(round(summary_vars_index, 2), caption = "Indici di posizione, variabilità e forma")
Indici di posizione, variabilità e forma
Mean Median Dev.Std Var Min Max Q1_25_percentile Q3_75_percentile Range_Interquartile_IQR Skewness Kurtosis
sales 192.29 175.50 79.65 6.34430e+03 79.00 423.00 127.00 247.00 120.00 0.72 2.69
volume 31.01 27.06 16.65 2.77270e+02 8.17 83.55 17.66 40.89 23.23 0.88 3.18
median_price 132665.42 134500.00 22662.15 5.13573e+08 73800.00 180000.00 117300.00 150050.00 32750.00 -0.36 2.38
listings 1738.02 1618.50 752.71 5.66569e+05 743.00 3296.00 1026.50 2056.00 1029.50 0.65 2.21
months_inventory 9.19 8.95 2.30 5.31000e+00 3.40 14.90 7.80 10.95 3.15 0.04 2.83

Commenti:

  • sales:
    • La media è più alta della mediana e l’asimmetria (skewness) è positiva > 0 (coda a destra) e questo indica che nella maggior parte dei mesi si hanno circa 175 vendite di media (quindi inferiore a 192 di Mean), ma ci sono alcuni mesi con un numero di vendite eccezionalmente alto.
    • La deviazione standard è molto alta e quindi indica grande variabilità (possibile incertezza).
    • Il range tra minimo e massimo è grande, il mese migliore ha registrato circa 5 volte le vendite del mese peggiore.
    • Kurtosi vicino a 3 indica una curtosi normale (mesocurtica), quindi la probabilità di valori estremi (le code) non è né esageratamente alta né bassa.
  • volume:
    • La media è più alta della mediana e l’assimetria è positiva ed anche più elevata rispetto a sales quindi nei mesi di picco, non solo si vende di più, ma si vendono anche case di valore mediamente più alto.
    • Kurtosis a 3.18 leggermente leptocurtica (maggiore di 3). A differenza delle sales, qui c’è una probabilità leggermente più alta di osservare valori estremi.
  • median_price:
    • La media è inferiore alla mediana e l’assimetria è negativa < 0 (-0.36) e questo indica che ci sono dei casi con prezzi mediani insolitamente bassi che abbassano la media, suggerisce che una o più città fanno scendere la media.
    • La variabilità è meno elevata e quindi si ha una maggiore stabiltà dei prezzi.
    • Kurtosis a 2.38 platicurtica (minore di 3), quindi più piatta di una normale e con code più sottili che indica che shock di prezzo estremi sono poco probabili.
  • listings:
    • Offerta tende a seguire la domanda, ecco perché è simile a sales.
  • months_inventory:
    • Media e mediana quasi identiche e la distribuzione è praticamente simmetrica (skewness = 0.04), quindi è l’indicatore più stabile e suggerisce che in media c’è più offerta che domanda.

3. Identificazione delle variabili con maggiore variabilità e asimmetria

summary_vars_index$CV <- (summary_vars_index$Dev.Std / summary_vars_index$Mean) * 100

kable(round(summary_vars_index, 2) %>% select(CV, Skewness) %>% arrange(desc(CV)))
CV Skewness
volume 53.71 0.88
listings 43.31 0.65
sales 41.42 0.72
months_inventory 25.06 0.04
median_price 17.08 -0.36

Commenti:

  1. volume è la variabile con il coefficiente di variazione (CV) più alto indicando quindi una forte variabilità nel volume totale di vendite.
  2. volume è la variabile con la distribuzione più asimmetrica (Skewness) (verso destra) e quindi indica una forte probabilità di outliers.

4. Creazione di classi per una variabile quantitativa

data$sales_class <- cut(data$sales, breaks = pretty(data$sales, n = 8), right = FALSE)
freq_sales_class <- data %>%
  count(sales_class, name = "Frequenza") %>%
  mutate(Percentuale = round(Frequenza / sum(Frequenza) * 100, 2))

kable(freq_sales_class, caption = "Distribuzione delle vendite mensili per classi")
Distribuzione delle vendite mensili per classi
sales_class Frequenza Percentuale
[50,100) 20 8.33
[100,150) 69 28.75
[150,200) 58 24.17
[200,250) 33 13.75
[250,300) 34 14.17
[300,350) 14 5.83
[350,400) 9 3.75
[400,450) 3 1.25

Commenti:

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

kable(data.frame(round(gini.index(data$sales_class), 2)), col.names = "Indice di Gini per le Classi di Vendite")
Indice di Gini per le Classi di Vendite
0.92

Commenti:

5. Calcolo della probabilità

total_rows <- nrow(data)

prob_beaumont          <- (sum(data$city  == "Beaumont") / total_rows) * 100
prob_july              <- (sum(data$month == 7) / total_rows) * 100
prob_december_2012     <- (nrow(filter(data, month == 12 & year == "2012")) / total_rows) * 100

prob_df <- data.frame(
  "Evento" = c("Una riga riporta la città Beaumont",
                           "Una riga riporta il mese di luglio",
                           "Una riga riporta il mese di Dicembre 2012"),
  "Probabilità" = c(prob_beaumont,
                    prob_july,
                    prob_december_2012)
)

kable(prob_df, digits = 2, caption = "Riepilogo Probabilità")
Riepilogo Probabilità
Evento Probabilità
Una riga riporta la città Beaumont 25.00
Una riga riporta il mese di luglio 8.33
Una riga riporta il mese di Dicembre 2012 1.67

6. Creazione di nuove variabili (prezzo medio immobili ed efficacia annunci attivi)

data <- data %>%
  mutate(
    average_price_per_sale = (volume * 1e6) / sales,
    listings_effectiveness    = sales / listings
  )

ggplot(data, aes(x = average_price_per_sale)) +
  geom_histogram(binwidth = 10000, fill = "cyan", color = "black") +
  scale_x_continuous(labels = dollar_format()) +
  labs(title = "Prezzo medio per vendita", x = "Prezzo medio", y = "Frequenza") +
  theme_minimal()

Commenti:

ggplot(data, aes(x = listings_effectiveness)) +
  geom_histogram(binwidth = 0.05, fill = "lightgreen", color = "black") +
  scale_x_continuous(
    labels = percent_format(accuracy = 1),
    breaks = seq(0, 1, by = 0.05)
  ) +
  labs(
    title = "Efficacia annunci (vendite/listings)",
    x = "Efficacia",
    y = "Frequenza"
  ) +
  theme_minimal()

Commenti:

7. Analisi condizionata

summary_stats <- data %>%
  group_by(city, year, month, date) %>%
  summarise(
    across(
      c(sales, volume, median_price, listings, months_inventory, average_price_per_sale, listings_effectiveness),
      list(media = ~mean(.x, na.rm = TRUE), sd = ~sd(.x, na.rm = TRUE)),
      .names = "{.col}_{.fn}"
    ),
    .groups = "drop"
  )

# Passaggio al formato long
summary_long <- summary_stats %>%
  pivot_longer(
    cols = -c(city, year, month, date),
    names_to = c("variabile", ".value"),
    names_pattern = "(.*)_(media|sd)$"
  )

variabili <- unique(summary_long$variabile)

for (v in variabili) {
  p <- ggplot(
    summary_long %>% filter(variabile == v),
    aes(x = date, y = media, color = city)
  ) +
    geom_line(size = 1.2) +
    labs(
      x = "Data (Anno-Mese)",
      y = v,
      color = "Città"
    ) +
    scale_x_date(date_labels = "%Y-%b", date_breaks = "3 months") +
    theme_minimal(base_size = 14) +
    theme(
      plot.title = element_text(size = 18, face = "bold"),
      axis.title = element_text(size = 13),
      axis.text = element_text(size = 11),
      legend.title = element_text(size = 12),
      legend.text = element_text(size = 12),
      axis.text.x = element_text(angle = 45, hjust = 1),
      legend.position = "bottom"
    )
  
  print(p)
}

8. Creazione di visualizzazioni con ggplot2

# boxplot prezzo mediano per città ed anno
ggplot(data, aes(x = factor(year), y = median_price, fill = city)) +
  geom_boxplot(outlier.size = 1) +
  labs(
    title = "Distribuzione del prezzo mediano per città ed anno",
    x = "Anno", y = "Prezzo mediano", fill = "Città"
  ) +
  theme_minimal()

Commenti:

# boxplot valore totale vendite per città ed anno 
ggplot(data, aes(x = city, y = sales, fill = factor(year))) +
  geom_boxplot(outlier.size = 1) +
  labs(
    title = "Distribuzione del valore totale delle vendite per città ed anno",
    x = "Città", y = "Totale vendite", fill = "Anno"
  ) +
  theme_minimal()

Commenti:

# grafico a barre sovrapposte vendite medie per mese e città
ggplot(data, aes(x = factor(month), y = sales, fill = city)) +
  geom_col(position = "stack") +
  labs(title = "Totale vendite per mese e città", x = "Mese", y = "Totale vendite") +
  theme_minimal()

Commenti:

# line chart andamento vendite in periodi storici differenti
ggplot(andamento_anni, aes(x = month, y = sales, color = factor(year))) +
  geom_line(size = 1.2) +
  geom_point(size = 2) +
  scale_x_continuous(breaks = 1:12, labels = month.abb) +
  labs(
    title = "Confronto andamento mensile delle vendite nei vari anni",
    x = "Mese",
    y = "Totale vendite",
    color = "Anno"
  ) +
  theme_minimal()

Commenti:

# plot normalizzato
ggplot(data, aes(x = month, y = sales, fill = city)) +
  geom_col(position = "fill") + 
  facet_wrap(~ year) +
  scale_x_continuous(breaks = seq(1, 12, by = 1)) +
  scale_y_continuous(labels = percent_format()) +
  labs(
    title = "Valore totale delle vendite (Normalizzato) per Città",
    x = "Mese",
    y = "Quota Percentuale sul Totale Vendite",
    fill = "Città"
  ) +
  theme_minimal()

# 9. Conclusioni

Commenti: