Análisis Predictivo de la Resistencia del Concreto

Introduccion

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.

Comprensión del Negocio

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

Comprensión de los Datos

# 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

Preparacion de los Datos

# 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, ]

Modelado

# 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

Evaluacion

# 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)

Despligue

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)
Shiny applications not supported in static R Markdown documents

Conclusion

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.