Con base en los datos de ofertas de vivienda descargadas del portal Fincaraiz para apartamentos de estrato 4 con área construida menor a \(200\: m^{2}\) (vivienda4.RDS) la inmobiliaria A&C requiere el apoyo de un científico de datos en la construcción de un modelo que lo oriente sobre los precios de inmuebles. Con este propósito el equipo de asesores a diseñado los siguientes pasos para obtener un modelo y así poder a futuro determinar los precios de los inmuebles a negociar.
suppressMessages(suppressWarnings(library(knitr)))
suppressMessages(suppressWarnings(library(rmarkdown)))
suppressMessages(suppressWarnings(library(paqueteMETODOS)))
suppressMessages(suppressWarnings(library(naniar)))
suppressMessages(suppressWarnings(library(mice)))
suppressMessages(suppressWarnings(library(ggmice)))
suppressMessages(suppressWarnings(library(pastecs)))
suppressMessages(suppressWarnings(library(stringr)))
suppressMessages(suppressWarnings(library(stringi)))
suppressMessages(suppressWarnings(library(dplyr)))
suppressMessages(suppressWarnings(library(tidyverse)))
suppressMessages(suppressWarnings(library(kableExtra)))
suppressMessages(suppressWarnings(library(reticulate)))
suppressMessages(suppressWarnings(library(lsr)))
suppressMessages(suppressWarnings(library(tibble)))
suppressMessages(suppressWarnings(library(patchwork)))
suppressMessages(suppressWarnings(library(sf)))
suppressMessages(suppressWarnings(library(osmdata)))
suppressMessages(suppressWarnings(library(mapview)))
suppressMessages(suppressWarnings(library(leaflet)))
suppressMessages(suppressWarnings(library(heatmaply)))
suppressMessages(suppressWarnings(library(ggplot2)))
suppressMessages(suppressWarnings(library(stats)))
suppressMessages(suppressWarnings(library(e1071)))
suppressMessages(suppressWarnings(library(ggstatsplot)))
suppressMessages(suppressWarnings(library(grid)))
suppressMessages(suppressWarnings(library(gridExtra)))
suppressMessages(suppressWarnings(library(moments)))
suppressMessages(suppressWarnings(library(diptest)))
suppressMessages(suppressWarnings(library(LaplacesDemon)))
suppressMessages(suppressWarnings(library(ggExtra)))
suppressMessages(suppressWarnings(library(RColorBrewer)))
suppressMessages(suppressWarnings(library(hexbin)))
suppressMessages(suppressWarnings(library(MASS)))
suppressMessages(suppressWarnings(library(ggfortify)))
suppressMessages(suppressWarnings(library(latex2exp)))
suppressMessages(suppressWarnings(library(lmtest)))
suppressMessages(suppressWarnings(library(interactions)))
data(vivienda4)df <- subset(vivienda4, select = -c(zona, estrato, tipo))
options(scipen = 999)
skewness_values <- sapply(df, skewness)
summary_df <- stat.desc(df)
summary_df <- round(summary_df, 2)
summary_df <- rbind(summary_df, round(skewness_values, 2))
rownames(summary_df)[nrow(summary_df)] <- "Skewness"
shapiro_results <- sapply(df, function(column) {
test_result <- shapiro.test(column)
c(W = round(test_result$statistic,2), p_value = formatC(test_result$p.value, format="e", digits=2))
})
summary_df <- rbind(summary_df, shapiro_results)
rownames(summary_df)[(nrow(summary_df) - 1):nrow(summary_df)] <- c("Shapiro W", "Shapiro p-value")
quartiles <- sapply(df, function(column) {
quantiles <- quantile(column, probs = c(0.25, 0.75))
c(Q1 = quantiles[1], Q3 = quantiles[2])
})
summary_df <- rbind(summary_df, round(quartiles, 2))
rownames(summary_df)[(nrow(summary_df) - 1):nrow(summary_df)] <- c("Q1 (25%)", "Q3 (75%)")
kurtosis_values <- sapply(df, kurtosis)
summary_df <- rbind(summary_df, round(kurtosis_values, 2))
rownames(summary_df)[nrow(summary_df)] <- "Kurtosis"
mad_values <- sapply(df, mad)
summary_df <- rbind(summary_df, round(mad_values, 2))
rownames(summary_df)[nrow(summary_df)] <- "Mean Absolute Deviation"
# Define the order of the rows
row_order <- c("mean", "std.dev", "var", "coef.var", "Mean Absolute Deviation", "Q1 (25%)", "median", "Q3 (75%)", "min", "max", "range", "sum", "SE.mean", "CI.mean.0.95", "Skewness", "Kurtosis", "Shapiro W", "Shapiro p-value")
# Order the rows in summary_df
summary_df <- summary_df[match(row_order, rownames(summary_df)), ]
paged_table(summary_df, options = list(rows.print = 20)) %>%
kable(caption = 'Variables Numéricas')| preciom | areaconst | |
|---|---|---|
| mean | 225.37 | 87.63 |
| std.dev | 85.89 | 36.35 |
| var | 7376.27 | 1321.07 |
| coef.var | 0.38 | 0.41 |
| Mean Absolute Deviation | 75.61 | 22.24 |
| Q1 (25%) | 160 | 60 |
| median | 210 | 75 |
| Q3 (75%) | 265 | 98 |
| min | 78 | 40 |
| max | 760 | 200 |
| range | 682 | 160 |
| sum | 384489 | 149496 |
| SE.mean | 2.08 | 0.88 |
| CI.mean.0.95 | 4.08 | 1.73 |
| Skewness | 1.49 | 1.53 |
| Kurtosis | 6.57 | 4.68 |
| Shapiro W | 0.89 | 0.82 |
| Shapiro p-value | 2.84e-33 | 2.51e-40 |
# Run the dip test and store the result
dip_test_result <- dip.test(vivienda4$preciom)
# Extract the D statistic and p-value
D_statistic <- dip_test_result$statistic
p_value <- dip_test_result$p.value
#is.unimodal(vivienda4$preciom)
#is.multimodal(vivienda4$preciom)
# Print the results in markdown
cat(paste0("Como podemos observar en los valores de la tabla anterior y en las gráficas a continuación, los valores de `preciom` se distribuyen con asimetría positiva, principalmente para valores menores a 400 millones de pesos con Q1=160 y Q3=265 millones de pesos. El estadístico D del Hartigans' dip test para uni- y multimodalidad sobre la columna de `preciom` es **", round(D_statistic, 6), "**. El valor p es menor a **", format(2.2e-15, scientific = TRUE), "**. La hipótesis alternativa es que la distribución no es unimodal. Del mismo modo, en la tabla anterior se presentan los valores de la prueba Shapiro-Wilks obteniendo que la distribución tampoco es normal para un valor de p de **", format(2.84e-33, scientific = TRUE), "** y un valor del estadístico W de**", 0.89,"**"))Como podemos observar en los valores de la tabla anterior y en las
gráficas a continuación, los valores de preciom se
distribuyen con asimetría positiva, principalmente para valores menores
a 400 millones de pesos con Q1=160 y Q3=265 millones de pesos. El
estadístico D del Hartigans’ dip test para uni- y multimodalidad sobre
la columna de preciom es 0.027806. El
valor p es menor a 2.2e-15. La hipótesis alternativa es
que la distribución no es unimodal. Del mismo modo, en la tabla anterior
se presentan los valores de la prueba Shapiro-Wilks obteniendo que la
distribución tampoco es normal para un valor de p de
2.84e-33 y un valor del estadístico W
de0.89
df <- subset(vivienda4, select = c(preciom))
# for reproducibility
set.seed(123)
# creating a plot
p <- gghistostats(
data = df,
x = preciom,
xlab = "preciom",
centrality.type = "np"
)
p2 <- ggplot(df, aes(x="", y=preciom)) +
geom_violin(trim = T, fill = "gray", width=0.3) +
geom_jitter(height = 0, width = 0.1, alpha=0.1, size=1, colour="#20263d") +
geom_boxplot(notch = TRUE, width=0.1)+
stat_summary(fun = "mean", geom = "point", shape = 23, size = 3, fill = "#20263d")+
xlab("")+
theme_bw()+
theme(axis.title.y = element_text(face = "bold", size=10))
grid.arrange(p, p2, nrow = 2, top = textGrob("Descripción de preciom", gp = gpar(fontface = "bold")))# Run the dip test and store the result
dip_test_result <- dip.test(vivienda4$areaconst)
# Extract the D statistic and p-value
D_statistic <- dip_test_result$statistic
p_value <- dip_test_result$p.value
# Print the results in markdown
cat(paste0("Al igual que `preciom`, los valores de `areaconst` se distribuyen con asimetría positiva, principalmente para valores menores a 100 $m^{2}$ con Q1=60 y Q3=98 $m^{2}$. El estadístico D del Hartigans' dip test para uni- y multimodalidad sobre la columna de `preciom` es **", round(D_statistic, 6), "**. El valor p es menor a **", format(2.2e-15, scientific = TRUE), "**. La hipótesis alternativa es que la distribución no es unimodal, como también se puede apreciar en el histograma aunque no de forma tan definida como `preciom`. Del mismo modo, en la tabla anterior se presentan los valores de la prueba Shapiro-Wilks obteniendo que la distribución tampoco es normal para un valor de p de **", format(2.51e-40, scientific = TRUE), "** y un valor del estadístico W de**", 0.82,"**"))Al igual que preciom, los valores de
areaconst se distribuyen con asimetría positiva,
principalmente para valores menores a 100 \(m^{2}\) con Q1=60 y Q3=98 \(m^{2}\). El estadístico D del Hartigans’
dip test para uni- y multimodalidad sobre la columna de
preciom es 0.024439. El valor p es menor a
2.2e-15. La hipótesis alternativa es que la
distribución no es unimodal, como también se puede apreciar en el
histograma aunque no de forma tan definida como preciom.
Del mismo modo, en la tabla anterior se presentan los valores de la
prueba Shapiro-Wilks obteniendo que la distribución tampoco es normal
para un valor de p de 2.51e-40 y un valor del
estadístico W de0.82
df <- subset(vivienda4, select = c(areaconst))
# for reproducibility
set.seed(123)
# creating a plot
p <- gghistostats(
data = df,
x = areaconst,
xlab = "areaconst",
centrality.type = "np"
)
p2 <- ggplot(df, aes(x="", y=areaconst)) +
geom_violin(trim = T, fill = "gray", width=0.3) +
geom_jitter(height = 0, width = 0.1, alpha=0.1, size=1, colour="#20263d") +
geom_boxplot(notch = TRUE, width=0.1)+
stat_summary(fun = "mean", geom = "point", shape = 23, size = 3, fill = "#20263d")+
xlab("")+
theme_bw()+
theme(axis.title.y = element_text(face = "bold", size=10))
grid.arrange(p, p2, nrow = 2, top = textGrob("Descripción de areaconst", gp = gpar(fontface = "bold")))df <- data.table::copy(vivienda4)
counts <- table(df$tipo)
percentages <- prop.table(counts) * 100
mode_value <- names(which.max(counts))
result <- data.frame(Value = names(counts),
Mode = ifelse(names(counts) == mode_value, mode_value, NA),
Count = as.integer(counts),
Prc = round(percentages, 2))
result <- subset(result, select = c(Value, Mode, Count, Prc.Freq))
paged_table(result, options = list(rows.print = 15)) %>%
kable(caption = 'Variables Categóricas - Tipo')| Value | Mode | Count | Prc.Freq |
|---|---|---|---|
| Apartamento | Apartamento | 1363 | 79.89 |
| Casa | NA | 343 | 20.11 |
categorical_df <- subset(df, select = c(tipo))
for (i in names(categorical_df)) {
x <- categorical_df[, i]
if (i == 'barrio') {
counts <- table(x)
top_counts <- counts[order(-counts)][1:5]
barplot(top_counts, xlab = 'barrio', ylab = 'Frequencia', main = paste('Barplot', i), lwd = 2, col=c("#003049","#d62828", "#f77f00", "#fcbf49", "#eae2b7"))
} else {
barplot(table(x), xlab = i, ylab = 'Frequencia', main = paste('Barplot', i), lwd = 2, col=c("#003049", "#d62828", "#E75414", "#F77F00", "#FA9F25", "#FCBF49", "#F8C865", "#F3D180"))
}
}counts <- table(df$zona)
percentages <- prop.table(counts) * 100
mode_value <- names(which.max(counts))
result <- data.frame(Value = names(counts),
Mode = ifelse(names(counts) == mode_value, mode_value, NA),
Count = as.integer(counts),
Prc = round(percentages, 2))
result <- subset(result, select = c(Value, Mode, Count, Prc.Freq))
paged_table(result, options = list(rows.print = 15)) %>%
kable(caption = 'Variables Categóricas - Zona')| Value | Mode | Count | Prc.Freq |
|---|---|---|---|
| Zona Centro | NA | 8 | 0.47 |
| Zona Norte | NA | 288 | 16.88 |
| Zona Oeste | NA | 60 | 3.52 |
| Zona Oriente | NA | 6 | 0.35 |
| Zona Sur | Zona Sur | 1344 | 78.78 |
categorical_df <- subset(df, select = c(zona))
for (i in names(categorical_df)) {
x <- categorical_df[, i]
if (i == 'barrio') {
counts <- table(x)
top_counts <- counts[order(-counts)][1:5]
barplot(top_counts, xlab = 'barrio', ylab = 'Frequencia', main = paste('Barplot', i), lwd = 2, col=c("#003049","#d62828", "#f77f00", "#fcbf49", "#eae2b7"))
} else {
barplot(table(x), xlab = i, ylab = 'Frequencia', main = paste('Barplot', i), lwd = 2, col=c("#003049", "#6B2C39","#d62828", "#E75414", "#F77F00", "#FA9F25", "#FCBF49", "#F8C865", "#F3D180"))
}
}Al graficar preciom en función de areaconst
encontramos una acumulación de valores menos a 200 millones de pesos
para preciom y de áreas construidas menores a 100 \(m^{2}\). Por otro lado, para esta misma
región se aprecia una relación proporcional entre el precio y el área
pero después de 100 \(m^{2}\) la
tendencia no es tan marcada. Finalmente, se encuentra la acumulación de
datos para dos sectores, los primeros para áreas entre 59 a 61 \(m^2\) con precios de 160 a 162 millones de
pesos y una segunda región para áreas de 72 a 75 \(m^2\) con precios entre 215 a 226
millones.
# classic plot :
p <- ggplot(vivienda4, aes(x=areaconst, y=preciom)) +
geom_point(color="#20263d", alpha=0.2) +
#ggtitle("Área construida vs Precio")
theme_bw()+
theme(legend.position="none")
#plot.title = element_text(face = "bold", hjust=0.5))
p3 <- ggMarginal(p, margins, color="#20263d", size=7,
xparams = list(linewidth = 1),
yparams=list(linewidth=1))
# Create data
x <- vivienda4$areaconst
y <- vivienda4$preciom
# Create the hexbin object
bin <- hexbin(x, y, xbins=40)
# Convert the hexbin object to a data frame
bin_df <- data.frame (hcell2xy (bin), counts = bin@count)
# Plot the hexbin object using ggplot
p_hex <- ggplot(bin_df, aes(x=x, y=y, fill=counts)) +
geom_hex(stat="identity") +
theme_minimal() +
scale_fill_gradientn(colours = rev(brewer.pal(11,'Spectral'))) +
labs(x="areaconst", y="preciom", fill="Counts")
# Arrange the plots in a grid
grid.arrange(p3, p_hex, nrow = 1, top = textGrob("areaconst vs preciom", gp = gpar(fontface = "bold")))# classic plot
filtered_df <- vivienda4 %>%
filter(tipo == "Casa")
p_1 <- ggplot(filtered_df, aes(x=areaconst, y=preciom)) +
geom_point(alpha=0.2, size=2, color="#6ea16a") +
theme_bw()
p_1 <- p_1 +
annotate("text", x = 60, y = 750, label = "Casa", fontface =2)
filtered_df <- vivienda4 %>%
filter(tipo == "Apartamento")
# classic plot :
p_2 <- ggplot(filtered_df, aes(x=areaconst, y=preciom)) +
geom_point(alpha=0.2, size=2, color="#e3665d") +
theme_bw()
p_2 <- p_2 +
annotate("text", x = 65, y = 750, label = "Apartamento", fontface =2)
grid.arrange(p_1, p_2, nrow = 1, top = textGrob("areaconst vs preciom - tipo", gp = gpar(fontface = "bold")))filtered_df <- vivienda4 %>%
filter(zona == "Zona Centro")
p_1 <- ggplot(filtered_df, aes(x=areaconst, y=preciom, colour=tipo)) +
geom_point(alpha=0.5, size=2) +
ylim(c(0,800))+
xlim(c(40,200))+
theme_bw()+
theme(legend.position="none")
p_1 <- p_1 +
annotate("text", x = 65, y = 780, label = "Zona Centro", fontface =2)
filtered_df <- vivienda4 %>%
filter(zona == "Zona Norte")
# classic plot :
p_2 <- ggplot(filtered_df, aes(x=areaconst, y=preciom, colour=tipo)) +
geom_point(alpha=0.2, size=2) +
ylim(c(0,800))+
xlim(c(40,200))+
theme_bw()+
theme(legend.position="none")
p_2 <- p_2 +
annotate("text",x = 65, y = 780, label = "Zona Norte", fontface =2)
filtered_df <- vivienda4 %>%
filter(zona == "Zona Oeste")
# classic plot :
p_3 <- ggplot(filtered_df, aes(x=areaconst, y=preciom, colour=tipo)) +
geom_point(alpha=0.5, size=2) +
ylim(c(0,800))+
xlim(c(40,200))+
theme_bw()+
theme(legend.position="none")
p_3 <- p_3 +
annotate("text", x = 64, y = 780, label = "Zona Oeste", fontface =2)
filtered_df <- vivienda4 %>%
filter(zona == "Zona Oriente")
# classic plot :
p_4 <- ggplot(filtered_df, aes(x=areaconst, y=preciom, colour=tipo)) +
geom_point(alpha=0.5, size=2) +
ylim(c(0,800))+
xlim(c(40,200))+
theme_bw()+
theme(legend.position="none")
p_4 <- p_4 +
annotate("text", x = 67, y = 780, label = "Zona Oriente", fontface =2)
filtered_df <- vivienda4 %>%
filter(zona == "Zona Sur")
# classic plot :
p_5 <- ggplot(filtered_df, aes(x=areaconst, y=preciom, colour=tipo)) +
geom_point(alpha=0.2, size=2) +
ylim(c(0,800))+
xlim(c(40,200))+
theme_bw()+
theme(legend.position="none")
p_5 <- p_5 +
annotate("text",x = 60, y = 780, label = "Zona Sur", fontface =2)
grid.arrange(p_2, p_5, p_3, p_4, p_1, nrow = 3, top = textGrob("areaconst vs preciom - zona", gp = gpar(fontface = "bold")))suppressWarnings({
plt <- ggbetweenstats(
data = vivienda4,
x = tipo,
y = areaconst
)
plt_2 <- ggbetweenstats(
data = vivienda4,
x = zona,
y = areaconst
)
grid.arrange(plt, plt_2, nrow = 2, top = textGrob("areaconst vs Variables categóricas", gp = gpar(fontface = "bold")))})suppressWarnings({
plt <- ggbetweenstats(
data = vivienda4,
x = tipo,
y = preciom
)
plt_2 <- ggbetweenstats(
data = vivienda4,
x = zona,
y = preciom
)
grid.arrange(plt, plt_2, nrow = 2, top = textGrob("preciom vs Variables categóricas", gp = gpar(fontface = "bold")))})
p <- ggplot(vivienda4, aes(x=areaconst, y=preciom)) +
geom_point(color="#20263d", alpha=0.2) +
ggtitle("Área construida vs Precio")+
theme_bw()+
theme(legend.position="none")+
geom_smooth(
aes(color = "y ~ x", fill = "y ~ x"),
method = "lm",
formula = y~x,
se = TRUE,
fullrange = TRUE)
# Fit a linear model
fit <- lm(preciom ~ areaconst, data = vivienda4)
conf_int <- confint(fit, level=0.95)
ci_areaconst <- conf_int["areaconst", ]
r_squared <- summary(fit)$r.squared
spearman <- cor(vivienda4$areaconst, vivienda4$preciom, method = "spearman")
kendall <- cor(vivienda4$areaconst, vivienda4$preciom, method = "kendall")
# Get the coefficients of the linear model
intercept <- round(coef(fit)[1],3)
slope <- round(coef(fit)[2],3)
# Get the summary of the model
summary_fit <- summary(fit)
coefficients_table <- summary_fit$coefficients
coefficients_df <- as.data.frame(coefficients_table)
coefficients_df$`Pr(>|t|)` <- formatC(coefficients_df$`Pr(>|t|)`, format="e", digits=2)
coefficients_df$Estimate <- round(coefficients_df$Estimate, 3)
coefficients_df$`Std. Error` <- round(coefficients_df$`Std. Error`, 3)
coefficients_df$`t value` <- round(coefficients_df$`t value`, 3)
paged_table(coefficients_df, options = list(rows.print = 20)) %>%
kable(caption = 'Resumen coeficientes Modelo Base')| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 67.381 | 3.510 | 19.197 | 1.65e-74 |
| areaconst | 1.803 | 0.037 | 48.728 | 0.00e+00 |
# Get the R-squared value
cat(paste0("**Resultados**\n\n
- La función obtenida para el ajuste lineal es $preciom =", slope, "(areaconst)+", intercept, "$. En el caso de $β_{1}$ podemos decir que por cada $m^2$ de área construida se tiene un aumento de $", slope, "$ millones de pesos.\n\n
- En la tabla anterior podemos ver los valores de `t-value` y `Pr(>|t|)`. Para el caso de $β_{0}$ y $β_{1}$ encontramos valores menores a $2e-16$ por lo que para una nivel de significancia de 0.05 se niega la hipótesis nula. Esto nos permite concluir que ambos coeficientes son diferentes de 0.\n\n
- El valor del coeficiente de Pearson ($R^2$) es $", round(r_squared, 4), "$ indicando una relación lineal positiva moderada. Por otro lado los coeficientes de Spearman y Kendall son $", round(spearman,4),"$ y $", round(kendall,4), "$ respectivamente. De ambos, el más interesante es el coeficiente de Spearman que indica una relación monotónica entre las variables de `preciom` y `areaconst` por lo que podríamos explorar una ecuación polinómica para esta relación.\n\n
- Respecto al intervalo de confianza ($95$%) se encontró que este corresponde con: $", round(ci_areaconst[1], 4), "$ a $", round(ci_areaconst[2], 4), "$ lo que implica que el valor de la pendiente ($β_{1}$) y por tanto `preciom` por $m^2$ puede variar entre $", round(ci_areaconst[1], 4), "$ a $", round(ci_areaconst[2], 4), "$ millones de pesos."))Resultados
La función obtenida para el ajuste lineal es \(preciom =1.803(areaconst)+67.381\). En el caso de \(β_{1}\) podemos decir que por cada \(m^2\) de área construida se tiene un aumento de \(1.803\) millones de pesos.
En la tabla anterior podemos ver los valores de
t-value y Pr(>|t|). Para el caso de \(β_{0}\) y \(β_{1}\) encontramos valores menores a \(2e-16\) por lo que para una nivel de
significancia de 0.05 se niega la hipótesis nula. Esto nos permite
concluir que ambos coeficientes son diferentes de 0.
El valor del coeficiente de Pearson (\(R^2\)) es \(0.5822\) indicando una relación lineal
positiva moderada. Por otro lado los coeficientes de Spearman y Kendall
son \(0.8277\) y \(0.648\) respectivamente. De ambos, el más
interesante es el coeficiente de Spearman que indica una relación
monotónica entre las variables de preciom y
areaconst por lo que podríamos explorar una ecuación
polinómica para esta relación.
Respecto al intervalo de confianza (\(95\)%) se encontró que este corresponde
con: \(1.7304\) a \(1.8755\) lo que implica que el valor de la
pendiente (\(β_{1}\)) y por tanto
preciom por \(m^2\) puede
variar entre \(1.7304\) a \(1.8755\) millones de pesos.
p <- p +
annotate("text", x = 68, y = 720, label = paste0(" preciom = ", slope, " (areaconst) + ", intercept)) +
annotate("text", x = 49, y = 679, label = paste0(" R^2 = ", round(r_squared, 4)))
pcat(paste0("**Validación de supuestos**\n\n
- **Linealidad y Homoscedasticidad:** Los gráficos de Residuals vs Fitted y Scale - Location muestran a primera vista no linealidad y heterosquedasticidad respectivamente.\n\n
- **Normalidad:** El Normal Q-Q muestra que los puntos no siguen una distribución normal especialemente en la cola superior que puede deberse a la presencia de outliers o que los residuos tienen una distribución con colas más pesadas que la distribución normal.\n\n
- **Presencia de valores extremos y de alta influencia:** Los gráficos Result vs Leverage, Cook's distance y Cook's dist vs leverage permiten determinar los outliers y los puntos de alta influencia en el modelo cuando al ser incluidos o excluidos, estos alteran los resultados de la regresión. Estos valores, usualmente están relacionados con valores altos de residuales. En el gráfico de Result vs Leverage, todos los puntos están dentro de las líneas de distancia de Cook por lo que los datos parecen no mostrar puntos de alta influencia pero si se presentan outliers con índices 961, 957 y 1050.
"))Validación de supuestos
Linealidad y Homoscedasticidad: Los gráficos de Residuals vs Fitted y Scale - Location muestran a primera vista no linealidad y heterosquedasticidad respectivamente.
Normalidad: El Normal Q-Q muestra que los puntos no siguen una distribución normal especialemente en la cola superior que puede deberse a la presencia de outliers o que los residuos tienen una distribución con colas más pesadas que la distribución normal.
Presencia de valores extremos y de alta influencia: Los gráficos Result vs Leverage, Cook’s distance y Cook’s dist vs leverage permiten determinar los outliers y los puntos de alta influencia en el modelo cuando al ser incluidos o excluidos, estos alteran los resultados de la regresión. Estos valores, usualmente están relacionados con valores altos de residuales. En el gráfico de Result vs Leverage, todos los puntos están dentro de las líneas de distancia de Cook por lo que los datos parecen no mostrar puntos de alta influencia pero si se presentan outliers con índices 961, 957 y 1050.
test <- predict(fit, list(areaconst=110), interval="confidence" )
pred_value <- round(test[,"fit"],3)
lwr_value <- round(test[,"lwr"],3)
upr_value <- round(test[,"upr"],3)
cat(paste0("**Precio promedio estimado**\n\n
- El precio promedio estimado para un apartamento de $110 m^2$ es $", pred_value, "$ millones de pesos con un intervalo de confianza $",lwr_value,"$ - $",upr_value,"$ por lo que un apartamento en la misma zona a $200$ millones de pesos puede ser una buena oferta al ahorrar aproximadamente $66$ millones de pesos.\n\n
- Como consideraciones adicionales para éste conjunto de datos, debería tenerse en cuenta la Zona en la que está ubicado el apartamento dado que las zonas Norte, Sur, Oeste, Oriente y Centro poseen valores promedio de $204.59$, $201.71$, $200.77$, $262.50$ y $236.00$ millones de pesos respectivamente. Como variables adicionales, se puede pensar en cantidad de habitaciones, baños, si posee o no parqueadero, la antigüedad de la propiedad y si se ha renovado."))Precio promedio estimado
El precio promedio estimado para un apartamento de \(110 m^2\) es \(265.708\) millones de pesos con un intervalo de confianza \(262.611\) - \(268.805\) por lo que un apartamento en la misma zona a \(200\) millones de pesos puede ser una buena oferta al ahorrar aproximadamente \(66\) millones de pesos.
Como consideraciones adicionales para éste conjunto de datos, debería tenerse en cuenta la Zona en la que está ubicado el apartamento dado que las zonas Norte, Sur, Oeste, Oriente y Centro poseen valores promedio de \(204.59\), \(201.71\), \(200.77\), \(262.50\) y \(236.00\) millones de pesos respectivamente. Como variables adicionales, se puede pensar en cantidad de habitaciones, baños, si posee o no parqueadero, la antigüedad de la propiedad y si se ha renovado.
Shapiro-Wilk normality test
data: fit$residuals W = 0.92671, p-value < 0.00000000000000022
Jarque-Bera test for normality
data: fit$residuals JB = 3069.4, p-value < 0.00000000000000022
Anderson-Darling normality test
data: fit$residuals A = 22.455, p-value < 0.00000000000000022
Lilliefors (Kolmogorov-Smirnov) normality test
data: fit$residuals D = 0.077968, p-value < 0.00000000000000022
studentized Breusch-Pagan test
data: fit BP = 152.8, df = 1, p-value < 0.00000000000000022
Goldfeld-Quandt test
data: fit GQ = 0.78143, df1 = 851, df2 = 851, p-value = 0.9998 alternative hypothesis: variance increases from segment 1 to 2
Durbin-Watson test
data: fit DW = 1.6713, p-value = 0.000000000005124 alternative hypothesis: true autocorrelation is greater than 0
preciomsuppressWarnings({
fit <- lm(preciom ~ areaconst, data = vivienda4)
bc <- boxcox(fit, lambda = seq(-2, 5, 0.05), plotit=FALSE)
bc_data <- data.frame(
lambda = bc$x,
log_likelihood = bc$y)
# The optimal lambda is the one that maximizes the log-likelihood
lambda_optimal <- bc$x[which.max(bc$y)]
vivienda4$preciom_transformed <- (vivienda4$preciom^lambda_optimal - 1) / lambda_optimal
fit_transformed <- lm(preciom_transformed ~ areaconst, data = vivienda4)
summary_fit <- summary(fit_transformed)
coefficients_table <- summary_fit$coefficients
coefficients_df <- as.data.frame(coefficients_table)
coefficients_df$`Pr(>|t|)` <- formatC(coefficients_df$`Pr(>|t|)`, format="e", digits=2)
coefficients_df$Estimate <- round(coefficients_df$Estimate, 3)
coefficients_df$`Std. Error` <- round(coefficients_df$`Std. Error`, 3)
coefficients_df$`t value` <- round(coefficients_df$`t value`, 3)
paged_table(coefficients_df, options = list(rows.print = 20)) %>%
kable(caption = 'Resumen coeficientes Modelo Box-Cox')})| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 3.777 | 0.008 | 450.118 | 0.00e+00 |
| areaconst | 0.004 | 0.000 | 47.127 | 4.71e-311 |
# Plot lambda vs log_likelihood
p<-ggplot(bc_data, aes(x = lambda, y = log_likelihood)) +
geom_line(color = "gray", linewidth=1) +
geom_point(aes(x = lambda_optimal, y = max(log_likelihood)), color = "red", size=3) +
geom_vline(aes(xintercept = lambda_optimal), linetype="dashed", color = "black") +
labs(title = TeX("$\\lambda$ vs Log-Likelihood"),
x = TeX("$\\lambda$"),
y = "Log-Likelihood") +
theme_bw()
p <- p +
annotate("text", x = 3.6, y = -3960, label = paste0("Optimal Lambda: ", lambda_optimal))
p## Warning in geom_point(aes(x = lambda_optimal, y = max(log_likelihood)), : All aesthetics have length 1, but the data has 141 rows.
## ℹ Did you mean to use `annotate()`?
suppressWarnings({
p <- ggplot(vivienda4, aes(x=areaconst, y=preciom_transformed)) +
geom_point(color="#20263d", alpha=0.2) +
ggtitle("Área construida vs Precio transformado")+
theme_bw()+
theme(legend.position="none")+
stat_smooth(method='lm', formula = y ~ x, se=TRUE, level=0.95)
conf_int <- confint(fit_transformed, level=0.95)
ci_areaconst <- conf_int["areaconst", ]
# Get the coefficients of the linear model
intercept <- round(coef(fit_transformed)[1],3)
slope <- round(coef(fit_transformed)[2],3)
# Get the R-squared value
r_squared <- summary(fit_transformed)$r.squared
x <- vivienda4$areaconst
y <- vivienda4$preciom_transformed
cat(paste0("**Resultados**\n\n
- La función obtenida para el ajuste lineal es $preciom\\_transformed =", slope, "(areaconst)+", intercept, "$. En el caso de $β_{1}$ podemos decir que por cada $m^2$ de área construida se tiene un aumento de $", round((lambda_optimal * slope + 1)^(1/lambda_optimal)),4), "$ millones de pesos.\n\n
- En la tabla anterior podemos ver los valores de `t-value` y `Pr(>|t|)`. Para el caso de $β_{0}$ y $β_{1}$ encontramos valores menores a $2e-16$ por lo que para una nivel de significancia de 0.05 se niega la hipótesis nula. Esto nos permite concluir que ambos coeficientes son diferentes de 0.\n\n
- El valor del coeficiente de Pearson ($R^2$) es ", round(r_squared, 4), " indicando una relación lineal positiva moderada.\n\n
- Respecto al intervalo de confianza ($95$%) se encontró que este corresponde con: ", round(ci_areaconst[1], 4), " a ", round(ci_areaconst[2], 4), " lo que implica que el valor de la pendiente ($β_{1}$) y por tanto `preciom` por $m^2$ puede variar entre ", round((lambda_optimal * ci_areaconst[1] + 1)^(1/lambda_optimal), 4), " a ", round((lambda_optimal * ci_areaconst[2] + 1)^(1/lambda_optimal), 4), " millones de pesos.")
p <- p +
annotate("text", x = 79, y = 4.85, label = paste0(" preciom_transformed = ", slope, " (areaconst) + ", intercept)) +
annotate("text", x = 72, y = 4.75, label = TeX("preciom=($\\lambda\\cdot$ preciom_transformed+ 1)$^{1/\\lambda}$"))+
#preciom_prediction <- (lambda_optimal * prediction + 1)^(1/lambda_optimal)
annotate("text", x = 49, y = 4.65, label = paste0(" R^2 = ", round(r_squared, 4)))})Resultados
La función obtenida para el ajuste lineal es \(preciom\_transformed =0.004(areaconst)+3.777\). En el caso de \(β_{1}\) podemos decir que por cada \(m^2\) de área construida se tiene un aumento de $14 $ millones de pesos.
En la tabla anterior podemos ver los valores de
t-value y Pr(>|t|). Para el caso de \(β_{0}\) y \(β_{1}\) encontramos valores menores a \(2e-16\) por lo que para una nivel de
significancia de 0.05 se niega la hipótesis nula. Esto nos permite
concluir que ambos coeficientes son diferentes de 0.
El valor del coeficiente de Pearson (\(R^2\)) es 0.5659 indicando una relación lineal positiva moderada.
Respecto al intervalo de confianza (\(95\)%) se encontró que este corresponde
con: 0.004 a 0.0043 lo que implica que el valor de la pendiente (\(β_{1}\)) y por tanto preciom
por \(m^2\) puede variar entre 1.004 a
1.0044 millones de pesos.
## Warning in is.na(x): is.na() aplicado a un objeto que no es (lista o vector) de
## tipo 'expression
cat(paste0("**Validación de supuestos**\n\n
- **Linealidad y Homoscedasticidad:** Los gráficos de Residuals vs Fitted y Scale - Location muestran a primera vista no linealidad y heterosquedasticidad respectivamente.\n\n
- **Normalidad:** El Normal Q-Q muestra que los puntos siguen una distribución más cercana a la normal respecto al anterior modelo.\n\n
- **Presencia de valores extremos y de alta influencia:**En el gráfico de Result vs Leverage, todos los puntos están dentro de las líneas de distancia de Cook por lo que los datos parecen no mostrar puntos de alta influencia pero si se presentan outliers con índices 531, 696 y 1562.
"))Validación de supuestos
Linealidad y Homoscedasticidad: Los gráficos de Residuals vs Fitted y Scale - Location muestran a primera vista no linealidad y heterosquedasticidad respectivamente.
Normalidad: El Normal Q-Q muestra que los puntos siguen una distribución más cercana a la normal respecto al anterior modelo.
Presencia de valores extremos y de alta influencia:En el gráfico de Result vs Leverage, todos los puntos están dentro de las líneas de distancia de Cook por lo que los datos parecen no mostrar puntos de alta influencia pero si se presentan outliers con índices 531, 696 y 1562.
Shapiro-Wilk normality test
data: fit_transformed$residuals W = 0.99384, p-value = 0.000001556
Jarque-Bera test for normality
data: fit_transformed$residuals JB = 7.6144, p-value = 0.033
Anderson-Darling normality test
data: fit_transformed$residuals A = 3.9255, p-value = 0.00000000089
Lilliefors (Kolmogorov-Smirnov) normality test
data: fit_transformed$residuals D = 0.048457, p-value = 0.0000000005191
studentized Breusch-Pagan test
data: fit_transformed BP = 39.604, df = 1, p-value = 0.0000000003111
Goldfeld-Quandt test
data: fit_transformed GQ = 0.81143, df1 = 851, df2 = 851, p-value = 0.9988 alternative hypothesis: variance increases from segment 1 to 2
Durbin-Watson test
data: fit_transformed DW = 1.4876, p-value < 0.00000000000000022 alternative hypothesis: true autocorrelation is greater than 0
vivienda4$tipo <- as.factor(vivienda4$tipo)
vivienda4$zona <- as.factor(vivienda4$zona)
fit_transformed_3 <- lm(preciom_transformed ~ poly(areaconst,3)*tipo+tipo*zona, data = vivienda4)
r_squared <- summary(fit_transformed_3)$r.squaredsummary_fit <- summary(fit_transformed_3)
coefficients_table <- summary_fit$coefficients
coefficients_df <- as.data.frame(coefficients_table)
coefficients_df$`Pr(>|t|)` <- formatC(coefficients_df$`Pr(>|t|)`, format="e", digits=2)
coefficients_df$Estimate <- round(coefficients_df$Estimate, 3)
coefficients_df$`Std. Error` <- round(coefficients_df$`Std. Error`, 3)
coefficients_df$`t value` <- round(coefficients_df$`t value`, 3)
paged_table(coefficients_df, options = list(rows.print = 20)) %>%
kable(caption = 'Resumen coeficientes Modelo Box-Cox - Polinomio grado 3')| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 4.057 | 0.044 | 93.124 | 0.00e+00 |
| poly(areaconst, 3)1 | 6.402 | 0.260 | 24.633 | 9.91e-115 |
| poly(areaconst, 3)2 | -2.113 | 0.218 | -9.701 | 1.09e-21 |
| poly(areaconst, 3)3 | 1.345 | 0.184 | 7.326 | 3.66e-13 |
| tipoCasa | 0.384 | 0.124 | 3.100 | 1.97e-03 |
| zonaZona Norte | 0.069 | 0.044 | 1.547 | 1.22e-01 |
| zonaZona Oeste | 0.087 | 0.046 | 1.876 | 6.09e-02 |
| zonaZona Oriente | 0.145 | 0.092 | 1.574 | 1.16e-01 |
| zonaZona Sur | 0.088 | 0.044 | 1.997 | 4.60e-02 |
| poly(areaconst, 3)1:tipoCasa | -1.018 | 0.519 | -1.964 | 4.97e-02 |
| poly(areaconst, 3)2:tipoCasa | 0.124 | 0.450 | 0.276 | 7.83e-01 |
| poly(areaconst, 3)3:tipoCasa | -0.284 | 0.316 | -0.898 | 3.69e-01 |
| tipoCasa:zonaZona Norte | -0.368 | 0.124 | -2.969 | 3.03e-03 |
| tipoCasa:zonaZona Oeste | -0.142 | 0.130 | -1.086 | 2.77e-01 |
| tipoCasa:zonaZona Oriente | -0.469 | 0.158 | -2.971 | 3.01e-03 |
| tipoCasa:zonaZona Sur | -0.345 | 0.123 | -2.802 | 5.13e-03 |
suppressMessages(suppressWarnings(library(ggplot2)))
newdata <- data.frame(
areaconst = seq(min(vivienda4$areaconst), max(vivienda4$areaconst), length.out = 100),
tipo = factor(levels(vivienda4$tipo)[1], levels = levels(vivienda4$tipo)), # replace with the level you want
zona = factor(levels(vivienda4$zona)[1], levels = levels(vivienda4$zona)) # replace with the level you want
)
predictions <- predict(fit_transformed_3, newdata = newdata, interval = "confidence")
newdata <- cbind(newdata, predictions)
ci_lower<- mean(newdata$lwr)
ci_upper<- mean(newdata$upr)
newdata <- tidyr::pivot_longer(newdata, c("fit", "lwr", "upr"), names_to = "type", values_to = "value")
suppressMessages(suppressWarnings({# Plot the original data and the predictions
p<-ggplot(vivienda4, aes(x = areaconst, y = preciom_transformed)) +
theme_bw()+
geom_point(aes(color = tipo, alpha=0.3)) +
geom_line(data = newdata, aes(y = value, linetype = type)) +
labs(title = "Modelo ajustado con CI=0.95",
x = "areaconst",
y = "preciom_transformed",
color = "tipo",
linetype = "Intervalo") +
scale_alpha(guide = 'none')+
suppressWarnings({annotate("text", x = 94, y = 4.85, label = TeX("$preciom\\_transformed=poly(areaconst,3)\\cdot tipo+tipo \\cdot zona$"))}) +
suppressWarnings({annotate("text", x = 77, y = 4.75, label = TeX("preciom=($\\lambda\\cdot$ preciom_transformed+ 1)$^{1/\\lambda}$"))})+
annotate("text", x = 49, y = 4.65, label = paste0(" R^2 = ", round(r_squared, 4)))}))
# Get the summary of your model
summary_fit <- summary(fit_transformed_3)
# Get the coefficients from the summary
coefficients <- summary_fit$coefficients
# Create an empty string to store your equation
equation <- "preciom_transformed = "
# Loop through each coefficient
for (i in 1:nrow(coefficients)) {
# Get the coefficient value
coef_value <- coefficients[i, 1]
# Get the coefficient name
coef_name <- rownames(coefficients)[i]
# Add the coefficient to the equation string
if (i == 1) {
# If it's the intercept, just add the coefficient value
equation <- paste(equation, round(coef_value, 2))
} else {
# If it's not the intercept, add the coefficient name and value
equation <- paste(equation, " + (", round(coef_value, 2), ") * ", coef_name)
}
}cat(paste0("**Resultados**\n\n
- La función obtenida para el ajuste es:\n\n
preciom_transformed = 4.06 + ( 6.4 ) * poly(areaconst, 3)1 + ( -2.11 ) * poly(areaconst, 3)2 + ( 1.34 ) * poly(areaconst, 3)3 \\
+ ( 0.38 ) * tipoCasa + ( 0.07 ) * zonaZona Norte + ( 0.09 ) * zonaZona Oeste + ( 0.14 ) * zonaZona Oriente\\
+ ( 0.09 ) * zonaZona Sur + ( -1.02 ) * poly(areaconst, 3)1:tipoCasa + ( 0.12 ) * poly(areaconst, 3)2:tipoCasa\\
+ ( -0.28 ) * poly(areaconst, 3)3:tipoCasa + ( -0.37 ) * tipoCasa:zonaZona Norte + ( -0.14 ) * tipoCasa:zonaZona Oeste\\
+ ( -0.47 ) * tipoCasa:zonaZona Oriente + ( -0.34 ) * tipoCasa:zonaZona Sur
- En la tabla anterior podemos ver los valores de `t-value` y `Pr(>|t|)`. Los coeficientes con un nivel de significancia menor o igual a 0.05 son: poly(areaconst, 3)1, poly(areaconst, 3)2, poly(areaconst, 3)3, tipoCasa, zonaZona Norte, zonaZona Sur, poly(areaconst, 3)1:tipoCasa, tipoCasa:zonaZona Norte, tipoCasa:zonaZona Oriente y tipoCasa:zonaZona Sur.\n\n
- El valor del coeficiente de Pearson ($R^2$) es $", round(r_squared, 4), "$.\n\n
- Se resalta una mayor variación en CI=0.95 sobre las mayores áreas ($areaconst>=150$) que corresponden con viviendas de tipo casa."
))Resultados
preciom_transformed = 4.06 + ( 6.4 ) * poly(areaconst, 3)1 + ( -2.11
) * poly(areaconst, 3)2 + ( 1.34 ) * poly(areaconst, 3)3
+ ( 0.38 ) * tipoCasa + ( 0.07 ) * zonaZona Norte + ( 0.09 ) * zonaZona
Oeste + ( 0.14 ) * zonaZona Oriente
+ ( 0.09 ) * zonaZona Sur + ( -1.02 ) * poly(areaconst, 3)1:tipoCasa + (
0.12 ) * poly(areaconst, 3)2:tipoCasa
+ ( -0.28 ) * poly(areaconst, 3)3:tipoCasa + ( -0.37 ) *
tipoCasa:zonaZona Norte + ( -0.14 ) * tipoCasa:zonaZona Oeste
+ ( -0.47 ) * tipoCasa:zonaZona Oriente + ( -0.34 ) * tipoCasa:zonaZona
Sur
En la tabla anterior podemos ver los valores de
t-value y Pr(>|t|). Los coeficientes con un
nivel de significancia menor o igual a 0.05 son: poly(areaconst, 3)1,
poly(areaconst, 3)2, poly(areaconst, 3)3, tipoCasa, zonaZona Norte,
zonaZona Sur, poly(areaconst, 3)1:tipoCasa, tipoCasa:zonaZona Norte,
tipoCasa:zonaZona Oriente y tipoCasa:zonaZona Sur.
El valor del coeficiente de Pearson (\(R^2\)) es \(0.6797\).
Se resalta una mayor variación en CI=0.95 sobre las mayores áreas (\(areaconst>=150\)) que corresponden con viviendas de tipo casa.
## Warning in is.na(x): is.na() aplicado a un objeto que no es (lista o vector) de
## tipo 'expression
## Warning in is.na(x): is.na() aplicado a un objeto que no es (lista o vector) de
## tipo 'expression
El siguiente gráfico nos permite ver la influencia de las variables
categóricas tipo y zona sobre el modelo de
regresión. Para los valores con tipo==Casa se tienen
precios más elevados lo que concuerda con el análisis de precio vs tipo
en la sección de exploración de datos. Se puede apreciar el mismo efecto
aunque en menor magnitud para la variable zona, donde los
valores con zona==Zona Oriente y
zona==Zona Sur corresponderían con las viviendas de mayor
valor de acuerdo al modelo.
suppressWarnings({
a<-interact_plot(fit_transformed_3, pred=areaconst, modx=tipo, data = vivienda4, interval=TRUE)+
theme_bw()
b<-interact_plot(fit_transformed_3, pred=areaconst, modx=zona, data = vivienda4, interval=TRUE)+
theme_bw()
grid.arrange(a, b, nrow = 2, top = textGrob("Efecto de moderadores sobre regresión", gp = gpar(fontface = "bold")))})# Create the model validation plots
cat(paste0("**Validación de supuestos**\n\n
- **Linealidad y Homoscedasticidad:** Los gráficos de Residuals vs Fitted y Scale - Location muestran linealidad dado que los residuos se distribuyen homogéneamente a lo largo del eje x. \n\n
- **Normalidad:** El Normal Q-Q muestra que los puntos siguen una distribución cercana a la normal respecto al modelo original. La distribuición en las colas se aleja en poco grado de la normalidad.\n\n
- **Presencia de valores extremos y de alta influencia:**En el gráfico de Residuals vs Leverage, todos los puntos están dentro de las líneas de distancia de Cook por lo que los datos parecen no mostrar puntos de alta influencia pero si se presentan outliers con índices 393, 831 y 1689.
"))Validación de supuestos
Linealidad y Homoscedasticidad: Los gráficos de Residuals vs Fitted y Scale - Location muestran linealidad dado que los residuos se distribuyen homogéneamente a lo largo del eje x.
Normalidad: El Normal Q-Q muestra que los puntos siguen una distribución cercana a la normal respecto al modelo original. La distribuición en las colas se aleja en poco grado de la normalidad.
Presencia de valores extremos y de alta influencia:En el gráfico de Residuals vs Leverage, todos los puntos están dentro de las líneas de distancia de Cook por lo que los datos parecen no mostrar puntos de alta influencia pero si se presentan outliers con índices 393, 831 y 1689.
## Warning: Removed 267 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_segment()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).
##
## Shapiro-Wilk normality test
##
## data: fit_transformed_3$residuals
## W = 0.9896, p-value = 0.000000001066
##
## Jarque-Bera test for normality
##
## data: fit_transformed_3$residuals
## JB = 86.415, p-value < 0.00000000000000022
##
## Anderson-Darling normality test
##
## data: fit_transformed_3$residuals
## A = 4.2306, p-value = 0.0000000001631
##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: fit_transformed_3$residuals
## D = 0.042555, p-value = 0.0000001298
##
## studentized Breusch-Pagan test
##
## data: fit_transformed_3
## BP = 120.65, df = 15, p-value < 0.00000000000000022
##
## Goldfeld-Quandt test
##
## data: fit_transformed_3
## GQ = 0.78819, df1 = 837, df2 = 837, p-value = 0.9997
## alternative hypothesis: variance increases from segment 1 to 2
##
## Durbin-Watson test
##
## data: fit_transformed_3
## DW = 1.4975, p-value < 0.00000000000000022
## alternative hypothesis: true autocorrelation is greater than 0
# Fit the model with the transformed response variable and the categorical variables
newdata <- data.frame(areaconst = 100, tipo = "Apartamento", zona="Zona Sur")
levels(newdata$tipo) <- levels(vivienda4$tipo)
levels(newdata$zona) <- levels(vivienda4$zona)
prediction <- predict(fit_transformed_3, newdata)
preciom_prediction_1 <- (lambda_optimal * prediction + 1)^(1/lambda_optimal)
newdata <- data.frame(areaconst = 100, tipo = "Apartamento", zona="Zona Norte")
levels(newdata$tipo) <- levels(vivienda4$tipo)
levels(newdata$zona) <- levels(vivienda4$zona)
prediction <- predict(fit_transformed_3, newdata)
preciom_prediction_2 <- (lambda_optimal * prediction + 1)^(1/lambda_optimal)
newdata <- data.frame(areaconst = 100, tipo = "Apartamento", zona="Zona Oriente")
levels(newdata$tipo) <- levels(vivienda4$tipo)
levels(newdata$zona) <- levels(vivienda4$zona)
prediction <- predict(fit_transformed_3, newdata)
preciom_prediction_3 <- (lambda_optimal * prediction + 1)^(1/lambda_optimal)
newdata <- data.frame(areaconst = 100, tipo = "Apartamento", zona="Zona Centro")
levels(newdata$tipo) <- levels(vivienda4$tipo)
levels(newdata$zona) <- levels(vivienda4$zona)
prediction <- predict(fit_transformed_3, newdata)
preciom_prediction_4 <- (lambda_optimal * prediction + 1)^(1/lambda_optimal)
newdata <- data.frame(areaconst = 100, tipo = "Apartamento", zona="Zona Oeste")
levels(newdata$tipo) <- levels(vivienda4$tipo)
levels(newdata$zona) <- levels(vivienda4$zona)
prediction <- predict(fit_transformed_3, newdata)
preciom_prediction_5 <- (lambda_optimal * prediction + 1)^(1/lambda_optimal)
cat(paste0("**Precio promedio estimado**\n\n
- Costo para un 'Apartamento' con areaconst=100, Zona Oriente: ", round(preciom_prediction_3,3), ".\n
- Costo para un 'Apartamento' con areaconst=100, Zona Sur: ", round(preciom_prediction_1,3), ".\n
- Costo para un 'Apartamento' con areaconst=100, Zona Oeste: ", round(preciom_prediction_5,3), ".\n
- Costo para un 'Apartamento' con areaconst=100, Zona Norte: ", round(preciom_prediction_2,3), ".\n
- Costo para un 'Apartamento' con areaconst=100, Zona Centro: ", round(preciom_prediction_4,3), ".\n
"))Precio promedio estimado
Costo para un ‘Apartamento’ con areaconst=100, Zona Oriente: 291.11.
Costo para un ‘Apartamento’ con areaconst=100, Zona Sur: 263.235.
Costo para un ‘Apartamento’ con areaconst=100, Zona Oeste: 262.994.
Costo para un ‘Apartamento’ con areaconst=100, Zona Norte: 254.652.
Costo para un ‘Apartamento’ con areaconst=100, Zona Centro: 226.186.
modelo1=lm(preciom ~ areaconst, data=vivienda4) # Lin - Lin
modelo2=lm(preciom ~ log(areaconst), data=vivienda4) # Lin - Log
modelo3=lm(log(preciom) ~ areaconst, data=vivienda4) # Log - Lin
modelo4=lm(log(preciom) ~ log(areaconst), data=vivienda4) # Log - Log
modelo5=lm(preciom ~ poly(areaconst,3)*tipo+zona*tipo, data=vivienda4) # Lin - Lin
modelo6=lm(preciom ~ log(poly(areaconst,3))*tipo+zona*tipo, data=vivienda4) # Lin - Log## Warning in log(poly(areaconst, 3)): Se han producido NaNs
modelo7=lm(log(preciom) ~ poly(areaconst,3)*tipo+zona*tipo, data=vivienda4) # Log - Lin
modelo8=lm(log(preciom) ~ log(poly(areaconst,3))*tipo+zona*tipo, data=vivienda4) # Log - Log## Warning in log(poly(areaconst, 3)): Se han producido NaNs
##
## Please cite as:
## Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
## R package version 5.2.3. https://CRAN.R-project.org/package=stargazer
stargazer(modelo1, modelo2, modelo3, modelo4, ci=TRUE, ci.level=0.95, single.row=FALSE, type="text")##
## ===================================================================================================
## Dependent variable:
## -------------------------------------------------------------------
## preciom log(preciom)
## (1) (2) (3) (4)
## ---------------------------------------------------------------------------------------------------
## areaconst 1.803*** 0.007***
## (1.730, 1.875) (0.007, 0.007)
##
## log(areaconst) 189.708*** 0.780***
## (182.573, 196.844) (0.752, 0.808)
##
## Constant 67.381*** -610.083*** 4.724*** 1.919***
## (60.501, 74.260) (-641.609, -578.557) (4.696, 4.752) (1.796, 2.042)
##
## ---------------------------------------------------------------------------------------------------
## Observations 1,706 1,706 1,706 1,706
## R2 0.582 0.614 0.571 0.639
## Adjusted R2 0.582 0.614 0.571 0.638
## Residual Std. Error (df = 1704) 55.531 53.347 0.227 0.208
## F Statistic (df = 1; 1704) 2,374.452*** 2,715.248*** 2,267.022*** 3,012.086***
## ===================================================================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
stargazer(modelo5, modelo6, modelo7, modelo8, ci=TRUE, ci.level=0.95, single.row=FALSE, type="text")##
## ==================================================================================================================================================
## Dependent variable:
## ----------------------------------------------------------------------------------------------------------------
## preciom log(preciom)
## (1) (2) (3) (4)
## --------------------------------------------------------------------------------------------------------------------------------------------------
## poly(areaconst, 3)1 2,859.257*** 11.088***
## (2,630.776, 3,087.738) (10.211, 11.965)
##
## poly(areaconst, 3)2 -438.598*** -3.489***
## (-630.071, -247.125) (-4.224, -2.754)
##
## poly(areaconst, 3)3 326.532*** 2.216***
## (165.132, 487.932) (1.596, 2.835)
##
## log(poly(areaconst, 3))1 3,391,040.000 7,052.899
## (-3,179,895.000, 9,961,975.000) (-8,599.328, 22,705.130)
##
## log(poly(areaconst, 3))2 -1,100,867.000 -2,289.914
## (-3,224,580.000, 1,022,846.000) (-7,348.682, 2,768.855)
##
## log(poly(areaconst, 3))3 77,848.570 161.990
## (-70,017.330, 225,714.500) (-190.232, 514.213)
##
## tipoCasa 224.858*** -6,150,057.000 0.681*** -12,754.300
## (115.999, 333.716) (-18,207,310.000, 5,907,196.000) (0.264, 1.099) (-41,475.150, 15,966.550)
##
## zonaZona Norte 24.984 0.117
## (-13.952, 63.921) (-0.032, 0.266)
##
## zonaZona Oeste 34.602* 316.590*** 0.149* 0.660**
## (-6.187, 75.390) (113.945, 519.236) (-0.007, 0.306) (0.178, 1.143)
##
## zonaZona Oriente 53.682 0.248
## (-27.247, 134.612) (-0.063, 0.558)
##
## zonaZona Sur 31.687 41.429 0.149** 0.105
## (-6.855, 70.230) (-128.687, 211.544) (0.001, 0.297) (-0.301, 0.510)
##
## poly(areaconst, 3)1:tipoCasa -403.066* -1.740*
## (-859.019, 52.886) (-3.490, 0.010)
##
## poly(areaconst, 3)2:tipoCasa -211.894 0.120
## (-607.923, 184.134) (-1.400, 1.641)
##
## poly(areaconst, 3)3:tipoCasa 151.168 -0.381
## (-126.859, 429.196) (-1.448, 0.686)
##
## tipoCasa:zonaZona Norte -224.561*** -0.657***
## (-333.598, -115.524) (-1.075, -0.238)
##
## log(poly(areaconst, 3))1:tipoCasa -3,359,064.000 -6,967.900
## (-9,931,173.000, 3,213,046.000) (-22,622.920, 8,687.124)
##
## log(poly(areaconst, 3))2:tipoCasa 1,092,761.000 2,268.524
## (-1,031,242.000, 3,216,765.000) (-2,790.937, 7,327.984)
##
## log(poly(areaconst, 3))3:tipoCasa -77,737.300 -161.752
## (-225,608.700, 70,134.130) (-513.987, 190.484)
##
## tipoCasa:zonaZona Oeste -58.473 -0.245
## (-173.021, 56.074) (-0.685, 0.195)
##
## tipoCasa:zonaZona Oriente -275.076*** -0.833***
## (-413.734, -136.418) (-1.365, -0.301)
##
## tipoCasa:zonaZona Sur -211.942*** -17.370 -0.615*** -0.057
## (-320.132, -103.753) (-207.034, 172.293) (-1.031, -0.200) (-0.509, 0.395)
##
## Constant 194.642*** 6,213,171.000 5.209*** 12,927.220
## (156.343, 232.941) (-5,841,760.000, 18,268,102.000) (5.062, 5.356) (-15,788.100, 41,642.540)
##
## --------------------------------------------------------------------------------------------------------------------------------------------------
## Observations 1,706 58 1,706 58
## R2 0.645 0.280 0.679 0.266
## Adjusted R2 0.642 0.127 0.676 0.110
## Residual Std. Error 51.364 (df = 1690) 93.750 (df = 47) 0.197 (df = 1690) 0.223 (df = 47)
## F Statistic 205.135*** (df = 15; 1690) 1.829* (df = 10; 47) 238.372*** (df = 15; 1690) 1.705 (df = 10; 47)
## ==================================================================================================================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
En conclusión, el previo análisis para la inmobiliaria A&C nos permite obtener nuevas perspectivas sobre las dinámicas de precios para bienes inmuebles de estrato 4 en Cali. El análisis permitió encontrar que un modelo lineal ajustado a un polinomio de grado 3 e incorporando una transformación Box-Cox sobre la variable precio, resultó más efectivo para describir de la relación entre esta variable y el área construida.
El modelo, usó el área construida como principal predictor y se incluyeron términos de interacción para la zona y el tipo de vivienda (casa o apartamento), lo que permite describir aproximadamente el 68% de la variación en el precio (\(R^2=0.6797\)). Los términos de interacción implican una paso de un coeficiente \(R^2\) del \(0.5822\) al \(0.6797\) y como se puede apreciar en las gráficas de interacción se observa un aumento del precio respecto a las categorías de Tipo=Casa y Zona=Oriente.
Finalmente, la tarea de regresión podría mejorar usando otras estrategias de modelado así como la recopilación de datos para inmuebles en zonas con muy pocos registros, además de la incorporación y transformación de variables adicionales que permitan capturar más efectivamente las dinámicas del mercado.
Filtro para evaluar sólo apartamentos.
filtered_data <- vivienda4 %>% filter(tipo == "Apartamento")
suppressWarnings({
plt <- ggbetweenstats(
data = filtered_data,
x = zona,
y = preciom
)
plt_2 <- ggbetweenstats(
data = filtered_data,
x = zona,
y = areaconst
)
grid.arrange(plt, plt_2, nrow = 2, top = textGrob("areaconst y precio vs zona", gp = gpar(fontface = "bold")))})p <- ggplot(filtered_data, aes(x=areaconst, y=preciom)) +
geom_point(color="#20263d", alpha=0.2) +
ggtitle("Área construida vs Precio")+
theme_bw()+
theme(legend.position="none")+
geom_smooth(
aes(color = "y ~ x", fill = "y ~ x"),
method = "lm",
formula = y~x,
se = TRUE,
fullrange = TRUE)
# Fit a linear model
fit <- lm(preciom ~ areaconst, data = filtered_data)
conf_int <- confint(fit, level=0.95)
ci_areaconst <- conf_int["areaconst", ]
r_squared <- summary(fit)$r.squared
spearman <- cor(filtered_data$areaconst, filtered_data$preciom, method = "spearman")
kendall <- cor(filtered_data$areaconst, filtered_data$preciom, method = "kendall")
# Get the coefficients of the linear model
intercept <- round(coef(fit)[1],3)
slope <- round(coef(fit)[2],3)
# Get the summary of the model
summary_fit <- summary(fit)
coefficients_table <- summary_fit$coefficients
coefficients_df <- as.data.frame(coefficients_table)
coefficients_df$`Pr(>|t|)` <- formatC(coefficients_df$`Pr(>|t|)`, format="e", digits=2)
coefficients_df$Estimate <- round(coefficients_df$Estimate, 3)
coefficients_df$`Std. Error` <- round(coefficients_df$`Std. Error`, 3)
coefficients_df$`t value` <- round(coefficients_df$`t value`, 3)
paged_table(coefficients_df, options = list(rows.print = 20)) %>%
kable(caption = 'Resumen coeficientes Modelo Base')| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 39.047 | 4.100 | 9.524 | 7.31e-21 |
| areaconst | 2.165 | 0.052 | 41.595 | 1.06e-244 |
p <- p +
annotate("text", x = 68, y = 720, label = paste0(" preciom = ", slope, " (areaconst) + ", intercept)) +
annotate("text", x = 49, y = 679, label = paste0(" R^2 = ", round(r_squared, 4)))
pmodelo1=lm(preciom ~ areaconst, data=filtered_data) # Lin - Lin
modelo2=lm(preciom ~ log(areaconst), data=filtered_data) # Lin - Log
modelo3=lm(log(preciom) ~ areaconst, data=filtered_data) # Log - Lin
modelo4=lm(log(preciom) ~ log(areaconst), data=filtered_data) # Log - Log
modelo5=lm(preciom ~ poly(areaconst,3)*zona, data=filtered_data) # Lin - Lin
modelo7=lm(log(preciom) ~ poly(areaconst,3)*zona, data=filtered_data) # Log - Lin
library(stargazer)
stargazer(modelo1, modelo2, modelo3, modelo4, ci=TRUE, ci.level=0.95, single.row=FALSE, type="text")##
## ===================================================================================================
## Dependent variable:
## -------------------------------------------------------------------
## preciom log(preciom)
## (1) (2) (3) (4)
## ---------------------------------------------------------------------------------------------------
## areaconst 2.165*** 0.009***
## (2.063, 2.267) (0.009, 0.010)
##
## log(areaconst) 195.419*** 0.882***
## (186.708, 204.130) (0.842, 0.921)
##
## Constant 39.047*** -635.532*** 4.551*** 1.484***
## (31.011, 47.082) (-672.953, -598.112) (4.513, 4.589) (1.313, 1.654)
##
## ---------------------------------------------------------------------------------------------------
## Observations 1,363 1,363 1,363 1,363
## R2 0.560 0.587 0.520 0.582
## Adjusted R2 0.559 0.587 0.519 0.582
## Residual Std. Error (df = 1361) 43.339 41.982 0.205 0.191
## F Statistic (df = 1; 1361) 1,730.157*** 1,933.199*** 1,473.424*** 1,894.288***
## ===================================================================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
##
## ================================================================================
## Dependent variable:
## -------------------------------------------
## preciom log(preciom)
## (1) (2)
## --------------------------------------------------------------------------------
## poly(areaconst, 3)1 2,054.332 7.803
## (-462.588, 4,571.251) (-3.449, 19.055)
##
## poly(areaconst, 3)2 973.395 2.676
## (-373.343, 2,320.134) (-3.344, 8.697)
##
## poly(areaconst, 3)3 460.180 1.092
## (-2,057.023, 2,977.382) (-10.162, 12.345)
##
## zonaZona Norte 24.838 0.119
## (-24.095, 73.771) (-0.100, 0.337)
##
## zonaZona Oeste 24.038 0.112
## (-26.415, 74.491) (-0.114, 0.337)
##
## zonaZona Oriente 6.881 0.127
## (-232.873, 246.634) (-0.945, 1.199)
##
## zonaZona Sur 30.338 0.145
## (-18.364, 79.040) (-0.072, 0.363)
##
## poly(areaconst, 3)1:zonaZona Norte -415.011 -0.479
## (-2,939.058, 2,109.037) (-11.763, 10.805)
##
## poly(areaconst, 3)2:zonaZona Norte -1,327.447* -4.883
## (-2,688.314, 33.420) (-10.966, 1.201)
##
## poly(areaconst, 3)3:zonaZona Norte -516.686 -0.926
## (-3,040.373, 2,007.001) (-12.208, 10.356)
##
## poly(areaconst, 3)1:zonaZona Oeste -1,629.250 -4.741
## (-4,323.193, 1,064.694) (-16.784, 7.303)
##
## poly(areaconst, 3)2:zonaZona Oeste -3,670.245*** -12.921***
## (-5,705.775, -1,634.714) (-22.021, -3.821)
##
## poly(areaconst, 3)3:zonaZona Oeste -1,825.685 -5.059
## (-4,544.351, 892.981) (-17.213, 7.095)
##
## poly(areaconst, 3)1:zonaZona Oriente 5,151.248 18.521
## (-10,854.970, 21,157.470) (-53.036, 90.079)
##
## poly(areaconst, 3)2:zonaZona Oriente
##
##
## poly(areaconst, 3)3:zonaZona Oriente
##
##
## poly(areaconst, 3)1:zonaZona Sur -210.975 0.166
## (-2,729.753, 2,307.803) (-11.095, 11.426)
##
## poly(areaconst, 3)2:zonaZona Sur -1,316.567* -5.552*
## (-2,666.511, 33.376) (-11.587, 0.483)
##
## poly(areaconst, 3)3:zonaZona Sur -168.705 0.679
## (-2,687.799, 2,350.388) (-10.583, 11.941)
##
## Constant 173.281*** 5.125***
## (124.642, 221.919) (4.908, 5.343)
##
## --------------------------------------------------------------------------------
## Observations 1,363 1,363
## R2 0.602 0.613
## Adjusted R2 0.597 0.608
## Residual Std. Error (df = 1345) 41.444 0.185
## F Statistic (df = 17; 1345) 119.719*** 125.149***
## ================================================================================
## Note: *p<0.1; **p<0.05; ***p<0.01
** Comentario ** Se obtienen valores menores para \(R^2\) que los que se muestran para los modelos de la base de datos completa (Casa y Apartamentos).