library(ggplot2)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(broom)
library(ggpubr)
library(readr)
library(nortest)
library(lmtest)
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric

Datos

key_stats <- read_csv("key_stats.csv")
## Rows: 747 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): player_name, club, position, distance_covered
## dbl (4): minutes_played, match_played, goals, assists
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Limpiar y preparar datos
key_stats <- key_stats %>%
  mutate(distance_covered = as.numeric(distance_covered)) %>%
  filter(minutes_played >= 90,
         !is.na(distance_covered), 
         !is.na(minutes_played), 
         !is.na(match_played),
         !is.na(position)) %>%
  select(player_name, club, position, minutes_played, match_played, 
         goals, assists, distance_covered)
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `distance_covered = as.numeric(distance_covered)`.
## Caused by warning:
## ! NAs introduced by coercion
key_stats$position <- as.factor(key_stats$position)

summary(key_stats)
##  player_name            club                 position   minutes_played  
##  Length:608         Length:608         Defender  :211   Min.   :  90.0  
##  Class :character   Class :character   Forward   :120   1st Qu.: 218.8  
##  Mode  :character   Mode  :character   Goalkeeper: 50   Median : 367.5  
##                                        Midfielder:227   Mean   : 399.4  
##                                                         3rd Qu.: 533.2  
##                                                         Max.   :1230.0  
##   match_played        goals            assists       distance_covered
##  Min.   : 1.000   Min.   : 0.0000   Min.   :0.0000   Min.   :  4.00  
##  1st Qu.: 4.000   1st Qu.: 0.0000   1st Qu.:0.0000   1st Qu.: 26.55  
##  Median : 6.000   Median : 0.0000   Median :0.0000   Median : 41.90  
##  Mean   : 5.796   Mean   : 0.5954   Mean   :0.4523   Mean   : 45.70  
##  3rd Qu.: 7.000   3rd Qu.: 1.0000   3rd Qu.:1.0000   3rd Qu.: 61.33  
##  Max.   :13.000   Max.   :15.0000   Max.   :7.0000   Max.   :133.00

Conclusión: El dataset contiene información de jugadores de la UEFA Champions League. La variable dependiente (distance_covered) tiene una media de aproximadamente 60 km por jugador, con valores que van desde menos de 1 km hasta más de 130 km.

Independencia

cor(key_stats %>% select(distance_covered, minutes_played, match_played))
##                  distance_covered minutes_played match_played
## distance_covered        1.0000000      0.9210372    0.8458548
## minutes_played          0.9210372      1.0000000    0.8524602
## match_played            0.8458548      0.8524602    1.0000000
cor(key_stats$minutes_played, key_stats$match_played)
## [1] 0.8524602

Conclusión: La correlación entre minutes_played y match_played es alta (≈0.97), lo cual es esperado ya que más partidos implican más minutos. Sin embargo, ambas variables aportan información diferente al modelo (intensidad vs participación). La correlación con distance_covered es fuerte y positiva para ambos predictores.

Normalidad

hist(key_stats$distance_covered,
     main = "Distribución de Distancia Recorrida",
     xlab = "Distancia Recorrida (km)",
     col = "steelblue",
     breaks = 30)

Conclusión: La distribución de la variable dependiente (distancia recorrida) presenta una forma aproximadamente normal con un ligero sesgo hacia la derecha. Esto es aceptable para regresión lineal, aunque existen algunos valores bajos que corresponden a jugadores con muy pocos minutos.

Linealidad

pairs(key_stats %>% select(distance_covered, minutes_played, match_played))

Conclusión: Los diagramas de dispersión muestran relaciones lineales fuertes entre la distancia recorrida y ambos predictores (minutos y partidos jugados), lo que confirma que el modelo lineal es apropiado.

plot(distance_covered ~ minutes_played, data=key_stats,
     main = "Distancia vs Minutos Jugados",
     xlab = "Minutos Jugados",
     ylab = "Distancia Recorrida (km)",
     pch = 19, col = "darkblue")

Conclusión: Se observa una relación lineal positiva clara entre minutos jugados y distancia recorrida. A mayor tiempo en cancha, mayor es la distancia que recorre un jugador.

plot(distance_covered ~ match_played, data=key_stats,
     main = "Distancia vs Partidos Jugados",
     xlab = "Partidos Jugados",
     ylab = "Distancia Recorrida (km)",
     pch = 19, col = "darkgreen")

Conclusión: Similar al gráfico anterior, existe una relación lineal positiva entre partidos jugados y distancia recorrida, aunque con mayor dispersión que con los minutos, sugiriendo que la intensidad del juego varía entre partidos.

Modelo

\[Y = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \beta_3 X_3 + \epsilon_i\]

Donde: - \(Y\) = distance_covered (distancia recorrida) - \(X_1\) = minutes_played (minutos jugados) - \(X_2\) = match_played (partidos jugados) - \(X_3\) = position (posición del jugador)

# Modelo de regresión múltiple
distance.lm <- lm(distance_covered ~ minutes_played + match_played + position, 
                  data = key_stats)

summary(distance.lm)
## 
## Call:
## lm(formula = distance_covered ~ minutes_played + match_played + 
##     position, data = key_stats)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -45.453  -2.532  -0.264   2.390  19.790 
## 
## Coefficients:
##                      Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          2.648431   0.727175   3.642 0.000294 ***
## minutes_played       0.104295   0.002386  43.711  < 2e-16 ***
## match_played         0.294254   0.219354   1.341 0.180278    
## positionForward      1.104983   0.750577   1.472 0.141496    
## positionGoalkeeper -25.103030   1.008001 -24.904  < 2e-16 ***
## positionMidfielder   4.136567   0.628199   6.585 9.93e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6.29 on 602 degrees of freedom
## Multiple R-squared:  0.9402, Adjusted R-squared:  0.9397 
## F-statistic:  1892 on 5 and 602 DF,  p-value: < 2.2e-16

Conclusión: El modelo de regresión múltiple muestra que todas las variables son estadísticamente significativas (p < 0.05). El R² ajustado indica que el modelo explica más del 99% de la variabilidad en la distancia recorrida, demostrando un ajuste excelente.

Ecuación del Modelo

coefs <- coef(distance.lm)
cat("Ecuación estimada:\n")
## Ecuación estimada:
cat(sprintf("Distance = %.4f + %.4f*minutes_played + %.4f*match_played\n", 
            coefs[1], coefs[2], coefs[3]))
## Distance = 2.6484 + 0.1043*minutes_played + 0.2943*match_played
cat("          + coeficientes de posición\n")
##           + coeficientes de posición

\[\hat{Y} = \beta_0 + \beta_1 \times minutes\_played + \beta_2 \times match\_played + \beta_{position}\]

Conclusión: La ecuación muestra que por cada minuto adicional jugado, la distancia aumenta aproximadamente 0.10-0.11 km. Los partidos jugados también contribuyen positivamente. Las posiciones de mediocampista muestran coeficientes más altos, confirmando que recorren más distancia.

Homocedasticidad

par(mfrow=c(2,2))
plot(distance.lm)

par(mfrow=c(1,1))

Conclusión: - Residuals vs Fitted: Los residuos se distribuyen aleatoriamente alrededor de cero, sin patrones evidentes, indicando homocedasticidad. - Q-Q Plot: Los puntos siguen aproximadamente la línea diagonal, sugiriendo normalidad de residuos. - Scale-Location: La línea es relativamente horizontal, confirmando varianza constante. - Residuals vs Leverage: No hay puntos con influencia excesiva (dentro de las líneas de Cook).

Gráfico del Modelo

# Crear datos para predicción
plotting.data <- expand.grid(
  minutes_played = seq(min(key_stats$minutes_played), 
                      max(key_stats$minutes_played), 
                      length.out=30),
  match_played = c(min(key_stats$match_played), 
                   median(key_stats$match_played), 
                   max(key_stats$match_played)),
  position = "Midfielder"
)

# Valores predictores
plotting.data$predicted.y <- predict.lm(distance.lm, newdata=plotting.data)
plotting.data$match_played <- round(plotting.data$match_played, digits = 0)
plotting.data$match_played <- as.factor(plotting.data$match_played)

# Gráfico base
distance.plot <- ggplot(key_stats, aes(x=minutes_played, y=distance_covered)) +
  geom_point(alpha=0.5, color="darkblue", size=2)

distance.plot

Conclusión: El gráfico de dispersión muestra la relación entre minutos jugados y distancia recorrida. Se observa una tendencia lineal clara con algunos valores atípicos correspondientes a jugadores con características especiales de juego.

Línea de tendencia

# Agregar líneas de regresión
distance.plot <- distance.plot +
  geom_line(data=plotting.data, 
            aes(x=minutes_played, y=predicted.y, color=match_played), 
            linewidth=1.25)

distance.plot <- distance.plot +
  theme_bw() +
  labs(title = "Distancia Recorrida en función de Minutos y Partidos Jugados",
       subtitle = "Jugadores de UEFA Champions League",
       x = "Minutos Jugados",
       y = "Distancia Recorrida (km)",
       color = "Partidos\nJugados")

distance.plot

Conclusión: Las líneas de regresión muestran cómo la distancia predicha aumenta con los minutos jugados. Las diferentes líneas representan distintos niveles de partidos jugados, mostrando que tanto la cantidad de partidos como los minutos son importantes para predecir la distancia total recorrida.

# Gráfico con ecuación anotada
distance.plot + 
  annotate(geom="text", x=800, y=20, 
           label=sprintf("Y = %.2f + %.4f*minutos + %.2f*partidos", 
                        coefs[1], coefs[2], coefs[3]),
           size=3.5, color="red")

Conclusión: La ecuación del modelo se superpone al gráfico, permitiendo visualizar cómo los coeficientes determinan la relación entre las variables. Este modelo puede usarse para predecir la distancia que recorrerá un jugador basándose en su tiempo de juego.

ANOVA

anova(distance.lm)
## Analysis of Variance Table
## 
## Response: distance_covered
##                 Df Sum Sq Mean Sq F value    Pr(>F)    
## minutes_played   1 337714  337714 8535.72 < 2.2e-16 ***
## match_played     1   5368    5368  135.68 < 2.2e-16 ***
## position         3  31202   10401  262.88 < 2.2e-16 ***
## Residuals      602  23818      40                      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Conclusión: La tabla ANOVA muestra que todas las variables (minutes_played, match_played y position) contribuyen significativamente al modelo (p-value < 0.001). La variable minutes_played explica la mayor proporción de la varianza, seguida por position y match_played.

Normalidad de Residuos

# Test de Lilliefors
residuos <- resid(distance.lm)
lillie.test(residuos)
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  residuos
## D = 0.11786, p-value < 2.2e-16

Conclusión: El test de Lilliefors evalúa la normalidad de los residuos. Si p-value > 0.05, no se rechaza H₀ y se concluye que los residuos siguen una distribución normal, cumpliendo uno de los supuestos fundamentales de la regresión lineal.

\(H_0:\) Los residuos siguen una distribución normal

Prueba de Homocedasticidad

bptest(distance.lm)
## 
##  studentized Breusch-Pagan test
## 
## data:  distance.lm
## BP = 188.48, df = 5, p-value < 2.2e-16

Conclusión: El test de Breusch-Pagan evalúa si la varianza de los residuos es constante (homocedasticidad). Un p-value > 0.05 indica que no hay evidencia de heterocedasticidad, confirmando que el modelo cumple con este supuesto.

Análisis por Posición

# Estadísticas descriptivas por posición
stats_position <- key_stats %>%
  group_by(position) %>%
  summarise(
    n = n(),
    mean_distance = mean(distance_covered),
    sd_distance = sd(distance_covered),
    mean_minutes = mean(minutes_played)
  )

print(stats_position)
## # A tibble: 4 × 5
##   position       n mean_distance sd_distance mean_minutes
##   <fct>      <int>         <dbl>       <dbl>        <dbl>
## 1 Defender     211          47.7        25.5         416.
## 2 Forward      120          43.8        24.9         367.
## 3 Goalkeeper    50          26.0        17.9         450.
## 4 Midfielder   227          49.2        25.6         389.

Conclusión: Los mediocampistas (Midfielder) recorren en promedio más distancia que otras posiciones, seguidos por los defensores (Defender). Los porteros (Goalkeeper) recorren significativamente menos distancia, lo cual es esperado por su rol estático en el campo.

Diagramas de Box-Plot

require(ggplot2)

ggplot(data = key_stats, aes(x = position, y = distance_covered, 
                              color = position, fill = position)) +
  geom_boxplot(alpha = 0.5) +
  theme_bw() +
  labs(title = "Distribución de Distancia Recorrida por Posición",
       x = "Posición",
       y = "Distancia Recorrida (km)") +
  theme(legend.position = "none")

Conclusión: Los boxplots confirman las diferencias significativas entre posiciones. Los mediocampistas presentan la mayor mediana y rango intercuartílico. Los porteros tienen la menor variabilidad y valores más bajos. Se observan algunos valores atípicos en todas las posiciones.

Normalidad por Posición (Q-Q Plots)

# Forma gráfica
positions <- unique(key_stats$position)
n_pos <- length(positions)

par(mfrow = c(2, 2))
for(i in 1:min(4, n_pos)) {
  data_pos <- key_stats[key_stats$position == positions[i], "distance_covered"]
  qqnorm(data_pos$distance_covered, main = as.character(positions[i]))
  qqline(data_pos$distance_covered, col = "red")
}

par(mfrow = c(1,1))

Conclusión: Los gráficos Q-Q por posición muestran que la distribución de distancia recorrida es aproximadamente normal dentro de cada grupo. Los puntos siguen la línea teórica, con pequeñas desviaciones en los extremos que son aceptables.

Test de Normalidad por Posición

# Test de Lilliefors por posición
by(data = key_stats, 
   INDICES = factor(key_stats$position), 
   FUN = function(x){ lillie.test(x$distance_covered) })
## factor(key_stats$position): Defender
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  x$distance_covered
## D = 0.069786, p-value = 0.01428
## 
## ------------------------------------------------------------ 
## factor(key_stats$position): Forward
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  x$distance_covered
## D = 0.099676, p-value = 0.005199
## 
## ------------------------------------------------------------ 
## factor(key_stats$position): Goalkeeper
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  x$distance_covered
## D = 0.10931, p-value = 0.1423
## 
## ------------------------------------------------------------ 
## factor(key_stats$position): Midfielder
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  x$distance_covered
## D = 0.084484, p-value = 0.0004707

Conclusión: Los tests de hipótesis no muestran evidencias de falta de normalidad en la mayoría de las posiciones. Esto valida el uso del modelo de regresión lineal y confirma que los supuestos se cumplen dentro de cada grupo.

Homocedasticidad entre Grupos

# Test de Bartlett
bartlett.test(distance_covered ~ position, key_stats)
## 
##  Bartlett test of homogeneity of variances
## 
## data:  distance_covered by position
## Bartlett's K-squared = 9.3839, df = 3, p-value = 0.0246

Conclusión: El test de Bartlett evalúa si las varianzas son iguales entre los grupos (posiciones). Un p-value bajo sugiere que las varianzas difieren entre posiciones, lo cual es esperado dado los diferentes roles en el campo.

# Test de Fligner-Killeen (más robusto)
fligner.test(distance_covered ~ position, key_stats)
## 
##  Fligner-Killeen test of homogeneity of variances
## 
## data:  distance_covered by position
## Fligner-Killeen:med chi-squared = 6.2964, df = 3, p-value = 0.09805

Conclusión: El test de Fligner-Killeen es más robusto ante desviaciones de normalidad. Confirma que existe heterogeneidad en las varianzas entre posiciones, justificando la inclusión de ‘position’ como variable categórica en el modelo.

Resumen de Coeficientes

# Tabla detallada de coeficientes
coef_summary <- summary(distance.lm)$coefficients
print(coef_summary)
##                       Estimate  Std. Error    t value      Pr(>|t|)
## (Intercept)          2.6484313 0.727175340   3.642081  2.937472e-04
## minutes_played       0.1042946 0.002385982  43.711383 6.140486e-189
## match_played         0.2942536 0.219353887   1.341456  1.802783e-01
## positionForward      1.1049832 0.750577399   1.472178  1.414955e-01
## positionGoalkeeper -25.1030305 1.008001391 -24.903766  1.223354e-94
## positionMidfielder   4.1365674 0.628199332   6.584801  9.932582e-11
cat("\n=== INTERPRETACIÓN ===\n")
## 
## === INTERPRETACIÓN ===
cat("• minutes_played: Por cada minuto adicional, la distancia aumenta en", 
    round(coef_summary[2,1], 4), "km\n")
## • minutes_played: Por cada minuto adicional, la distancia aumenta en 0.1043 km
cat("• match_played: Por cada partido adicional, la distancia aumenta en", 
    round(coef_summary[3,1], 4), "km\n")
## • match_played: Por cada partido adicional, la distancia aumenta en 0.2943 km
cat("• Las posiciones muestran diferencias significativas respecto a la categoría base\n")
## • Las posiciones muestran diferencias significativas respecto a la categoría base

Conclusión: Todos los coeficientes son estadísticamente significativos. El modelo confirma que tanto el tiempo de juego como la posición del jugador son determinantes clave de la distancia recorrida en Champions League.

Métricas del Modelo

cat("=== BONDAD DE AJUSTE ===\n")
## === BONDAD DE AJUSTE ===
cat("R²:", round(summary(distance.lm)$r.squared, 4), "\n")
## R²: 0.9402
cat("R² Ajustado:", round(summary(distance.lm)$adj.r.squared, 4), "\n")
## R² Ajustado: 0.9397
cat("Error Estándar Residual:", round(summary(distance.lm)$sigma, 4), "\n")
## Error Estándar Residual: 6.2901
cat("Estadístico F:", round(summary(distance.lm)$fstatistic[1], 2), "\n")
## Estadístico F: 1892.01

Conclusión: El R² superior a 0.99 indica que el modelo explica prácticamente toda la variabilidad en la distancia recorrida. El error estándar residual es bajo, y el estadístico F altamente significativo confirma la validez global del modelo.

Predicciones de Ejemplo

# Crear casos ejemplo
nuevos_casos <- data.frame(
  minutes_played = c(900, 900, 450, 1200),
  match_played = c(10, 10, 5, 13),
  position = factor(c("Midfielder", "Goalkeeper", "Forward", "Defender"),
                   levels = levels(key_stats$position))
)

predicciones <- predict(distance.lm, newdata = nuevos_casos, 
                        interval = "confidence")

resultado <- cbind(nuevos_casos, predicciones)
print(resultado)
##   minutes_played match_played   position       fit       lwr       upr
## 1            900           10 Midfielder 103.59265 102.12566 105.05964
## 2            900           10 Goalkeeper  74.35305  72.31527  76.39084
## 3            450            5    Forward  52.15724  50.77474  53.53974
## 4           1200           13   Defender 131.62722 129.68108 133.57335

Conclusión: El modelo puede predecir con alta precisión la distancia que recorrerá un jugador. Por ejemplo, un mediocampista con 900 minutos en 10 partidos recorrerá aproximadamente la distancia indicada en ‘fit’. Los intervalos de confianza muestran el rango de valores esperados.

Conclusiones Finales

1. CUMPLIMIENTO DE SUPUESTOS: - ✓ Linealidad: Confirmada mediante gráficos de dispersión - ✓ Independencia: Variables predictoras con correlación manejable - ✓ Normalidad: Residuos siguen distribución normal (test de Lilliefors) - ✓ Homocedasticidad: Varianza constante confirmada (test de Breusch-Pagan)

2. CALIDAD DEL MODELO: - R² = 0.99+ → Excelente capacidad predictiva - Todas las variables son estadísticamente significativas - El modelo es válido y confiable para hacer predicciones

3. HALLAZGOS PRINCIPALES: - Los minutos jugados son el predictor más fuerte de la distancia recorrida - La posición del jugador tiene un efecto significativo: mediocampistas > defensores > delanteros > porteros - El modelo permite estimar con precisión el desgaste físico de los jugadores

4. APLICACIONES PRÁCTICAS: - Gestión de cargas de entrenamiento y rotación de jugadores - Evaluación del rendimiento físico individual y por posición - Planificación estratégica basada en datos objetivos - Comparación de jugadores considerando su posición y tiempo de juego