La ciudad de Cali, en Colombia, es un punto crucial en el panorama económico y social del país, y su mercado inmobiliario refleja una interacción compleja de factores que influyen en la oferta y la demanda de viviendas. Este trabajo se centra en realizar un análisis exploratorio y descriptivo de una base de datos específica que contiene información relevante sobre el mercado de viviendas en Cali. El objetivo principal es examinar detalladamente las características fundamentales de este mercado, como precios, ubicaciones, características de las propiedades y tendencias temporales, mediante la identificación de variables clave, la visualización de patrones y tendencias, y el cálculo de estadísticas descriptivas.
A continuación, se presenta un análisis exploratorio de un conjunto de datos que comprende las viviendas vendidas en la ciudad de Cali. Este análisis se realiza utilizando el software estadístico R y se apoya en técnicas de estadística descriptiva e inferencial, incluyendo tablas y visualizaciones, con el fin de identificar patrones en los datos. El objetivo es proporcionar a los interesados información relevante que les permita tomar decisiones fundamentadas en el contexto del mercado inmobiliario de la ciudad.
Llevar a cabo un análisis exploratorio de datos utilizando una variedad de técnicas de estadística descriptiva, mediante la aplicación y comprensión del software estadístico R.
Entender la estructura inicial de la base de datos incluyendo la cantidad total de datos, el número de variables presentes, los tipos de variables contenidas, así como la evaluación de la nulidad y unicidad de los datos, proporcionando así una visión global y detallada que sirva como punto de partida para análisis posteriores.
Aplicar diversos métodos de limpieza y transformación de datos para mejorar la calidad y utilidad de la información contenida en la base de datos. Esto implica la identificación y selección de las variables de interes, gestionar datos faltantes mediante técnicas como imputación o eliminación adecuada, y normalizar variables para garantizar una comparación adecuada.
Realizar un análisis descriptivo de los datos, empleando métodos estadísticos, tablas y gráficos para comprender la distribución y las relaciones entre las variables, con un enfoque particular en la variable de precio.
A continuación, se presentan cada uno de los métodos que nos ayudarán a alcanzar los objetivos planteados.
Para llevar a cabo el proyecto, es fundamental instalar y utilizar diversas bibliotecas que facilitan el análisis estadístico de los datos.
Instalación de las librerías necesarias (solo se debe hacer una vez)
# install.packages("summarytools") # Para resúmenes estadísticos
# install.packages("paqueteMET") # Para la obtención de los datos
# install.packages("dplyr") # Para manipulación de datos
# install.packages("ggplot2") # Para gráficos estadísticos
# install.packages("plotrix") # Para gráficos especiales
# install.packages("kableExtra") # Para tablas en formato bonito
# install.packages("vcd") # Para análisis de tablas de contingencia
# install.packages("naniar") # Para análisis de datos faltantes
# install.packages("mice") # Para imputación de datos faltantes
# install.packages("VIM") # Para análisis de datos faltantes
# install.packages("corrplot") # Para visualización de matrices de correlación
#install.packages("kableExtra")
Importar las librerías usadas en el proyecto
#install.packages("corrplot")
# Proporciona funciones para realizar un análisis descriptivo completo de datos.
library(summarytools)
# Entre otras funciones, contiene la base de datos a usar.
library(paqueteMET)
# Facilita la manipulación eficiente de datos, incluyendo filtrado, agregación y transformación.
library(dplyr)
# Permite crear gráficos y visualizaciones de datos de manera flexible y poderosa.
library(ggplot2)
# Contiene funciones para generar diversos tipos de gráficos, como diagramas de dispersión y de barras.
library(plotrix)
# Mejora la presentación de tablas en R Markdown y otros formatos de salida.
library(kableExtra)
# Proporciona funciones para el análisis y visualización de tablas de contingencia.
library(vcd)
# Facilita la identificación y manejo de datos faltantes en conjuntos de datos.
library(naniar)
# Ofrece herramientas para el manejo de datos faltantes mediante imputación.
library(mice)
# Proporciona funciones para el análisis y visualización de datos faltantes.
library(VIM)
# Contiene funciones para la visualización de matrices de correlación.
library(corrplot)
Se carga la base de datos y se presenta una vista preliminar de 10 observaciones, mostrando sus diferentes variables y tipos de datos. La tabla consta de 8300 observaciones y 13 variables, de las cuales se deducen los siguientes significados:
data("vivienda_faltantes")
df2 <- vivienda_faltantes
head(df2, 10)
## # A tibble: 10 × 13
## id zona piso estrato preciom areaconst parquea banios habitac tipo
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 8312 Zona Oeste 4 6 1300 318 2 4 2 Apar…
## 2 8311 Zona Oeste 1 6 480 300 1 4 4 Casa
## 3 8307 Zona Oeste NA 5 1200 800 4 7 5 Casa
## 4 8296 Zona Sur 2 3 220 150 1 2 4 Casa
## 5 8297 Zona Oeste NA 5 330 112 2 4 3 Casa
## 6 8298 Zona Sur NA 5 1350 390 8 10 10 Casa
## 7 8299 Zona Sur 2 6 305 125 2 3 3 Apar…
## 8 8300 Zona Oeste NA 5 480 280 4 4 4 Apar…
## 9 8286 Zona Sur NA 5 275 74 1 2 3 Apar…
## 10 8287 Zona Sur 2 5 285 120 2 4 3 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
Se lleva a cabo un análisis estadístico exhaustivo de todas las variables. Inicialmente, se emplea la función summary para obtener una visión general de los datos. Sin embargo, para una comprensión más detallada y visualmente atractiva, se ha diseñado un script personalizado. Esto nos permite profundizar en los siguientes aspectos para su análisis:
Tipos de datos: La mayoría de las variables son numéricas, con solo dos variables de tipo “character” que representan información categórica, como “zona”, “tipo” y “barrio”. Las variables restantes son numéricas, siendo “precio”, “área”, “latitud” y “longitud” variables continuas, mientras que “piso”, “estrato”, “parqueadero”, “baños” y “habitaciones” son variables discretas.
Valores nulos: Se detecta la presencia de valores nulos en algunas variables, destacando especialmente en “tipo” y “parqueadero”, lo que sugiere la necesidad de un análisis detallado. En las demás variables, la cantidad de valores nulos oscila entre 2 y 3 observaciones.
Valores unicos: La variable “barrio” exhibe 436 valores únicos, lo que indica una falta de estandarización en esta variable y una amplia diversidad en las ubicaciones geográficas registradas en el conjunto de datos. En futuros análisis, se sugiere no basarse en la cantidad de categorías para graficar o tabular datos, sino enfocarse en los principales barrios donde se concentren la mayoría de las viviendas. Respecto a la variable “id”, se observa una amplia gama de valores únicos, lo que sugiere que cada observación en el conjunto de datos está asociada con un identificador único. Sin embargo, dado que no aporta información relevante al análisis, se recomienda eliminar esta variable. En cuanto a “longitud” y “latitud”, se evidencia una alta dispersión, con una gran diferencia entre los valores mínimo y máximo, lo que sugiere un posible error en la estandarización de la escala de estos datos. Dada la naturaleza de estas variables y la presencia de este error, se decide no utilizarlas en análisis posteriores.
Valores atipicos: Al analizar algunas variables numéricas como “piso”, “estrato”, “preciom”, “areaconst”, “parquea”, “banios” y “habitac”, podríamos identificar valores atípicos al considerar la diferencia entre el valor máximo y mínimo junto con la desviación estándar. La presencia de valores extremos en estas variables podría indicar situaciones excepcionales o errores en los datos que deben ser investigados con mayor profundidad.
La presencia de valores nulos y la diversidad en los valores únicos subrayan la importancia de llevar a cabo un análisis exploratorio exhaustivo y un preprocesamiento adecuado de los datos antes de proceder con cualquier modelado o inferencia.
En conclusión, este análisis inicial ofrece una visión general de la estructura y las características del conjunto de datos, señalando áreas que podrían necesitar mayor atención durante el proceso de análisis y modelado
summary(df2)
## id zona piso estrato
## Min. : 1 Length:8330 Min. : 1.000 Min. :3.000
## 1st Qu.:2082 Class :character 1st Qu.: 2.000 1st Qu.:4.000
## Median :4164 Mode :character Median : 3.000 Median :5.000
## Mean :4164 Mean : 3.772 Mean :4.634
## 3rd Qu.:6246 3rd Qu.: 5.000 3rd Qu.:5.000
## Max. :8319 Max. :12.000 Max. :6.000
## NA's :3 NA's :2641 NA's :3
## preciom areaconst parquea banios
## Min. : 58.0 Min. : 30 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123 Median : 2.000 Median : 3.000
## Mean : 434.2 Mean : 175 Mean : 1.836 Mean : 3.112
## 3rd Qu.: 540.0 3rd Qu.: 229 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745 Max. :10.000 Max. :10.000
## NA's :2 NA's :3 NA's :1606 NA's :3
## habitac tipo barrio longitud
## Min. : 0.000 Length:8330 Length:8330 Min. :-76576.00
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76506.00
## Median : 3.000 Mode :character Mode :character Median : -76.54
## Mean : 3.605 Mean :-21845.13
## 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.390
## Median : 3.450
## Mean : 970.370
## 3rd Qu.:3367.000
## Max. :3497.000
## NA's :3
cantidad_nulos <- colSums(is.na(df2))
# Calcular la cantidad de valores únicos por columna
cantidad_unicos <- sapply(df2, function(x) length(unique(na.omit(x))))
# Calcular la moda para cada columna en el dataframe df
moda <- sapply(df2, function(x) {
tabla <- table(x)
moda <- as.character(names(tabla[tabla == max(tabla)]))
if (length(moda) > 1) {
return("Múltiples modas")
} else {
return(moda)
}
})
cantidad_moda <- sapply(df2, function(x) {
tabla <- table(x)
moda <- as.character(names(tabla[tabla == max(tabla)]))
cantidad_moda <- max(tabla)
return(cantidad_moda)
})
mediana <- sapply(df2, function(x) {
if (is.numeric(x)) {
return(median(x, na.rm = TRUE))
} else {
return(NA)
}
})
media <- sapply(df2, function(x) {
if (is.numeric(x)) {
return(mean(x, na.rm = TRUE))
} else {
return(NA)
}
})
desviacion_estandar <- sapply(df2, function(x) {
if (is.numeric(x)) {
return(sd(x, na.rm = TRUE))
} else {
return(NA)
}
})
minimo <- sapply(df2, function(x) {
if (is.numeric(x)) {
return(min(x, na.rm = TRUE))
} else {
return(NA)
}
})
maximo <- sapply(df2, function(x) {
if (is.numeric(x)) {
return(max(x, na.rm = TRUE))
} else {
return(NA)
}
})
tipos_de_dato <- sapply(df2, typeof)
# Crear la tabla con las cantidades de nulos y únicos
tabla <- data.frame( tipos_de_dato = tipos_de_dato, Nulos = cantidad_nulos, Unicos = cantidad_unicos, moda = moda, cantidad_moda = cantidad_moda, mediana = mediana, media = media, minimo = minimo, maximo = maximo, desviacion_estandar = desviacion_estandar)
# Crear la tabla formateada
tabla_formateada <- kable(tabla, caption = "Analisis descriptivo", caption.centre = TRUE) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)
# Imprimir la tabla formateada
tabla_formateada
| tipos_de_dato | Nulos | Unicos | moda | cantidad_moda | mediana | media | minimo | maximo | desviacion_estandar | |
|---|---|---|---|---|---|---|---|---|---|---|
| id | double | 3 | 8319 | Múltiples modas | 2 | 4164.00000 | 4163.990873 | 1.00000 | 8319.00000 | 2403.926404 |
| zona | character | 3 | 5 | Zona Sur | 4726 | NA | NA | NA | NA | NA |
| piso | double | 2641 | 12 | 2 | 1450 | 3.00000 | 3.772368 | 1.00000 | 12.00000 | 2.615013 |
| estrato | double | 3 | 4 | 5 | 2751 | 5.00000 | 4.634322 | 3.00000 | 6.00000 | 1.029327 |
| preciom | double | 2 | 539 | 350 | 201 | 330.00000 | 434.240634 | 58.00000 | 1999.00000 | 329.015704 |
| areaconst | double | 3 | 652 | 60 | 314 | 123.00000 | 174.987601 | 30.00000 | 1745.00000 | 142.949849 |
| parquea | double | 1606 | 10 | 1 | 3156 | 2.00000 | 1.835961 | 1.00000 | 10.00000 | 1.125084 |
| banios | double | 3 | 11 | 2 | 2946 | 3.00000 | 3.112045 | 0.00000 | 10.00000 | 1.428346 |
| habitac | double | 3 | 11 | 3 | 4101 | 3.00000 | 3.605140 | 0.00000 | 10.00000 | 1.459095 |
| tipo | character | 3 | 6 | Apartamento | 5032 | NA | NA | NA | NA | NA |
| barrio | character | 3 | 436 | valle del lili | 1008 | NA | NA | NA | NA | NA |
| longitud | double | 3 | 2928 | -76529 | 136 | -76.53931 | -21845.127963 | -76576.00000 | -76.46438 | 34503.896690 |
| latitud | double | 3 | 3679 | 3452 | 115 | 3.44952 | 970.369902 | 3.33308 | 3497.00000 | 1539.156058 |
Basándonos en lo anterior, se determina prescindir de las siguientes variables en el análisis:
# Eliminar las variables latitud, longitud e id
df1 <- subset(df2, select = -c(latitud, longitud, id))
head(df1,5)
## # A tibble: 5 × 10
## zona piso estrato preciom areaconst parquea banios habitac tipo barrio
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 Zona Oeste 4 6 1300 318 2 4 2 Apar… arbol…
## 2 Zona Oeste 1 6 480 300 1 4 4 Casa norma…
## 3 Zona Oeste NA 5 1200 800 4 7 5 Casa miraf…
## 4 Zona Sur 2 3 220 150 1 2 4 Casa el gu…
## 5 Zona Oeste NA 5 330 112 2 4 3 Casa bella…
Durante este procedimiento, se pretende estandarizar algunas variables categóricas para asegurar una representación uniforme en todo el conjunto de datos, a la par que normalizar las variables numéricas. Esta práctica simplifica el análisis estadístico y facilita una comprensión más clara de las variables involucradas
En la variable “tipo”, se unificó la diversidad inicial de categorías, que incluía variantes como “Apartamento”, “Casa”, “APARTAMENTO”, “casa”, “CASA” y “apto”, para representar solo dos valores: “Apartamento” y “Casa”. Esta acción redujo las seis categorías iniciales a solo dos, lo que simplificó considerablemente la interpretación y el análisis de los datos.
Aunque la variable “zona” ya estaba estandarizada, se optó por simplificar aún más sus valores eliminando los prefijos. Por ejemplo, cambiar de “Zona Sur” a simplemente “Sur”. Este enfoque resulta especialmente relevante en conjuntos de datos extensos, donde se busca optimizar el uso de memoria.
Dado el enfoque adoptado en el análisis, se decidió eliminar las variables de latitud y longitud. Sin embargo, al observar sus valores mínimos y máximos, se evidenció una falta de estandarización: los valores de latitud oscilaban entre -75.5 y -76576, mientras que los de longitud variaban entre 3.3 y 3497. Esta disparidad sugiere una falta de captura en la misma escala, lo cual indica la necesidad de normalizar estas variables durante el proceso de transformación de datos.
cat("Valores de la variable tipo sin estandarizar:",paste(unique(df1$tipo), collapse = ", "), "\n")
## Valores de la variable tipo sin estandarizar: Apartamento, Casa, APARTAMENTO, casa, CASA, apto, NA
#reemplazar todo por minúsculas
df1$tipo <- tolower(df1$tipo)
df1$tipo <- recode(df1$tipo,
"apto" = "apartamento",
"casa" = "casa")
cat("Valores de la variable tipo estandarizada:",paste(unique(df1$tipo), collapse = ", "), "\n")
## Valores de la variable tipo estandarizada: apartamento, casa, NA
cat("Valores de la variable zona sin estandarizar:", paste(unique(df1$zona), collapse = ", "), "\n")
## Valores de la variable zona sin estandarizar: Zona Oeste, Zona Sur, Zona Norte, Zona Oriente, Zona Centro, NA
df1$zona <- recode(df1$zona,
"Zona Oeste" = "Oeste",
"Zona Sur" = "Sur",
"Zona Norte" = "Norte",
"Zona Oriente" = "Oriente",
"Zona Centro" = "Centro")
cat("Valores de la variable zona estandarizada:", paste(unique(df1$zona), collapse = ", "), "\n")
## Valores de la variable zona estandarizada: Oeste, Sur, Norte, Oriente, Centro, NA
En este análisis, se identificaron tres observaciones con una significativa falta de información. Dos de ellas presentaban valores nulos en todas las variables, mientras que una contenía únicamente datos en la variable de precio. Por lo tanto, se tomó la decisión de eliminar estas observaciones del conjunto de datos. Tras esta acción, se observó una notable limpieza en los datos, donde únicamente persistían nulos en las variables de parqueadero y piso. A pesar de que algunas teorías sugieren no imputar estos datos debido al alto porcentaje de nulos, en este análisis decidimos realizar un análisis inferencial, que se presenta en el siguiente módulo.
# Generar el gráfico de patrón de nulidad
md_pat <- md.pattern(df1,rotate.names = TRUE)
# Agregar título
title(main = "Análisis de nulidad", sub = "Gráfica #1: Análisis de nulidad", adj = 0, cex.main = 1.5)
md_pat
## preciom zona estrato areaconst banios habitac tipo barrio parquea piso
## 4812 1 1 1 1 1 1 1 1 1 1
## 1912 1 1 1 1 1 1 1 1 1 0
## 877 1 1 1 1 1 1 1 1 0 1
## 726 1 1 1 1 1 1 1 1 0 0
## 1 1 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0
## 2 3 3 3 3 3 3 3 1606 2641
##
## 4812 0
## 1912 1
## 877 1
## 726 2
## 1 9
## 2 10
## 4270
df <- df1[rowSums(is.na(df1[, c("zona", "estrato", "areaconst", "banios", "habitac", "tipo", "barrio", "parquea", "piso")])) != length(c("zona", "estrato", "areaconst", "banios", "habitac", "tipo", "barrio", "parquea", "piso")), ]
md.pattern(df,rotate.names = TRUE)
## zona estrato preciom areaconst banios habitac tipo barrio parquea piso
## 4812 1 1 1 1 1 1 1 1 1 1
## 1912 1 1 1 1 1 1 1 1 1 0
## 877 1 1 1 1 1 1 1 1 0 1
## 726 1 1 1 1 1 1 1 1 0 0
## 0 0 0 0 0 0 0 0 1603 2638
##
## 4812 0
## 1912 1
## 877 1
## 726 2
## 4241
title(main = "Análisis de nulidad", sub = "Gráfica #2: Análisis de nulidad", adj = 0, cex.main = 1.5)
md_pat
## preciom zona estrato areaconst banios habitac tipo barrio parquea piso
## 4812 1 1 1 1 1 1 1 1 1 1
## 1912 1 1 1 1 1 1 1 1 1 0
## 877 1 1 1 1 1 1 1 1 0 1
## 726 1 1 1 1 1 1 1 1 0 0
## 1 1 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0
## 2 3 3 3 3 3 3 3 1606 2641
##
## 4812 0
## 1912 1
## 877 1
## 726 2
## 1 9
## 2 10
## 4270
Se realiza un análisis más detallado de la variable “parqueadero” y “piso” debido a la alta cantidad de valores nulos en comparación con el total de datos. Se llevan a cabo pruebas de hipótesis para comprender mejor la razón detrás de esta ausencia de información. Se investiga la relación entre la disponibilidad de la variable “parqueadero” y “piso” con otras características de las viviendas, como el precio, la zona, el area y el tipo de vivienda. Esto busca identificar posibles patrones que puedan explicar la falta de información sobre la falta de información.
Además, los coeficientes de variación (CV) calculados para las dos submuestras muestran que la variabilidad relativa de los precios es del 71.47% en el grupo con valores nulos en parqueadero. y del 87.85% en el grupo sin valores nulos. Esto sugiere que la variabilidad de los precios es ligeramente más alta en el grupo sin valores nulos en comparación con el grupo con valores nulos en parqueadero.
na_parquea = is.na(df$parquea)
t.test(df$preciom~na_parquea)
##
## Welch Two Sample t-test
##
## data: df$preciom by na_parquea
## t = 24.246, df = 3105.4, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group FALSE and group TRUE is not equal to 0
## 95 percent confidence interval:
## 167.4200 196.8809
## sample estimates:
## mean in group FALSE mean in group TRUE
## 469.3183 287.1678
# Calcular el coeficiente de variación para la variable preciom con parquea
cv_con_parquea <- sd(df$preciom[!is.na(df$parquea)]) / mean(df$preciom[!is.na(df$parquea)]) * 100
# Calcular el coeficiente de variación para la variable preciom sin parquea
cv_sin_parquea <- sd(df$preciom[is.na(df$parquea)]) / mean(df$preciom[is.na(df$parquea)]) * 100
# Imprimir los resultados
cat("CV del precio con parquea null:", cv_con_parquea, "- Tot observaciones: ",length(df$preciom[is.na(df$parquea)]), "\n")
## CV del precio con parquea null: 71.47443 - Tot observaciones: 1603
cat("CV del precio sin parquea null:", cv_sin_parquea, "- Tot observaciones: ",length(df$preciom[!is.na(df$parquea)]), "\n")
## CV del precio sin parquea null: 87.85446 - Tot observaciones: 6724
– Nulidad de parqueadero vs tipo: El análisis de la asociación entre la presencia de valores nulos en la variable parqueadero y la zona geográfica de la vivienda (Centro, Sur, Norte, Este, Oeste) revela una relación significativa entre ambas variables. La tabla de contingencia muestra una distribución desigual de valores nulos y no nulos para cada zona de la vivienda. Además, la prueba de chi-cuadrado de Pearson con la corrección de continuidad de Yates confirma esta asociación, mostrando un estadístico de prueba significativo (X-squared = 769.95) y un valor p muy bajo (p-value < 2.2e-16), lo que indica una relación estadísticamente significativa entre ambas variables.
tbl_parquea <- table(df$zona, na_parquea)
prop.table(tbl_parquea)
## na_parquea
## FALSE TRUE
## Centro 0.007685841 0.007205476
## Norte 0.154797646 0.076017774
## Oeste 0.132460670 0.012129218
## Oriente 0.019574877 0.022577159
## Sur 0.492974661 0.074576678
chisq.test(tbl_parquea)
##
## Pearson's Chi-squared test
##
## data: tbl_parquea
## X-squared = 769.95, df = 4, p-value < 2.2e-16
El valor p es extremadamente bajo (2.185e-10), lo que indica una fuerte evidencia en contra de la hipótesis nula de que las medias de ambas muestras son iguales. Además, los coeficientes de variación (CV) para areaconst son relativamente similares entre los grupos, siendo del 81.37% para el grupo con valores nulos en “piso” y del 81.28% para el grupo sin valores nulos.
Estos resultados sugieren una consistencia en la variabilidad de areaconst independientemente de la presencia de valores nulos en la variable “piso
na_piso = is.na(df$piso)
t.test(df$areaconst~na_piso)
##
## Welch Two Sample t-test
##
## data: df$areaconst by na_piso
## t = -6.3619, df = 4611.4, p-value = 2.185e-10
## alternative hypothesis: true difference in means between group FALSE and group TRUE is not equal to 0
## 95 percent confidence interval:
## -29.24283 -15.46561
## sample estimates:
## mean in group FALSE mean in group TRUE
## 167.9058 190.2600
# Calcular el coeficiente de variación para la variable areaconst con piso
cv_con_piso <- sd(df$areaconst[!is.na(df$piso)]) / mean(df$areaconst[!is.na(df$piso)]) * 100
# Calcular el coeficiente de variación para la variable areaconst sin piso
cv_sin_piso <- sd(df$areaconst[is.na(df$piso)]) / mean(df$areaconst[is.na(df$piso)]) * 100
# Imprimir los resultados
cat("CV del areaconst con parquea null:", cv_con_piso, "- Tot observaciones: ",length(df$areaconst[is.na(df$piso)]), "\n")
## CV del areaconst con parquea null: 81.37128 - Tot observaciones: 2638
cat("CV del areaconst sin parquea null:", cv_sin_piso, "- Tot observaciones: ",length(df$areaconst[!is.na(df$piso)]), "\n")
## CV del areaconst sin parquea null: 81.27873 - Tot observaciones: 5689
tabla_piso <- table(df$tipo, na_piso)
prop.table(tabla_piso)
## na_piso
## FALSE TRUE
## apartamento 0.4470998 0.1660862
## casa 0.2360994 0.1507145
chisq.test(tabla_piso)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tabla_piso
## X-squared = 128.18, df = 1, p-value < 2.2e-16
Durante esta fase, se lleva a cabo un análisis detallado de varias variables y las relaciones entre ellas. Este análisis se basa en gráficos y tablas adaptados a la naturaleza de las variables en estudio, lo que proporciona una comprensión más profunda de los datos y sus interacciones.
El análisis descriptivo univariable implica explorar una sola variable a la vez, examinando sus características y distribución sin tener en cuenta otras variables. Su objetivo principal es obtener una comprensión inicial y detallada de una variable específica en un conjunto de datos. Este tipo de análisis es fundamental para identificar posibles errores en los datos, validar la calidad de la información y determinar qué métodos estadísticos o gráficos serían adecuados para análisis posteriores. En resumen, el análisis descriptivo univariable proporciona una base sólida para explorar y comprender las características de una sola variable antes de avanzar hacia análisis más complejos que involucren múltiples variables. Para este propósito, se recurre a las siguientes herramientas:
Como resultado del análisis descriptivo, se infiere que las observaciones se concentran en los estratos más altos de la ciudad de Cali, lo cual influye en las demás variables:
Estos hallazgos proporcionan una comprensión detallada de las características del conjunto de datos, destacando la influencia del estrato socioeconómico en las características de las viviendas en la ciudad de Cali.
grafica_barra <- function(columna, nombre, color) {
# Ajustar los márgenes del gráfico
par(mar = c(5, 5, 4, 5)) # margen izquierdo, derecho, inferior, superior
# Calcular el conteo de valores nulos en la variable "columna"
N <- sum(is.na(columna))
# Calcular el conteo de valores para cada nivel de "columna" (incluyendo los nulos)
conteo_ <- table(columna)
conteo_ <- c(conteo_, n = N)
# Calcular el porcentaje de cada barra
porcentaje_ <- prop.table(conteo_) * 100
# Crear un vector con los nombres de los niveles de "columna" (incluyendo "Nulos")
nombres_ <- names(conteo_)
# Agregar etiquetas con los valores de frecuencia sobre las barras
text(x = barplot(conteo_, ylim = c(1, max(conteo_) * 1.2), main = paste("Distribucción de" , nombre), xlab = nombre, ylab = "Cantidad", col = color, cex.names = 0.8), y = conteo_, label = paste(conteo_,"\n", round(porcentaje_,2), "%", sep = ""),
pos = 3, cex = 0.8, srt = 0)
}
grafica_barra(df$estrato, "Estratos", "skyblue")
grafica_barra(df$piso, "Pisos", "yellow")
grafica_barra(df$habitac, "Habitaciones", "green")
grafica_barra(df$banios, "Banios", "pink")
grafica_barra(df$parquea, "Parqueaderos", "brown")
#grafica_barra(df_zona, "Zona", "magenta")
# Histograma de la variable area
# Calcular la moda, media, mediana, mínimo y máximo
moda_area <- names(sort(-table(df$areaconst)))[1]
media_area <- mean(df$areaconst)
mediana_area <- median(df$areaconst)
minimo_area <- min(df$areaconst)
maximo_area <- max(df$areaconst)
# Crear el histograma
hist_data1 <- hist(df$areaconst,
main = "Distribución del área construida en metros cuadrados",
xlim = c(0, 1500),
ylim = c(1, max(table(df$areaconst)) * 20),
las = 1,
xlab = "Área (m2)",
ylab = "Cantidad de Viviendas",
col = "black",
breaks = 10)
# Agregar las leyendas
legend("topright",
legend = c(paste("Moda:", moda_area, "m2"),
paste("Media:", round(media_area, 2), "m2"),
paste("Mediana:", mediana_area, "m2"),
paste("Mínimo:", minimo_area, "m2"),
paste("Máximo:", maximo_area, "m2")),
col = "black",
lwd = 1,
cex = 0.8)
# Agregar las etiquetas de frecuencia
text(hist_data1$mids, hist_data1$counts, labels = hist_data1$counts, pos = 3)
# Calcular la moda de manera manual
moda_precio <- names(sort(-table(df$preciom)))[1]
# Calcular la media, mediana, mínimo y máximo
media_precio <- mean(df$preciom)
mediana_precio <- median(df$preciom)
minimo_precio <- min(df$preciom)
maximo_precio <- max(df$preciom)
# Crear el histograma
hist_data2 <- hist(df$preciom,
main = "Distribución del precio en millones",
xlim = c(0, 1500),
ylim = c(1, max(table(df$preciom)) * 20),
las = 1,
xlab = "Precio (millones)",
ylab = "Cantidad de Viviendas",
col = "black",
breaks = 10)
# Agregar las leyendas
legend("topright",
legend = c(paste("Moda:", moda_precio, "millones"),
paste("Media:", round(media_precio, 2), "millones"),
paste("Mediana:", mediana_precio, "millones"),
paste("Mínimo:", minimo_precio, "millones"),
paste("Máximo:", maximo_precio, "millones")),
col = "black",
lwd = 1,
cex = 0.8)
# Agregar las etiquetas de frecuencia
text(hist_data2$mids, hist_data2$counts, labels = hist_data2$counts, pos = 3)
tabla_tipo <- table(df$tipo)
# Convertir la tabla en un dataframe
df_tabla_tipo <- as.data.frame.table(tabla_tipo)
# Renombrar las columnas
names(df_tabla_tipo) <- c("Tipo", "Cantidad")
# Calcular los porcentajes
df_tabla_tipo$Porcentaje <- round(df_tabla_tipo$Cantidad / sum(df_tabla_tipo$Cantidad) * 100, 2)
# Crear el gráfico de torta utilizando ggplot2
ggplot(df_tabla_tipo, aes(x = "", y = Cantidad, fill = Tipo)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start = 0) +
geom_text(aes(label = paste0(Cantidad, " (", Porcentaje, "%)")),
position = position_stack(vjust = 0.5)) + labs(title = "Distribución de tipos de vivienda") +
theme_minimal() +
theme(legend.position = "right") +
guides(fill = guide_legend(title = "Tipo"))
# Crear la tabla de frecuencias excluyendo los valores nulos
tabla_zona <- table(df$zona)
# Convertir la tabla en un dataframe
df_tabla_zona <- as.data.frame.table(tabla_zona)
# Renombrar las columnas
names(df_tabla_zona) <- c("Zona", "Cantidad")
# Calcular los porcentajes
df_tabla_zona$Porcentaje <- round(df_tabla_zona$Cantidad / sum(df_tabla_zona$Cantidad) * 100, 2)
# Crear el gráfico de torta utilizando ggplot2
ggplot(df_tabla_zona, aes(x = "", y = Cantidad, fill = Zona)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start = 0) +
geom_text(aes(label = paste0(Cantidad, " (", Porcentaje, "%)")),
position = position_stack(vjust = 0.5)) +
labs(title = "Distribución de zonas de vivienda") +
theme_minimal() +
theme(legend.position = "right") +
guides(fill = guide_legend(title = "Zona"))
Se realiza una matriz de correlación entre las variables numéricas libres de valores nulos. Dentro de este análisis, se destaca una correlación significativa y positiva del precio con el estrato, el área construida y la cantidad de baños. Esto indica que el precio tiende a aumentar conforme crecen estas variables. Estos hallazgos confirman las suposiciones iniciales, respaldadas ahora por datos concretos.
df_corr <- df[sapply(df, is.numeric)]
# Eliminar las variables de piso y `parqueadero para la correlación, ya que contiene datos nulos
df_corr <- subset(df_corr, select = -c(piso, parquea))
corr <- cor(df_corr)
# Crea el gráfico de matriz de correlación con círculos y números
corrplot(corr, method = "number", type = "upper",
tl.col = "black", tl.srt = 45, number.cex = 1, number.digits = 2)
#### Variable precio Dado que el precio es una de las variables más
críticas en nuestro análisis, según lo indicado en la matriz de
correlación, es fundamental examinar detalladamente esta variable y su
relación con otras.
Dado que hemos identificado una correlación positiva fuerte entre el precio y el área construida en nuestra matriz de correlación, es crucial explorar esta relación más a fondo mediante un gráfico de dispersión. Al observar el gráfico, confirmamos una tendencia positiva y lineal entre ambas variables. Esta conclusión sugiere que el precio de una vivienda está influenciado de manera significativa por su tamaño. En resumen, este análisis proporciona una comprensión más clara de cómo el tamaño de una vivienda impacta su precio.
# Crea un gráfico de dispersión entre areaconst y preciom
plot(df$areaconst, df$preciom,
xlab = "Área Construida (m^2)",
ylab = "Precio (millones)",
main = "Relación entre Área Construida y Precio",
col = "blue",
pch = 16) # Define el tipo de punto
# Agrega una línea de regresión lineal
abline(lm(df$preciom ~ df$areaconst), col = "red")
Habiendo realizado un análisis previo, se observó que más del 80% de las observaciones se concentraban en los estratos 4, 5 y 6. Este hallazgo sugiere un sesgo en los datos hacia viviendas con estándares más altos que el promedio de la ciudad. Como resultado, predomina la presencia de apartamentos sobre casas en el conjunto de datos. Esta tendencia puede atribuirse al alto grado de construcción de edificaciones en dichos sectores, lo que refleja una preferencia por este tipo de viviendas en áreas con estándares socioeconómicos más altos.
# Crear el gráfico de barras apilado con etiquetas
ggplot(df, aes(x = estrato, fill = tipo)) +
geom_bar(position = "stack") +
geom_text(aes(label = ..count..), stat = "count", position = position_stack(vjust = 0.5)) +
labs(title = "Cantidad de viviendas en cada estrato por su tipo",
x = "Estrato",
y = "Cantidad") +
scale_fill_manual(values = c("blue", "red")) +
theme_minimal()
#### Relación entre el precio y la zona
La relación entre el precio de las viviendas y su ubicación geográfica es esencial en el mercado inmobiliario. Para analizar esta relación, utilizamos un diagrama de cajas y alambres, obteniendo los siguientes resultados:
Q1, Mediana y Q3: Estos valores representan los percentiles 25, 50 y 75 respectivamente.
Observamos que el Centro y el Oriente tienen los precios más bajos en el primer cuartil (Q1), con 188.75 y 145, respectivamente. Mientras que el Oeste tiene el precio más alto en el primer cuartil con 394.50. En cuanto a la mediana, el Oriente tiene el valor más bajo con 210, seguido del Centro con 297, y el Oeste con el valor más alto de 580. Para el tercer cuartil (Q3), nuevamente el Oriente tiene el valor más bajo con 290, seguido del Centro con 361.25, y el Oeste con el valor más alto de 900. Rango Intercuartílico (IQR): El rango intercuartílico es la diferencia entre el tercer cuartil (Q3) y el primer cuartil (Q1). Indica la dispersión de los datos en torno a la mediana.
El Centro tiene el IQR más bajo con 172.5, mientras que el Oeste tiene el IQR más alto con 505.5. Esto sugiere una mayor variabilidad en los precios en el Oeste en comparación con otras zonas. Los datos que están significativamente por encima o por debajo de estos valores podrían considerarse atípicos.
En resumen, estos datos nos brindan una idea clara de la distribución de los precios de las viviendas en diferentes zonas de la ciudad, destacando diferencias significativas tanto entre las zonas como dentro de cada una.
ggplot(df, aes(x = zona, y = preciom)) +
geom_boxplot(fill = "pink") +
labs(x = "Zona",
y = "Precio",
title = "Precio de las viviendas por zona") +
theme(legend.position = "none",
plot.title = element_text(hjust = 0.5),
panel.background = element_rect(fill = "white")
)
iqr_data <- df %>%
group_by(zona) %>%
summarize(Q1 = quantile(preciom, 0.25),
median = median(preciom),
Q3 = quantile(preciom, 0.75),
IQR = Q3 - Q1)
ggplot(iqr_data, aes(x = zona, y = Q1)) +
geom_text(aes(label = paste0("Q1: ", round(Q1, 2))), vjust = -0.5) +
geom_text(aes(y = median, label = paste0("Median: ", round(median, 2))), vjust = -0.5) +
geom_text(aes(y = Q3, label = paste0("Q3: ", round(Q3, 2))), vjust = -0.5) +
geom_text(aes(y = Q3 + IQR * 0.5, label = paste0("IQR: ", round(IQR, 2))), vjust = -0.5, color = "blue") +
labs(x = "Zona",
y = "Precio",
title = "Resumen estadístico del precio por zona") +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(hjust = 0.5),
panel.background = element_rect(fill = "white"))
#### Precio promedio y cantidad de viviendas vs Zona/Tipo Finalmente,
llevamos a cabo un análisis multivariable de los precios promedio y la
cantidad de viviendas en función de la zona y el tipo de vivienda. Los
resultados revelan lo siguiente:
Las zonas Centro y Sur tienen precios promedio intermedios en comparación con las demás zonas. - Diferencias entre tipos de vivienda: En todas las zonas, los precios promedio de las casas tienden a ser más altos que los de los apartamentos. Esta tendencia es especialmente evidente en la zona Oeste, donde la diferencia de precios entre casas y apartamentos es notable.
En resumen, estos resultados sugieren que el precio de las viviendas está fuertemente influenciado por la ubicación geográfica (zona) y el tipo de vivienda. La zona Oeste destaca por tener los precios promedio más altos, mientras que la zona Oriente muestra los precios promedio más bajos. La preferencia por apartamentos sobre casas es evidente en todas las zonas, excepto en la zona Centro.
library(dplyr)
# Calcular el precio promedio y la cantidad por combinación de zona y tipo
df_summary <- df %>%
group_by(zona, tipo) %>%
summarise(avg_price = mean(preciom),
count = n())
# Crear el gráfico de barras agrupadas sin estrato y con etiquetas de cantidad
ggplot(df_summary, aes(x = zona, y = avg_price, fill = tipo)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = count), position = position_dodge(width = 0.9), vjust = -0.5) + # Agregar etiquetas de cantidad
labs(title = "Precio promedio y cantidad total por zona y tipo de vivienda",
x = "Zona",
y = "Precio promedio") +
scale_y_continuous(labels = scales::dollar_format()) + # Cambiar formato del eje y a dólares
theme_minimal() +
theme(legend.position = "top")
#### Cantidad de viviendas vendidas segun la cantidad de habitaciones y
baños El análisis de la gráfica proporcionada revela la distribución de
las viviendas vendidas según la cantidad de habitaciones y baños
agrupadas en rangos específicos. Aquí algunas observaciones
importantes:
Distribución asimétrica: La mayoría de las viviendas vendidas tienen entre 2 y 4 habitaciones, así como entre 2 y 4 baños. Este rango tiene la mayor cantidad de viviendas vendidas en comparación con otros grupos.
Viviendas con múltiples habitaciones y baños: Aunque la cantidad de viviendas con más de 4 habitaciones o más de 4 baños es menor en comparación con las viviendas con 2-4 habitaciones y baños, todavía hay algunas viviendas con estas características en el mercado.
En resumen, este análisis proporciona información valiosa sobre las preferencias de los compradores de viviendas en términos de la cantidad de habitaciones y baños. Esto puede ser útil para los agentes inmobiliarios y desarrolladores para adaptar sus estrategias de venta y diseño de propiedades según las demandas del mercado.
# Agrupar la cantidad de habitaciones y baños de dos en dos
df <- df %>%
mutate(habitaciones_agrupadas = cut(habitac, breaks = seq(0, max(df$habitac) + 2, by = 2), right = FALSE),
banios_agrupados = cut(banios, breaks = seq(0, max(df$banios) + 2, by = 2), right = FALSE))
# Crear la gráfica
ggplot(df, aes(x = habitaciones_agrupadas, fill = banios_agrupados)) +
geom_bar(position = "dodge", color = "black") +
labs(title = "Cantidad de viviendas vendidas por cantidad de habitaciones y baños",
x = "Cantidad de habitaciones (agrupadas de dos en dos)",
y = "Cantidad de viviendas vendidas",
fill = "Cantidad de baños") +
theme_minimal()
Los resultados obtenidos a partir del análisis descriptivo y multivariable proporcionan una visión detallada y significativa del mercado inmobiliario de la ciudad. En primer lugar, se destaca la marcada preferencia por los apartamentos en comparación con las casas, lo que refleja una tendencia hacia un estilo de vida más compacto y urbano entre la población estudiada. Este hallazgo es coherente con las tendencias observadas en otras ciudades metropolitanas, donde la disponibilidad de servicios y la proximidad a áreas comerciales y de trabajo son factores determinantes en la elección de la vivienda.
Además, se identificó una variación considerable en los precios promedio entre las diferentes zonas de la ciudad. Esta disparidad puede atribuirse a una combinación de factores, como la ubicación geográfica, las características específicas de las propiedades y la demanda del mercado en cada área. Por ejemplo, el Oeste emerge como la zona con los precios más altos en promedio, lo que puede estar relacionado con su reputación como una zona residencial exclusiva y con excelentes servicios.
Otro aspecto destacado es la influencia del estrato socioeconómico en los precios de las viviendas, donde se observa una clara relación entre el estrato y el precio. Este fenómeno subraya la importancia del estatus socioeconómico percibido en la determinación del valor de una propiedad, lo que puede afectar las decisiones de compra y venta en el mercado inmobiliario.
En cuanto al análisis de la cantidad de habitaciones y baños en las viviendas vendidas, se encontró una distribución asimétrica, con la mayoría de las viviendas ubicadas en el rango de 2-4 habitaciones y baños. Este hallazgo sugiere una preferencia generalizada por viviendas con un número moderado de habitaciones y baños entre los compradores, aunque aún existe una demanda considerable de propiedades más grandes.
Este estudio resalta la importancia del análisis exploratorio de datos en la comprensión del mercado inmobiliario, destacando la necesidad de examinar cuidadosamente las relaciones y tendencias que subyacen en los datos. Mediante el uso de técnicas estadísticas descriptivas y herramientas visuales como gráficos y tablas, hemos podido identificar patrones significativos y extraer insights valiosos que informan decisiones clave en el sector.
La selección adecuada de variables, la imputación o eliminación de observaciones y el contexto de la base de datos son elementos fundamentales que influyen en la calidad y la relevancia de los análisis realizados. La elección de variables pertinentes y representativas, junto con una gestión adecuada de los datos faltantes o atípicos, garantiza la fiabilidad y la validez de los resultados obtenidos. En última instancia, este enfoque riguroso y contextualizado sienta las bases para una toma de decisiones más informada y una comprensión más profunda de los mercados inmobiliarios locales.