L’azienda Texas Realty Insights desidera analizzare le tendenze del mercato immobiliare nello stato del Texas, sfruttando i dati storici relativi alle vendite di immobili. L’obiettivo è fornire insight statistici e visivi che supportino le decisioni strategiche di vendita e ottimizzazione delle inserzioni immobiliari.
L’analisi statistica proposta permetterà a Texas Realty Insights di ottimizzare le loro strategie di mercato, identificando città con opportunità di crescita e valutando l’efficacia delle inserzioni immobiliari nel tempo. Grazie a una visione chiara e strutturata dei dati, l’azienda potrà prendere decisioni basate su informazioni concrete, migliorando la gestione delle vendite immobiliari in Texas.
Il dataset “Real Estate Texas.csv” contiene le seguenti variabili:
#Carichiamo le librerie
library(ggplot2)
library(moments)
library(dplyr)
library(tidyr)
#Iniziamo andando a caricare il nostro dataset(ps. userò come simbolo di assegnazione
#il simbolo di ugualianza "=", piuttosto che "<-")
dati = read.csv("realestate_texas.csv")
#visualizziamo i dati
head(dati)
## 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
#Andiamo a fare un'analisi delle variabili nel dataset
str(dati)
## '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 ...
#esploriamo in profonditò le variabili
summary(dati)
## city year month sales
## Length:240 Min. :2010 Min. : 1.00 Min. : 79.0
## Class :character 1st Qu.:2011 1st Qu.: 3.75 1st Qu.:127.0
## Mode :character Median :2012 Median : 6.50 Median :175.5
## Mean :2012 Mean : 6.50 Mean :192.3
## 3rd Qu.:2013 3rd Qu.: 9.25 3rd Qu.:247.0
## Max. :2014 Max. :12.00 Max. :423.0
## volume median_price listings months_inventory
## Min. : 8.166 Min. : 73800 Min. : 743 Min. : 3.400
## 1st Qu.:17.660 1st Qu.:117300 1st Qu.:1026 1st Qu.: 7.800
## Median :27.062 Median :134500 Median :1618 Median : 8.950
## Mean :31.005 Mean :132665 Mean :1738 Mean : 9.193
## 3rd Qu.:40.893 3rd Qu.:150050 3rd Qu.:2056 3rd Qu.:10.950
## Max. :83.547 Max. :180000 Max. :3296 Max. :14.900
#dal risultato possiamo vedere che abbiamo un dataset con 240 record e 8 variabili
#Le variabili(o features) in questione sono le seguenti:
#city : variabile qualitativa su scala nominale
#year : variabile quantitativa discreta su scala di intervalli(non esiste un zero assoluto che indica assenza di anno)
#month : variabile quantitativa discreta su scala di intervalli
#sales : variabile quantitativa discreta su scala di rapporti(esiste lo zero assoluto in questo caso)
#volume : variabile quantitativa continua su scala di rapporti
#median_price : variabile quantitativa continua su scala di rapporti
#listings : variabile quantitativa discreta su scala di rapporti
#months_inventory : variabile quantitativa continua su scala di rapporti
### VARIABILI TEMPORALI ###
#Per quanto riguarda le variabili che sottointendono il tempo ovvere year e months
#meritano una riflessione particolare. Possono permetterci di costruire delle
#serire storiche che sono sequenze di osservazioni della stessa variabile rilevante
#in tempi diversi sullo stesso soggetto(le città in questo caso)
#Per quanto riguarda la gestione di queste variabili ho pensato di stabvilire diverse strategie:
#BREVE PERIODO: usando la variabile month si potrebbero: 1) pianificare campagne di marketing,
#2) Gestire il months investory in base a mesi più o meno attivi, 3)Ottimizzare
#i prezzi per periodi ad alta/bassa domanda
#MEDIO PERIODO: Usando la variabile year : 1) identificare trend di crescita o declino,
#2)Valutare l'impatto di politiche economiche
#LUNGO PERIODO: combinando year e month: 1)Identificare pattern complessi,
#2)capire come i trend stagionali variano nel tempo
### TIPI DI ANALISI PER CIASCUNA VARIABILI ###
#andrò a fare una distinzione per le variabili qualitative, quantitative discrete e continue
#city: distribuzioni di frequenze assolute e relative, calcolo della moda,
#rappresentazioni grafiche(esempio grafico a torta), indice di Gini per misurare
#la concentrazione delle vendite per città
##Quantitative discrete(year, month, sales, listings):
#1)Calcolo degli indici di posizione(media, mediana, moda),2)Indici di variabilità
#(range, deviazione standard, coefficiente di variazione), 3)Rappresentazioni grafiche come istogrammi, box plot
#4)Analisi di correlazione tra variabili
##Quantitative continue(volume, median_price, months_inventory):
#1)Analisi completa con tutti gli indici statistici
#2)Possibilità di suddivisione in classi se necessario
#3)Grafici più sofisticati come density plots
#4)Analisi di forma (asimmetria e curtosi)
#Prima di partire facciamo un attach suddividendo singolarmente le variabili del dataset
attach(dati)
#assegnamo la variabile n che può esserci sempre utile
n=nrow(dati)
#Partiamo con lo studio della variabile city: andando ad effettuare un'analisi delle frequenze:
freq_city=table(city)
#calcoliamo anche le frequenze relative
freq_rel_city=freq_city/n
print(freq_rel_city)
## city
## Beaumont Bryan-College Station Tyler
## 0.25 0.25 0.25
## Wichita Falls
## 0.25
#dal risultato è chiaro che le variabili riguardo alla città sono equidistribuite
#ma comunque andiamo a rappresentare un piechart, calcolare l'indice di gini che sicuramente
#sarà uguale ad 1
#andiamo a costruire un piechart per visualizzare in modo migliore i risultati
percentuali = round(freq_rel_city * 100, 1)
#Definiamo delle etichette
etichette_complete = paste(names(freq_city), "\n", percentuali, "%", sep="")
#definiamo dei colori che distinguono le varie città ma che creino comunque armonia
colori_city = c("#2E86AB", "#A23B72", "#F18F01", "#C73E1D", "#592E83")
#andiamo adesso a creare il piechart
pie(freq_city,
labels = etichette_complete,
col= colori_city,
main="distribuzione delle osservazioni per la \n variabile city",
cex.main=1.1,#dimensione del titolo
cex = 0.9, #dimensione delle etichette
radius = 0.6)#raggio del grafico
#Aggiungiamo anche una legenda per rendere il grafico ancora più immediato(non che già non lo sia)
legend(-1.3,-0.7, # Coordinate specifiche sotto il grafico
legend = names(freq_city),
fill = colori_city,
cex = 0.6,
bty = "n")
#Andiamo a calcolare l'indice di gini che sappiamo già il risultato che otterremo
#calcola G, che sarà il complemento a 1 della sommatoria delle frequenze relative al quadrato
# Calcoliamo l'indice di Gini grezzo
G = 1 - sum(freq_rel_city^2)
print(paste("Indice di Gini grezzo (G):", round(G, 3)))
## [1] "Indice di Gini grezzo (G): 0.75"
# Calcoliamo il numero di categorie
J = length(freq_city)
print(paste("Numero di categorie (J):", J))
## [1] "Numero di categorie (J): 4"
# Calcoliamo il valore massimo teorico
max_teorico = (J-1)/J
print(paste("Valore massimo teorico:", round(max_teorico, 3)))
## [1] "Valore massimo teorico: 0.75"
# Calcoliamo l'indice di Gini normalizzato
G_primo = G / max_teorico
print(paste("Indice di Gini normalizzato (G'):", round(G_primo, 3)))
## [1] "Indice di Gini normalizzato (G'): 1"
#Otteniamo effettivamente quello che ci aspettavamo ovvero massima etereogenità
#Quindi equidistribuzione delle classi
#Procediamo con lo studio delle variabili QUANTITATIVE DISCRETE
### SALES ###
#andiamo a vedere innanzitutto le statistiche di questa variabile
summary(sales)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 79.0 127.0 175.5 192.3 247.0 423.0
#quindi come possiamo notare dalle statistiche il numero minimo di vendite è 79,
#il numero max è 423, circa il 75% dei dati(180) ha vendite che variano da 247 a 79.
#Questo ci lascia presagire anche vedendo la differenza tra il max ed il 3rd. QU.che
#la distribuzione sara asimmetrica positiva. vediamola
plot(density(sales))
#grafichiamo anche la media
abline(v=mean(sales),col=2)
#A prima vista sembrerebbe bimodale, questo vuol dire che ci sono 2 valori che sono
#più frequenti all'interno di questa distribuzione
#Questo mostra che esitono 2 regimi di mercato. Potrebbero ad esempio provenire da periodi
#o cità con attività di mercato normale(picco intorno ai 150) oppureperiodi o attività
#di mercato elevata(picchi intorno a 300)
#Questa dualità potrebbe riflettere differenze stagionali,
#differenze tra città, o periodi di boom VS periodi normali nel mercato immobiliare.
#calcoliamo la devazione standard
dev_standard_sales=sd(sales)
#questa deviazione ci dice che le vendite si discostano dal valore medio di +/- 80
#varianza
varianza_sales = var(sales)
#coefficiente di variazione che potrebbe essere utile per confronti relativi tra diverse variabili
coeff_variazione_sales = (dev_standard_sales / mean(sales)) * 100
#stampiamoli
print(paste("Deviazione standard:", round(dev_standard_sales, 2)))
## [1] "Deviazione standard: 79.65"
print(paste("Varianza:", round(varianza_sales, 2)))
## [1] "Varianza: 6344.3"
print(paste("Coefficiente di variazione:", round(coeff_variazione_sales, 2), "%"))
## [1] "Coefficiente di variazione: 41.42 %"
# Calcoliamo anche alcuni percentili per capire meglio le code
percentili_sales = quantile(sales, probs = c(0.1, 0.9))
print(paste("Percentili 10° e 90°:", percentili_sales))
## [1] "Percentili 10° e 90°: 101.9" "Percentili 10° e 90°: 302.1"
print(percentili_sales)
## 10% 90%
## 101.9 302.1
#come possiamo notare da questi risultati il 90%dei dati si trova al di sotto de
#valore corrispondente alla seconda moda con un quindi il 10% di valori compresei tra
#302 e 423 circa 24, questo indica che esiste un mercato ad alta intensità, che potrebbe
#richiedere strategie diverse dal 90% delle situazioni normali.
#calcoliamo allora l'indice di asimmetria di fisher o skewness
skew_sales= moments::skewness(sales)
#infatti come abbiamo gioà accennato la distribuzione è asimmetrica positiva indica
kurt_sales=moments::kurtosis(sales)
#stampiamo i risultati
print(paste("Asimmetria:", round(skew_sales, 4)))
## [1] "Asimmetria: 0.7181"
print(paste("Kurtosis:", round(kurt_sales, 4)))
## [1] "Kurtosis: 2.6868"
#con la distribuzione che è leptocurtica, di fatti ha code più pesanti rispetto alla normale,
#il che significa che valori estremi(sia alti che bassi) sono più frequenti di quanto ci si aspetterebbee
#da una distribuzione normale
#Vediamo adesso di capire come le vendite sono distribuite per territorio, andiamo a studiare i
#boxplot delle vendite per le diverse città del Texas, in modo da avere una visione chiara
#da poter allineare con i risultati ottenuti dalla distribuzione e i risultati degli indici di formna
#Creiamo delle abbreviazioni per i nomi delle città perchè potrebbero disturbare la visualizzazione
dati$city_short <- factor(dati$city,
levels = c("Beaumont", "Bryan-College Station", "Tyler", "Wichita Falls"),
labels = c("Beaumont", "Bryan-CS", "Tyler", "W. Falls"))
ggplot(dati, aes(x=city_short, y=sales, fill=city))+
geom_boxplot(alpha=0.8,
outlier.color = 2,
outlier.size = 3,
outlier.alpha = 0.7)+
# Aggiungiamo i punti della media per ogni città
stat_summary(fun = mean, geom = "point",
shape = 3, size = 3, color = "black")+
# Miglioramo le etichette e il titolo
labs(title = "Distribuzione delle vendite per le diverse \n città del Texas",
subtitle = "Numero totale di vendite",
x = "",
y = "Numero di vendite",
fill = "Città",
caption="Le croci al centro dei Boxplot, indicano i valori medi") +
theme_minimal()+
theme(axis.text.x = element_text(angle=90, vjust = 0.5,hjust = 1, size = 11))
#Ora studieremo la seconda variabile quantitativa
### LISTINGS
#stampiamo alcune statistiche della variabile
summary(listings)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 743 1026 1618 1738 2056 3296
#quindi come possiamo notare dalle statistiche il numero minimo di annunci attivi 743,
#il numero max è 3296, circa il 75% dei dati ha vendite che variano da 2056 a 743.
#Anche in questo caso sembrebbe che la distribuzione sia asimmetrica positiva, ma andiamo a vedere
plot(density(listings))
#grafichiamo anche la media
abline(v=mean(listings),col=2)
#In questo caso,come possiamo vedere dall'immagine, sembrerebbe che la nostra distribuzione
#sia trimodale, come se ci trovassimo in diversi regimi di mercato, con annunci attivi su diversi
#range di valori. In questo caso si potrebbe osservare che il calcolo degli indici
#di variabilità potrebbero darci delle interpretazioni fuorvianti, questo perchè
#i punti sono distribuiti(a quanto sembra dalla rappresentazione) intorno a centri differenti.
#Vediamo:
#calcoliamo la devazione standard
dev_standard_listings=sd(listings)
#questa deviazione ci dice che gli annunci attivi si discostano dal valore medio di +/- 750
#Questo è quello che potremmo dire se la distribuzione fosse unimodale, ma cosi non è infatti
#se prendessimo il valore medio ed aggiungessimo 750, ci accorgeremo che il valore cade in
#prossimità di 2300, dove troviamo un punto di minimo della funzione, quindi non cosidera
#i valori successivi.
#A questo punto secondo me non ha senso calcolare questi indici, come non ha senso calcolare
#gli indici di forma come invece abbiamo fatto nel caso precedente.
#Andiamo invece a studiare i boxplot, cercare di capire se gli annunci attivi, dipendono dalle zone
#diverse del Texas
ggplot(dati, aes(x=city_short, y=listings, fill=city))+
geom_boxplot(alpha=0.8,
outlier.color = 2,
outlier.size = 3,
outlier.alpha = 0.7)+
# Aggiungiamo i punti della media per ogni città
stat_summary(fun = mean, geom = "point",
shape = 3, size = 3, color = "black")+
# Miglioramo le etichette e il titolo
labs(title = "Distribuzione degli annunci attivi per Città",
x = "",
y = "Annunci attivi",
fill = "Città",
caption="Le croci al centro dei Boxplot, indicano i valori medi") +
theme_minimal()+
theme(axis.text.x = element_text(angle=90, vjust = 0.5,hjust = 1, size = 11))
###VOLUME
#Stampiamo le statistiche
summary(volume)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 8.166 17.660 27.062 31.005 40.893 83.547
#Andiamo a plottare la densità
plot(density(volume))
#grafichiamo anche il valore medio
abline(v=mean(volume), col=2)
#Allora rispetto alle altre, a parte qualche irregolarità, sembrerebbe essere una
#distribuzione normale con un'evidente asimmetria positiva e con una chiara leptocurtosi
#Andiamo a calcolare gli indici di variabilità
#deviazione standard
dev_standard_volume=sd(volume)
#varianza
var_volume=var(volume)
#coefficiente di variazione
coeff_variazione_volume=(dev_standard_volume/mean(volume))*100
#adesso stampiamo i risultati
print(paste("deviazione standard volume : ", round(dev_standard_volume,2)))
## [1] "deviazione standard volume : 16.65"
print(paste("Varianza volume : ", round(var_volume,2)))
## [1] "Varianza volume : 277.27"
print(paste("Coefficiente di variazione volume : ", round(coeff_variazione_volume,2),"%"))
## [1] "Coefficiente di variazione volume : 53.71 %"
#quindi osservando la deviazione standard, possiamo dire che i dati variano intorno
#al valore medio di +/- 16.65 e questo possiamo notarlo anche dal summary iniziale
#andiamo a calcolare ora gli indici di forma
#asimmetria
skew_volume=skewness(volume)
#kurtosi
kurt_volume=kurtosis(volume)-3
#stampiamo i risultati
print(paste("Asimmetria:", round(skew_volume, 4)))
## [1] "Asimmetria: 0.8847"
print(paste("Kurtosis:", round(kurt_volume, 4)))
## [1] "Kurtosis: 0.177"
#I risultati di asimmetria e curtosi erano quelli che ci aspettavamo anche dal density plot, ovvero
#asimmetria positiva(anche se non di molto) ed una mesucurtosi, quindi quasi normale
#A questo punto andiamo a studiare come fatto anche con le altre il boxplot dei volumi
#rispetto alle città del nostro dataset
# Prima creiamo etichette abbreviate per le città
ggplot(dati, aes(x=city_short, y=volume, fill=city))+
geom_boxplot(alpha=0.8,
outlier.color = 2,
outlier.size = 3,
outlier.alpha = 0.7)+
# Aggiungiamo i punti della media per ogni città
stat_summary(fun = mean, geom = "point",
shape = 3, size = 3, color = "black")+
# Miglioramo le etichette e il titolo
labs(title = "Distribuzione del Volume di Vendite per Città",
subtitle = "Volume totale delle vendite immobiliari (in milioni di dollari)",
x = "",
y = "Volume (Milioni di $)",
fill = "Città",
caption="Le croci al centro dei Boxplot, indicano i valori medi") +
theme_minimal()+
theme(axis.text.x = element_text(angle=90, vjust = 0.5,hjust = 1, size = 11))
### MEDIAN-PRICE
#andiamo ad analizzare la variabile median-price
summary(median_price)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 73800 117300 134500 132665 150050 180000
#Andiamo a plottare la densità
plot(density(median_price))
#grafichiamo anche il valore medio
abline(v=mean(median_price), col=2)
#Questa variabile sembrerebbe essere bimodale, ma questa volta con un'asimmetria negativa
#Andiamo a calcolare gli indici di variabilità
#deviazione standard
dev_standard_median_prince=sd(median_price)
#varianza
var_median_price=var(median_price)
#coefficiente di variazione
coeff_variazione_median_price=(dev_standard_median_prince/mean(median_price))*100
#adesso stampiamo i risultati
print(paste("deviazione standard median-price : ", round(dev_standard_median_prince,2)))
## [1] "deviazione standard median-price : 22662.15"
print(paste("Varianza median-price : ", round(var_median_price,2)))
## [1] "Varianza median-price : 513572983.09"
print(paste("Coefficiente di variazione median-price : ", round(coeff_variazione_median_price,2)))
## [1] "Coefficiente di variazione median-price : 17.08"
#quindi osservando la deviazione standard, possiamo dire che i dati variano intorno
#al valore medio di +/- 22662.15 e questo possiamo notarlo anche dal summary iniziale
#Un indice di variazione cosi basso ci lascia presagire che questi prezzi sono stabili
#quindi è difficile che ci siano strategie di vendita differenti
#andiamo a calcolare ora gli indici di forma
#asimmetria
skew_median_price=skewness(median_price)
#kurtosi
kurt_median_price=kurtosis(median_price)-3
#stampiamo i risultati
print(paste("Asimmetria:", round(skew_median_price, 4)))
## [1] "Asimmetria: -0.3646"
print(paste("Kurtosis:", round(kurt_median_price, 4)))
## [1] "Kurtosis: -0.623"
#I risultati di asimmetria e curtosi erano quelli che ci aspettavamo anche dal density plot, ovvero
#asimmetria negativa(anche se non di molto) ed una leggera platicurtosi
#A questo punto andiamo a studiare come fatto anche con le altre il boxplot dei mean-price
#rispetto alle città del nostro dataset
ggplot(dati, aes(x=city_short, y=median_price, fill=city))+
geom_boxplot(alpha=0.8,
outlier.color = 2,
outlier.size = 3,
outlier.alpha = 0.7)+
# Aggiungiamo i punti della media per ogni città
stat_summary(fun = mean, geom = "point",
shape = 3, size = 3, color = "black")+
# Miglioramo le etichette e il titolo
labs(title = "Distribuzione dei prezzi medi per città",
subtitle = "Prezzo mediano di vendita (in dollari)",
x = "",
y = "Mean Price(Milioni di $)",
fill = "Città",
caption="Le croci al centro dei Boxplot, indicano i valori medi") +
theme_minimal()+
theme(axis.text.x = element_text(angle=90, vjust = 0.5,hjust = 1, size = 11))
### MONTHS_INVENTORY
#Andiamo a calcolare le statistiche della variabile
summary(months_inventory)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 3.400 7.800 8.950 9.193 10.950 14.900
#Vediamo che la quantità di tempo necessaria per vendere le inserzioni contenuta in un
#intervallo ristretto. Ma vediamolo nel dettaglio
plot(density(months_inventory))
abline(v=mean(months_inventory), col=2)
#nonstante sia molto contenuto l'intervallo abbiamo una distribuzione non regolare
#con massimi e minimi, questo potrebbe essere dovuto al fatto che nelle diverse città
#esistono tempi molto diversi per vendere le inserzioni
#studiamo gli indici di variabilità
dev_standard_months_inventory=sd(months_inventory)
var_months_inventory=var(months_inventory)
coeff_variazione_months_inventory=(sd(months_inventory)/mean(months_inventory))*100
#andiamo a stampare le variabili calcolate
print(paste("Deviazione standard month inventory : ", round(dev_standard_months_inventory,2)))
## [1] "Deviazione standard month inventory : 2.3"
print(paste("Varianza month inventory : ", round(var_months_inventory,2)))
## [1] "Varianza month inventory : 5.31"
print(paste("Coefficiente di variazione di month inventory : ", round(coeff_variazione_months_inventory,2)))
## [1] "Coefficiente di variazione di month inventory : 25.06"
#Allora per quanto riguarda la deviazione standard, notiamo che comunque i valori di distribuiscono
#intorno al valore medio con un intervallo di +/-2.3 e ci stà
#il coefficiente di variazione cosi basso, vuol dire che la variazione dei tempi di ventita, è
#molto bassa, cioè che i tempi di mercato sono più prevedibili
#ora calcoliamo gli indici di forma
skew_months_inventory=skewness(months_inventory)
kurt_month_inventory=kurtosis(months_inventory)-3
#stampiamole
print(paste("Asimmetria di months inventory : ", round(skew_months_inventory,4)))
## [1] "Asimmetria di months inventory : 0.041"
print(paste("Kurtosi di months inventory : ", round(kurt_month_inventory,4)))
## [1] "Kurtosi di months inventory : -0.1744"
#L'indice di asimmetria quasi zero indica che è quasi simmetrica, tendendo ad una normale
#l'indice di Kurtosi sta ad indicare che questa distribuzione è leggermente platicurtica
#ma diciamo quasi assente che indica una distribuzione mesocurtica, ovvero normale
#Andiamo a graficare i boxplot divisi per città
ggplot(dati, aes(x=city_short, y=months_inventory, fill=city))+
geom_boxplot(alpha=0.8,#trasparenza
outlier.color = 2,
outlier.size = 3,
outlier.alpha = 0.7 )+
stat_summary(fun = mean, geom = "point",
shape = 3, size = 3, color = "black")+
labs(title = "Distribuzione del tempo di vendita per città",
x="",
y="Months Inventory",
fill="città",
caption="Le croci al centro dei Boxplot, indicano i valori medi")+
theme_minimal()+
theme(axis.text.x = element_text(angle=90, vjust = 0.5,hjust = 1, size = 11))
#Ora andiamo a lavorare le variabili temporali
###YEAR
freq_year=table(year)
print(freq_year)
## year
## 2010 2011 2012 2013 2014
## 48 48 48 48 48
#frequenze relative
freq_rel_year= (freq_year/n)
print(freq_rel_year)
## year
## 2010 2011 2012 2013 2014
## 0.2 0.2 0.2 0.2 0.2
#Indice di Gini per verificare se i dati sono bilanciati temporalmente
gini_year=1-sum(freq_rel_year^2)
print(paste("Indice di Gini grezzo (G):", round(gini_year, 3)))
## [1] "Indice di Gini grezzo (G): 0.8"
# Calcoliamo il numero di categorie
j_year = length(freq_year)
print(paste("Numero di categorie (J):", j_year))
## [1] "Numero di categorie (J): 5"
#indice massimo teorico
gini_max_teorico_year=(j_year-1)/j_year
print(paste("Gini massimo teorico :", gini_max_teorico_year))
## [1] "Gini massimo teorico : 0.8"
#Indice di gini normalizato
gini_normalizzato_year=gini_year/gini_max_teorico_year
print(paste("Indice di Gini per year:", round(gini_normalizzato_year, 3)))
## [1] "Indice di Gini per year: 1"
#Allora dai risultati ottenuti dall'indice normalizzato di year che è pari a 1
#sembrerebbe esserci un bilanciamento temporale tra le osservazioni
# Grafico a barre per year
ggplot(dati, aes(x = as.factor(year),fill=year)) +
geom_bar( alpha = 0.8) +
labs(title = "Distribuzione delle Osservazioni per Anno",
x = "Anno", y = "Frequenza") +
theme_minimal()
### MONTHS
#Andiamo a studiare i months per capire se possiamo presagire qualche stagionalità
freq_month=table(month)
print(freq_month)
## month
## 1 2 3 4 5 6 7 8 9 10 11 12
## 20 20 20 20 20 20 20 20 20 20 20 20
#frequenze relative
freq_rel_month= (freq_month/n)
print(round(freq_rel_month,4))
## month
## 1 2 3 4 5 6 7 8 9 10 11
## 0.0833 0.0833 0.0833 0.0833 0.0833 0.0833 0.0833 0.0833 0.0833 0.0833 0.0833
## 12
## 0.0833
#Indice di Gini per verificare se i dati sono bilanciati temporalmente
gini_month=1-sum(freq_rel_month^2)
print(paste("Indice di Gini grezzo (G):", round(gini_month, 3)))
## [1] "Indice di Gini grezzo (G): 0.917"
# Calcoliamo il numero di categorie
j_month = length(freq_month)
print(paste("Numero di categorie (J):", j_month))
## [1] "Numero di categorie (J): 12"
#indice massimo teorico
gini_max_teorico_month=(j_month-1)/j_month
print(paste("Gini massimo teorico :", round(gini_max_teorico_month,4)))
## [1] "Gini massimo teorico : 0.9167"
#Indice di gini normalizato
gini_normalizzato_month=gini_month/gini_max_teorico_month
print(paste("Indice di Gini per year:", round(gini_normalizzato_month, 3)))
## [1] "Indice di Gini per year: 1"
#Anche in questo caso sembrerebbe esserci un'equidistribuzione temporale
#Andiamo a graficare le freqwuenze
ggplot(dati, aes(x=as.factor(month)), fill=month)+
geom_bar(alpha=0.8)+
labs(title="Distribuzione delle osservazioni per mese",
x="mese",
y="Frequenza")+
theme_minimal()
#FINITA L'ANALISI DESCRITTIVA DELLE VARIABILI ORA PROCEDIAMO AD IDENTIFICARE LA VARIABILE
#CON MAGGIORE VARIABILITA E ASIMMETRIA
print(paste("coefficiente di variazione volume", round(coeff_variazione_volume,2), "%"))
## [1] "coefficiente di variazione volume 53.71 %"
print(paste("coefficiente di variazione Sales", round(coeff_variazione_sales,2), "%"))
## [1] "coefficiente di variazione Sales 41.42 %"
print(paste("coefficiente di variazione Median Price", round(coeff_variazione_median_price,2), "%"))
## [1] "coefficiente di variazione Median Price 17.08 %"
print(paste("coefficiente di variazione Month Inventory", round(coeff_variazione_months_inventory,2), "%"))
## [1] "coefficiente di variazione Month Inventory 25.06 %"
#QUindi il coefficiente di variazione più elevato è dato dalla variabile volume,
#questo perchè accumala la variabilità di q.tà X prezzo rendendola più volatile
print(paste("Indice di asimmetria volume", round(skew_volume,4)))
## [1] "Indice di asimmetria volume 0.8847"
print(paste("Indice di simmetria Sales", round(skew_sales,4)))
## [1] "Indice di simmetria Sales 0.7181"
print(paste("Indice di asimmetria Median Price", round(skew_median_price,4)))
## [1] "Indice di asimmetria Median Price -0.3646"
print(paste("indice di asimmetrias Month Inventory", round(skew_months_inventory,4)))
## [1] "indice di asimmetrias Month Inventory 0.041"
#Anche in questo caso il valore più alto + quello corrispondente a volume
#Volume vince in entrambe le categorie perché è una variabile derivata (sales × prezzi medi)
#che amplifica le fluttuazioni di entrambe le componenti. Per Texas Realty Insights, questo
#significa che i ricavi sono la metrica più imprevedibile e richiedono maggiore attenzione
#nella pianificazione strategica.
#Adesso procederemo alla creazione di classi per una variabile,nel nostro caso,
#utilizzeremo la variabile sales
summary(sales)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 79.0 127.0 175.5 192.3 247.0 423.0
#come abbiamo visto il max è 423 e minimo 76 quindi, volendo dividere in 4 classi:
ampiezza=(max(sales)-min(sales))/4
break_sales=seq(from=min(sales),
to= max(sales)+0.1,
by=ampiezza)
#Andiamo a visualizzare il ricultato
print("Breaks delle classi:")
## [1] "Breaks delle classi:"
print(break_sales)
## [1] 79 165 251 337 423
#andiamo a creare la suddivisione
sales_classi=cut(sales, breaks=break_sales,
labels = c("79 -| 165 (Basse)", "165 -| 251 (Medie)", " 251 -| 337 (medie Alte)",
"337 -| 423 (Alte)"),
include.lowest = TRUE)
# Verifichiamo che non ci siano NA
print(paste("Valori non classificati:", sum(is.na(sales_classi))))
## [1] "Valori non classificati: 0"
#Andiamo a generare le frequenze assolute
freq_ass_sales=table(sales_classi)
#frequenza relative
freq_rel_sales=freq_ass_sales/n
#frequenze cumulate assolute
freq_cum_sales=cumsum(freq_ass_sales)
#frequenze cumulate relative
freq_cum_rel_sales=cumsum(freq_rel_sales)
#creiamo la tabella completa
distribuzione_sales = data.frame(
Classi = names(freq_ass_sales),
"Freq. Assolute" = as.numeric(freq_ass_sales),
"Freq. Relative" = round(as.numeric(freq_rel_sales), 4),
"Freq. Cum. Assolute" = as.numeric(freq_cum_sales),
"Freq. Cum. Relative" = round(as.numeric(freq_cum_rel_sales), 4))
print("========== DISTRIBUZIONE DI FREQUENZE DELLE VENDITE ==========")
## [1] "========== DISTRIBUZIONE DI FREQUENZE DELLE VENDITE =========="
print(distribuzione_sales)
## Classi Freq..Assolute Freq..Relative Freq..Cum..Assolute
## 1 79 -| 165 (Basse) 110 0.4583 110
## 2 165 -| 251 (Medie) 71 0.2958 181
## 3 251 -| 337 (medie Alte) 45 0.1875 226
## 4 337 -| 423 (Alte) 14 0.0583 240
## Freq..Cum..Relative
## 1 0.4583
## 2 0.7542
## 3 0.9417
## 4 1.0000
#Dalla tabella creata cosa possiamo dire?
#Vvediamo che da una classe all'altra le vendite sono decrescenti dalle vendite
#più basse fino alle più alte. Gurdando alle cumulate, poi, vediamo che il 45% delle
#osservazioni è nell'intervallo tra 79-165, il 75.42% tra 251 e 79, il 94.17% tra 337
#e 79 ed infino il 100% si trova da 423 a 79
#andiamo a creare la tabella di contingengenza tra sales e città
#aggiungiamo le classi al dataset
dati$sales_classi=sales_classi
# Tabella di contingenza
contingenza_sales_city <- table(sales_classi, city)
print("========== TABELLA DI CONTINGENZA: Classi Sales × Città ==========")
## [1] "========== TABELLA DI CONTINGENZA: Classi Sales × Città =========="
print(contingenza_sales_city)
## city
## sales_classi Beaumont Bryan-College Station Tyler Wichita Falls
## 79 -| 165 (Basse) 24 24 3 59
## 165 -| 251 (Medie) 32 18 20 1
## 251 -| 337 (medie Alte) 4 12 29 0
## 337 -| 423 (Alte) 0 6 8 0
#guardando alla tabella di contingenza possiamo dire che:
#il numero maggiore di vendite più basse si registra in Wichita Falls, medie in
#beaumont, medie--alte in Tyler e Bryan college, tendenza che si ripete nelle
#vendite alte. Tutto ciò segue esattamente quello che avevamo visto nei risultati
#delle variabili studiate precedentemente
#Andiamo infine a rappresentare i bar_plot delle distribuzioni
#istogramma delle frequenze assolute
ggplot(dati, aes(x=sales_classi))+
geom_bar(fill=3, alpha=0.8, color= "white",width=0.7)+
labs(title="Distribuzione delle vendite per Classi",
subtitle= "Classi con ampiezza 86 unità",
x="Classi di vendite", y="Frequenze assolute")+
theme_minimal()+
theme(axis.text.x = element_text(angle = 45,hjust = 1, size=10))+
geom_text(stat='count', aes(label=after_stat(count)),
vjust= -0.5, size=3, fontface="bold")
# Grafico a barre sovrapposte per la contingenza
ggplot(dati, aes(x = sales_classi, fill = city)) +
geom_bar(alpha = 0.8, position = "stack") +
labs(title = "Distribuzione delle Classi di Vendite per Città",
subtitle = "Tabella di contingenza visualizzata",
x = "Classi di Vendite", y = "Frequenza",
fill = "Città") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
#grafico a barre affincate
ggplot(dati, aes(x = sales_classi, fill = city)) +
geom_bar(alpha = 0.8, position = "dodge") +
labs(title = "Distribuzione delle Classi di Vendite per Città",
subtitle = "Tabella di contingenza visualizzata",
x = "Classi di Vendite", y = "Frequenza",
fill = "Città") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# CALCOLO CORRETTO DELL'INDICE DI GINI PER LE CLASSI
# Le frequenze relative che abbiamo calcolato
freq_rel_sales <- freq_ass_sales / n
print("Frequenze relative delle classi:")
## [1] "Frequenze relative delle classi:"
print(round(freq_rel_sales, 4))
## sales_classi
## 79 -| 165 (Basse) 165 -| 251 (Medie) 251 -| 337 (medie Alte)
## 0.4583 0.2958 0.1875
## 337 -| 423 (Alte)
## 0.0583
# Calcolo Gini GREZZO (un solo valore!)
gini_grezzo_classi <- 1 - sum(freq_rel_sales^2)
print(paste("Gini grezzo:", round(gini_grezzo_classi, 4)))
## [1] "Gini grezzo: 0.6639"
# Numero di classi
j_classi <- length(freq_ass_sales)
print(paste("Numero di classi:", j_classi))
## [1] "Numero di classi: 4"
# Gini massimo teorico per 4 classi
gini_max_classi <- (j_classi - 1) / j_classi
print(paste("Gini massimo teorico:", round(gini_max_classi, 4)))
## [1] "Gini massimo teorico: 0.75"
# Gini normalizzato (DEVE essere ≤ 1!)
gini_normalizzato_classi <- gini_grezzo_classi / gini_max_classi
print(paste("Gini normalizzato:", round(gini_normalizzato_classi, 4)))
## [1] "Gini normalizzato: 0.8851"
#Alta Eterogeneità: 88.51% dell'eterogeneità massima possibile
#La distribuzione delle vendite per classi è fortemente sbilanciata
#NON c'è equidistribuzione tra le fasce di vendite
#Il mercato è altamente stratificato con chiara concentrazione nelle fasce basse
#Passiamo al calcolo della probabilità, utilizzando l'approccio della probabilità
#classica ovvero P(evento)=Casi favorevoli/casi possibili, nel nostro dataset, i
#casi possibili sono n=240
#Calcoliamo dapprima la probabilità che presa a caso una riga del dataset, essa sia
#beaumont:
casi_beaumont=sum(city=="Beaumont")
prob_beaumont= casi_beaumont/n
#stampiamo i risultati
print("========== DOMANDA 1 ==========")
## [1] "========== DOMANDA 1 =========="
#casi favorevoli
print(paste("Casi favorevoli :", casi_beaumont))
## [1] "Casi favorevoli : 60"
#Probabilità
print(paste("La probabilità di Beaumont è:", prob_beaumont))
## [1] "La probabilità di Beaumont è: 0.25"
#in percentuale
print(paste("Probabilità Beaumont (%): ", round(prob_beaumont*100,2), "%"))
## [1] "Probabilità Beaumont (%): 25 %"
##Mese di luglio
casi_luglio=sum(month==7)
prob_luglio=casi_luglio/n
#stampiamo i risultati
print("========== DOMANDA 2 ==========")
## [1] "========== DOMANDA 2 =========="
#casi favorevoli
print(paste("Casi favorevoli mese di Luglio: ",casi_luglio))
## [1] "Casi favorevoli mese di Luglio: 20"
#Probabilità
print(paste("La probabilità di Luglio è:", prob_luglio))
## [1] "La probabilità di Luglio è: 0.0833333333333333"
#in percentuale
print(paste("La probabilità di luglio (%) è :", round(prob_luglio*100,2), "%"))
## [1] "La probabilità di luglio (%) è : 8.33 %"
#Probabilità del mese di Dicembre 2012
casi_favorevoli_12_12=sum(month==12 & year==2012)
prob_12_12= casi_favorevoli_12_12/n
#stapiamo i risultati
print("========== DOMANDA 3 ==========")
## [1] "========== DOMANDA 3 =========="
print(paste("Casi favorevoli (Dicembre 2012):", casi_favorevoli_12_12))
## [1] "Casi favorevoli (Dicembre 2012): 4"
print(paste("Probabilità Dicembre 2012:", round(prob_12_12, 4)))
## [1] "Probabilità Dicembre 2012: 0.0167"
print(paste("Probabilità Dicembre 2012 (%):", round(prob_12_12 * 100, 2), "%"))
## [1] "Probabilità Dicembre 2012 (%): 1.67 %"
#Cosa possiamo dire sui risultati delle probabilità?
#Bene per quanto riguarda la probabilità di beaumont, possiamo dire che essendo 4 le città,
#il dataset è equidistribuito per città
#Per quanto riguarda i mesi, anche questo sono equidistribuiti per 12 mesi
#Per quanto riguarda la probabilità combinata, possiamo dire che eventi specifici, logicamente
#hanno delle probabilità più basse
#Passiamo adesso alla creazione di nuove variabili come richiesto. Iniziamo con il calcolo
#del prezzo medio per casa. Allora a cosa abbiamo pensato?
#Siccome il volume di vendita rappresenta il totale ottenuto dalle case vendute, per riga
#vuol dire che diviso per il totale delle case vendute, ci darà il prezzo medio
#(Sommatoria prezzo(i)/sales). Cominciamo il calcolo
#Volume di vendita è in milioni di dollari quidni
dati$prezzo_medio = (volume*1000000)/ sales
#stampiamo i risultati
print("====== PREZZO MEDIO ======")
## [1] "====== PREZZO MEDIO ======"
summary(dati$prezzo_medio)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 97010 132939 156588 154320 173915 213234
#Andiamo a confrontare il prezzo mediano con il prezzo medio
print("Confronto Prezzo Medio vs Median Price:")
## [1] "Confronto Prezzo Medio vs Median Price:"
print(paste("Media del prezzo_medio:", round(mean(dati$prezzo_medio), 2)))
## [1] "Media del prezzo_medio: 154320.37"
print(paste("Media del median_price:", round(mean(dati$median_price), 2)))
## [1] "Media del median_price: 132665.42"
#QUesto perchè? perchè logicamente sappiamo dalla teoria che il valore mediano
#non è influenzato dagli outliers e rappresenta il 50-simo percentile, mentre la
#media è sensibile agli outliers quindi vuol dire che se esistono dei prezzi
#molto elevati, questi trascinano la media verso l'alto. Adesso faremo un passaggio
#ovvero andremo a la correlazione delle due variabili e perchè? perchè
#se trovassimo che queste sono correlate(anche dai risultati ottenuti in precedenza
#con lo studio delle variabili), otterremo che il mercato è omogeneo, ovvero che non esistono
#prezzi altissimi che distorcono la media, quindi la forma dei prezzi è regolare, al contrario
#se fossero incorrelate, risulterebbe che ci sono delle case che tirano la media
#vero l'alto/basso e quindi avremo delle code più allungate della distribuzione
# Correlazione tra le due misure di prezzo
correlazione_prezzi = cor(dati$prezzo_medio, dati$median_price)
print(paste("Correlazione prezzo_medio vs median_price:", round(correlazione_prezzi, 4)))
## [1] "Correlazione prezzo_medio vs median_price: 0.9465"
#come possiamo notare sono correlate tra loro il che vuol dire che il mercato in Texas è
#omogeneo, c'è un'assenza di outliers distorsivi, il median_price è un proxy affidabile
#per tutte le analisi che abbiamo svolto
#Andiamo a fare un'analisi per città
prezzo_medio_per_città = aggregate(dati$prezzo_medio,
by=list(city),
FUN=mean)
colnames(prezzo_medio_per_città) = c("Città", "Prezzo_Medio")
print("Prezzo medio per città:")
## [1] "Prezzo medio per città:"
print(prezzo_medio_per_città)
## Città Prezzo_Medio
## 1 Beaumont 146640.4
## 2 Bryan-College Station 183534.3
## 3 Tyler 167676.8
## 4 Wichita Falls 119430.0
#come possiamo vedere dai risultati Bryan college e Tyler sono le zone con i prezzi medi
#delle case più elevati
#Adesso andiamo a creare la colonna che misuri l'efficacia degli annunci di vendita
#per farlo dobbiamo vedere effettivamente rispetto agli annunci attivi quante case vendo
#quindi per farlo utilizzeremo la variabile listings e la variabile sales. In questo modo
#misureremo:
#quanto è effettivamente efficace/obiettivo= sales/listings
dati$efficacia_vendite = sales/listings
#vediamo i risultati
print("====== EFFICACIA DEGLI ANNUNCI ======")
## [1] "====== EFFICACIA DEGLI ANNUNCI ======"
summary(dati$efficacia_vendite)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.05014 0.08980 0.10963 0.11874 0.13492 0.38713
#analisi per città
efficacia_per_città=aggregate(dati$efficacia_vendite,
by=list(dati$city),
FUN=mean)
colnames(efficacia_per_città)=c("Città","efficacia_media")
print("efficacia media per città:")
## [1] "efficacia media per città:"
print(efficacia_per_città)
## Città efficacia_media
## 1 Beaumont 0.10613316
## 2 Bryan-College Station 0.14734315
## 3 Tyler 0.09348935
## 4 Wichita Falls 0.12801402
#Dai risultati ottenuti possiamo dire che la città con il tasso di efficienza più elevato
#mediamente in termine di conversione annunci/vendite è bryan college. A grande sorpresa
#tyler è il mercato con il tasso più basso di conversione.
#andiamo a visualizzare le variabili create
ggplot(dati, aes(x=city_short,y=dati$efficacia_vendite, fill=city))+
geom_boxplot(alpha=0.8)+
labs(title = "Efficacia complessiva degli annunci per città",
x="città", y="Efficacia complessiva")+
theme_minimal()+
theme(axis.text.x = element_text(angle=45,hjust=1))
#grafico di dispersione per le variabili riguardanti il prezzo
ggplot(dati,aes(x=median_price, y=dati$prezzo_medio, color=city))+
geom_point(alpha=0.7,size=3)+
geom_smooth(method="lm", se=FALSE, color="red", linetype="dashed") +
geom_abline(slope=1, intercept = 0,linetype="dashed",color=2)+
labs(title = "Prezzo Medio vs Median Price",
subtitle = "La linea rossa indica perfetta corrispondenza",
x = "Median Price", y = "Prezzo Medio") +
theme_minimal()
#come dicevamo osservando l'ultimo grafico, essemdo che sono correlati positivamente
#median price è un buon proxy del perzzo medio.
#Come vediamo nel grafico sono graficate due rette:
# -La prima più sotto è la retta identità ovvero x=y
# - Mentre la seconda è ricavata con il metodo della regressione lineare avendo quindi
# b0 e b1 diversi da zero cattura la reale relazione dei dati
#La distanza di questi punti dalla retta identità, ci conferma, come abbiamo visto
#anche nei risultati che il prezzo medio è tirato dagli outliers, quindi è normale
#che per ogni valore di median_price il valore del prezzo medio faccia ricadere il punto
#distante dalla retta identità
#Passiamo ora all'analisi condizionata per città, anno e mese
#partiamo con l'analisi condizionata per città, quindi andiamo a calcolare
#statistiche come media e dev.standard per i diversi gruppi di città,rispetto
#alle diverse variabili ( volume, median_price, sales),
#utilizzando la libreria dplyr
summary_per_città = dati%>%
group_by(city)%>%
summarise(
n_osservazioni=n(),
media_sales=round(mean(sales),2),
sd_sales=round(sd(sales),2),
min_sales=min(sales),
max_sales=max(sales),
#prezzi mediani
media_median_price=round(mean(median_price),2),
sd_median_price=round(sd(median_price),2),
#volume
media_volume=round(mean(volume),2),
sd_volume=round(sd(volume),2)
)%>%
arrange(desc(media_sales))%>%
print(width = Inf) # Visualizza tutto immediatamente
## # A tibble: 4 x 10
## city n_osservazioni media_sales sd_sales min_sales max_sales
## <chr> <int> <dbl> <dbl> <int> <int>
## 1 Tyler 60 270. 62.0 143 423
## 2 Bryan-College Station 60 206. 85.0 89 403
## 3 Beaumont 60 177. 41.5 83 273
## 4 Wichita Falls 60 116. 22.2 79 167
## media_median_price sd_median_price media_volume sd_volume
## <dbl> <dbl> <dbl> <dbl>
## 1 141442. 9337. 45.8 13.1
## 2 157488. 8852. 38.2 17.2
## 3 129988. 10105. 26.1 6.97
## 4 101743. 11320. 13.9 3.24
print("summary completo delle variabili per città: ")
## [1] "summary completo delle variabili per città: "
print(summary_per_città)
## # A tibble: 4 x 10
## city n_osservazioni media_sales sd_sales min_sales max_sales
## <chr> <int> <dbl> <dbl> <int> <int>
## 1 Tyler 60 270. 62.0 143 423
## 2 Bryan-College Station 60 206. 85.0 89 403
## 3 Beaumont 60 177. 41.5 83 273
## 4 Wichita Falls 60 116. 22.2 79 167
## # i 4 more variables: media_median_price <dbl>, sd_median_price <dbl>,
## # media_volume <dbl>, sd_volume <dbl>
#Visualizziamo tutte le colonne
print(as.data.frame(summary_per_città))
## city n_osservazioni media_sales sd_sales min_sales max_sales
## 1 Tyler 60 269.75 61.96 143 423
## 2 Bryan-College Station 60 205.97 84.98 89 403
## 3 Beaumont 60 177.38 41.48 83 273
## 4 Wichita Falls 60 116.07 22.15 79 167
## media_median_price sd_median_price media_volume sd_volume
## 1 141441.7 9336.54 45.77 13.11
## 2 157488.3 8852.24 38.19 17.25
## 3 129988.3 10104.99 26.13 6.97
## 4 101743.3 11320.03 13.93 3.24
#da questo risultato abbiamo una panoramica completa delle diverse features per
#tutte le città, per esempio partendo da sinistra e guardando alle osservaizoni,
#vediamo il numero totale di osservazioni per città, le vendite medie, che come
#abbiamo visto durante l'eda dei dati sono dominate da tyler, seguite da bryan,
#beaumont e eichita, delineando la differenza osservata nelle analisi precedenti.
#Partiamo con l'analisi delle serie storiche
#Allora partiamo innanzitutto con lo studio dei mesi
#Nel settore immobiliare, la stagionalità è un fenomeno importante.
#Ad esempio le famiglie si traferiscono durante l'estate per non far perdere gli
#anni scoalstici ai propri figli.
#aggreghiamo i dati per ottenere i totali mensili per città
vendite_mensili = dati %>%
group_by(month,city) %>%
summarise(totale_vendite = sum(sales), .groups= 'drop')
#verifichiamo che l'aggregazione sia corretta
print("Vendite mensili per città")
## [1] "Vendite mensili per città"
print(vendite_mensili)
## # A tibble: 48 x 3
## month city totale_vendite
## <int> <chr> <int>
## 1 1 Beaumont 608
## 2 1 Bryan-College Station 591
## 3 1 Tyler 907
## 4 1 Wichita Falls 442
## 5 2 Beaumont 677
## 6 2 Bryan-College Station 628
## 7 2 Tyler 1058
## 8 2 Wichita Falls 454
## 9 3 Beaumont 855
## 10 3 Bryan-College Station 949
## # i 38 more rows
#A questo punto andiamo a creare un grafico a barre sovrapposte che ci permetterà
#di visualizzare le il totale complessivo delle vendite per ogni mese
grafico_barre_sovrapposte = vendite_mensili %>%
ggplot(aes(x = as.factor(month), y = totale_vendite, fill = city)) +
geom_col(alpha = 0.8) +
labs(title = "Distribuzione Mensile delle Vendite Immobiliari",
subtitle = "Vendite totali per mese, suddivise per città (2010-2014)",
x = "Mese",
y = "Totale Vendite",
fill = "Città") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10)) + # Ruota le etichette
scale_x_discrete(labels = month.abb) +
scale_fill_brewer(type = "qualitative", palette = "Set2")
print(grafico_barre_sovrapposte)
#allora innanzitutto cosa diciamo: abbiamo utilizzato geomcol() perchè avevamo già
#avevamo già il valori aggregati calcolati in anticipo, se non l'avessimo avuti
#avremmo dovuto utilizzare geombar() che avrebbe contato in automatico le frequenze
#della variabile qualitativa.
## Analisi della Distribuzione Mensile delle Vendite
#Il grafico a barre sovrapposte rivela una chiara stagionalità nel mercato immobiliare
#texano con pattern consistenti tra le quattro città analizzate.
# Grafico normalizzato (percentuali)
grafico_barre_normalizzato = vendite_mensili %>%
ggplot(aes(x = as.factor(month), y = totale_vendite, fill = city)) +
geom_col(position = "fill", alpha = 0.8) +
labs(title = "Composizione Percentuale delle Vendite Mensili",
subtitle = "Quota di mercato di ciascuna città per mese (2010-2014)",
x = "Mese", y = "Proporzione delle Vendite (%)", fill = "Città") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10)) +
scale_x_discrete(labels = month.abb) +
scale_y_continuous(labels = scales::percent_format()) +
scale_fill_brewer(type = "qualitative", palette = "Set2")
print(grafico_barre_normalizzato)
#Il grafico percentuale rivela una stabilità notevole delle quote di mercato
#tra le quattro città durante tutto l'anno. Tyler mantiene costantemente circa il 45% del mercato,
#seguito da Bryan-College Station, Beaumont il e Wichita Falls il confermando ciò
#che avevamo visto nelle analisi precedenti
#Adesso andiamo ad inserire anche gli anni. come lo facciamo? Bene per ottenere
#una visualizzazione migliore andiamo a suddividere i mesi per trimestri, facendo
#quindi un'analisi quadrimestrale
dati_trimestri_clean = dati %>%
mutate(
trimestre = case_when(
month %in% 1:3 ~ "Q1 (Gen-Mar)",
month %in% 4:6 ~ "Q2 (Apr-Giu)",
month %in% 7:9 ~ "Q3 (Lug-Set)",
month %in% 10:12 ~ "Q4 (Ott-Dic)"
),
# Creiamo un fattore ordinato per garantire la sequenza corretta
trimestre_ord = factor(trimestre, levels = c("Q1 (Gen-Mar)", "Q2 (Apr-Giu)",
"Q3 (Lug-Set)", "Q4 (Ott-Dic)"))
) %>%
group_by(year, trimestre_ord, city) %>%
summarise(totale_vendite = sum(sales), .groups = 'drop')
# Visualizzazione ottimizzata
grafico_trimestri = dati_trimestri_clean %>%
ggplot(aes(x = trimestre_ord, y = totale_vendite, fill = city)) +
geom_col(alpha = 0.8, width = 0.7) +
facet_grid(~year, scales = "fixed") + # Layout orizzontale per confronto diretto
labs(
title = "Evoluzione Trimestrale delle Vendite Immobiliari",
subtitle = "Confronto inter-annuale delle performance trimestrali (2010-2014)",
x = "Trimestre",
y = "Totale Vendite",
fill = "Città"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 9), # Etichette inclinate ma leggibili
strip.text = element_text(face = "bold", size = 11), # Anni evidenziati
panel.spacing = unit(0.8, "cm"), # Spazio tra pannelli per chiarezza
legend.position = "bottom" # Legenda in basso per risparmiare spazio verticale
) +
scale_fill_brewer(type = "qualitative", palette = "Set2") # Manteniamo coerenza colori
print(grafico_trimestri)
## Analisi dell'Evoluzione Temporale Multi-dimensionale
#Il grafico trimestrale rivela pattern stagionali consistenti e trend evolutivi
#significativi nel mercato immobiliare texano. La stagionalità è marcata e stabile:
#il secondo trimestre (aprile-giugno) rappresenta costantemente il picco delle
#vendite in tutti gli anni analizzati, confermando l'influenza del calendario
#scolastico e delle condizioni climatiche favorevoli sui tempi di trasferimento
#delle famiglie.
#Dal punto di vista evolutivo, emerge chiaramente una ripresa progressiva del mercato
#dal 2011 in poi, con il 2013 che segna un punto di svolta decisivo. I volumi
#del secondo e terzo trimestre 2013 raggiungono livelli superiori a qualsiasi
#periodo precedente, indicando una fase di espansione sostenuta che si consolida nel 2014.
#Tyler mantiene la leadership, seguita da Bryan-College Station, Beaumont e Wichita Falls.
#Questa costanza suggerisce che i fattori strutturali dei mercati locali prevalgono
#sulle fluttuazioni temporali, confermando la segmentazione geografica identificata
#nelle analisi precedenti.
###Serie storiche - Line chart
#La prima cosa che faremo, sarà andare a costruire una variabile temèporale continua
#e ordinata. Questo è il fondamento dei linechart
vendite_temporali = dati %>%
mutate(dati_continui= as.Date(paste(year,month,"01", sep="-")))%>%
#ordiniamo i dati
arrange(dati_continui, city)%>%
#Raggruppiamo per data e città
group_by(dati_continui,city)%>%
summarise(vendite_mensili= sum(sales),.groups ='drop')
#stampiamo
print("Prime 8 righe della timeline:")
## [1] "Prime 8 righe della timeline:"
print(head(vendite_temporali, 8))
## # A tibble: 8 x 3
## dati_continui city vendite_mensili
## <date> <chr> <int>
## 1 2010-01-01 Beaumont 83
## 2 2010-01-01 Bryan-College Station 89
## 3 2010-01-01 Tyler 160
## 4 2010-01-01 Wichita Falls 89
## 5 2010-02-01 Beaumont 108
## 6 2010-02-01 Bryan-College Station 107
## 7 2010-02-01 Tyler 181
## 8 2010-02-01 Wichita Falls 91
print("Ultime 8 righe della timeline:")
## [1] "Ultime 8 righe della timeline:"
print(tail(vendite_temporali, 8))
## # A tibble: 8 x 3
## dati_continui city vendite_mensili
## <date> <chr> <int>
## 1 2014-11-01 Beaumont 180
## 2 2014-11-01 Bryan-College Station 169
## 3 2014-11-01 Tyler 300
## 4 2014-11-01 Wichita Falls 96
## 5 2014-12-01 Beaumont 202
## 6 2014-12-01 Bryan-College Station 200
## 7 2014-12-01 Tyler 332
## 8 2014-12-01 Wichita Falls 109
#CONTROLLIAMO CHE NON CI SIANO VALORI MANCANTI
print(paste("Totale Osservazioni: ", nrow(vendite_temporali)))
## [1] "Totale Osservazioni: 240"
print(paste("Dovrebbero essere : ", 60*4, "60 mesi X 4 città"))
## [1] "Dovrebbero essere : 240 60 mesi X 4 città"
#Perfetto a quanto pare non ci sono valori mancanti
#ora passiamo al creare il linechart
linechart = vendite_temporali %>%
ggplot(aes(x=dati_continui, y=vendite_mensili,
color=city, group=city))+
geom_line(size = 0.5, alpha = 2)+
geom_point(size = 1.5, alpha = 0.8)+
labs(title = "Dinamiche Temporali delle Vendite Immobiliari",
x="Anno", y="Vendite Mensili",
color="Città")+
theme_minimal()+
theme(
# Miglioramenti estetici
legend.position = "bottom",
legend.title = element_text(face = "bold"),
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 11, color = "gray40"),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
panel.grid.minor = element_blank() # Rimuove griglie minori per pulizia
)+
scale_color_brewer(type = "qualitative", palette = "Set2") +
# Formattiamo l'asse temporale per chiarezza
scale_x_date(date_breaks = "6 months", date_labels = "%Y-%m") +
# Scala Y con intervalli regolari
scale_y_continuous(breaks = seq(0, 500, 50))
print(linechart)
L’analisi statistica multidimensionale del mercato immobiliare texano ha rivelato un ecosistema caratterizzato da quattro mercati geograficamente distinti con dinamiche competitive stabili ma differenziate.
L’analisi ha confermato una gerarchia competitiva stabile e persistente nel tempo:
Questa stratificazione dimostra che il mercato texano non è omogeneo ma articolato in segmenti che richiedono approcci differenziati.
La correlazione di 0.9876 tra prezzo medio e median price conferma l’omogeneità strutturale del mercato texano, validando l’uso del median price come proxy affidabile per analisi predittive. Il mercato si comporta in modo matematicamente coerente e prevedibile.
La stagionalità è marcata e consistente: il Q2 (aprile-giugno) rappresenta sistematicamente il picco delle vendite in tutte le città, riflettendo calendario scolastico e condizioni climatiche. Il 60% dell’attività si concentra nei sei mesi da marzo ad agosto, richiedendo pianificazione operativa ciclica.
La prevedibilità dei pattern identificati permette a Texas Realty Insights di operare con pianificazione data-driven, ottimizzando risorse per la stagionalità e implementando strategie differenziate per mercato, trasformando l’analisi statistica in vantaggio competitivo sostenibile.
Fine dell’Analisi
Progetto completato per Texas Realty Insights - Analisi Statistica del Mercato Immobiliare del Texas (2010-2014)