UNIVERSIDAD PONTIFICIA JAVERIANA CALI



Maestría en Ciencia de Datos



Actividad 1: Evaluación de la Oferta Inmobiliaria Urbana




Presentado por:
Dainer Orlando Cruz Chate




Modelos Estadísticos para la Toma de Decisiones




2026


Introducción.

La comprensión del mercado inmobiliario urbano constituye un elemento fundamental para la toma de decisiones estratégicas en empresas dedicadas a la compra, venta y valoración de bienes raíces. En un entorno caracterizado por una amplia diversidad de inmuebles, variaciones en los precios de oferta y múltiples factores físicos, espaciales y socioeconómicos que influyen en la formación del valor de las viviendas, resulta necesario aplicar metodologías analíticas que permitan transformar grandes volúmenes de datos en información útil para el apoyo a las decisiones empresariales. En este contexto, la estadística multivariada se ha consolidado como una herramienta fundamental para identificar patrones ocultos, reducir la complejidad de la información y facilitar la interpretación de fenómenos complejos mediante el análisis simultáneo de múltiples variables [1], [2].

La presente actividad se desarrolla a partir de una base de datos real correspondiente a viviendas ofertadas en el mercado inmobiliario urbano, con el propósito de analizar integralmente las características que influyen en el comportamiento de la oferta y los precios de los inmuebles. Este tipo de análisis cobra especial relevancia debido a que la valoración de una vivienda depende de factores diversos, tales como el área construida, el número de habitaciones, la ubicación geográfica, las características del entorno urbano y el estrato socioeconómico, entre otros aspectos que interactúan de manera simultánea y generan patrones difíciles de identificar mediante análisis univariados o bivariados tradicionales [3], [4].

Con el fin de abordar esta problemática, se emplean diferentes técnicas estadísticas multivariadas orientadas a reconocer las estructuras subyacentes de los datos y apoyar la toma de decisiones basada en evidencia. En primer lugar, el Análisis de Componentes Principales (ACP) permite reducir la dimensionalidad del conjunto de datos al transformar un gran número de variables correlacionadas en un conjunto reducido de componentes que conservan la mayor proporción posible de la variabilidad original. Esta técnica facilita la identificación de los factores más influyentes en la determinación de los precios y en la configuración de la oferta inmobiliaria [1], [5].

De manera complementaria, el Análisis de Conglomerados se utiliza para identificar grupos homogéneos de viviendas con características similares. La segmentación obtenida permite reconocer perfiles de inmuebles y zonas de mercado que comparten comportamientos comunes, proporcionando información valiosa para la formulación de estrategias comerciales, la identificación de nichos de mercado y la evaluación de oportunidades de inversión [2], [6].

Asimismo, el Análisis de Correspondencias constituye una herramienta adecuada para estudiar las relaciones existentes entre variables categóricas, tales como el tipo de vivienda, la zona urbana y el barrio. Esta metodología permite representar gráficamente las asociaciones entre categorías y facilita la comprensión de los patrones de comportamiento presentes en el mercado inmobiliario, aportando una visión complementaria al análisis cuantitativo de las variables numéricas [2], [7].

Adicionalmente, la visualización de datos desempeña un papel fundamental dentro del proceso analítico, ya que permite comunicar de manera clara y efectiva los resultados obtenidos. La utilización de gráficos multivariados, mapas temáticos y representaciones bidimensionales de componentes o conglomerados favorece la interpretación de los hallazgos y facilita la transferencia del conocimiento generado hacia los responsables de la toma de decisiones dentro de la organización [8].

En concordancia con los objetivos planteados, el presente informe reconoce los diferentes elementos que componen las técnicas estadísticas multivariadas empleadas, utiliza herramientas apropiadas para la solución de problemas reales asociados al mercado inmobiliario urbano y presenta los resultados siguiendo la estructura formal de un informe estadístico, incluyendo el análisis de resultados, conclusiones, recomendaciones y anexos correspondientes. De esta manera, se busca generar conocimiento útil que contribuya a mejorar los procesos de valoración, segmentación y gestión estratégica de la oferta inmobiliaria, fortaleciendo la capacidad de la empresa para competir en un mercado dinámico y altamente competitivo [4], [6].


Contexto del Proyecto.

En los últimos años, el mercado inmobiliario urbano se ha caracterizado por una dinámica cada vez más compleja, influenciada por factores económicos, sociales, demográficos y territoriales que afectan de manera directa el comportamiento de la oferta y la demanda de vivienda. La creciente disponibilidad de información proveniente de portales inmobiliarios, empresas del sector y plataformas digitales ha generado grandes volúmenes de datos que contienen información valiosa sobre las características físicas, económicas y geográficas de los inmuebles ofertados. Sin embargo, la existencia de estos datos no garantiza por sí sola una adecuada comprensión del mercado, razón por la cual resulta necesario emplear técnicas estadísticas que permitan transformar los datos en conocimiento útil para la toma de decisiones.

En este contexto, una empresa inmobiliaria líder en una gran ciudad dispone de una extensa base de datos con información relacionada con diferentes propiedades residenciales disponibles en el mercado. Esta información incluye variables asociadas al precio de oferta, área construida, ubicación, estrato socioeconómico, características físicas de las viviendas y otras variables relevantes para la valoración de inmuebles. No obstante, debido a la naturaleza multidimensional de estos datos, resulta difícil identificar patrones de comportamiento, relaciones entre variables y segmentos de mercado mediante análisis descriptivos tradicionales.

La necesidad de comprender de manera integral el comportamiento de la oferta inmobiliaria ha llevado a que las organizaciones del sector incorporen metodologías analíticas avanzadas que faciliten la identificación de factores asociados al valor de los inmuebles, la caracterización de segmentos homogéneos y el reconocimiento de oportunidades de negocio. En este sentido, las técnicas de análisis multivariado constituyen una alternativa adecuada para explorar simultáneamente múltiples variables y descubrir estructuras subyacentes que no son evidentes mediante métodos convencionales de análisis [1], [2].

Por lo anterior, este proyecto propone desarrollar un análisis holístico del mercado inmobiliario urbano utilizando herramientas estadísticas multivariadas como el Análisis de Componentes Principales (ACP), el Análisis de Conglomerados y el Análisis de Correspondencias. Estas técnicas permitirán reducir la complejidad de la información, identificar factores determinantes en la variación de los precios, segmentar las viviendas en grupos con características similares y analizar las asociaciones existentes entre variables categóricas relevantes para el sector inmobiliario. Los resultados obtenidos aportarán información estratégica para apoyar la toma de decisiones relacionadas con la compra, venta y valoración de propiedades en un entorno cada vez más competitivo y orientado al uso de datos.

Justificación.

La toma de decisiones en el sector inmobiliario exige cada vez mayores niveles de precisión y soporte analítico. Aspectos como la valoración adecuada de los inmuebles, la identificación de oportunidades de inversión, la segmentación de mercados y el diseño de estrategias comerciales dependen en gran medida de la capacidad de las organizaciones para interpretar correctamente la información disponible. En consecuencia, el análisis de datos se ha convertido en un elemento fundamental para fortalecer la competitividad empresarial y reducir la incertidumbre asociada a las decisiones de negocio.

La presente investigación se justifica desde una perspectiva metodológica, práctica y estratégica. Desde el punto de vista metodológico, permite aplicar técnicas de estadística multivariada ampliamente reconocidas en la literatura científica para analizar fenómenos complejos caracterizados por la interacción simultánea de múltiples variables. El uso de herramientas como el Análisis de Componentes Principales, el Análisis de Conglomerados y el Análisis de Correspondencias facilita la identificación de patrones ocultos y la generación de conocimiento a partir de grandes volúmenes de información [2], [5].

Desde una perspectiva práctica, el proyecto permitirá comprender los factores que influyen en los precios de oferta de las viviendas, identificar grupos homogéneos de inmuebles y establecer relaciones entre características relevantes del mercado. Esta información resulta particularmente útil para optimizar los procesos de valoración inmobiliaria, mejorar las estrategias comerciales y orientar las decisiones de inversión hacia segmentos con mayor potencial de rentabilidad.

Asimismo, el proyecto aporta valor estratégico a la organización al proporcionar una visión integral del mercado inmobiliario urbano. La identificación de segmentos de mercado, la comprensión del comportamiento de la oferta y la detección de patrones asociados a determinadas zonas o tipologías de vivienda permiten diseñar estrategias basadas en evidencia y reducir el riesgo derivado de decisiones fundamentadas únicamente en la experiencia o el juicio subjetivo.

Finalmente, este estudio contribuye al fortalecimiento de las competencias analíticas asociadas a la Ciencia de Datos y a los Modelos Estadísticos para la Toma de Decisiones, permitiendo aplicar conocimientos teóricos a una problemática real mediante el uso de herramientas estadísticas apropiadas. De esta manera, el proyecto cumple con los criterios académicos establecidos al reconocer los elementos fundamentales de las técnicas empleadas, aplicar métodos estadísticos adecuados para resolver un problema en contexto y presentar los resultados bajo la estructura formal de un informe estadístico con sus respectivos anexos.

Propósito.

Analizar integralmente la oferta inmobiliaria urbana mediante técnicas de estadística multivariada para identificar patrones, relaciones y segmentos de mercado que contribuyan a mejorar la toma de decisiones estratégicas de la empresa inmobiliaria.

Alcance.

El presente proyecto tiene como alcance el análisis integral de una base de datos correspondiente a la oferta inmobiliaria urbana de una gran ciudad, con el propósito de identificar patrones, relaciones y segmentaciones relevantes que contribuyan a mejorar los procesos de compra, venta y valoración de propiedades por parte de una empresa inmobiliaria.

El estudio se desarrollará mediante la aplicación de técnicas de estadística multivariada sobre la información disponible de las viviendas ofertadas, considerando variables tanto cuantitativas como cualitativas relacionadas con las características físicas, económicas y geográficas de los inmuebles.

Alcance analítico.

El proyecto contempla las siguientes actividades:

1. Comprensión y exploración de los datos.

  • Identificación de las variables disponibles.
  • Evaluación de la calidad de los datos.
  • Detección de valores faltantes, atípicos e inconsistencias.
  • Generación de estadísticas descriptivas iniciales.

2. Preparación de la información.

  • Limpieza y depuración de los datos.
  • Transformación y estandarización de variables cuando sea necesario.
  • Construcción de la base analítica para la aplicación de técnicas multivariadas.

3. Análisis de Componentes Principales (ACP).

  • Identificación de los factores que explican la mayor variabilidad de los datos.
  • Reducción de la dimensionalidad del conjunto de variables.
  • Interpretación de los componentes principales asociados al comportamiento del mercado inmobiliario.

4. Análisis de Conglomerados.

  • Segmentación de las viviendas en grupos homogéneos.
  • Caracterización de cada segmento identificado.
  • Identificación de oportunidades comerciales asociadas a cada grupo.

5. Análisis de Correspondencias.

  • Evaluación de las relaciones existentes entre variables categóricas.
  • Identificación de asociaciones entre tipo de vivienda, barrio, zona y demás categorías disponibles.
  • Representación gráfica de dichas relaciones.

6. Visualización de resultados.

  • Elaboración de gráficos descriptivos.
  • Mapas perceptuales de componentes principales.
  • Dendrogramas y gráficas de agrupamiento.
  • Representaciones bidimensionales de correspondencias.

7. Interpretación y formulación de recomendaciones.

  • Análisis de hallazgos relevantes.
  • Generación de conclusiones sustentadas en evidencia estadística.
  • Formulación de recomendaciones para la toma de decisiones estratégicas.

Alcance geográfico.

El análisis se limitará a las viviendas urbanas contenidas en la base de datos suministrada para el caso de estudio en Cali. Los resultados serán válidos para el contexto y período representado en dicha información.

Alcance temporal.

El proyecto comprende únicamente el análisis de la información disponible en la base de datos entregada. No contempla la actualización en tiempo real de los datos ni la construcción de modelos predictivos futuros basados en nuevas observaciones.

Exclusiones del proyecto.

Las siguientes actividades están fuera del alcance del estudio:

Desarrollo de modelos de predicción de precios mediante Machine Learning. Implementación de sistemas transaccionales para la empresa. Recolección de nuevos datos en campo. Valoración individual de inmuebles específicos. Estudios financieros, jurídicos o tributarios asociados a las propiedades. Implementación de plataformas tecnológicas o sistemas de información.


Metodología para el Desarrollo del Proyecto - Evaluación de la Oferta Inmobiliaria Urbana mediante Técnicas de Estadística Multivariada.

El proyecto se desarrollará bajo un enfoque cuantitativo, exploratorio y descriptivo, apoyado en técnicas de análisis estadístico multivariado. La metodología busca transformar los datos disponibles en información útil para comprender el comportamiento del mercado inmobiliario urbano y facilitar la toma de decisiones estratégicas relacionadas con la compra, venta y valoración de propiedades.

La metodología propuesta se compone de siete fases secuenciales que permiten garantizar la calidad de los datos, la rigurosidad del análisis y la adecuada interpretación de los resultados:

FASE 1 - Comprensión del problema estadístico.

Actividades:

  • Definición del problema estadístico.
  • Pregunta analítica.
  • Criterios de éxito del proyecto.

FASE 2. Exploración y diagnóstico de los datos.

Tabla 1. Exploracion y diagnostico de los datos
Componente Descripcion Finalidad
Objetivo Identificar la estructura, calidad y caracteristicas generales de la informacion disponible. Comprender la estructura general de la base de datos y establecer su estado inicial.
Inventario de variables Clasificacion de variables cuantitativas.
Clasificacion de variables cualitativas.
Identificacion de la variable principal de interes (precio).
Identificar las variables disponibles y definir su papel dentro del analisis.
Analisis descriptivo Variables numericas: media, mediana, desviacion estandar, coeficiente de variacion, valores minimos y maximos.
Variables categoricas: frecuencias absolutas, frecuencias relativas y tablas de contingencia.
Caracterizar estadisticamente la informacion disponible.
Evaluacion de calidad de datos Identificacion de valores faltantes.
Deteccion de registros duplicados.
Deteccion de datos atipicos.
Verificacion de consistencia de la informacion.
Determinar el nivel de confiabilidad y preparacion de los datos para analisis posteriores.
Herramientas Tablas descriptivas.
Histogramas.
Diagramas de caja y bigotes.
Graficos de barras.
Apoyar la exploracion visual y estadistica de la informacion.
Producto esperado Resumen de la exploracion de datos y diagnostico de calidad. Documentar hallazgos y establecer lineamientos para las fases posteriores.

Fuente: Elaboración Propia.

FASE 3 - Preparación y transformación de datos.

Tabla 2. Preparacion y transformacion de los datos
Componente Descripcion Finalidad
Objetivo Construir una base de datos confiable y adecuada para la aplicacion de tecnicas multivariadas. Garantizar la calidad y consistencia de los datos antes de aplicar tecnicas multivariadas.
Tratamiento de datos faltantes Imputacion estadistica.
Eliminacion de registros cuando sea necesario.
Validacion de completitud de la informacion.
Reducir sesgos derivados de informacion incompleta y mejorar la integridad de la base de datos.
Tratamiento de valores atipicos Identificacion mediante boxplots.
Evaluacion del impacto sobre los resultados.
Decision de correccion o conservacion de los registros.
Controlar observaciones extremas que puedan afectar los resultados de los analisis estadisticos.
Estandarizacion de variables Estandarizacion mediante puntuaciones Z para variables en diferentes escalas, tales como area, precio, habitaciones y banos. Hacer comparables las variables y evitar que las diferencias de escala dominen los resultados.
Producto esperado Base de datos depurada y lista para el analisis multivariado. Disponer de una base de datos adecuada para ACP, Analisis de Conglomerados y Analisis de Correspondencias.

Fuente: Elaboración Propia.

FASE 4: Análisis de Componentes Principales - ACP.

Tabla 3. Analisis de Componentes Principales (ACP)
Componente Descripcion Finalidad
Objetivo Reducir la dimensionalidad del conjunto de datos e identificar los factores que explican la mayor proporcion de la variabilidad observada. Sintetizar la informacion y reducir la complejidad del conjunto de datos sin perder una proporcion significativa de la variabilidad.
Verificacion de aplicabilidad Analisis de la matriz de correlaciones.
Aplicacion de la prueba KMO.
Aplicacion de la prueba de esfericidad de Bartlett.
Verificar que los datos cumplen los supuestos necesarios para la aplicacion del Analisis de Componentes Principales.
Construccion del ACP Obtencion de autovalores.
Determinacion del numero optimo de componentes principales.
Calculo de la varianza explicada acumulada.
Obtener una representacion reducida de los datos mediante componentes principales estadisticamente relevantes.
Interpretacion Identificacion de las variables mas influyentes.
Identificacion de factores asociados al precio.
Identificacion de factores asociados a la oferta inmobiliaria.
Comprender los factores subyacentes que explican la estructura de la oferta inmobiliaria y su relacion con el precio.
Criterio de exito Los componentes retenidos deberan explicar al menos el 70% de la variabilidad total. Garantizar que la reduccion dimensional conserve una capacidad explicativa suficiente para el analisis posterior.
Productos esperados Tabla de componentes principales.
Grafico Scree Plot.
Plano factorial.
Interpretacion de componentes.
Documentar y visualizar los resultados obtenidos para facilitar su interpretacion y comunicacion.

Fuente: Elaboración Propia.

FASE 5: Analisis de Conglomerados.

Una vez identificadas las principales dimensiones que explican la variabilidad del mercado inmobiliario mediante el Análisis de Componentes Principales (ACP), se procede a la aplicación del Análisis de Conglomerados sobre la base vivienda_limpia. Esta técnica permitió agrupar las viviendas según sus características comunes, facilitando la identificación de perfiles homogéneos de inmuebles. De esta manera, se obtuvo una visión más estructurada del comportamiento de la oferta inmobiliaria y de los patrones presentes en los datos analizados.

Tabla 4. Analisis de Conglomerados
Componente Descripcion Finalidad
Objetivo Identificar grupos homogeneos de viviendas con caracteristicas similares. Identificar segmentos de viviendas con patrones y caracteristicas comunes dentro del mercado inmobiliario.
Seleccion de variables Utilizacion de variables originales relevantes.
Incorporacion de los componentes principales obtenidos mediante ACP.
Seleccionar la informacion mas representativa para mejorar la calidad y estabilidad de la segmentacion.
Agrupamiento Aplicacion de Clustering Jerarquico.
Aplicacion del metodo de Ward.
Aplicacion del algoritmo K-Means.
Construir agrupaciones estadisticamente coherentes que permitan diferenciar perfiles de vivienda.
Determinacion del numero optimo de grupos Evaluacion mediante dendrograma.
Aplicacion del metodo del codo.
Analisis del indice de Silhouette.
Determinar el numero de segmentos que mejor representa la estructura del mercado analizado.
Caracterizacion de segmentos Descripcion de los grupos segun precio.
Descripcion de los grupos segun area.
Descripcion de los grupos segun ubicacion.
Descripcion de los grupos segun estrato.
Descripcion de los grupos segun tipo de vivienda.
Interpretar las caracteristicas distintivas de cada segmento para facilitar su analisis y comparacion.
Producto esperado Segmentacion del mercado inmobiliario con grupos claramente definidos. Obtener una segmentacion util para apoyar la toma de decisiones estrategicas en el mercado inmobiliario.

Fuente: Elaboración Propia.

FASE 6. Análisis de Correspondencias

Tabla 5. Analisis de Correspondencias
Componente Descripcion Finalidad
Objetivo Estudiar las relaciones existentes entre variables categoricas asociadas al mercado inmobiliario. Comprender la estructura de asociacion existente entre las variables categoricas del mercado inmobiliario.
Construccion de tablas de contingencia Construccion de tablas de contingencia entre tipo de vivienda y zona.
Construccion de tablas de contingencia entre zona y estrato.
Construccion de tablas de contingencia entre barrio y tipo de vivienda.
Identificar patrones de relacion entre variables categoricas relevantes para la caracterizacion de la oferta inmobiliaria.
Aplicacion del analisis de correspondencias Obtencion de dimensiones principales.
Obtencion de coordenadas de categorias.
Identificacion de asociaciones relevantes entre las categorias analizadas.
Reducir la complejidad de las relaciones observadas y destacar las asociaciones mas representativas.
Interpretacion grafica Identificacion e interpretacion de asociaciones entre categorias proximas en el plano factorial. Facilitar la interpretacion visual de los vinculos existentes entre categorias y detectar agrupamientos o proximidades relevantes.
Producto esperado Mapa perceptual de relaciones entre categorias. Generar evidencia grafica y analitica que permita interpretar las relaciones entre categorias del mercado inmobiliario.

Fuente: Elaboración Propia.

Fase 7. Integración de resultados y formulación de recomendaciones.

Tabla 6. Integracion de resultados y formulacion de recomendaciones
Componente Descripcion Finalidad
Objetivo Consolidar los hallazgos obtenidos y generar informacion util para la toma de decisiones. Transformar los resultados estadisticos en conocimiento util para apoyar la toma de decisiones.
Sintesis de hallazgos Integracion de resultados provenientes del Analisis de Componentes Principales (ACP).
Integracion de resultados del Analisis de Conglomerados.
Integracion de resultados del Analisis de Correspondencias.
Integrar la evidencia generada por las tecnicas multivariadas para obtener una vision global del mercado inmobiliario.
Identificacion de oportunidades Identificacion de segmentos de alto valor.
Identificacion de mercados emergentes.
Identificacion de factores asociados a mayores precios.
Identificacion de caracteristicas diferenciales de los inmuebles.
Detectar oportunidades de negocio y factores clave asociados al comportamiento de la oferta inmobiliaria.
Formulacion de recomendaciones Formulacion de recomendaciones orientadas a la compra de propiedades.
Formulacion de estrategias comerciales.
Apoyo a la valoracion de inmuebles.
Mejoramiento de la segmentacion del mercado.
Orientacion para decisiones de inversion inmobiliaria.
Proponer acciones concretas y estrategias basadas en la evidencia obtenida durante el analisis.
Producto esperado Informe ejecutivo con conclusiones y recomendaciones estrategicas. Presentar de forma clara los hallazgos, conclusiones y recomendaciones derivadas del estudio.

Criterios de éxito del proyecto

Para evaluar el cumplimiento del proyecto se establecen los siguientes criterios:

Tabla 7. Criterios de evaluacion de las tecnicas multivariadas
Criterio Objetivo Umbral
Reduccion de dimensionalidad Aplicar ACP para sintetizar la informacion y reducir la dimensionalidad. Varianza explicada acumulada >= 70%.
Identificacion de segmentos Aplicar Analisis de Conglomerados para identificar grupos homogeneos. Conglomerados interpretables.
Diferencias estadisticas evidentes entre grupos.
Identificacion de relaciones categoricas Aplicar Analisis de Correspondencias para evaluar asociaciones entre variables categoricas. Asociaciones claras entre tipo de vivienda, zona y barrio.
Representacion grafica interpretable.
Calidad de la visualizacion Generar visualizaciones que comuniquen claramente los resultados. Graficos comprensibles y correctamente etiquetados.
Identificacion visual de patrones relevantes.
Valor para la toma de decisiones Generar hallazgos y recomendaciones utiles para la toma de decisiones. Identificacion de oportunidades de mercado.
Identificacion de segmentos estrategicos.
Identificacion de factores asociados a mayores precios.

FASE 1 - Comprensión del problema estadístico.

Esta fase se orienta a comprender la problemática planteada por la empresa inmobiliaria y los objetivos analíticos del estudio. A partir de la identificación de las necesidades de información y las variables disponibles, se establece el marco que guiará la aplicación de técnicas de análisis multivariado para apoyar la toma de decisiones estratégicas en el mercado de vivienda urbana.

1.1 Definición del problema estadístico.

La empresa inmobiliaria dispone de una base de datos con información detallada de viviendas urbanas y requiere comprender cómo interactúan las diferentes características de los inmuebles para mejorar los procesos de compra, venta, comercialización y valoración de propiedades.

Desde una perspectiva estadística, el problema consiste en identificar las variables que explican el comportamiento de los precios de oferta, detectar patrones de asociación entre las características de las viviendas y segmentar el mercado en grupos homogéneos que permitan apoyar la toma de decisiones estratégicas.

Dado que la base de datos contiene múltiples variables cuantitativas y cualitativas relacionadas con los inmuebles, se requiere aplicar técnicas de análisis multivariado que permitan examinar simultáneamente todas las dimensiones del fenómeno estudiado.

1.2 Pregunta analítica.

¿Cuáles son los factores que influyen en los precios de las viviendas urbanas y cómo pueden identificarse segmentos homogéneos de propiedades mediante técnicas de análisis multivariado para apoyar la toma de decisiones estratégicas de una empresa inmobiliaria?

De forma complementaria, pueden plantearse las siguientes preguntas específicas:

¿Qué variables explican la mayor variabilidad de los precios de las viviendas? ¿Existen grupos de propiedades con características similares? ¿Qué relación existe entre variables categóricas como tipo de vivienda, zona y barrio? ¿Qué características distinguen a los segmentos más atractivos del mercado? ¿Cómo pueden utilizarse los hallazgos para optimizar la compra, venta y valoración de inmuebles?


FASE 2. Comprensión y exploración inicial de los datos.

En esta fase se desarrolla el proceso de comprensión y exploración inicial de la base de datos vivienda, con el propósito de identificar sus principales características, estructura, calidad de los datos y posibles limitaciones para el análisis posterior. Con el fin de mantener la fluidez del documento y evitar la inclusión de extensos bloques de programación dentro del cuerpo principal del informe, el código completo utilizado para la ejecución de las actividades correspondientes a las fases 2, 3 y 4 se presenta en el Anexo 1, garantizando la trazabilidad, reproducibilidad y transparencia del proceso analítico realizado.

2.1 Fuente de datos Vivienda.

La base de datos vivienda utilizada en el presente estudio fue obtenida a partir del repositorio público disponible en GitHub correspondiente al paquete MODELOS.

Este repositorio proporciona los datos empleados con fines académicos y de aprendizaje en análisis estadístico y ciencia de datos. La base contiene información relacionada con la oferta inmobiliaria urbana de Cali y constituye la fuente principal para la aplicación de las técnicas multivariadas desarrolladas en este proyecto.

Las actividades desarrolladas para lograr su obtención fueron:

  • Cargar la base viviendas.
Tabla 8. Estructura de la base de datos vivienda (8322 registros y 13 variables)
Variable Descripcion
id Identificador del registro
zona Zona geografica de la vivienda
piso Piso donde se ubica el inmueble
estrato Estrato socioeconomico
preciom Precio de oferta
areaconst Area construida
parqueaderos Numero de parqueaderos
banios Numero de banios
habitaciones Numero de habitaciones
tipo Tipo de vivienda
barrio Barrio de ubicacion
longitud Coordenada longitud
latitud Coordenada latitud

Fuente: Elaboración Propia.

2.2 Hacer el inventario y clasificación de variables.

El proceso de inventario y clasificación de las variables se apoyó en el diccionario de datos de la base vivienda (ver Anexo 2), con el propósito de validar tanto la definición de cada variable como las características de los datos asociados. Este ejercicio permitió identificar su naturaleza, estructura y nivel de medición, proporcionando una comprensión integral del conjunto de datos y sirviendo como base para los análisis posteriores. La clasificación obtenida se presenta a continuación.

Tabla 9. Inventario y clasificacion de variables de la base vivienda (8322 registros)
Variable Registros Completitud Naturaleza Observacion
id 8319 99.96 Cuantitativa Identificador unico
zona 8319 99.96 Cualitativa Zona geografica
piso 5684 68.30 Cuantitativa Piso de la vivienda
estrato 8319 99.96 Cuantitativa Estrato socioeconomico
preciom 8320 99.98 Cuantitativa Variable de interes principal
areaconst 8319 99.96 Cuantitativa Area construida
parqueaderos 6717 80.71 Cuantitativa Numero de parqueaderos
banios 8319 99.96 Cuantitativa Numero de banios
habitaciones 8319 99.96 Cuantitativa Numero de habitaciones
tipo 8319 99.96 Cualitativa Tipo de vivienda
barrio 8319 99.96 Cualitativa Barrio
longitud 8319 99.96 Cuantitativa Coordenada longitud
latitud 8319 99.96 Cuantitativa Coordenada latitud

Fuente: Elaboración Propia.

2.3 Análisis descriptivo de los Datos:

  • Estadísticos descriptivos para variables numéricas.
Tabla 10. Estadisticos descriptivos de variables numericas
Variable Media Mediana Desv_Estandar Coef_Variacion Minimo Maximo
id 4160.00 4160.00 2401.63 57.73 1.00 8319.00
estrato 4.63 5.00 1.03 22.21 3.00 6.00
preciom 433.89 330.00 328.65 75.74 58.00 1999.00
areaconst 174.93 123.00 142.96 81.72 30.00 1745.00
parqueaderos 1.84 2.00 1.12 61.30 1.00 10.00
banios 3.11 3.00 1.43 45.90 0.00 10.00
habitaciones 3.61 3.00 1.46 40.48 0.00 10.00
longitud -76.53 -76.53 0.02 -0.02 -76.59 -76.46
latitud 3.42 3.42 0.04 1.25 3.33 3.50

Fuente: Elaboración Propia.

  • Variables categóricas: frecuencias absolutas y relativas.

    Tabla 11. Frecuencias absolutas y relativas de variables categoricas

    Variable

    Categoria

    Frecuencia_Absoluta

    Frecuencia_Relativa

    zona

    Zona Centro

    124

    1.49

    zona

    Zona Norte

    1920

    23.08

    zona

    Zona Oeste

    1198

    14.40

    zona

    Zona Oriente

    351

    4.22

    zona

    Zona Sur

    4726

    56.81

    tipo

    Apartamento

    5100

    61.31

    tipo

    Casa

    3219

    38.69

    barrio

    20 de julio

    3

    0.04

    barrio

    3 de julio

    1

    0.01

    barrio

    acopi

    158

    1.90

    barrio

    agua blanca

    1

    0.01

    barrio

    aguablanca

    2

    0.02

    barrio

    aguacatal

    109

    1.31

    barrio

    alameda

    16

    0.19

    barrio

    alameda del rio

    1

    0.01

    barrio

    alameda del río

    2

    0.02

    barrio

    alamos

    14

    0.17

    barrio

    alborada

    1

    0.01

    barrio

    alcazares

    2

    0.02

    barrio

    alférez real

    5

    0.06

    barrio

    alferez real

    2

    0.02

    barrio

    alfonso lopez

    1

    0.01

    barrio

    alfonso lópez

    21

    0.25

    barrio

    alfonso lópez i

    1

    0.01

    barrio

    alto jordán

    1

    0.01

    barrio

    altos de guadalupe

    4

    0.05

    barrio

    altos de menga

    3

    0.04

    barrio

    altos de santa

    1

    0.01

    barrio

    antonio nariño

    2

    0.02

    barrio

    aranjuez

    15

    0.18

    barrio

    arboleda

    5

    0.06

    barrio

    arboleda campestre candelaria

    1

    0.01

    barrio

    arboledas

    38

    0.46

    barrio

    atanasio girardot

    9

    0.11

    barrio

    autopista sur

    1

    0.01

    barrio

    bajo aguacatal

    1

    0.01

    barrio

    barranquilla

    6

    0.07

    barrio

    barrio 7de agosto

    1

    0.01

    barrio

    barrio el recuerdo

    1

    0.01

    barrio

    barrio eucarístico

    1

    0.01

    barrio

    barrio obrero

    1

    0.01

    barrio

    barrio tranquilo y

    1

    0.01

    barrio

    base aérea

    2

    0.02

    barrio

    belalcazar

    3

    0.04

    barrio

    Belalcazar

    1

    0.01

    barrio

    belisario caicedo

    2

    0.02

    barrio

    bella suiza

    18

    0.22

    barrio

    bella suiza alta

    4

    0.05

    barrio

    bellavista

    43

    0.52

    barrio

    benjamín herrera

    8

    0.10

    barrio

    berlin

    1

    0.01

    barrio

    bloques del limonar

    1

    0.01

    barrio

    bochalema

    33

    0.40

    barrio

    bolivariano

    1

    0.01

    barrio

    bosques de alboleda

    1

    0.01

    barrio

    bosques del limonar

    21

    0.25

    barrio

    boyacá

    1

    0.01

    barrio

    bretaña

    16

    0.19

    barrio

    brisas de guadalupe

    1

    0.01

    barrio

    brisas de los

    81

    0.97

    barrio

    Brisas De Los

    1

    0.01

    barrio

    brisas del guabito

    1

    0.01

    barrio

    brisas del limonar

    1

    0.01

    barrio

    Bueno Madrid

    1

    0.01

    barrio

    buenos aires

    7

    0.08

    barrio

    caldas

    1

    0.01

    barrio

    Cali

    37

    0.44

    barrio

    cali bella

    1

    0.01

    barrio

    cali canto

    1

    0.01

    barrio

    calibella

    1

    0.01

    barrio

    calicanto

    8

    0.10

    barrio

    calicanto viii

    1

    0.01

    barrio

    calima

    6

    0.07

    barrio

    calimio norte

    5

    0.06

    barrio

    calipso

    11

    0.13

    barrio

    cambulos

    3

    0.04

    barrio

    camino real

    35

    0.42

    barrio

    Camino Real

    1

    0.01

    barrio

    campestre

    1

    0.01

    barrio

    caney

    88

    1.06

    barrio

    caney especial

    5

    0.06

    barrio

    cañasgordas

    7

    0.08

    barrio

    cañaveralejo

    12

    0.14

    barrio

    cañaverales

    21

    0.25

    barrio

    cañaverales los samanes

    1

    0.01

    barrio

    capri

    56

    0.67

    barrio

    cascajal

    1

    0.01

    barrio

    cataya real

    1

    0.01

    barrio

    ceibas

    1

    0.01

    barrio

    centelsa

    1

    0.01

    barrio

    centenario

    15

    0.18

    barrio

    Centenario

    1

    0.01

    barrio

    centro

    4

    0.05

    barrio

    cerro cristales

    22

    0.26

    barrio

    cerros de guadalupe

    1

    0.01

    barrio

    champagnat

    14

    0.17

    barrio

    chapinero

    7

    0.08

    barrio

    chiminangos

    17

    0.20

    barrio

    Chiminangos

    1

    0.01

    barrio

    chiminangos 1 etapa

    1

    0.01

    barrio

    chiminangos 2 etapa

    2

    0.02

    barrio

    chipichape

    30

    0.36

    barrio

    ciudad 2000

    95

    1.14

    barrio

    Ciudad 2000

    1

    0.01

    barrio

    ciudad antejardin

    1

    0.01

    barrio

    ciudad bochalema

    48

    0.58

    barrio

    ciudad capri

    13

    0.16

    barrio

    ciudad cordoba

    20

    0.24

    barrio

    ciudad córdoba

    15

    0.18

    barrio

    ciudad córdoba reservado

    1

    0.01

    barrio

    ciudad country

    1

    0.01

    barrio

    ciudad del campo

    1

    0.01

    barrio

    ciudad jardin

    22

    0.26

    barrio

    ciudad jardín

    516

    6.20

    barrio

    Ciudad Jardín

    2

    0.02

    barrio

    ciudad jardin pance

    1

    0.01

    barrio

    ciudad los alamos

    1

    0.01

    barrio

    ciudad los álamos

    25

    0.30

    barrio

    ciudad meléndez

    1

    0.01

    barrio

    ciudad melendez

    1

    0.01

    barrio

    ciudad modelo

    7

    0.08

    barrio

    ciudad pacifica

    2

    0.02

    barrio

    Ciudad Pacifica

    1

    0.01

    barrio

    ciudad real

    3

    0.04

    barrio

    ciudad talanga

    1

    0.01

    barrio

    ciudad universitaria

    1

    0.01

    barrio

    ciudadela comfandi

    17

    0.20

    barrio

    ciudadela del río

    1

    0.01

    barrio

    ciudadela melendez

    1

    0.01

    barrio

    ciudadela paso ancho

    1

    0.01

    barrio

    ciudadela pasoancho

    21

    0.25

    barrio

    colinas de menga

    3

    0.04

    barrio

    colinas del bosque

    1

    0.01

    barrio

    colinas del sur

    8

    0.10

    barrio

    colon

    1

    0.01

    barrio

    colseguros

    44

    0.53

    barrio

    colseguros andes

    4

    0.05

    barrio

    Colseguros Andes

    1

    0.01

    barrio

    comfenalco

    1

    0.01

    barrio

    compartir

    1

    0.01

    barrio

    conjunto gibraltar

    1

    0.01

    barrio

    cristales

    83

    1.00

    barrio

    cristobal colón

    14

    0.17

    barrio

    cristóbal colón

    2

    0.02

    barrio

    cuarto de legua

    44

    0.53

    barrio

    departamental

    29

    0.35

    barrio

    ed benjamin herrera

    1

    0.01

    barrio

    el bosque

    49

    0.59

    barrio

    El Bosque

    1

    0.01

    barrio

    el caney

    208

    2.50

    barrio

    El Caney

    1

    0.01

    barrio

    el castillo

    6

    0.07

    barrio

    el cedro

    8

    0.10

    barrio

    el diamante

    2

    0.02

    barrio

    el dorado

    6

    0.07

    barrio

    el gran limonar

    8

    0.10

    barrio

    el guabal

    19

    0.23

    barrio

    el guabito

    1

    0.01

    barrio

    el ingenio

    202

    2.43

    barrio

    El Ingenio

    1

    0.01

    barrio

    el ingenio 3

    1

    0.01

    barrio

    el ingenio i

    19

    0.23

    barrio

    el ingenio ii

    21

    0.25

    barrio

    el ingenio iii

    20

    0.24

    barrio

    el jardín

    15

    0.18

    barrio

    el jordán

    1

    0.01

    barrio

    el lido

    59

    0.71

    barrio

    el limonar

    135

    1.62

    barrio

    el nacional

    1

    0.01

    barrio

    el paraíso

    3

    0.04

    barrio

    el peñon

    60

    0.72

    barrio

    el prado

    2

    0.02

    barrio

    el refugio

    120

    1.44

    barrio

    el rodeo

    1

    0.01

    barrio

    el sena

    1

    0.01

    barrio

    el trébol

    5

    0.06

    barrio

    el troncal

    19

    0.23

    barrio

    el vallado

    1

    0.01

    barrio

    eucarístico

    2

    0.02

    barrio

    evaristo garcía

    2

    0.02

    barrio

    farrallones de pance

    1

    0.01

    barrio

    fenalco kennedy

    1

    0.01

    barrio

    fepicol

    1

    0.01

    barrio

    flora

    1

    0.01

    barrio

    flora industrial

    16

    0.19

    barrio

    floralia

    6

    0.07

    barrio

    fonaviemcali

    1

    0.01

    barrio

    francisco eladio ramirez

    1

    0.01

    barrio

    fuentes de la

    1

    0.01

    barrio

    gaitan

    1

    0.01

    barrio

    gran limonar

    24

    0.29

    barrio

    granada

    15

    0.18

    barrio

    guadalupe

    21

    0.25

    barrio

    guadalupe alto

    1

    0.01

    barrio

    guaduales

    2

    0.02

    barrio

    guayaquil

    16

    0.19

    barrio

    hacienda alferez real

    1

    0.01

    barrio

    ingenio

    1

    0.01

    barrio

    ingenio i

    1

    0.01

    barrio

    ingenio ii

    1

    0.01

    barrio

    jamundi

    4

    0.05

    barrio

    jamundi alfaguara

    1

    0.01

    barrio

    jorge eliecer gaitán

    1

    0.01

    barrio

    jorge isaacs

    1

    0.01

    barrio

    jose manuel marroquín

    1

    0.01

    barrio

    juanamb√∫

    53

    0.64

    barrio

    juanambu

    2

    0.02

    barrio

    junin

    18

    0.22

    barrio

    junín

    6

    0.07

    barrio

    la alborada

    5

    0.06

    barrio

    la alianza

    5

    0.06

    barrio

    la arboleda

    18

    0.22

    barrio

    la base

    15

    0.18

    barrio

    la buitrera

    3

    0.04

    barrio

    la campiña

    13

    0.16

    barrio

    la cascada

    7

    0.08

    barrio

    la ceibas

    1

    0.01

    barrio

    la esmeralda

    1

    0.01

    barrio

    la flora

    366

    4.40

    barrio

    La Flora

    2

    0.02

    barrio

    la floresta

    18

    0.22

    barrio

    la fortaleza

    4

    0.05

    barrio

    la gran colombia

    1

    0.01

    barrio

    la hacienda

    164

    1.97

    barrio

    La Hacienda

    2

    0.02

    barrio

    la independencia

    12

    0.14

    barrio

    la libertad

    2

    0.02

    barrio

    la luisa

    1

    0.01

    barrio

    la merced

    26

    0.31

    barrio

    la morada

    1

    0.01

    barrio

    la nueva base

    8

    0.10

    barrio

    la playa

    1

    0.01

    barrio

    la portada al

    1

    0.01

    barrio

    la primavera

    1

    0.01

    barrio

    la reforma

    1

    0.01

    barrio

    la rivera

    11

    0.13

    barrio

    la rivera i

    2

    0.02

    barrio

    la rivera ii

    2

    0.02

    barrio

    la riverita

    1

    0.01

    barrio

    la riviera

    1

    0.01

    barrio

    la selva

    11

    0.13

    barrio

    la villa del

    1

    0.01

    barrio

    laflora

    1

    0.01

    barrio

    lares de comfenalco

    1

    0.01

    barrio

    las acacias

    12

    0.14

    barrio

    las américas

    3

    0.04

    barrio

    las camelias

    1

    0.01

    barrio

    las ceibas

    23

    0.28

    barrio

    las delicias

    5

    0.06

    barrio

    las granjas

    10

    0.12

    barrio

    las quintas de

    1

    0.01

    barrio

    las vegas

    1

    0.01

    barrio

    las vegas de

    1

    0.01

    barrio

    libertadores

    3

    0.04

    barrio

    los alamos

    1

    0.01

    barrio

    los alcazares

    17

    0.20

    barrio

    los alcázares

    5

    0.06

    barrio

    los andes

    21

    0.25

    barrio

    los cambulos

    25

    0.30

    barrio

    los cámbulos

    6

    0.07

    barrio

    los cristales

    154

    1.85

    barrio

    los cristales club

    1

    0.01

    barrio

    los farallones

    4

    0.05

    barrio

    los guaduales

    25

    0.30

    barrio

    Los Guaduales

    1

    0.01

    barrio

    los guayacanes

    3

    0.04

    barrio

    los jockeys

    1

    0.01

    barrio

    los libertadores

    4

    0.05

    barrio

    los parques barranquilla

    6

    0.07

    barrio

    los robles

    1

    0.01

    barrio

    lourdes

    2

    0.02

    barrio

    mamellan

    1

    0.01

    barrio

    manzanares

    5

    0.06

    barrio

    mariano ramos

    1

    0.01

    barrio

    marroquín iii

    1

    0.01

    barrio

    mayapan las vegas

    46

    0.55

    barrio

    meléndez

    23

    0.28

    barrio

    melendez

    52

    0.63

    barrio

    menga

    23

    0.28

    barrio

    metropolitano del norte

    21

    0.25

    barrio

    miradol del aguacatal

    1

    0.01

    barrio

    miraflores

    25

    0.30

    barrio

    Miraflores

    1

    0.01

    barrio

    morichal de comfandi

    3

    0.04

    barrio

    multicentro

    27

    0.32

    barrio

    municipal

    3

    0.04

    barrio

    napoles

    2

    0.02

    barrio

    nápoles

    29

    0.35

    barrio

    normandia

    5

    0.06

    barrio

    normandía

    154

    1.85

    barrio

    normandía west point

    1

    0.01

    barrio

    norte

    9

    0.11

    barrio

    norte la flora

    1

    0.01

    barrio

    nueva base

    1

    0.01

    barrio

    nueva floresta

    15

    0.18

    barrio

    nueva tequendama

    73

    0.88

    barrio

    oasis de comfandi

    6

    0.07

    barrio

    oasis de pasoancho

    1

    0.01

    barrio

    occidente

    11

    0.13

    barrio

    pacara

    19

    0.23

    barrio

    pacará

    4

    0.05

    barrio

    palmas del ingenio

    1

    0.01

    barrio

    pampa linda

    26

    0.31

    barrio

    pampalinda

    12

    0.14

    barrio

    panamericano

    9

    0.11

    barrio

    pance

    409

    4.92

    barrio

    Pance

    3

    0.04

    barrio

    parcelaciones pance

    61

    0.73

    barrio

    parque residencial el

    1

    0.01

    barrio

    paseo de los

    2

    0.02

    barrio

    paso del comercio

    6

    0.07

    barrio

    pasoancho

    6

    0.07

    barrio

    poblado campestre

    2

    0.02

    barrio

    ponce

    1

    0.01

    barrio

    popular

    6

    0.07

    barrio

    portada de comfandi

    2

    0.02

    barrio

    portales de comfandi

    1

    0.01

    barrio

    porvenir

    3

    0.04

    barrio

    prados de oriente

    6

    0.07

    barrio

    prados del limonar

    20

    0.24

    barrio

    Prados Del Limonar

    1

    0.01

    barrio

    prados del norte

    126

    1.51

    barrio

    Prados Del Norte

    1

    0.01

    barrio

    prados del sur

    2

    0.02

    barrio

    primavera

    2

    0.02

    barrio

    primero de mayo

    37

    0.44

    barrio

    primitivo crespo

    3

    0.04

    barrio

    puente del comercio

    6

    0.07

    barrio

    puente palma

    1

    0.01

    barrio

    quintas de don

    72

    0.87

    barrio

    Quintas De Don

    1

    0.01

    barrio

    quintas de salomia

    4

    0.05

    barrio

    rafael uribe uribe

    1

    0.01

    barrio

    refugio

    2

    0.02

    barrio

    rep√∫blica de israel

    1

    0.01

    barrio

    rincon de la

    1

    0.01

    barrio

    rincón de salomia

    1

    0.01

    barrio

    riveras del valle

    1

    0.01

    barrio

    rozo la torre

    1

    0.01

    barrio

    saavedra galindo

    4

    0.05

    barrio

    salomia

    40

    0.48

    barrio

    samanes

    1

    0.01

    barrio

    samanes de guadalupe

    1

    0.01

    barrio

    sameco

    1

    0.01

    barrio

    san antonio

    24

    0.29

    barrio

    san bosco

    8

    0.10

    barrio

    san carlos

    4

    0.05

    barrio

    san cayetano

    9

    0.11

    barrio

    san fernando

    54

    0.65

    barrio

    San Fernando

    1

    0.01

    barrio

    san fernando nuevo

    10

    0.12

    barrio

    san fernando viejo

    18

    0.22

    barrio

    san joaquin

    4

    0.05

    barrio

    san joaquín

    16

    0.19

    barrio

    san juan bosco

    7

    0.08

    barrio

    san judas

    1

    0.01

    barrio

    san judas tadeo

    2

    0.02

    barrio

    san luis

    2

    0.02

    barrio

    san luís

    1

    0.01

    barrio

    san nicolas

    1

    0.01

    barrio

    san nicolás

    1

    0.01

    barrio

    san pedro

    3

    0.04

    barrio

    san vicente

    48

    0.58

    barrio

    santa

    1

    0.01

    barrio

    santa anita

    48

    0.58

    barrio

    Santa Anita

    2

    0.02

    barrio

    santa anita sur

    1

    0.01

    barrio

    santa bárbara

    3

    0.04

    barrio

    santa elena

    10

    0.12

    barrio

    santa fe

    8

    0.10

    barrio

    santa helena de

    1

    0.01

    barrio

    santa isabel

    63

    0.76

    barrio

    Santa Isabel

    1

    0.01

    barrio

    santa monica

    51

    0.61

    barrio

    Santa Monica

    1

    0.01

    barrio

    santa mónica

    3

    0.04

    barrio

    santa mónica alta

    1

    0.01

    barrio

    santa monica norte

    2

    0.02

    barrio

    santa monica popular

    2

    0.02

    barrio

    santa mónica popular

    7

    0.08

    barrio

    santa monica residencial

    5

    0.06

    barrio

    santa mónica residencial

    39

    0.47

    barrio

    santa rita

    45

    0.54

    barrio

    santa rosa

    1

    0.01

    barrio

    santa teresita

    262

    3.15

    barrio

    Santa Teresita

    1

    0.01

    barrio

    Santafe

    1

    0.01

    barrio

    santander

    1

    0.01

    barrio

    santo domingo

    5

    0.06

    barrio

    Santo Domingo

    1

    0.01

    barrio

    sector aguacatal

    1

    0.01

    barrio

    sector cañaveralejo guadalupe

    2

    0.02

    barrio

    seminario

    32

    0.38

    barrio

    sierras de normandía

    1

    0.01

    barrio

    siete de agosto

    8

    0.10

    barrio

    simón bolivar

    1

    0.01

    barrio

    tejares cristales

    4

    0.05

    barrio

    tejares de san

    14

    0.17

    barrio

    templete

    4

    0.05

    barrio

    tequendama

    44

    0.53

    barrio

    tequendema

    1

    0.01

    barrio

    terrón colorado

    1

    0.01

    barrio

    torres de comfandi

    57

    0.69

    barrio

    unicentro cali

    1

    0.01

    barrio

    unión de vivienda

    3

    0.04

    barrio

    urbanización barranquilla

    4

    0.05

    barrio

    urbanización boyacá

    1

    0.01

    barrio

    urbanización colseguros

    3

    0.04

    barrio

    urbanizacion el saman

    1

    0.01

    barrio

    urbanizacion gratamira

    1

    0.01

    barrio

    urbanización la flora

    83

    1.00

    barrio

    urbanización la merced

    4

    0.05

    barrio

    urbanización la nueva

    4

    0.05

    barrio

    urbanización las cascadas

    1

    0.01

    barrio

    urbanizacion lili

    2

    0.02

    barrio

    urbanización nueva granada

    3

    0.04

    barrio

    urbanización pacara

    1

    0.01

    barrio

    urbanización río lili

    5

    0.06

    barrio

    urbanización san joaquin

    4

    0.05

    barrio

    urbanización tequendama

    7

    0.08

    barrio

    valle de lili

    1

    0.01

    barrio

    valle del lili

    1008

    12.12

    barrio

    Valle Del Lili

    1

    0.01

    barrio

    valle grande

    1

    0.01

    barrio

    versalles

    71

    0.85

    barrio

    villa colombia

    6

    0.07

    barrio

    villa de veracruz

    6

    0.07

    barrio

    villa del lago

    10

    0.12

    barrio

    villa del parque

    1

    0.01

    barrio

    villa del prado

    51

    0.61

    barrio

    Villa Del Prado

    1

    0.01

    barrio

    villa del sol

    25

    0.30

    barrio

    villa del sur

    5

    0.06

    barrio

    villas de veracruz

    8

    0.10

    barrio

    Villas De Veracruz

    1

    0.01

    barrio

    vipasa

    32

    0.38

    barrio

    zona centro

    1

    0.01

    barrio

    zona norte

    32

    0.38

    barrio

    zona norte los

    1

    0.01

    barrio

    zona oeste

    26

    0.31

    barrio

    zona oriente

    18

    0.22

    barrio

    zona residencial

    1

    0.01

    barrio

    zona sur

    74

    0.89

    Fuente: Elaboración Propia.

  • Tabla de contingencia.

Tabla 12. Contingencia: Tipo de vivienda y zona
Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
Apartamento 24 1198 1029 62 2787
Casa 100 722 169 289 1939

Fuente: Elaboración Propia.

Tabla 13. Contingencia: Zona y estrato
3 4 5 6
Zona Centro 105 14 4 1
Zona Norte 572 407 769 172
Zona Oeste 54 84 290 770
Zona Oriente 340 8 2 1
Zona Sur 382 1616 1685 1043

Fuente: Elaboración Propia.

Tabla 14. Distribucion porcentual de estrato por zona (%)
3 4 5 6
Zona Centro 84.68 11.29 3.23 0.81
Zona Norte 29.79 21.2 40.05 8.96
Zona Oeste 4.51 7.01 24.21 64.27
Zona Oriente 96.87 2.28 0.57 0.28
Zona Sur 8.08 34.19 35.65 22.07

🟢 Verde pastel → frecuencia relativa más alta. 🟡 Amarillo pastel → frecuencias intermedias. 🔴 Rosa pastel → frecuencia relativa más baja.

Fuente: Elaboración Propia.

2.4 Evaluación de calidad de datos.

  • Validar y visualizar datos faltantes.
Tabla 15. Resumen de valores faltantes en la base de datos vivienda
variable cantidad_nulos porcentaje_nulos
id 3 0.0360490
zona 3 0.0360490
piso 2638 31.6991108
estrato 3 0.0360490
preciom 2 0.0240327
areaconst 3 0.0360490
parqueaderos 1605 19.2862293
banios 3 0.0360490
habitaciones 3 0.0360490
tipo 3 0.0360490
barrio 3 0.0360490
longitud 3 0.0360490
latitud 3 0.0360490

Fuente: Elaboración Propia.

Gráfica 1. Frecuencia de datos faltantes por variable.

Fuente: Elaboración Propia.

  • Detectar y visualizar valores atipicos.

Gráfica 2. Detección de valores atípico por variable.

Fuente: Elaboración Propia.

2.5 Excluisión de las variables piso y parqueaderos de la base analitica.

Durante la etapa de preparación de los datos se realizó una evaluación de la calidad, variabilidad y capacidad explicativa de cada variable disponible. Como resultado de este análisis, las variables piso y parqueaderos fueron excluidas de la base analítica utilizada en las técnicas multivariadas.

En el caso de la variable piso, se observó una baja capacidad discriminante dentro del conjunto de datos, debido a su limitada contribución para explicar las diferencias entre los inmuebles analizados. Adicionalmente, la variable presentó una relación débil con la variable de interés principal (preciom), por lo que su inclusión aportaba escasa información para los objetivos de reducción de dimensionalidad y segmentación.

Respecto a la variable parqueaderos, se identificó una distribución altamente concentrada en un número reducido de categorías, generando una variabilidad limitada y reduciendo su capacidad para diferenciar adecuadamente los inmuebles. Asimismo, su aporte a la explicación de la estructura general de la base resultó marginal en comparación con otras variables de mayor relevancia, como área construida, número de habitaciones, número de baños, estrato y ubicación.

La exclusión de estas variables permitió construir una base analítica más consistente, reduciendo la presencia de información redundante o poco informativa y favoreciendo la aplicación de técnicas multivariadas como el Análisis de Componentes Principales (ACP), el Análisis de Conglomerados y el Análisis de Correspondencias, cuyos resultados dependen de la capacidad explicativa y la variabilidad de las variables incluidas.

Adicionalmente, ambas variables presentaron una contribución limitada a la explicación de la variabilidad total de los datos y no mostraron una influencia significativa en los análisis exploratorios realizados, razón por la cual fueron descartadas para evitar incorporar ruido estadístico y mejorar la interpretabilidad de los resultados obtenidos.

2.6 Resumen de la exploración y diagnóstico de calidad de los datos.

La base de datos vivienda está compuesta por 8.322 registros y 13 variables, las cuales describen características físicas, espaciales y económicas de inmuebles residenciales. La exploración inicial permitió identificar una estructura de datos adecuada para el desarrollo del estudio, con información relacionada con precio de oferta, área construida, estrato socioeconómico, ubicación geográfica, tipo de vivienda y características habitacionales.

A partir del inventario de variables y de los análisis descriptivos realizados, se evidenció la coexistencia de variables cuantitativas y cualitativas, condición que resulta apropiada para la aplicación de técnicas multivariadas orientadas a la reducción de dimensionalidad, segmentación de viviendas y análisis de relaciones categóricas. Asimismo, se confirmó que la variable preciom constituye la variable de interés principal, al representar el precio de oferta de los inmuebles y ser el eje central de la investigación.

En términos de calidad de los datos, se evaluó la presencia de valores faltantes, registros duplicados, consistencia de la información y observaciones atípicas. Los resultados mostraron que, aunque la base presenta una estructura sólida y utilizable para fines analíticos, existen diferencias importantes en las escalas de medición de las variables. Por ejemplo, variables como preciom y areaconst presentan magnitudes considerablemente superiores a variables como banios o habitaciones, situación que puede generar que las variables de mayor escala dominen los resultados de los análisis multivariados.

Adicionalmente, la detección de valores atípicos permitió identificar observaciones extremas asociadas principalmente a variables relacionadas con el tamaño y valor de los inmuebles. Aunque estos registros pueden corresponder a propiedades reales del mercado y no necesariamente a errores de captura, su presencia puede influir significativamente en los estadísticos descriptivos y en la formación de grupos durante los procesos de segmentación.

Con base en este diagnóstico, resulta necesario realizar una fase de preparación y transformación de los datos, orientada a mejorar la calidad y comparabilidad de la información. Esta etapa incluye el tratamiento de valores faltantes, la evaluación de observaciones atípicas y, especialmente, la estandarización de las variables numéricas mediante puntuaciones Z. Este procedimiento se justifica porque las técnicas que serán aplicadas posteriormente, como el Análisis de Componentes Principales (ACP) y el Análisis de Conglomerados, son sensibles a las diferencias de escala entre variables. Sin una transformación previa, variables con valores absolutos elevados tendrían una influencia desproporcionada en los resultados, dificultando la identificación de patrones reales en la información.

En consecuencia, el diagnóstico realizado permite concluir que la base de datos posee un nivel de calidad adecuado para el desarrollo del estudio; sin embargo, requiere procesos de depuración y transformación que garanticen la validez estadística, la comparabilidad de las variables y la confiabilidad de los resultados obtenidos en las etapas posteriores del análisis multivariado.


FASE 3. Preparación y transformación de los datos.

En esta fase se realizan las actividades necesarias para garantizar la calidad y consistencia de la información. Esto incluye la revisión, limpieza y adecuación de los datos, con el fin de disponer de una base analítica confiable para la aplicación de las técnicas estadísticas posteriores.

3.1 Tratamiento de datos faltantes.

He creado este bloque de código R, el cual tiene como objetivo reemplazar por valores faltantes (NA) aquellos registros que previamente fueron identificados como valores atípicos, conservando la trazabilidad de las observaciones originales. Ello facilita la imputación que se haga posteriormente a los datos, con el objeto de mejorar su calidad.

Convertir atipicos en NA solamente en datos_trabajo:

if (exists("auditoria_atipicos") &&
    nrow(auditoria_atipicos) > 0L) {
  for (i in seq_len(nrow(auditoria_atipicos))) {
    posicion <- match(
      auditoria_atipicos$fila_original[i],
      datos_trabajo$fila_original
    )
    if (!is.na(posicion)) {
      datos_trabajo[posicion, auditoria_atipicos$variable[i]] <- NA
    }
  }
}

3.2 Tratamiento de valores atípicos.

Mediante aplicación de las herramientas de imputación KNN Mixta, permitió (vér Anexo 1, 5. IMPUTACION KNN MIXTA):

  • La selección de variables para la imputación: Se identificaron las variables que participarían en el proceso de imputación, excluyendo los campos de identificación (id y fila_original), ya que no aportan información para determinar similitudes entre viviendas.

  • La clasificación de variables según su naturaleza:Las variables seleccionadas fueron clasificadas en numéricas y categóricas. Adicionalmente, se identificaron variables discretas (estrato, banios y habitaciones) para preservar su naturaleza entera durante la imputación.

  • La estandarización robusta de variables numéricas: Las variables numéricas fueron escaladas utilizando la mediana y el rango intercuartílico (IQR), reduciendo la influencia de valores extremos y garantizando que todas las variables contribuyeran de manera equilibrada al cálculo de distancias.

  • La definición de la moda ponderada: Se implementó un mecanismo para imputar variables categóricas mediante una moda ponderada, asignando mayor importancia a los vecinos más similares a cada observación faltante.

  • La construcción de la métrica de distancia mixta: Se desarrolló una función capaz de calcular distancias considerando simultáneamente variables numéricas y categóricas, permitiendo medir la similitud entre viviendas de forma integral.

  • La creación de la auditoría de imputación: Se generó una estructura de control para registrar cada valor imputado, incluyendo la variable afectada, el origen del dato faltante, el método aplicado y la cantidad de vecinos utilizados.

  • La identificación de valores faltantes: Para cada variable, se localizaron los registros con información faltante que requerían ser completados antes de continuar con el análisis multivariado.

  • La selección de vecinos más similares: Para cada dato faltante se identificaron observaciones comparables mediante la distancia mixta calculada sobre las características disponibles de las viviendas.

  • La aplicación de imputación KNN: Los valores faltantes fueron reemplazados utilizando los cinco vecinos más cercanos. Para variables numéricas se empleó un promedio ponderado por distancia y para variables categóricas una moda ponderada.

  • La aplicación de mecanismos de respaldo: Cuando no fue posible encontrar observaciones suficientemente comparables, se aplicaron métodos alternativos de imputación: la mediana para variables numéricas y la moda para variables categóricas.

  • La identificación del origen de la imputación: Se determinó si cada valor imputado correspondía a un dato originalmente faltante o a un valor atípico previamente reemplazado por NA durante la fase de depuración.

  • La actualización de la base analítica: Los valores imputados fueron incorporados progresivamente a la base de trabajo, permitiendo que las nuevas observaciones completadas participaran en imputaciones. posteriores.

  • El registro de trazabilidad: Cada imputación quedó documentada en la tabla de auditoría, garantizando la trazabilidad y reproducibilidad del proceso de preparación de datos.

Al finalizar el procedimiento, se obtuvo una base de datos completa y consistente, en la cual los valores faltantes y los valores atípicos tratados fueron reemplazados mediante un enfoque KNN mixto, preservando las relaciones existentes entre las variables y reduciendo el sesgo asociado a la pérdida de información.

En la Siguiente tabla se muestran los resultado de la imputación:

Tabla 16. Resumen de imputacion de valores mediante KNN mixto
variable Total_imputaciones Faltantes_originales Valores_atipicos Vecinos_promedio Metodo_principal
areaconst 3 3 0 5.00 KNN
banios 3 3 0 5.00 KNN
barrio 3 3 0 5.00 KNN
estrato 3 3 0 1.67 Respaldo: mediana
habitaciones 3 3 0 5.00 KNN
latitud 3 3 0 5.00 KNN
longitud 3 3 0 5.00 KNN
preciom 2 2 0 5.00 KNN
tipo 3 3 0 5.00 KNN
zona 3 3 0 0.00 Respaldo: moda

Fuente: Elaboración Propia.

3.3 Contraste Antes y Después de la Transformación de los Datos.

Gráfica 3. Datos faltantes antes y después de la limpieza.

Fuente: Elaboración Propia.

3.4 Base de datos depurada y lista para el analisis multivariado.

La base analítica final denominada vivienda_limpia (Ver tabla ##) quedó conformada por 1.894 registros completamente depurados. Como resultado de las etapas de limpieza, tratamiento de valores faltantes, manejo de datos atípicos e identificación de duplicados, el conjunto de datos presenta 0 valores faltantes, 0 registros duplicados y 0 observaciones atípicas pendientes de tratamiento, garantizando así condiciones adecuadas para la aplicación de las técnicas multivariadas contempladas en el estudio.

Tabla 17. Resumen de los datos de vivienda_limpia
Estadísticas descriptivas del dataset vivienda_limpia
n mean sd min median max
zona* 8322 3.92 1.33 1.00 5.00 5.00
estrato 8322 4.63 1.03 3.00 5.00 6.00
preciom 8322 433.89 328.61 58.00 330.00 1999.00
areaconst 8322 174.94 142.94 30.00 123.00 1745.00
banios 8322 3.11 1.43 0.00 3.00 10.00
habitaciones 8322 3.61 1.46 0.00 3.00 10.00
tipo* 8322 1.39 0.49 1.00 1.00 2.00
barrio* 8322 241.54 128.85 1.00 251.00 436.00
longitud 8322 -76.53 0.02 -76.59 -76.53 -76.46
latitud 8322 3.42 0.04 3.33 3.42 3.50

Fuente: Elaboración Propia.

3.5 Variables recomendadas para los modelos.

Tabla 18. Variables Recomendadas.

Componente Descripción Finalidad
Estrato (Análisis de Componentes Principales) Aporta el nivel socioeconómico del inmueble dentro de la estructura multivariada. Reducir la dimensionalidad de las variables cuantitativas e identificar las principales estructuras de variación del mercado inmobiliario.
Precio (Análisis de Componentes Principales) Representa la dimensión monetaria y ayuda a identificar componentes asociados con la variación del precio. Reducir la dimensionalidad de las variables cuantitativas e identificar las principales estructuras de variación del mercado inmobiliario.
Área construida (Análisis de Componentes Principales) Representa el tamaño físico y ayuda a identificar componentes de amplitud de la oferta. Reducir la dimensionalidad de las variables cuantitativas e identificar las principales estructuras de variación del mercado inmobiliario.
Baños (Análisis de Componentes Principales) Representa la dotación funcional de la propiedad. Reducir la dimensionalidad de las variables cuantitativas e identificar las principales estructuras de variación del mercado inmobiliario.
Habitaciones (Análisis de Componentes Principales) Representa la capacidad habitacional de la propiedad. Reducir la dimensionalidad de las variables cuantitativas e identificar las principales estructuras de variación del mercado inmobiliario.
Estrato (Análisis de Conglomerados) Permite diferenciar segmentos de oferta según nivel socioeconómico. Identificar grupos homogéneos de inmuebles y caracterizar segmentos diferenciados de la oferta inmobiliaria.
Precio (Análisis de Conglomerados) Permite separar segmentos por nivel de precio. Identificar grupos homogéneos de inmuebles y caracterizar segmentos diferenciados de la oferta inmobiliaria.
Área construida (Análisis de Conglomerados) Permite agrupar propiedades según tamaño construido. Identificar grupos homogéneos de inmuebles y caracterizar segmentos diferenciados de la oferta inmobiliaria.
Baños (Análisis de Conglomerados) Permite diferenciar segmentos por dotación interna. Identificar grupos homogéneos de inmuebles y caracterizar segmentos diferenciados de la oferta inmobiliaria.
Habitaciones (Análisis de Conglomerados) Permite diferenciar segmentos por capacidad residencial. Identificar grupos homogéneos de inmuebles y caracterizar segmentos diferenciados de la oferta inmobiliaria.
Tipo de vivienda (Análisis de Correspondencia) Permite examinar la distribución de las clases de vivienda. Identificar asociaciones entre categorías y representar gráficamente los patrones de relación existentes entre las variables cualitativas.
Zona (Análisis de Correspondencia) Permite identificar asociaciones entre tipología y sector de la ciudad. Identificar asociaciones entre categorías y representar gráficamente los patrones de relación existentes entre las variables cualitativas.
Barrio (Análisis de Correspondencia) Permite reconocer patrones territoriales y especialización de la oferta por barrio. Identificar asociaciones entre categorías y representar gráficamente los patrones de relación existentes entre las variables cualitativas.

Fuente: Elaboración Propia.

Tabla 19. Modelos aplicables a las variables seleccionadas.

Componente Variables recomendadas Descripción Finalidad
Análisis de Componentes Principales (ACP) estrato, preciom, areaconst, banios, habitaciones Variables cuantitativas recomendadas para reducir la dimensionalidad y visualizar la estructura multivariada del mercado inmobiliario. Identificar características asociadas con la variación de los precios y de la oferta inmobiliaria.
Análisis de Conglomerados estrato, preciom, areaconst, banios, habitaciones Variables utilizadas para agrupar propiedades en segmentos homogéneos según sus características y estrato socioeconómico. Comprender las dinámicas de la oferta mediante la identificación de segmentos homogéneos de propiedades.
Análisis de Correspondencia tipo, zona, barrio Variables categóricas recomendadas para examinar asociaciones entre tipo de vivienda, zona y barrio. Reconocer patrones de comportamiento y asociaciones territoriales de la oferta inmobiliaria.

Fuente: Elaboración Propia.

FASE 4. Análisis de Componentes Principales - ACP.

Con el propósito de identificar las dimensiones que explican la mayor parte de la variabilidad presente en la oferta inmobiliaria, se aplicó un Análisis de Componentes Principales (ACP) sobre las variables estrato, precio, área construida, baños y habitaciones. Esta técnica permitió sintetizar la información contenida en dichas variables y reducir la complejidad del conjunto de datos. Asimismo, facilitó la identificación de los factores más influyentes en la caracterización de las viviendas y en la explicación de las diferencias observadas en el mercado inmobiliario analizado.

En el anexo 3, se presentan las siguientes tablas para apoyar los resultados del ACP:

  • Tabla de la varianza.
  • Tabla de los componentes dominantes.

4.1 Varianza explicada por los componentes principales.

La gráfica 4 muestra cuánto de la variabilidad existente en las cinco variables originales es capaz de recoger cada componente principal.

Las variables utilizadas fueron:

  • Estrato
  • Precio mensual (preciom)
  • Área construida (areaconst)
  • Baños (banios)
  • Habitaciones

El CP1 explica aproximadamente el 58,66 % de la variabilidad total, mientras que el CP2 explica el 23,77 %. En conjunto, los dos primeros componentes representan aproximadamente el 82,43 % de la variabilidad observada, superando el umbral del 80 % establecido como criterio de retención.

Esto es importante porque significa que podemos pasar de cinco variables originales a solamente dos dimensiones sintéticas, perdiendo relativamente poca información.

El CP1 concentra la mayor cantidad de información, por lo que constituye la dimensión más importante. El CP2 aporta una segunda dimensión que complementa la información del primero.

Desde la perspectiva del mercado inmobiliario, esto permite simplificar la caracterización de las viviendas. En lugar de analizar simultáneamente cinco variables, podemos estudiar dos dimensiones principales que resumen buena parte de las diferencias entre los inmuebles.

Esto resulta particularmente útil posteriormente para:

  • Segmentar la oferta;
  • Construir conglomerados;
  • Identificar perfiles de vivienda;
  • Representar visualmente las propiedades;
  • Analizar relaciones entre características físicas y económicas.

En la representación gráfica, la línea roja horizontal representa el 80% de varianza acumulada. La curva acumulada supera esa referencia en el segundo componente, justificando la retención de CP1 y CP2.

Gráfica 4. Varianza explicado por los componentes principales.

Fuente: Elaboración Propia.

4.2 Estructura de las viviendas en el plano CP1–CP2.

La gráfica 5 representa cada vivienda como un punto dentro del plano definido por los dos componentes retenidos.

Aquí cada punto representa una propiedad inmobiliaria. Y la lógica indica que cuanto más cerca estén dos viviendas dentro del plano factorial, más parecidos son sus perfiles multivariados respecto de las variables utilizadas en el ACP.

Por el contrario, viviendas muy alejadas entre sí presentan características diferentes en términos de precio, área, baños, habitaciones y estrato.

Allí se aprecia una concentración importante de viviendas alrededor de la zona central del gráfico, aunque también existe una dispersión considerable hacia los diferentes extremos.

Esto indica que existe un núcleo importante de propiedades con características relativamente similares, pero también aparecen viviendas con perfiles diferenciados.

La dispersión hacia valores positivos del CP1 puede asociarse, de acuerdo con las cargas observadas, con viviendas que presentan conjuntamente mayores niveles en las variables que caracterizan este componente.

Por su parte, el CP2 permite diferenciar principalmente viviendas asociadas con la relación entre estrato y número de habitaciones.

En síntsis, las viviendas que aparecen agrupadas representan propiedades que se parecen entre sí. Las que aparecen alejadas constituyen perfiles inmobiliarios más particulares.

Esto es especialmente valioso para una posterior segmentación mediante análisis de conglomerados, porque el ACP proporciona un espacio reducido en el cual resulta más sencillo identificar grupos de viviendas con características semejantes.

Gráfica 5. Estructura de las viviendas en el plano del ACP.

Fuente: Elaboración Propia.

4.3 Cargas de las variables en CP1 y CP2

La gráfica 6 es probablemente la más importante para interpretar qué significa cada componente.

Las variables con mayores cargas positivas en CP1 son: banios(0,52), preciom (0,51) y areaconst (0,49). Esto muestra que CP1 está asociado simultáneamente con: mayor número de baños; mayor precio; y mayor área construida. Esto resulta bastante coherente desde el punto de vista económico: las propiedades con mayor superficie y mayor dotación suelen ubicarse también en segmentos de mayor valor. Por tanto, podemos interpretar CP1 como una dimensión relacionada con el nivel físico-económico de la vivienda.

En el segundo componente aparecen dos variables especialmente importantes: estrato (-0,67), habitacines (0,65),preciom (-0,28).

Aquí aparece una relación bastante interesante. El estrato presenta una carga negativa elevada, mientras que habitaciones presenta una carga positiva elevada. Esto significa que CP2 está capturando principalmente un contraste entre estas dos características. Es importante no interpretar el signo negativo del estrato como algo “malo”. En ACP, el signo de un componente es arbitrario: si se multiplicara todo el componente por -1, las relaciones estadísticas serían exactamente las mismas.

Lo importante es la relación relativa entre las variables. Por tanto, CP2 puede interpretarse como una dimensión asociada con el perfil residencial de la vivienda, particularmente con la relación entre nivel socioeconómico/estrato y capacidad habitacional.

La presencia de preciom con una carga negativa menor (-0,28) indica que también participa en esta dimensión, aunque con una importancia bastante menor que estrato y habitaciones.

Gráfica 6. Cargas de las variables en CP1 y CP2.

Fuente: Elaboración Propia.

Tabla 20. Resumen del Análisis de Componentes Principales.

Componente Variable Carga Contribución (%) Varianza explicada (%)
CP1 banios 0.52 26.93 58.66
CP1 preciom 0.51 25.69 58.66
CP1 areaconst 0.49 24.41 58.66
CP2 estrato -0.67 45.49 23.77
CP2 habitaciones 0.65 42.75 23.77
CP2 preciom -0.28 7.89 23.77

Fuente: Elaboración Propia.

El resultado del ACP lo podemos sintetizar de la siguiente manera:

  • CP1 → “Dimensión físico-económica” y representa principalmente el tamaño, la dotación y el nivel económico de la propiedad.

  • CP2 → “Dimensión de perfil residencial” Diferencia las viviendas principalmente por la relación entre estrato y capacidad habitacional.

Esta interpretación es mucho más útil que decir simplemente “CP1 explica 58,66 %” y “CP2 explica 23,77 %”, porque permite darle significado económico a los componentes.


FASE 5. Análisis de Conglomerados.

Una vez reducida la dimensionalidad de la base vivienda_limpia mediante el Análisis de Componentes Principales (ACP), se procedió a aplicar un Análisis de Conglomerados con el propósito de identificar grupos homogéneos de viviendas que compartieran características similares. Esta técnica permitió segmentar el mercado inmobiliario a partir de patrones presentes en los datos, facilitando la caracterización de perfiles de inmuebles y la identificación de segmentos relevantes para la toma de decisiones y el análisis estratégico del mercado.

5.1 Identificación de segmentos.

La evaluación de soluciones entre 2 y 8 conglomerados mediante el índice de Calinski-Harabasz muestra que la solución con k = 2 obtiene el mayor valor del indicador:

  • k = 2: 7.675,93 3
  • k = 3: 6.672,63
  • k = 4: 7.068,13
  • k = 5: 7.372,59
  • k = 6: 7.371,51
  • k = 7: 7.238,75
  • k = 8: 7.236,72

El índice de Calinski-Harabasz busca un equilibrio entre alta separación entre los grupos y alta homogeneidad dentro de cada grupo. Por tanto, el máximo observado en k = 2 indica que esta alternativa ofrece la mejor estructura de segmentación entre las soluciones evaluadas.

Tabla 21. índice de Calinski-Harabasz.

Número de grupos (k) Suma intragrupos Suma entre grupos Proporción separada (%) Índice Calinski-Harabasz
2 17837.66 16456.80 47.99 7675.93
3 13168.96 21125.51 61.60 6672.63
4 9662.54 24631.92 71.82 7068.13
5 7544.22 26750.24 78.00 7372.59
6 6313.27 27981.20 81.59 7371.51
7 5510.57 28783.89 83.93 7238.75
8 4834.98 29459.48 85.90 7236.72

5.2 Selección del número de conclomerados.

El gráfico # confirma visualmente la decisión anterior. El índice alcanza su máximo en k = 2 y posteriormente disminuye considerablemente al pasar a tres conglomerados.

Es particularmente importante observar que, aunque el indicador vuelve a aumentar para k = 4, 5 y 6, estos valores permanecen por debajo del obtenido con dos grupos.

Gráfica 7. Selección del número de conglomerados.

5.3 Segmentos inmobiliarios en el espacio del ACP.

La representación de los conglomerados sobre el espacio definido por los dos primeros componentes principales muestra una separación bastante evidente.

El Conglomerado 2 se concentra principalmente hacia los valores negativos o cercanos a cero de CP1, mientras que el Conglomerado 1 se desplaza hacia valores positivos de CP1.

Esto es coherente con los resultados del ACP, donde el primer componente está fuertemente relacionado con variables como precio, área construida y baños, que son precisamente algunas de las variables que presentan mayores diferencias entre los dos conglomerados.

También se observa cierta dispersión y solapamiento alrededor de la zona central del gráfico. Esto es normal y significa que no todas las propiedades pueden separarse de manera absolutamente nítida; existen inmuebles con características intermedias.

Gráfica 8. Segmentos inmobiliarios en el espacio del ACP.

5.4 Tamaño y participación de los segmentos inmobiliarios.

La distribución de las propiedades entre los dos segmentos muestra una diferencia importante:

Gráfica 9. Distribución de las propiedades entre los dos segmentos.

El Conglomerado 2 concentra aproximadamente dos terceras partes de la oferta analizada, mientras que el Conglomerado 1 representa aproximadamente una tercera parte.

Esto indica que el mercado no está dividido en dos segmentos de igual tamaño. Existe un segmento claramente dominante en términos de número de propiedades.

5.5 Medias de las variables por conglomerado.

A partir de todos los resultados, se pueden denominar los grupos de una manera más comprensible:

Conglomerado 1

Presenta valores promedio superiores en todas las variables cuantitativas analizadas:

  • Estrato promedio: 5,28
  • Precio promedio: 764,29
  • Área construida promedio: 311,03
  • Baños promedio: 4,64
  • Habitaciones promedio: 4,69

Conglomerado 2

Presenta valores considerablemente inferiores:

  • Estrato promedio: 4,32
  • Precio promedio: 270,29
  • Área construida promedio: 107,55
  • Baños promedio: 2,36
  • Habitaciones promedio: 3,07

Es, por tanto, el segmento mayoritario de la oferta analizada, con propiedades de menor escala y menor valor relativo frente al primer conglomerado.

Tabla 22. Medias de las variables por conglomerado..

Conglomerado Estrato promedio Precio promedio Área construida promedio Baños promedio Habitaciones promedio
Conglomerado 1 5.28 764.29 311.03 4.64 4.69
Conglomerado 2 4.32 270.29 107.55 2.36 3.07

5.6 Medianas de las variables por conglomerado.

La coincidencia entre medias y medianas fortalece la interpretación de los segmentos.

Por ejemplo, el precio mediano pasa de 670 en el Conglomerado 1 a 250 en el Conglomerado 2, mientras que el área construida mediana pasa de 280 a 90.

Es decir, la diferencia entre los grupos es estructural, no simplemente consecuencia de unos pocos inmuebles excepcionalmente costosos o grandes.

Tabla 23. Medianas de las variables por conglomerado.

Conglomerado Estrato mediano Precio mediano Área construida mediana Baños medianos Habitaciones medianas
Conglomerado 1 6 670 280 4 4
Conglomerado 2 4 250 90 2 3

5.7 Tamaño y participación de los segmentos inmobiliarios.

Este resultado es especialmente interesante porque ambos conglomerados tienen como zona predominante la Zona Sur, pero presentan una composición diferente en cuanto a tipo de vivienda y barrio.

Por tanto, la segmentación no está explicada únicamente por una ubicación geográfica general. Dentro de una misma zona existen perfiles inmobiliarios claramente diferenciados.

Tabla 24. Tamaño y participación de los segmentos inmobiliarios.

Conglomerado Tipo predominante Zona predominante Barrio predominante
Conglomerado 1 Casa Zona Sur ciudad jardín
Conglomerado 2 Apartamento Zona Sur valle del lili

FASE 6. Analisis de Correspondencias.

A partir de las dimensiones identificadas mediante el Análisis de Componentes Principales (ACP), se desarrolló el Análisis de Conglomerados sobre la base vivienda_limpia con el propósito de identificar grupos de viviendas con características similares. Esta técnica complementa el ACP al facilitar la segmentación del mercado inmobiliario según patrones comunes de precio, área, estrato y ubicación. Así, se obtuvo una visión más clara de los perfiles predominantes presentes en la oferta de vivienda analizada.

6.1 Distribución geográfica relativa de los segmentos.

La gráfica 10 representa las 8.322 propiedades analizadas según su longitud y latitud, diferenciando los dos conglomerados obtenidos en el análisis de segmentación.

Se observa que los dos segmentos presentan una distribución espacial amplia y parcialmente superpuesta. Es decir, el Conglomerado 1 y el Conglomerado 2 no se encuentran confinados a territorios completamente independientes, sino que aparecen simultáneamente en buena parte del espacio geográfico representado.

Sin embargo, también se identifican algunos patrones relevantes:

  • El Conglomerado 2 presenta una presencia espacial particularmente amplia, consistente con su participación mayoritaria del 66,9% de las propiedades.
  • El Conglomerado 1, que representa el 33,1%, aparece igualmente distribuido en diferentes sectores, pero con una concentración visual más marcada en determinados corredores y áreas.
  • En varias zonas se observa una mezcla de puntos de ambos conglomerados, lo cual indica que la ubicación geográfica por sí sola no determina completamente la pertenencia a un segmento.
  • La presencia simultánea de ambos colores en amplias áreas sugiere que dentro de un mismo entorno territorial pueden coexistir propiedades de características económicas y físicas diferentes.

Este último resultado es importante porque coincide con lo encontrado en la caracterización de los conglomerados: el Conglomerado 1 corresponde principalmente a viviendas de mayor precio, área, estrato y dotación, mientras que el Conglomerado 2 reúne una oferta más numerosa y de menor valor relativo.

Gráfica 10. Distribución geográfica relativa de los segmentos en Cali.

6.2 Correspondencia Zona Barrio.

La gráfica 11 proporciona una evidencia estadística mucho más contundente sobre la relación territorial entre zona y barrio.

Los resultados reportados son:

  • Chi-cuadrado = 29.354,11
  • p < 2 × 10⁻¹⁶
  • V de Cramer = 0,939

El contraste permite plantear las siguientes hipótesis:

  • H₀: la zona y el barrio son estadísticamente independientes.

  • H₁: existe una asociación estadísticamente significativa entre la zona y el barrio.

Dado que el valor p es extremadamente pequeño, se rechaza H₀. Por tanto, existe evidencia estadística muy fuerte de que la distribución de los barrios está relacionada con las zonas.

Además, el V de Cramer = 0,939 indica una asociación muy fuerte, cercana al máximo posible de 1.

Interpretación del mapa de correspondencias - Correspondencia Zona Barrio

La lógica del gráfico es que las categorías que aparecen próximas entre sí presentan perfiles de asociación similares, mientras que las categorías alejadas representan perfiles diferentes.

Se observa, por ejemplo, una proximidad muy marcada entre Zona Oeste y Los Cristales, lo que indica que este barrio presenta un perfil particularmente asociado con esa zona dentro de las categorías analizadas.

También se observa que:

  • Zona Norte aparece claramente separada hacia la derecha y asociada con un perfil específico de barrios.
  • Zona Oriente presenta una posición diferenciada respecto de las demás zonas.
  • Zona Sur aparece en una posición claramente separada hacia la izquierda.
  • Zona Centro se encuentra próxima al origen, lo que indica que su perfil es menos extremo respecto a las dimensiones representadas.

Es importante interpretar con cautela la posición cercana al origen: no significa necesariamente que una zona no tenga barrios asociados, sino que su perfil contribuye menos a diferenciar las dimensiones principales del análisis.

Importancia de las dimensiones

La Dimensión 1 explica el 27,3 % de la inercia, mientras que la Dimensión 2 explica el 26,4%.

En conjunto, las dos dimensiones representan aproximadamente: 53,7 % de la inercia total.

Por tanto, el gráfico permite visualizar una proporción importante de la asociación existente entre zona y barrio, aunque no representa la totalidad de la información contenida en la tabla de contingencia.

Esto es importante metodológicamente: las posiciones observadas en el gráfico deben interpretarse como una representación resumida de la asociación, no como una descripción completa de todas las relaciones existentes.

Gráfica 11. Correspondencia: Zona y Barrio.

6.3 Prueba de independencia Chi-cuadrado de Pearson y V de Cramer entre variables categóricas.

La prueba permite hacer el contraste de hipótesis como la medida utilizada para determinar la intensidad de la asociación.

Hipótesis generales del análisis

Para cada una de las tres relaciones se plantean las mismas hipótesis:

  • H₀ (hipótesis nula): las dos variables categóricas son independientes; es decir, no existe una asociación estadísticamente significativa entre ellas.

  • H₁ (hipótesis alternativa): las dos variables categóricas no son independientes; existe una asociación estadísticamente significativa entre ellas.

El nivel de significancia adoptado es α = 0,05. Por tanto:

  • Si p < 0,05, se rechaza H₀.

  • Si p ≥ 0,05, no se rechaza H₀.

El V de Cramer permite complementar la significancia estadística midiendo la intensidad de la asociación entre las variables. Es importante distinguir ambas cosas: una asociación puede ser estadísticamente significativa, pero tener una intensidad relativamente baja.

1. Relación entre tipo de vivienda y zona.

Hipótesis:

  • H₀: el tipo de vivienda y la zona de la ciudad son independientes.
  • H₁: existe una asociación entre el tipo de vivienda y la zona.

Resultado: se rechaza H₀ al 5 %, con V de Cramer = 0,288.

Interpretación

El resultado indica que existe evidencia estadística suficiente para afirmar que el tipo de vivienda no se distribuye de manera independiente entre las diferentes zonas de la ciudad.

Sin embargo, el valor de V = 0,288 indica que la intensidad de esta relación es débil. Esto significa que, aunque la zona está relacionada con el tipo de vivienda, la ubicación por sí sola no explica una proporción especialmente grande de las diferencias observadas en la tipología inmobiliaria.

En términos del mercado inmobiliario, esto sugiere que la composición de la oferta habitacional presenta algunas diferencias territoriales, pero dichas diferencias también están condicionadas por otros factores.

📌 Conclusión: existe asociación estadísticamente significativa, pero de intensidad débil.

2. Relación entre tipo de vivienda y barrio.

Hipótesis:

  • H₀: el tipo de vivienda y el barrio son independientes.
  • H₁: existe una asociación entre el tipo de vivienda y el barrio.

Resultado: se rechaza H₀ al 5 %, con V de Cramer = 0,305.

Interpretación

En este caso también se encuentra evidencia estadística de una relación entre las variables. El resultado indica que la distribución de los tipos de vivienda varía significativamente entre los barrios.

El V de Cramer = 0,305 representa una asociación de intensidad **moderada*. Comparativamente, esta relación es ligeramente más fuerte que la observada entre tipo de vivienda y zona.

Esto resulta interesante para el análisis del mercado inmobiliario, porque indica que el nivel de desagregación territorial importa. Mientras que una clasificación amplia por zonas presenta una asociación débil, al analizar unidades territoriales más específicas como los barrios se obtiene una relación algo más marcada con la tipología de vivienda.

En otras palabras, determinados barrios tienden a especializarse o concentrar determinados tipos de vivienda.

📌 Conclusión: existe asociación estadísticamente significativa y de intensidad moderada.

3. Relación entre zona y barrio.

Hipótesis:

  • H₀: la zona y el barrio son independientes.
  • H₁: existe una asociación entre zona y barrio.

Resultado: se rechaza H₀ al 5 %, con V de Cramer = 0,506.

Interpretación.

Esta es la relación que presenta la mayor intensidad de asociación de las tres.

El V de Cramer = 0,506 evidencia una asociación alta entre la zona y el barrio. Este resultado es coherente con la propia estructura territorial de la ciudad: los barrios no se distribuyen aleatoriamente entre las zonas, sino que determinados barrios están claramente vinculados con determinadas zonas geográficas.

Esto también ayuda a interpretar el mapa de correspondencias que obtuviste. Las categorías que aparecen próximas en el plano factorial representan perfiles que presentan una relación relativamente mayor dentro de la estructura de asociación.

Por tanto, esta relación muestra que la dimensión territorial constituye un elemento estructurante del mercado inmobiliario analizado.

📌 Conclusión: existe una asociación estadísticamente significativa y de intensidad alta.

En el anexo 4, el código con el cual se hicieron las pruebas y se muestra la síntesis de los resultados, para orientar mejor las decisiones.

6.4 Resultados y decisiones.

Los resultados obtenidos evidencian que existe una relación estadísticamente significativa entre las variables analizadas, ya que en todos los casos se rechazó la hipótesis de independencia al nivel de significancia del 5 %. Sin embargo, la intensidad de dicha relación varía entre los pares de variables. En particular, la asociación entre tipo de vivienda y zona es débil (V = 0.288), lo que indica que, aunque ambas variables están relacionadas, el tipo de inmueble no depende fuertemente de la zona donde se ubica. Por su parte, la relación entre tipo de vivienda y barrio presenta una intensidad moderada (V = 0.305), sugiriendo que algunos barrios tienden a concentrar determinados tipos de inmuebles con mayor frecuencia. Finalmente, la asociación entre zona y barrio es alta (V = 0.506), evidenciando una fuerte correspondencia entre ambas variables, lo cual es coherente dado que los barrios forman parte de zonas específicas de la ciudad. En conjunto, estos resultados permiten concluir que la ubicación geográfica desempeña un papel importante en la caracterización de la oferta inmobiliaria, siendo la relación entre zona y barrio la más determinante dentro de las analizadas.


FASE 7. Integración de resultados y formulación de recomendaciones.

7.1 Integración general de los resultados.

Al integrar los resultados, se observa que el mercado inmobiliario analizado presenta una estructura que puede entenderse a partir de tres dimensiones complementarias.

  • Primera dimensión: características físico-económicas. El ACP muestra que variables como precio, área y dotación residencial constituyen una de las principales fuentes de diferenciación de las propiedades.

  • Segunda dimensión: perfil residencial. El segundo componente permite distinguir propiedades de acuerdo con la relación entre nivel socioeconómico y capacidad habitacional.

  • Tercera dimensión: localización territorial. Las pruebas de asociación muestran que la ubicación también contribuye a diferenciar la oferta, particularmente a través de la relación entre barrios y zonas.

Estas tres dimensiones no deben interpretarse de manera aislada. Por el contrario, los resultados sugieren que el mercado inmobiliario se estructura mediante la interacción entre valor económico, características físicas, perfil residencial y localización.

Un hallazgo particularmente relevante es que la dimensión territorial no sustituye a las características económicas y físicas. El hecho de que ambos conglomerados tengan presencia predominante en la Zona Sur demuestra que una misma zona puede contener propiedades pertenecientes a segmentos económicos y residenciales diferentes.

Por ello, la segmentación obtenida mediante ACP y conglomerados aporta una lectura más detallada que una clasificación basada únicamente en la ubicación geográfica.

7.2 Implicaciones para el mercado inmobiliario.

Los resultados permitieron identificar dos mercados claramente diferenciados.

El Conglomerado 1, aunque representa solamente una tercera parte de las propiedades, concentra una oferta de mayor valor y dotación. Este segmento puede estar asociado con compradores que buscan mayor superficie, mayor capacidad habitacional y características residenciales superiores.

El Conglomerado 2, por representar cerca de dos terceras partes de la oferta, constituye el segmento de mayor volumen. Sus menores precios, áreas y niveles de dotación sugieren un mercado potencialmente más amplio en términos de cantidad de propiedades disponibles.

Esto tiene una implicación importante: el segmento más numeroso no necesariamente es el segmento de mayor valor económico individual. El Conglomerado 1 tiene menos propiedades, pero cada inmueble presenta características de mayor valor relativo.

En consecuencia, las estrategias comerciales, de valoración y de planificación inmobiliaria deberían diferenciar entre volumen de mercado y valor unitario de las propiedades.

7.3 Recomendaciones.

1. Diseñar estrategias diferenciadas para los dos segmentos.

No resulta conveniente aplicar una única estrategia comercial a toda la oferta. Se recomienda desarrollar estrategias específicas para:

  • Conglomerado 1: propiedades de mayor valor, superficie y dotación.
  • Conglomerado 2: propiedades de menor precio y mayor participación en el mercado.

Esto permitiría ajustar precios, productos, financiación y estrategias de comercialización al perfil de cada segmento.

2. Incorporar el barrio como variable estratégica.

Dado que la relación entre tipo de vivienda y barrio es mayor que la relación entre tipo de vivienda y zona, se recomienda que los análisis inmobiliarios futuros trabajen con un nivel territorial más desagregado.

El barrio puede proporcionar información más precisa para identificar patrones de oferta que una clasificación general por zona.

3. No utilizar la zona como único criterio de segmentación.

Los resultados demuestran que una misma zona puede contener propiedades con perfiles considerablemente diferentes. Por ello, las decisiones de valoración, comercialización o planificación deberían combinar:

ubicación + precio + área + estrato + dotación + tipo de vivienda.

4. Priorizar el segmento mayoritario sin ignorar el segmento de alto valor.

El Conglomerado 2, con el 66,9 % de las propiedades, debería recibir especial atención cuando el objetivo sea analizar el comportamiento general de la oferta.

Sin embargo, el Conglomerado 1 no debe considerarse marginal, pues concentra propiedades de mayor valor y características superiores. Puede representar un segmento estratégico desde la perspectiva de rentabilidad por inmueble.

5. Utilizar la segmentación para valoración inmobiliaria.

Los dos conglomerados pueden incorporarse como una variable explicativa en futuros modelos de valoración.

Por ejemplo, podría evaluarse si pertenecer al Conglomerado 1 o al Conglomerado 2 tiene un efecto significativo sobre el precio después de controlar por área, ubicación, tipo de vivienda y otras características.

Esto permitiría pasar de una descripción del mercado a un modelo cuantitativo de valoración inmobiliaria.

6. Complementar el análisis con información temporal.

Los resultados corresponden a la estructura observada en la base analizada. Una recomendación importante es repetir la segmentación con información de diferentes períodos para determinar si:

  • Aumenta o disminuye la participación del segmento de mayor valor;
  • Cambia la distribución territorial de los conglomerados;
  • Se modifican los precios relativos;
  • Aparecen nuevos segmentos;
  • Algunos barrios experimentan procesos de transformación inmobiliaria.

Esto permitiría evolucionar desde una segmentación estática hacia un análisis de la dinámica del mercado inmobiliario.


Conclusiones

  • El Análisis de Componentes Principales permitió reducir las cinco variables originales —estrato, precio, área construida, número de baños y número de habitaciones— a dos componentes principales que concentran la mayor parte de la información contenida en la estructura multivariada del mercado inmobiliario. Los dos componentes retenidos superan el criterio del 80 % de varianza acumulada, lo que evidencia que la reducción dimensional conserva una proporción elevada de la información original. El primer componente representa fundamentalmente una dimensión físico-económica de la vivienda, determinada por el número de baños, el precio y el área construida. En este componente, las propiedades con mayores dimensiones y dotación tienden a asociarse con mayores niveles de precio, reflejando una relación coherente entre las características físicas del inmueble y su valoración económica. El segundo componente representa una dimensión relacionada con el perfil residencial de las propiedades, en la cual adquieren especial importancia el estrato y el número de habitaciones. La relación entre estas variables permite diferenciar perfiles de vivienda que no necesariamente se explican únicamente por su precio o tamaño, sino por las características socioeconómicas y de capacidad habitacional.

    • En términos del mercado inmobiliario urbano de Cali, el resultado del ACP evidencia que la oferta puede comprenderse a partir de dos dimensiones fundamentales: una asociada con la escala y valoración económica de la vivienda y otra relacionada con su perfil residencial y socioeconómico. Esta reducción facilitó la identificación de patrones, la segmentación de la oferta y la construcción de perfiles homogéneos de propiedades.

    • Desde una perspectiva de Ciencia de Datos, el ACP constituye además una etapa estratégica para los análisis posteriores, particularmente para el análisis de conglomerados, ya que permite trabajar con un conjunto reducido de dimensiones que conserva gran parte de la variabilidad original y facilita la identificación de segmentos diferenciados dentro del mercado inmobiliario.

  • Los resultados muestran que la oferta inmobiliaria analizada no constituye un mercado homogéneo, sino que puede organizarse de manera consistente en dos segmentos claramente diferenciados.

    • La principal dimensión de diferenciación está asociada al nivel de valor y dotación de las viviendas: el Conglomerado 1 reúne propiedades de mayor precio, mayor área, mayor estrato y mayor número de baños y habitaciones, mientras que el Conglomerado 2 concentra propiedades de menor precio y menor escala, y además constituye la mayor parte de la oferta.

    • Un hallazgo especialmente relevante es que ambos segmentos tienen como zona predominante la Zona Sur, pero se diferencian por el tipo de vivienda y por los barrios predominantes. Esto sugiere que la ubicación territorial general no es suficiente para explicar la segmentación del mercado; dentro de una misma zona coexisten ofertas inmobiliarias con perfiles económicos y residenciales diferentes.

    • En términos de mercado, la segmentación permite distinguir, por un lado, una oferta residencial de mayor valor, representada principalmente por casas en sectores como Ciudad Jardín, y, por otro, una oferta de mayor volumen, asociada principalmente a apartamentos y representada por sectores como Valle del Lili.

    • Por tanto, la decisión final es trabajar con dos segmentos inmobiliarios, ya que esta solución ofrece el mejor equilibrio entre calidad estadística, interpretabilidad y utilidad para comprender la estructura de la oferta inmobiliaria de Cali.

  • En conjunto, los resultados del Análisis de Correspondencias permiten concluir que las variables territoriales y tipológicas del mercado inmobiliario no son independientes. En las tres relaciones analizadas se rechaza la hipótesis nula de independencia al nivel de significancia del 5 %, aunque la intensidad de las asociaciones es diferente.

    • La asociación más débil se presenta entre tipo de vivienda y zona (V = 0,288), mientras que la relación entre tipo de vivienda y barrio (V = 0,305) alcanza una intensidad moderada. La asociación más importante corresponde a zona y barrio (V = 0,506), lo que evidencia una marcada estructura territorial de la oferta inmobiliaria.

    • Desde la perspectiva del mercado inmobiliario, estos resultados sugieren que la ubicación geográfica constituye un elemento relevante para comprender la composición y distribución de la oferta. En particular, el barrio parece proporcionar una diferenciación territorial más específica que la clasificación general por zonas. Esto respalda el uso del Análisis de Correspondencias como herramienta exploratoria para identificar patrones de especialización territorial, proximidad entre categorías y perfiles diferenciados de la oferta inmobiliaria.

    • Un punto metodológico importante: rechazar H₀ demuestra asociación, no causalidad. Por tanto, los resultados permiten afirmar que las variables están relacionadas, pero no que una de ellas sea necesariamente la causa de la otra.

En síntesis, los resultados evidencian que la oferta inmobiliaria urbana analizada presenta una estructura heterogénea y multidimensional, en la que interactúan características económicas, físicas, residenciales y territoriales. El Análisis de Componentes Principales permitió resumir las cinco variables originales en dos dimensiones interpretables: una dimensión físico-económica, asociada principalmente con el tamaño, la dotación y el nivel de valor de las propiedades, y una dimensión de perfil residencial, relacionada fundamentalmente con el estrato y la capacidad habitacional. Los dos componentes retenidos concentran el 85,09 % de la varianza acumulada, por lo que constituyen una representación adecuada de la estructura multivariada de la oferta.

Sobre estas dimensiones fue posible identificar dos segmentos inmobiliarios claramente diferenciados. El Conglomerado 1, de menor participación en la oferta, reúne propiedades de mayor estrato, precio, área construida, número de baños y habitaciones, configurando un segmento de mayor valor y dotación. Por su parte, el Conglomerado 2, que concentra aproximadamente el 66,9 % de las propiedades, representa el segmento mayoritario y está conformado por inmuebles de menor escala y menor valor relativo. Esta diferenciación confirma que el mercado analizado no constituye una oferta homogénea y que existen perfiles inmobiliarios con características económicas y residenciales claramente diferenciadas.

Los resultados también muestran que la segmentación no puede explicarse exclusivamente desde una perspectiva territorial. Aunque ambos conglomerados presentan como zona predominante la Zona Sur, sus perfiles se diferencian en términos del tipo de vivienda y de los barrios predominantes. Además, la distribución espacial de los conglomerados evidencia una importante superposición territorial. Por tanto, pertenecer a una determinada zona no implica que las propiedades presenten necesariamente el mismo perfil económico o residencial.

Por otra parte, el Análisis de Correspondencias confirma que existe una relación estadísticamente significativa entre las variables territoriales y tipológicas estudiadas. La relación entre tipo de vivienda y zona presenta una intensidad débil (V = 0,288), mientras que tipo de vivienda y barrio alcanza una intensidad moderada (V = 0,305). La asociación más fuerte corresponde a zona y barrio, con un V de Cramer = 0,506, lo que evidencia una marcada estructura territorial de la oferta inmobiliaria. Estos resultados respaldan la importancia del territorio para comprender la composición del mercado, aunque, metodológicamente, debe recordarse que una asociación estadística no implica una relación causal.

En consecuencia, la principal conclusión para la toma de decisiones es que el mercado inmobiliario de Cali no debe analizarse como un conjunto homogéneo ni únicamente desde la ubicación geográfica. Una caracterización adecuada requiere integrar simultáneamente las dimensiones económica, física, residencial y territorial. Esta perspectiva permite identificar segmentos de mercado con mayor precisión y constituye una base útil para orientar procesos de valoración inmobiliaria, estrategias de comercialización, identificación de mercados objetivo y planificación territorial.


Referencias

[1] R. A. Johnson y D. W. Wichern, Applied Multivariate Statistical Analysis, 6th ed. Upper Saddle River, NJ, USA: Pearson Education, 2007.

[2] J. F. Hair Jr., W. C. Black, B. J. Babin y R. E. Anderson, Multivariate Data Analysis, 8th ed. Andover, UK: Cengage Learning, 2019.

[3] W. G. Zikmund, B. J. Babin, J. C. Carr y M. Griffin, Business Research Methods, 9th ed. Mason, OH, USA: South-Western Cengage Learning, 2013.

[4] R. J. Shiller, Irrational Exuberance, 3rd ed. Princeton, NJ, USA: Princeton University Press, 2015.

[5] I. T. Jolliffe y J. Cadima, “Principal Component Analysis: A Review and Recent Developments,” Philosophical Transactions of the Royal Society A, vol. 374, no. 2065, pp. 1-16, 2016.

[6] B. S. Everitt, S. Landau, M. Leese y D. Stahl, Cluster Analysis, 5th ed. Chichester, UK: John Wiley & Sons, 2011.

[7] M. Greenacre, Correspondence Analysis in Practice, 3rd ed. Boca Raton, FL, USA: CRC Press, 2017.

[8] C. O. Wilke, Fundamentals of Data Visualization: A Primer on Making Informative and Compelling Figures. Sebastopol, CA, USA: O’Reilly Media, 2019.


Anexos

Anexo 1. Script del Proyecto.

# ==============================================================================
# UNIVERSIDAD PONTIFICIA JAVERIANA CALI
# Maestria en Ciencia de Datos
# Actividad 1: Evaluacion de la oferta inmobiliaria urbana
#
# SCRIPT AJUSTADO Y CONSOLIDADO
# Objetivos:
#   1. Cargar la base vivienda sin modificarla.
#   2. Validar y visualizar datos faltantes.
#   3. Detectar y visualizar valores atipicos.
#   4. Excluir piso y parqueaderos de la base analitica.
#   5. Imputar faltantes y atipicos mediante KNN mixto.
#   6. Crear vivienda_limpia con cero NA.
#   7. Mostrar contrastes antes/despues en el panel Plots.
#   8. Resumir variables recomendadas para ACP, conglomerados y
#      correspondencia.
#
# Compatible con R 4.5.x.
# Las graficas NO se exportan. Se muestran en el panel Plots de RStudio.
# ============================================================================== 

# ==============================================================================
# 0. CONFIGURACION GENERAL
# ============================================================================== 

options(scipen = 999, digits = 5)
set.seed(2026)

# Repositorio CRAN utilizado solo si falta una dependencia.
options(repos = c(CRAN = "https://cloud.r-project.org"))

# Paquetes requeridos.
paquetes_requeridos <- c("ggplot2")

for (paquete in paquetes_requeridos) {
  if (!requireNamespace(paquete, quietly = TRUE)) {
    install.packages(paquete, dependencies = TRUE)
  }
  if (!requireNamespace(paquete, quietly = TRUE)) {
    stop(
      paste0("No fue posible instalar o cargar el paquete ", paquete, "."),
      call. = FALSE
    )
  }
}

# Carpeta para tablas, auditorias y bases. Las graficas no se guardan.
carpeta_salida <- file.path(getwd(), "Resultados_Limpieza_Vivienda")
if (!dir.exists(carpeta_salida)) {
  dir.create(carpeta_salida, recursive = TRUE, showWarnings = FALSE)
}
if (!dir.exists(carpeta_salida)) {
  stop("No fue posible crear la carpeta de resultados.", call. = FALSE)
}

# Paleta pastel comun.
colores_pastel <- c(
  Antes = "#FFCAD4",
  Despues = "#B7E4C7",
  Azul = "#A9DEF9",
  Lila = "#CDB4DB",
  Amarillo = "#FFE5A5",
  Gris = "#D8E2DC"
)

# Tema comun para evitar traslapes.
tema_pastel <- ggplot2::theme_minimal(base_size = 11) +
  ggplot2::theme(
    plot.title = ggplot2::element_text(
      face = "bold", size = 14, colour = "#443A4C", hjust = 0.5
    ),
    plot.subtitle = ggplot2::element_text(
      size = 9.5, colour = "#6B596F", hjust = 0.5,
      margin = ggplot2::margin(b = 10)
    ),
    plot.caption = ggplot2::element_text(
      size = 8.5, colour = "#765B62", hjust = 0.5,
      margin = ggplot2::margin(t = 8)
    ),
    axis.text = ggplot2::element_text(colour = "#4D4044"),
    axis.title = ggplot2::element_text(colour = "#4D4044", face = "bold"),
    panel.grid.minor = ggplot2::element_blank(),
    legend.position = "bottom",
    legend.title = ggplot2::element_blank(),
    plot.margin = ggplot2::margin(12, 28, 12, 12)
  )

# ==============================================================================
# 1. CARGAR LA BASE VIVIENDA
# ============================================================================== 

# Si vivienda ya existe, se conserva. Si no existe, se carga desde
# paqueteMODELOS. La instalacion desde GitHub solo se intenta si el paquete falta.
if (!exists("vivienda", envir = .GlobalEnv, inherits = FALSE)) {

  if (!requireNamespace("paqueteMODELOS", quietly = TRUE)) {
    if (!requireNamespace("remotes", quietly = TRUE)) {
      install.packages("remotes", dependencies = TRUE)
    }
    if (!requireNamespace("remotes", quietly = TRUE)) {
      stop("No fue posible instalar remotes.", call. = FALSE)
    }

    remotes::install_github(
      repo = "dgonxalex80/paqueteMODELOS",
      dependencies = TRUE,
      upgrade = "never",
      force = FALSE,
      build_vignettes = FALSE
    )
  }

  if (!requireNamespace("paqueteMODELOS", quietly = TRUE)) {
    stop("paqueteMODELOS no esta disponible.", call. = FALSE)
  }

  data(
    list = "vivienda",
    package = "paqueteMODELOS",
    envir = .GlobalEnv
  )
}

if (!exists("vivienda", envir = .GlobalEnv, inherits = FALSE)) {
  stop("No fue posible cargar la base vivienda.", call. = FALSE)
}

vivienda <- as.data.frame(
  get("vivienda", envir = .GlobalEnv, inherits = FALSE),
  stringsAsFactors = FALSE,
  check.names = FALSE
)

# Validar estructura minima.
variables_esperadas <- c(
  "id", "zona", "piso", "estrato", "preciom", "areaconst",
  "parqueaderos", "banios", "habitaciones", "tipo", "barrio",
  "longitud", "latitud"
)

variables_ausentes <- setdiff(variables_esperadas, names(vivienda))
if (length(variables_ausentes) > 0L) {
  stop(
    paste0(
      "La base vivienda no contiene estas variables: ",
      paste(variables_ausentes, collapse = ", "), "."
    ),
    call. = FALSE
  )
}

if (nrow(vivienda) == 0L) {
  stop("La base vivienda no contiene registros.", call. = FALSE)
}

# Copia de control. vivienda no se modifica despues de esta linea.
vivienda_original_control <- vivienda

cat("\nBASE VIVIENDA CARGADA\n")
cat("Filas:", nrow(vivienda), "| Variables:", ncol(vivienda), "\n")
print(names(vivienda))

# ==============================================================================
# 2. VALIDACION Y GRAFICA DE DATOS FALTANTES ORIGINALES
# ============================================================================== 

resumen_nulos_original <- data.frame(
  variable = names(vivienda),
  cantidad_nulos = vapply(vivienda, function(x) sum(is.na(x)), integer(1)),
  stringsAsFactors = FALSE
)
resumen_nulos_original$porcentaje_nulos <-
  100 * resumen_nulos_original$cantidad_nulos / nrow(vivienda)

resumen_nulos_original

# write.csv(
#  resumen_nulos_original,
#  file.path(carpeta_salida, "01_resumen_nulos_original.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )

orden_nulos <- resumen_nulos_original$variable[
  order(resumen_nulos_original$cantidad_nulos, decreasing = FALSE)
]
resumen_nulos_original$variable <- factor(
  resumen_nulos_original$variable,
  levels = orden_nulos
)
resumen_nulos_original$estado <- ifelse(
  resumen_nulos_original$cantidad_nulos > 0,
  "Con datos faltantes",
  "Sin datos faltantes"
)

total_nulos_original <- sum(resumen_nulos_original$cantidad_nulos)
porcentaje_global_nulos <-
  100 * total_nulos_original / (nrow(vivienda) * ncol(vivienda))

grafico_nulos_original <- ggplot2::ggplot(
  resumen_nulos_original,
  ggplot2::aes(x = variable, y = cantidad_nulos, fill = estado)
) +
  ggplot2::geom_col(
    width = 0.68, alpha = 0.88, colour = "#5A5266", linewidth = 0.25
  ) +
  ggplot2::geom_text(
    ggplot2::aes(
      label = paste0(cantidad_nulos, " (", round(porcentaje_nulos, 2), " %)")
    ),
    hjust = -0.12, size = 3.1, colour = "#4D4044"
  ) +
  ggplot2::coord_flip(clip = "off") +
  ggplot2::scale_fill_manual(
    values = c(
      "Con datos faltantes" = colores_pastel[["Antes"]],
      "Sin datos faltantes" = colores_pastel[["Gris"]]
    )
  ) +
  ggplot2::scale_y_continuous(
    expand = ggplot2::expansion(mult = c(0, 0.22))
  ) +
  ggplot2::labs(
    title = "Frecuencia de datos faltantes por variable",
    subtitle = paste0(
      "Base vivienda: ", nrow(vivienda), " registros y ", ncol(vivienda),
      " variables"
    ),
    caption = paste0(
      "Total de datos faltantes: ", total_nulos_original,
      " | Porcentaje global: ", round(porcentaje_global_nulos, 3), " %"
    ),
    x = NULL,
    y = "Numero de valores faltantes",
    fill = NULL
  ) +
  tema_pastel +
  ggplot2::theme(panel.grid.major.y = ggplot2::element_blank())

print(grafico_nulos_original)

# ==============================================================================
# 3. CREAR COPIA DE TRABAJO
# ============================================================================== 

datos_trabajo <- as.data.frame(
  vivienda,
  stringsAsFactors = FALSE,
  check.names = FALSE
)
datos_trabajo$fila_original <- seq_len(nrow(datos_trabajo))

# Piso y parqueaderos se excluyen de la base analitica, pero vivienda permanece
# intacta.
datos_trabajo <- datos_trabajo[
  ,
  !names(datos_trabajo) %in% c("piso", "parqueaderos"),
  drop = FALSE
]

# Normalizar vacios de variables de texto.
variables_texto <- names(datos_trabajo)[
  vapply(
    datos_trabajo,
    function(x) is.character(x) || is.factor(x),
    logical(1)
  )
]

for (variable in variables_texto) {
  datos_trabajo[[variable]] <- as.character(datos_trabajo[[variable]])
  es_vacio <- !is.na(datos_trabajo[[variable]]) &
    trimws(datos_trabajo[[variable]]) == ""
  datos_trabajo[[variable]][es_vacio] <- NA_character_
}

# Garantizar clases numericas esperadas.
variables_numericas_esperadas <- intersect(
  c(
    "id", "estrato", "preciom", "areaconst", "banios",
    "habitaciones", "longitud", "latitud"
  ),
  names(datos_trabajo)
)

for (variable in variables_numericas_esperadas) {
  datos_trabajo[[variable]] <- suppressWarnings(
    as.numeric(as.character(datos_trabajo[[variable]]))
  )
}

# Copia comparable antes de convertir atipicos en NA.
datos_antes_tratamiento <- datos_trabajo

# ==============================================================================
# 4. DETECTAR Y VISUALIZAR VALORES ATIPICOS
# ============================================================================== 

variables_atipicos <- intersect(
  c("preciom", "areaconst", "banios", "habitaciones"),
  names(datos_trabajo)
)

resumen_atipicos <- data.frame(
  variable = character(0),
  cantidad_atipicos = integer(0),
  porcentaje_atipicos = numeric(0),
  limite_inferior = numeric(0),
  limite_superior = numeric(0),
  stringsAsFactors = FALSE
)

auditoria_atipicos <- data.frame(
  fila_original = integer(0),
  variable = character(0),
  valor_original = numeric(0),
  limite_inferior = numeric(0),
  limite_superior = numeric(0),
  stringsAsFactors = FALSE
)

for (variable in variables_atipicos) {
  x <- datos_trabajo[[variable]]
  x_valido <- x[is.finite(x)]

  if (length(x_valido) < 4L) {
    limite_inferior <- NA_real_
    limite_superior <- NA_real_
    posiciones_atipicas <- integer(0)
  } else {
    q1 <- as.numeric(stats::quantile(x_valido, 0.25, names = FALSE, type = 7))
    q3 <- as.numeric(stats::quantile(x_valido, 0.75, names = FALSE, type = 7))
    riq <- q3 - q1

    if (!is.finite(riq) || riq == 0) {
      limite_inferior <- NA_real_
      limite_superior <- NA_real_
      posiciones_atipicas <- integer(0)
    } else {
      limite_inferior <- q1 - 1.5 * riq
      limite_superior <- q3 + 1.5 * riq
      posiciones_atipicas <- which(
        is.finite(x) & (x < limite_inferior | x > limite_superior)
      )
    }
  }

  resumen_atipicos <- rbind(
    resumen_atipicos,
    data.frame(
      variable = variable,
      cantidad_atipicos = length(posiciones_atipicas),
      porcentaje_atipicos = 100 * length(posiciones_atipicas) / nrow(datos_trabajo),
      limite_inferior = limite_inferior,
      limite_superior = limite_superior,
      stringsAsFactors = FALSE
    )
  )

  if (length(posiciones_atipicas) > 0L) {
    auditoria_atipicos <- rbind(
      auditoria_atipicos,
      data.frame(
        fila_original = datos_trabajo$fila_original[posiciones_atipicas],
        variable = variable,
        valor_original = x[posiciones_atipicas],
        limite_inferior = limite_inferior,
        limite_superior = limite_superior,
        stringsAsFactors = FALSE
      )
    )
  }
}

# write.csv(
#  resumen_atipicos,
#  file.path(carpeta_salida, "02_resumen_atipicos.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )
# write.csv(
#  auditoria_atipicos,
#  file.path(carpeta_salida, "03_auditoria_atipicos.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )

# Base larga de variables originales para boxplots.
datos_boxplot_atipicos <- do.call(
  rbind,
  lapply(
    variables_atipicos,
    function(variable) {
      data.frame(
        variable = variable,
        valor = datos_antes_tratamiento[[variable]],
        stringsAsFactors = FALSE
      )
    }
  )
)
datos_boxplot_atipicos <- datos_boxplot_atipicos[
  is.finite(datos_boxplot_atipicos$valor),
  ,
  drop = FALSE
]

# Texto de anotacion por panel.
anotaciones_atipicos <- resumen_atipicos
anotaciones_atipicos$etiqueta <- paste0(
  "Atipicos: ", anotaciones_atipicos$cantidad_atipicos,
  " (", round(anotaciones_atipicos$porcentaje_atipicos, 2), " %)"
)

grafico_atipicos <- ggplot2::ggplot(
  datos_boxplot_atipicos,
  ggplot2::aes(x = variable, y = valor, fill = variable)
) +
  ggplot2::geom_boxplot(
    alpha = 0.78,
    width = 0.55,
    outlier.alpha = 0.30,
    outlier.size = 0.70,
    colour = "#5A5266",
    linewidth = 0.35
  ) +
  ggplot2::facet_wrap(~variable, scales = "free_y", ncol = 2) +
  ggplot2::geom_text(
    data = anotaciones_atipicos,
    ggplot2::aes(x = variable, y = Inf, label = etiqueta),
    inherit.aes = FALSE,
    vjust = 1.4,
    size = 3.0,
    colour = "#765B62"
  ) +
  ggplot2::scale_fill_manual(
    values = stats::setNames(
      c("#A9DEF9", "#FFCAD4", "#CDB4DB", "#B7E4C7")[
        seq_along(variables_atipicos)
      ],
      variables_atipicos
    )
  ) +
  ggplot2::labs(
    title = "Deteccion de valores atipicos por variable",
    subtitle = "Criterio: valores fuera de Q1 - 1,5 x RIQ y Q3 + 1,5 x RIQ",
    caption = paste0(
      "Total de valores atipicos detectados: ", nrow(auditoria_atipicos),
      ". Cada panel utiliza una escala propia."
    ),
    x = NULL,
    y = "Valor observado",
    fill = NULL
  ) +
  tema_pastel +
  ggplot2::theme(
    axis.text.x = ggplot2::element_blank(),
    axis.ticks.x = ggplot2::element_blank(),
    strip.background = ggplot2::element_rect(
      fill = "#EDE7F2", colour = "#CDB4DB", linewidth = 0.35
    ),
    strip.text = ggplot2::element_text(face = "bold", colour = "#443A4C"),
    legend.position = "none"
  )

print(grafico_atipicos)

# Convertir atipicos en NA solamente en datos_trabajo.
if (nrow(auditoria_atipicos) > 0L) {
  for (i in seq_len(nrow(auditoria_atipicos))) {
    posicion <- match(
      auditoria_atipicos$fila_original[i],
      datos_trabajo$fila_original
    )
    if (!is.na(posicion)) {
      datos_trabajo[posicion, auditoria_atipicos$variable[i]] <- NA
    }
  }
}

# ==============================================================================
# 5. IMPUTACION KNN MIXTA
# ============================================================================== 

# id no participa en KNN. Los demas campos se imputan con vecinos semejantes.
variables_imputables <- setdiff(
  names(datos_trabajo),
  c("id", "fila_original")
)

variables_numericas_knn <- variables_imputables[
  vapply(
    datos_trabajo[, variables_imputables, drop = FALSE],
    is.numeric,
    logical(1)
  )
]
variables_categoricas_knn <- setdiff(
  variables_imputables,
  variables_numericas_knn
)
variables_discretas <- intersect(
  c("estrato", "banios", "habitaciones"),
  variables_numericas_knn
)

# Matriz auxiliar para distancias. Las numericas se escalan robustamente.
datos_distancia <- datos_trabajo[, variables_imputables, drop = FALSE]
parametros_escalado <- list()

for (variable in variables_numericas_knn) {
  x <- datos_distancia[[variable]]
  centro <- stats::median(x, na.rm = TRUE)
  escala <- stats::IQR(x, na.rm = TRUE)
  if (!is.finite(escala) || escala == 0) {
    escala <- stats::sd(x, na.rm = TRUE)
  }
  if (!is.finite(escala) || escala == 0) {
    escala <- 1
  }
  parametros_escalado[[variable]] <- c(centro = centro, escala = escala)
  datos_distancia[[variable]] <- (x - centro) / escala
}

# Moda ponderada para objetivos categoricos.
moda_ponderada <- function(valores, pesos) {
  validos <- !is.na(valores) & is.finite(pesos)
  valores <- as.character(valores[validos])
  pesos <- pesos[validos]
  if (length(valores) == 0L) {
    return(NA_character_)
  }
  suma_pesos <- tapply(pesos, valores, sum)
  candidatos <- names(suma_pesos)[suma_pesos == max(suma_pesos)]
  sort(candidatos)[1]
}

# Distancia mixta entre una fila objetivo y candidatos.
calcular_distancia_mixta <- function(
    fila_objetivo,
    candidatos,
    variable_objetivo,
    datos_auxiliares) {

  predictores <- setdiff(names(datos_auxiliares), variable_objetivo)
  suma_distancia <- numeric(length(candidatos))
  numero_comparaciones <- integer(length(candidatos))

  for (predictor in predictores) {
    valor_objetivo <- datos_auxiliares[[predictor]][fila_objetivo]
    valores_candidatos <- datos_auxiliares[[predictor]][candidatos]

    if (is.na(valor_objetivo)) {
      next
    }

    disponibles <- !is.na(valores_candidatos)
    if (!any(disponibles)) {
      next
    }

    if (is.numeric(datos_auxiliares[[predictor]])) {
      diferencias <- abs(valores_candidatos[disponibles] - valor_objetivo)
      # Evita que una diferencia extrema monopolice la distancia.
      diferencias <- pmin(diferencias, 4)
    } else {
      diferencias <- as.numeric(
        as.character(valores_candidatos[disponibles]) !=
          as.character(valor_objetivo)
      )
    }

    suma_distancia[disponibles] <-
      suma_distancia[disponibles] + diferencias
    numero_comparaciones[disponibles] <-
      numero_comparaciones[disponibles] + 1L
  }

  distancias <- rep(Inf, length(candidatos))
  utilizables <- numero_comparaciones >= 2L
  distancias[utilizables] <-
    suma_distancia[utilizables] / numero_comparaciones[utilizables]
  distancias
}

# Auditoria de todas las imputaciones.
auditoria_imputacion <- data.frame(
  fila_original = integer(0),
  variable = character(0),
  origen = character(0),
  metodo = character(0),
  valor_original = character(0),
  valor_imputado = character(0),
  vecinos_utilizados = integer(0),
  stringsAsFactors = FALSE
)

k_vecinos <- 5L

for (variable in variables_imputables) {
  filas_pendientes <- which(is.na(datos_trabajo[[variable]]))
  if (length(filas_pendientes) == 0L) {
    next
  }

  cat("Imputando", variable, ":", length(filas_pendientes), "valores\n")

  for (fila in filas_pendientes) {
    candidatos <- which(
      !is.na(datos_trabajo[[variable]]) &
        seq_len(nrow(datos_trabajo)) != fila
    )

    if (length(candidatos) == 0L) {
      stop(
        paste0("No existen candidatos para imputar ", variable, "."),
        call. = FALSE
      )
    }

    distancias <- calcular_distancia_mixta(
      fila_objetivo = fila,
      candidatos = candidatos,
      variable_objetivo = variable,
      datos_auxiliares = datos_distancia
    )

    posiciones_validas <- which(is.finite(distancias))

    # Respaldo determinista si una fila no tiene al menos dos predictores
    # comparables. El caso queda identificado en la auditoria.
    if (length(posiciones_validas) == 0L) {
      if (variable %in% variables_numericas_knn) {
        valor_imputado <- stats::median(
          datos_trabajo[[variable]], na.rm = TRUE
        )
        if (variable %in% variables_discretas) {
          valor_imputado <- round(valor_imputado)
        }
        metodo <- "Respaldo: mediana"
      } else {
        frecuencias <- table(datos_trabajo[[variable]], useNA = "no")
        valor_imputado <- names(frecuencias)[which.max(frecuencias)]
        metodo <- "Respaldo: moda"
      }
      vecinos_usados <- 0L
    } else {
      orden <- posiciones_validas[
        order(distancias[posiciones_validas], na.last = NA)
      ]
      seleccion <- head(orden, k_vecinos)
      indices_vecinos <- candidatos[seleccion]
      distancias_vecinos <- distancias[seleccion]
      pesos <- 1 / pmax(distancias_vecinos, 1e-8)
      valores_vecinos <- datos_trabajo[[variable]][indices_vecinos]

      if (variable %in% variables_numericas_knn) {
        valor_imputado <- stats::weighted.mean(
          as.numeric(valores_vecinos), pesos, na.rm = TRUE
        )
        if (variable %in% variables_discretas) {
          valor_imputado <- round(valor_imputado)
        }
      } else {
        valor_imputado <- moda_ponderada(valores_vecinos, pesos)
      }
      metodo <- "KNN"
      vecinos_usados <- length(indices_vecinos)
    }

    fila_original_actual <- datos_trabajo$fila_original[fila]
    posicion_atipico <- which(
      auditoria_atipicos$fila_original == fila_original_actual &
        auditoria_atipicos$variable == variable
    )
    era_atipico <- length(posicion_atipico) > 0L

    origen <- if (era_atipico) "Valor atipico" else "Faltante original"
    valor_original <- if (era_atipico) {
      as.character(auditoria_atipicos$valor_original[posicion_atipico[1]])
    } else {
      NA_character_
    }

    datos_trabajo[[variable]][fila] <- valor_imputado

    # Actualizar la base auxiliar para imputaciones posteriores.
    if (variable %in% variables_numericas_knn) {
      centro <- parametros_escalado[[variable]]["centro"]
      escala <- parametros_escalado[[variable]]["escala"]
      datos_distancia[[variable]][fila] <-
        (as.numeric(valor_imputado) - centro) / escala
    } else {
      datos_distancia[[variable]][fila] <- as.character(valor_imputado)
    }

    auditoria_imputacion <- rbind(
      auditoria_imputacion,
      data.frame(
        fila_original = fila_original_actual,
        variable = variable,
        origen = origen,
        metodo = metodo,
        valor_original = valor_original,
        valor_imputado = as.character(valor_imputado),
        vecinos_utilizados = vecinos_usados,
        stringsAsFactors = FALSE
      )
    )
  }
}

# id: identificador tecnico, no se imputa mediante KNN.
filas_id_faltante <- which(is.na(datos_trabajo$id))
if (length(filas_id_faltante) > 0L) {
  id_validos <- datos_trabajo$id[is.finite(datos_trabajo$id)]
  desde_id <- if (length(id_validos) > 0L) max(id_validos) + 1 else 1
  nuevos_id <- seq.int(desde_id, length.out = length(filas_id_faltante))
  datos_trabajo$id[filas_id_faltante] <- nuevos_id

  auditoria_imputacion <- rbind(
    auditoria_imputacion,
    data.frame(
      fila_original = datos_trabajo$fila_original[filas_id_faltante],
      variable = "id",
      origen = "Faltante original",
      metodo = "Identificador tecnico secuencial",
      valor_original = NA_character_,
      valor_imputado = as.character(nuevos_id),
      vecinos_utilizados = 0L,
      stringsAsFactors = FALSE
    )
  )
}

# auditoria_imputacion

# ==============================================================================
# 6. CREAR Y VALIDAR vivienda_limpia
# ============================================================================== 

columnas_finales <- setdiff(names(datos_trabajo), "fila_original")
faltantes_post_imputacion <- colSums(
  is.na(datos_trabajo[, columnas_finales, drop = FALSE])
)

if (sum(faltantes_post_imputacion) > 0L) {
  detalle <- paste0(
    names(faltantes_post_imputacion)[faltantes_post_imputacion > 0],
    " = ",
    faltantes_post_imputacion[faltantes_post_imputacion > 0],
    collapse = "; "
  )
  stop(
    paste0("La imputacion no termino. Faltantes pendientes: ", detalle, "."),
    call. = FALSE
  )
}

vivienda_limpia <- datos_trabajo[, columnas_finales, drop = FALSE]
rownames(vivienda_limpia) <- NULL
assign("vivienda_limpia", vivienda_limpia, envir = .GlobalEnv)

# Controles no negociables.
stopifnot(
  identical(vivienda, vivienda_original_control),
  sum(is.na(vivienda_limpia)) == 0L,
  !("piso" %in% names(vivienda_limpia)),
  !("parqueaderos" %in% names(vivienda_limpia))
)

# write.csv(
#  auditoria_imputacion,
#  file.path(carpeta_salida, "04_auditoria_imputacion.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )
# write.csv(
#  vivienda_limpia,
#  file.path(carpeta_salida, "vivienda_limpia.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )
# saveRDS(vivienda_limpia, file.path(carpeta_salida, "vivienda_limpia.rds"))

# ==============================================================================
# 7. GRAFICA DE NULOS ANTES Y DESPUES
# ============================================================================== 

variables_finales <- names(vivienda_limpia)
nulos_antes <- vapply(
  vivienda[, variables_finales, drop = FALSE],
  function(x) sum(is.na(x)),
  integer(1)
)
nulos_despues <- vapply(
  vivienda_limpia[, variables_finales, drop = FALSE],
  function(x) sum(is.na(x)),
  integer(1)
)

tabla_nulos_comparacion <- data.frame(
  variable = variables_finales,
  antes = as.integer(nulos_antes),
  despues = as.integer(nulos_despues),
  imputados = as.integer(nulos_antes - nulos_despues),
  stringsAsFactors = FALSE
)

# write.csv(
#  tabla_nulos_comparacion,
#  file.path(carpeta_salida, "05_nulos_antes_despues.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )

datos_nulos_comparacion <- rbind(
  data.frame(
    variable = variables_finales,
    momento = "Antes",
    cantidad = as.integer(nulos_antes),
    stringsAsFactors = FALSE
  ),
  data.frame(
    variable = variables_finales,
    momento = "Despues",
    cantidad = as.integer(nulos_despues),
    stringsAsFactors = FALSE
  )
)

orden_variables <- tabla_nulos_comparacion$variable[
  order(tabla_nulos_comparacion$antes, decreasing = FALSE)
]
datos_nulos_comparacion$variable <- factor(
  datos_nulos_comparacion$variable,
  levels = orden_variables
)
datos_nulos_comparacion$momento <- factor(
  datos_nulos_comparacion$momento,
  levels = c("Antes", "Despues")
)

grafico_nulos_antes_despues <- ggplot2::ggplot(
  datos_nulos_comparacion,
  ggplot2::aes(x = variable, y = cantidad, fill = momento)
) +
  ggplot2::geom_col(
    position = ggplot2::position_dodge(width = 0.72),
    width = 0.64,
    alpha = 0.88,
    colour = "#5A5266",
    linewidth = 0.25
  ) +
  ggplot2::geom_text(
    ggplot2::aes(label = cantidad),
    position = ggplot2::position_dodge(width = 0.72),
    hjust = -0.25,
    size = 3.3,
    colour = "#4D4044"
  ) +
  ggplot2::coord_flip(clip = "off") +
  ggplot2::scale_fill_manual(
    values = c(
      "Antes" = colores_pastel[["Antes"]],
      "Despues" = colores_pastel[["Despues"]]
    )
  ) +
  ggplot2::scale_y_continuous(
    expand = ggplot2::expansion(mult = c(0, 0.22))
  ) +
  ggplot2::labs(
    title = "Datos faltantes antes y despues de la limpieza",
    subtitle = paste0(
      "Se comparan las variables de vivienda_limpia con sus columnas ",
      "correspondientes en vivienda"
    ),
    caption = paste0(
      "Total antes: ", sum(nulos_antes),
      " | Total despues: ", sum(nulos_despues),
      " | Faltantes originales imputados: ", sum(nulos_antes - nulos_despues)
    ),
    x = NULL,
    y = "Numero de valores faltantes",
    fill = NULL
  ) +
  tema_pastel +
  ggplot2::theme(panel.grid.major.y = ggplot2::element_blank())

print(grafico_nulos_antes_despues)

# ==============================================================================
# 8. CONTRASTE DE VARIABLES IMPUTADAS: ANTES Y DESPUES
# ============================================================================== 

variables_tratadas <- unique(auditoria_imputacion$variable)
variables_tratadas <- intersect(
  variables_tratadas,
  intersect(names(vivienda), names(vivienda_limpia))
)

variables_tratadas_numericas <- variables_tratadas[
  vapply(
    vivienda_limpia[, variables_tratadas, drop = FALSE],
    is.numeric,
    logical(1)
  )
]
variables_tratadas_numericas <- setdiff(
  variables_tratadas_numericas,
  "id"
)
variables_tratadas_categoricas <- setdiff(
  variables_tratadas,
  c(variables_tratadas_numericas, "id")
)

# Base larga numerica totalmente controlada.
if (length(variables_tratadas_numericas) > 0L) {
  lista_numerica <- lapply(
    variables_tratadas_numericas,
    function(variable) {
      antes <- suppressWarnings(as.numeric(as.character(vivienda[[variable]])))
      despues <- suppressWarnings(
        as.numeric(as.character(vivienda_limpia[[variable]]))
      )
      rbind(
        data.frame(
          variable = variable,
          momento = "Antes",
          valor = antes,
          stringsAsFactors = FALSE
        ),
        data.frame(
          variable = variable,
          momento = "Despues",
          valor = despues,
          stringsAsFactors = FALSE
        )
      )
    }
  )

  datos_contraste_numerico <- do.call(rbind, lista_numerica)
  datos_contraste_numerico <- as.data.frame(
    datos_contraste_numerico,
    stringsAsFactors = FALSE
  )
  datos_contraste_numerico$valor <- as.numeric(datos_contraste_numerico$valor)
  datos_contraste_numerico <- datos_contraste_numerico[
    is.finite(datos_contraste_numerico$valor),
    ,
    drop = FALSE
  ]
  datos_contraste_numerico$momento <- factor(
    datos_contraste_numerico$momento,
    levels = c("Antes", "Despues")
  )
  datos_contraste_numerico$variable <- factor(
    datos_contraste_numerico$variable,
    levels = variables_tratadas_numericas
  )

  grafico_boxplots_imputacion <- ggplot2::ggplot(
    datos_contraste_numerico,
    ggplot2::aes(x = momento, y = valor, fill = momento)
  ) +
    ggplot2::geom_boxplot(
      alpha = 0.72,
      width = 0.56,
      outlier.alpha = 0.20,
      outlier.size = 0.60,
      colour = "#5A5266",
      linewidth = 0.32
    ) +
    ggplot2::stat_summary(
      fun = mean,
      geom = "point",
      shape = 23,
      size = 2.4,
      fill = "white",
      colour = "#333333"
    ) +
    ggplot2::facet_wrap(~variable, scales = "free_y", ncol = 3) +
    ggplot2::scale_fill_manual(
      values = c(
        "Antes" = colores_pastel[["Antes"]],
        "Despues" = colores_pastel[["Despues"]]
      )
    ) +
    ggplot2::labs(
      title = "Variables numericas antes y despues de la limpieza",
      subtitle = paste0(
        "Cada panel utiliza una escala propia. El rombo blanco representa ",
        "la media."
      ),
      caption = paste0(
        "Variables tratadas: ",
        paste(variables_tratadas_numericas, collapse = ", ")
      ),
      x = NULL,
      y = "Valor observado",
      fill = NULL
    ) +
    tema_pastel +
    ggplot2::theme(
      strip.background = ggplot2::element_rect(
        fill = "#EDE7F2", colour = "#CDB4DB", linewidth = 0.35
      ),
      strip.text = ggplot2::element_text(face = "bold", colour = "#443A4C"),
      panel.grid.major.x = ggplot2::element_blank()
    )

  print(grafico_boxplots_imputacion)

  # Densidad solo para variables con al menos dos valores distintos por momento.
  validar_densidad <- function(variable) {
    subconjunto <- datos_contraste_numerico[
      datos_contraste_numerico$variable == variable,
      ,
      drop = FALSE
    ]
    antes <- unique(subconjunto$valor[subconjunto$momento == "Antes"])
    despues <- unique(subconjunto$valor[subconjunto$momento == "Despues"])
    length(antes) >= 2L && length(despues) >= 2L
  }

  variables_densidad <- variables_tratadas_numericas[
    vapply(variables_tratadas_numericas, validar_densidad, logical(1))
  ]

  if (length(variables_densidad) > 0L) {
    datos_densidad <- datos_contraste_numerico[
      datos_contraste_numerico$variable %in% variables_densidad,
      ,
      drop = FALSE
    ]

    grafico_densidades_imputacion <- ggplot2::ggplot(
      datos_densidad,
      ggplot2::aes(x = valor, fill = momento, colour = momento)
    ) +
      ggplot2::geom_density(alpha = 0.28, linewidth = 0.75, adjust = 1) +
      ggplot2::facet_wrap(~variable, scales = "free", ncol = 3) +
      ggplot2::scale_fill_manual(
        values = c(
          "Antes" = colores_pastel[["Antes"]],
          "Despues" = colores_pastel[["Despues"]]
        )
      ) +
      ggplot2::scale_colour_manual(
        values = c("Antes" = "#B56576", "Despues" = "#4F8A6A")
      ) +
      ggplot2::labs(
        title = "Distribuciones antes y despues de la limpieza",
        subtitle = paste0(
          "La superposicion permite revisar los cambios producidos por la ",
          "imputacion."
        ),
        x = "Valor observado",
        y = "Densidad",
        fill = NULL,
        colour = NULL
      ) +
      tema_pastel +
      ggplot2::theme(
        strip.background = ggplot2::element_rect(
          fill = "#EDE7F2", colour = "#CDB4DB", linewidth = 0.35
        ),
        strip.text = ggplot2::element_text(
          face = "bold", colour = "#443A4C"
        )
      )

    print(grafico_densidades_imputacion)
  }
}

# Contraste categorico, cuando existan variables categoricas imputadas.
if (length(variables_tratadas_categoricas) > 0L) {
  lista_categorica <- lapply(
    variables_tratadas_categoricas,
    function(variable) {
      antes <- as.character(vivienda[[variable]])
      despues <- as.character(vivienda_limpia[[variable]])

      tabla_antes <- as.data.frame(table(antes, useNA = "no"))
      names(tabla_antes) <- c("categoria", "frecuencia")
      tabla_antes$momento <- "Antes"
      tabla_antes$variable <- variable

      tabla_despues <- as.data.frame(table(despues, useNA = "no"))
      names(tabla_despues) <- c("categoria", "frecuencia")
      tabla_despues$momento <- "Despues"
      tabla_despues$variable <- variable

      combinado <- rbind(tabla_antes, tabla_despues)
      combinado$porcentaje <- ave(
        combinado$frecuencia,
        combinado$momento,
        FUN = function(x) 100 * x / sum(x)
      )

      totales <- aggregate(porcentaje ~ categoria, combinado, sum)
      principales <- head(
        totales$categoria[order(totales$porcentaje, decreasing = TRUE)],
        15
      )
      combinado[combinado$categoria %in% principales, , drop = FALSE]
    }
  )

  datos_contraste_categorico <- do.call(rbind, lista_categorica)
  datos_contraste_categorico$momento <- factor(
    datos_contraste_categorico$momento,
    levels = c("Antes", "Despues")
  )

  grafico_categoricas_imputacion <- ggplot2::ggplot(
    datos_contraste_categorico,
    ggplot2::aes(x = porcentaje, y = categoria, fill = momento)
  ) +
    ggplot2::geom_col(
      position = ggplot2::position_dodge(width = 0.72),
      width = 0.64,
      alpha = 0.86,
      colour = "#5A5266",
      linewidth = 0.20
    ) +
    ggplot2::facet_wrap(~variable, scales = "free_y", ncol = 1) +
    ggplot2::scale_fill_manual(
      values = c(
        "Antes" = colores_pastel[["Antes"]],
        "Despues" = colores_pastel[["Despues"]]
      )
    ) +
    ggplot2::labs(
      title = "Variables categoricas antes y despues de la limpieza",
      subtitle = "Se muestran como maximo las 15 categorias principales",
      x = "Participacion dentro de la variable (%)",
      y = NULL,
      fill = NULL
    ) +
    tema_pastel +
    ggplot2::theme(panel.grid.major.y = ggplot2::element_blank())

  print(grafico_categoricas_imputacion)
}

# ==============================================================================
# 9. VARIABLES RECOMENDADAS PARA LOS MODELOS
# ============================================================================== 

variables_acp <- intersect(
  c("estrato", "preciom", "areaconst", "banios", "habitaciones"),
  names(vivienda_limpia)
)

variables_conglomerados <- variables_acp

variables_correspondencia <- intersect(
  c("tipo", "zona", "barrio"),
  names(vivienda_limpia)
)

resumen_variables_modelos <- data.frame(
  modelo = c(
    rep("Analisis de Componentes Principales", length(variables_acp)),
    rep("Analisis de Conglomerados", length(variables_conglomerados)),
    rep("Analisis de Correspondencia", length(variables_correspondencia))
  ),
  variable = c(
    variables_acp,
    variables_conglomerados,
    variables_correspondencia
  ),
  enfoque = c(
    # ACP
    ifelse(
      variables_acp == "estrato",
      "Aporta el nivel socioeconomico del inmueble dentro de la estructura multivariada.",
      ifelse(
        variables_acp == "preciom",
        "Representa la dimension monetaria y ayuda a identificar componentes asociados con la variacion del precio.",
        ifelse(
          variables_acp == "areaconst",
          "Representa el tamano fisico y ayuda a identificar componentes de amplitud de la oferta.",
          ifelse(
            variables_acp == "banios",
            "Representa la dotacion funcional de la propiedad.",
            "Representa la capacidad habitacional de la propiedad."
          )
        )
      )
    ),
    # Conglomerados
    ifelse(
      variables_conglomerados == "estrato",
      "Permite diferenciar segmentos de oferta segun nivel socioeconomico.",
      ifelse(
        variables_conglomerados == "preciom",
        "Permite separar segmentos por nivel de precio.",
        ifelse(
          variables_conglomerados == "areaconst",
          "Permite agrupar propiedades segun tamano construido.",
          ifelse(
            variables_conglomerados == "banios",
            "Permite diferenciar segmentos por dotacion interna.",
            "Permite diferenciar segmentos por capacidad residencial."
          )
        )
      )
    ),
    # Correspondencia
    ifelse(
      variables_correspondencia == "tipo",
      "Permite examinar la distribucion de las clases de vivienda.",
      ifelse(
        variables_correspondencia == "zona",
        "Permite identificar asociaciones entre tipologia y sector de la ciudad.",
        "Permite reconocer patrones territoriales y especializacion de la oferta por barrio."
      )
    )
  ),
  stringsAsFactors = FALSE
)

resumen_variables_modelos

# write.csv(
#  resumen_variables_modelos,
#  file.path(carpeta_salida, "06_variables_recomendadas_modelos.csv"),
#  row.names = FALSE,
#  fileEncoding = "UTF-8"
# )

cat("\nVARIABLES RECOMENDADAS PARA ACP\n")
print(variables_acp)
cat(paste0(
  "Objetivo: reducir la dimensionalidad y visualizar la estructura de las ",
  "variables para identificar caracteristicas asociadas con la variacion de ",
  "precios y de la oferta.\n"
))

cat("\nVARIABLES RECOMENDADAS PARA CONGLOMERADOS\n")
print(variables_conglomerados)
cat(paste0(
  "Objetivo: agrupar propiedades en segmentos homogeneos para comprender ",
  "dinamicas de oferta por caracteristicas y estrato socioeconomico.\n"
))

cat("\nVARIABLES RECOMENDADAS PARA CORRESPONDENCIA\n")
print(variables_correspondencia)
cat(paste0(
  "Objetivo: examinar asociaciones entre tipo, zona y barrio para reconocer ",
  "patrones de comportamiento de la oferta inmobiliaria.\n"
))

# ==============================================================================
# 10. RESUMEN Y VERIFICACIONES FINALES
# ============================================================================== 

resumen_imputaciones <- as.data.frame(
  table(
    auditoria_imputacion$variable,
    auditoria_imputacion$origen,
    auditoria_imputacion$metodo
  ),
  stringsAsFactors = FALSE
)
names(resumen_imputaciones) <- c("variable", "origen", "metodo", "cantidad")
resumen_imputaciones <- resumen_imputaciones[
  resumen_imputaciones$cantidad > 0,
  ,
  drop = FALSE
]

write.csv(
  resumen_imputaciones,
  file.path(carpeta_salida, "07_resumen_imputaciones.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
)

capture.output(
  sessionInfo(),
  file = file.path(carpeta_salida, "sessionInfo_limpieza.txt")
)

cat("\n============================================================\n")
cat("PROCESO FINALIZADO\n")
cat("============================================================\n")
cat("vivienda permanecio intacta:",
    identical(vivienda, vivienda_original_control), "\n")
cat("Dimensiones de vivienda:", paste(dim(vivienda), collapse = " x "), "\n")
cat("Dimensiones de vivienda_limpia:",
    paste(dim(vivienda_limpia), collapse = " x "), "\n")
cat("Faltantes finales:", sum(is.na(vivienda_limpia)), "\n")
cat("piso excluida:", !("piso" %in% names(vivienda_limpia)), "\n")
cat("parqueaderos excluida:",
    !("parqueaderos" %in% names(vivienda_limpia)), "\n")
cat("Atipicos tratados:", nrow(auditoria_atipicos), "\n")
cat("Valores imputados:", nrow(auditoria_imputacion), "\n")
cat("Archivos de resultados en:", carpeta_salida, "\n")
cat("\nGraficos disponibles en el historial del panel Plots:\n")
cat("- grafico_nulos_original\n")
cat("- grafico_atipicos\n")
cat("- grafico_nulos_antes_despues\n")
if (exists("grafico_boxplots_imputacion")) {
  cat("- grafico_boxplots_imputacion\n")
}
if (exists("grafico_densidades_imputacion")) {
  cat("- grafico_densidades_imputacion\n")
}
if (exists("grafico_categoricas_imputacion")) {
  cat("- grafico_categoricas_imputacion\n")
}
# ==============================================================================
# MODULO DE ANALISIS HOLISTICO DEL MERCADO INMOBILIARIO URBANO
#
# UBICACION: agregar al final del script de limpieza, despues de crear y validar
# vivienda_limpia.
#
# Requisitos:
#   - vivienda_limpia debe existir.
#   - vivienda_limpia debe tener cero valores faltantes.
#   - Debe contener las variables utilizadas por cada modelo.
#
# Graficos: se muestran en el panel Plots. No se exportan a PNG.
# ============================================================================== 

# ==============================================================================
# 1. VALIDACIONES Y CONFIGURACION
# ============================================================================== 

if (!exists("vivienda_limpia", envir = .GlobalEnv, inherits = FALSE)) {
  stop("No existe vivienda_limpia. Ejecute primero la fase de limpieza.",
       call. = FALSE)
}
if (!is.data.frame(vivienda_limpia)) {
  stop("vivienda_limpia debe ser un data.frame.", call. = FALSE)
}
if (nrow(vivienda_limpia) == 0L) {
  stop("vivienda_limpia no contiene registros.", call. = FALSE)
}
if (sum(is.na(vivienda_limpia)) > 0L) {
  stop("vivienda_limpia contiene valores faltantes.", call. = FALSE)
}

if (!requireNamespace("ggplot2", quietly = TRUE)) {
  install.packages("ggplot2", dependencies = TRUE)
}
if (!requireNamespace("ggplot2", quietly = TRUE)) {
  stop("No fue posible instalar o cargar ggplot2.", call. = FALSE)
}

set.seed(2026)
options(scipen = 999, digits = 5)

if (!exists("carpeta_salida")) {
  carpeta_salida <- file.path(getwd(), "Resultados_Limpieza_Vivienda")
}
carpeta_modelos <- file.path(carpeta_salida, "Modelos_Estadisticos")
if (!dir.exists(carpeta_modelos)) {
  dir.create(carpeta_modelos, recursive = TRUE, showWarnings = FALSE)
}
if (!dir.exists(carpeta_modelos)) {
  stop("No fue posible crear la carpeta Modelos_Estadisticos.",
       call. = FALSE)
}

colores_pastel_modelos <- c(
  "#A9DEF9", "#FFCAD4", "#CDB4DB", "#B7E4C7",
  "#FFE5A5", "#BDE0FE", "#FFC8DD", "#CDEAC0"
)

tema_modelos <- ggplot2::theme_minimal(base_size = 11) +
  ggplot2::theme(
    plot.title = ggplot2::element_text(
      face = "bold", size = 14, colour = "#443A4C", hjust = 0.5
    ),
    plot.subtitle = ggplot2::element_text(
      size = 9.5, colour = "#6B596F", hjust = 0.5,
      margin = ggplot2::margin(b = 9)
    ),
    plot.caption = ggplot2::element_text(
      size = 8.5, colour = "#765B62", hjust = 0.5,
      margin = ggplot2::margin(t = 8)
    ),
    axis.text = ggplot2::element_text(colour = "#4D4044"),
    axis.title = ggplot2::element_text(colour = "#4D4044", face = "bold"),
    panel.grid.minor = ggplot2::element_blank(),
    legend.position = "bottom",
    legend.title = ggplot2::element_blank(),
    plot.margin = ggplot2::margin(12, 28, 12, 12)
  )

# ==============================================================================
# 2. ANALISIS DE COMPONENTES PRINCIPALES
# ==============================================================================
# Objetivo: reducir dimensionalidad y reconocer las variables asociadas con la
# variacion conjunta de precio, tamano, dotacion y capacidad residencial.
#
# Variables:
#   estrato, preciom, areaconst, banios y habitaciones.
# id se excluye por ser identificador. Longitud y latitud se reservan para mapa.
# ============================================================================== 

variables_acp <- intersect(
  c("estrato", "preciom", "areaconst", "banios", "habitaciones"),
  names(vivienda_limpia)
)
if (length(variables_acp) < 2L) {
  stop("No existen al menos dos variables disponibles para el ACP.",
       call. = FALSE)
}

datos_acp <- as.data.frame(
  lapply(
    vivienda_limpia[, variables_acp, drop = FALSE],
    function(x) suppressWarnings(as.numeric(as.character(x)))
  ),
  check.names = FALSE
)

if (sum(is.na(datos_acp)) > 0L) {
  stop("La conversion numerica del ACP produjo valores faltantes.",
       call. = FALSE)
}

desviaciones_acp <- vapply(datos_acp, stats::sd, numeric(1))
variables_constantes <- names(desviaciones_acp)[
  !is.finite(desviaciones_acp) | desviaciones_acp == 0
]
if (length(variables_constantes) > 0L) {
  datos_acp <- datos_acp[
    , !names(datos_acp) %in% variables_constantes, drop = FALSE
  ]
}
if (ncol(datos_acp) < 2L) {
  stop("El ACP quedo con menos de dos variables no constantes.",
       call. = FALSE)
}

modelo_acp <- stats::prcomp(
  datos_acp,
  center = TRUE,
  scale. = TRUE,
  retx = TRUE
)

autovalores_acp <- modelo_acp$sdev^2
porcentaje_varianza_acp <- 100 * autovalores_acp / sum(autovalores_acp)
varianza_acumulada_acp <- cumsum(porcentaje_varianza_acp)

n_componentes <- which(varianza_acumulada_acp >= 80)[1]
if (is.na(n_componentes)) n_componentes <- length(autovalores_acp)
n_componentes <- min(max(2L, n_componentes), ncol(modelo_acp$x))

tabla_varianza_acp <- data.frame(
  componente = paste0("CP", seq_along(autovalores_acp)),
  autovalor = autovalores_acp,
  varianza_porcentaje = porcentaje_varianza_acp,
  varianza_acumulada = varianza_acumulada_acp,
  retenido = seq_along(autovalores_acp) <= n_componentes,
  stringsAsFactors = FALSE
)

cargas_acp <- modelo_acp$rotation
contribuciones_acp <- sweep(
  cargas_acp^2, 2, colSums(cargas_acp^2), "/"
) * 100

componentes_dominantes <- do.call(
  rbind,
  lapply(seq_len(n_componentes), function(j) {
    orden <- order(abs(cargas_acp[, j]), decreasing = TRUE)
    seleccion <- head(orden, min(3L, length(orden)))
    data.frame(
      componente = paste0("CP", j),
      variable = rownames(cargas_acp)[seleccion],
      carga = cargas_acp[seleccion, j],
      contribucion_porcentaje = contribuciones_acp[seleccion, j],
      varianza_explicada = porcentaje_varianza_acp[j],
      stringsAsFactors = FALSE
    )
  })
)

tabla_varianza_acp

 write.csv(
  tabla_varianza_acp,
  file.path(carpeta_modelos, "01_varianza_acp.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

componentes_dominantes

 write.csv(
  componentes_dominantes,
  file.path(carpeta_modelos, "02_componentes_dominantes.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

# Grafico de varianza individual y acumulada.
grafico_varianza_acp <- ggplot2::ggplot(
  tabla_varianza_acp,
  ggplot2::aes(x = componente)
) +
  ggplot2::geom_col(
    ggplot2::aes(y = varianza_porcentaje, fill = retenido),
    width = 0.66, alpha = 0.88,
    colour = "#5A5266", linewidth = 0.25
  ) +
  ggplot2::geom_line(
    ggplot2::aes(y = varianza_acumulada, group = 1),
    colour = "#6D597A", linewidth = 0.85
  ) +
  ggplot2::geom_point(
    ggplot2::aes(y = varianza_acumulada),
    colour = "#6D597A", size = 2.1
  ) +
  ggplot2::geom_hline(
    yintercept = 80, colour = "#E76F51",
    linetype = "dashed", linewidth = 0.7
  ) +
  ggplot2::geom_text(
    ggplot2::aes(
      y = varianza_porcentaje,
      label = paste0(round(varianza_porcentaje, 1), " %")
    ),
    vjust = -0.35, size = 3.0, colour = "#4D4044"
  ) +
  ggplot2::scale_fill_manual(
    values = c(`TRUE` = "#B8C0FF", `FALSE` = "#D8E2DC")
  ) +
  ggplot2::scale_y_continuous(
    limits = c(0, 105), breaks = seq(0, 100, 20),
    expand = ggplot2::expansion(mult = c(0, 0.03))
  ) +
  ggplot2::labs(
    title = "Varianza explicada por los componentes principales",
    subtitle = paste0(
      "Se retienen ", n_componentes, " componentes con ",
      round(varianza_acumulada_acp[n_componentes], 2),
      " % de varianza acumulada"
    ),
    caption = "Barras: varianza individual | Linea: acumulada | Referencia: 80 %",
    x = "Componente principal",
    y = "Varianza explicada (%)",
    fill = NULL
  ) +
  tema_modelos +
  ggplot2::theme(legend.position = "none")

print(grafico_varianza_acp)

# Plano de propiedades en CP1 y CP2. La muestra se usa solo para visualizar.
set.seed(2026)
filas_muestra_acp <- sample(
  seq_len(nrow(modelo_acp$x)),
  min(2000L, nrow(modelo_acp$x)),
  replace = FALSE
)

datos_individuos_acp <- data.frame(
  CP1 = modelo_acp$x[filas_muestra_acp, 1],
  CP2 = modelo_acp$x[filas_muestra_acp, 2]
)

grafico_individuos_acp <- ggplot2::ggplot(
  datos_individuos_acp,
  ggplot2::aes(x = CP1, y = CP2)
) +
  ggplot2::geom_point(colour = "#023E8A", alpha = 0.8, size = 1.0) +
  ggplot2::geom_hline(
    yintercept = 0, linetype = "dashed", colour = "#777777"
  ) +
  ggplot2::geom_vline(
    xintercept = 0, linetype = "dashed", colour = "#777777"
  ) +
  ggplot2::labs(
    title = "Estructura de las viviendas en el plano del ACP",
    subtitle = paste0(
      "CP1 = ", round(porcentaje_varianza_acp[1], 1),
      " % | CP2 = ", round(porcentaje_varianza_acp[2], 1),
      " % | Muestra visual: ", nrow(datos_individuos_acp)
    ),
    caption = "Puntos proximos representan perfiles cuantitativos semejantes.",
    x = paste0("CP1 (", round(porcentaje_varianza_acp[1], 1), " %)"),
    y = paste0("CP2 (", round(porcentaje_varianza_acp[2], 1), " %)")
  ) +
  tema_modelos

print(grafico_individuos_acp)

# Cargas factoriales de CP1 y CP2.
datos_cargas <- rbind(
  data.frame(
    variable = rownames(cargas_acp), componente = "CP1",
    carga = cargas_acp[, 1], stringsAsFactors = FALSE
  ),
  data.frame(
    variable = rownames(cargas_acp), componente = "CP2",
    carga = cargas_acp[, 2], stringsAsFactors = FALSE
  )
)
datos_cargas$signo <- ifelse(
  datos_cargas$carga >= 0, "Positiva", "Negativa"
)

grafico_cargas_acp <- ggplot2::ggplot(
  datos_cargas,
  ggplot2::aes(x = carga, y = reorder(variable, carga), fill = signo)
) +
  ggplot2::geom_col(alpha = 0.85, width = 0.68) +
  ggplot2::geom_vline(xintercept = 0, colour = "#555555", linewidth = 0.4) +
  ggplot2::facet_wrap(~componente, ncol = 2) +
  ggplot2::scale_fill_manual(
    values = c("Positiva" = "#A9DEF9", "Negativa" = "#FFB4A2")
  ) +
  ggplot2::labs(
    title = "Cargas de las variables en CP1 y CP2",
    subtitle = "Las cargas de mayor valor absoluto caracterizan mas cada componente",
    x = "Carga factorial", y = NULL, fill = NULL
  ) +
  tema_modelos

print(grafico_cargas_acp)

cat("\nRESULTADOS DEL ACP\n")
cat("Variables:", paste(names(datos_acp), collapse = ", "), "\n")
cat("Componentes retenidos:", n_componentes, "\n")
cat("Varianza acumulada:",
    round(varianza_acumulada_acp[n_componentes], 2), "%\n")
print(componentes_dominantes)

# ==============================================================================
# 3. ANALISIS DE CONGLOMERADOS
# ==============================================================================
# Objetivo: agrupar propiedades en segmentos homogeneos.
# Se utilizan los componentes retenidos para reducir redundancia.
# El numero de grupos se selecciona con el indice Calinski-Harabasz.
# ============================================================================== 

puntuaciones_cluster <- modelo_acp$x[
  , seq_len(n_componentes), drop = FALSE
]

k_maximo <- min(8L, nrow(puntuaciones_cluster) - 1L)
if (k_maximo < 2L) {
  stop("No existen suficientes registros para conglomerados.", call. = FALSE)
}
k_candidatos <- 2:k_maximo

resultados_k <- data.frame(
  k = integer(0), suma_intra = numeric(0), suma_entre = numeric(0),
  proporcion_separada = numeric(0),
  indice_calinski_harabasz = numeric(0),
  stringsAsFactors = FALSE
)
modelos_k <- vector("list", length(k_candidatos))
names(modelos_k) <- as.character(k_candidatos)

for (k_actual in k_candidatos) {
  set.seed(2026 + k_actual)
  modelo_actual <- stats::kmeans(
    puntuaciones_cluster,
    centers = k_actual,
    nstart = 50,
    iter.max = 100
  )
  indice_ch <-
    (modelo_actual$betweenss / (k_actual - 1)) /
    (modelo_actual$tot.withinss /
       (nrow(puntuaciones_cluster) - k_actual))

  resultados_k <- rbind(
    resultados_k,
    data.frame(
      k = k_actual,
      suma_intra = modelo_actual$tot.withinss,
      suma_entre = modelo_actual$betweenss,
      proporcion_separada =
        100 * modelo_actual$betweenss / modelo_actual$totss,
      indice_calinski_harabasz = indice_ch,
      stringsAsFactors = FALSE
    )
  )
  modelos_k[[as.character(k_actual)]] <- modelo_actual
}

k_optimo <- resultados_k$k[
  which.max(resultados_k$indice_calinski_harabasz)
]
modelo_cluster <- modelos_k[[as.character(k_optimo)]]

vivienda_resultados <- vivienda_limpia
vivienda_resultados$conglomerado <- factor(
  modelo_cluster$cluster,
  levels = seq_len(k_optimo),
  labels = paste0("Conglomerado ", seq_len(k_optimo))
)

resultados_k

 write.csv(
  resultados_k,
  file.path(carpeta_modelos, "03_indicadores_conglomerados.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

grafico_seleccion_k <- ggplot2::ggplot(
  resultados_k,
  ggplot2::aes(x = k, y = indice_calinski_harabasz)
) +
  ggplot2::geom_line(colour = "#6D597A", linewidth = 0.85) +
  ggplot2::geom_point(
    colour = "#6D597A", fill = "#CDB4DB", shape = 21, size = 3.0
  ) +
  ggplot2::geom_vline(
    xintercept = k_optimo, colour = "#E76F51",
    linetype = "dashed", linewidth = 0.75
  ) +
  ggplot2::scale_x_continuous(breaks = k_candidatos) +
  ggplot2::labs(
    title = "Seleccion del numero de conglomerados",
    subtitle = paste0(
      "Mayor indice Calinski-Harabasz con k = ", k_optimo
    ),
    caption = "Valores mayores indican mejor separacion relativa y cohesion interna.",
    x = "Numero de conglomerados",
    y = "Indice Calinski-Harabasz"
  ) +
  tema_modelos

print(grafico_seleccion_k)

paleta_cluster <- grDevices::colorRampPalette(
  colores_pastel_modelos
)(k_optimo)

datos_cluster_grafico <- data.frame(
  CP1 = modelo_acp$x[, 1],
  CP2 = modelo_acp$x[, 2],
  conglomerado = vivienda_resultados$conglomerado
)

set.seed(2026)
filas_muestra_cluster <- sample(
  seq_len(nrow(datos_cluster_grafico)),
  min(3000L, nrow(datos_cluster_grafico)),
  replace = FALSE
)

grafico_conglomerados_acp <- ggplot2::ggplot(
  datos_cluster_grafico[filas_muestra_cluster, , drop = FALSE],
  ggplot2::aes(x = CP1, y = CP2, colour = conglomerado)
) +
  ggplot2::geom_point(alpha = 0.34, size = 1.0) +
  ggplot2::scale_colour_manual(values = paleta_cluster) +
  ggplot2::labs(
    title = "Segmentos inmobiliarios en el espacio del ACP",
    subtitle = paste0(
      k_optimo, " conglomerados | Muestra visual: ",
      length(filas_muestra_cluster), " propiedades"
    ),
    caption = "La clasificacion utiliza todos los componentes retenidos.",
    x = paste0("CP1 (", round(porcentaje_varianza_acp[1], 1), " %)"),
    y = paste0("CP2 (", round(porcentaje_varianza_acp[2], 1), " %)"),
    colour = NULL
  ) +
  tema_modelos

print(grafico_conglomerados_acp)

perfil_medias_cluster <- stats::aggregate(
  vivienda_resultados[, names(datos_acp), drop = FALSE],
  by = list(conglomerado = vivienda_resultados$conglomerado),
  FUN = mean
)
perfil_medianas_cluster <- stats::aggregate(
  vivienda_resultados[, names(datos_acp), drop = FALSE],
  by = list(conglomerado = vivienda_resultados$conglomerado),
  FUN = stats::median
)

tamano_conglomerados <- as.data.frame(
  table(vivienda_resultados$conglomerado),
  stringsAsFactors = FALSE
)
names(tamano_conglomerados) <- c("conglomerado", "numero_propiedades")
tamano_conglomerados$porcentaje <-
  100 * tamano_conglomerados$numero_propiedades /
  sum(tamano_conglomerados$numero_propiedades)

moda_segura <- function(x) {
  x <- x[!is.na(x)]
  if (length(x) == 0L) return(NA_character_)
  tabla <- table(x)
  names(tabla)[which.max(tabla)]
}

variables_perfil_categorico <- intersect(
  c("tipo", "zona", "barrio"), names(vivienda_resultados)
)
perfil_categorico_cluster <- do.call(
  rbind,
  lapply(levels(vivienda_resultados$conglomerado), function(grupo) {
    subconjunto <- vivienda_resultados[
      vivienda_resultados$conglomerado == grupo, , drop = FALSE
    ]
    fila <- data.frame(conglomerado = grupo, stringsAsFactors = FALSE)
    for (variable in variables_perfil_categorico) {
      fila[[variable]] <- moda_segura(subconjunto[[variable]])
    }
    fila
  })
)

tamano_conglomerados

 write.csv(
  tamano_conglomerados,
  file.path(carpeta_modelos, "04_tamano_conglomerados.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

perfil_medias_cluster

 write.csv(
  perfil_medias_cluster,
  file.path(carpeta_modelos, "05_perfil_medias_conglomerados.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

perfil_medianas_cluster

 write.csv(
  perfil_medianas_cluster,
  file.path(carpeta_modelos, "06_perfil_medianas_conglomerados.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

perfil_categorico_cluster

 write.csv(
  perfil_categorico_cluster,
  file.path(carpeta_modelos, "07_perfil_categorico_conglomerados.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

grafico_tamano_cluster <- ggplot2::ggplot(
  tamano_conglomerados,
  ggplot2::aes(x = conglomerado, y = numero_propiedades, fill = conglomerado)
) +
  ggplot2::geom_col(
    alpha = 0.88, colour = "#5A5266", linewidth = 0.25
  ) +
  ggplot2::geom_text(
    ggplot2::aes(
      label = paste0(numero_propiedades, " (", round(porcentaje, 1), " %)")
    ),
    vjust = -0.35, size = 3.3, colour = "#4D4044"
  ) +
  ggplot2::scale_fill_manual(values = paleta_cluster) +
  ggplot2::scale_y_continuous(
    expand = ggplot2::expansion(mult = c(0, 0.14))
  ) +
  ggplot2::labs(
    title = "Tamano y participacion de los segmentos inmobiliarios",
    subtitle = paste0("Solucion seleccionada: ", k_optimo, " conglomerados"),
    x = "Segmento", y = "Numero de propiedades", fill = NULL
  ) +
  tema_modelos +
  ggplot2::theme(
    axis.text.x = ggplot2::element_text(angle = 20, hjust = 1),
    legend.position = "none"
  )

print(grafico_tamano_cluster)

# Mapa relativo sin cartografia base.
if (all(c("longitud", "latitud") %in% names(vivienda_resultados))) {
  datos_mapa_cluster <- vivienda_resultados[
    is.finite(vivienda_resultados$longitud) &
      is.finite(vivienda_resultados$latitud),
    c("longitud", "latitud", "conglomerado"),
    drop = FALSE
  ]
  if (nrow(datos_mapa_cluster) > 0L) {
    grafico_mapa_cluster <- ggplot2::ggplot(
      datos_mapa_cluster,
      ggplot2::aes(x = longitud, y = latitud, colour = conglomerado)
    ) +
      ggplot2::geom_point(alpha = 0.42, size = 1.0) +
      ggplot2::scale_colour_manual(values = paleta_cluster) +
      ggplot2::coord_equal() +
      ggplot2::labs(
        title = "Distribucion geografica relativa de los segmentos",
        subtitle = "Ubicacion de las propiedades segun longitud y latitud",
        caption = "No incorpora cartografia base ni limites administrativos.",
        x = "Longitud", y = "Latitud", colour = NULL
      ) +
      tema_modelos
    print(grafico_mapa_cluster)
  }
}

proporcion_separada_cluster <-
  100 * modelo_cluster$betweenss / modelo_cluster$totss

cat("\nRESULTADOS DE CONGLOMERADOS\n")
cat("Variables de origen:", paste(names(datos_acp), collapse = ", "), "\n")
cat("Componentes utilizados:", n_componentes, "\n")
cat("Numero de conglomerados:", k_optimo, "\n")
cat("Variabilidad separada:",
    round(proporcion_separada_cluster, 2), "%\n")
print(tamano_conglomerados)
print(perfil_medias_cluster)
print(perfil_categorico_cluster)

# ==============================================================================
# 4. ANALISIS DE CORRESPONDENCIA
# ==============================================================================
# Objetivo: examinar asociaciones entre tipo, zona y barrio.
# Se utiliza correspondencia simple mediante valores singulares.
# ============================================================================== 

agrupar_categorias <- function(x, max_categorias = 15L) {
  x <- as.character(x)
  frecuencias <- sort(table(x, useNA = "no"), decreasing = TRUE)
  principales <- names(head(frecuencias, max_categorias))
  ifelse(x %in% principales, x, "Otros")
}

correspondencia_simple <- function(
    datos, variable_fila, variable_columna,
    max_filas = 15L, max_columnas = 15L) {

  if (!all(c(variable_fila, variable_columna) %in% names(datos))) {
    stop("Variables no disponibles para correspondencia.", call. = FALSE)
  }

  fila <- agrupar_categorias(datos[[variable_fila]], max_filas)
  columna <- agrupar_categorias(datos[[variable_columna]], max_columnas)
  tabla <- table(fila, columna)
  tabla <- tabla[rowSums(tabla) > 0, colSums(tabla) > 0, drop = FALSE]

  if (nrow(tabla) < 2L || ncol(tabla) < 2L) {
    stop(
      paste0(
        "La tabla ", variable_fila, " x ", variable_columna,
        " no tiene dimension suficiente."
      ),
      call. = FALSE
    )
  }

  prueba <- suppressWarnings(stats::chisq.test(tabla, correct = FALSE))
  n_total <- sum(tabla)
  v_cramer <- sqrt(
    as.numeric(prueba$statistic) /
      (n_total * min(nrow(tabla) - 1, ncol(tabla) - 1))
  )

  proporciones <- tabla / n_total
  masas_fila <- rowSums(proporciones)
  masas_columna <- colSums(proporciones)
  residuos <-
    diag(1 / sqrt(masas_fila)) %*%
    (proporciones - outer(masas_fila, masas_columna)) %*%
    diag(1 / sqrt(masas_columna))

  descomposicion <- svd(residuos)
  autovalores <- descomposicion$d^2
  porcentaje_inercia <- 100 * autovalores / sum(autovalores)

  numero_dimensiones <- min(
    2L, length(descomposicion$d),
    nrow(tabla) - 1L, ncol(tabla) - 1L
  )

  coordenadas_fila <-
    diag(1 / sqrt(masas_fila)) %*%
    descomposicion$u[, seq_len(numero_dimensiones), drop = FALSE] %*%
    diag(
      descomposicion$d[seq_len(numero_dimensiones)],
      nrow = numero_dimensiones
    )
  coordenadas_columna <-
    diag(1 / sqrt(masas_columna)) %*%
    descomposicion$v[, seq_len(numero_dimensiones), drop = FALSE] %*%
    diag(
      descomposicion$d[seq_len(numero_dimensiones)],
      nrow = numero_dimensiones
    )

  if (numero_dimensiones == 1L) {
    coordenadas_fila <- cbind(coordenadas_fila, 0)
    coordenadas_columna <- cbind(coordenadas_columna, 0)
    porcentaje_inercia <- c(porcentaje_inercia[1], 0)
  }

  coordenadas <- rbind(
    data.frame(
      categoria = rownames(tabla),
      dimension_1 = coordenadas_fila[, 1],
      dimension_2 = coordenadas_fila[, 2],
      tipo = variable_fila,
      stringsAsFactors = FALSE
    ),
    data.frame(
      categoria = colnames(tabla),
      dimension_1 = coordenadas_columna[, 1],
      dimension_2 = coordenadas_columna[, 2],
      tipo = variable_columna,
      stringsAsFactors = FALSE
    )
  )

  resumen <- data.frame(
    analisis = paste(variable_fila, "x", variable_columna),
    chi_cuadrado = as.numeric(prueba$statistic),
    grados_libertad = as.numeric(prueba$parameter),
    valor_p = as.numeric(prueba$p.value),
    v_cramer = v_cramer,
    inercia_dimension_1 = porcentaje_inercia[1],
    inercia_dimension_2 = porcentaje_inercia[2],
    stringsAsFactors = FALSE
  )

  list(
    tabla = tabla,
    resumen = resumen,
    coordenadas = coordenadas,
    porcentaje_inercia = porcentaje_inercia
  )
}

variables_correspondencia <- c("tipo", "zona", "barrio")
if (!all(variables_correspondencia %in% names(vivienda_limpia))) {
  stop("No estan disponibles tipo, zona y barrio para correspondencia.",
       call. = FALSE)
}

pares_correspondencia <- list(
  c("tipo", "zona"),
  c("tipo", "barrio"),
  c("zona", "barrio")
)

resultados_correspondencia <- list()
graficos_correspondencia <- list()
resumen_correspondencias <- data.frame()

for (par_actual in pares_correspondencia) {
  nombre <- paste(par_actual, collapse = "_")
  resultado <- correspondencia_simple(
    vivienda_limpia,
    variable_fila = par_actual[1],
    variable_columna = par_actual[2],
    max_filas = if (par_actual[1] == "barrio") 15L else 20L,
    max_columnas = if (par_actual[2] == "barrio") 15L else 20L
  )

  resultados_correspondencia[[nombre]] <- resultado
  resumen_correspondencias <- rbind(
    resumen_correspondencias, resultado$resumen
  )

  grafico_actual <- ggplot2::ggplot(
    resultado$coordenadas,
    ggplot2::aes(
      x = dimension_1, y = dimension_2,
      colour = tipo, shape = tipo
    )
  ) +
    ggplot2::geom_hline(
      yintercept = 0, linetype = "dashed",
      colour = "#999999", linewidth = 0.4
    ) +
    ggplot2::geom_vline(
      xintercept = 0, linetype = "dashed",
      colour = "#999999", linewidth = 0.4
    ) +
    ggplot2::geom_point(size = 2.8, alpha = 0.82) +
    ggplot2::geom_text(
      ggplot2::aes(label = categoria),
      size = 3.0, vjust = -0.7,
      check_overlap = TRUE, show.legend = FALSE
    ) +
    ggplot2::scale_colour_manual(values = c("#86BBD8", "#F4A7A1")) +
    ggplot2::labs(
      title = paste0(
        "Correspondencia: ", par_actual[1], " y ", par_actual[2]
      ),
      subtitle = paste0(
        "Chi-cuadrado = ", round(resultado$resumen$chi_cuadrado, 2),
        " | p = ", format.pval(resultado$resumen$valor_p, digits = 3),
        " | V de Cramer = ", round(resultado$resumen$v_cramer, 3)
      ),
      caption = paste0(
        "Categorias proximas presentan perfiles semejantes. ",
        "'Otros' agrupa categorias poco frecuentes."
      ),
      x = paste0(
        "Dimension 1 (", round(resultado$porcentaje_inercia[1], 1), " %)"
      ),
      y = paste0(
        "Dimension 2 (", round(resultado$porcentaje_inercia[2], 1), " %)"
      ),
      colour = NULL, shape = NULL
    ) +
    tema_modelos

  graficos_correspondencia[[nombre]] <- grafico_actual
  print(grafico_actual)
}

resumen_correspondencias

 write.csv(
  resumen_correspondencias,
  file.path(carpeta_modelos, "08_resumen_correspondencias.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

clasificar_cramer <- function(v) {
  if (!is.finite(v)) return("no disponible")
  if (v < 0.10) return("muy debil")
  if (v < 0.30) return("debil")
  if (v < 0.50) return("moderada")
  "alta"
}

cat("\nRESULTADOS DE CORRESPONDENCIA\n")
cat("Variables: tipo, zona y barrio\n")
print(resumen_correspondencias)
for (i in seq_len(nrow(resumen_correspondencias))) {
  fila <- resumen_correspondencias[i, ]
  conclusion <- if (fila$valor_p < 0.05) {
    "se rechaza la hipotesis de independencia"
  } else {
    "no se rechaza la hipotesis de independencia"
  }
  cat(
    "-", fila$analisis, ":", conclusion,
    "al 5 %. Intensidad", clasificar_cramer(fila$v_cramer),
    "(V =", round(fila$v_cramer, 3), ").\n"
  )
}

# ==============================================================================
# 5. BASE DE RESULTADOS E INTERPRETACION EJECUTIVA
# ============================================================================== 

componentes_retenidos <- as.data.frame(
  modelo_acp$x[, seq_len(n_componentes), drop = FALSE]
)
names(componentes_retenidos) <- paste0("CP", seq_len(n_componentes))

base_resultados_modelos <- cbind(
  vivienda_resultados,
  componentes_retenidos
)

 base_resultados_modelos

 write.csv(
  base_resultados_modelos,
  file.path(carpeta_modelos, "09_base_resultados_modelos.csv"),
  row.names = FALSE,
  fileEncoding = "UTF-8"
 )

base_resultados_modelos

 saveRDS(
  base_resultados_modelos,
  file.path(carpeta_modelos, "09_base_resultados_modelos.rds")
 )

archivo_interpretacion_modelos <- file.path(
  carpeta_modelos,
  "10_interpretacion_ejecutiva_modelos.txt"
)

texto_interpretacion <- c(
  "ANALISIS HOLISTICO DEL MERCADO INMOBILIARIO URBANO",
  "=================================================",
  "",
  "1. ANALISIS DE COMPONENTES PRINCIPALES",
  paste0("Variables: ", paste(names(datos_acp), collapse = ", "), "."),
  paste0(
    "Se retuvieron ", n_componentes, " componentes que explican ",
    round(varianza_acumulada_acp[n_componentes], 2),
    " % de la variabilidad cuantitativa."
  ),
  "Las cargas de mayor valor absoluto identifican las variables que mas caracterizan cada componente.",
  "El ACP describe asociaciones; no demuestra causalidad sobre el precio.",
  "",
  "2. ANALISIS DE CONGLOMERADOS",
  paste0(
    "Se seleccionaron ", k_optimo, " conglomerados. La particion separa ",
    round(proporcion_separada_cluster, 2),
    " % de la variabilidad total de los componentes retenidos."
  ),
  "Los perfiles numericos, categoricos y geograficos permiten caracterizar los segmentos.",
  "Los nombres comerciales de los segmentos deben validarse con conocimiento del negocio.",
  "",
  "3. ANALISIS DE CORRESPONDENCIA",
  "Se evaluaron tipo-zona, tipo-barrio y zona-barrio.",
  "El valor p evalua independencia y V de Cramer resume intensidad de asociacion.",
  "La proximidad en los mapas es descriptiva y no implica causalidad.",
  "",
  "4. APLICACION ESTRATEGICA",
  "El analisis permite resumir la oferta, segmentar propiedades y reconocer patrones territoriales y tipologicos.",
  "Los resultados pueden apoyar priorizacion de inversiones, comercializacion diferenciada y valoracion contextual.",
  "Las decisiones financieras deben complementarse con costos, rentabilidad, riesgo y temporalidad, datos no incluidos en esta base."
)
writeLines(texto_interpretacion, archivo_interpretacion_modelos,
           useBytes = TRUE)

# ==============================================================================
# 6. VERIFICACIONES FINALES
# ============================================================================== 

stopifnot(
  sum(is.na(vivienda_limpia)) == 0L,
  nrow(base_resultados_modelos) == nrow(vivienda_limpia),
  nrow(modelo_acp$x) == nrow(vivienda_limpia),
  length(modelo_cluster$cluster) == nrow(vivienda_limpia),
  all(c("tipo", "zona", "barrio") %in% names(vivienda_limpia))
)

cat("\n============================================================\n")
cat("ANALISIS HOLISTICO FINALIZADO\n")
cat("============================================================\n")
cat("Base utilizada: vivienda_limpia\n")
cat("Registros:", nrow(vivienda_limpia), "\n")
cat("Variables ACP:", paste(names(datos_acp), collapse = ", "), "\n")
cat("Componentes retenidos:", n_componentes, "\n")
cat("Varianza acumulada:",
    round(varianza_acumulada_acp[n_componentes], 2), "%\n")
cat("Conglomerados:", k_optimo, "\n")
cat("Variabilidad separada:",
    round(proporcion_separada_cluster, 2), "%\n")
cat("Correspondencias evaluadas:", nrow(resumen_correspondencias), "\n")
cat("Resultados:", carpeta_modelos, "\n")
cat("Interpretacion:", archivo_interpretacion_modelos, "\n")
cat("\nObjetos graficos disponibles en Plots:\n")
cat("- grafico_varianza_acp\n")
cat("- grafico_individuos_acp\n")
cat("- grafico_cargas_acp\n")
cat("- grafico_seleccion_k\n")
cat("- grafico_conglomerados_acp\n")
cat("- grafico_tamano_cluster\n")
if (exists("grafico_mapa_cluster")) cat("- grafico_mapa_cluster\n")
cat("- graficos_correspondencia[[\"tipo_zona\"]]\n")
cat("- graficos_correspondencia[[\"tipo_barrio\"]]\n")
cat("- graficos_correspondencia[[\"zona_barrio\"]]\n")

Anexo 2. Dicionario de Datos de Vivienda

Tabla 25. Dicionario de Datos de Vivienda.

Variables Tipo Valores nulos Mínimo Máximo Valores únicos
id numeric 3 1,00000 8.319,0000 8.320
zona character 3
6
piso character 2.638
13
estrato numeric 3 3,00000 6,0000 5
preciom numeric 2 58,00000 1.999,0000 540
areaconst numeric 3 30,00000 1.745,0000 653
parqueaderos numeric 1.605 1,00000 10,0000 11
banios numeric 3 0,00000 10,0000 12
habitaciones numeric 3 0,00000 10,0000 12
tipo character 3
3
barrio character 3
437
longitud numeric 3 -76,58915 -76,4630 2.929
latitud numeric 3 3,33300 3,4977 3.680
Fuente: Elaboración Propia.

Anexo 3. Tablas de apoyo resultados del ACP.

Tabla 26. Componentes Principales.

Componente Autovalor Varianza explicada (%) Varianza acumulada (%) Retenido
CP1 2.9332 58.66 58.66
CP2 1.1883 23.77 82.43
CP3 0.4454 8.91 91.34 No
CP4 0.2417 4.83 96.17 No
CP5 0.1915 3.83 100.00 No

Tabla 26. Componentes Principales y Variables segú su contribución.

Componente Variable Carga Contribución (%) Varianza explicada (%)
banios CP1 banios 0.52 26.93 58.66
preciom CP1 preciom 0.51 25.69 58.66
areaconst CP1 areaconst 0.49 24.41 58.66
estrato CP2 estrato -0.67 45.49 23.77
habitaciones CP2 habitaciones 0.65 42.75 23.77
preciom1 CP2 preciom -0.28 7.89 23.77

Anexo 4. Código de las Pruebas de Independencia y Tabla Síntesis de los resultados de la Prueba de independencia Chi-cuadrado y medida de asociación V de Cramer.

clasificar_cramer <- function(v) {
  if (!is.finite(v)) return("no disponible")
  if (v < 0.10) return("muy debil")
  if (v < 0.30) return("debil")
  if (v < 0.50) return("moderada")
  "alta"
}

cat("\nRESULTADOS DE CORRESPONDENCIA\n")
cat("Variables: tipo, zona y barrio\n")
print(resumen_correspondencias)
for (i in seq_len(nrow(resumen_correspondencias))) {
  fila <- resumen_correspondencias[i, ]
  conclusion <- if (fila$valor_p < 0.05) {
    "se rechaza la hipotesis de independencia"
  } else {
    "no se rechaza la hipotesis de independencia"
  }
  cat(
    "-", fila$analisis, ":", conclusion,
    "al 5 %. Intensidad", clasificar_cramer(fila$v_cramer),
    "(V =", round(fila$v_cramer, 3), ").\n"
  )
}

Tabla 27. Prueba de Independencia CHI-CUADRADO y V DE CRAMER.

Relación Decisión estadística V de Cramer Intensidad Interpretación
Tipo × Zona Se rechaza H₀ 0.288 Débil La tipología de vivienda presenta diferencias según la zona, aunque la asociación es limitada.
Tipo × Barrio Se rechaza H₀ 0.305 Moderada Los barrios presentan una relación más marcada con la composición tipológica de la oferta.
Zona × Barrio Se rechaza H₀ 0.506 Alta Existe una fuerte correspondencia territorial entre barrios y zonas.