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>
df_vivienda_apt = df_vivienda_apt[c("preciom", "areaconst", "estrato", "banios", "habitaciones", "zona")]
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’
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.
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.
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:
Las variables areaconst, banios, habitaciones, E4, E6 y Z2 son estadísticamente significativas, ya que sus valores p son menores a 0.05. Esto indica que cada una de estas variables tiene un impacto significativo en la explicación de la variable dependiente preciom. En otras palabras, estas variables contribuyen de manera importante a la variabilidad del precio de la vivienda (preciom) y son esenciales para entender y modelar su comportamiento.
Las variables Z1, Z3 y Z4 no son estadísticamente significativas, dado que sus valores p son mayores a 0.05. Esto sugiere que no tienen un efecto claro ni consistente sobre la variable dependiente preciom. En otras palabras, estas variables no aportan evidencia suficiente para influir en la explicación del precio de la vivienda (preciom) en el contexto del modelo analizado.
A pesar de que el intercepto no es estadísticamente significativo, dado que su valor p es ligeramente superior al umbral del 5%, esto no afecta la interpretación de los coeficientes de las variables independientes. Este valor sugiere que el intercepto no difiere significativamente de cero, pero esto no implica que los efectos de las variables independientes sobre la variable dependiente preciom sean inválidos o irrelevantes.
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.
# 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.
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.
# 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
# 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.