La resistencia a la compresión del concreto es una de sus propiedades más importantes, ya que determina su capacidad para soportar cargas sin fallar. Predecir esta resistencia con precisión es crucial para garantizar la seguridad y durabilidad de las estructuras. En este análisis, utilizaremos técnicas de aprendizaje automático para desarrollar un modelo predictivo basado en la composición del concreto y su edad.
Objetivo: Desarrollar un modelo predictivo que estime la resistencia a la compresión del concreto (en MPa) basado en sus componentes y edad.
Aplicaciones:
Optimización de mezclas de concreto para cumplir con especificaciones de resistencia.
Reducción de costos al minimizar el uso excesivo de materiales.
Control de calidad en la producción de concreto.
Stakeholders:
Ingenieros civiles y de materiales
Empresas constructoras
Laboratorios de pruebas de materiales
# Cargar los datos
datos <- read_excel("Concrete_Data.xlsx")
# Ver las primeras filas
head(datos)
## # A tibble: 6 × 9
## Cement (component 1)(kg in a m…¹ Blast Furnace Slag (…² Fly Ash (component 3…³
## <dbl> <dbl> <dbl>
## 1 540 0 0
## 2 540 0 0
## 3 332. 142. 0
## 4 332. 142. 0
## 5 199. 132. 0
## 6 266 114 0
## # ℹ abbreviated names: ¹`Cement (component 1)(kg in a m^3 mixture)`,
## # ²`Blast Furnace Slag (component 2)(kg in a m^3 mixture)`,
## # ³`Fly Ash (component 3)(kg in a m^3 mixture)`
## # ℹ 6 more variables: `Water (component 4)(kg in a m^3 mixture)` <dbl>,
## # `Superplasticizer (component 5)(kg in a m^3 mixture)` <dbl>,
## # `Coarse Aggregate (component 6)(kg in a m^3 mixture)` <dbl>,
## # `Fine Aggregate (component 7)(kg in a m^3 mixture)` <dbl>, …
# Cambiar nombres para simplificar
colnames(datos) <- c("cement", "slag", "flyash", "water", "superplasticizer",
"coarseagg", "fineagg", "age", "strength")
# Revisar estructura
str(datos)
## tibble [1,030 × 9] (S3: tbl_df/tbl/data.frame)
## $ cement : num [1:1030] 540 540 332 332 199 ...
## $ slag : num [1:1030] 0 0 142 142 132 ...
## $ flyash : num [1:1030] 0 0 0 0 0 0 0 0 0 0 ...
## $ water : num [1:1030] 162 162 228 228 192 228 228 228 228 228 ...
## $ superplasticizer: num [1:1030] 2.5 2.5 0 0 0 0 0 0 0 0 ...
## $ coarseagg : num [1:1030] 1040 1055 932 932 978 ...
## $ fineagg : num [1:1030] 676 676 594 594 826 ...
## $ age : num [1:1030] 28 28 270 365 360 90 365 28 28 28 ...
## $ strength : num [1:1030] 80 61.9 40.3 41.1 44.3 ...
# Resumen estadístico
summary(datos)
## cement slag flyash water
## Min. :102.0 Min. : 0.0 Min. : 0.00 Min. :121.8
## 1st Qu.:192.4 1st Qu.: 0.0 1st Qu.: 0.00 1st Qu.:164.9
## Median :272.9 Median : 22.0 Median : 0.00 Median :185.0
## Mean :281.2 Mean : 73.9 Mean : 54.19 Mean :181.6
## 3rd Qu.:350.0 3rd Qu.:142.9 3rd Qu.:118.27 3rd Qu.:192.0
## Max. :540.0 Max. :359.4 Max. :200.10 Max. :247.0
## superplasticizer coarseagg fineagg age
## Min. : 0.000 Min. : 801.0 Min. :594.0 Min. : 1.00
## 1st Qu.: 0.000 1st Qu.: 932.0 1st Qu.:731.0 1st Qu.: 7.00
## Median : 6.350 Median : 968.0 Median :779.5 Median : 28.00
## Mean : 6.203 Mean : 972.9 Mean :773.6 Mean : 45.66
## 3rd Qu.:10.160 3rd Qu.:1029.4 3rd Qu.:824.0 3rd Qu.: 56.00
## Max. :32.200 Max. :1145.0 Max. :992.6 Max. :365.00
## strength
## Min. : 2.332
## 1st Qu.:23.707
## Median :34.443
## Mean :35.818
## 3rd Qu.:46.136
## Max. :82.599
# Verificar valores faltantes
colSums(is.na(datos))
## cement slag flyash water
## 0 0 0 0
## superplasticizer coarseagg fineagg age
## 0 0 0 0
## strength
## 0
# Escalar variables predictoras
datos_scaled <- datos%>%
mutate(across(cement:age, scale))
# Separar variables predictoras y respuesta
set.seed(123)
trainIndex <- createDataPartition(datos_scaled$strength, p = .8,
list = FALSE,
times = 1)
datos_train <- datos_scaled[trainIndex, ]
datos_test <- datos_scaled[-trainIndex, ]
# Modelo de regresión lineal
modelo_lm <- lm(strength ~ ., data = datos_train)
# Resumen del modelo
summary(modelo_lm)
##
## Call:
## lm(formula = strength ~ ., data = datos_train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -29.627 -6.263 0.612 6.634 32.598
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 35.7618 0.3624 98.688 < 2e-16 ***
## cement 12.2202 0.9867 12.385 < 2e-16 ***
## slag 8.7995 0.9733 9.041 < 2e-16 ***
## flyash 5.3298 0.8876 6.005 2.89e-09 ***
## water -2.8371 0.9624 -2.948 0.003289 **
## superplasticizer 2.2823 0.6253 3.650 0.000279 ***
## coarseagg 1.5968 0.8089 1.974 0.048714 *
## fineagg 1.5845 0.9576 1.655 0.098366 .
## age 7.5302 0.3921 19.203 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 10.41 on 817 degrees of freedom
## Multiple R-squared: 0.6182, Adjusted R-squared: 0.6145
## F-statistic: 165.4 on 8 and 817 DF, p-value: < 2.2e-16
# Predicciones sobre el conjunto de prueba
pred <- predict(modelo_lm, newdata = datos_test)
# Calcular RMSE
rmse <- sqrt(mean((pred - datos_test$strength)^2))
paste("RMSE:", round(rmse, 2))
## [1] "RMSE: 10.42"
# Comparación entre predicho y real
plot(pred, datos_test$strength,
xlab = "Predicción", ylab = "Valor Real",
main = "Predicción vs Real",
pch = 19, col = "darkblue")
abline(0, 1, col = "red", lwd = 2)
library(shiny)
library(readxl)
library(caret)
library(dplyr)
# Cargar y preparar los datos
datos <- read_excel("Concrete_Data.xlsx")
colnames(datos) <- c("cement", "slag", "flyash", "water", "superplasticizer",
"coarseagg", "fineagg", "age", "strength")
# Guardar medias y desv. estándar antes de escalar
escaladores <- datos %>%
summarise(across(cement:age, list(media = mean, sd = sd)))
# Escalar variables predictoras
datos_scaled <- datos %>%
mutate(across(cement:age, ~ (.-mean(.))/sd(.)))
# Separar entrenamiento
set.seed(123)
trainIndex <- createDataPartition(datos_scaled$strength, p = 0.8, list = FALSE)
datos_train <- datos_scaled[trainIndex, ]
# Entrenar modelo de regresión lineal
modelo_lm <- lm(strength ~ ., data = datos_train)
# UI
ui <- fluidPage(
titlePanel("Predicción de Resistencia del Concreto"),
sidebarLayout(
sidebarPanel(
numericInput("cement", "Cemento (kg/m³)", value = 540),
numericInput("slag", "Escoria (kg/m³)", value = 0),
numericInput("flyash", "Ceniza volante (kg/m³)", value = 0),
numericInput("water", "Agua (kg/m³)", value = 162),
numericInput("superplasticizer", "Superplastificante (kg/m³)", value = 2.5),
numericInput("coarseagg", "Agregado grueso (kg/m³)", value = 1040),
numericInput("fineagg", "Agregado fino (kg/m³)", value = 676),
numericInput("age", "Edad (días)", value = 28),
actionButton("predict", "Predecir resistencia")
),
mainPanel(
h3("Resultado"),
verbatimTextOutput("prediccion")
)
)
)
# Server
server <- function(input, output) {
observeEvent(input$predict, {
# Crear nuevo data frame con valores del usuario
nuevo <- data.frame(
cement = (input$cement - escaladores$cement_media)/escaladores$cement_sd,
slag = (input$slag - escaladores$slag_media)/escaladores$slag_sd,
flyash = (input$flyash - escaladores$flyash_media)/escaladores$flyash_sd,
water = (input$water - escaladores$water_media)/escaladores$water_sd,
superplasticizer = (input$superplasticizer - escaladores$superplasticizer_media)/escaladores$superplasticizer_sd,
coarseagg = (input$coarseagg - escaladores$coarseagg_media)/escaladores$coarseagg_sd,
fineagg = (input$fineagg - escaladores$fineagg_media)/escaladores$fineagg_sd,
age = (input$age - escaladores$age_media)/escaladores$age_sd
)
# Reordenar columnas para que coincidan con el modelo
nuevo <- nuevo[, names(modelo_lm$coefficients)[-1]]
# Predecir
pred <- predict(modelo_lm, newdata = nuevo)
output$prediccion <- renderText({
paste("La resistencia estimada del concreto es de", round(pred, 2), "MPa")
})
})
}
# Ejecutar app
shinyApp(ui = ui, server = server)
El modelo permite estimar la resistencia del concreto con un RMSE aceptable, lo que puede tener implicaciones importantes en control de calidad y optimización de materiales. Se recomienda experimentar con modelos más complejos (random forest, redes neuronales) para mejorar el rendimiento predictivo.