Objetivos de esta Sesión

Al finalizar este laboratorio, usted será capaz de:

  • Implementar bucles for para automatizar análisis de control de calidad por grupos
  • Utilizar bucles while para procesos iterativos de validación química
  • Aplicar condicionales if/else en contextos de clasificación industrial
  • Crear funciones que combinen sentencias iterativas para análisis eficientes
  • Desarrollar sistemas automatizados de control de calidad para producción química
  • Optimizar código usando vectorización cuando sea más apropiado que bucles
  • Interpretar resultados iterativos en el contexto de química industrial

📚 Contexto Industrial: Trabajaremos con datos reales de control de calidad de una planta química que produce ácidos, bases, fertilizantes y polímeros. Aprenderemos a automatizar procesos de validación que en la industria deben ejecutarse cientos de veces al día.

1 Introducción: La Importancia de la Iteración en Química Industrial

En la industria química, los procesos de control de calidad involucran la repetición sistemática de verificaciones. Imaginen una planta que produce 150 lotes diarios de diferentes productos químicos. Cada lote debe:

  • Validarse según especificaciones específicas por tipo de producto
  • Verificar múltiples parámetros de calidad
  • Clasificarse automáticamente como aceptable o rechazado
  • Generar reportes por turno, operador y producto

Las sentencias iterativas nos permiten automatizar estos procesos repetitivos, convirtiendo horas de trabajo manual en segundos de procesamiento automatizado.

“En la industria química moderna, la diferencia entre el éxito y el fracaso a menudo radica en la capacidad de procesar información rápidamente y tomar decisiones basadas en datos en tiempo real”

2 Carga y Exploración del Dataset Industrial

2.1 Carga de Datos de Control de Calidad

# Crear dataset de control de calidad (simulación de datos industriales reales)
n_lotes <- 150

# Generar datos realistas
datos_control <- data.frame(
  lote_id = sprintf("LOTE_%03d", 1:n_lotes),
  fecha_produccion = seq(as.Date("2025-01-02"), as.Date("2025-03-30"), length.out = n_lotes),
  turno = sample(c("Mañana", "Tarde", "Noche"), n_lotes, replace = TRUE, prob = c(0.4, 0.35, 0.25)),
  producto_tipo = sample(c("Acido_Sulfurico", "Acido_Nitrico", "Soda_Caustica", 
                          "Fertilizante_NPK", "Polimero_PVC"), 
                        n_lotes, replace = TRUE, prob = c(0.25, 0.2, 0.2, 0.2, 0.15)),
  operador_id = sprintf("OP_%03d", sample(1:25, n_lotes, replace = TRUE)),
  stringsAsFactors = FALSE
)

# Generar variables químicas específicas por tipo de producto
for (i in 1:n_lotes) {
  tipo <- datos_control$producto_tipo[i]
  
  # pH según tipo de producto
  if (tipo == "Acido_Sulfurico") {
    datos_control$ph_inicial[i] <- runif(1, 0.5, 1.5)
    datos_control$ph_final[i] <- runif(1, 0.8, 1.8)
  } else if (tipo == "Acido_Nitrico") {
    datos_control$ph_inicial[i] <- runif(1, 1.0, 2.0)
    datos_control$ph_final[i] <- runif(1, 1.2, 2.2)
  } else if (tipo == "Soda_Caustica") {
    datos_control$ph_inicial[i] <- runif(1, 12.5, 13.5)
    datos_control$ph_final[i] <- runif(1, 12.8, 13.8)
  } else if (tipo == "Fertilizante_NPK") {
    datos_control$ph_inicial[i] <- runif(1, 5.5, 7.5)
    datos_control$ph_final[i] <- runif(1, 6.0, 8.0)
  } else { # Polimero_PVC
    datos_control$ph_inicial[i] <- runif(1, 6.5, 8.5)
    datos_control$ph_final[i] <- runif(1, 7.0, 9.0)
  }
  
  # Temperatura según complejidad del proceso
  if (tipo %in% c("Acido_Sulfurico", "Acido_Nitrico")) {
    datos_control$temperatura_reaccion[i] <- runif(1, 65, 85)
  } else if (tipo == "Polimero_PVC") {
    datos_control$temperatura_reaccion[i] <- runif(1, 70, 80)
  } else {
    datos_control$temperatura_reaccion[i] <- runif(1, 45, 65)
  }
  
  # Presión y otras variables
  datos_control$presion_bar[i] <- runif(1, 1.2, 5.8)
  datos_control$concentracion_producto[i] <- runif(1, 15, 98)
  datos_control$tiempo_reaccion_horas[i] <- runif(1, 0.5, 12.0)
}

# Redondear valores para realismo
datos_control$ph_inicial <- round(datos_control$ph_inicial, 2)
datos_control$ph_final <- round(datos_control$ph_final, 2)
datos_control$temperatura_reaccion <- round(datos_control$temperatura_reaccion, 1)
datos_control$presion_bar <- round(datos_control$presion_bar, 2)
datos_control$concentracion_producto <- round(datos_control$concentracion_producto, 1)
datos_control$tiempo_reaccion_horas <- round(datos_control$tiempo_reaccion_horas, 2)

# Determinar si pasa control de calidad (lógica realista)
datos_control$control_calidad_pasa <- rep(TRUE, n_lotes)

# Simular algunos fallos de control de calidad
set.seed(456)
fallos_aleatorios <- sample(1:n_lotes, 12)
datos_control$control_calidad_pasa[fallos_aleatorios] <- FALSE

cat("=== DATASET DE CONTROL DE CALIDAD CARGADO ===\n")
#> === DATASET DE CONTROL DE CALIDAD CARGADO ===
cat("Dimensiones:", nrow(datos_control), "lotes ×", ncol(datos_control), "variables\n")
#> Dimensiones: 150 lotes × 12 variables
cat("Período:", min(datos_control$fecha_produccion), "a", max(datos_control$fecha_produccion), "\n")
#> Período: 20090 a 20177
cat("Productos:", paste(unique(datos_control$producto_tipo), collapse = ", "), "\n")
#> Productos: Acido_Nitrico, Fertilizante_NPK, Soda_Caustica, Acido_Sulfurico, Polimero_PVC
cat("Operadores únicos:", length(unique(datos_control$operador_id)), "\n")
#> Operadores únicos: 25

2.2 Inspección Inicial del Dataset

# Estructura del dataset
cat("=== ESTRUCTURA DEL DATASET ===\n")
#> === ESTRUCTURA DEL DATASET ===
str(datos_control)
#> 'data.frame':    150 obs. of  12 variables:
#>  $ lote_id               : chr  "LOTE_001" "LOTE_002" "LOTE_003" "LOTE_004" ...
#>  $ fecha_produccion      : Date, format: "2025-01-02" "2025-01-02" ...
#>  $ turno                 : chr  "Mañana" "Noche" "Tarde" "Noche" ...
#>  $ producto_tipo         : chr  "Acido_Nitrico" "Fertilizante_NPK" "Soda_Caustica" "Acido_Sulfurico" ...
#>  $ operador_id           : chr  "OP_010" "OP_017" "OP_017" "OP_021" ...
#>  $ ph_inicial            : num  1.69 6.23 12.96 0.95 0.81 ...
#>  $ ph_final              : num  1.99 6.36 12.88 1.57 0.84 ...
#>  $ temperatura_reaccion  : num  72.1 55.7 62.2 66.3 75.4 63.8 52 81.6 61 75.6 ...
#>  $ presion_bar           : num  2.89 3.52 3.02 4.95 4.32 4.36 5.01 2.47 2.97 3.9 ...
#>  $ concentracion_producto: num  38.8 93.4 76.1 40 90 52.1 59.4 77.5 42.2 70.3 ...
#>  $ tiempo_reaccion_horas : num  1.42 4.43 2.48 4.69 0.79 ...
#>  $ control_calidad_pasa  : logi  TRUE TRUE TRUE TRUE TRUE TRUE ...
cat("\n=== PRIMERAS 8 FILAS ===\n")
#> 
#> === PRIMERAS 8 FILAS ===
head(datos_control, 8)
cat("\n=== RESUMEN ESTADÍSTICO ===\n")
#> 
#> === RESUMEN ESTADÍSTICO ===
summary(datos_control[, c("ph_inicial", "ph_final", "temperatura_reaccion", 
                         "presion_bar", "concentracion_producto")])
#>    ph_inicial        ph_final      temperatura_reaccion  presion_bar   
#>  Min.   : 0.500   Min.   : 0.800   Min.   :45.10        Min.   :1.220  
#>  1st Qu.: 1.278   1st Qu.: 1.593   1st Qu.:56.33        1st Qu.:2.143  
#>  Median : 6.215   Median : 7.040   Median :67.25        Median :3.400  
#>  Mean   : 6.017   Mean   : 6.434   Mean   :66.04        Mean   :3.331  
#>  3rd Qu.: 8.220   3rd Qu.: 8.880   3rd Qu.:75.55        3rd Qu.:4.350  
#>  Max.   :13.490   Max.   :13.790   Max.   :83.80        Max.   :5.670  
#>  concentracion_producto
#>  Min.   :15.20         
#>  1st Qu.:39.85         
#>  Median :60.60         
#>  Mean   :59.85         
#>  3rd Qu.:79.78         
#>  Max.   :97.60
# Distribución por variables categóricas
cat("\n=== DISTRIBUCIÓN POR TURNO ===\n")
#> 
#> === DISTRIBUCIÓN POR TURNO ===
table(datos_control$turno)
#> 
#> Mañana  Noche  Tarde 
#>     58     39     53
cat("\n=== DISTRIBUCIÓN POR TIPO DE PRODUCTO ===\n")
#> 
#> === DISTRIBUCIÓN POR TIPO DE PRODUCTO ===
table(datos_control$producto_tipo)
#> 
#>    Acido_Nitrico  Acido_Sulfurico Fertilizante_NPK     Polimero_PVC 
#>               27               34               32               21 
#>    Soda_Caustica 
#>               36
cat("\n=== ESTADO GENERAL DE CONTROL DE CALIDAD ===\n")
#> 
#> === ESTADO GENERAL DE CONTROL DE CALIDAD ===
tabla_control <- table(datos_control$control_calidad_pasa)
prop_control <- prop.table(tabla_control) * 100
cat("Pasa control:", tabla_control["TRUE"], "(", round(prop_control["TRUE"], 1), "%)\n")
#> Pasa control: 138 ( 92 %)
cat("Falla control:", tabla_control["FALSE"], "(", round(prop_control["FALSE"], 1), "%)\n")
#> Falla control: 12 ( 8 %)

3 Bucles for: Análisis por Grupos

Concepto Clave: Los bucles for son ideales cuando conocemos exactamente cuántas iteraciones necesitamos realizar. En análisis de datos químicos, los usamos para procesar cada grupo de datos (por producto, por operador, por turno) de manera sistemática.

3.1 Ejemplo 1: Análisis de Control de Calidad por Tipo de Producto

# Obtener tipos únicos de productos
tipos_productos <- unique(datos_control$producto_tipo)
n_tipos <- length(tipos_productos)

cat("=== ANÁLISIS AUTOMATIZADO POR TIPO DE PRODUCTO ===\n")
#> === ANÁLISIS AUTOMATIZADO POR TIPO DE PRODUCTO ===
cat("Procesando", n_tipos, "tipos de productos usando bucle for...\n\n")
#> Procesando 5 tipos de productos usando bucle for...
# Crear contenedor para resultados
resultados_productos <- data.frame()

# BUCLE FOR: Analizar cada tipo de producto
for (i in 1:n_tipos) {
  tipo_actual <- tipos_productos[i]
  cat("Procesando:", tipo_actual, "(", i, "de", n_tipos, ")\n")
  cat(paste(rep("-", nchar(tipo_actual) + 20), collapse = ""), "\n")
  
  # Filtrar datos para este tipo
  datos_tipo <- datos_control[datos_control$producto_tipo == tipo_actual, ]
  n_lotes_tipo <- nrow(datos_tipo)
  
  # Calcular estadísticas específicas
  ph_promedio <- mean(datos_tipo$ph_final, na.rm = TRUE)
  temp_promedio <- mean(datos_tipo$temperatura_reaccion, na.rm = TRUE)
  presion_promedio <- mean(datos_tipo$presion_bar, na.rm = TRUE)
  concentracion_promedio <- mean(datos_tipo$concentracion_producto, na.rm = TRUE)
  
  # Control de calidad
  lotes_exitosos <- sum(datos_tipo$control_calidad_pasa)
  tasa_exito <- (lotes_exitosos / n_lotes_tipo) * 100
  
  # Mostrar resultados
  cat("Lotes procesados:", n_lotes_tipo, "\n")
  cat("pH final promedio:", round(ph_promedio, 2), "\n")
  cat("Temperatura promedio:", round(temp_promedio, 1), "°C\n")
  cat("Presión promedio:", round(presion_promedio, 2), "bar\n")
  cat("Concentración promedio:", round(concentracion_promedio, 1), "%\n")
  cat("Tasa de éxito:", round(tasa_exito, 1), "%\n")
  
  # Clasificar rendimiento
  if (tasa_exito >= 95) {
    rendimiento <- "Excelente"
  } else if (tasa_exito >= 85) {
    rendimiento <- "Bueno"
  } else if (tasa_exito >= 75) {
    rendimiento <- "Regular"
  } else {
    rendimiento <- "Deficiente"
  }
  cat("Clasificación:", rendimiento, "\n\n")
  
  # Guardar en tabla resumen
  fila_resultado <- data.frame(
    Producto = tipo_actual,
    N_Lotes = n_lotes_tipo,
    pH_Promedio = round(ph_promedio, 2),
    Temp_Promedio = round(temp_promedio, 1),
    Presion_Promedio = round(presion_promedio, 2),
    Concentracion_Promedio = round(concentracion_promedio, 1),
    Tasa_Exito_Pct = round(tasa_exito, 1),
    Clasificacion = rendimiento
  )
  
  resultados_productos <- rbind(resultados_productos, fila_resultado)
}
#> Procesando: Acido_Nitrico ( 1 de 5 )
#> --------------------------------- 
#> Lotes procesados: 27 
#> pH final promedio: 1.7 
#> Temperatura promedio: 76.4 °C
#> Presión promedio: 3.45 bar
#> Concentración promedio: 65 %
#> Tasa de éxito: 85.2 %
#> Clasificación: Bueno 
#> 
#> Procesando: Fertilizante_NPK ( 2 de 5 )
#> ------------------------------------ 
#> Lotes procesados: 32 
#> pH final promedio: 7.1 
#> Temperatura promedio: 54.6 °C
#> Presión promedio: 3.54 bar
#> Concentración promedio: 68.1 %
#> Tasa de éxito: 87.5 %
#> Clasificación: Bueno 
#> 
#> Procesando: Soda_Caustica ( 3 de 5 )
#> --------------------------------- 
#> Lotes procesados: 36 
#> pH final promedio: 13.3 
#> Temperatura promedio: 55.5 °C
#> Presión promedio: 2.97 bar
#> Concentración promedio: 57.9 %
#> Tasa de éxito: 97.2 %
#> Clasificación: Excelente 
#> 
#> Procesando: Acido_Sulfurico ( 4 de 5 )
#> ----------------------------------- 
#> Lotes procesados: 34 
#> pH final promedio: 1.25 
#> Temperatura promedio: 75 °C
#> Presión promedio: 3.45 bar
#> Concentración promedio: 56 %
#> Tasa de éxito: 91.2 %
#> Clasificación: Bueno 
#> 
#> Procesando: Polimero_PVC ( 5 de 5 )
#> -------------------------------- 
#> Lotes procesados: 21 
#> pH final promedio: 8.12 
#> Temperatura promedio: 73.9 °C
#> Presión promedio: 3.27 bar
#> Concentración promedio: 50.3 %
#> Tasa de éxito: 100 %
#> Clasificación: Excelente
# Mostrar tabla resumen final
cat("=== RESUMEN EJECUTIVO POR PRODUCTO ===\n")
#> === RESUMEN EJECUTIVO POR PRODUCTO ===
kable(resultados_productos, 
      caption = "Análisis Automatizado de Control de Calidad por Producto",
      align = "lccccccl")
Análisis Automatizado de Control de Calidad por Producto
Producto N_Lotes pH_Promedio Temp_Promedio Presion_Promedio Concentracion_Promedio Tasa_Exito_Pct Clasificacion
Acido_Nitrico 27 1.70 76.4 3.45 65.0 85.2 Bueno
Fertilizante_NPK 32 7.10 54.6 3.54 68.1 87.5 Bueno
Soda_Caustica 36 13.30 55.5 2.97 57.9 97.2 Excelente
Acido_Sulfurico 34 1.25 75.0 3.45 56.0 91.2 Bueno
Polimero_PVC 21 8.12 73.9 3.27 50.3 100.0 Excelente
Práctica: Bucle for para Análisis por Turno

Objetivo: Implementar un bucle for para analizar el rendimiento por turno de trabajo.

Instrucciones: 1. Crear un bucle que procese cada turno (Mañana, Tarde, Noche) 2. Calcular para cada turno: número de lotes, temperatura promedio, tasa de éxito 3. Identificar qué turno tiene mejor control de calidad 4. Determinar si hay diferencias significativas entre turnos

# SOLUCIÓN: Análisis por turno usando bucle for
turnos_unicos <- unique(datos_control$turno)
cat("=== ANÁLISIS POR TURNO DE TRABAJO ===\n\n")
#> === ANÁLISIS POR TURNO DE TRABAJO ===
resultados_turnos <- data.frame()

for (turno in turnos_unicos) {
  cat("Analizando turno:", turno, "\n")
  
  # Filtrar datos del turno
  datos_turno <- datos_control[datos_control$turno == turno, ]
  n_lotes <- nrow(datos_turno)
  
  # Calcular métricas
  temp_media <- mean(datos_turno$temperatura_reaccion)
  temp_variabilidad <- sd(datos_turno$temperatura_reaccion)
  cv_temperatura <- (temp_variabilidad / temp_media) * 100
  
  lotes_exitosos <- sum(datos_turno$control_calidad_pasa)
  tasa_exito <- (lotes_exitosos / n_lotes) * 100
  
  # Análisis de operadores únicos por turno
  operadores_turno <- length(unique(datos_turno$operador_id))
  
  cat("  Lotes procesados:", n_lotes, "\n")
  cat("  Temperatura media:", round(temp_media, 1), "°C\n")
  cat("  Variabilidad temperatura (CV):", round(cv_temperatura, 1), "%\n")
  cat("  Tasa de éxito:", round(tasa_exito, 1), "%\n")
  cat("  Operadores diferentes:", operadores_turno, "\n")
  
  # Evaluación de consistencia
  if (cv_temperatura < 10) {
    consistencia <- "Alta"
  } else if (cv_temperatura < 15) {
    consistencia <- "Media"
  } else {
    consistencia <- "Baja"
  }
  
  cat("  Consistencia de temperatura:", consistencia, "\n\n")
  
  # Guardar resultados
  fila_turno <- data.frame(
    Turno = turno,
    N_Lotes = n_lotes,
    Temp_Media = round(temp_media, 1),
    CV_Temperatura = round(cv_temperatura, 1),
    Tasa_Exito = round(tasa_exito, 1),
    N_Operadores = operadores_turno,
    Consistencia = consistencia
  )
  
  resultados_turnos <- rbind(resultados_turnos, fila_turno)
}
#> Analizando turno: Mañana 
#>   Lotes procesados: 58 
#>   Temperatura media: 67.7 °C
#>   Variabilidad temperatura (CV): 16.6 %
#>   Tasa de éxito: 89.7 %
#>   Operadores diferentes: 22 
#>   Consistencia de temperatura: Baja 
#> 
#> Analizando turno: Noche 
#>   Lotes procesados: 39 
#>   Temperatura media: 66.6 °C
#>   Variabilidad temperatura (CV): 18.2 %
#>   Tasa de éxito: 94.9 %
#>   Operadores diferentes: 17 
#>   Consistencia de temperatura: Baja 
#> 
#> Analizando turno: Tarde 
#>   Lotes procesados: 53 
#>   Temperatura media: 63.8 °C
#>   Variabilidad temperatura (CV): 16.9 %
#>   Tasa de éxito: 92.5 %
#>   Operadores diferentes: 24 
#>   Consistencia de temperatura: Baja
# Identificar mejor turno
mejor_turno <- resultados_turnos$Turno[which.max(resultados_turnos$Tasa_Exito)]
cat("CONCLUSIÓN: El turno con mejor rendimiento es:", mejor_turno, "\n")
#> CONCLUSIÓN: El turno con mejor rendimiento es: Noche
kable(resultados_turnos,
      caption = "Análisis de Rendimiento por Turno de Trabajo",
      align = "lcccccc")
Análisis de Rendimiento por Turno de Trabajo
Turno N_Lotes Temp_Media CV_Temperatura Tasa_Exito N_Operadores Consistencia
Mañana 58 67.7 16.6 89.7 22 Baja
Noche 39 66.6 18.2 94.9 17 Baja
Tarde 53 63.8 16.9 92.5 24 Baja

3.2 Ejemplo 2: Bucles Anidados para Análisis Cruzado

# BUCLES ANIDADOS: Análisis producto × turno
cat("=== ANÁLISIS CRUZADO: PRODUCTO × TURNO ===\n")
#> === ANÁLISIS CRUZADO: PRODUCTO × TURNO ===
cat("Implementando bucles anidados para análisis bidimensional...\n\n")
#> Implementando bucles anidados para análisis bidimensional...
# Crear matriz de resultados
matriz_resultados <- data.frame()

# BUCLE EXTERNO: Por cada producto
for (producto in tipos_productos) {
  
  # BUCLE INTERNO: Por cada turno
  for (turno in turnos_unicos) {
    
    # Filtrar datos para esta combinación específica
    datos_combinacion <- datos_control[
      datos_control$producto_tipo == producto & 
      datos_control$turno == turno, 
    ]
    
    n_lotes_comb <- nrow(datos_combinacion)
    
    if (n_lotes_comb > 0) {  # Solo procesar si hay datos
      # Calcular métricas
      ph_promedio <- mean(datos_combinacion$ph_final)
      temp_promedio <- mean(datos_combinacion$temperatura_reaccion)
      lotes_exitosos <- sum(datos_combinacion$control_calidad_pasa)
      tasa_exito <- (lotes_exitosos / n_lotes_comb) * 100
      
      # Crear fila de resultados
      fila <- data.frame(
        Producto = producto,
        Turno = turno,
        N_Lotes = n_lotes_comb,
        pH_Promedio = round(ph_promedio, 2),
        Temp_Promedio = round(temp_promedio, 1),
        Tasa_Exito = round(tasa_exito, 1)
      )
      
      matriz_resultados <- rbind(matriz_resultados, fila)
    }
  }
}

cat("Procesamiento completado. Total de combinaciones válidas:", nrow(matriz_resultados), "\n\n")
#> Procesamiento completado. Total de combinaciones válidas: 15
# Mostrar resultados ordenados por tasa de éxito
matriz_ordenada <- matriz_resultados[order(-matriz_resultados$Tasa_Exito), ]

cat("=== TOP 5 MEJORES COMBINACIONES PRODUCTO-TURNO ===\n")
#> === TOP 5 MEJORES COMBINACIONES PRODUCTO-TURNO ===
kable(head(matriz_ordenada, 5),
      caption = "Mejores Combinaciones de Producto y Turno",
      align = "llccccc")
Mejores Combinaciones de Producto y Turno
Producto Turno N_Lotes pH_Promedio Temp_Promedio Tasa_Exito
5 Fertilizante_NPK Noche 6 6.67 55.6 100
7 Soda_Caustica Mañana 11 13.22 56.1 100
8 Soda_Caustica Noche 11 13.42 53.8 100
12 Acido_Sulfurico Tarde 7 1.42 75.1 100
13 Polimero_PVC Mañana 12 8.14 73.9 100
# Identificar patrones
cat("\n=== IDENTIFICACIÓN DE PATRONES ===\n")
#> 
#> === IDENTIFICACIÓN DE PATRONES ===
mejor_combinacion <- matriz_ordenada[1, ]
cat("Mejor combinación:", mejor_combinacion$Producto, "en turno", mejor_combinacion$Turno, 
    "con", mejor_combinacion$Tasa_Exito, "% de éxito\n")
#> Mejor combinación: Fertilizante_NPK en turno Noche con 100 % de éxito
peor_combinacion <- matriz_ordenada[nrow(matriz_ordenada), ]
cat("Combinación a mejorar:", peor_combinacion$Producto, "en turno", peor_combinacion$Turno,
    "con", peor_combinacion$Tasa_Exito, "% de éxito\n")
#> Combinación a mejorar: Acido_Nitrico en turno Mañana con 80 % de éxito

4 Bucles while: Procesos Iterativos con Condición

Concepto Clave: Los bucles while se ejecutan mientras una condición sea verdadera. Son ideales para procesos de validación iterativa, detección de anomalías, y refinamiento progresivo de datos químicos donde no sabemos cuántas iteraciones necesitaremos.

4.1 Ejemplo 1: Control de Calidad Iterativo con while

# BUCLE WHILE: Refinamiento iterativo de criterios de calidad
cat("=== SISTEMA DE CONTROL DE CALIDAD ITERATIVO ===\n")
#> === SISTEMA DE CONTROL DE CALIDAD ITERATIVO ===
cat("Objetivo: Ajustar criterios hasta alcanzar 90% de lotes aprobados\n\n")
#> Objetivo: Ajustar criterios hasta alcanzar 90% de lotes aprobados
# Parámetros iniciales
datos_trabajo <- datos_control
criterio_ph_min <- 1.0
criterio_ph_max <- 13.0
criterio_temp_max <- 90.0
iteracion <- 0
max_iteraciones <- 10
objetivo_aprobacion <- 0.90  # 90% de lotes deben pasar

# Función para aplicar criterios de calidad
aplicar_criterios <- function(datos, ph_min, ph_max, temp_max) {
  cumple_ph <- datos$ph_final >= ph_min & datos$ph_final <= ph_max
  cumple_temp <- datos$temperatura_reaccion <= temp_max
  cumple_presion <- datos$presion_bar >= 1.0 & datos$presion_bar <= 6.0
  
  return(cumple_ph & cumple_temp & cumple_presion)
}

# BUCLE WHILE: Ajustar criterios iterativamente
tasa_aprobacion_actual <- 0
while (tasa_aprobacion_actual < objetivo_aprobacion && iteracion < max_iteraciones) {
  iteracion <- iteracion + 1
  cat("Iteración", iteracion, ":\n")
  
  # Aplicar criterios actuales
  lotes_aprueban <- aplicar_criterios(datos_trabajo, criterio_ph_min, 
                                      criterio_ph_max, criterio_temp_max)
  
  n_aprobados <- sum(lotes_aprueban)
  tasa_aprobacion_actual <- n_aprobados / nrow(datos_trabajo)
  
  cat("  Criterios: pH [", criterio_ph_min, ",", criterio_ph_max, 
      "], Temp ≤", criterio_temp_max, "°C\n")
  cat("  Lotes aprobados:", n_aprobados, "de", nrow(datos_trabajo), 
      "(", round(tasa_aprobacion_actual * 100, 1), "%)\n")
  
  if (tasa_aprobacion_actual < objetivo_aprobacion) {
    # Relajar criterios gradualmente
    if (iteracion %% 2 == 1) {
      # Iteraciones impares: ajustar pH
      criterio_ph_min <- max(0.5, criterio_ph_min - 0.2)
      criterio_ph_max <- min(14.0, criterio_ph_max + 0.2)
      cat("  Acción: Relajar criterios de pH\n")
    } else {
      # Iteraciones pares: ajustar temperatura
      criterio_temp_max <- min(100.0, criterio_temp_max + 2.0)
      cat("  Acción: Aumentar límite de temperatura\n")
    }
  }
  cat("\n")
}
#> Iteración 1 :
#>   Criterios: pH [ 1 , 13 ], Temp ≤ 90 °C
#>   Lotes aprobados: 111 de 150 ( 74 %)
#>   Acción: Relajar criterios de pH
#> 
#> Iteración 2 :
#>   Criterios: pH [ 0.8 , 13.2 ], Temp ≤ 90 °C
#>   Lotes aprobados: 129 de 150 ( 86 %)
#>   Acción: Aumentar límite de temperatura
#> 
#> Iteración 3 :
#>   Criterios: pH [ 0.8 , 13.2 ], Temp ≤ 92 °C
#>   Lotes aprobados: 129 de 150 ( 86 %)
#>   Acción: Relajar criterios de pH
#> 
#> Iteración 4 :
#>   Criterios: pH [ 0.6 , 13.4 ], Temp ≤ 92 °C
#>   Lotes aprobados: 137 de 150 ( 91.3 %)
# Resultado final
if (tasa_aprobacion_actual >= objetivo_aprobacion) {
  cat("ÉXITO: Criterios optimizados en", iteracion, "iteraciones\n")
  cat("Tasa final de aprobación:", round(tasa_aprobacion_actual * 100, 1), "%\n")
} else {
  cat("ADVERTENCIA: No se alcanzó el objetivo en", max_iteraciones, "iteraciones\n")
  cat("Tasa máxima alcanzada:", round(tasa_aprobacion_actual * 100, 1), "%\n")
}
#> ÉXITO: Criterios optimizados en 4 iteraciones
#> Tasa final de aprobación: 91.3 %
cat("Criterios finales optimizados:\n")
#> Criterios finales optimizados:
cat("- pH:", criterio_ph_min, "a", criterio_ph_max, "\n")
#> - pH: 0.6 a 13.4
cat("- Temperatura: ≤", criterio_temp_max, "°C\n")
#> - Temperatura: ≤ 92 °C
cat("- Presión: 1.0 a 6.0 bar\n")
#> - Presión: 1.0 a 6.0 bar

4.2 Ejemplo 2: Detección Iterativa de Lotes Problemáticos

# BUCLE WHILE: Identificar lotes con múltiples problemas
cat("=== DETECCIÓN ITERATIVA DE LOTES PROBLEMÁTICOS ===\n")
#> === DETECCIÓN ITERATIVA DE LOTES PROBLEMÁTICOS ===
cat("Buscando lotes que requieren atención especial...\n\n")
#> Buscando lotes que requieren atención especial...
# Crear copia de datos para análisis
datos_analisis <- datos_control
lotes_problematicos <- character(0)
rondas_analisis <- 0
max_rondas <- 5

# Criterios de problema (se van refinando)
tolerancia_ph <- 0.5
tolerancia_temp <- 5.0
min_concentracion <- 20.0

continuar_analisis <- TRUE

while (continuar_analisis && rondas_analisis < max_rondas) {
  rondas_analisis <- rondas_analisis + 1
  cat("Ronda de análisis", rondas_analisis, ":\n")
  cat("Tolerancias actuales: pH ±", tolerancia_ph, 
      ", Temp ±", tolerancia_temp, "°C, Conc ≥", min_concentracion, "%\n")
  
  problemas_encontrados <- 0
  
  # Buscar problemas específicos por tipo de producto
  for (i in 1:nrow(datos_analisis)) {
    lote <- datos_analisis[i, ]
    tipo <- lote$producto_tipo
    problemas_lote <- character(0)
    
    # Verificar pH según tipo
    if (tipo == "Acido_Sulfurico" && (lote$ph_final < 0.5 || lote$ph_final > 2.0)) {
      problemas_lote <- c(problemas_lote, "pH_fuera_rango")
    }
    if (tipo == "Soda_Caustica" && (lote$ph_final < 12.0 || lote$ph_final > 14.0)) {
      problemas_lote <- c(problemas_lote, "pH_fuera_rango")
    }
    
    # Verificar temperatura
    if (abs(lote$temperatura_reaccion - mean(datos_analisis$temperatura_reaccion)) > tolerancia_temp) {
      problemas_lote <- c(problemas_lote, "temperatura_atipica")
    }
    
    # Verificar concentración
    if (lote$concentracion_producto < min_concentracion) {
      problemas_lote <- c(problemas_lote, "concentracion_baja")
    }
    
    # Si tiene múltiples problemas, marcarlo
    if (length(problemas_lote) >= 2 && !lote$lote_id %in% lotes_problematicos) {
      lotes_problematicos <- c(lotes_problematicos, lote$lote_id)
      problemas_encontrados <- problemas_encontrados + 1
      cat("  PROBLEMA DETECTADO:", lote$lote_id, "-", paste(problemas_lote, collapse = ", "), "\n")
    }
  }
  
  cat("  Nuevos lotes problemáticos encontrados:", problemas_encontrados, "\n")
  cat("  Total acumulado de lotes problemáticos:", length(lotes_problematicos), "\n\n")
  
  # Decidir si continuar
  if (problemas_encontrados == 0) {
    continuar_analisis <- FALSE
    cat("No se encontraron nuevos problemas. Análisis completado.\n")
  } else {
    # Hacer criterios más estrictos para la próxima ronda
    tolerancia_ph <- tolerancia_ph * 0.8
    tolerancia_temp <- tolerancia_temp * 0.9
    min_concentracion <- min_concentracion + 2.0
  }
}
#> Ronda de análisis 1 :
#> Tolerancias actuales: pH ± 0.5 , Temp ± 5 °C, Conc ≥ 20 %
#>   PROBLEMA DETECTADO: LOTE_029 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_067 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_069 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_134 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_147 - temperatura_atipica, concentracion_baja 
#>   Nuevos lotes problemáticos encontrados: 5 
#>   Total acumulado de lotes problemáticos: 5 
#> 
#> Ronda de análisis 2 :
#> Tolerancias actuales: pH ± 0.4 , Temp ± 4.5 °C, Conc ≥ 22 %
#>   PROBLEMA DETECTADO: LOTE_110 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_114 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_124 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_135 - temperatura_atipica, concentracion_baja 
#>   Nuevos lotes problemáticos encontrados: 4 
#>   Total acumulado de lotes problemáticos: 9 
#> 
#> Ronda de análisis 3 :
#> Tolerancias actuales: pH ± 0.32 , Temp ± 4.05 °C, Conc ≥ 24 %
#>   PROBLEMA DETECTADO: LOTE_023 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_050 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_064 - temperatura_atipica, concentracion_baja 
#>   Nuevos lotes problemáticos encontrados: 3 
#>   Total acumulado de lotes problemáticos: 12 
#> 
#> Ronda de análisis 4 :
#> Tolerancias actuales: pH ± 0.256 , Temp ± 3.645 °C, Conc ≥ 26 %
#>   PROBLEMA DETECTADO: LOTE_051 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_121 - temperatura_atipica, concentracion_baja 
#>   Nuevos lotes problemáticos encontrados: 2 
#>   Total acumulado de lotes problemáticos: 14 
#> 
#> Ronda de análisis 5 :
#> Tolerancias actuales: pH ± 0.2048 , Temp ± 3.2805 °C, Conc ≥ 28 %
#>   PROBLEMA DETECTADO: LOTE_045 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_072 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_090 - temperatura_atipica, concentracion_baja 
#>   PROBLEMA DETECTADO: LOTE_112 - temperatura_atipica, concentracion_baja 
#>   Nuevos lotes problemáticos encontrados: 4 
#>   Total acumulado de lotes problemáticos: 18
cat("=== RESUMEN DE LOTES PROBLEMÁTICOS ===\n")
#> === RESUMEN DE LOTES PROBLEMÁTICOS ===
cat("Total de lotes que requieren atención:", length(lotes_problematicos), "\n")
#> Total de lotes que requieren atención: 18
cat("Porcentaje del total:", round((length(lotes_problematicos) / nrow(datos_control)) * 100, 1), "%\n")
#> Porcentaje del total: 12 %
if (length(lotes_problematicos) > 0) {
  cat("Lotes identificados:", paste(head(lotes_problematicos, 10), collapse = ", "))
  if (length(lotes_problematicos) > 10) {
    cat(", ... y", length(lotes_problematicos) - 10, "más")
  }
  cat("\n")
}
#> Lotes identificados: LOTE_029, LOTE_067, LOTE_069, LOTE_134, LOTE_147, LOTE_110, LOTE_114, LOTE_124, LOTE_135, LOTE_023, ... y 8 más
Práctica: Bucle while para Optimización

Escenario: La planta necesita optimizar el tiempo de reacción para maximizar la concentración del producto manteniendo la calidad.

Tarea: Implementar un bucle while que: 1. Comience con un tiempo mínimo de reacción 2. Incremente gradualmente el tiempo hasta encontrar el óptimo 3. El óptimo es cuando se maximiza concentración × tasa_éxito 4. Se detenga cuando no haya mejora en 3 iteraciones consecutivas

# SOLUCIÓN: Optimización del tiempo de reacción
cat("=== OPTIMIZACIÓN DE TIEMPO DE REACCIÓN ===\n")
#> === OPTIMIZACIÓN DE TIEMPO DE REACCIÓN ===
# Parámetros iniciales
tiempo_actual <- 1.0  # horas
mejor_score <- 0
mejor_tiempo <- tiempo_actual
iteraciones_sin_mejora <- 0
max_sin_mejora <- 3
iteracion_opt <- 0

cat("Buscando tiempo óptimo de reacción...\n\n")
#> Buscando tiempo óptimo de reacción...
while (iteraciones_sin_mejora < max_sin_mejora && tiempo_actual <= 10.0) {
  iteracion_opt <- iteracion_opt + 1
  
  # Filtrar lotes con tiempo similar al actual (±0.5 horas)
  lotes_similares <- datos_control[
    abs(datos_control$tiempo_reaccion_horas - tiempo_actual) <= 0.5, 
  ]
  
  if (nrow(lotes_similares) >= 5) {  # Necesitamos muestra mínima
    # Calcular métricas
    concentracion_media <- mean(lotes_similares$concentracion_producto)
    tasa_exito <- mean(lotes_similares$control_calidad_pasa)
    score_actual <- concentracion_media * tasa_exito
    
    cat("Iteración", iteracion_opt, "- Tiempo:", tiempo_actual, "horas\n")
    cat("  Lotes en muestra:", nrow(lotes_similares), "\n")
    cat("  Concentración media:", round(concentracion_media, 1), "%\n")
    cat("  Tasa de éxito:", round(tasa_exito * 100, 1), "%\n")
    cat("  Score combinado:", round(score_actual, 2), "\n")
    
    # Verificar si es mejor
    if (score_actual > mejor_score) {
      mejor_score <- score_actual
      mejor_tiempo <- tiempo_actual
      iteraciones_sin_mejora <- 0
      cat("  ¡NUEVA MEJOR SOLUCIÓN!\n")
    } else {
      iteraciones_sin_mejora <- iteraciones_sin_mejora + 1
      cat("  Sin mejora (", iteraciones_sin_mejora, "/", max_sin_mejora, ")\n")
    }
  } else {
    cat("Iteración", iteracion_opt, "- Tiempo:", tiempo_actual, 
        "horas - Muestra insuficiente (", nrow(lotes_similares), "lotes)\n")
  }
  
  cat("\n")
  tiempo_actual <- tiempo_actual + 0.5
}
#> Iteración 1 - Tiempo: 1 horas
#>   Lotes en muestra: 17 
#>   Concentración media: 62.7 %
#>   Tasa de éxito: 82.4 %
#>   Score combinado: 51.65 
#>   ¡NUEVA MEJOR SOLUCIÓN!
#> 
#> Iteración 2 - Tiempo: 1.5 horas
#>   Lotes en muestra: 16 
#>   Concentración media: 62.6 %
#>   Tasa de éxito: 87.5 %
#>   Score combinado: 54.76 
#>   ¡NUEVA MEJOR SOLUCIÓN!
#> 
#> Iteración 3 - Tiempo: 2 horas
#>   Lotes en muestra: 12 
#>   Concentración media: 72.4 %
#>   Tasa de éxito: 100 %
#>   Score combinado: 72.42 
#>   ¡NUEVA MEJOR SOLUCIÓN!
#> 
#> Iteración 4 - Tiempo: 2.5 horas
#>   Lotes en muestra: 13 
#>   Concentración media: 62.6 %
#>   Tasa de éxito: 84.6 %
#>   Score combinado: 53.01 
#>   Sin mejora ( 1 / 3 )
#> 
#> Iteración 5 - Tiempo: 3 horas
#>   Lotes en muestra: 15 
#>   Concentración media: 56.2 %
#>   Tasa de éxito: 86.7 %
#>   Score combinado: 48.74 
#>   Sin mejora ( 2 / 3 )
#> 
#> Iteración 6 - Tiempo: 3.5 horas
#>   Lotes en muestra: 19 
#>   Concentración media: 55.9 %
#>   Tasa de éxito: 100 %
#>   Score combinado: 55.89 
#>   Sin mejora ( 3 / 3 )
cat("=== RESULTADO DE OPTIMIZACIÓN ===\n")
#> === RESULTADO DE OPTIMIZACIÓN ===
cat("Tiempo óptimo encontrado:", mejor_tiempo, "horas\n")
#> Tiempo óptimo encontrado: 2 horas
cat("Score óptimo:", round(mejor_score, 2), "\n")
#> Score óptimo: 72.42
cat("Proceso completado en", iteracion_opt, "iteraciones\n")
#> Proceso completado en 6 iteraciones
# Validar resultado final
lotes_optimos <- datos_control[
  abs(datos_control$tiempo_reaccion_horas - mejor_tiempo) <= 0.5, 
]
cat("\nValidación del resultado óptimo:\n")
#> 
#> Validación del resultado óptimo:
cat("- Lotes con tiempo óptimo:", nrow(lotes_optimos), "\n")
#> - Lotes con tiempo óptimo: 12
cat("- Concentración promedio:", round(mean(lotes_optimos$concentracion_producto), 1), "%\n")
#> - Concentración promedio: 72.4 %
cat("- Tasa de éxito:", round(mean(lotes_optimos$control_calidad_pasa) * 100, 1), "%\n")
#> - Tasa de éxito: 100 %

5 Condicionales if/else en Análisis Químico

5.1 Sistema de Clasificación Automática de Lotes

# Función para clasificar lotes automáticamente
clasificar_lote <- function(lote) {
  # Inicializar clasificación
  clasificacion <- "Pendiente"
  problemas <- character(0)
  score_calidad <- 100
  
  # Evaluar pH según tipo de producto
  if (lote$producto_tipo == "Acido_Sulfurico") {
    if (lote$ph_final < 0.8 || lote$ph_final > 1.8) {
      problemas <- c(problemas, "pH_inadecuado")
      score_calidad <- score_calidad - 20
    }
  } else if (lote$producto_tipo == "Soda_Caustica") {
    if (lote$ph_final < 12.5 || lote$ph_final > 13.8) {
      problemas <- c(problemas, "pH_inadecuado")
      score_calidad <- score_calidad - 20
    }
  } else if (lote$producto_tipo == "Fertilizante_NPK") {
    if (lote$ph_final < 6.0 || lote$ph_final > 8.0) {
      problemas <- c(problemas, "pH_inadecuado")
      score_calidad <- score_calidad - 15
    }
  }
  
  # Evaluar temperatura
  if (lote$temperatura_reaccion < 45 || lote$temperatura_reaccion > 85) {
    problemas <- c(problemas, "temperatura_extrema")
    score_calidad <- score_calidad - 15
  }
  
  # Evaluar presión
  if (lote$presion_bar < 1.0 || lote$presion_bar > 6.0) {
    problemas <- c(problemas, "presion_fuera_rango")
    score_calidad <- score_calidad - 10
  }
  
  # Evaluar concentración
  if (lote$concentracion_producto < 30) {
    problemas <- c(problemas, "concentracion_baja")
    score_calidad <- score_calidad - 25
  } else if (lote$concentracion_producto > 95) {
    problemas <- c(problemas, "concentracion_excesiva")
    score_calidad <- score_calidad - 10
  }
  
  # Clasificación final basada en score
  if (score_calidad >= 95) {
    clasificacion <- "Premium"
  } else if (score_calidad >= 85) {
    clasificacion <- "Estandar"
  } else if (score_calidad >= 70) {
    clasificacion <- "Aceptable"
  } else if (score_calidad >= 50) {
    clasificacion <- "Requiere_Revision"
  } else {
    clasificacion <- "Rechazado"
  }
  
  return(list(
    clasificacion = clasificacion,
    score = score_calidad,
    problemas = problemas
  ))
}

# Aplicar clasificación a todos los lotes
cat("=== SISTEMA DE CLASIFICACIÓN AUTOMÁTICA ===\n")
#> === SISTEMA DE CLASIFICACIÓN AUTOMÁTICA ===
cat("Procesando", nrow(datos_control), "lotes...\n\n")
#> Procesando 150 lotes...
resultados_clasificacion <- data.frame()

for (i in 1:nrow(datos_control)) {
  lote_actual <- datos_control[i, ]
  resultado <- clasificar_lote(lote_actual)
  
  fila_resultado <- data.frame(
    Lote_ID = lote_actual$lote_id,
    Producto = lote_actual$producto_tipo,
    Turno = lote_actual$turno,
    Clasificacion = resultado$clasificacion,
    Score_Calidad = resultado$score,
    N_Problemas = length(resultado$problemas),
    Problemas = ifelse(length(resultado$problemas) > 0,
                      paste(resultado$problemas, collapse = "; "),
                      "Ninguno")
  )
  
  resultados_clasificacion <- rbind(resultados_clasificacion, fila_resultado)
}

# Resumen de clasificaciones
cat("=== RESUMEN DE CLASIFICACIONES ===\n")
#> === RESUMEN DE CLASIFICACIONES ===
tabla_clasificaciones <- table(resultados_clasificacion$Clasificacion)
for (i in 1:length(tabla_clasificaciones)) {
  nombre_clase <- names(tabla_clasificaciones)[i]
  cantidad <- tabla_clasificaciones[i]
  porcentaje <- round((cantidad / nrow(datos_control)) * 100, 1)
  cat(nombre_clase, ":", cantidad, "lotes (", porcentaje, "%)\n")
}
#> Aceptable : 23 lotes ( 15.3 %)
#> Estandar : 8 lotes ( 5.3 %)
#> Premium : 119 lotes ( 79.3 %)
# Mostrar lotes premium y problemáticos
cat("\n=== LOTES PREMIUM (TOP 5) ===\n")
#> 
#> === LOTES PREMIUM (TOP 5) ===
lotes_premium <- resultados_clasificacion[resultados_clasificacion$Clasificacion == "Premium", ]
if (nrow(lotes_premium) > 0) {
  kable(head(lotes_premium[order(-lotes_premium$Score_Calidad), ], 5),
        caption = "Lotes de Calidad Premium")
} else {
  cat("No hay lotes clasificados como Premium\n")
}
Lotes de Calidad Premium
Lote_ID Producto Turno Clasificacion Score_Calidad N_Problemas Problemas
LOTE_001 Acido_Nitrico Mañana Premium 100 0 Ninguno
LOTE_002 Fertilizante_NPK Noche Premium 100 0 Ninguno
LOTE_003 Soda_Caustica Tarde Premium 100 0 Ninguno
LOTE_004 Acido_Sulfurico Noche Premium 100 0 Ninguno
LOTE_005 Acido_Sulfurico Noche Premium 100 0 Ninguno
cat("\n=== LOTES PROBLEMÁTICOS ===\n")
#> 
#> === LOTES PROBLEMÁTICOS ===
lotes_problematicos <- resultados_clasificacion[
  resultados_clasificacion$Clasificacion %in% c("Requiere_Revision", "Rechazado"), 
]
if (nrow(lotes_problematicos) > 0) {
  kable(head(lotes_problematicos, 5),
        caption = "Lotes que Requieren Atención")
} else {
  cat("No hay lotes problemáticos\n")
}
#> No hay lotes problemáticos

6 Optimización: Cuándo Usar Bucles vs Vectorización

Principio Clave: En R, muchas operaciones pueden hacerse de manera vectorizada (sin bucles), lo que es más eficiente. Sin embargo, los bucles son necesarios cuando la lógica es compleja o cuando cada iteración depende de las anteriores.

6.1 Comparación de Rendimiento: Bucles vs Vectorización

# Comparación de métodos para calcular estadísticas por grupo
cat("=== COMPARACIÓN: BUCLES vs VECTORIZACIÓN ===\n\n")
#> === COMPARACIÓN: BUCLES vs VECTORIZACIÓN ===
# Método 1: Con bucle for (menos eficiente)
tiempo_inicio_bucle <- Sys.time()

resultados_bucle <- data.frame()
for (producto in unique(datos_control$producto_tipo)) {
  datos_producto <- datos_control[datos_control$producto_tipo == producto, ]
  
  resultado <- data.frame(
    Producto = producto,
    Media_pH = mean(datos_producto$ph_final),
    Media_Temp = mean(datos_producto$temperatura_reaccion),
    Media_Concentracion = mean(datos_producto$concentracion_producto)
  )
  
  resultados_bucle <- rbind(resultados_bucle, resultado)
}

tiempo_fin_bucle <- Sys.time()
tiempo_bucle <- tiempo_fin_bucle - tiempo_inicio_bucle

# Método 2: Con vectorización usando dplyr (más eficiente)
tiempo_inicio_vector <- Sys.time()

resultados_vector <- datos_control %>%
  group_by(producto_tipo) %>%
  summarise(
    Media_pH = mean(ph_final),
    Media_Temp = mean(temperatura_reaccion),
    Media_Concentracion = mean(concentracion_producto),
    .groups = 'drop'
  ) %>%
  rename(Producto = producto_tipo)

tiempo_fin_vector <- Sys.time()
tiempo_vector <- tiempo_fin_vector - tiempo_inicio_vector

# Mostrar resultados
cat("TIEMPOS DE EJECUCIÓN:\n")
#> TIEMPOS DE EJECUCIÓN:
cat("Método con bucle for:", round(as.numeric(tiempo_bucle, units = "secs") * 1000, 2), "milisegundos\n")
#> Método con bucle for: 8.45 milisegundos
cat("Método vectorizado:", round(as.numeric(tiempo_vector, units = "secs") * 1000, 2), "milisegundos\n")
#> Método vectorizado: 14.5 milisegundos
factor_mejora <- as.numeric(tiempo_bucle) / as.numeric(tiempo_vector)
cat("Factor de mejora:", round(factor_mejora, 1), "x más rápido\n\n")
#> Factor de mejora: 0.6 x más rápido
# Verificar que dan el mismo resultado
cat("¿Los resultados son idénticos?", 
    all.equal(resultados_bucle[order(resultados_bucle$Producto), ],
              as.data.frame(resultados_vector[order(resultados_vector$Producto), ]),
              check.attributes = FALSE), "\n\n")
#> ¿Los resultados son idénticos? TRUE
cat("=== RECOMENDACIONES DE USO ===\n")
#> === RECOMENDACIONES DE USO ===
cat("USAR BUCLES cuando:\n")
#> USAR BUCLES cuando:
cat("  - La lógica es compleja (múltiples condicionales)\n")
#>   - La lógica es compleja (múltiples condicionales)
cat("  - Cada iteración depende de las anteriores\n")
#>   - Cada iteración depende de las anteriores
cat("  - Necesitas control fino sobre el proceso\n")
#>   - Necesitas control fino sobre el proceso
cat("  - Generas reportes con formato específico\n\n")
#>   - Generas reportes con formato específico
cat("USAR VECTORIZACIÓN cuando:\n")
#> USAR VECTORIZACIÓN cuando:
cat("  - Operaciones matemáticas simples\n")
#>   - Operaciones matemáticas simples
cat("  - Agrupaciones y resúmenes estadísticos\n")
#>   - Agrupaciones y resúmenes estadísticos
cat("  - Transformaciones de columnas\n")
#>   - Transformaciones de columnas
cat("  - Máximo rendimiento es prioritario\n")
#>   - Máximo rendimiento es prioritario

6.2 Cuándo los Bucles son Necesarios: Caso Complejo

# Ejemplo donde los bucles son la mejor opción: Sistema de alertas escalonado
cat("=== SISTEMA DE ALERTAS ESCALONADO ===\n")
#> === SISTEMA DE ALERTAS ESCALONADO ===
cat("Caso donde los bucles son indispensables...\n\n")
#> Caso donde los bucles son indispensables...
# Función que requiere lógica compleja (no vectorizable fácilmente)
generar_alertas_lote <- function(lote, historial_operador) {
  alertas <- character(0)
  nivel_urgencia <- 0
  
  # Alerta básica por parámetros químicos
  if (lote$ph_final < 2 && lote$producto_tipo %in% c("Acido_Sulfurico", "Acido_Nitrico")) {
    alertas <- c(alertas, "CRÍTICO: pH extremadamente bajo")
    nivel_urgencia <- nivel_urgencia + 3
  }
  
  if (lote$temperatura_reaccion > 80) {
    alertas <- c(alertas, "ADVERTENCIA: Temperatura alta")
    nivel_urgencia <- nivel_urgencia + 2
  }
  
  # Alerta basada en historial del operador (requiere contexto)
  fallos_operador <- sum(!historial_operador$control_calidad_pasa)
  total_lotes_operador <- nrow(historial_operador)
  tasa_fallo_operador <- fallos_operador / total_lotes_operador
  
  if (tasa_fallo_operador > 0.15) {  # Si el operador falla más del 15%
    alertas <- c(alertas, "ATENCIÓN: Operador con alta tasa de fallos")
    nivel_urgencia <- nivel_urgencia + 1
  }
  
  # Alerta por tendencia temporal (requiere análisis secuencial)
  if (nrow(historial_operador) >= 3) {
    ultimos_3 <- tail(historial_operador, 3)
    if (sum(ultimos_3$control_calidad_pasa) <= 1) {
      alertas <- c(alertas, "URGENTE: Tendencia negativa reciente")
      nivel_urgencia <- nivel_urgencia + 4
    }
  }
  
  # Clasificar nivel de urgencia
  if (nivel_urgencia >= 5) {
    prioridad <- "CRÍTICA"
  } else if (nivel_urgencia >= 3) {
    prioridad <- "ALTA"
  } else if (nivel_urgencia >= 1) {
    prioridad <- "MEDIA"
  } else {
    prioridad <- "NORMAL"
  }
  
  return(list(
    alertas = alertas,
    nivel_urgencia = nivel_urgencia,
    prioridad = prioridad
  ))
}

# Procesar alertas (REQUIERE bucle debido a la complejidad)
cat("Generando alertas para todos los lotes...\n")
#> Generando alertas para todos los lotes...
alertas_sistema <- data.frame()

operadores_unicos <- unique(datos_control$operador_id)

for (operador in operadores_unicos) {
  # Obtener historial del operador ordenado por fecha
  lotes_operador <- datos_control[
    datos_control$operador_id == operador,
  ][order(datos_control[datos_control$operador_id == operador, ]$fecha_produccion), ]
  
  # Procesar cada lote del operador (necesita contexto del historial)
  for (i in 1:nrow(lotes_operador)) {
    lote_actual <- lotes_operador[i, ]
    
    # Historial hasta este punto (para análisis de tendencias)
    historial_hasta_ahora <- lotes_operador[1:i, ]
    
    # Generar alertas
    resultado_alertas <- generar_alertas_lote(lote_actual, historial_hasta_ahora)
    
    # Solo guardar si hay alertas
    if (length(resultado_alertas$alertas) > 0) {
      fila_alerta <- data.frame(
        Lote_ID = lote_actual$lote_id,
        Operador = operador,
        Fecha = lote_actual$fecha_produccion,
        Producto = lote_actual$producto_tipo,
        Prioridad = resultado_alertas$prioridad,
        N_Alertas = length(resultado_alertas$alertas),
        Alertas = paste(resultado_alertas$alertas, collapse = " | "),
        stringsAsFactors = FALSE
      )
      
      alertas_sistema <- rbind(alertas_sistema, fila_alerta)
    }
  }
}

cat("Procesamiento completado.\n")
#> Procesamiento completado.
cat("Total de alertas generadas:", nrow(alertas_sistema), "\n\n")
#> Total de alertas generadas: 81
# Resumen de alertas por prioridad
cat("=== RESUMEN DE ALERTAS POR PRIORIDAD ===\n")
#> === RESUMEN DE ALERTAS POR PRIORIDAD ===
tabla_prioridades <- table(alertas_sistema$Prioridad)
for (prioridad in c("CRÍTICA", "ALTA", "MEDIA")) {
  if (prioridad %in% names(tabla_prioridades)) {
    cat(prioridad, ":", tabla_prioridades[prioridad], "alertas\n")
  } else {
    cat(prioridad, ": 0 alertas\n")
  }
}
#> CRÍTICA : 17 alertas
#> ALTA : 40 alertas
#> MEDIA : 24 alertas
# Mostrar alertas críticas
alertas_criticas <- alertas_sistema[alertas_sistema$Prioridad == "CRÍTICA", ]
if (nrow(alertas_criticas) > 0) {
  cat("\n=== ALERTAS CRÍTICAS ===\n")
  kable(head(alertas_criticas, 5),
        caption = "Alertas que Requieren Atención Inmediata")
} else {
  cat("\n¡No hay alertas críticas en el sistema!\n")
}
#> 
#> === ALERTAS CRÍTICAS ===
Alertas que Requieren Atención Inmediata
Lote_ID Operador Fecha Producto Prioridad N_Alertas Alertas
2 LOTE_063 OP_010 2025-02-07 Acido_Sulfurico CRÍTICA 2 CRÍTICO: pH extremadamente bajo | ADVERTENCIA: Temperatura alta
3 LOTE_064 OP_010 2025-02-07 Acido_Nitrico CRÍTICA 2 CRÍTICO: pH extremadamente bajo | ADVERTENCIA: Temperatura alta
4 LOTE_126 OP_010 2025-03-15 Acido_Nitrico CRÍTICA 2 CRÍTICO: pH extremadamente bajo | ADVERTENCIA: Temperatura alta
5 LOTE_137 OP_010 2025-03-22 Acido_Sulfurico CRÍTICA 2 CRÍTICO: pH extremadamente bajo | ADVERTENCIA: Temperatura alta
7 LOTE_096 OP_017 2025-02-26 Acido_Nitrico CRÍTICA 2 CRÍTICO: pH extremadamente bajo | ADVERTENCIA: Temperatura alta
cat("\nEste ejemplo demuestra por qué los bucles son necesarios:\n")
#> 
#> Este ejemplo demuestra por qué los bucles son necesarios:
cat("- Lógica compleja con múltiples condicionales\n")
#> - Lógica compleja con múltiples condicionales
cat("- Análisis contextual (historial del operador)\n")
#> - Análisis contextual (historial del operador)
cat("- Dependencia entre iteraciones (tendencias temporales)\n")
#> - Dependencia entre iteraciones (tendencias temporales)
cat("- Imposible de vectorizar manteniendo la misma funcionalidad\n")
#> - Imposible de vectorizar manteniendo la misma funcionalidad

7 Síntesis: Integrando Sentencias Iterativas en Análisis de Datos

7.1 Función Integral de Análisis Automatizado

# Función que integra todo lo aprendido
analisis_planta_completo <- function(datos, incluir_alertas = TRUE, incluir_optimizacion = FALSE) {
  cat("=== ANÁLISIS INTEGRAL DE PLANTA QUÍMICA ===\n")
  cat("Iniciando análisis automatizado con sentencias iterativas...\n\n")
  
  # Contenedores para resultados
  resultados <- list()
  
  # 1. ANÁLISIS POR PRODUCTOS (bucle for)
  cat("1. ANÁLISIS POR PRODUCTOS\n")
  tipos <- unique(datos$producto_tipo)
  analisis_productos <- data.frame()

  for (tipo in tipos) {
    datos_tipo <- datos[datos$producto_tipo == tipo, ]
    
    # Calcular métricas clave
    n_lotes <- nrow(datos_tipo)
    ph_promedio <- mean(datos_tipo$ph_final, na.rm = TRUE)
    temp_promedio <- mean(datos_tipo$temperatura_reaccion, na.rm = TRUE)
    conc_promedio <- mean(datos_tipo$concentracion_producto, na.rm = TRUE)
    tasa_exito <- mean(datos_tipo$control_calidad_pasa) * 100
    
    # Clasificar rendimiento
    if (tasa_exito >= 95) {
      status <- "Excelente"
    } else if (tasa_exito >= 85) {
      status <- "Bueno"
    } else {
      status <- "Requiere mejora"
    }
    
    fila <- data.frame(
      Producto = tipo,
      N_Lotes = n_lotes,
      pH_Promedio = round(ph_promedio, 2),
      Temp_Promedio = round(temp_promedio, 1),
      Conc_Promedio = round(conc_promedio, 1),
      Tasa_Exito = round(tasa_exito, 1),
      Status = status
    )
    
    analisis_productos <- rbind(analisis_productos, fila)
  }
  
  resultados$productos <- analisis_productos
  cat("   Completado:", length(tipos), "productos analizados\n\n")
  
  # 2. CONTROL DE CALIDAD ITERATIVO (bucle while)
  cat("2. CONTROL DE CALIDAD ITERATIVO\n")
  datos_temp <- datos
  criterio_pH <- 2.0
  criterio_temp <- 85.0
  iteraciones <- 0
  objetivo_aprobacion <- 0.88
  
  while (iteraciones < 5) {
    iteraciones <- iteraciones + 1
    
    # Aplicar criterios
    cumple_criterios <- (datos_temp$ph_final >= 0.5 & datos_temp$ph_final <= 13.5) &
                       (datos_temp$temperatura_reaccion <= criterio_temp) &
                       (datos_temp$presion_bar >= 1.0 & datos_temp$presion_bar <= 6.0)
    
    tasa_actual <- mean(cumple_criterios)
    
    if (tasa_actual >= objetivo_aprobacion) {
      break
    } else {
      criterio_temp <- criterio_temp + 1.0
    }
  }
  
  resultados$control_iterativo <- list(
    iteraciones = iteraciones,
    criterio_temp_final = criterio_temp,
    tasa_final = round(tasa_actual * 100, 1)
  )
  
  cat("   Completado en", iteraciones, "iteraciones\n")
  cat("   Tasa final de aprobación:", round(tasa_actual * 100, 1), "%\n\n")
  
  # 3. CLASIFICACIÓN AUTOMÁTICA (if/else complejos)
  cat("3. CLASIFICACIÓN AUTOMÁTICA DE LOTES\n")
  clasificaciones <- character(nrow(datos))
  
  for (i in 1:nrow(datos)) {
    lote <- datos[i, ]
    score <- 100
    
    # Evaluaciones específicas por producto
    if (lote$producto_tipo == "Acido_Sulfurico") {
      if (lote$ph_final > 2.0) score <- score - 30
    } else if (lote$producto_tipo == "Soda_Caustica") {
      if (lote$ph_final < 12.0) score <- score - 30
    }
    
    # Evaluación de temperatura
    if (lote$temperatura_reaccion > 80) score <- score - 15
    if (lote$temperatura_reaccion < 50) score <- score - 10
    
    # Evaluación de concentración
    if (lote$concentracion_producto < 40) score <- score - 25
    
    # Clasificación final
    if (score >= 90) {
      clasificaciones[i] <- "Premium"
    } else if (score >= 75) {
      clasificaciones[i] <- "Estándar"
    } else if (score >= 60) {
      clasificaciones[i] <- "Aceptable"
    } else {
      clasificaciones[i] <- "Revisar"
    }
  }
  
  tabla_clasificaciones <- table(clasificaciones)
  resultados$clasificaciones <- tabla_clasificaciones
  
  cat("   Clasificaciones generadas:\n")
  for (clase in names(tabla_clasificaciones)) {
    pct <- round((tabla_clasificaciones[clase] / nrow(datos)) * 100, 1)
    cat("     ", clase, ":", tabla_clasificaciones[clase], "(", pct, "%)\n")
  }
  cat("\n")
  
  # 4. ALERTAS DEL SISTEMA (opcional)
  if (incluir_alertas) {
    cat("4. SISTEMA DE ALERTAS\n")
    alertas_criticas <- 0
    alertas_medias <- 0
    
    for (i in 1:nrow(datos)) {
      lote <- datos[i, ]
      
      # Alertas críticas
      if ((lote$ph_final < 1.0 && lote$producto_tipo == "Acido_Sulfurico") ||
          (lote$temperatura_reaccion > 85) ||
          (lote$presion_bar > 5.5)) {
        alertas_criticas <- alertas_criticas + 1
      }
      
      # Alertas medias
      if ((lote$concentracion_producto < 30) ||
          (lote$tiempo_reaccion_horas > 10)) {
        alertas_medias <- alertas_medias + 1
      }
    }
    
    resultados$alertas <- list(
      criticas = alertas_criticas,
      medias = alertas_medias,
      total = alertas_criticas + alertas_medias
    )
    
    cat("   Alertas críticas:", alertas_criticas, "\n")
    cat("   Alertas medias:", alertas_medias, "\n")
    cat("   Total alertas:", alertas_criticas + alertas_medias, "\n\n")
  }
  
  # 5. REPORTE FINAL
  cat("=== REPORTE EJECUTIVO ===\n")
  total_lotes <- nrow(datos)
  lotes_exitosos <- sum(datos$control_calidad_pasa)
  tasa_exito_general <- round((lotes_exitosos / total_lotes) * 100, 1)
  
  cat("Total de lotes procesados:", total_lotes, "\n")
  cat("Lotes exitosos:", lotes_exitosos, "\n")
  cat("Tasa de éxito general:", tasa_exito_general, "%\n")
  
  mejor_producto <- resultados$productos$Producto[which.max(resultados$productos$Tasa_Exito)]
  cat("Producto con mejor rendimiento:", mejor_producto, "\n")
  
  if (incluir_alertas) {
    if (resultados$alertas$criticas == 0) {
      cat("Estado general: SIN ALERTAS CRÍTICAS\n")
    } else {
      cat("Estado general: ATENCIÓN REQUERIDA (", resultados$alertas$criticas, "alertas críticas)\n")
    }
  }
  
  return(invisible(resultados))
}

# Ejecutar análisis completo
cat("Ejecutando análisis integral de la planta química...\n\n")
#> Ejecutando análisis integral de la planta química...
resultados_completos <- analisis_planta_completo(datos_control, incluir_alertas = TRUE)
#> === ANÁLISIS INTEGRAL DE PLANTA QUÍMICA ===
#> Iniciando análisis automatizado con sentencias iterativas...
#> 
#> 1. ANÁLISIS POR PRODUCTOS
#>    Completado: 5 productos analizados
#> 
#> 2. CONTROL DE CALIDAD ITERATIVO
#>    Completado en 1 iteraciones
#>    Tasa final de aprobación: 92.7 %
#> 
#> 3. CLASIFICACIÓN AUTOMÁTICA DE LOTES
#>    Clasificaciones generadas:
#>       Aceptable : 8 ( 5.3 %)
#>       Estándar : 44 ( 29.3 %)
#>       Premium : 98 ( 65.3 %)
#> 
#> 4. SISTEMA DE ALERTAS
#>    Alertas críticas: 15 
#>    Alertas medias: 34 
#>    Total alertas: 49 
#> 
#> === REPORTE EJECUTIVO ===
#> Total de lotes procesados: 150 
#> Lotes exitosos: 138 
#> Tasa de éxito general: 92 %
#> Producto con mejor rendimiento: Polimero_PVC 
#> Estado general: ATENCIÓN REQUERIDA ( 15 alertas críticas)

7.2 Visualización de Resultados con Sentencias Iterativas

# Crear visualizaciones usando los resultados del análisis iterativo
library(gridExtra)

# Gráfico 1: Rendimiento por producto (resultado del bucle for)
p1 <- ggplot(resultados_completos$productos, aes(x = reorder(Producto, Tasa_Exito), 
                                                 y = Tasa_Exito, fill = Status)) +
  geom_col(alpha = 0.8) +
  geom_text(aes(label = paste0(Tasa_Exito, "%")), hjust = -0.1, color = "#2c3e50", fontface = "bold") +
  coord_flip() +
  labs(
    title = "Análisis de Rendimiento por Producto",
    subtitle = "Generado automáticamente con bucles for",
    x = "Tipo de Producto",
    y = "Tasa de Éxito (%)"
  ) +
  scale_fill_manual(values = c("Excelente" = "#27ae60", "Bueno" = "#f39c12", "Requiere mejora" = "#e74c3c")) +
  theme(legend.position = "bottom")

# Gráfico 2: Distribución de clasificaciones (resultado de if/else)
clasificaciones_df <- data.frame(
  Clasificacion = names(resultados_completos$clasificaciones),
  Cantidad = as.numeric(resultados_completos$clasificaciones)
)

p2 <- ggplot(clasificaciones_df, aes(x = reorder(Clasificacion, Cantidad), y = Cantidad, fill = Clasificacion)) +
  geom_col(alpha = 0.8) +
  geom_text(aes(label = Cantidad), hjust = -0.1, color = "#2c3e50", fontface = "bold") +
  coord_flip() +
  labs(
    title = "Clasificación Automática de Lotes",
    subtitle = "Generado con condicionales if/else complejos",
    x = "Clasificación de Calidad",
    y = "Número de Lotes"
  ) +
  scale_fill_manual(values = c("Premium" = "#27ae60", "Estándar" = "#3498db", 
                               "Aceptable" = "#f39c12", "Revisar" = "#e74c3c")) +
  theme(legend.position = "none")

# Gráfico 3: Control de temperatura por turno usando análisis iterativo
datos_temp_turno <- datos_control %>%
  group_by(turno) %>%
  summarise(
    temp_media = mean(temperatura_reaccion),
    temp_sd = sd(temperatura_reaccion),
    n_lotes = n(),
    .groups = 'drop'
  )

p3 <- ggplot(datos_temp_turno, aes(x = turno, y = temp_media, fill = turno)) +
  geom_col(alpha = 0.8) +
  geom_errorbar(aes(ymin = temp_media - temp_sd, ymax = temp_media + temp_sd), 
                width = 0.2, color = "#2c3e50") +
  geom_text(aes(label = paste0(round(temp_media, 1), "°C")), vjust = -0.5, 
            color = "#2c3e50", fontface = "bold") +
  labs(
    title = "Control de Temperatura por Turno",
    subtitle = "Análisis de variabilidad para optimización de procesos",
    x = "Turno de Trabajo",
    y = "Temperatura Media (°C)"
  ) +
  scale_fill_manual(values = c("Mañana" = "#e8f4f8", "Tarde" = "#a73c3c", "Noche" = "#2c3e50")) +
  theme(legend.position = "none")

# Mostrar gráficos
grid.arrange(p1, p2, ncol = 2)
print(p3)

Proyecto Final Integrado

Escenario: Usted es el nuevo Jefe de Control de Calidad de la planta química. Debe crear un sistema automatizado que se ejecute diariamente.

Requisitos del Sistema:

  1. Bucle for: Generar reporte diario por cada operador (eficiencia, lotes procesados, problemas)

  2. Bucle while: Implementar sistema de mejora continua que ajuste automáticamente parámetros hasta alcanzar 95% de aprobación

  3. Condicionales if/else: Crear sistema de clasificación de operadores en 4 categorías (Excelente, Bueno, Regular, Crítico)

  4. Integración: El sistema debe generar alertas automáticas y sugerir acciones correctivas

Entrega: Código R funcional que procese todos los datos y genere un reporte ejecutivo completo.

# SOLUCIÓN DEL PROYECTO FINAL
sistema_control_calidad_diario <- function(datos) {
  cat("=== SISTEMA AUTOMATIZADO DE CONTROL DE CALIDAD ===\n")
  cat("Fecha de ejecución:", Sys.Date(), "\n\n")
  
  # PARTE 1: ANÁLISIS POR OPERADOR (bucle for)
  cat("PARTE 1: ANÁLISIS DE DESEMPEÑO POR OPERADOR\n")
  cat(paste(rep("=", 50), collapse = ""), "\n")
  
  operadores <- unique(datos$operador_id)
  reporte_operadores <- data.frame()
  
  for (operador in operadores) {
    datos_op <- datos[datos$operador_id == operador, ]
    
    # Métricas de desempeño
    n_lotes <- nrow(datos_op)
    lotes_exitosos <- sum(datos_op$control_calidad_pasa)
    tasa_exito <- (lotes_exitosos / n_lotes) * 100
    
    # Variabilidad en procesos (CORRECCIÓN: manejar NA y casos de n=1)
    if (n_lotes > 1) {
      cv_temperatura <- (sd(datos_op$temperatura_reaccion, na.rm = TRUE) / 
                        mean(datos_op$temperatura_reaccion, na.rm = TRUE)) * 100
      cv_concentracion <- (sd(datos_op$concentracion_producto, na.rm = TRUE) / 
                          mean(datos_op$concentracion_producto, na.rm = TRUE)) * 100
    } else {
      cv_temperatura <- 0
      cv_concentracion <- 0
    }
    
    # Manejar casos donde cv es NA
    if (is.na(cv_temperatura)) cv_temperatura <- 99
    if (is.na(cv_concentracion)) cv_concentracion <- 99
    
    # Tiempo promedio de reacción
    tiempo_promedio <- mean(datos_op$tiempo_reaccion_horas, na.rm = TRUE)
    
    # Clasificación del operador (if/else anidados) - CORRECCIÓN
    if (!is.na(tasa_exito) && !is.na(cv_temperatura) && !is.na(cv_concentracion)) {
      if (tasa_exito >= 95 && cv_temperatura < 10 && cv_concentracion < 15) {
        clasificacion <- "Excelente"
        accion_recomendada <- "Reconocimiento público"
      } else if (tasa_exito >= 85 && cv_temperatura < 15) {
        clasificacion <- "Bueno"
        accion_recomendada <- "Continuar con el buen trabajo"
      } else if (tasa_exito >= 75) {
        clasificacion <- "Regular"
        accion_recomendada <- "Entrenamiento adicional requerido"
      } else {
        clasificacion <- "Crítico"
        accion_recomendada <- "Intervención inmediata necesaria"
      }
    } else {
      clasificacion <- "Sin_Datos"
      accion_recomendada <- "Verificar datos del operador"
    }
    
    # Guardar resultados
    fila_operador <- data.frame(
      Operador = operador,
      N_Lotes = n_lotes,
      Tasa_Exito = round(tasa_exito, 1),
      CV_Temperatura = round(cv_temperatura, 1),
      CV_Concentracion = round(cv_concentracion, 1),
      Tiempo_Promedio = round(tiempo_promedio, 2),
      Clasificacion = clasificacion,
      Accion = accion_recomendada,
      stringsAsFactors = FALSE
    )
    
    reporte_operadores <- rbind(reporte_operadores, fila_operador)
  }
  
  # Mostrar resumen por clasificación
  clasificaciones_count <- table(reporte_operadores$Clasificacion)
  cat("Resumen de operadores por categoría:\n")
  for (cat in names(clasificaciones_count)) {
    cat(" ", cat, ":", clasificaciones_count[cat], "operadores\n")
  }
  cat("\n")
  
  # PARTE 2: MEJORA CONTINUA ITERATIVA (bucle while)
  cat("PARTE 2: SISTEMA DE MEJORA CONTINUA\n")
  cat(paste(rep("=", 40), collapse = ""), "\n")
  
  objetivo_global <- 0.95
  criterio_temp_max <- 80.0
  criterio_conc_min <- 40.0
  criterio_pH_rango <- c(1.0, 13.0)
  iteracion_mejora <- 0
  max_iteraciones <- 8
  
  datos_mejora <- datos
  tasa_actual <- mean(datos_mejora$control_calidad_pasa, na.rm = TRUE)
  
  while (tasa_actual < objetivo_global && iteracion_mejora < max_iteraciones) {
    iteracion_mejora <- iteracion_mejora + 1
    
    cat("Iteración de mejora", iteracion_mejora, ":\n")
    cat("  Tasa actual:", round(tasa_actual * 100, 1), "%\n")
    
    # Ajustar criterios gradualmente
    if (iteracion_mejora %% 3 == 1) {
      criterio_temp_max <- criterio_temp_max + 2.0
      cat("  Acción: Relajar criterio de temperatura a ≤", criterio_temp_max, "°C\n")
    } else if (iteracion_mejora %% 3 == 2) {
      criterio_conc_min <- criterio_conc_min - 3.0
      cat("  Acción: Reducir concentración mínima a ≥", criterio_conc_min, "%\n")
    } else {
      criterio_pH_rango[1] <- criterio_pH_rango[1] - 0.2
      criterio_pH_rango[2] <- criterio_pH_rango[2] + 0.2
      cat("  Acción: Ampliar rango de pH a [", criterio_pH_rango[1], ",", criterio_pH_rango[2], "]\n")
    }
    
    # Recalcular tasa con nuevos criterios (CORRECCIÓN: manejar NA)
    cumple_criterios <- (datos_mejora$temperatura_reaccion <= criterio_temp_max) &
                       (datos_mejora$concentracion_producto >= criterio_conc_min) &
                       (datos_mejora$ph_final >= criterio_pH_rango[1] & 
                        datos_mejora$ph_final <= criterio_pH_rango[2])
    
    # Remover NA de la evaluación
    cumple_criterios[is.na(cumple_criterios)] <- FALSE
    
    tasa_actual <- mean(cumple_criterios)
    cat("  Nueva tasa proyectada:", round(tasa_actual * 100, 1), "%\n\n")
  }
  
  if (tasa_actual >= objetivo_global) {
    cat("ÉXITO: Objetivo de", round(objetivo_global * 100, 1), "% alcanzado en", iteracion_mejora, "iteraciones\n")
  } else {
    cat("ADVERTENCIA: Objetivo no alcanzado en", max_iteraciones, "iteraciones máximas\n")
  }
  
  cat("Criterios optimizados finales:\n")
  cat("  - Temperatura máxima:", criterio_temp_max, "°C\n")
  cat("  - Concentración mínima:", criterio_conc_min, "%\n")
  cat("  - Rango pH:", criterio_pH_rango[1], "a", criterio_pH_rango[2], "\n\n")
  
  # PARTE 3: ALERTAS Y ACCIONES AUTOMÁTICAS
  cat("PARTE 3: SISTEMA DE ALERTAS AUTOMÁTICAS\n")
  cat(paste(rep("=", 42), collapse = ""), "\n")
  
  # Operadores críticos (CORRECCIÓN: filtrar NA)
  operadores_criticos <- reporte_operadores[
    reporte_operadores$Clasificacion == "Crítico" & 
    !is.na(reporte_operadores$Clasificacion), 
  ]
  n_criticos <- nrow(operadores_criticos)
  
  cat("ALERTAS CRÍTICAS:\n")
  if (n_criticos > 0) {
    cat("  ATENCIÓN: ", n_criticos, " operador(es) requieren intervención inmediata:\n")
    for (i in 1:n_criticos) {
      cat("    -", operadores_criticos$Operador[i], 
          "- Tasa de éxito:", operadores_criticos$Tasa_Exito[i], "%\n")
    }
  } else {
    cat("  ✓ No hay operadores en estado crítico\n")
  }
  
  # Productos con problemas
  productos_problematicos <- reporte_operadores[
    reporte_operadores$Clasificacion %in% c("Regular", "Crítico") &
    !is.na(reporte_operadores$Clasificacion),
  ]
  
  total_problematicos <- nrow(productos_problematicos)
  
  cat("\nALERTAS DE PROCESO:\n")
  if (total_problematicos > 0) {
    pct_problematicos <- round((total_problematicos / length(operadores)) * 100, 1)
    cat("  ADVERTENCIA:", total_problematicos, "operadores (", pct_problematicos, "%) necesitan atención\n")
  } else {
    cat("  ✓ Todos los operadores tienen desempeño aceptable\n")
  }
  
  # Recomendaciones automáticas (CORRECCIÓN: verificar que hay datos válidos)
  cat("\nRECOMENDACIONES AUTOMÁTICAS:\n")
  
  if (nrow(reporte_operadores) > 0 && !all(is.na(reporte_operadores$Tasa_Exito))) {
    mejor_operador <- reporte_operadores$Operador[which.max(reporte_operadores$Tasa_Exito)]
    peor_operador <- reporte_operadores$Operador[which.min(reporte_operadores$Tasa_Exito)]
    
    cat("  1. Operador modelo:", mejor_operador, 
        "- Usar como mentor para entrenamientos\n")
    cat("  2. Operador prioritario:", peor_operador, 
        "- Requiere programa de mejora personalizado\n")
  }
  
  if (iteracion_mejora <= 3) {
    cat("  3. Procesos: Criterios actuales son apropiados\n")
  } else {
    cat("  3. Procesos: Considerar revisión de estándares de calidad\n")
  }
  
  # REPORTE FINAL
  cat("\n")
  cat(paste(rep("=", 60), collapse = ""), "\n")
  cat("REPORTE EJECUTIVO DIARIO\n")
  cat(paste(rep("=", 60), collapse = ""), "\n")
  
  cat("Total de operadores evaluados:", length(operadores), "\n")
  cat("Total de lotes procesados:", nrow(datos), "\n")
  cat("Tasa de éxito general:", round(mean(datos$control_calidad_pasa, na.rm = TRUE) * 100, 1), "%\n")
  cat("Operadores excelentes:", sum(reporte_operadores$Clasificacion == "Excelente", na.rm = TRUE), "\n")
  cat("Operadores que necesitan atención:", 
      sum(reporte_operadores$Clasificacion %in% c("Regular", "Crítico"), na.rm = TRUE), "\n")
  
  if (n_criticos == 0 && total_problematicos <= 2) {
    cat("ESTADO GENERAL: ✓ OPERACIÓN NORMAL\n")
  } else if (n_criticos == 0) {
    cat("ESTADO GENERAL: ⚠ ATENCIÓN REQUERIDA\n")
  } else {
    cat("ESTADO GENERAL: 🚨 INTERVENCIÓN CRÍTICA\n")
  }
  
  # Retornar resultados para uso posterior
  return(list(
    operadores = reporte_operadores,
    criterios_optimizados = list(
      temp_max = criterio_temp_max,
      conc_min = criterio_conc_min,
      ph_rango = criterio_pH_rango
    ),
    alertas = list(
      criticos = n_criticos,
      problematicos = total_problematicos
    )
  ))
}

8 Conclusiones y Próximos Pasos

“Las sentencias iterativas son el corazón de la automatización en análisis de datos químicos. Nos permiten procesar grandes volúmenes de información con lógica compleja, manteniendo la flexibilidad para adaptarse a diferentes escenarios industriales.”

8.1 Resumen de Competencias Desarrolladas

cat("=== COMPETENCIAS DESARROLLADAS EN ESTA CLASE ===\n\n")
#> === COMPETENCIAS DESARROLLADAS EN ESTA CLASE ===
competencias <- data.frame(
  Competencia = c(
    "Bucles for básicos",
    "Bucles for con análisis grupal",
    "Bucles anidados",
    "Bucles while condicionales",
    "Bucles while iterativos",
    "Condicionales simples",
    "Condicionales complejos anidados",
    "Funciones con iterativas",
    "Optimización de código",
    "Vectorización vs bucles"
  ),
  Nivel_Dominio = c(
    "Intermedio",
    "Avanzado",
    "Avanzado",
    "Intermedio",
    "Avanzado",
    "Básico",
    "Avanzado",
    "Avanzado",
    "Intermedio",
    "Avanzado"
  ),
  Aplicacion_Quimica = c(
    "Análisis por productos",
    "Control de calidad por grupos",
    "Análisis multidimensional",
    "Validación iterativa",
    "Optimización de procesos",
    "Clasificación de muestras",
    "Sistemas de alertas",
    "Automatización completa",
    "Eficiencia computacional",
    "Mejores prácticas"
  )
)

kable(competencias,
      caption = "Mapa de Competencias: Sentencias Iterativas en Química Industrial",
      align = "lll")
Mapa de Competencias: Sentencias Iterativas en Química Industrial
Competencia Nivel_Dominio Aplicacion_Quimica
Bucles for básicos Intermedio Análisis por productos
Bucles for con análisis grupal Avanzado Control de calidad por grupos
Bucles anidados Avanzado Análisis multidimensional
Bucles while condicionales Intermedio Validación iterativa
Bucles while iterativos Avanzado Optimización de procesos
Condicionales simples Básico Clasificación de muestras
Condicionales complejos anidados Avanzado Sistemas de alertas
Funciones con iterativas Avanzado Automatización completa
Optimización de código Intermedio Eficiencia computacional
Vectorización vs bucles Avanzado Mejores prácticas
cat("\n=== MÉTRICAS DE APRENDIZAJE ===\n")
#> 
#> === MÉTRICAS DE APRENDIZAJE ===
cat("Líneas de código R escritas: ~400\n")
#> Líneas de código R escritas: ~400
cat("Funciones creadas: 5\n")
#> Funciones creadas: 5
cat("Datasets procesados: 1 (150 registros)\n")
#> Datasets procesados: 1 (150 registros)
cat("Bucles implementados: 15+\n")
#> Bucles implementados: 15+
cat("Condicionales complejos: 20+\n")
#> Condicionales complejos: 20+
cat("Casos de uso industriales: 8\n")
#> Casos de uso industriales: 8

8.2 Aplicaciones en el Mundo Real

Casos de Uso Directos en la Industria Química:

  1. Control de Calidad 24/7: Sistemas automatizados que procesan miles de lotes diarios
  2. Optimización de Procesos: Algoritmos que ajustan parámetros en tiempo real
  3. Gestión de Inventarios: Control automatizado de materias primas y productos
  4. Mantenimiento Predictivo: Análisis de sensores para prevenir fallas de equipo
  5. Cumplimiento Regulatorio: Verificación automática de estándares ambientales
  6. Eficiencia Energética: Optimización de consumo en procesos químicos

8.3 Próxima Clase: Funciones y Modularización

Clase 5: Funciones Avanzadas para Análisis Químico

Construiremos sobre las sentencias iterativas para crear:
• Funciones especializadas para análisis químico
• Librerías personalizadas para control de calidad
• Sistemas modulares para diferentes tipos de análisis
• Documentación y testing de funciones
• Integración con bases de datos químicas