Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado.
La presente investigación tiene como objetivo analizar en profundidad
el mercado de viviendas urbanas, utilizando una base de datos
proporcionada por el paquete paqueteMODELOS
. Este análisis
permitirá identificar patrones relevantes, relaciones entre variables, y
segmentaciones útiles para la toma de decisiones estratégicas por parte
de una empresa inmobiliaria. Se utilizarán herramientas estadísticas
modernas que incluyen análisis exploratorio, regresión, técnicas de
agrupamiento y visualización de datos.
Después de realizar un proceso de limpieza de datos (ver Anexos), obtenemos una base depurada con las siguientes características:
tabla_tipos <- data.frame(
Variable = names(vivienda_limpia),
Tipo = sapply(vivienda_limpia, function(x) paste(class(x), collapse = "/")),
Descripción = descripciones,
row.names = NULL,
stringsAsFactors = FALSE
)
kable(tabla_tipos,
align = "lll", booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Variable | Tipo | Descripción |
---|---|---|
id | character | Identificador único del registro |
zona | character | Zona de la ciudad donde se ubica la vivienda |
piso | numeric | Piso en el que se encuentra el apartamento/ Numero de pisos que tiene la casa |
estrato | ordered/factor | Estrato socioeconómico asignado a la vivienda |
preciom | numeric | Precio de la vivienda en millones de pesos |
areaconst | numeric | Área construida en metros cuadrados |
parqueaderos | numeric | Número de parqueaderos disponibles |
banios | numeric | Número de baños |
habitaciones | numeric | Número de habitaciones |
tipo | character | Tipo de vivienda (apartamento, casa, etc.) |
barrio | character | Nombre del barrio donde se encuentra la propiedad |
longitud | numeric | Longitud geográfica de la ubicación |
latitud | numeric | Latitud geográfica de la ubicación |
vivienda_limpia %>%
select(where(is.numeric), -longitud, -latitud) %>%
describe() %>%
kable(caption = "Estadísticos descriptivos de variables numéricas",
booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed", "responsive"))
vars | n | mean | sd | median | trimmed | mad | min | max | range | skew | kurtosis | se | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
piso | 1 | 8319 | 3.535641 | 2.255105 | 3 | 3.173652 | 1.4826 | 1 | 12 | 11 | 1.5967990 | 2.649618 | 0.0247247 |
preciom | 2 | 8319 | 433.904436 | 328.665025 | 330 | 374.478594 | 207.5640 | 58 | 1999 | 1941 | 1.8490675 | 3.671372 | 3.6034453 |
areaconst | 3 | 8319 | 174.934938 | 142.964126 | 123 | 149.152171 | 84.5082 | 30 | 1745 | 1715 | 2.6933603 | 12.913820 | 1.5674421 |
parqueaderos | 4 | 8319 | 1.762471 | 1.044838 | 2 | 1.558810 | 1.4826 | 1 | 10 | 9 | 2.5006188 | 9.981324 | 0.0114555 |
banios | 5 | 8319 | 3.111311 | 1.428210 | 3 | 2.985579 | 1.4826 | 0 | 10 | 10 | 0.9252097 | 1.127110 | 0.0156587 |
habitaciones | 6 | 8319 | 3.605361 | 1.459537 | 3 | 3.410245 | 1.4826 | 0 | 10 | 10 | 1.6348042 | 3.984035 | 0.0160022 |
Esta base contiene dos tipos de vivienda: el 61,3 % corresponde a apartamentos y el 38,7 % a casas. En su mayoría, las casas cuentan con dos pisos, mientras que la mayor parte de los apartamentos se encuentran ubicados en un cuarto piso.
# Definir paleta de colores
colores_tipo <- c("Casa" = "#1f77b4", "Apartamento" = "#ff7f0e")
# Gráfico de pastel
tipo_data <- vivienda_limpia %>%
group_by(tipo) %>%
summarise(total = n()) %>%
mutate(porcentaje = total / sum(total) * 100,
etiqueta = paste0(tipo, " (", round(porcentaje, 1), "%)"))
g1 <- ggplot(tipo_data, aes(x = "", y = total, fill = tipo)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
labs(title = "Distribución por tipo de vivienda") +
theme_void() +
geom_text(aes(label = etiqueta), position = position_stack(vjust = 0.5)) +
scale_fill_manual(values = colores_tipo)
# Gráfico de número de pisos por tipo de vivienda
vivienda_limpia$piso <- as.numeric(vivienda_limpia$piso)
g2 <- vivienda_limpia %>%
filter(!is.na(piso)) %>%
count(tipo, piso) %>%
ggplot(aes(x = factor(piso), y = n, fill = tipo)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Cantidad de viviendas por número de pisos y tipo",
x = "Número de pisos", y = "Cantidad") +
theme_minimal() +
scale_fill_manual(values = colores_tipo)
# Mostrar juntos
g1 + g2
La mayoría de las viviendas se encuentran ubicadas en la zona sur de la ciudad, especialmente en los barrios Valle del Lili y Ciudad Jardín, y en su mayoría pertenecen al estrato 5.
# Definir colores
colores_tipo <- c("Casa" = "#1f77b4", "Apartamento" = "#ff7f0e")
# Gráfico 1: Zona por tipo
g_zona <- vivienda_limpia %>%
count(zona, tipo) %>%
ggplot(aes(x = zona, y = n, fill = tipo)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Cantidad de viviendas por zona y tipo",
x = "Zona", y = "Cantidad") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_fill_manual(values = colores_tipo)
# Gráfico 2: Estrato por tipo
g_estrato <- vivienda_limpia %>%
count(estrato, tipo) %>%
ggplot(aes(x = factor(estrato), y = n, fill = tipo)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Cantidad de viviendas por estrato y tipo",
x = "Estrato", y = "Cantidad") +
theme_minimal() +
scale_fill_manual(values = colores_tipo)
# Gráfico 3: Top 20 barrios por tipo (ahora vertical)
g_barrio <- vivienda_limpia %>%
count(barrio, tipo) %>%
arrange(desc(n)) %>%
slice_max(n, n = 20) %>%
ggplot(aes(x = reorder(barrio, -n), y = n, fill = tipo)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Top 20 combinaciones barrio-tipo",
x = "Barrio", y = "Cantidad") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_fill_manual(values = colores_tipo)
# Mostrar los tres gráficos
(g_zona + g_estrato) / g_barrio
De manera general, los precios de las viviendas oscilan entre los 50 y los 2.000 millones de pesos. Las casas presentan un precio promedio de 540 millones, mientras que los apartamentos tienen una media de 367 millones.
En cuanto al área construida, los apartamentos cuentan con un promedio de 113 m², mientras que las casas alcanzan, en promedio, los 273 m².
# Definir colores por tipo
colores_tipo <- c("Casa" = "#1f77b4", "Apartamento" = "#ff7f0e")
# Calcular medias por tipo
medias <- vivienda_limpia %>%
group_by(tipo) %>%
summarise(
media_precio = mean(preciom, na.rm = TRUE),
media_area = mean(areaconst, na.rm = TRUE)
)
# Gráfico de precios
g_precio <- ggplot(vivienda_limpia, aes(x = preciom, fill = tipo)) +
geom_histogram(bins = 30, color = "white", alpha = 0.7) +
geom_vline(data = medias, aes(xintercept = media_precio), color = "red", linetype = "dashed") +
geom_text(
data = medias,
aes(x = media_precio, y = Inf,
label = paste0("Media: ", round(media_precio, 0), " M")),
inherit.aes = FALSE,
vjust = 2,
hjust = 0,
color = "red",
size = 3.5
) +
facet_wrap(~ tipo, scales = "free_y") +
scale_fill_manual(values = colores_tipo) +
labs(title = "Distribución del precio por tipo de vivienda",
x = "Precio (millones)", y = "Frecuencia") +
theme_minimal()
# Gráfico de área construida
g_area <- ggplot(vivienda_limpia, aes(x = areaconst, fill = tipo)) +
geom_histogram(bins = 30, color = "white", alpha = 0.7) +
geom_vline(data = medias, aes(xintercept = media_area), color = "red", linetype = "dashed") +
geom_text(
data = medias,
aes(x = media_area, y = Inf,
label = paste0("Media: ", round(media_area, 0), " m²")),
inherit.aes = FALSE,
vjust = 2,
hjust = 0,
color = "red",
size = 3.5
) +
facet_wrap(~ tipo, scales = "free_y") +
scale_fill_manual(values = colores_tipo) +
labs(title = "Distribución del área construida por tipo de vivienda",
x = "Área construida (m²)", y = "Frecuencia") +
theme_minimal()
# Mostrar ambos gráficos juntos
g_precio / g_area
En promedio, los apartamentos cuentan con un solo parqueadero, mientras que las casas tienen un promedio de dos por vivienda. En cuanto a las habitaciones, los apartamentos disponen en promedio de tres, mientras que las casas tienen cuatro. Por otro lado, el número de baños en los apartamentos es de aproximadamente 2.5, mientras que las casas cuentan con cerca de cuatro.
# Paleta de colores por tipo
colores_tipo <- c("Casa" = "#1f77b4", "Apartamento" = "#ff7f0e")
# Gráfico de parqueaderos
g_parqueaderos <- ggplot(vivienda_limpia, aes(x = parqueaderos, fill = tipo)) +
geom_histogram(color = "white", binwidth = 1, alpha = 0.7, boundary = 0) +
geom_vline(data = vivienda_limpia %>% group_by(tipo) %>% summarise(media = mean(parqueaderos, na.rm = TRUE)),
aes(xintercept = media), color = "red", linetype = "dashed", linewidth = 1) +
facet_wrap(~ tipo, scales = "free_y") +
scale_fill_manual(values = colores_tipo) +
labs(title = "Distribucion de parqueaderos por tipo de vivienda",
x = "Cantidad de parqueaderos", y = "Frecuencia") +
theme_minimal()
# Gráfico de habitaciones
g_habitaciones <- ggplot(vivienda_limpia, aes(x = habitaciones, fill = tipo)) +
geom_histogram(color = "white", binwidth = 1, alpha = 0.7, boundary = 0) +
geom_vline(data = vivienda_limpia %>% group_by(tipo) %>% summarise(media = mean(habitaciones, na.rm = TRUE)),
aes(xintercept = media), color = "red", linetype = "dashed", linewidth = 1) +
facet_wrap(~ tipo, scales = "free_y") +
scale_fill_manual(values = colores_tipo) +
labs(title = "Distribucion de habitaciones por tipo de vivienda",
x = "Cantidad de habitaciones", y = "Frecuencia") +
theme_minimal()
# Gráfico de baños
g_banios <- ggplot(vivienda_limpia, aes(x = banios, fill = tipo)) +
geom_histogram(color = "white", binwidth = 1, alpha = 0.7, boundary = 0) +
geom_vline(data = vivienda_limpia %>% group_by(tipo) %>% summarise(media = mean(banios, na.rm = TRUE)),
aes(xintercept = media), color = "red", linetype = "dashed", linewidth = 1) +
facet_wrap(~ tipo, scales = "free_y") +
scale_fill_manual(values = colores_tipo) +
labs(title = "Distribucion de banos por tipo de vivienda",
x = "Cantidad de banos", y = "Frecuencia") +
theme_minimal()
# Mostrar las tres gráficas apiladas
g_parqueaderos / g_habitaciones / g_banios
El Análisis de Componentes Principales (ACP) es una técnica estadística multivariada que permite reducir la dimensionalidad de un conjunto de variables, conservando la mayor cantidad posible de información. En el contexto inmobiliario urbano, donde las viviendas se describen mediante múltiples variables (área, número de baños, parqueaderos, habitaciones, etc.), el ACP facilita la identificación de patrones subyacentes y las relaciones entre dichas variables.
El ACP nos ayuda a visualizar y comprender la estructura del mercado inmobiliario, destacando las características que más contribuyen a la variabilidad de los datos. Gracias a esta reducción, se simplifican análisis posteriores como la segmentación o la construcción de modelos predictivos, aportando información valiosa para la toma de decisiones estratégicas en cuanto a la valoración y posicionamiento de las propiedades en el mercado.
# Seleccionar variables numéricas para ACP
vars_num <- vivienda_limpia %>%
select(preciom, areaconst, parqueaderos, banios, habitaciones)
# Ejecutar ACP con escalamiento
acp <- prcomp(vars_num, scale. = TRUE)
# Resumen del ACP
summary(acp)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5
## Standard deviation 1.7931 0.9233 0.62738 0.58837 0.43866
## Proportion of Variance 0.6431 0.1705 0.07872 0.06923 0.03848
## Cumulative Proportion 0.6431 0.8136 0.89228 0.96152 1.00000
# Gráfico de varianza explicada (scree plot)
fviz_eig(acp, addlabels = TRUE, ylim = c(0, 60))
# Biplot componentes principales 1 y 2
fviz_pca_biplot(acp,
repel = TRUE,
col.ind = "blue", # color puntos (observaciones)
col.var = "red", # color variables
legend.title = "Variables")
# Matriz de correlación para variables numéricas
corrplot(cor(vars_num), method = "circle", type = "upper", tl.cex = 0.8)
El ACP identificó que con solo dos componentes podemos representar más del 80% de toda la información que tienen las seis variables mencionadas. Es decir, en lugar de analizar seis cosas por separado, podemos observar solo dos sin perder lo importante.
Podemos analiszar estos componentes como dos grandes ejes que agrupan las principales diferencias entre las viviendas:
El primer componente (PC1) resume las diferencias entre viviendas que son más grandes, costosas y con más parqueaderos.
El segundo componente (PC2) nos ayuda a distinguir otras características no explicadas por el primero, como posibles diferencias en número de baños o estrato.
Al realizar esta simplificacion podemos visualizar mejor los datos y detectar patrones, como por ejemplo, qué tipos de viviendas son similares entre sí, lo que nos será útil para el siguiente paso: la segmentación o agrupación por similitud.
Una vez realizada la reduccion de informacion, prcedemos a identificar los grupos de viviendas que sean similares entre si. para este proceso utilizaremos la tencina de analisis de conglomerados o clustering.
Mediante esra tecnica lograremos agrupar las viviendas en conjuntos o segmentos basados en las caracteristicas mas relevantes como el tamaño, el numero de habitaciones, baños etc. Lo que buscamos es que las viviendas de un mismo grupo sean lo mas homogeneas entre si. Esto es muy útil porque nos permite:
Detectar patrones de oferta en el mercado inmobiliario.
Identificar segmentos de mercado que pueden tener distintas necesidades o comportamientos.
Apoyar la toma de decisiones, por ejemplo, para construir, vender o promover viviendas según su perfil.
A continuación, se presentan los resultados del análisis, junto con una visualización que permite observar claramente la conformación de los grupos de viviendas.
#preparar datos
# Tomar los dos primeros componentes principales
componentes <- acp$x[, 1:2]
# calcular distancias y realizar cluster jerarquico
# Calcular matriz de distancias
distancias <- dist(componentes)
# Aplicar agrupamiento jerárquico
hc <- hclust(distancias, method = "ward.D2")
# Visualizar el dendrograma
plot(hc, labels = FALSE, hang = -1, main = "Dendrograma de viviendas")
rect.hclust(hc, k = 3, border = 2:4) # Puedes ajustar k = número de grupos
# Asignar los grupos al dataset original
grupos <- cutree(hc, k = 3) # Cambia k si quieres más o menos grupos
vivienda_limpia$grupo <- as.factor(grupos)
#visualizar grupos
fviz_cluster(list(data = componentes, cluster = grupos),
geom = "point",
ellipse.type = "convex",
palette = "jco",
ggtheme = theme_minimal(),
main = "Grupos de viviendas según componentes principales")
# Asegúrate de tener una columna llamada 'grupo' que identifique a qué cluster pertenece cada vivienda
# Reorganizamos los datos para graficar
datos_box <- vivienda_limpia %>%
select(grupo, preciom, areaconst, parqueaderos, banios, habitaciones) %>%
pivot_longer(cols = -grupo, names_to = "variable", values_to = "valor")
# Creamos los boxplots
ggplot(datos_box, aes(x = grupo, y = valor, fill = grupo)) +
geom_boxplot() +
facet_wrap(~variable, scales = "free", ncol = 5) +
theme_minimal() +
labs(title = "DistribuciOn de variables por grupo de viviendas",
x = "Grupo",
y = "Valor") +
scale_fill_brewer(palette = "Set2")
# Calcular los promedios
medias_grupo <- vivienda_limpia %>%
group_by(grupo) %>%
summarise(across(c(preciom, areaconst, parqueaderos, banios, habitaciones), mean))
# Reorganizamos para graficar
medias_long <- pivot_longer(medias_grupo, -grupo, names_to = "variable", values_to = "media")
# Gráfico de barras
ggplot(medias_long, aes(x = variable, y = media, fill = grupo)) +
geom_col(position = "dodge") +
theme_minimal() +
labs(title = "Promedio de caracteristicas por grupo",
x = "Variable",
y = "Promedio") +
scale_fill_brewer(palette = "Dark2")
En los gráficos anteriores podemos observar cómo se agrupan las viviendas de la ciudad según sus características físicas y económicas.
Grupo 1: Representan las viviendas medianamente amplias con caracteristicas estandar tanto en tamanño como numero de habitaciones y baños.
Area construida: mediana moderada (~300 m² aprox), con algunos valores atípicos hacia arriba.
Parqueaderos: Tiene en promedio mas parqueaderos que los otros grupos.
Baños: cerca de 3–4 baños en promedio.
Grupo 2: Viviendas mas pequeñas y economicas con menos comodidades que los otros grupos. pueden ser los apartamentos o unidades residenciales mas basicas.
Area construida: más pequeña (mediana <200 m²).
Parqueaderos: menor cantidad, pueden no tener parqueaderos o max 1.
Baños: cerca de 2–3 baños en promedio.
Grupo 3 Viviendas de gran tamaño, probablemente de alta gama o de lujo. Ideales para familias numerosas o personas con alto poder adquisitivo.
Area construida: claramente más grande (mediana >400 m²).
Parqueaderos: Mayor cantidad, en algunos casos hasta 5 por vivienda.
Baños: entre 5 y 6 en promedio.
Este análisis nos permite entender mejor qué tipo de viviendas existen en la ciudad y cómo se relacionan entre sí. Las gráficas permiten ver claramente las diferencias entre los grupos, lo cual es útil para toma de decisiones urbanas, análisis de precios y segmentación del mercado inmobiliario.
Esta técnica nos permite explorar y visualizar la relación entre variables categóricas, facilitando la identificación de patrones entre el tipo de vivienda, el estrato y la zona de la ciudad.
Este análisis es útil para comprender cómo están distribuidos los distintos tipos de vivienda y determinar si ciertos tipos se asocian con determinados estratos socioeconómicos o zonas geográficas.
A continuación, se presentan los resultados mediante gráficos, en los cuales las categorías se ubican en un plano: cuanto más cerca se encuentren entre sí, mayor será la relación entre ellas.
# Crear tabla de contingencia tipo vs estrato
tabla <- table(vivienda_limpia$tipo, vivienda_limpia$estrato)
# Eliminar columnas con ceros (si existen)
tabla_filtrada <- tabla[, colSums(tabla) != 0]
# Realiza el análisis de correspondencia
ca_resultado <- ca(tabla_filtrada)
# Coordenadas de filas (tipos de vivienda)
coord_filas <- data.frame(ca_resultado$rowcoord)
coord_filas$tipo <- rownames(coord_filas)
# Coordenadas de columnas (estratos)
coord_columnas <- data.frame(ca_resultado$colcoord)
coord_columnas$estrato <- rownames(coord_columnas)
# Crear los gráficos individualmente
grafico_tipo <- ggplot(coord_filas, aes(x = Dim1, y = tipo)) +
geom_point(color = "steelblue", size = 4) +
geom_vline(xintercept = 0, linetype = "dashed", color = "gray") +
ggtitle("Tipos de Vivienda (1D)") +
theme_minimal()
grafico_estrato <- ggplot(coord_columnas, aes(x = Dim1, y = factor(estrato))) +
geom_point(color = "darkred", size = 4) +
geom_vline(xintercept = 0, linetype = "dashed", color = "gray") +
ggtitle("Estratos (1D)") +
theme_minimal()
# Mostrar ambos gráficos uno al lado del otro
grafico_tipo + grafico_estrato
Al aplicar el analisis de correspondencia simple, se genera una sola dimension y esto se debe a que utilizamos para este proceso pocas categorias. por lo tanto si queremos realizar un analisis mas profundo deberiamos tener mas variables categoricas para hacerlos.
La base de datos permitio comprender de forma profunda el mecado de viviendas urbanas en la ciudad, identificando patrones claros de oferta y segmentaciones estrategicas.
En primer lugar, tenemos una caracterizacion descriptiva que reelo que la mayor parte del inventario corresponde a apartamentos, con una concentracion geografica significativa en el sur especialmente en barrios ed estrato 5 como valle de lili o ciudad jardin. las casas aunque menos fecuentes se distinguen por mayor tamano, numero de habitciones y precio promedio mas alto que los apartamentos.
porsteriormente en el analisis de componentes prncipales )ACP), SE DEMOSTRO QUE CON SOLO DOS EJES DE INFORMACION ES POSIBLE EXPLICAR EL 80% DE LA VARIABILIDAD DE LAS CARACTERISTICAS FISICAS Y ECONOCMICAS DE LAS PROPIEDADES. EL PROMER componente sisntetiza el tamaño , precio y numero de parqueaderos, mientras que el segudno recoge diferncias complementarias como baño o estratos. esta simplificacion nos ayuda con la segmentacion.
El Análisis de Conglomerados permitió identificar tres segmentos diferenciados:
Viviendas de tamaño y características estándar, con precios intermedios.
Viviendas más pequeñas y económicas, con menos comodidades, típicas de apartamentos básicos.
Viviendas de gran tamaño y alto precio, asociadas a un mercado de lujo.
Finalmente, el Análisis de Correspondencias evidenció asociaciones entre tipo de vivienda, estrato y localización, aunque con una única dimensión significativa debido a la limitada variedad categórica. Esto confirma que para obtener relaciones más complejas sería recomendable ampliar el número de variables categóricas analizadas.
Diversificación del portafolio: Ampliar la oferta en estratos medios y bajos para captar un segmento de mercado menos representado y potencialmente más dinámico.
Segmentación comercial: Adaptar la estrategia de marketing y ventas a los tres grupos detectados, diseñando mensajes, canales y ofertas específicas para cada uno.
Enfoque geográfico: Potenciar la presencia en zonas de alta demanda identificadas, pero también explorar nuevas localizaciones con potencial de valorización.
Análisis categórico ampliado: Incorporar más variables cualitativas (por ejemplo, antigüedad, tipo de acabados, acceso a transporte, proximidad a servicios) para profundizar el análisis de correspondencias y enriquecer la comprensión del mercado.
Optimización de precios: Utilizar los componentes principales como base para modelos predictivos que permitan establecer precios competitivos y ajustados al perfil de cada vivienda
A continuacion se mostraran los codigos utilizados previamente para la limoieza e inputacion de datos faltantes y atipicos.
# librerías necesarias
library(paqueteMODELOS)
library(tidyverse)
library(ggplot2)
library(GGally)
library(cluster)
library(corrplot)
library(skimr)
library(dplyr)
library(summarytools)
library(knitr)
library(DT)
library(kableExtra)
library(psych)
library(corrr)
library(ggcorrplot)
library(patchwork)
library(factoextra)
library(tidyr)
library(FactoMineR)
library(ca)
#DESCRIPCION DE DATOS
##Base de datos
# devtools::install_github(“centromagis/paqueteMODELOS”, force = TRUE)
data(“vivienda”)
datatable(head(vivienda, 20),
options = list(pageLength = 5),
caption = “Primeras 20 observaciones de la base de datos de vivienda”)
#DESCRIPCION DE LOS DATOS iniciales
#TABLA
descripciones <- c(
“Identificador único del registro”,
“Zona de la ciudad donde se ubica la vivienda”,
“Piso en el que se encuentra el apartamento/ Numero de pisos que tiene la casa”,
“Estrato socioeconómico asignado a la vivienda”,
“Precio de la vivienda en millones de pesos”,
“Área construida en metros cuadrados”,
“Número de parqueaderos disponibles”,
“Número de baños”,
“Número de habitaciones”,
“Tipo de vivienda (apartamento, casa, etc.)”,
“Nombre del barrio donde se encuentra la propiedad”,
“Longitud geográfica de la ubicación”,
“Latitud geográfica de la ubicación”
)
tabla_tipos <- data.frame(
Variable = names(vivienda),
Tipo = sapply(vivienda, class),
Descripción = descripciones,
row.names = NULL,
stringsAsFactors = FALSE
)
kable(tabla_tipos,
align = “lll”, booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c(“striped”, “hover”, “condensed”, “responsive”))
#Limpieza y preparación de los datos
##Antes de elaborar cualquier informe, es fundamental conocer nuestras variables y realizar una limpieza adecuada de los datos.
# Calcular duplicados
n_duplicados <- sum(duplicated(vivienda))
# Crear tabla con una fila
tabla_duplicados <- data.frame(
Descripción = “Total de registros duplicados en la base de datos”,
Cantidad = n_duplicados
)
# Mostrar tabla
kable(tabla_duplicados,
align = “lr”, booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c(“striped”, “hover”, “condensed”, “responsive”))
#ELIMINAR DUPLICADOS
##Dado que solo tenemos una fila con valores duplicados, procedemos a eliminarla y a verificar nuevamente que no existan duplicados en el conjunto de datos.
vivienda <- vivienda %>% distinct()
##VERIFICACION DE DUPLICADOS
# Calcular duplicados
n_duplicados <- sum(duplicated(vivienda))
# Crear tabla con una fila
tabla_duplicados <- data.frame(
Descripción = “Total de registros duplicados en la base de datos”,
Cantidad = n_duplicados
)
# Mostrar tabla
kable(tabla_duplicados,
align = “lr”, booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c(“striped”, “hover”, “condensed”, “responsive”))
#Revisar valores faltantes
# Calcular los NA por variable
na_totales <- colSums(is.na(vivienda))
# Convertir en data.frame
tabla_na <- data.frame(
Variable = names(na_totales),
`Total NA` = as.numeric(na_totales)
)
# Mostrar tabla
kable(tabla_na,
align = “lr”, booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c(“striped”, “hover”, “condensed”, “responsive”))
#Según la tabla, podemos observar que las variables piso y parqueaderos son las que presentan el mayor número de valores faltantes. Sin embargo, antes de tratarlos, procederemos a eliminar las dos filas donde el ID es NA y, posteriormente, verificaremos nuevamente la cantidad de datos faltantes.
# Eliminar filas con NA en la variable id
vivienda <- vivienda %>%
filter(!is.na(id))
#Calcular los NA por variable
na_totales <- colSums(is.na(vivienda))
# Convertir en data.frame
tabla_na <- data.frame(
Variable = names(na_totales),
`Total NA` = as.numeric(na_totales)
)
# Mostrar tabla
kable(tabla_na,
align = “lr”, booktabs = TRUE) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c(“striped”, “hover”, “condensed”, “responsive”))
##Faltantes en variable Piso
#Para poder tratar estos valores faltantes debemos conocer como se encuentran distribuidos los numeros de pisos entre apartamentos y casas para saber con que variable debemos rellenar esos faltantes.
# Función para calcular la moda
moda <- function(x) {
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
# Calcular mediana y moda por tipo
resumen_pisos <- vivienda %>%
filter(!is.na(piso)) %>%
mutate(piso = as.numeric(as.character(piso)),
tipo_grupo = ifelse(tipo == “Casa”, “Casa”, “Apartamentos”)) %>%
group_by(tipo_grupo) %>%
summarise(
mediana = median(piso, na.rm = TRUE),
moda = moda(piso)
)
resumen_pisos
#para poder tratar estos faltantes en el numero de pisos, vamos a utilizar la mediana ya que es mas robusta y distribuye mejor los datos, evitando sesgos hacia el valor mas comun. ideal cuando se van a realizar modelados.
# Calcular la mediana de piso por tipo
mediana_pisos <- vivienda %>%
filter(tipo %in% c(“Casa”, “Apartamento”)) %>%
mutate(piso = as.numeric(as.character(piso))) %>%
group_by(tipo) %>%
summarise(mediana_piso = median(piso, na.rm = TRUE))
# Imputar NA en piso según la mediana
vivienda_limpia <- vivienda %>%
mutate(piso = as.numeric(as.character(piso))) %>%
mutate(piso = ifelse(
is.na(piso) & tipo == “Casa”,
mediana_pisos$mediana_piso[mediana_pisos$tipo == “Casa”],
piso)) %>%
mutate(piso = ifelse(
is.na(piso) & tipo == “Apartamento”,
mediana_pisos$mediana_piso[mediana_pisos$tipo == “Apartamento”],
piso))
#Verificamos que no se presenten valores con NA
# Crear tabla con conteo de NA solo en la variable ‘piso’
tabla_na_piso <- data.frame(
Variable = “piso”,
`Total NA` = sum(is.na(vivienda_limpia$piso))
)
sum(is.na(vivienda_limpia$piso))
#Faltantes en variable parqueaderos
#CALCULO MODA Y MEDIANA
# Función para calcular la moda
get_moda <- function(x) {
ux <- na.omit(unique(x))
ux[which.max(tabulate(match(x, ux)))]
}
# Calcular mediana y moda de parqueaderos por tipo de vivienda
resumen_parqueaderos <- vivienda_limpia %>%
filter(!is.na(parqueaderos)) %>%
mutate(parqueaderos = as.numeric(as.character(parqueaderos)),
tipo_grupo = ifelse(tipo == “Casa”, “Casa”, “Apartamentos”)) %>%
group_by(tipo_grupo) %>%
summarise(
mediana_parqueaderos = median(parqueaderos, na.rm = TRUE),
moda_parqueaderos = get_moda(parqueaderos)
)
resumen_parqueaderos
# Calcular la mediana de parqueaderos por tipo de vivienda
mediana_parqueaderos <- vivienda_limpia %>%
filter(tipo %in% c(“Casa”, “Apartamento”)) %>%
mutate(parqueaderos = as.numeric(as.character(parqueaderos))) %>%
group_by(tipo) %>%
summarise(mediana_parq = median(parqueaderos, na.rm = TRUE))
# Imputar valores NA en parqueaderos según la mediana por tipo
vivienda_limpia <- vivienda_limpia %>%
mutate(parqueaderos = as.numeric(as.character(parqueaderos))) %>%
mutate(parqueaderos = ifelse(
is.na(parqueaderos) & tipo == “Casa”,
mediana_parqueaderos$mediana_parq[mediana_parqueaderos$tipo == “Casa”],
parqueaderos)) %>%
mutate(parqueaderos = ifelse(
is.na(parqueaderos) & tipo == “Apartamento”,
mediana_parqueaderos$mediana_parq[mediana_parqueaderos$tipo == “Apartamento”],
parqueaderos))
#Verificamos que no se presenten valores con NA
# Crear tabla con conteo de NA solo en la variable ‘parqueaderos’
tabla_na_parqueaderos <- data.frame(
Variable = “parqueaderos”,
`Total NA` = sum(is.na(vivienda_limpia$parqueaderos))
)
sum(is.na(vivienda_limpia$parqueaderos))
# Calcular los NA por variable
na_totales <- colSums(is.na(vivienda_limpia))
# Convertir a data frame
tabla_na_total <- data.frame(
Variable = names(na_totales),
`Total NA` = as.numeric(na_totales)
)
colSums(is.na(vivienda_limpia))
#Valores atipicos casas
vivienda_limpia %>%
filter(tipo == “Casa”) %>%
count(piso) %>%
arrange(piso)
#para este punto no tiene sentido que una casa tenga 5, 6, 7 o hasta 10 pisos por lo tanto se calculara la mediana de los pisos sin estos valores y se realizara la imputacion, no se pueden eliminar pues se podrian perder datos importantes
# Calcular la mediana de pisos en casas con valores entre 1 y 3
mediana_pisos_casa_valida <- vivienda_limpia %>%
filter(tipo == “Casa”, piso %in% 1:3) %>%
pull(piso) %>%
median(na.rm = TRUE)
# Reemplazar los valores sospechosos (5,6,7,10) con la mediana
vivienda_limpia <- vivienda_limpia %>%
mutate(piso = ifelse(
tipo == “Casa” & piso %in% c(5, 6, 7, 10),
mediana_pisos_casa_valida,
piso
))
# Ver conteo de pisos en viviendas tipo Casa después de imputación
table(vivienda_limpia$piso[vivienda_limpia$tipo == “Casa”])
#Transformar variables id y estarto en caracter
vivienda_limpia <- vivienda_limpia %>%
mutate(
id = as.character(id),
estrato = factor(as.character(estrato), levels = as.character(1:6), ordered = TRUE)
)
#FIN DE LIMPIEZA