INGENIERÍA DE DATOS — Trabajo Grupal

Temas cubiertos: 7 (Análisis y algoritmos), 8 (Validación y evaluación de modelos)

Descripción: Este documento carga el dataset Gold del Script 2, construye modelos de regresión lineal (simple y múltiple) para predecir los puntos obtenidos en una carrera, evalúa su rendimiento con métricas (MSE, RMSE, R²) y realiza predicciones sobre nuevos datos.

IMPORTANTE: Ejecutar primero 01_ingesta_y_limpieza.Rmd y luego 02_transformacion_y_EDA.Rmd.


0. Carga de librerías

library(dplyr)      # Manipulación de datos
library(ggplot2)    # Visualizaciones

1. Carga del dataset Gold

# [PROPORCIONADO]
df_gold <- readRDS("datos/gold/f1_datos_gold.rds")

cat("Dataset Gold cargado:", nrow(df_gold), "filas x", ncol(df_gold), "columnas\n")
## Dataset Gold cargado: 3760 filas x 43 columnas
head(df_gold)
##   season round            race_name    circuit_id
## 1   2022     4   Russian Grand Prix         sochi
## 2   2015     1 Hungarian Grand Prix   hungaroring
## 3   2017    12 Hungarian Grand Prix   hungaroring
## 4   2018     4  Austrian Grand Prix red_bull_ring
## 5   2014     5   Spanish Grand Prix     catalunya
## 6   2022    16  Austrian Grand Prix red_bull_ring
##                     circuit_name       date  driver_id     driver_name
## 1                 Sochi Autodrom 2022-06-01    tsunoda    Yuki Tsunoda
## 2                    Hungaroring 2015-10-18     lawson     Liam Lawson
## 3                    Hungaroring 2017-08-18 hulkenberg Nico Hülkenberg
## 4                  Red Bull Ring 2018-10-23   sargeant  Logan Sargeant
## 5 Circuit de Barcelona-Catalunya 2014-11-05  raikkonen  Kimi Räikkönen
## 6                  Red Bull Ring 2022-04-14   sargeant  Logan Sargeant
##   driver_nationality constructor grid position position_text points laps
## 1           Japanese  alfa romeo    6       20            20      0   67
## 2      New Zealander       manor    1        4             4     12   65
## 3             German       manor    5       19            19      0   56
## 4           American        haas    5       NA             R      0   20
## 5            Finnish     ferrari   10       NA             R      0    5
## 6           American  alfa romeo    2        2             2     18   58
##       status fastest_lap_time fastest_lap_speed posiciones_ganadas es_podio
## 1    +2 Laps         1:35.727           206.174                -14        0
## 2   Finished         1:33.721           230.861                 -3        0
## 3   Finished                                 NA                -14        0
## 4 Hydraulics         1:23.903           231.109                 NA       NA
## 5    Gearbox         1:17.230           234.613                 NA       NA
## 6   Finished         1:18.126           232.487                  0        1
##   puntos_por_vuelta constructor_key driver_id_key nombre_equipo     pais_sede
## 1         0.0000000      alfa romeo       tsunoda    Alfa Romeo   Switzerland
## 2         0.1846154           manor        lawson          <NA>          <NA>
## 3         0.0000000           manor    hulkenberg          <NA>          <NA>
## 4         0.0000000            haas      sargeant          Haas United States
## 5         0.0000000         ferrari     raikkonen       Ferrari         Italy
## 6         0.3103448      alfa romeo      sargeant    Alfa Romeo   Switzerland
##   anio_fundacion num_empleados         director_equipo
## 1           2019           450 Alessandro Alunni Bravi
## 2             NA            NA                    <NA>
## 3             NA            NA                    <NA>
## 4           2016           300        Guenther Steiner
## 5           1929          1200        Frédéric Vasseur
## 6           2019           450 Alessandro Alunni Bravi
##   nombre_equipo_presupuesto presupuesto_millones pct_desarrollo pct_personal
## 1                Alfa Romeo                 89.8           39.1         39.6
## 2                      <NA>                   NA             NA           NA
## 3                      <NA>                   NA             NA           NA
## 4                      <NA>                   NA             NA           NA
## 5                      <NA>                   NA             NA           NA
## 6                Alfa Romeo                 89.8           39.1         39.6
##   code nombre   apellido fecha_nacimiento nacionalidad equipo_actual
## 1  TSU   Yuki    Tsunoda       2000-05-11     Japanese    AlphaTauri
## 2 <NA>   <NA>       <NA>             <NA>         <NA>          <NA>
## 3  HUL   Nico Hülkenberg       1987-08-19       German          Haas
## 4 <NA>   <NA>       <NA>             <NA>         <NA>          <NA>
## 5  RAI   Kimi  Räikkönen       1979-10-17      Finnish      Retirado
## 6 <NA>   <NA>       <NA>             <NA>         <NA>          <NA>
##   estatura_cm peso_kg salario_anual_millones titulos_mundiales driver_id_bio
## 1         159      54                      2                 0       tsunoda
## 2          NA      NA                     NA                NA          <NA>
## 3         184      74                      4                 0    hulkenberg
## 4          NA      NA                     NA                NA          <NA>
## 5         175      70                      0                 1     raikkonen
## 6          NA      NA                     NA                NA          <NA>

2. Preparación de datos para el modelo

Referencia: Tema 7 — Análisis y algoritmos

>>> BLOQUE 1 — A COMPLETAR POR EL GRUPO <<<

Preparar el dataset para modelado:

  1. Seleccionar las columnas relevantes. Variable objetivo: points. Variables predictoras sugeridas: grid, laps, posiciones_ganadas.
  2. Eliminar filas con NAs en esas columnas. PISTA: na.omit().
  3. Filtrar solo resultados válidos (points >= 0, grid > 0, laps > 0).

Guardar en df_modelo. Mostrar dim() y summary().

# Seleccionamos variables relevantes y limpiamos datos para modelado
df_modelo <- df_gold %>%
  select(points, grid, laps, posiciones_ganadas) %>%
  filter(points >= 0, grid > 0, laps > 0) %>%
  na.omit()

dim(df_modelo)
## [1] 2295    4
summary(df_modelo)
##      points            grid            laps       posiciones_ganadas 
##  Min.   : 0.000   Min.   : 1.00   Min.   :48.00   Min.   :-19.00000  
##  1st Qu.: 0.000   1st Qu.: 5.00   1st Qu.:55.00   1st Qu.: -6.00000  
##  Median : 1.000   Median :10.00   Median :59.00   Median :  0.00000  
##  Mean   : 5.134   Mean   :10.38   Mean   :59.69   Mean   : -0.01743  
##  3rd Qu.: 9.000   3rd Qu.:15.00   3rd Qu.:65.00   3rd Qu.:  6.00000  
##  Max.   :25.000   Max.   :20.00   Max.   :70.00   Max.   : 19.00000

El conjunto de modelado queda en 2295 observaciones con 4 variables. Al filtrar valores invalidos y eliminar NAs, el modelo trabajara con un subconjunto mas coherente y comparable.


3. Correlación previa

# [PROPORCIONADO] — Verificamos la correlación antes de modelar
cat("--- Correlación entre las variables seleccionadas ---\n")
## --- Correlación entre las variables seleccionadas ---
cor_modelo <- cor(df_modelo, use = "complete.obs")
print(round(cor_modelo, 3))
##                    points   grid   laps posiciones_ganadas
## points              1.000 -0.021  0.004              0.583
## grid               -0.021  1.000 -0.019              0.709
## laps                0.004 -0.019  1.000             -0.011
## posiciones_ganadas  0.583  0.709 -0.011              1.000
# [PROPORCIONADO] — Scatter plots de las predictoras vs. la variable objetivo
pairs(df_modelo, main = "Scatter Plot Matrix - Variables del modelo")


4. Regresión lineal simple

Referencia: Tema 7 — Análisis y algoritmos (regresión lineal)

>>> BLOQUE 2 — A COMPLETAR POR EL GRUPO <<<

Entrenar un modelo de regresión lineal simple con lm() que prediga points a partir de grid.

PISTA: modelo_simple <- lm(points ~ grid, data = df_modelo)

Mostrar summary() e interpretar:

  • ¿El coeficiente de grid es positivo o negativo? ¿Tiene sentido?
  • ¿Cuál es el R-cuadrado? ¿Qué porcentaje de la variabilidad explica?
  • ¿El p-valor es significativo (< 0.05)?
# Ajustamos un modelo de regresión lineal simple usando grid como predictor
modelo_simple <- lm(points ~ grid, data = df_modelo)
summary(modelo_simple)
## 
## Call:
## lm(formula = points ~ grid, data = df_modelo)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -5.382 -5.144 -4.250  3.869 20.120 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  5.40849    0.30664  17.638   <2e-16 ***
## grid        -0.02640    0.02578  -1.024    0.306    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 7.162 on 2293 degrees of freedom
## Multiple R-squared:  0.0004573,  Adjusted R-squared:  2.135e-05 
## F-statistic: 1.049 on 1 and 2293 DF,  p-value: 0.3059
coef_grid_simple <- coef(modelo_simple)["grid"]
r2_simple <- summary(modelo_simple)$r.squared
pvalor_grid_simple <- summary(modelo_simple)$coefficients["grid", "Pr(>|t|)"]

Tu interpretación aquí:

El coeficiente de grid es -0.02640, por lo que la relación estimada es negativa entre la posición de salida y los puntos obtenidos. Esto sugiere que, en promedio, salir en posiciones más retrasadas se asocia con una menor puntuación.

Sin embargo, el p-valor de grid es 0.306, superior a 0.05, lo que indica que esta relación no es estadísticamente significativa.

Además, el modelo presenta un R-cuadrado de 0.046%, lo que implica que prácticamente no explica la variabilidad de los puntos.

En conjunto, estos resultados sugieren que la posición de salida, por sí sola, no es un buen predictor del rendimiento final en este dataset.


5. Regresión lineal múltiple

Referencia: Tema 7 — Análisis y algoritmos

>>> BLOQUE 3 — A COMPLETAR POR EL GRUPO <<<

Entrenar un modelo de regresión lineal múltiple con lm() y al menos 2 predictores (ej: grid y laps, o grid y posiciones_ganadas).

PISTA: modelo_multiple <- lm(points ~ grid + laps, data = df_modelo)

Mostrar summary() y comparar:

  • ¿Ha mejorado el R-cuadrado respecto al modelo simple?
  • ¿Todos los predictores son significativos?
# Ajustamos un modelo múltiple para capturar mejor la complejidad del rendimiento
modelo_multiple <- lm(points ~ grid + laps + posiciones_ganadas, data = df_modelo)
summary(modelo_multiple)
## 
## Call:
## lm(formula = points ~ grid + laps + posiciones_ganadas, data = df_modelo)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -10.8841  -3.2240  -0.6732   2.6481  10.2344 
## 
## Coefficients:
##                      Estimate Std. Error t value Pr(>|t|)    
## (Intercept)        16.3333590  0.8196418  19.927   <2e-16 ***
## grid               -1.0813006  0.0193494 -55.883   <2e-16 ***
## laps                0.0008096  0.0131923   0.061    0.951    
## posiciones_ganadas  1.0533136  0.0137050  76.856   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.788 on 2291 degrees of freedom
## Multiple R-squared:  0.7207, Adjusted R-squared:  0.7203 
## F-statistic:  1970 on 3 and 2291 DF,  p-value: < 2.2e-16
r2_multiple <- summary(modelo_multiple)$r.squared
coeficientes_multiple <- summary(modelo_multiple)$coefficients
predictores_significativos <- rownames(coeficientes_multiple)[-1][coeficientes_multiple[-1, "Pr(>|t|)"] < 0.05]

Tu interpretación aquí:

El modelo múltiple alcanza un R-cuadrado de 0.7207, frente al 0.0005 del modelo simple, por lo que mejora de forma significativa la capacidad explicativa al añadir más predictores.

El coeficiente de grid es -1.0813, lo que indica una relación negativa: salir en posiciones más retrasadas se asocia con una menor puntuación.

El coeficiente de posiciones_ganadas es 1.0533, lo que sugiere que ganar posiciones durante la carrera tiene un impacto positivo y muy relevante en los puntos obtenidos.

Por otro lado, la variable laps presenta un p-valor de 0.951, por lo que no es estadísticamente significativa y no parece aportar capacidad explicativa al modelo.

En conjunto, los resultados indican que el modelo múltiple mejora sustancialmente respecto al modelo simple y que variables relacionadas con la posición en carrera (grid y posiciones ganadas) son determinantes para explicar el rendimiento, mientras que otras como el número de vueltas tienen un impacto limitado en este dataset.


6. Evaluación del modelo — Métricas

Referencia: Tema 8 — Validación y evaluación de modelos

>>> BLOQUE 4 — A COMPLETAR POR EL GRUPO <<<

Calcular las métricas de evaluación del modelo múltiple:

  1. Predicciones: predicciones <- predict(modelo_multiple, df_modelo)
  2. MSE = media de (real - predicho)²: mse <- mean((df_modelo$points - predicciones)^2)
  3. RMSE = raíz del MSE: rmse <- sqrt(mse)
  4. del modelo: r2 <- summary(modelo_multiple)$r.squared
# Evaluamos el modelo mediante métricas de error (MSE, RMSE) y ajuste (R2)
predicciones <- predict(modelo_multiple, df_modelo)
mse <- mean((df_modelo$points - predicciones)^2)
rmse <- sqrt(mse)
r2 <- summary(modelo_multiple)$r.squared

cat("MSE:", round(mse, 4), "\n")
## MSE: 14.3232
cat("RMSE:", round(rmse, 4), "\n")
## RMSE: 3.7846
cat("R2:", round(r2, 4), "\n")
## R2: 0.7207

[PROPORCIONADO] — Gráfico predicho vs. real

# [PROPORCIONADO]
df_pred <- data.frame(
  real = df_modelo$points,
  predicho = predict(modelo_multiple, df_modelo)
)

ggplot(df_pred, aes(x = real, y = predicho)) +
  geom_point(alpha = 0.3, color = "steelblue") +
  geom_abline(intercept = 0, slope = 1, color = "red",
              linetype = "dashed", linewidth = 1) +
  labs(title = "Regresión Lineal Múltiple: Predicho vs. Real",
       subtitle = paste("R² =", round(summary(modelo_multiple)$r.squared, 3)),
       x = "Puntos reales",
       y = "Puntos predichos") +
  theme_minimal()


7. Predicción con nuevos datos

Referencia: Tema 7 — Análisis y algoritmos (predict)

>>> BLOQUE 5 — A COMPLETAR POR EL GRUPO <<<

Usar predict() para estimar los puntos en un escenario hipotético. El data.frame de nuevos datos debe tener exactamente las mismas columnas predictoras del modelo.

Probar al menos 2 escenarios y comentar si los resultados tienen sentido.

Ejemplo:

nuevos_datos <- data.frame(grid = 3, laps = 55)
prediccion <- predict(modelo_multiple, nuevos_datos)
# Generamos predicciones para escenarios hipotéticos
nuevos_datos <- data.frame(
  grid = c(3, 12),
  laps = c(55, 55),
  posiciones_ganadas = c(2, -1)
)

predicciones_nuevas <- predict(modelo_multiple, nuevos_datos)

data.frame(
  escenario = c("Salida P3 y gana 2 posiciones", "Salida P12 y pierde 1 posicion"),
  prediccion_puntos = round(predicciones_nuevas, 2)
)
##                        escenario prediccion_puntos
## 1  Salida P3 y gana 2 posiciones             15.24
## 2 Salida P12 y pierde 1 posicion              2.35

Tu interpretación de las predicciones aquí:

Las predicciones son coherentes si el escenario mas favorable recibe una estimacion de puntos mayor. En este caso, el escenario de salida P3 con ganancia de posiciones se estima en 15.24 puntos, frente a 2.35 para el escenario de salida P12. Aun asi, conviene recordar que el modelo no capta factores como lluvia, estrategia, sanciones o abandonos.


8. Guardado del modelo

# [PROPORCIONADO]
saveRDS(modelo_simple, "datos/gold/modelo_regresion_simple.rds")
saveRDS(modelo_multiple, "datos/gold/modelo_regresion_multiple.rds")

cat("\n========================================\n")
## 
## ========================================
cat("Modelos guardados en datos/gold/\n")
## Modelos guardados en datos/gold/
cat("Script 3 completado con éxito.\n")
## Script 3 completado con éxito.
cat("========================================\n")
## ========================================

En este script se ha construido y evaluado un modelo predictivo para estimar los puntos obtenidos en una carrera.

El modelo simple ha mostrado una capacidad explicativa prácticamente nula, lo que indica que la posición de salida por sí sola no es suficiente para explicar el rendimiento.

Sin embargo, el modelo múltiple mejora significativamente el ajuste, destacando la importancia de variables como la posición en parrilla y la evolución durante la carrera.

A pesar de ello, el modelo sigue siendo una simplificación de la realidad, ya que no incorpora factores externos como estrategia, condiciones meteorológicas o abandonos, lo que limita su capacidad predictiva completa.


Pregunta teórica 5

¿Qué significa el R-cuadrado de un modelo de regresión lineal? Si obtienes un R-cuadrado de 0.35, ¿cómo interpretarías el resultado? ¿Significa que el modelo es malo?

Tu respuesta aquí (5-10 líneas):

El R-cuadrado indica la proporcion de la variabilidad de la variable objetivo que el modelo consigue explicar a partir de las variables predictoras. Si el R-cuadrado es 0.35, significa que el modelo explica aproximadamente el 35% de la variacion observada, mientras que el resto depende de otros factores no incluidos o de ruido. Eso no implica automaticamente que el modelo sea malo: en problemas complejos, un valor asi puede ser razonable y util. Su interpretacion siempre depende del contexto, del objetivo del analisis y de si mejora frente a alternativas mas simples.