Minería de Datos

Práctica 3. Clasificación y Regresión

Author

Dr. Jorge Párraga Álava

Facultad de Ciencias Informáticas

Introducción

En esta práctica, se trabajará con un conjunto de datos sobre ventas inmobiliarias en Chile, un país con una economía dinámica y en crecimiento en América Latina. El mercado inmobiliario chileno es conocido por su diversidad, influenciada por factores como la ubicación geográfica, el nivel socioeconómico, y la infraestructura urbana. Este conjunto de datos incluye 1500 propiedades situadas en diversas zonas de la capital chilena, Santiago, que se caracteriza por su expansión urbana y variedad en los precios de las propiedades según la cercanía al centro y las características de las zonas residenciales.

Las variables disponibles son:

  • metros_cuadrados: Tamaño de la propiedad en metros cuadrados.
  • num_habitaciones: Número de habitaciones.
  • num_banos: Número de baños.
  • antiguedad: Edad de la propiedad en años.
  • distancia_centro: Distancia al centro de la ciudad en kilómetros.
  • zona: Zona de la ciudad (Centro, Norte, Sur, Este, Oeste).
  • precio: Precio de la propiedad (variable objetivo para regresión).
  • condicion: Categoría de la propiedad basada en el precio (No Lujosa, Lujosa) (variable objetivo para clasificación).

Los vendedores indican que cada propiedad comienza con un precio base de 150.000USD. Aumenta con los metros cuadrados, número de habitaciones y baños. Disminuye con la antigüedad y la distancia al centro. Las zonas Centro, Norte y Oeste tienden a ser más caras. Se solicita que, a partir de los datos, se prediga el precio y la condición de una propiedad.

Configuración Inicial

# Bibliotecas -------------------------------------------------------------
if(!require(tidyverse)) install.packages("tidyverse") 
if(!require(e1071)) install.packages("e1071") 
if(!require(caret)) install.packages("caret")  
if(!require(rpart)) install.packages("rpart")  
if(!require(rpart.plot)) install.packages("rpart.plot") 

# Config -------------------------------------------------------------
# Para notación no numérica.
options(scipen=999) 
# Para reproducibilidad.
set.seed(2024) 

0. Dominio del problema

  1. ¿Cuál es el problema específico que estamos tratando de resolver?

    Estamos tratando de predecir el precio y la condición de una propiedad.

  2. ¿Es factible lograr nuestros objetivos con los datos disponibles? ¿Cómo podemos alcanzarlos?

    Sí, es factible. Contamos con un conjunto de datos histórico que incluye variables clave que afectan el precio de las propiedades, como el tamaño, la ubicación, la antigüedad y la cercanía al centro de la ciudad. Podemos alcanzar nuestros objetivos utilizando técnicas de análisis de regresión para predecir el precio y de clasificación para determinar la condición (lujosa/no lujosa) de la propiedad.

  3. ¿Cuáles son los beneficios si nuestra solución funciona y cuáles son las consecuencias si falla?

    • Si nuestra solución funciona:

      • Para compradores y vendedores: Ofrecemos una estimación precisa del valor de las propiedades, lo que facilita la toma de decisiones informadas en el mercado inmobiliario.

      • Para agentes inmobiliarios: Proporcionamos una herramienta valiosa para la valoración de propiedades, optimizando estrategias de precios y marketing.

    • Si la solución falla:

      • Para compradores y vendedores: Pueden tomar decisiones basadas en estimaciones incorrectas, lo que podría llevar a pérdidas financieras o dificultades para vender una propiedad.

      • Para agentes inmobiliarios: La falta de precisión en la valoración podría afectar la competitividad y reputación en el mercado.

  4. ¿Qué tipo de problema se va a resolver: Predictivo/Descriptivo?

    Estamos abordando principalmente un problema predictivo, ya que queremos predecir el precio y la condición de una propiedad en función de datos históricos y variables predictoras.

  5. ¿El objetivo es predecir, segmentar, …?

    Nuestro objetivo principal es predecir (regresión y clasificación)

  6. ¿De dónde vendrán los datos, cuánto cuesta conseguirlos?

    Los datos provienen de la base de datos interna de empresas dedicadas a ventas inmobiliaria.

1. Selección

En esta etapa, se realiza la selección del conjunto de datos. Como este es proporcionado por la empresa, únicamente lo cargamos.

 # Cargar datos 
inmobiliaria_chile <- read.csv("inmobiliaria_chile.csv")
# mostrar un vista previa de datos
glimpse(inmobiliaria_chile)
Rows: 1,500
Columns: 8
$ metros_cuadrados <dbl> 46.78991, 166.85823, 133.28757, 95.08148, 199.62663, …
$ num_habitaciones <int> 2, 4, 3, 3, 2, 1, 3, 1, 1, 3, 2, 3, 4, 3, 1, 3, 3, 3,…
$ num_banos        <int> 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2,…
$ antiguedad       <int> 21, 16, 12, 4, 5, 9, 12, 14, 16, 4, 2, 9, 9, 6, 4, 7,…
$ distancia_centro <dbl> 13.16033928, 11.10143025, 0.01171242, 1.10572401, 13.…
$ zona             <chr> "Centro", "Oeste", "Norte", "Oeste", "Norte", "Este",…
$ precio           <dbl> 409942.5, 780050.9, 683005.9, 539468.0, 706804.1, 823…
$ condicion        <chr> "No Lujosa", "Lujosa", "Lujosa", "No Lujosa", "Lujosa…

Se observa que el conjunto de datos tiene 1500 instancias y 8 variables. Se muestran ejemplos de los datos que contiene cada columna así como su tipo de variable.

2. Limpieza

Para este conjunto de datos no es necesario realizar procesos de limpieza toda vez que la empresa con su equipo de Ciencia de Datos ya ha realizado todas estas actividades. El único proceso que realizaremos será el EDA.

2.3 Análisis Exploratorio de Datos (EDA)

El EDA nos ayudará a comprender mejor la naturaleza de nuestros datos. Analizaremos cada variable considerando si es cuantitativa o cualitativa.

Variable: metros_cuadrados (Cuantitativa)

Para la variable metros_cuadrados realizamos estadística descriptiva.

# Resumen estadístico
metros_cuadrados_resumen <- inmobiliaria_chile |>
  summarise(
    conteo = n(),
    media = mean(metros_cuadrados, na.rm = TRUE),
    mediana = median(metros_cuadrados, na.rm = TRUE),
    desviacion_estandar = sd(metros_cuadrados, na.rm = TRUE),
    min = min(metros_cuadrados, na.rm = TRUE),
    max = max(metros_cuadrados, na.rm = TRUE),
    q1 = quantile(metros_cuadrados, 0.25, na.rm = TRUE),
    q3 = quantile(metros_cuadrados, 0.75, na.rm = TRUE)
  )

print(metros_cuadrados_resumen)
  conteo    media mediana desviacion_estandar      min      max       q1
1   1500 116.2913 116.122            50.73508 30.08886 199.9665 70.92984
        q3
1 160.3972

También, visualizamos la distribución de metros cuadrados de las propiedades.

# Histograma de metros_cuadrados
ggplot(inmobiliaria_chile, aes(x =metros_cuadrados)) +
  geom_histogram(bins = 30, fill = "lightcoral", color = "black") +
  labs(title = "Histograma de variable metros_cuadrados",
       x = "Metros cuadrados", y = "Frecuencia")

La distribución de los metros cuadrados sugiere que el mercado inmobiliario en este conjunto de datos está compuesto por una variedad de propiedades, desde pequeñas hasta grandes. La mayoría de las propiedades tienen un tamaño moderado, pero existe una diversidad significativa en los tamaños comenzando en los 30 metros cuadrados hasta los casi 200 metros cuadrados.

Variable: num_habitaciones (Cualitativa)

Para la variable num_habitaciones analizamos la cantidad de registros.

# Frecuencia de ventas por producto
inmobiliaria_chile |> count(num_habitaciones) 
  num_habitaciones   n
1                1 133
2                2 464
3                3 589
4                4 232
5                5  82

Observamos que hay propiedades de hasta 5 habitaciones. Ahora veamos los precios según el número de habitaciones.

# Visualización de precio por número de habitaciones 
ggplot(inmobiliaria_chile, aes(x = factor(num_habitaciones), y = precio)) +
  geom_boxplot(fill = "lightgreen") +
  labs(title = "Precios según número de habitaciones",
       x = "Número de habitaciones", y = "Precio (USD)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Con el análisis podemos concluir que las propiedades tienen entre 1 y 5 habitaciones. Sin embargo, la mayoria de los registros (589) corresponden a propiedades de 3 habitaciones. Únicamente existen 82 propiedades con 5 habitaciones. Como es de suponer, el precio de la propiedad parece incrementar a medida que crece el número de habitaciones.

Variable: num_banos (Cualitativa)

Para la variable num_banos analizamos la cantidad de registros.

# Frecuencia de baños en propiedades
inmobiliaria_chile |> count(num_banos) 
  num_banos   n
1         1 904
2         2 455
3         3 141

Observamos que en total hay propiedades de hasta 3 baños. Ahora veamos los precios según el número de baños.

# Visualización de precio por número de baños 
ggplot(inmobiliaria_chile, aes(x = factor(num_banos), y = precio)) +
  geom_boxplot(fill = "lightgreen") +
  labs(title = "Precio según el número de baños",
       x = "Número de baños", y = "Precio (USD)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Con el análisis podemos concluir que las propiedades tienen entre 1 y 3 baños. Sin embargo, la mayoria de los registros (904) corresponden a propiedades con 1 baño. Únicamente existen 141 propiedades con 3 baños. Como es de suponer, el precio de la propiedad parece incrementar a medida que crece el número de baños.

Variable: antiguedad (Cuantitativa)

Para la variable antiguedad realizamos estadística descriptiva.

# Resumen estadístico
antiguedad_resumen <- inmobiliaria_chile |>
  summarise(
    conteo = n(),
    media = mean(antiguedad, na.rm = TRUE),
    mediana = median(antiguedad, na.rm = TRUE),
    desviacion_estandar = sd(antiguedad, na.rm = TRUE),
    min = min(antiguedad, na.rm = TRUE),
    max = max(antiguedad, na.rm = TRUE),
    q1 = quantile(antiguedad, 0.25, na.rm = TRUE),
    q3 = quantile(antiguedad, 0.75, na.rm = TRUE)
  )

print(antiguedad_resumen)
  conteo    media mediana desviacion_estandar min max q1 q3
1   1500 10.30133      10            5.052876   0  27  7 14

También, visualizamos la distribución de antigüedad de propiedades.

# Histograma de antiguedad
ggplot(inmobiliaria_chile, aes(x =antiguedad)) +
  geom_histogram(bins = 30, fill = "lightcoral", color = "black") +
  labs(title = "Histograma de variable antiguedad",
       x = "Antigüedad", y = "Frecuencia")

La distribución de los años de antiguedad sugiere que el mercado inmobiliario en este conjunto de datos está compuesto por propiedades con un promedio de 10 años de antiguedad. La propiedad más vieja tiene 27 años. Y la más reciente tiene menos de 1 año.

Variable: distancia_centro (Cuantitativa)

Para la variable distancia_centro realizamos estadística descriptiva.

# Resumen estadístico
distancia_centro_resumen <- inmobiliaria_chile |>
  summarise(
    conteo = n(),
    media = mean(distancia_centro, na.rm = TRUE),
    mediana = median(distancia_centro, na.rm = TRUE),
    desviacion_estandar = sd(distancia_centro, na.rm = TRUE),
    min = min(distancia_centro, na.rm = TRUE),
    max = max(distancia_centro, na.rm = TRUE),
    q1 = quantile(distancia_centro, 0.25, na.rm = TRUE),
    q3 = quantile(distancia_centro, 0.75, na.rm = TRUE)
  )

print(distancia_centro_resumen)
  conteo    media  mediana desviacion_estandar         min      max     q1
1   1500 7.430329 7.202922            4.279007 0.006919771 14.97225 3.8702
        q3
1 11.16475

También, visualizamos la distribución de distancia al centro de las propiedades.

# Histograma de distancia_centro
ggplot(inmobiliaria_chile, aes(x =distancia_centro)) +
  geom_histogram(bins = 30, fill = "lightcoral", color = "black") +
  labs(title = "Histograma de variable distancia_centro",
       x = "Distancia (km) al centro de Santiago", y = "Frecuencia")

La distribución de la distancia al centro de la ciudad sugiere que el mercado inmobiliario en este conjunto de datos está compuesto por propiedades a una distancia de entre 0 y 15 kms. En promedio las propiedades se encuentran a 7.43 kms del centro.

Variable: zona (Cualitativa)

Para la variable zona analizamos la cantidad de registros para cada zona.

# Frecuencia de zonas
inmobiliaria_chile |> count(zona) 
    zona   n
1 Centro 306
2   Este 315
3  Norte 297
4  Oeste 284
5    Sur 298

Ahora analizamos la distribución de precio por zonas mediante un diagrama de cajas.

# Visualización de precio por zonas
ggplot(inmobiliaria_chile, aes(x = zona, y = precio)) +
  geom_boxplot(fill = "lightgreen") +
  labs(title = "Distribución de precio por condición de propiedad",
       x = "Zona", y = "Precio (USD)") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Finalmente podemos darnos cuenta que la cantidad de registros por zonas de Santiago parecen muy similares. La zona Centro, Norte y Oeste agrupan las viviendos con mayor precio.

Variable: precio (Cuantitativa)

Para la variable precio, que es la variable a predecir, realizamos estadística descriptiva.

# Resumen estadístico
precio_resumen <- inmobiliaria_chile |>
  summarise(
    conteo = n(),
    media = mean(precio, na.rm = TRUE),
    mediana = median(precio, na.rm = TRUE),
    desviacion_estandar = sd(precio, na.rm = TRUE),
    min = min(precio, na.rm = TRUE),
    max = max(precio, na.rm = TRUE),
    q1 = quantile(precio, 0.25, na.rm = TRUE),
    q3 = quantile(precio, 0.75, na.rm = TRUE)
  )

print(precio_resumen)
  conteo    media  mediana desviacion_estandar      min     max       q1     q3
1   1500 574189.7 575251.8            175863.4 114855.5 1031868 435129.1 711937

También, visualizamos la distribución de precios de propiedades.

# Histograma de ventas
ggplot(inmobiliaria_chile, aes(x = precio)) +
  geom_histogram(bins = 30, fill = "lightseagreen", color = "black") +
  labs(title = "Histograma de variable ventas",
       x = "Ventas (USD)", y = "Frecuencia")

Finalmente, mostramos el diagrama de cajas de la variable.

# Boxplot de ventas
ggplot(inmobiliaria_chile, aes(y = precio)) +
  geom_boxplot(fill = "lightyellow") +
  labs(title = "Boxplot de precios",
       y = "Precio (USD)")

Del análisis se puede concluir que las ventas parecen tener una forma aproximadamente simétrica, con una mayor concentración de valores alrededor de la media de 574189.70 USD. La mayor parte de las ventas se encuentra entre los 400,000 y 700,000 USD, como lo muestra el histograma con las barras más altas en ese rango. Este es el rango donde la mayoría de las ventas ocurren, lo que sugiere que las propiedades dentro de este rango de precios son las más comunes o deseadas.

Variable: condicion (Cualitativa)

Para la variable condicion, que es la variable a clasificar, mostramos la cantidad de registros para cada condición.

# Calcular la frecuencia de cada categoría en la variable `condicion`
condicion_freq <- inmobiliaria_chile |>
  count(condicion) |>
  rename(Frecuencia = n)

# Mostrar la tabla de frecuencias
print(condicion_freq)
  condicion Frecuencia
1    Lujosa        692
2 No Lujosa        808

Ahora analizamos la distribución de distribucion mediante un diagrama de cajas.

# Crear un gráfico de barras para mostrar la distribución de `condicion`
ggplot(inmobiliaria_chile, aes(x = condicion)) +
  geom_bar(fill = "lightseagreen") +
  labs(title = "Distribución variable condicion",
       x = "Condición de la propiedad", y = "Frecuencia") +
  theme_minimal()

A partir del análisis observamos que cada clase presenta las siguientes situaciones:

  • Lujosa: Con 692 registros, la clase Lujosa es la menos frecuente, representando la clase minoritaria en el conjunto de datos.

  • No lujosa: Con 808 registros corresponden a propiedades en la categoría No Lujosa. Esto sugiere que esta clase está mayoritariamente representada en el conjunto de datos.

Correlación entre variables y precio

Ahora verificamos si existe correlación entre las variables independientes cuantitativas y la variable precio.

 # Calcular la matriz de correlación excluyendo la variable categórica
correlaciones <- inmobiliaria_chile |>
  select(-c(zona,condicion)) |>  # Excluir las variable textuales
  cor()
 
print(correlaciones)
                 metros_cuadrados num_habitaciones    num_banos    antiguedad
metros_cuadrados       1.00000000      -0.01000659 0.0296860419  0.0318323801
num_habitaciones      -0.01000659       1.00000000 0.0183523337 -0.0235523045
num_banos              0.02968604       0.01835233 1.0000000000  0.0001829542
antiguedad             0.03183238      -0.02355230 0.0001829542  1.0000000000
distancia_centro      -0.06740082       0.02027301 0.0102157038  0.0115061560
precio                 0.88589340       0.15138564 0.1015090458  0.0074642040
                 distancia_centro       precio
metros_cuadrados      -0.06740082  0.885893399
num_habitaciones       0.02027301  0.151385639
num_banos              0.01021570  0.101509046
antiguedad             0.01150616  0.007464204
distancia_centro       1.00000000 -0.289938727
precio                -0.28993873  1.000000000

La matriz de correlación entre las variables evidencia lo siguiente:

  • Metros cuadrados con el precio (0.886): Existe una fuerte correlación positiva entre el tamaño de la propiedad en metros cuadrados y el precio. Esto sugiere que las propiedades más grandes tienden a ser más caras.

  • Número de habitaciones con el precio (0.151): La correlación positiva pero débil indica que las propiedades con más habitaciones tienden a ser un poco más caras, pero el impacto no es tan fuerte como el tamaño en metros cuadrados.

  • Número de baños con el precio (0.102): La correlación positiva entre el número de baños y el precio también es débil. Aunque las propiedades con más baños suelen ser más caras, esta relación no es tan significativa.

  • Antigüedad con el precio (0.007): La correlación entre la antigüedad de la propiedad y su precio es prácticamente nula. Esto sugiere que la antigüedad no es un factor determinante en el precio de las propiedades en este conjunto de datos.

  • Distancia al centro con el precio (-0.290): Hay una correlación negativa moderada entre la distancia al centro y el precio. Esto indica que las propiedades más alejadas del centro tienden a ser más baratas, lo que es consistente con la expectativa de que la proximidad al centro suele aumentar el valor de la propiedad.

  • Correlaciones entre variables independientes: Las correlaciones entre las demás variables (metros cuadrados, número de habitaciones, número de baños, antigüedad y distancia al centro) son en su mayoría bajas, lo que sugiere que estas características no están fuertemente relacionadas entre sí en este conjunto de datos.

En resumen, la variable que más influye en el precio de las propiedades es el tamaño en metros cuadrados, seguido por la distancia al centro. El número de habitaciones y baños también tienen una relación positiva con el precio, aunque menos significativa, mientras que la antigüedad parece tener un impacto insignificante.

Correlación entre variables y condicion

Ahora verificamos si existe correlación entre las variables independientes cuantitativas y la variable condicion.

# convierte la variable objetivo a numero
df <- inmobiliaria_chile |>
  mutate(
    condicion = as.numeric(factor(condicion)),
    zona = as.numeric(factor(zona))
  )

 # Calcular la correlación de cada variable numérica con la variable objetivo
cor_objetivo <- df |>
  select(-c(condicion, precio)) |>  # Excluir la variable categórica y la variable objetivo
  cor(df$condicion) |>  # Calcular la correlación con la variable objetivo
  as.data.frame() |>
  rownames_to_column(var = "Variable") |>
  rename(cor_objetivo = 2)

# Mostrar las correlaciones
cor_objetivo
          Variable cor_objetivo
1 metros_cuadrados  -0.79177213
2 num_habitaciones  -0.09834388
3        num_banos  -0.07476733
4       antiguedad  -0.01892413
5 distancia_centro   0.20416491
6             zona   0.05785389

El resultado muestra las correlaciones entre cada variable numérica y la variable condicion

  • Metros cuadrados con la condición (-0.79) La fuerte correlación negativa sugiere que los inmuebles con más metros cuadrados están asociados con la categoría “No Lujoso” en lugar de “Lujoso”. Esto podría indicar que los inmuebles más grandes en el conjunto de datos tienden a ser menos costosos o menos valorados como lujosos, lo que puede ser inusual o indicar un patrón específico en el mercado o en los datos.

  • Número de habitaciones con la condición (-0.09) La correlación muy baja y negativa sugiere que el número de habitaciones tiene una relación débil con la clasificación de “Lujoso” versus “No Lujoso”. En otras palabras, el número de habitaciones no parece ser un predictor fuerte de si un inmueble es considerado lujoso o no.

  • Número de baños con la condición (-0.07) La baja correlación negativa indica que el número de baños también tiene una relación débil con la variable condicion. Esto sugiere que la cantidad de baños no es un factor significativo para determinar si un inmueble es lujoso o no.

  • Antigüedad con la condición (-0.01) La correlación casi nula indica que la antigüedad del inmueble tiene una relación muy débil con la clasificación de lujoso. La antigüedad del inmueble no parece influir en si se clasifica como lujoso o no en el conjunto de datos.

  • Distancia al centro con la condición (0.20) La correlación positiva baja sugiere que una mayor distancia al centro está ligeramente asociada con la clasificación de “Lujoso”. Esto puede indicar que los inmuebles más alejados del centro tienden a ser considerados lujosos, aunque la relación es débil.

  • Zona con la condición (0.05) La correlación muy baja entre zona y condicion indica que la zona en la que se encuentra el inmueble tiene una relación mínima con su clasificación de lujoso. Esto sugiere que la zona no es un factor importante para determinar si un inmueble es lujoso.

En resumen, la mayoría de las variables numéricas no tienen una relación fuerte con la variable categórica binaria condicion, excepto por metros_cuadrados y distancia_centro, que muestran algunas correlaciones. Esto puede indicar que otros factores no incluidos en el análisis podrían estar influyendo en la clasificación de lujoso versus no lujoso

3. Transformación

Para esta práctica únicamente realizaremos la numerización de las variables cualitativas.

Numerización de atributos categóricos

Convertiremos algunas variables categóricas en numéricas para poder realizar el proceso predictivo.

# # Convertiremos algunas variables categóricas en numéricas:
inmobiliaria_chile_transformado <- inmobiliaria_chile |>
  mutate(
    zona_numerica = as.numeric(factor(zona))
  )

# Verificamos el resultado
glimpse(inmobiliaria_chile_transformado)
Rows: 1,500
Columns: 9
$ metros_cuadrados <dbl> 46.78991, 166.85823, 133.28757, 95.08148, 199.62663, …
$ num_habitaciones <int> 2, 4, 3, 3, 2, 1, 3, 1, 1, 3, 2, 3, 4, 3, 1, 3, 3, 3,…
$ num_banos        <int> 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2,…
$ antiguedad       <int> 21, 16, 12, 4, 5, 9, 12, 14, 16, 4, 2, 9, 9, 6, 4, 7,…
$ distancia_centro <dbl> 13.16033928, 11.10143025, 0.01171242, 1.10572401, 13.…
$ zona             <chr> "Centro", "Oeste", "Norte", "Oeste", "Norte", "Este",…
$ precio           <dbl> 409942.5, 780050.9, 683005.9, 539468.0, 706804.1, 823…
$ condicion        <chr> "No Lujosa", "Lujosa", "Lujosa", "No Lujosa", "Lujosa…
$ zona_numerica    <dbl> 1, 4, 3, 4, 3, 2, 1, 3, 2, 5, 1, 5, 1, 3, 4, 1, 5, 5,…

Normalización de atributos numéricos

Finalmente, normalizamos todas las variables independientes.

# Normalización de atributos numéricos
 inmobiliaria_chile_transformado <- inmobiliaria_chile_transformado |>
 mutate(
   metros_normalizadas = as.vector(scale(metros_cuadrados)),
   habitaciones_normalizadas = as.vector(scale(num_habitaciones)),
   banos_normalizadas = as.vector(scale(num_banos)),
   antiguedad_normalizadas = as.vector(scale(antiguedad)),
   distancia_normalizadas = as.vector(scale(distancia_centro)),
   zona_normalizadas = as.vector(scale(zona_numerica))
 )

# Verificamos el resultado
glimpse(inmobiliaria_chile_transformado)
Rows: 1,500
Columns: 15
$ metros_cuadrados          <dbl> 46.78991, 166.85823, 133.28757, 95.08148, 19…
$ num_habitaciones          <int> 2, 4, 3, 3, 2, 1, 3, 1, 1, 3, 2, 3, 4, 3, 1,…
$ num_banos                 <int> 3, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1,…
$ antiguedad                <int> 21, 16, 12, 4, 5, 9, 12, 14, 16, 4, 2, 9, 9,…
$ distancia_centro          <dbl> 13.16033928, 11.10143025, 0.01171242, 1.1057…
$ zona                      <chr> "Centro", "Oeste", "Norte", "Oeste", "Norte"…
$ precio                    <dbl> 409942.5, 780050.9, 683005.9, 539468.0, 7068…
$ condicion                 <chr> "No Lujosa", "Lujosa", "Lujosa", "No Lujosa"…
$ zona_numerica             <dbl> 1, 4, 3, 4, 3, 2, 1, 3, 2, 5, 1, 5, 1, 3, 4,…
$ metros_normalizadas       <dbl> -1.36988821, 0.99668577, 0.33500041, -0.4180…
$ habitaciones_normalizadas <dbl> -0.7818768, 1.2298131, 0.2239681, 0.2239681,…
$ banos_normalizadas        <dbl> 2.2790228, 0.7684023, -0.7422182, -0.7422182…
$ antiguedad_normalizadas   <dbl> 2.1173422, 1.1278066, 0.3361782, -1.2470786,…
$ distancia_normalizadas    <dbl> 1.33909820, 0.85793306, -1.73372377, -1.4780…
$ zona_normalizadas         <dbl> -1.38846660, 0.72738159, 0.02209886, 0.72738…

4 y 5. Modelado y validación

Para el modelado consideramos el conjunto de datos de calidad:

# Usar el conjunto de datos de calidad
 inmobiliaria_final <- inmobiliaria_chile_transformado |> 
  select(metros_normalizadas,
   habitaciones_normalizadas,
   banos_normalizadas,
   antiguedad_normalizadas,
   distancia_normalizadas,
   zona_normalizadas,
   precio,
   condicion
   )

# Verificamos el resultado
glimpse(inmobiliaria_final)
Rows: 1,500
Columns: 8
$ metros_normalizadas       <dbl> -1.36988821, 0.99668577, 0.33500041, -0.4180…
$ habitaciones_normalizadas <dbl> -0.7818768, 1.2298131, 0.2239681, 0.2239681,…
$ banos_normalizadas        <dbl> 2.2790228, 0.7684023, -0.7422182, -0.7422182…
$ antiguedad_normalizadas   <dbl> 2.1173422, 1.1278066, 0.3361782, -1.2470786,…
$ distancia_normalizadas    <dbl> 1.33909820, 0.85793306, -1.73372377, -1.4780…
$ zona_normalizadas         <dbl> -1.38846660, 0.72738159, 0.02209886, 0.72738…
$ precio                    <dbl> 409942.5, 780050.9, 683005.9, 539468.0, 7068…
$ condicion                 <chr> "No Lujosa", "Lujosa", "Lujosa", "No Lujosa"…

Clasificación con Naive Bayes

Usaremos el método de Naive Bayes para clasificar la variable condicion, esto es, la categoría de la propiedad, Lujosa, No Lujosa. Primero dividimos el dataset con 70% para entrenamiento y 30% para prueba.

# Dividir los datos en conjunto de entrenamiento y prueba
indices_entrenamiento <- createDataPartition(inmobiliaria_final$condicion, p = 0.70, list = FALSE)
datos_entrenamiento <- inmobiliaria_final[indices_entrenamiento, ]
datos_prueba <- inmobiliaria_final[-indices_entrenamiento, ]

Luego se entreta y prueba el algoritmo de Naive Bayes:

# Entrenar el modelo Bayesiano Ingenuo
modelo_nb <- naiveBayes(condicion ~ 
                          metros_normalizadas + 
                          habitaciones_normalizadas + 
                          banos_normalizadas + 
                          antiguedad_normalizadas + 
                          distancia_normalizadas + 
                          zona_normalizadas,
                        data = datos_entrenamiento)

# Realizar predicciones en el conjunto de prueba
predicciones <- predict(modelo_nb, datos_prueba)

Finalmente, evalúo el rendimiento del algoritmo con métricas de calidad:

# Calcular la matriz de confusión
matriz_confusion <- table(Prediccion = predicciones, Real = datos_prueba$condicion)
print("Matriz de Confusión:")
[1] "Matriz de Confusión:"
print(matriz_confusion)
           Real
Prediccion  Lujosa No Lujosa
  Lujosa       183        22
  No Lujosa     24       220
# Calcular métricas
# Asumimos que "Lujosa" es la clase positiva
VP <- matriz_confusion["Lujosa", "Lujosa"]
FP <- matriz_confusion["Lujosa", "No Lujosa"]
VN <- matriz_confusion["No Lujosa", "No Lujosa"]
FN <- matriz_confusion["No Lujosa", "Lujosa"]

# Accuracy
accuracy <- (VP + VN) / sum(matriz_confusion)
# Precision
precision <- VP / (VP + FP)
# Recall
recall <- VP / (VP + FN)
# F1 Score
f1_score <- 2 * (precision * recall) / (precision + recall)

# Imprimir resultados
print(paste("Accuracy:", round(accuracy, 4)))
[1] "Accuracy: 0.8976"
print(paste("Precision:", round(precision, 4)))
[1] "Precision: 0.8927"
print(paste("Recall:", round(recall, 4)))
[1] "Recall: 0.8841"
print(paste("F1 Score:", round(f1_score, 4)))
[1] "F1 Score: 0.8883"

Los resultados que has proporcionado provienen de una evaluación de un modelo de clasificación binaria para predecir si un inmueble es “Lujoso” o “No Lujoso”.

  • Accuracy (Exactitud): 0.8998 La exactitud del modelo es del 89.98%. Esto indica que el modelo clasificó correctamente el 89.98% de los inmuebles en las categorías “Lujoso” y “No Lujoso”. Es una métrica global que muestra la proporción total de predicciones correctas.

  • Precision (Precisión): 0.8857 La precisión es del 88.57% para la clase “Lujoso”. Esto significa que cuando el modelo predice que un inmueble es lujoso, tiene un 88.57% de probabilidad de que la predicción sea correcta. La precisión se enfoca en la calidad de las predicciones positivas.

  • Recall (Sensibilidad): 0.8986 La sensibilidad es del 89.86%. Esto indica que el modelo identificó correctamente el 89.86% de los inmuebles que realmente son lujosos. El recall mide la capacidad del modelo para encontrar todas las instancias positivas en los datos.

  • F1 Score: 0.8921 El F1 Score es del 89.21%, que es la media armónica de la precisión y el recall. Esta métrica proporciona un equilibrio entre la precisión y el recall, y es útil cuando se necesita una medida única para evaluar la calidad del modelo, especialmente en casos de desbalance en las clases.

También se pueden calcular métricas con funciones de R y generar visualizaciones a partir de ellas:

# Métricas más detalladas usando caret
metricas <- confusionMatrix(predicciones, factor(datos_prueba$condicion))
print(metricas)
Confusion Matrix and Statistics

           Reference
Prediction  Lujosa No Lujosa
  Lujosa       183        22
  No Lujosa     24       220
                                             
               Accuracy : 0.8976             
                 95% CI : (0.8657, 0.924)    
    No Information Rate : 0.539              
    P-Value [Acc > NIR] : <0.0000000000000002
                                             
                  Kappa : 0.7937             
                                             
 Mcnemar's Test P-Value : 0.8828             
                                             
            Sensitivity : 0.8841             
            Specificity : 0.9091             
         Pos Pred Value : 0.8927             
         Neg Pred Value : 0.9016             
             Prevalence : 0.4610             
         Detection Rate : 0.4076             
   Detection Prevalence : 0.4566             
      Balanced Accuracy : 0.8966             
                                             
       'Positive' Class : Lujosa             
                                             
# Visualización de las métricas
metricas_df <- data.frame(
  Metrica = c("Accuracy", "Precision", "Recall", "F1 Score"),
  Valor = c(accuracy, precision, recall, f1_score)
)

ggplot(metricas_df, aes(x = Metrica, y = Valor)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  ylim(0, 1) +
  geom_text(aes(label = round(Valor, 3)), vjust = -0.3) +
  labs(title = "Métricas de Evaluación del Clasificador Bayesiano Ingenuo",
       y = "Valor") +
  theme_minimal()

El análisis de la matriz de confusión y las estadísticas muestra que el modelo tiene un rendimiento robusto en la clasificación de inmuebles como “Lujosa” o “No Lujosa”. Con una exactitud del 89.98%, el modelo clasifica correctamente la mayoría de los inmuebles. La exactitud está respaldada por un intervalo de confianza del 95% de (0.8682, 0.926), lo que indica un alto grado de precisión y confiabilidad en el modelo. El valor p asociado con la exactitud es extremadamente bajo (<0.0000000000000002), indicando que el desempeño del modelo es significativamente mejor que el azar. La métrica Kappa de 0.7985 refleja una buena concordancia entre las predicciones del modelo y las categorías reales, ajustando por la posibilidad de coincidencias aleatorias. La sensibilidad del 89.86% y la especificidad del 90.08% muestran que el modelo tiene un buen equilibrio en la identificación tanto de inmuebles lujosos como no lujosos. Además, el valor predictivo positivo del 88.57% y el valor predictivo negativo del 91.21% indican que las predicciones positivas y negativas son bastante fiables. En general, las métricas sugieren que el modelo es efectivo y está bien ajustado para la tarea de clasificación, con una precisión equilibrada entre las categorías positivas y negativas.

Clasificación con Árboles de Decisión

Usaremos el método de árboles de decisión para clasificar la variable condicion, esto es, la categoría de la propiedad: Lujosa, No Lujosa.

# Entrenar el modelo de árbol de decisión
modelo_arbol <- rpart(condicion ~ 
                        metros_normalizadas + 
                        habitaciones_normalizadas + 
                        banos_normalizadas + 
                        antiguedad_normalizadas + 
                        distancia_normalizadas + 
                        zona_normalizadas,
                      data = datos_entrenamiento, method = "class")

# Visualizar el árbol
rpart.plot(modelo_arbol, type=5, extra = 106, under = TRUE, cex = 0.8)

El árbol de decisión representa un modelo para clasificar propiedades como “Lujosa” o “No Lujosa” basándose principalmente en dos variables: metros_normalizadas y distancia_normalizadas. Esto quiere decir que para nuestro modelo de clasificación son únicamente relevante estas dos variables. El árbol se interpreta de la siguiente forma:

  • Nodo Raíz (metros_normalizadas):

    • Si metros_normalizadas es mayor o igual a 0.19, va hacia la izquierda. Si es menor que 0.19, va hacia la derecha. En la derecha clasifica directamente como “No Lujosa” (55% de los casos del dataset, con una probabilidad de 89% de ser clasificación correcta).
  • Rama Izquierda de nivel 1 (metros_normalizadas >= 0.19):

    • Se hace otra división basada en metros_normalizadas nuevamente.

      • Si es mayor o igual a 0.72, clasifica directamente como “Lujosa” (29% de los casos del dataset, con una probabilidad de 3% de ser clasificación correcta).

      • Si es menor que 0.72, pasa al siguiente nivel.

  • Rama de nivel 2 (0.19 <= distancia_normalizadas < 0.72):

    • Si distancia_normalizadas es menor que 1.1, clasifica como “Lujosa” (14% de los casos del dataset, con una probabilidad de 17% de ser clasificación correcta).

    • Si es mayor o igual a 1.1, clasifica como “No Lujosa” (2% de los casos, con una probabilidad de 78% de ser correcta).

Luego, se evalúa el rendimiento del algoritmo con métricas de calidad:

# Realizar predicciones en el conjunto de prueba
predicciones <- predict(modelo_arbol, datos_prueba, type = "class")

# Calcular la matriz de confusión
matriz_confusion <- table(Prediccion = predicciones, Real = datos_prueba$condicion)
print("Matriz de Confusión:")
[1] "Matriz de Confusión:"
print(matriz_confusion)
           Real
Prediccion  Lujosa No Lujosa
  Lujosa       175        16
  No Lujosa     32       226
# Calcular métricas
# Asumimos que "Lujosa" es la clase positiva
VP <- matriz_confusion["Lujosa", "Lujosa"]
FP <- matriz_confusion["Lujosa", "No Lujosa"]
VN <- matriz_confusion["No Lujosa", "No Lujosa"]
FN <- matriz_confusion["No Lujosa", "Lujosa"]

# Accuracy
accuracy <- (VP + VN) / sum(matriz_confusion)
# Precision
precision <- VP / (VP + FP)
# Recall
recall <- VP / (VP + FN)
# F1 Score
f1_score <- 2 * (precision * recall) / (precision + recall)

# Imprimir resultados
print(paste("Accuracy:", round(accuracy, 4)))
[1] "Accuracy: 0.8931"
print(paste("Precision:", round(precision, 4)))
[1] "Precision: 0.9162"
print(paste("Recall:", round(recall, 4)))
[1] "Recall: 0.8454"
print(paste("F1 Score:", round(f1_score, 4)))
[1] "F1 Score: 0.8794"

Los resultados del árbol de decisión muestran que:

  • Accuracy (Exactitud): 0.8842 La exactitud del modelo es del 88.42%. Esto indica que el modelo clasificó correctamente el 88.42% de los inmuebles en las categorías “Lujoso” y “No Lujoso”.

  • Precision (Precisión): 0.8673 La precisión es del 86.73% para la clase “Lujoso”. Esto significa que cuando el modelo predice que un inmueble es lujoso, tiene un 86.73% de probabilidad de que la predicción sea correcta.

  • Recall (Sensibilidad): 0.8841 La sensibilidad es del 88.41%. Esto indica que el modelo identificó correctamente el 88.41% de los inmuebles que realmente son lujosos.

  • F1 Score: 0.8756 El F1 Score es del 87.56%, que es la media armónica de la precisión y el recall.

Predicción con Regresión Lineal Múltiple

Ahora aplicaremos regresión múltiple para predecir la variable precio a partir de las variables independientes. El modelo de regresión inicial sería:

\[ \hat{precio}= \beta_{0} + \beta_{1}.metros + \beta_{2}.habitaciones +\beta_{3}.banos + \beta_{4}.antiguedad +\beta_{5}.distancia + \beta_{6}.zona \]

Aplicamos esto en R:

# Crear el modelo de regresión múltiple
modelo_regresion <- lm(precio ~ 
                         metros_normalizadas +
                         habitaciones_normalizadas +
                         banos_normalizadas +
                         antiguedad_normalizadas +
                         distancia_normalizadas +
                         zona_normalizadas, 
                       data = datos_entrenamiento)

# Resumen del modelo
summary(modelo_regresion)

Call:
lm(formula = precio ~ metros_normalizadas + habitaciones_normalizadas + 
    banos_normalizadas + antiguedad_normalizadas + distancia_normalizadas + 
    zona_normalizadas, data = datos_entrenamiento)

Residuals:
    Min      1Q  Median      3Q     Max 
-199578  -41959    3238   42291  198806 

Coefficients:
                          Estimate Std. Error t value             Pr(>|t|)    
(Intercept)                 573764       1844 311.091 < 0.0000000000000002 ***
metros_normalizadas         156009       1853  84.182 < 0.0000000000000002 ***
habitaciones_normalizadas    27956       1827  15.300 < 0.0000000000000002 ***
banos_normalizadas           12813       1853   6.916     0.00000000000809 ***
antiguedad_normalizadas      -4776       1830  -2.610              0.00918 ** 
distancia_normalizadas      -41736       1872 -22.300 < 0.0000000000000002 ***
zona_normalizadas           -24192       1857 -13.027 < 0.0000000000000002 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 59770 on 1044 degrees of freedom
Multiple R-squared:  0.8884,    Adjusted R-squared:  0.8878 
F-statistic:  1386 on 6 and 1044 DF,  p-value: < 0.00000000000000022

El modo resultante encontrado por la regresión es:

\[ precio = 573764 + 156009.metros + 27956.habitaciones + 12813.banos - 4776.antiguedad - 41736.distancia - 24192.zona \]

Ahora realizamos la validación del modelo:

# Realizar predicciones
predicciones <- predict(modelo_regresion, newdata = datos_prueba)

# Calcular métricas de validación
mse <- mean((datos_prueba$precio - predicciones)^2)
rmse <- sqrt(mse)
r2 <- summary(modelo_regresion)$r.squared

# Mostrar las métricas
cat("MSE:", mse, "\n")
MSE: 3329995349 
cat("RMSE:", rmse, "\n")
RMSE: 57706.11 
cat("R-cuadrado:", r2, "\n")
R-cuadrado: 0.8884367 

El resultado de la regresión muestra un rendimiento bastante sólido del modelo en términos de su capacidad para predecir la variable objetivo, el precio de los inmuebles.

El MSE (Error Cuadrático Medio) es de 3,329,995,349, lo que indica la magnitud del error promedio al cuadrado en las predicciones del modelo. Aunque es difícil interpretar el MSE directamente debido a su escala en unidades cuadradas, el RMSE (Raíz del Error Cuadrático Medio), que es de 57,706.11, es más fácil de entender: en promedio, las predicciones del modelo se desvían del valor real por unos 57,706.11 unidades monetarias. Aunque esto parece ser un error considerable, su interpretación depende del rango de precios en los datos.

El \(Rˆ{2}\) es 0.8884, lo que significa que el modelo explica el 88.84% de la variabilidad en el precio de los inmuebles. Esto sugiere que el modelo tiene un buen ajuste y es capaz de capturar la mayor parte de la información relevante que afecta al precio. Sin embargo, el 11.16% de la variabilidad aún no se explica, lo que podría estar asociado a factores que no se incluyeron en el modelo o a la variabilidad inherente en los datos.

Finalmente, visualizamos el resultado.

ggplot(datos_prueba, aes(x = precio, y = predicciones)) +
  geom_point(alpha = 0.5) +
  geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
  labs(x = "Precio real", y = "Precio predicho",
       title = "Comparación entre precio real y predicho") +
  theme_minimal()

En la gráfica la mayoría de los puntos se alinean a lo largo de la línea diagonal roja (trazada en 45 grados), lo que indica que los precios predichos por el modelo están bastante cercanos a los precios reales. Esto sugiere que el modelo de regresión predice de manera razonablemente precisa los precios de los inmuebles, con un buen alineamiento de los precios predichos con los precios reales. Sin embargo, existen algunos casos en los que la precisión disminuye, especialmente en los extremos del rango de precios, lo que podría ser un área de mejora para el modelo.

Actividad calificada

Se debe utilizar el conjunto de datos “datos_credito” del paquete datos en CRAN, que contiene información sobre créditos de consumo. La tarea es aplicar modelos de regresión y clasificación para clasificar el estado (bueno/malo) y predecir el precio.

Instrucciones

  1. Crear un nuevo proyecto en RStudio con el nombre Mineria_Datos. Dentro del proyecto, organizar las carpetas de la siguiente manera:

    • Scripts: Para almacenar tus scripts de código R.

    • Datos: Para almacenar cualquier dataset que utilice en la actividad.

  2. Dentro del directorio Scripts, crear un script R llamado etapas_4_5.R. En este script, codificar las siguientes tareas:

    • Preprocesamiento de Datos:

      • Cargar los datos en R utilizando el paquete datos.

      • Realizar los procesos de limpieza y transformación de datos.

      • Dividir el conjunto de datos en un 80% para entrenamiento y un 20% para prueba.

    • Clasificación:

      • Naive Bayes: 1. Entrenar un modelo de Naive Bayes para clasificar los clientes segun su estado de pago (bueno/malo). 2. Evaluar el modelo utilizando una matriz de confusión y calcular las métricas de rendimiento (accuracy, precision, recall, F1 score).
      • Árboles de Decisión: 1. Entrenar un árbol de decisión clasificar los clientes segun su estado de pago (bueno/malo). 2. Visualizar el árbol generado y evalúa su rendimiento utilizando las mismas métricas que en Naive Bayes.
    • Regresión:

      • Regresión Lineal Múltiple: 1. Crear un modelo de regresión múltiple para predecir el precio en función de las variables independientes (todas excepto Estado y Precio). 2. Evaluar el modelo utilizando el MSE, RMSE y \(R^2\). 3. Realizar una visualización comparando los precios reales con los predichos.
    • Documentación:

      • Agregar comentarios al código explicando la funcionalidad de cada sección. Incluir al inicio del script información sobre carrera e integrantes del grupo.

Entrega

Una vez completada la actividad, subir comprimir el proyecto en formato .zip con el nombre MD_Practica_3_GrupoX. Donde X debe ser reemplazado por su número de grupo.

Criterios de Evaluación

  • Estructura y organización del proyecto (10%).

  • Preprocesamiento (20%).

  • Clasificación (30%).

  • Regresión (30%).

  • Documentación, formato de entrega y comentarios en el código (10%).