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. Se requiere 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.
Retos:
Realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere:
Análisis de Componentes Principales: Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y oferta del mercado.
Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender la oferta en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.
Análisis de Correspondencia: Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta.
Visualización de resultados: Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos.
El informe final debe incluir análisis detallados de los resultados obtenidos, las conclusiones clave y las recomendaciones específicas para guiar las decisiones estratégicas de la empresa inmobiliaria.
## [1] "El dataframe cargado tiene las siguientes dimensiones: 8322 x 13"
El dataframe tiene 8322 registros y 13 variables. En primera instancia se evidencia que existen algunos valores faltantes en varias columnas.
A continuación se describen cada una de las variables contenidas en la base:
id - Identificador único de la vivienda
zona - Zona de la ciudad (Centro/Sur/Oeste/Oriente/Norte)
piso - Piso en que está ubicada la vivienda (entre 1 y 12 pisos)
estrato - Estrato socioeconómico del sector de la vivienda (3 al 6. No se presentan registros de 1 y 2)
preciom - Precio en millones de pesos (entre 58 y 1999 millones)
areaconst - Área construida en metros cuadrados (entre 30 y 1745 m cuadrados)
parqueaderos - Número de parqueaderos que tiene la vivienda(entre 1 y 10)
banios - Número de baños que tiene la vivienda (entre 0 y 10)
habitaciones - Número de habitaciones que tiene la vivienda (entre 0 y 10)
tipo - Tipo de vivienda (Casa/Apartamento).
barrio - Barrio donde está ubicada la vivienda.
longitud - Coordenada de longitud donde está ubicada la vivienda
latitud - Coordenada de latitud donde está ubicada la vivienda
En la revisión y reconocimiento preliminar de la base de datos, se identificó que algunas de esas variables no se encontraban estadarizadas durante la captura de la data, por lo que se hace necesario realizar la limpieza y depuración de los registros.
## id zona piso estrato
## Min. : 1 Length:8322 Length:8322 Min. :3.000
## 1st Qu.:2080 Class :character Class :character 1st Qu.:4.000
## Median :4160 Mode :character Mode :character Median :5.000
## Mean :4160 Mean :4.634
## 3rd Qu.:6240 3rd Qu.:5.000
## Max. :8319 Max. :6.000
## NA's :3 NA's :3
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean : 1.835 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :10.000 Max. :10.000
## NA's :2 NA's :3 NA's :1605 NA's :3
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8322 Length:8322 Min. :-76.59
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76.54
## Median : 3.000 Mode :character Mode :character Median :-76.53
## Mean : 3.605 Mean :-76.53
## 3rd Qu.: 4.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.46
## NA's :3 NA's :3
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
## NA's :3
Los datos de vivienda ofertados pertenecen a los estratos socioeconómicos más altos (entre 3 y 6).Dado que la mediana es 5, se identifica que la mayoría de las propiedades pertenecen a ese estrato. Adicionalmente existiendo 3 valores faltantes, podría requerir imputación basada en otras variables como zona o barrio.
El preciom, que representa el precio de las propiedades en millones, es de aproximadamente 433.9 millones en su media, con un mínimo de 58 millones y un máximo de 1999 millones, indicando una gran variabilidad en los precios de las propiedades. Adicionalmente, dado que la mediana es 330 millones, pero la media es 433.9 millones, se infiere que hay propiedades con precios muy altos que sesgan la media hacia arriba.Finalmente, hay 2 valores faltantes, que son pocos pero críticos, ya que el precio es una variable clave.
A nivel del área construida (en metros cuadrados), se observa un rango que va desde 30 m² hasta 1745 m². Dado que la media de 174.9 m² es mayor que la mediana es 123 m², sugiere la presencia de propiedades con áreas construidas muy grandes que sesgan la media. Adicionalmente hay 3 valores faltantes que deben revisarse y posiblemente imputarse.
La mayoría de las viviendas tienen entre 2 baños, 3 habitaciones y 3 parqueaderos.
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/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 ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Se cuenta con 9 variables numéricas y 4 de tipo character. Entendiendo el concepto de cada variable, se evidencia que “piso” está como CHR pero realmente corresponde a un número, por tanto se debe proceder a cambiar el tipo a NUM para facilitar los cálculos y poder realizar comparaciones.
## id zona piso estrato preciom areaconst
## 3 3 2638 3 2 3
## parqueaderos banios habitaciones tipo barrio longitud
## 1605 3 3 3 3 3
## latitud
## 3
Todos los atributos tienen datos faltantes, sin embargo en el caso de piso y parqueaderos el volumen es considerablemente mayor (2.638 y 1.605, respectivamente).
# 1. Identificar si existen registros con datos faltantes en todos los atributos
faltantes_todos_atributos <- vivienda[, !names(vivienda) %in% c("piso", "parqueaderos")]
indices_na <- which(rowSums(is.na(faltantes_todos_atributos)) > 0)
registros_na <- vivienda[indices_na, ]
print(registros_na)## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 NA <NA> <NA> NA NA NA NA NA NA
## 2 NA <NA> <NA> NA NA NA NA NA NA
## 3 NA <NA> <NA> NA 330 NA NA NA NA
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Convertir la variable 'piso' de character a numeric
vivienda$piso <- as.numeric(vivienda$piso)
# Verificar la conversión
str(vivienda$piso)## num [1:8322] NA NA NA 2 1 1 1 1 2 2 ...
# Eliminar los registros donde 'id' es NA
vivienda2 <- vivienda[!is.na(vivienda$id), ]
dim(vivienda2)## [1] 8319 13
Se aplica este código pues en la consulta anterior se evidenciaron 3 registros que tienen todos los atributos en NULO. Una vez eliminados los 3 registros, queda un dataframe de 8319 registros. Posteriormente se procede a calcular e imputar la MODA para ‘piso’ y ‘parqueadero’ en cada combinación de ‘tipo’, ‘zona’, y ‘estrato’.
library(dplyr)
# Función para calcular la moda
moda <- function(x) {
uniq_x <- unique(x[!is.na(x)])
uniq_x[which.max(tabulate(match(x, uniq_x)))]
}
# Calculo de la moda por grupo
moda_imputacion <- vivienda2 %>%
group_by(tipo, zona, estrato) %>%
summarise(
moda_piso = moda(piso),
moda_parqueaderos = moda(parqueaderos),
.groups = 'drop'
)
# Unir los resultados de moda con los datos originales
vivienda2 <- vivienda2 %>%
left_join(moda_imputacion, by = c("tipo", "zona", "estrato"))
# Imputar los valores faltantes con la moda y Eliminar las columnas auxiliares
vivienda2 <- vivienda2 %>%
mutate(
piso = ifelse(is.na(piso), moda_piso, piso),
parqueaderos = ifelse(is.na(parqueaderos), moda_parqueaderos, parqueaderos)
) %>%
select(-moda_piso, -moda_parqueaderos) # Identificar filas con al menos un valor NA
registros_na <- which(rowSums(is.na(vivienda2)) > 0)
# Ver las filas con datos faltantes
vivienda2[registros_na, ]Dado que con la imputación de la moda por grupos, aun permanecen algunos registros con NA, pudo ocurrir que se tuvieran:
Grupos sin valores no faltantes: Si en un grupo específico (definido por las combinaciones de tipo, zona, y estrato), todas las observaciones de piso o parqueaderos son NA, entonces la función moda no puede calcular la moda porque no tiene valores válidos para elegir.
Valores únicos que son NA: Si un grupo tiene todos los valores de piso o parqueaderos como NA, la imputación con la moda no se puede realizar porque no hay un valor no-NA con el cual reemplazar.
Dado lo anterior, se decide eliminar los registros con NA, pues es la opción más sencilla y segura, teniendo en cuenta que los NA restantes son muy pocos respecto al total de registros.
## id zona piso estrato preciom areaconst
## 0 0 0 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 0 0 0 0 0 0
## latitud
## 0
dims <- dim(vivienda2)
mensaje2 <- paste("Despues de eliminar y/o imputar los NA, se tienen las siguientes dimensiones:", paste(dims, collapse = " x "))
print(mensaje2)## [1] "Despues de eliminar y/o imputar los NA, se tienen las siguientes dimensiones: 8312 x 13"
Se eliminan la columnas ID, Longitud y Latitud ya que no son reelevantes para el objetivo. Debido que este informe no tiene por objeto profudizar en un análisis via georeferenciación.
# Contar cuántos registros tienen valor cero en cada variable
cero_counts <- sapply(vivienda2, function(x) sum(x == 0, na.rm = TRUE))
print(cero_counts)## zona piso estrato preciom areaconst parqueaderos
## 0 0 0 0 0 0
## banios habitaciones tipo barrio
## 45 66 0 0
Dado que no es factible que una vivienda cuente con cero baños y habitaciones, se procede a imputar la moda.
# Función para calcular la moda
calculate_mode <- function(x) {
unique_x <- unique(x)
unique_x[which.max(tabulate(match(x, unique_x)))]
}
# Calcular la moda sin contar los ceros
mode_habitaciones <- calculate_mode(vivienda2$habitaciones[vivienda2$habitaciones != 0])
mode_banios <- calculate_mode(vivienda2$banios[vivienda2$banios != 0])
# Imputar la moda en 'habitaciones' y 'banios' donde el valor es 0
vivienda2$habitaciones[vivienda2$habitaciones == 0] <- mode_habitaciones
vivienda2$banios[vivienda2$banios == 0] <- mode_banios
cero_counts <- sapply(vivienda2, function(x) sum(x == 0, na.rm = TRUE))
print(cero_counts)## zona piso estrato preciom areaconst parqueaderos
## 0 0 0 0 0 0
## banios habitaciones tipo barrio
## 0 0 0 0
# Se corrigen todos los nombres de los barrios y se llevan a minúscula
vivienda2$barrio <- tolower(vivienda2$barrio)
# Se eliminan las tildes
vivienda2$barrio <- iconv(vivienda2$barrio, to = "ASCII//TRANSLIT")
# Se hace limpieza de los nombres de los barrios
vivienda2 <- vivienda2 %>%
mutate(barrio = ifelse(barrio == "agua blanca", "aguablanca", barrio),
barrio = ifelse(barrio == "alf?crez real", "alferez real", barrio),
barrio = ifelse(barrio == "alfonso lopez i", "alfonso lopez", barrio),
barrio = ifelse(barrio == "arboledas", "arboleda", barrio),
barrio = ifelse(barrio == "cali bella", "calibella", barrio),
barrio = ifelse(barrio == "cali canto", "calicanto", barrio),
barrio = ifelse(barrio == "calicanto viii", "calicanto", barrio),
barrio = ifelse(barrio == "ciudad mel?cndez", "ciudadela melendez", barrio),
barrio = ifelse(barrio == "ciudad melendez", "ciudadela melendez", barrio),
barrio = ifelse(barrio == "ciudadela pasoancho", "ciudadela paso ancho", barrio),
barrio = ifelse(barrio == "el tr?cbol", "el trebol", barrio),
barrio = ifelse(barrio == "ingenio i", "el ingenio i", barrio),
barrio = ifelse(barrio == "ingenio", "el ingenio", barrio),
barrio = ifelse(barrio == "ingenio ii", "el ingenio ii", barrio),
barrio = ifelse(barrio == "el ingenio 3", "el ingenio iii", barrio),
barrio = ifelse(barrio == "laflora", "la flora", barrio),
barrio = ifelse(barrio == "las am?cricas", "las americas", barrio),
barrio = ifelse(barrio == "las vegas de", "las vegas", barrio),
barrio = ifelse(barrio == "los alamos", "alamos", barrio),
barrio = ifelse(barrio == "alborada", "la alborada", barrio),
barrio = ifelse(barrio == "alcazares", "los alcazares", barrio),
barrio = ifelse(barrio == "mel?cndez", "melendez", barrio),
barrio = ifelse(barrio == "pampalinda", "pampa linda", barrio),
barrio = ifelse(barrio == "portales de comfandi", "portada de comfandi", barrio),
barrio = ifelse(barrio == "rep??blica de israel", "republica de israel", barrio),
barrio = ifelse(barrio == "rincon de la", "rincon de salomia", barrio),
barrio = ifelse(barrio == "san judas", "san judas tadeo", barrio),
barrio = ifelse(barrio == "tequendeme", "tequendama", barrio),
barrio = ifelse(barrio == "valle de lili", "valle del lili", barrio),
barrio = ifelse(barrio == "tequendema", "tequendama", barrio),
barrio = ifelse(barrio == "barrio 7de agosto", "7 de agosto", barrio),
barrio = ifelse(barrio == "barrio el recuerdo", "el recuerdo", barrio), barrio = ifelse(barrio == "barrio eucaristico", "eucaristico", barrio),
barrio = ifelse(barrio == "barrio obrero", "obrero", barrio),
barrio = ifelse(barrio == "barrio tranquilo y", "tranquilo", barrio),
barrio = ifelse(barrio == "base a?crea", "base aerea", barrio),
barrio = ifelse(barrio == "juanamb??", "juanambu", barrio),
barrio = ifelse(barrio == "la riviera", "la rivera", barrio),
barrio = ifelse(barrio == "flora", "la flora", barrio),
barrio = ifelse(barrio == "cristales", "los cristales", barrio),
barrio = ifelse(barrio == "normandia west point", "normandia", barrio),
barrio = ifelse(barrio == "zona norte los", "zona norte", barrio))Aun después de realizar diversas imputaciones para normalizar los nombres de barrios, se determina que no es una variable confiable para el análisis debido que los registros no se cargaron estandarizadamente, por tanto hay aun información poco confiable. De preferencia se debe utilizar la variable zona.
# Calcular la tabla y la suma total
numero_viviendas_tipo <- table(vivienda2$tipo)
suma_total_tipo_vivienda <- sum(numero_viviendas_tipo)
# Crear el gráfico de barras
barplot(
numero_viviendas_tipo,
col = c("#0d3b66", "#f4d35e"),
xlab = "Tipo de Vivienda",
ylab = "Cantidad de Viviendas",
main = "Distribución de Viviendas por Tipo",
ylim = c(0, max(numero_viviendas_tipo) * 1.2), # Ajustar el límite superior para dejar espacio a las etiquetas
border = "white", # Añadir bordes blancos a las barras para mejor visualización
las = 1, # Girar etiquetas del eje Y para mejor legibilidad
cex.names = 0.9 # Ajustar el tamaño de las etiquetas en el eje X
)
# Añadir las etiquetas de texto encima de las barras
for (i in seq_along(numero_viviendas_tipo)) {
text(
x = i,
y = numero_viviendas_tipo[i] + max(numero_viviendas_tipo) * 0.05, # Colocar el texto ligeramente por encima de las barras
label = paste(numero_viviendas_tipo[i], sprintf("(%.1f%%)", numero_viviendas_tipo[i] / suma_total_tipo_vivienda * 100)),
cex = 0.8, # Ajustar el tamaño del texto
col = "black" # Establecer el color del texto
)
}Se evidencia que la mayoría de las viviendas ofertadas son de tipo apartamento.
# Calcular la tabla y la suma total
numero_viviendas_zona <- table(vivienda2$zona)
suma_total_zona_vivienda <- sum(numero_viviendas_zona) # Se suman las viviendas por zona
barplot(
numero_viviendas_zona,
col = c("#0d3b66", "red", "skyblue", "darkgreen", "#f4d35e"),
xlab = "Zona",
ylab = "Cantidad de Viviendas",
main = "Distribución de Viviendas por Zona",
ylim = c(0, max(numero_viviendas_zona) * 1.2), # Ajustar el límite superior
border = "white", # Añadir bordes blancos a las barras
las = 1, # Girar etiquetas del eje Y para mejor legibilidad
cex.names = 0.9 # Ajustar el tamaño de las etiquetas en el eje X
)
for (i in seq_along(numero_viviendas_zona))
{ # Añadir las etiquetas de texto encima de las barras
text(
x = i,
y = numero_viviendas_zona[i] + max(numero_viviendas_zona) * 0.05, # ubicar el texto ligeramente por encima de las barras
label = paste(numero_viviendas_zona[i], sprintf("(%.1f%%)", numero_viviendas_zona[i] / suma_total_zona_vivienda * 100)),
cex = 0.8, # Ajustar el tamaño del texto
col = "black" # Establecer el color del texto
)
}Se evidencia que la mayoría de las viviendas ofertadas (el pareto) están focalizadas en las zonas sur y norte.
Al fusionar los os gráficos anteriores, encontramos la ditribución de tipos de vivienda por Zona:
p =ggplot(vivienda2, aes(x = zona, fill = tipo)) +
geom_bar(position = "stack", width = 0.7) +
labs(title = "Distribucion por Zona",
x = "Zona de Cali",
y = "Tipo de Vivienda") +
scale_fill_manual(values = c("#0d3b66", "#f4d35e")) +
theme_minimal() +
theme(legend.position = "top", legend.title = element_blank())
print(p)En el gráfico anterior se evidencia que en las zonas Sur, Norte y Oeste, que es donde están focalizadas la mayoría de ofertas de vivienda, en % mayor de participación de esa oferta lo componen los apartamentos, mientras que en la Zona Oriente y Centro, la mayoría de las ofertas son casas.
numero_viviendas_estrato <- table(vivienda2$estrato)
suma_total_estrato_vivienda <- sum(numero_viviendas_estrato)
barplot(
numero_viviendas_estrato,
col = c("#0d3b66", "red", "skyblue", "darkgreen", "#f4d35e"),
xlab = "Estrato",
ylab = "Cantidades",
main = "Viviendas por Estrato",
ylim = c(0, max(numero_viviendas_estrato) * 1.2),
border = "white", # Añadir bordes blancos a las barras
las = 1, # Girar etiquetas del eje Y para mejor legibilidad
cex.names = 0.9 # Ajustar el tamaño de las etiquetas en el eje X
)
for (i in seq_along(numero_viviendas_estrato))
{
text(i,
y = numero_viviendas_estrato[i] + max(numero_viviendas_estrato) * 0.05,
label = paste(numero_viviendas_estrato[i], sprintf("(%.1f%%)",
numero_viviendas_estrato[i] / suma_total_estrato_vivienda * 100)),
cex = 0.8, # Ajustar el tamaño del texto
col = "black" # Establecer el color del texto
)
}promedio_valor_tipo <- tapply(vivienda2$preciom, vivienda2$tipo, mean)
barplot_heights <- barplot(
promedio_valor_tipo,
col = c("#0d3b66", "#f4d35e"), # Colores mejorados
xlab = "Tipo de Vivienda", # Etiqueta más descriptiva
ylab = "Precio Promedio (Millones)", # Unidad incluida
main = "Precio Promedio por Tipo de Vivienda", # Título más claro
ylim = c(0, max(promedio_valor_tipo) * 1.1), # Ajuste de los límites del eje Y para mejor visualización
border = "white", # Sin borde en las barras
las = 1 # Rotar las etiquetas del eje Y para mejor legibilidad
)
text(
barplot_heights,
promedio_valor_tipo,
labels = sprintf("%.0f", promedio_valor_tipo),
pos = 3, # Colocar etiquetas encima de las barras
col = "black",
cex = 0.8,
font = 2 # Negrita para mejor legibilidad
)
abline(h = 0, col = "gray50", lwd = 2)promedio_valor_viviendas_zona <- tapply(vivienda2$preciom, vivienda2$zona, mean)
barplot_heights <- barplot(
promedio_valor_viviendas_zona,
col = c("#0d3b66", "red", "skyblue", "darkgreen", "#f4d35e"), # Colores mejorados
xlab = "Zona", # Etiqueta descriptiva
ylab = "Precio Promedio (Millones)", # Unidad incluida
main = "Precio Promedio de las Viviendas por Zona", # Título más claro
ylim = c(0, max(promedio_valor_viviendas_zona) * 1.1), # Ajuste de los límites del eje Y para mejor visualización
border = "white", # Sin borde en las barras
las = 1 # Rotar las etiquetas del eje Y para mejor legibilidad
)
text(
barplot_heights,
promedio_valor_viviendas_zona,
labels = sprintf("%.0f", promedio_valor_viviendas_zona),
pos = 3, # Colocar etiquetas encima de las barras
col = "black",
cex = 0.8,
font = 2 # Negrita para mejor legibilidad
)
abline(h = 0, col = "gray50", lwd = 2)precio_maximo_viviendas_zona <- tapply(vivienda2$preciom, vivienda2$zona, max)
barplot_heights <- barplot(
precio_maximo_viviendas_zona,
col = c("#0d3b66", "red", "skyblue", "darkgreen", "#f4d35e"), # Colores consistentes
xlab = "Zona", # Etiqueta descriptiva
ylab = "Precio Máximo (Millones)", # Unidad incluida en la etiqueta del eje Y
main = "Precio Máximo de las Viviendas por Zona", # Título más claro
ylim = c(0, max(precio_maximo_viviendas_zona) * 1.1), # Ajuste del eje Y para incluir las etiquetas
border = "white", # Borde blanco para un aspecto limpio
las = 1 # Rotación de etiquetas del eje Y para mejor legibilidad
)
text(
barplot_heights,
precio_maximo_viviendas_zona,
labels = sprintf("%.0f", precio_maximo_viviendas_zona),
pos = 3, # Colocar etiquetas encima de las barras
col = "black",
cex = 0.8,
font = 2 # Negrita para mayor legibilidad
)
abline(h = 0, col = "gray50", lwd = 2)# Gráfico de dispersión entre 'areaconst' y 'preciom' con color arcoíris
ggplot(vivienda2, aes(x = areaconst, y = preciom, color = areaconst)) +
geom_point() +
scale_color_gradient(low = "blue", high = "red") +
ggtitle("Área construida vs precio")
El gráfico muestra una relación positiva entre las variables, lo cual es
esperado pues en la medida que aumenta el área construida, debería
aumentar también el precio. La tendencia es lineal, con una pendiente
positiva. Existe una alta dispersión de los puntos alrededor de la línea
de tendencia.
A continuación se realiza el análisis de los componentes principales:
# seleccionar solo las columnas numéricas:
data_numeric <- vivienda2[, sapply(vivienda2, is.numeric)]
# estandarizar los datos:
data_scaled <- scale(data_numeric)
# análisis de componentes principales:
componentes_principales <- prcomp(data_scaled, center = TRUE, scale. = TRUE)
# Se visualian los datos:
summary(componentes_principales)## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 1.8810 1.1828 0.9026 0.6848 0.60107 0.48302 0.42969
## Proportion of Variance 0.5054 0.1999 0.1164 0.0670 0.05161 0.03333 0.02638
## Cumulative Proportion 0.5054 0.7053 0.8217 0.8887 0.94029 0.97362 1.00000
library(dplyr)
# Proporción de varianza explicada
varianza_explicada_2 <- summary(componentes_principales)$importance[2, ]
# Crear el scree plot
barplot(varianza_explicada_2,
main = "Proporcion de Varianza Explicada por Componentes Principales",
xlab = "Componentes Principales",
ylab = "Proporcion de Varianza Explicada",
col = "skyblue")# ggplot2 para una visualización más detallada. Visualizar los scores de las observaciones en los primeros dos componentes principales
scores <- as.data.frame(componentes_principales$x)
scores$tipo <- vivienda2$tipo
ggplot(scores, aes(x = PC1, y = PC2, color = tipo)) +
geom_point(alpha = 0.7) +
labs(title = "PCA: Primeros dos Componentes Principales",
x = "Componente Principal 1",
y = "Componente Principal 2") +
theme_minimal()id_componentes_principales <- componentes_principales$rotation
# Ver las cargas para los primeros componentes
id_componentes_principales[, 1:6] # Muestra las cargas para PC1 y PC2## PC1 PC2 PC3 PC4 PC5
## piso 0.1233824 -0.5097239 0.83952589 0.12449399 0.03649808
## estrato -0.3031274 -0.5522896 -0.23506651 -0.56801182 -0.04239433
## preciom -0.4670897 -0.2232240 -0.04891294 0.08987985 0.30763839
## areaconst -0.4468059 0.1841179 0.07504580 0.28400769 0.65286066
## parqueaderos -0.4186572 -0.1891176 -0.12913065 0.61745514 -0.60532938
## banios -0.4608633 0.0906164 0.20718848 -0.36719310 -0.17824463
## habitaciones -0.2956577 0.5544840 0.41510706 -0.23883770 -0.27893975
## PC6
## piso 0.04547167
## estrato 0.45141705
## preciom -0.23569101
## areaconst 0.24661643
## parqueaderos 0.10384991
## banios -0.67461139
## habitaciones 0.46032568
Las variables más importantes para PC1 son el precio, la cantidad de baños, el área construída y la cantidad de parqueaderos (de mayor a menor relevancia).
# Visualizar las cargas de las variables en los primeros dos componentes principales
biplot(componentes_principales, scale = 0,
main = "Biplot Componentes Principales",
xlab = "Componente Principal 1",
ylab = "Componente Principal 2",
col = c("grey", "darkorange"))Con el gráfico anterior, se evidencia que la variable piso tiene una carga positiva hacia el componente principal (PC1), así que mientras esta variable incrementa se genera un aumento en el primer componente principal. En cuanto a la variable habitaciones, a diferencia de piso, cuenta con una carga negativa frente a PC1, por lo tanto, si esta variable incrementa, el valor de PC1 disminuye.
Por otro lado, en cuanto a PC2, las variables área construída y baño cuentan con una carga similar hacia dicho componente, generando un crecimiento entre ambas; así que se podría decir que si el área construída incrementa, también incrementará el número de baños, disminuyendo el valor del componente principal (PC2).
# Visualizar las cargas de las variables en los primeros dos componentes principales
loadings <- as.data.frame(componentes_principales$rotation)
loadings$variable <- rownames(loadings)
ggplot(loadings, aes(x = PC1, y = PC2, label = variable)) +
geom_text(aes(label = variable), hjust = 1, vjust = 1) +
geom_point() +
labs(title = "Cargas de las Variables en los Primeros Dos Componentes Principales",
x = "Componente Principal 1",
y = "Componente Principal 2") +
theme_minimal()Teniendo en cuenta que los dos primeros PC están capturando el 70.54% de los datos, y que esto representa un buen balance entre dimensionalidad y cantidad de información, se decide conservar solo PC1 y PC2.
## PC1 PC2
## [1,] 0.7682545 2.4501983
## [2,] 1.4621422 1.2339515
## [3,] 0.5040696 1.5534352
## [4,] -1.1622918 0.4647239
## [5,] 1.0522392 0.1630549
## [6,] 0.7636810 0.2369515
# Extraer las cargas de los componentes principales del resultado del PCA
id_componentes_principales <- componentes_principales$rotation
# Seleccionar las cargas de los dos primeros componentes principales (PC1 a PC2)
cargas_componentes_principales <- id_componentes_principales[, 1:2]
# Mostrar las cargas para los primeros cuatro componentes principales
print(cargas_componentes_principales)## PC1 PC2
## piso 0.1233824 -0.5097239
## estrato -0.3031274 -0.5522896
## preciom -0.4670897 -0.2232240
## areaconst -0.4468059 0.1841179
## parqueaderos -0.4186572 -0.1891176
## banios -0.4608633 0.0906164
## habitaciones -0.2956577 0.5544840
if (!require(factoextra)) install.packages("factoextra")
library(factoextra)
# Visualizar variables en función de contribución a 1ros componentes principales
fviz_pca_var(componentes_principales,
col.var = "contrib", # Color por contribuciones a los PC
gradient.cols = c("#FF7F00", "#034D94"), # Colores del gradiente
repel = TRUE, # Evitar superposición de texto
axes = c(1, 2) # Cambiar los ejes a los primeros dos componentes principales
)Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas de las ofertas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.
# Cargar los datos
if (!exists("vivienda_modificado2"))
variables_clustering <- select(vivienda2, estrato, preciom, areaconst, banios, habitaciones, parqueaderos)
# Seleccionar las variables relevantes para el clustering
variables_clustering <- select(vivienda2, estrato, preciom, areaconst, banios, habitaciones, parqueaderos)
# Escalar las variables si es necesario
variables_clustering_scaled <- scale(variables_clustering)
# Determinar el número óptimo de clusters usando el método del codo
wcss <- numeric(10)
for (i in 1:10) {
kmeans_fit <- kmeans(variables_clustering_scaled, centers = i, nstart = 10)
wcss[i] <- kmeans_fit$tot.withinss
}
# Graficar el método del codo
plot(1:10, wcss, type = "b", pch = 19, frame = FALSE,
xlab = "Numero de clusters (k)", ylab = "WCSS")Antes de aplicar el algoritmo K-Means, es necesario determinar el número óptimo de clústeres para agrupar las viviendas. En este caso se utilizó el método del codo (Elbow Method) para encontrar el número óptimo de clústeres que explica la mayor varianza en los datos.
El gráfico de la curva del codo representa la Suma de Cuadrados Internos (WCSS) en función del número de clústeres. La WCSS es una métrica que mide la dispersión interna de los puntos dentro de cada clúster. La idea es identificar el “codo”, es decir, el punto en el cual el aumento en el número de clústeres deja de reducir significativamente la WCSS.
fviz_nbclust(x = variables_clustering_scaled, FUNcluster = kmeans, method = "wss", k.max = 10,
diss = get_dist(variables_clustering_scaled, method = "euclidean"), nstart = 50)## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
library(cluster)
require(cluster)
pam.res <- pam(variables_clustering_scaled, 5)
fviz_cluster(pam.res, geom = "point", ellipse.type = "norm", show.clust.cent = TRUE, star.plot = TRUE) + labs(title = "Resultado del clustering 5 PC") + theme_bw()library(cluster)
require(cluster)
pam.res <- pam(variables_clustering_scaled, 4)
fviz_cluster(pam.res, geom = "point", ellipse.type = "norm", show.clust.cent = TRUE, star.plot = TRUE) + labs(title = "Resultado del clustering 4 PC") + theme_bw()library(cluster)
require(cluster)
pam.res <- pam(variables_clustering_scaled, 3)
fviz_cluster(pam.res, geom = "point", ellipse.type = "norm", show.clust.cent = TRUE, star.plot = TRUE) + labs(title = "Resultado del clustering 3 PC") + theme_bw()library(cluster)
require(cluster)
pam.res <- pam(variables_clustering_scaled, 2)
fviz_cluster(pam.res, geom = "point", ellipse.type = "norm", show.clust.cent = TRUE, star.plot = TRUE) + labs(title = "Resultado del clustering 2 PC") + theme_bw()Con las graficas anteriores, se evidencia la gran cantidad de datos que recogen los dos primeros componentes principales (PC1 & PC2); por lo tanto, a través de este método se confirma nuevamente que los 2 primeros componentes son suficientes para la captura de los datos.
correlation_matriz <- cor(variables_clustering_scaled)
correlation_df <- as.data.frame(as.table(correlation_matriz))
colnames(correlation_df) <- c("Var1", "Var2", "Correlation")
# Convertir las correlaciones a porcentaje
correlation_df$Correlation_Percentage <- round(correlation_df$Correlation * 100, 1)
# Graficar la matriz de correlación como un mapa de calor con los valores de correlación en porcentaje
ggplot(data = correlation_df, aes(x = Var1, y = Var2, fill = Correlation)) +
geom_tile() +
scale_fill_gradient2(low = "green", high = "red", mid = "white", midpoint = 0) +
theme_minimal() +
labs(title = "Matriz de Correlación") +
geom_text(aes(label = paste0(Correlation_Percentage, "%")), color = "black", size = 3) # Agregar valores de correlación
Se evidencia que el precio mantiene una correlación importante con
parqueaderos, baños, área construida e incluso con el estrato.
k_optimo <- 4
set.seed(444)
modelo_kmeans <- kmeans(variables_clustering_scaled, centers = k_optimo, nstart = 10)
cluster_labels <- as.factor(modelo_kmeans$cluster)
vivienda_limpia_con_cluster <- mutate(vivienda2, cluster = cluster_labels)
# Visualizar los clusters en función de algunas variables relevantes
ggplot(vivienda_limpia_con_cluster, aes(x = estrato, y = preciom, color = cluster)) +
geom_point() +
labs(title = "Clusters de Propiedades Residenciales",
x = "Estrato Socioeconómico",
y = "Precio de la Propiedad") +
theme_minimal() +
scale_color_discrete(name = "Cluster")ggplot(vivienda_limpia_con_cluster, aes(x = estrato, fill = cluster)) +
geom_density(alpha = 0.5) +
labs(title = "Distribución de Estrato por Cluster", x = "estrato", y = "Densidad") +
theme_minimal()ggplot(vivienda_limpia_con_cluster, aes(x = preciom, fill = cluster)) +
geom_density(alpha = 0.5) +
labs(title = "Distribución de Precios por Cluster", x = "Precio", y = "Densidad") +
theme_minimal()ggplot(vivienda_limpia_con_cluster, aes(x = areaconst, fill = cluster)) +
geom_density(alpha = 0.5) +
labs(title = "Distribución de Área construída por Cluster", x = "areaconst", y = "Densidad") +
theme_minimal()
Area construída tiene una fuerte densidad en viviendas con área
construída hasta 125 metros en el cluster 1. En cuanto al cluster 2, la
densidad es baja, sin embargo, se visualiza un alto desempeño en áreas
construídas entre 120 y 270 metros.
ggplot(vivienda_limpia_con_cluster, aes(x = banios, fill = cluster)) +
geom_density(alpha = 0.5) +
labs(title = "Distribución de Baños por Cluster", x = "banios", y = "Densidad") +
theme_minimal()
Por otro lado, en la variable baños la densidad más alta para el cluster
1, oscila entre 2 y 2.5 baños; en cambio, el cluster 2 tiene una
densidad alta entre 4 y 5 baños.
ggplot(vivienda_limpia_con_cluster, aes(x = habitaciones, fill = cluster)) +
geom_density(alpha = 0.5) +
labs(title = "Distribución de Habitaciones por Cluster", x = "Habitaciones", y = "Densidad") +
theme_minimal()
El Cluster 1 tiene una alta densidad entre 2.5 y 3 habitaciones.
ggplot(vivienda_limpia_con_cluster, aes(x = parqueaderos, fill = cluster)) +
geom_density(alpha = 0.5) +
labs(title = "Distribución de Parqueaderos por Cluster", x = "Parqueaderos", y = "Densidad") +
theme_minimal()
El cluster 1 tiene una alta densidad cuando se tiene 1 parqueadero.
Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.
##
## Adjuntando el paquete: 'pander'
## The following object is masked from 'package:GGally':
##
## wrap
## corrplot 0.92 loaded
library(gridExtra)
datos_inicial <- subset(vivienda2, select = c("barrio", "zona", "tipo", "estrato"))
Barrio <- datos_inicial$barrio
Zona <- datos_inicial$zona
Tipo <- datos_inicial$tipo
Estrato <- datos_inicial$estrato
Datos <- cbind(datos_inicial, Barrio, Zona, Tipo, Estrato)
Datos[, 1:4] <- NULL
summary(Datos)## Barrio Zona Tipo Estrato
## Length:8312 Length:8312 Length:8312 Min. :3.000
## Class :character Class :character Class :character 1st Qu.:4.000
## Mode :character Mode :character Mode :character Median :5.000
## Mean :4.633
## 3rd Qu.:5.000
## Max. :6.000
F1<-ggplot(Datos, aes(x=Zona)) + geom_bar(fill= "red")
F2<-ggplot(Datos, aes(x=Tipo)) + geom_bar(fill= "#FFD4A5")
F3<-ggplot(Datos, aes(x=Barrio)) + geom_bar(fill= "green")
F4<-ggplot(Datos, aes(x=Estrato)) + geom_bar(fill= "orange")
F5 <- grid.arrange(F1,F2,F3,F4, nrow = 4)
## 3.1. Relación barrio y zona
library(FactoMineR)
tabla_barrio_zona <- table(Datos$Barrio, Datos$Zona)
head(tabla_barrio_zona, n=10)##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## 20 de julio 0 0 0 3 0
## 3 de julio 0 0 0 0 1
## 7 de agosto 0 0 0 1 0
## acopi 0 157 0 0 1
## aguablanca 0 0 0 2 1
## aguacatal 0 0 108 0 1
## alameda 11 0 0 0 5
## alameda del rio 0 3 0 0 0
## alamos 0 15 0 0 0
## alf?Crez real 0 0 0 0 5
##
## Pearson's Chi-squared test
##
## data: tabla_barrio_zona
## X-squared = 29300, df = 1428, p-value < 2.2e-16
library(FactoMineR)
library(factoextra)
library(gridExtra)
resultados_barrio_zona <- CA(tabla_barrio_zona)## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 0.9622200 27.29711 27.29711
## Dim.2 0.9287627 26.34796 53.64507
## Dim.3 0.8963060 25.42720 79.07227
## Dim.4 0.7377001 20.92773 100.00000
fviz_screeplot(resultados_barrio_zona, addlabels = TRUE, ylim = c(0, 30))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")library(FactoMineR)
tabla_barrio_tipo <- table(Datos$Barrio, Datos$Tipo)
head(tabla_barrio_tipo, n=10)##
## Apartamento Casa
## 20 de julio 0 3
## 3 de julio 0 1
## 7 de agosto 0 1
## acopi 88 70
## aguablanca 1 2
## aguacatal 98 11
## alameda 4 12
## alameda del rio 2 1
## alamos 12 3
## alf?Crez real 4 1
##
## Pearson's Chi-squared test
##
## data: tabla_barrio_tipo
## X-squared = 2411.1, df = 357, p-value < 2.2e-16
library(FactoMineR)
library(factoextra)
library(gridExtra)
resultados_barrio_tipo <- CA(tabla_barrio_tipo)## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 0.2900752 100 100
fviz_screeplot(resultados_barrio_tipo, addlabels = TRUE, ylim = c(0, 30))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
##
## Apartamento Casa
## Zona Centro 21 98
## Zona Norte 1198 722
## Zona Oeste 1029 169
## Zona Oriente 61 288
## Zona Sur 2787 1939
##
## Pearson's Chi-squared test
##
## data: tabla_zona_tipo
## X-squared = 695.36, df = 4, p-value < 2.2e-16
library(FactoMineR)
library(factoextra)
library(gridExtra)
resultados_zona_tipo <- CA(tabla_zona_tipo)## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 0.08365695 100 100
fviz_screeplot(resultados_zona_tipo, addlabels = TRUE, ylim = c(0, 30))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
library(FactoMineR)
tabla_zona_estrato <- table(Datos$Zona, Datos$Estrato)
head(tabla_zona_estrato, n=10)##
## 3 4 5 6
## Zona Centro 105 14 0 0
## Zona Norte 572 407 769 172
## Zona Oeste 54 84 290 770
## Zona Oriente 340 8 1 0
## Zona Sur 382 1616 1685 1043
##
## Pearson's Chi-squared test
##
## data: tabla_zona_estrato
## X-squared = 3867.7, df = 12, p-value < 2.2e-16
library(FactoMineR)
library(factoextra)
library(gridExtra)
resultados_zona_estrato <- CA(tabla_zona_estrato)## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 0.32644555 70.155929 70.15593
## Dim.2 0.12764789 27.432619 97.58855
## Dim.3 0.01122083 2.411452 100.00000
fviz_screeplot(resultados_zona_estrato, addlabels = TRUE, ylim = c(0, 30))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")library(FactoMineR)
tabla_tipo_estrato <- table(Datos$Tipo, Datos$Estrato)
head(tabla_tipo_estrato, n=10)##
## 3 4 5 6
## Apartamento 639 1404 1763 1290
## Casa 814 725 982 695
##
## Pearson's Chi-squared test
##
## data: tabla_tipo_estrato
## X-squared = 224.45, df = 3, p-value < 2.2e-16
library(FactoMineR)
library(factoextra)
library(gridExtra)
resultados_tipo_estrato <- CA(tabla_tipo_estrato)## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 0.0270036 100 100
fviz_screeplot(resultados_tipo_estrato, addlabels = TRUE, ylim = c(0, 30))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.
# Gráfico de líneas con línea de tendencia
ggplot(vivienda2, aes(x = areaconst, y = preciom)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Precio Medio en Función del Área Construida", x = "Área Construida", y = "Precio Medio") +
theme_minimal()## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using formula = 'y ~ x'
Se evidencia que el precio promedio está en función del tamaño de las
viviendas, puesto que a mayor tamaño resultan ser más costosas.
# Gráfico de líneas con línea de tendencia
ggplot(vivienda2, aes(x = estrato, y = preciom)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Precio Medio en función del Estrato", x = "Estrato", y = "Precio Medio") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
Con el gráfico anterior, se evidencia el precio promedio por cada una de
los estratos; la línea roja, representa la tendencia de los precios a
través del método de regresión linea. De esta forma, se evidencia qeu
que el incremento real del valor de las viviendas de los estratos 5 al
6, está en línea con la ergresión lineal, a diferencia del precio
promedio de las viviendas de estrato 4 a 5.
# Gráfico de líneas con línea de tendencia
ggplot(vivienda2, aes(x = areaconst, y = habitaciones)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Número de Habitaciones en Función del Área Construida",
x = "Área Construida (m²)",
y = "Número de Habitaciones") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
# Gráfico de líneas con línea de tendencia
ggplot(vivienda2, aes(x = banios, y = habitaciones)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Número de habitaciones en Función al número de baños",
x = "Número de baños",
y = "Número de Habitaciones") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
# Gráfico de líneas con línea de tendencia
ggplot(vivienda2, aes(x = areaconst, y = piso)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Número de Pisos en Función del Área Construida - total viviendas", x = "Área Construida (m²)", y = "Número de Pisos") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
Con el gráfico anterior, se evidencia que entre menor área construída se
cuenta con más pisos.
Teniendo en cuenta que esto podría generar un sesgo en la interpretación y toma de decisiones por parte de la empresa, se desagrega el gráfico a nivel de viviendas tipo casa y viviendas tipo apartamento.
# Filtrar los datos para tipo "Casa" y graficar
ggplot(data = subset(vivienda2, tipo == "Casa"), aes(x = areaconst, y = piso)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Número de Pisos en Función del Área Construida - casas", x = "Área Construida (m²)", y = "Número de Pisos") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
# Filtrar los datos para tipo "Casa" y graficar
ggplot(data = subset(vivienda2, tipo == "Apartamento"), aes(x = areaconst, y = piso)) +
geom_line(color = "blue", size = 1) +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Número de Pisos en Función del Área Construida - apartamento", x = "Área Construida (m²)", y = "Número de Pisos") +
theme_minimal()## `geom_smooth()` using formula = 'y ~ x'
Se evidencia que para las viviendas tipo casa, el area construída no
afecta el número de pisos, ya que mantienen una media muy estable. En
cuanto a las viviendas tipo apartamento, el área construída si genera un
cambio en el número de pisos, mostrando una tendencia incremental
leve.
4.2. Gráficos de proporción
# Contar el número de viviendas por estrato
estrato_count <- as.data.frame(table(vivienda2$estrato))
colnames(estrato_count) <- c("estrato", "count")
# Calcular el porcentaje
estrato_count <- estrato_count %>%
mutate(percentage = count / sum(count) * 100)
# Crear gráfico de pastel con porcentajes
ggplot(estrato_count, aes(x = "", y = count, fill = estrato)) +
geom_bar(width = 1, stat = "identity") +
coord_polar(theta = "y") +
geom_text(aes(label = sprintf("%.1f%%", percentage)), position = position_stack(vjust = 0.5), color = "white") +
scale_fill_manual(values = c("#1f77b4", "#aec7e8", "#d62728", "#ff7f0e", "#ffbb78")) +
labs(title = "Proporción de Estratos", x = "", y = "") +
theme_void()# Contar el número de pisos
piso_count <- as.data.frame(table(vivienda2$piso))
colnames(piso_count) <- c("piso", "count")
# Calcular el porcentaje
piso_count <- piso_count %>%
mutate(percentage = count / sum(count) * 100)
num_colors <- length(unique(piso_count$piso))
# Crear gráfico de pastel con porcentajes
ggplot(piso_count, aes(x = "", y = count, fill = as.factor(piso))) +
geom_bar(width = 1, stat = "identity") +
coord_polar(theta = "y") +
geom_text(aes(label = sprintf("%.1f%%", percentage)),
position = position_stack(vjust = 0.5),
color = "white") +
scale_fill_manual(values = RColorBrewer::brewer.pal(num_colors, "Set3")) +
labs(title = "Proporción de Propiedades por Piso", x = "", y = "") +
theme_void()# Contar el número de baños
banios_count <- as.data.frame(table(vivienda2$banios))
colnames(banios_count) <- c("banios", "count")
banios_count <- banios_count %>%
mutate(percentage = count / sum(count) * 100)
num_colors <- length(unique(banios_count$banios))
ggplot(banios_count, aes(x = "", y = count, fill = as.factor(banios))) +
geom_bar(width = 1, stat = "identity") +
coord_polar(theta = "y") +
geom_text(aes(label = sprintf("%.1f%%", percentage)),
position = position_stack(vjust = 0.5),
color = "white") +
scale_fill_manual(values = RColorBrewer::brewer.pal(num_colors, "Set3")) +
labs(title = "Proporción de Propiedades por Número de Baños", x = "", y = "") +
theme_void()La reducción de dimensionalidad mediante PCA permitió identificar las variables numéricas más influyentes en la variabilidad del mercado inmobiliario. El 70 % de la varianza total fue capturada por los primeros 2 componentes. Estos resultados evidencian la capacidad del ACP para discernir patrones y descomponer relaciones subyacentes que dan forma a las disparidades presentes en los datos.
Las variables en las componentes principales brindan un enfoque estratégico para abordar futuros análisis y modelización. En contextos como la selección de modelos de regresión, las dos primeras componentes pueden actuar como predictores clave.
En el Análisis de Correlación, se evidenció una fuerte correlación positiva el precio mantiene una correlación y la cantidad de parqueaderos, baños, área construida y el estrato.
*La visualización gráfica brinda una mayor perspectiva de las relaciones entre las variables estrato y zona en el mercado inmobiliario. Podemos identificar patrones significativos permitiendo una segmentación más precisa del mercado y una mejor comprensión de las preferencias de los compradores en diferentes áreas geográficas.