#install.packages("readxl")
#devtools::install_github("dgonxalex80/paqueteMETODOS")
#library(paqueteMETODOS)
library(readxl)
library(knitr)
library(ggplot2)
library(gridExtra)
library(lmtest)
library(GGally)
library(dplyr)
library(mice)
library(psych)
library(wordcloud)
library(factoextra)
library(fpc)
library(FactoMineR)
library(ca)
library(cluster)
library(dbscan)
library(mclust)
library(gplots)
ruta_del_archivo <- "/Users/Julian/Documents/Material Estudio/MDS Javeriana/Sem 2/[006]EstadTomaDeci/vivienda.xlsx"
data <- read_excel(ruta_del_archivo)
El presente documento constituye un análisis de las ofertas dinmobiliarias urbanas. El objetivo fundamental es realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.
Se nos pide, un analisis integral y multidimensional empleando entre diversas tecnicsas, las siguientes:
Análisis de Componentes Principales
Análisis de Conglomerados
Análisis de Correspondencia: Examinando la relación entre las variables categóricas (tipo de vivienda, zona y barrio)
A continuación veremos los primeros registros de nuestra base de datos
kable(do.call(data.frame, head(data)),
format = "markdown",
caption = "**Tabla de los primeros registros base vivienda**",
align = "c", escape = FALSE,
row.names = FALSE,
booktabs = TRUE)
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1147 | Zona Oriente | NA | 3 | 250 | 70 | 1 | 3 | 6 | Casa | 20 de julio | -76.51168 | 3.43382 |
| 1169 | Zona Oriente | NA | 3 | 320 | 120 | 1 | 2 | 3 | Casa | 20 de julio | -76.51237 | 3.43369 |
| 1350 | Zona Oriente | NA | 3 | 350 | 220 | 2 | 2 | 4 | Casa | 20 de julio | -76.51537 | 3.43566 |
| 5992 | Zona Sur | 02 | 4 | 400 | 280 | 3 | 5 | 3 | Casa | 3 de julio | -76.54000 | 3.43500 |
| 1212 | Zona Norte | 01 | 5 | 260 | 90 | 1 | 2 | 3 | Apartamento | acopi | -76.51350 | 3.45891 |
| 1724 | Zona Norte | 01 | 5 | 240 | 87 | 1 | 3 | 3 | Apartamento | acopi | -76.51700 | 3.36971 |
Observamos que tenemos 12 campos que constituyen los campos de la base, alguno de ellos categoricos y otros númericos.
Antes de dar inicio a los puntos que nos facilitaron para el analisis, veamos como vienen constituidos cada uno de los campos de nuestra base de datos:
resumen_data <- list(
num_filas = nrow(data),
num_col= ncol(data),
vacios_totales = sum(is.na(data)),
porc_vacios = sprintf('%.2f%%',sum(is.na(data))/(nrow(data)*ncol(data))*100),
filas_con_mas_un_vacio = sum(apply(is.na(data), 1, any)),
filas_all_vacios = sum(apply(is.na(data), 1, all)),
col_all_vacios = sum(colSums(is.na(data)) == nrow(data)),
filas_duplicadas= nrow(data[duplicated(data), ])
)
kable(do.call(data.frame, resumen_data),
format = "markdown",
caption = "**Tabla n°1 Resumen de dataset antes de tratamientos**",
align = "c", escape = FALSE,
row.names = FALSE,
booktabs = TRUE)
| num_filas | num_col | vacios_totales | porc_vacios | filas_con_mas_un_vacio | filas_all_vacios | col_all_vacios | filas_duplicadas |
|---|---|---|---|---|---|---|---|
| 8322 | 13 | 4275 | 3.95% | 3514 | 2 | 0 | 1 |
Se ha identificado la existencia de dos filas vacías y nueve registros duplicados en el conjunto de datos. Estos elementos, por varias razones, no parecen ser esenciales para los objetivos de este estudio:
Concluyendo que debemos hacer algun tipo de tratamiento de los faltantes antes de iniciar el modelado
col_originales = names(data)
str(data)
## tibble [8,322 × 13] (S3: tbl_df/tbl/data.frame)
## $ id : num [1:8322] 1147 1169 1350 5992 1212 ...
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ piso : chr [1:8322] NA NA NA "02" ...
## $ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
## $ longitud : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
Se observa 4 campos categoricos y resto de campos numericos. Dentro los campos categoricos observamos que:
campo piso se considera un campo categorico ordinal, dado que existe una jerarquia entre pisos.
campo ‘Tipo’ se asemeja a un campo binomial
grafico_vacios <-md.pattern(data, rotate.names = TRUE)
Se observa que los siguientes registros que deseamos eliminar:
Nota: Tanto ‘parqueadores’ como ‘piso’ son campos que no se sabria como inputar(completar). Parcialmente el piso en una casa podria ser valor 1, pero existen cassa de mas de 1 piso, y podria ser referencia a dicho atributo. Se decide remover los campos del analisis, dado que su proporción es mas del 30% de faltantes en estos dos campos. Nota 2 El campo ‘id’ se trata de un número concecutivo que no aporta valor a nuestro analisis. Nota 3 Los campos ‘latitud’ y ‘longitud’ al ser caracteristicas espaciales, puede ser variables de dificil interpretación, se puede considerar mas adelante si se desean incluir en el analisis.
#REMOVIENDO FILAS FALTANTES
#Se remueve los registros llenos de campos sin datos
data <- data[rowSums(is.na(data)) < ncol(data), ]
#Se remueve los duplicados
data <- data[!duplicated(data), ]
#Se remueve el campo que solo tiene el precio (se saca por id, es mas facil de identificar)
data <- data[!is.na(data$id), ]
#REMOVIENDO COLUMNAS
data <- select(data, -id, -parqueaderos,-piso)
resumen_data <- list(
num_filas = nrow(data),
num_col= ncol(data),
vacios_totales = sum(is.na(data)), #sum(is.na(data), na.rm = TRUE),
porc_vacios = sprintf("%.2f%%",sum(is.na(data))/(nrow(data)*ncol(data))*100),
filas_con_mas_un_vacio = sum(apply(is.na(data), 1, any)),
filas_all_vacios = sum(apply(is.na(data), 1, all)),
col_all_vacios = sum(colSums(is.na(data)) == nrow(data)),
filas_duplicadas= nrow(data[duplicated(data), ])
)
kable(do.call(data.frame, resumen_data),
format = "markdown",
caption = "**Tabla n°2 Resumen de dataset con tratamientos y registros mayormente completados**",
align = "c", escape = FALSE,
row.names = FALSE,
booktabs = TRUE)
| num_filas | num_col | vacios_totales | porc_vacios | filas_con_mas_un_vacio | filas_all_vacios | col_all_vacios | filas_duplicadas |
|---|---|---|---|---|---|---|---|
| 8319 | 10 | 0 | 0.00% | 0 | 0 | 0 | 87 |
Se obtiene la Tabla n°2 Resumen de dataset con tratamientos donde se aprecia el total de registros con el que se procederan a realizar las siguientes fases.
Se inicia con una clasificación de cada uno de los campos entre cuantitativos y cualitativos
resumen_df <- as.data.frame.matrix(summary(data))
resumen_df <- t(resumen_df)
types <- sapply(data, class)
resumen_cuantitativa <- resumen_df[types %in% c("numeric", "integer"), ]
resumen_cuantitativa <- rownames(resumen_cuantitativa)
resumen_cuantitativa <- gsub(" ", "", resumen_cuantitativa) # para quitar los espacios en blanco
resumen_cualitativa <- resumen_df[!(types %in% c("numeric", "integer")), ]
resumen_cualitativa <- rownames(resumen_cualitativa)
resumen_cualitativa <- gsub(" ", "", resumen_cualitativa) # para quitar los espacios en blanco
Algunas variables directamente no se ven representantes para nuestro estudio, como es el caso de: ‘id’
col <- 'estrato'
frecuencias <- table(data$estrato)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))
ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
geom_bar(stat = "identity", fill = "#0576FF") +
geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.
describe(data$estrato)
## vars n mean sd median trimmed mad min max range skew kurtosis se
## X1 1 8319 4.63 1.03 5 4.67 1.48 3 6 3 -0.18 -1.11 0.01
Vemos que no estan estrato 1 y 2,(nicho de mercado nuestro son cliente con poder adquisitivo medio alto). El gráfico indica que los registros estan algo sesgados a la derecha, aunque no esta muy concentrado y con una media en alrededor del estrato 4.63 y desviacion estandar de 1.03.
col<-'preciom'
ggplot(data = data, aes(x = !!as.name(col))) +
geom_histogram(bins = 30, color = "#515354", fill = "#0576FF") +
geom_vline(aes(xintercept = mean(!!as.name(col))), color = "#FF0576", linetype = "dashed", size = 1) +
geom_vline(aes(xintercept = median(!!as.name(col))), color = "#50AC05", linetype = "dashed", size = 1) +
theme_bw() +
labs(title = paste('Histograma de', col), x = col, y = "Frecuencia")+
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
describe(data$preciom)
## vars n mean sd median trimmed mad min max range skew kurtosis
## X1 1 8319 433.9 328.67 330 374.48 207.56 58 1999 1941 1.85 3.67
## se
## X1 3.6
El gráfico indica que los registros estan sesgados a la izquierda, con una media en 433.9 millones y desviación de 328.67 millones muy alta para ser el campo mas representativo para el estudio. Vemos tambien, una kurtosis de 3.67 muy por arriba del 0; indicandonos que tenemos un precio muy concentrado y punteagudo en el grafico.
col<-'areaconst'
ggplot(data = data, aes(x = !!as.name(col))) +
geom_histogram(bins = 30, color = "#515354", fill = "#0576FF") +
geom_vline(aes(xintercept = mean(!!as.name(col))), color = "#FF0576", linetype = "dashed", size = 1) +
geom_vline(aes(xintercept = median(!!as.name(col))), color = "#50AC05", linetype = "dashed", size = 1) +
theme_bw() +
labs(title = paste('Histograma de', col), x = col, y = "Frecuencia")+
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
describe(data$areaconst)
## vars n mean sd median trimmed mad min max range skew kurtosis
## X1 1 8319 174.93 142.96 123 149.15 84.51 30 1745 1715 2.69 12.91
## se
## X1 1.57
El gráfico ind ica que los registros estan sesgados a la izquierda, con una media de 174.93 y desviación de 142.96 muy alto, casi igual a la media, su kurtosis es muy alta 12.91 dando una distribución muy punteaguda.
col <- 'habitaciones'
frecuencias <- table(data$habitaciones)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))
ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
geom_bar(stat = "identity", fill = "#0576FF") +
geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.
describe(data$habitaciones)
## vars n mean sd median trimmed mad min max range skew kurtosis se
## X1 1 8319 3.61 1.46 3 3.41 1.48 0 10 10 1.63 3.98 0.02
El gráfico revela que los registros muestran un sesgo hacia la derecha. media en 3.61 con desviacion de 1.46 muy alta para ser habitaciones. NOTA vemos que aparecen casas con 0 habitaciones de 1.66% , pueden tratarse de error o de apartaestudios con un solo ambiente. no obstante es un valor poco representativo dentro del estudio, y creemos que no se debe tener encuenta.
col <- 'banios'
frecuencias <- table(data$banios)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))
ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
geom_bar(stat = "identity", fill = "#0576FF") +
geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.
describe(data$banios)
## vars n mean sd median trimmed mad min max range skew kurtosis se
## X1 1 8319 3.11 1.43 3 2.99 1.48 0 10 10 0.93 1.13 0.02
El gráfico no muestra un comportamiento de los datos, de hecho llama la atención su skew de 0.93, muy cercana al cero. no obstante la media en 3.11 con su desviacion de 1.41 todavia algo a revisar. NOTA vemos que aparecen casas con 0 baños 0.85% , pueden tratarse de error o de apartaestudios con baños comunitarios algo improbable para la ciudad de Cali no obstante es un valor poco representativo dentro del estudio y creemos que no se debe tener encuenta.
ggplot(data=data) +
geom_point(aes(x=longitud,y=latitud, col=tipo),
col='#50AC05',
size=3, alpha = 1/5 )+
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
El gráfico representa la ciudad de Cali; sin embargo, seria bueno graficarlo separando por zonas, para identificar si el dato es confiable
ggplot(data=data) +
geom_point(aes(x=longitud,y=latitud, col=zona),
size=3, alpha = 1/5 )+
theme_minimal()+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
Vemos que si se define en gran manera las zonas, pero muchos puntos fuera de las zonas en donde se deberian demarcar, lo que nos hace un dato que no es 100% confiable. Y al ser una variable que puede ser de dificil interpretación, se opta por la decisión de removerlo del dataset a analizar.
data<- select(data, -longitud, -latitud)
lista_graficos_cuali <- list()
for (col in resumen_cualitativa) {
frecuencia_palabras_temp = aggregate(data[[col]], list(data[[col]]), FUN=length)
colnames(frecuencia_palabras_temp)=c("Palabra","Frecuencia")
lista_graficos_cuali[[col]] <- as.data.frame(frecuencia_palabras_temp)
}
frecuencia_palabras_temp = lista_graficos_cuali[['tipo']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
freq = frecuencia_palabras_temp$Frecuencia,
min.freq = 2,
max.words = 60,
colors = c("#515354","#50AC05","#0576FF", "#FF0576"),
random.order = F,
random.color = F,
scale = c(4 ,0.5),
rot.per = 0.25)
???
frecuencia_palabras_temp = lista_graficos_cuali[['zona']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
freq = frecuencia_palabras_temp$Frecuencia,
min.freq = 2,
max.words = 60,
colors = c("#515354","#50AC05","#0576FF", "#FF0576"),
random.order = F,
random.color = F,
scale = c(4 ,0.5),
rot.per = 0.25)
Es evidente la distinción de cinco zonas, siendo la zona Sur la más destacada, seguida por las zonas Norte y Oeste en términos de participación de registros.
frecuencia_palabras_temp = lista_graficos_cuali[['barrio']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
freq = frecuencia_palabras_temp$Frecuencia,
min.freq = 2,
max.words = 60,
colors = c("#515354","#50AC05","#0576FF", "#FF0576"),
random.order = F,
random.color = F,
scale = c(4 ,0.5),
rot.per = 0.25)
Se puede observar que hay barrios con una falta de estandarización en los datos. Ejemplos de esto incluyen el uso inconsistente de mayúsculas, abreviaciones, puntuaciones y símbolos, lo que indica problemas en el manejo de la normalización de la información.
-Se estandariza el campo barrio
reemplazar_caracteres <- function(texto) {
#Función empleada para hacer limpieza de los caracteres encontrados en la base de datos
#**NOTA**: No se trata de una función genérica, sino de una función personalizada
#construida específicamente para estos datos.
#
#@texto(string): el texto que vamos a procesar
#@return(string): el texto previamente tratado
caractares_remplazar <- c('Á','É','Í','Ó','Ú','√©','√∫')
caracteres_remplazado <- c('A','E','I','O','U','E','U')
texto <- toupper(texto)
for (i in 1:length(caractares_remplazar)){
texto <- gsub(caractares_remplazar[i],caracteres_remplazado[i],texto)
}
return (texto)
}
data$barrio <- sapply(data$barrio, reemplazar_caracteres)
Se desea realizar un analisis exploratorio con tecnicas de aproximación que piden tener valores númericos, es por ello que se realizara alguna transformación en algunos de ellos:
crear_variables_dummy <- function(data, columna) {
dummy_matrix <- model.matrix(~ . - 1, data = data[, columna, drop = FALSE])
dummy_df <- as.data.frame(dummy_matrix)
data <- cbind(data, dummy_df)
data <- data[, -which(names(data) == columna)]
return(data)
}
data_num <- data
Los valores de zona al no tener un orden inherente; realizaremos el tratamiento de Codificación Dummy (One-Hot Encoding)
unique (data_num$zona)
## [1] "Zona Oriente" "Zona Sur" "Zona Norte" "Zona Oeste" "Zona Centro"
data_num <- crear_variables_dummy(data_num, "zona")
Los valores de tipo de vivienda, se identifican que no tienen un orden inherente; se les realizará una Codificación Dummy (One-Hot Encoding)
unique (data_num$tipo)
## [1] "Casa" "Apartamento"
data_num <- crear_variables_dummy(data_num, "tipo")
#Recordemos que barrio es un campo categorico
data_num <- select(data_num, -barrio)
str(data_num)
## 'data.frame': 8319 obs. of 12 variables:
## $ estrato : num 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num 70 120 220 280 90 87 52 137 150 380 ...
## $ banios : num 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones : num 6 3 4 3 3 3 3 4 6 3 ...
## $ zonaZona Centro : num 0 0 0 0 0 0 0 0 0 0 ...
## $ zonaZona Norte : num 0 0 0 0 1 1 1 1 1 1 ...
## $ zonaZona Oeste : num 0 0 0 0 0 0 0 0 0 0 ...
## $ zonaZona Oriente: num 1 1 1 0 0 0 0 0 0 0 ...
## $ zonaZona Sur : num 0 0 0 1 0 0 0 0 0 0 ...
## $ tipoApartamento : num 0 0 0 0 1 1 1 1 0 0 ...
## $ tipoCasa : num 1 1 1 1 0 0 0 0 1 1 ...
grafico_n1 <- corPlot(cor(data_num),
cex = 0.7,
xsrt = 75,
main = "Matriz de correlación")
Se concluye los siguientes enunciados con base en al correlación:
areaconst y preciom : 0.69 positiva a mayor area construida mayor el precio,
baños y precio : 0.67 positiva a mayor cantidad de baños mayor el precio
baños y area contruida : 0.65 positiva a mayor cantidad de baños mayor el área construida
preciom y estrato : 0.61 positiva a mayor estrato mayor el precio.
areacons/habitaciones y tipo y : 0.55 negativa para apartamento y positiva para casa, una relación bastante llamativa que no afirma , pero tamposo resuelve sospechas.
habitaciones y baños: 0.59 positiva a mayor cantidad de habitaciones más baños
Aplicar el Análisis de Componentes Principales a nuestros datos
# Aplicar el Análisis de Componentes Principales
resultado_acp <- prcomp(data_num, scale = TRUE)
fviz_eig(resultado_acp,addlabels = TRUE)
En este caso el primer componente principal explica el 27.4% de la variabilidad contenida en la base de datos y entre los tres primeros se casi el 63.6% de los datos, es decir con estos 3 componentes se esta obteniendo una combinación lineal de las variables que puede resumir gran parte de la variabilidad que contiene la base de datos.
fviz_contrib(resultado_acp,axes=1:3,choice="var")
Se identifica los campos mas representativos en estos 3 componentes son: Zona Sur, ambos tipos de casa, latitud, preciom, area construc, baños y el estrato.
fviz_pca_var(resultado_acp,
col.var = "contrib",
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE)
Partiendo del precio, se observa que el número de baños, áreas de contrucción , habitaciones son directamente proporcionales. y que todos los anteriores son directamente proporcional en una Casa. Caso contrario, es el apartamento, en donde para el precio es casi indeferente, pero para el área de contrucción y habitaciones, son opuestos en dirección.
Llama la atención de estrato. pues se hace una dirección paralela a la casa y al apartamento, y justo son son las de mayor distribución en el reporte.
fviz_pca_biplot(resultado_acp,
repel=FALSE,
col.var='#2e9fdf',
col.ind='#ffff00')
Al contrastar las observaciones con las direcciones de las variables, da la impresión que las variables de alta contriunción de tipo de casa/apartamento estan generando una división entre los datos y justo el resto de los demas atributos disperzan los datos a lo ancho de los datos. para corroborar esta apreciación, emplearemos un analisis indivisual de PCA pero haciendo la diferenciación entre el tipo de apartamento.
colores <- function(vec){
col <- rainbow(length(unique(vec)))
return(col[as.numeric(as.factor(vec))])
} # Función para el color
plot(resultado_acp$x[,1:2], col = colores(data$tipo),
pch = 19,
xlab = "Dim1",
ylab = "Dim2")
Se observa una clara distinción de los resultados de pca an contrastarlos frente al tipo de apartamento, este comportamiento se puede corroborar si vemos las cotribuciones de cada variable por las dimensiones de la PCA creadas:
get_pca_var(resultado_acp)$contrib[,1:3]
## Dim.1 Dim.2 Dim.3
## estrato 2.11512168 28.7238409 0.20537736
## preciom 12.86122322 13.7419769 0.62062367
## areaconst 19.15372595 0.6275313 0.54577977
## banios 18.01398317 3.1823677 0.01241309
## habitaciones 13.70329281 3.2834131 0.15786622
## zonaZona Centro 0.07404437 2.3040615 0.59612599
## zonaZona Norte 0.59707941 2.9757001 26.75161079
## zonaZona Oeste 0.01104357 16.1897482 11.84361335
## zonaZona Oriente 0.17922098 9.2420309 1.80669159
## zonaZona Sur 0.11862002 0.0486055 57.35347392
## tipoApartamento 16.58632241 9.8403619 0.05321212
## tipoCasa 16.58632241 9.8403619 0.05321212
Se observa que en las principales dimensiones 1 y 2 ambos tipos de casa tiene un valor igual y representativo en los datos ratificando lo que apreciabamos con el grafico individual de PCA con separación de tipo de vivienda
Dado el fuerte componente de tipo de vivienda que tenemos en los datos, vamos a segregar los datos y a realizar un PCA segun el tipo, para tener una comparación mas detallada.
Dado el fuerte componente de tipo de vivienda que tenemos en los datos, vamos a segregar los datos y a realizar un PCA segun el tipo, para tener una comparación mas detallada.
data_filtrada_apartamento <- data_num[data_num$tipoApartamento == 1,-which(names(data_num) %in% c("tipoApartamento", "tipoCasa"))]
pca_apartamento<- prcomp(data_filtrada_apartamento, scale = TRUE)
fviz_eig(pca_apartamento,addlabels = TRUE)
Se observa que solo con las dos primeras dimensiones tenemos el 54% de
la explicción de la variación
fviz_contrib(pca_apartamento,axes=1:2,choice="var")
Este 54% corresponde a los campos zonaSur, latitud (relacionada a la zona), preciom, area de construcción, banios,zonaNorte y estrato. Una composición algo distinta a la obtenida inicialmente al incluir tipo de apartamento. pero con mayor precensia de la zonaNorte como componente para el tipo de Apartamento.
fviz_pca_var(pca_apartamento,
col.var = "contrib",
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE)
El grafico ratifica lo mencionado en el grafico de contribución, donde se destaca el contraste que tenemos por zonaNorte y zonaSur. se identifica que areaconst, baños, precio y estrato esas muy relacionados para el tipo Apartamento.
data_filtrada_casa <- data_num[data_num$tipoCasa == 1,-which(names(data_num) %in% c("tipoApartamento", "tipoCasa"))]
pca_casa<- prcomp(data_filtrada_casa, scale = TRUE)
fviz_eig(pca_casa,addlabels = TRUE)
Se observa que en casa las primeras 3 dimensiones son el 60% de la explicación de la variación
fviz_contrib(pca_casa,axes=1:3,choice="var")
Este 57% corresponde a los campos zonaSur, zonaNorte, latitud, precio, banios y estrato. Una composición algo distinta a la obtenida inicialmente al incluir tipo de apartamento y al de solo tipo ‘Apartamento’. nuevamente observamos la presencia de la zonaNorte y la salida del área de construccuión dentro de estas dimensiones.
fviz_pca_var(pca_casa,
col.var = "contrib",
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE)
El grafico muestra, el contraste que se da entre zonaSu y Norte en tipo en ‘Casa’ y llama la atención la salida del área de contrucción dentro de elementos mas representativos.
El mercado de inmuebles de la ciudad de Cali se caracteriza principalmente por el tipo de vivienda en la cual se desea analizar:
Los Apartamento: Se destacan en dos zonas de la ciudad Sur y Norte, y su área de construcción,estrato y baños estan relacionados con el precio de la vivienda.
Las casa: Se destacan en dos zonas de la ciudad Sur y Norte e igualmente destacan en estrato, baños relacionados con el precio de la vivienda. se ausenta importancia al área de contrucción y se aporta importancia al estrato.
Para alizar este nuevo analisis, requerimos que nuestros datos esten estandarizados
data_z =scale(data_num)
data_z = as.data.frame(data_z)
fviz_nbclust(data_z,kmeans,method ="wss")+
labs(subtitle="Elbow Method")+geom_vline(xintercept=6)
El metodo de codo, nos recomienda armar con nuestra data de 5 a 6 cluster. recordemos que este metodo es muy visual, y queda muy subjetivo el identificar cual número de cluster es el ideal.
fviz_nbclust(data_z,kmeans,method="silhouette")+
labs(subtitle = "Silhouette method")
Empleando el metodo de la silueta, observamos que la sugerencia es 6 cluster. Muy similar a la recomendación que teniamos con el metodo de codo.
Se procede a calcular la distancia de los puntos al centroide de cada cluster, y el metodo elegido es el ‘Euclidiano’
dist_data <- dist(data_z, method = 'euclidean')
hc_data <- hclust(dist_data, method = 'complete') #complete, average
cluster_assigments_hclust <- cutree(hc_data, k = 6)
plot(hc_data$height[8300:8330],type='b')
El gráfico se dispuso para mostrar los últimos pesos del metodo Hierarchical Clustering, en donde nos dan como conclusión 5 a 6 clustering
k6 <- kmeans(data_z, centers = 6)
k6$betweenss/k6$totss
## [1] 0.6023816
Validando la propoción sinificativa con 6 cluster, encontramos con un 0.602 . un valor que nos dice que tenemos una buena expicación, pero que tiene mucha posibilidad para mejorar la variabilidad total.
Se desea explorar diversas algoritmos de asociar los centroides por kmeans, para ello exploraremos cada uno de ellos y sus resultados
k6 <- kmeans(data_z, centers = 6 , algorithm = "Lloyd")
k6$betweenss/k6$totss
## [1] 0.5414488
k6 <- kmeans(data_z, centers = 6 , algorithm = "Hartigan-Wong")
k6$betweenss/k6$totss
## [1] 0.5361003
k6 <- kmeans(data_z, centers = 6 , algorithm = "Forgy")
k6$betweenss/k6$totss
## [1] 0.5731432
k6 <- kmeans(data_z, centers = 6 , algorithm = "MacQueen")
k6$betweenss/k6$totss
## [1] 0.5653691
Observamos que el metodo de centroide que brinda mayor explicación a la variación, es la brindada por el metodo por defecto.
Empleamos el número de 6 cluster para armar nuestro analisis y encontramos lo siguiente:
num_conglomerados <- 6
resultado_kmeans <- kmeans(data_z, centers = num_conglomerados)
data_z_v2 <- data.frame(data_z,
resultado_kmeans$cluster)
kable(do.call(data.frame,as.data.frame( table(data_z_v2$resultado_kmeans.cluster))),
format = "markdown",
caption = "**Tabla n°? Resumen de dataset con tratamientos y registros mayormente completados**",
align = "c", escape = FALSE,
row.names = FALSE,
booktabs = TRUE)
| Var1 | Freq |
|---|---|
| 1 | 1282 |
| 2 | 2227 |
| 3 | 1093 |
| 4 | 1875 |
| 5 | 595 |
| 6 | 1247 |
Encontramos 6 cluster con mucha participación en los ultimos 3 cluster , y muy pocos datos en el primero y tercer cluster. A continuación vamos a ver dos formas de como ver la caracterización, uno seria con solo la mediana de los datos
resultado_aggregate <- aggregate(data_num,
by = list(data_z_v2$resultado_kmeans.cluster),
FUN = median) # Medianas
kable(do.call(data.frame, as.data.frame(resultado_aggregate)),
format = "markdown",
caption = "**Tabla n°? Resumen de dataset con tratamientos y registros mayormente completados**",
align = "c", escape = FALSE,
row.names = FALSE,
booktabs = TRUE)
| Group.1 | estrato | preciom | areaconst | banios | habitaciones | zonaZona.Centro | zonaZona.Norte | zonaZona.Oeste | zonaZona.Oriente | zonaZona.Sur | tipoApartamento | tipoCasa |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 5 | 750 | 350 | 5 | 5 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| 2 | 4 | 225 | 77 | 2 | 3 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 3 | 6 | 570 | 154 | 3 | 3 | 0 | 0 | 1 | 0 | 0 | 1 | 0 |
| 4 | 4 | 320 | 176 | 3 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| 5 | 6 | 570 | 148 | 4 | 3 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
| 6 | 4 | 230 | 79 | 2 | 3 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
El otro metodo es desde rangos (lo aplicaremos solo a campos númericos), pero en estos momentos esta generando problemas con los calculos, no genera las medias simialres al anerior,
#require(table1)
#table1(~estrato+preciom+areaconst+banios+habitaciones|resultado_kmeans$cluster,data=data_z_v2)
Al observar ambas aproximaciones de caracterización, resulta muy interesante:
Los 3 cluster iniciales tiene preferencias por casa, donde se diferencia por estrato y precio promedio. donde varian por la zonas.
Los 3 cluster finales tiene preferencias por apartamentos, donde el precio promedio, estrato y área de contrucción , no son equivalentes en magnitud,
Se puede apreciar que apesar de que el promedio de estrato en un cluster es inferior que otro, puede tener mayor metros cuadrados y menor precio.es decir, se estan traslapando entre rango de diverentes cluster
colores_contrastantes <- c( "#7F8C8D", "#FC4E07","#FF6600","#5E5E5E","#F4D03F", "#BA4A00" )
numero_de_columnas <- ncol(data_z_v2)
fviz_cluster(list(data = data_z_v2[1:(numero_de_columnas-1)],
cluster = data_z_v2$resultado_kmeans.cluster),
palette = colores_contrastantes,
ellipse.type = "convex",repel = F,
show.clust.cent = FALSE, ggtheme = theme_minimal())
Nota: Los colores calidos se dejaron para agrupación de tipo Apartamento y colores frios para Casa
Partiendo de la comparación de colores calidos y frios que se implemento para los cluster por tipo de vivienda, encontramos que si existe una tendencia a grupar muy similar al grafico de de 3.4 Invididual PCA. en donde los apartamentos tenian una área en la parte superior de la grafica y las casas una sección en la parte inferior.
cluster_assignments <- resultado_kmeans$cluster
sil <- silhouette(cluster_assignments, dist(data_z))
sil_avg <- mean(sil[,3])
cat("Coeficiente de Silhouette promedio KMEAN k=6 : ", sil_avg)
## Coeficiente de Silhouette promedio KMEAN k=6 : 0.3135149
sil <- silhouette(cluster_assigments_hclust, dist(data_z))
sil_avg <- mean(sil[,3])
cat("Coeficiente de Silhouette promedio Hierarchical Clustering k=5 : ", sil_avg)
## Coeficiente de Silhouette promedio Hierarchical Clustering k=5 : 0.2661317
Realizar analisis de tipo de vivienda, zona y barrio Nota: los valores no requieren ser tratados a númericos Nota 2: recordemos que ya se realizo una limpieza del campo barrio en el paso 2.5 / variable cualitativas /barrio
grupo <- as.factor(data$tipo)
tabla <- data[, c("zona", "tipo", "barrio")]
par(mfrow = c(1, 1), mar = c(4, 4, 2, 1), oma = c(0, 0, 0, 0), mgp = c(2, 1, 0), las = 1)
resultados_mca <- MCA(tabla, graph = FALSE)
plot(resultados_mca, invisible = "ind", cex = 0.5)
plot(resultados_mca, invisible = "var", cex = 0.5)
plot(resultados_mca, invisible = "quanti", cex = 0.5)
fviz_mca_var(resultados_mca, label='none')
Partiendo de los graficos de multiple correspondencia, no se alcanza apreciar una relación muy significativa entre las diversas variables. procedemos a compararlas entre ellas
tabla <- table(data$zona, data$tipo)
balloonplot(t(tabla))
chisq.test(tabla)
##
## Pearson's Chi-squared test
##
## data: tabla
## X-squared = 690.93, df = 4, p-value < 2.2e-16
Hay evidencia significativa para rechazar la hipótesis nula de que no hay diferencia entre las frecuencias observadas y esperadas en la tabla de contingencia. La relación entre las variables es estadísticamente significativa.
resultados_ac <- CA(tabla)
valores_prop <-resultados_ac$eig
print(valores_prop)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08305442 100 100
Se aprecia que el porcentaje de varianza esta explicado en 100% con una dimensió que tiene un valor propio de tan solo 0.0830. al igual,que esta indicando la información que esta explicando por la primera dimensión del ananlisis de correspondencia.
Desde el punto de vista para el negocio, podemos concluir que la correlacion entre el tipo de vivienda y zona puede ser de una variación del 0.0830% (no es buena idea). esto sucede porque cada zona abarca muchos apartamentos y casas , con distintas caracteristicas en precio, estrato, area ,etc.
tabla <- table(data$barrio, data$tipo)
chisq.test(tabla)
## Warning in chisq.test(tabla): Chi-squared approximation may be incorrect
##
## Pearson's Chi-squared test
##
## data: tabla
## X-squared = 2431.8, df = 384, p-value < 2.2e-16
la evidencia en contra de la hipótesis nula es muy fuerte, lo que sugiere que hay una asociación significativa entre las variables en la tabla de contingencia.
resultados_ac <- CA(tabla)
valores_prop <-resultados_ac$eig
print(valores_prop)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.2923159 100 100
Se aprecia que el porcentaje de varianza esta explicado en 100% con una dimensió que tiene un valor propio de 0.2923.
Desde el punto de vista para el negocio, podemos concluir que la correlacion entre el tipo de vivienda y el barrio puede ser de una variación del 30%. no tan buena, pues los barrios pueden tener los dos tipos de vivienda existentes.
tabla <- table(data$barrio, data$zona)
chisq.test(tabla)
## Warning in chisq.test(tabla): Chi-squared approximation may be incorrect
##
## Pearson's Chi-squared test
##
## data: tabla
## X-squared = 29260, df = 1536, p-value < 2.2e-16
la evidencia en contra de la hipótesis nula es muy fuerte, lo que sugiere que hay una asociación significativa entre las variables en la tabla de contingencia. La magnitud del estadístico chi-cuadrado y el gran número de grados de libertad indican que hay una diferencia sustancial entre las frecuencias observadas y esperadas.
par(mfrow = c(1, 1), mar = c(4, 4, 2, 1), oma = c(0, 0, 0, 0), mgp = c(2, 1, 0), las = 1)
resultados_ac <- CA(tabla, graph = FALSE)
plot(resultados_ac, cex = 0.5)
valores_prop <-resultados_ac$eig
print(valores_prop)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.9617200 27.34332 27.34332
## dim 2 0.9288778 26.40956 53.75288
## dim 3 0.8944248 25.43000 79.18289
## dim 4 0.7321801 20.81711 100.00000
Entre Zona Barrio se observa que existen mas dimenciones requeridas, que casdi porcentajes similares, donde se destacan las primeras dimensiones que explican el 27.3% y el 26.4% de la variación respectivamente. con un valores eigen de 0.96 y 0.92 respectivamente.
Para el negocio, es evidente que existe una clara relación entre zona y barrio, pues su relación es directa, y se divide en 4 dimensiones que se asemejan a las zonas que tenemos en la ciudad de Cali