[1]: Realice un filtro a la base de datos e incluya sólo las ofertas de apartamentos. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta.

data("vivienda")
ds_vivienda = vivienda

df_vivienda_apt <- ds_vivienda %>% filter(tipo == "Apartamento")

df_vivienda_apt[1:3, ]
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1212 Zona N… 01          5     260        90            1      2            3
## 2  1724 Zona N… 01          5     240        87            1      3            3
## 3  2326 Zona N… 01          4     220        52            2      2            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>


[2]: Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

df_vivienda_apt = df_vivienda_apt[c("preciom", "areaconst", "estrato", "banios", "habitaciones", "zona")]


[2.1]: Análisis exploratorio de datos [EDA]:


- Existencia de ceros y valores nulos:

summary(df_vivienda_apt)
##     preciom         areaconst        estrato          banios     
##  Min.   :  58.0   Min.   : 35.0   Min.   :3.000   Min.   :0.000  
##  1st Qu.: 175.0   1st Qu.: 68.0   1st Qu.:4.000   1st Qu.:2.000  
##  Median : 279.0   Median : 90.0   Median :5.000   Median :2.000  
##  Mean   : 366.9   Mean   :112.8   Mean   :4.727   Mean   :2.617  
##  3rd Qu.: 430.0   3rd Qu.:130.0   3rd Qu.:6.000   3rd Qu.:3.000  
##  Max.   :1950.0   Max.   :932.0   Max.   :6.000   Max.   :8.000  
##   habitaciones       zona          
##  Min.   :0.000   Length:5100       
##  1st Qu.:3.000   Class :character  
##  Median :3.000   Mode  :character  
##  Mean   :2.971                     
##  3rd Qu.:3.000                     
##  Max.   :9.000
# Cantidad de valores "0" en cada variable:

conteo_ceros <- sapply(df_vivienda_apt, function(x) sum(x == 0, na.rm = TRUE))

df_ceros <- data.frame(Variable = names(conteo_ceros), CantidadCeros = conteo_ceros)

df_ceros <- df_ceros %>%
  arrange(desc(CantidadCeros)) %>%
  mutate(Variable = factor(Variable, levels = Variable))

ggplot(df_ceros, aes(x = Variable, y = CantidadCeros)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = CantidadCeros), vjust = -0.5, color = "black", size = 3) +
  theme_minimal() +
  labs(title = "Cantidad de \"CEROS\" por variable",
       x = "Variable",
       y = "Cantidad de Ceros") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        plot.title = element_text(hjust = 0.5, size = 14))


Interpretación: Dado que el número de registros con valores ‘0’ es insignificante, se decidió eliminarlos del conjunto de datos.

df_vivienda_apt <- df_vivienda_apt %>%
  filter(banios != 0, habitaciones != 0)
# Cantidad de valores "nulos" en cada variable:

na_counts <- colSums(is.na(df_vivienda_apt))

na_df <- data.frame(
  Column = names(na_counts),
  NA_Count = na_counts
)

ggplot(na_df, aes(x = Column, y = NA_Count)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = NA_Count), vjust = -0.5, color = "black", size = 3) +
  theme_minimal() +
  labs(title = "Conteo de valores \"NULOS\" por variable",
       x = "Variable",
       y = NULL) +  # Eliminar el título del eje y
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        plot.title = element_text(hjust = 0.5, size = 14))

Interpretación: No se evidencia la existencia de valores ‘nulos’


[2.2]: Análisis de variables cuantitativas:

df_vivi_apt_cuanti = df_vivienda_apt[c("preciom", "areaconst", "banios", "habitaciones")]

GGally::ggpairs(df_vivi_apt_cuanti, title="Análisis de significancia - Variables cuantitativas")

# Análisis de multicolinealidad:

modelo <- lm(preciom ~ areaconst + banios + habitaciones, data = df_vivi_apt_cuanti)

# VIF (Valor de Inflación de la Varianza):

vif(modelo)
##    areaconst       banios habitaciones 
##     2.266040     2.475220     1.370322

Interpretación: Con base en los valores obtenidos para el VIF en cada variable, se concluye que estas variables cuantitativas no presentan colinealidad entre sí.

# ANOVA del modelo
anova(modelo)
## Analysis of Variance Table
## 
## Response: preciom
##                Df    Sum Sq   Mean Sq  F value    Pr(>F)    
## areaconst       1 295692993 295692993 14102.59 < 2.2e-16 ***
## banios          1  15984033  15984033   762.33 < 2.2e-16 ***
## habitaciones    1   6999759   6999759   333.84 < 2.2e-16 ***
## Residuals    5070 106304124     20967                       
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Interpretación: Considerando que todos los valores del ‘F value’ son altos, se puede concluir que cada variable predictora tiene un efecto significativo sobre la variable dependiente ‘preciom’ en el modelo.

Interpretación: Con base en el análisis de significancia anterior, se concluye que no es apropiado omitir ninguna de las variables cuantitativas.


[2.3]: Análisis de variables cualitativas:

df_vivi_apt_cuali = df_vivienda_apt[c("preciom", "estrato", "zona")]

df_vivi_apt_cuali$estrato <- factor(df_vivi_apt_cuali$estrato)
df_vivi_apt_cuali$zona <- factor(df_vivi_apt_cuali$zona)

# Frecuencia de cada categoría en 'estrato':

table(df_vivi_apt_cuali$estrato)
## 
##    3    4    5    6 
##  637 1396 1754 1287
# Frecuencia de cada categoría en 'zona':

table(df_vivi_apt_cuali$zona)
## 
##  Zona Centro   Zona Norte   Zona Oeste Zona Oriente     Zona Sur 
##           24         1188         1024           61         2777
# Gráfico de barras para 'estrato':

ggplot(df_vivi_apt_cuali, aes(x = estrato)) +
  geom_bar(fill = "steelblue") +
  theme_minimal() +
  labs(title = "Distribución de la variable \"ESTRATO\"", x = "Estrato", y = "Frecuencia") +
  theme(plot.title = element_text(hjust = 0.5))

# Gráfico de barras para 'zona'
ggplot(df_vivi_apt_cuali, aes(x = zona)) +
  geom_bar(fill = "steelblue") +
  theme_minimal() +
  labs(title = "Distribución de la variable \"ZONA\"", x = "zona", y = "Frecuencia") +
  theme(plot.title = element_text(hjust = 0.5))

# Crear tabla de contingencia entre 'estrato' y 'zona':

table(df_vivi_apt_cuali$estrato, df_vivi_apt_cuali$zona)
##    
##     Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
##   3          14        337         29           57      200
##   4           7        243         58            2     1086
##   5           3        491        228            1     1031
##   6           0        117        709            1      460
# Gráfico de barras apiladas para 'estrato' dentro de cada 'zona':

ggplot(df_vivi_apt_cuali, aes(x = zona, fill = estrato)) +
  geom_bar(position = "stack") +
  theme_minimal() +
  labs(title = "Distribución de \"ESTRATO\" dentro de cada \"ZONA\"", x = "zona", y = "Frecuencia") +
  theme(plot.title = element_text(hjust = 0.5))

# Boxplot de 'preciom' por 'estrato'
ggplot(df_vivi_apt_cuali, aes(x = estrato, y = preciom)) +
  geom_boxplot(fill = "lightblue") +
  theme_minimal() +
  labs(title = "Distribución de \"PRECIOM\" por \"ESTRATO\"", x = "Estrato", y = "preciom") +
  theme(plot.title = element_text(hjust = 0.5))

# Boxplot de 'preciom' por 'zona'
ggplot(df_vivi_apt_cuali, aes(x = zona, y = preciom)) +
  geom_boxplot(fill = "lightblue") +
  theme_minimal() +
  labs(title = "Distribución de \"PRECIOM\" por \"ZONA\"", x = "zona", y = "preciom") +
  theme(plot.title = element_text(hjust = 0.5))

# Análisis de significancia:

modelo <- lm(preciom ~ estrato + zona, data = df_vivi_apt_cuali)

summary(modelo)
## 
## Call:
## lm(formula = preciom ~ estrato + zona, data = df_vivi_apt_cuali)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -609.09  -74.09  -16.61   45.79 1429.87 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       134.124     39.782   3.371 0.000753 ***
## estrato4           92.906     10.017   9.275  < 2e-16 ***
## estrato5          202.897      9.545  21.257  < 2e-16 ***
## estrato6          506.855     10.808  46.897  < 2e-16 ***
## zonaZona Norte     -2.509     40.100  -0.063 0.950117    
## zonaZona Oeste    133.111     40.617   3.277 0.001055 ** 
## zonaZona Oriente    1.047     46.853   0.022 0.982165    
## zonaZona Sur      -32.816     40.032  -0.820 0.412403    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 194 on 5066 degrees of freedom
## Multiple R-squared:  0.5515, Adjusted R-squared:  0.5509 
## F-statistic: 890.1 on 7 and 5066 DF,  p-value: < 2.2e-16
# Análisis de Varianza (ANOVA):

anova(modelo)
## Analysis of Variance Table
## 
## Response: preciom
##             Df    Sum Sq  Mean Sq F value    Pr(>F)    
## estrato      3 218716364 72905455 1937.92 < 2.2e-16 ***
## zona         4  15679345  3919836  104.19 < 2.2e-16 ***
## Residuals 5066 190585199    37620                      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Interpretación: De manera similar, y basados en los resultados del ANOVA para estas dos variables categóricas, se concluye que no es apropiado omitir ni eliminar ninguna de ellas del modelo.


[3]: Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

df_vivienda_apt_bk = df_vivienda_apt

# Variable dummies para la "estrato" --> categoría de referencia "3":

df_vivienda_apt$E4 <- as.numeric(df_vivienda_apt$estrato==4)
df_vivienda_apt$E5 <- as.numeric(df_vivienda_apt$estrato==5)
df_vivienda_apt$E6 <- as.numeric(df_vivienda_apt$estrato==6)

# Variable dummies para la "zona" --> categoría de referencia "Zona Centro":

df_vivienda_apt$Z1 <- as.numeric(df_vivienda_apt$zona=="Zona Norte")
df_vivienda_apt$Z2 <- as.numeric(df_vivienda_apt$zona=="Zona Oeste")
df_vivienda_apt$Z3 <- as.numeric(df_vivienda_apt$zona=="Zona Oriente")
df_vivienda_apt$Z4 <- as.numeric(df_vivienda_apt$zona=="Zona Sur")

df_vivienda_apt <- df_vivienda_apt[, !(names(df_vivienda_apt) %in% c("estrato", "zona"))]

# Estimación del modelo:

modelo <- lm(preciom ~ areaconst + banios + habitaciones + E4 + E5 + E6 + Z1 + Z2 + Z3 + Z4, data = df_vivienda_apt)

summary(modelo)
## 
## Call:
## lm(formula = preciom ~ areaconst + banios + habitaciones + E4 + 
##     E5 + E6 + Z1 + Z2 + Z3 + Z4, data = df_vivienda_apt)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1881.44   -48.72     1.96    44.21   950.88 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -50.83077   28.05010  -1.812  0.07002 .  
## areaconst      2.23167    0.04257  52.422  < 2e-16 ***
## banios        58.88372    3.01425  19.535  < 2e-16 ***
## habitaciones -37.08801    3.43837 -10.787  < 2e-16 ***
## E4            18.85254    6.89473   2.734  0.00627 ** 
## E5            47.03699    6.85074   6.866 7.40e-12 ***
## E6           183.09264    8.69129  21.066  < 2e-16 ***
## Z1            47.34843   26.87116   1.762  0.07812 .  
## Z2           110.12558   27.21805   4.046 5.29e-05 ***
## Z3             6.71178   31.39613   0.214  0.83073    
## Z4            38.93694   26.83363   1.451  0.14683    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 129.9 on 5063 degrees of freedom
## Multiple R-squared:  0.7989, Adjusted R-squared:  0.7985 
## F-statistic:  2011 on 10 and 5063 DF,  p-value: < 2.2e-16

Interpretación: Del resumen del modelo anterior, y basándonos en el ‘valor p’ de cada coeficiente y del intercepto, es posible afirmar que:

Ahora bien, la interpretación de los valores de los coeficientes de las variables en el modelo es la siguiente:

areaconst –> 1 m2 adicional incrementa el precio de la vivienda en promedio en 2.2. millones

banios –> Cada baño adicional incrementa el valor de la vivienda en 58.8 millones

habitaciones –> De manera contraintuitiva, en promedio el precio de la vivienda se reduce en 37.1 millones por cada habitación de menos

E4 –> Este coeficiente indica que en promedio el precio de una vivienda de estrato 4 es 18.8 millones más elevado que el de una vivienda de estrato 3, siendo esta última la categoría de referencia

E5 –> En promedio el precio de una vivienda de estrato 5 con relación a una de estrato 3 es de 47 millones más elevado

E6 –> Las viviendas de estrato 6 tienen en promedio un precio superior en 183 millones con relación a las de estrato 3, lo que, o por lo menos en cuando a una ciudad como Bogotá, es perfectamente coherente

Z1–> En promedio, la zona norte tiene una valoración superior en 47 millones con relación a la zona centro, esta última, la zona de referencia para las variables dummies

Z2 –> La zona oeste presenta las viviendas con precios más altos, teniendo una vivienda en esta zona en promedio un mayor valor en 110.1 millones con relación a una vivienda de la zona centro

Z3 –> Las vivienda de la zona oriente presentan un precio similar, puesto que en promedio son tan solo 6.7 millones superior a las de la zona centro

Z4 –> La zona sur está mejor valorizada que la zona centro, puesto que las viviendas allí en promedio tienen un precio superior en 38.9 millones con relación a las viviendas de la zona centro.

Por otra parte, el valor obtenido para el coeficiente de determinación R2 de 79.9% indica que el modelo logra explicar aproximadamente el 80% de la variabilidad en la variable dependiente, que en este caso es el precio de la vivienda. Aunque este valor no es perfecto, es bastante aceptable, ya que muestra que el modelo captura una gran parte de la variabilidad en el precio de la vivienda. Sin embargo, siempre es importante considerar que un R2 alto no garantiza que el modelo sea perfecto o que todas las variables relevantes estén incluidas.

En relación con el valor del R2 ajustado, la diferencia con el R2 es mínima, lo que sugiere que las variables utilizadas en el modelo son adecuadas y que no existe una sobrecarga de variables. Sin embargo, el hecho de que aún quede un 20% de la variabilidad de la variable dependiente (precio de la vivienda) sin explicar indica que hay margen para mejorar el ajuste del modelo. Esto sugiere la posibilidad de explorar otros métodos, como la validación cruzada, para optimizar el modelo y lograr un mejor ajuste.


[4]: Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

# Validación de que los errores son variables aleatorias normales:

residuos <- residuals(modelo)

hist(residuos, breaks = 30, main = "Histograma de residuos", xlab = "Residuos")

qqnorm(residuos, main = "Gráfico Q-Q de Residuos")
qqline(residuos, col = "red")

ks.test(residuos, "pnorm", mean = mean(residuos), sd = sd(residuos))
## Warning in ks.test.default(residuos, "pnorm", mean = mean(residuos), sd =
## sd(residuos)): ties should not be present for the Kolmogorov-Smirnov test
## 
##  Asymptotic one-sample Kolmogorov-Smirnov test
## 
## data:  residuos
## D = 0.15227, p-value < 2.2e-16
## alternative hypothesis: two-sided

Interpretación: El anterior análisis de normalidad aplicado desde diferentes métodos arrojó resultados sumamente interesante. Aunque tanto el histograma como el gráfico Q-Q, que son validaciones visuales, sugieren que los residuos podrían seguir una distribución normal, la prueba de Kolmogorov-Smirnov ofrece una perspectiva diferente. Esta prueba, al rechazar la hipótesis nula debido al valor p obtenido, indica que los residuos no siguen una distribución normal.

# Validación de que los errores tienen media cero:

t.test(residuos, mu = 0)
## 
##  One Sample t-test
## 
## data:  residuos
## t = -2.5898e-15, df = 5073, p-value = 1
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  -3.572463  3.572463
## sample estimates:
##     mean of x 
## -4.719372e-15

Interpretación: El resultado de la prueba establece que, efectivamente, el valor esperado de los errores es cero. Esto indica que, en promedio, los residuos no presentan sesgo sistemático y, por lo tanto, el modelo está ajustado de manera que la media de los errores es cercana a cero.

# Validación de que los errores tienen varianza constante:

valores_ajustados <- fitted(modelo)

ggplot(data = data.frame(valores_ajustados, residuos), aes(x = valores_ajustados, y = residuos)) +
  geom_point() +
  geom_hline(yintercept = 0, color = "red", linetype = "dashed") +
  theme_minimal() +
  labs(
    title = "Gráfico de [RESIDUOS] vs [VALORES AJUSTADOS]",
    x = "Valores ajustados",
    y = "Residuos"
  ) +
  theme(plot.title = element_text(hjust = 0.5))

resultado_gq <- gqtest(modelo, order.by = ~ areaconst, data = df_vivienda_apt)

print(resultado_gq)
## 
##  Goldfeld-Quandt test
## 
## data:  modelo
## GQ = 22.751, df1 = 2526, df2 = 2526, p-value < 2.2e-16
## alternative hypothesis: variance increases from segment 1 to 2

Interpretación: El resultado de la prueba de Goldfeld-Quandt sugiere la existencia de heterocedasticidad en los residuos. La prueba rechaza la hipótesis nula (valor p por debajo del nivel de significancia del 0.05), que establece que los residuos tienen varianza constante. Esto indica que la varianza de los residuos no es constante a lo largo del rango de valores predichos.

# Validación de que los errores son mutuamente independientes

resultado_dw <- dwtest(modelo)

print(resultado_dw)
## 
##  Durbin-Watson test
## 
## data:  modelo
## DW = 1.7647, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

Interpretación: La aplicación de la prueba de Durbin-Watson da como resultado que los residuos pueden estar correlacionados, es decir, que no son mutuamente independientes.

A juzgar por los resultados de las validaciones de los supuestos del modelo, en donde el único supuesto que se cumple es el de que los residuos tienen una media de cero, se sugiere explorar diferentes transformaciones de las variables del modelo. Se recomienda comenzar con transformaciones que añadan la menor complejidad al modelo y avanzar gradualmente hacia opciones más complejas. Este enfoque permitirá encontrar un equilibrio adecuado entre la mejora del ajuste del modelo y la complejidad añadida.

Es interesante observar que, aunque el modelo explica una proporción significativa de la variabilidad de la variable dependiente, sugiriendo un buen ajuste a los datos, los supuestos fundamentales sobre los residuos no se cumplen. Esta situación es preocupante, ya que puede afectar negativamente las inferencias realizadas y la precisión de las predicciones basadas en el modelo. En particular, la falta de cumplimiento de los supuestos puede conducir a estimaciones inestables de los coeficientes, lo que dificulta la interpretación del impacto de las variables independientes. Debido a estas inconsistencias, no se puede confiar plenamente en los resultados del modelo, desde la inferencia de parámetros hasta las pruebas de hipótesis y las predicciones. En consecuencia, el modelo no es adecuado para la toma de decisiones.


[5]: Realice una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba. Estime el modelo con la muestra del 70%. Muestre los resultados

set.seed(123)

index_train <- createDataPartition(df_vivienda_apt$preciom, p = 0.7, list = FALSE)
train_data <- df_vivienda_apt[index_train, ]
test_data <- df_vivienda_apt[-index_train, ]

modelo <- lm(preciom ~ areaconst + banios + habitaciones + E4 + E5 + E6 + Z1 + Z2 + Z3 + Z4, data = train_data)

summary(modelo)
## 
## Call:
## lm(formula = preciom ~ areaconst + banios + habitaciones + E4 + 
##     E5 + E6 + Z1 + Z2 + Z3 + Z4, data = train_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1798.42   -50.59     2.24    45.40   955.17 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -32.65917   34.35322  -0.951   0.3418    
## areaconst      2.13521    0.05026  42.485  < 2e-16 ***
## banios        61.73795    3.63628  16.978  < 2e-16 ***
## habitaciones -37.12437    4.18293  -8.875  < 2e-16 ***
## E4            20.54351    8.36834   2.455   0.0141 *  
## E5            46.56743    8.31573   5.600 2.31e-08 ***
## E6           191.06904   10.45878  18.269  < 2e-16 ***
## Z1            31.58897   32.80565   0.963   0.3357    
## Z2            93.55136   33.22646   2.816   0.0049 ** 
## Z3           -11.14180   37.86878  -0.294   0.7686    
## Z4            19.65603   32.74925   0.600   0.5484    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 133.5 on 3542 degrees of freedom
## Multiple R-squared:   0.79,  Adjusted R-squared:  0.7894 
## F-statistic:  1332 on 10 and 3542 DF,  p-value: < 2.2e-16

Interpretación: En el modelo ajustado con el 70% de los datos, se obtiene un R2 de 79%, que es prácticamente igual al R2 obtenido utilizando la totalidad de los datos.


[6]: Realice predicciones con el modelo anterior usando los datos de prueba (30%)

# Predicciones:
predicciones <- predict(modelo, newdata = test_data)

# Primeras predicciones:
head(predicciones)
##         1         2         3         4         5         6 
## 305.10149 142.60717 277.52694  91.04444 152.55836  79.54262


[7]: Calcule el error cuadrático medio, el error absoluto medio y el R2, interprete

# Calcular error cuadrático medio (RMSE):
rmse <- sqrt(mean((test_data$preciom - predicciones)^2))

# Calcular el error absoluto medio (MAE):
mae <- mean(abs(test_data$preciom - predicciones))

# Calcular el coeficiente de determinación (R2):
rss <- sum((test_data$preciom - predicciones)^2)
tss <- sum((test_data$preciom - mean(test_data$preciom))^2)
r_squared <- 1 - (rss/tss)

# Resultados:
rmse
## [1] 121.648
mae
## [1] 75.68914
r_squared
## [1] 0.8193204

Interpretación: En el análisis del modelo en el conjunto de prueba, se obtiene un R2 bastante bueno, del 82%. Sin embargo, los otros dos indicadores, que se centran en los errores, muestran valores muy altos. Esta discrepancia podría deberse a la penalización que estos indicadores aplican a los valores atípicos. Dado que la variable dependiente presenta valores significativamente altos, esto se refleja más en los indicadores de error.

Es evidente que el modelo está capturando correctamente la variabilidad de los datos, lo que sugiere que está prediciendo el valor correcto en la mayoría de los casos. No obstante, se recomienda realizar un análisis de outliers para identificar y, si es posible, eliminar aquellos que no expliquen un comportamiento específico en el conjunto de datos. Esto podría ayudar a mejorar los indicadores de error y proporcionar una visión más precisa del rendimiento del modelo.