1 Presentación de la base de datos

La base de datos seleccionada en la Actividad 112 corresponde a las estadísticas individuales de los jugadores de la Premier League (primera división del fútbol inglés). Cada fila es un jugador y cada columna un atributo personal o una estadística acumulada de su carrera en la liga: club, posición, nacionalidad, edad, partidos jugados, goles, pases, tarjetas, entre otras.

# Importación de datos.
# Si el Excel está en la misma carpeta del .Rmd se usa directamente;
# si no, se busca automáticamente en las carpetas habituales del usuario.

archivo <- "premierleague_probabilidad.xlsx"

if (!file.exists(archivo)) {
  # Carpeta personal real (en Windows ~ apunta a Documentos, no al perfil)
  perfil <- c(path.expand("~"), Sys.getenv("USERPROFILE"), dirname(path.expand("~")))
  perfil <- unique(perfil[perfil != ""])

  subcarpetas <- c("Downloads", "Descargas", "Desktop", "Escritorio",
                   "Documents", "Documentos")

  carpetas <- c(
    getwd(),
    dirname(getwd()),
    perfil,
    as.vector(outer(perfil, subcarpetas, file.path)),
    as.vector(outer(file.path(perfil, "OneDrive"), subcarpetas, file.path))
  )
  encontrados <- character(0)
  for (d in unique(carpetas)) {
    if (dir.exists(d)) {
      encontrados <- c(encontrados,
                       list.files(d, pattern = "premierleague.*\\.xlsx$",
                                  full.names = TRUE, ignore.case = TRUE))
    }
  }
  if (length(encontrados) == 0) {
    stop("No se encontró el archivo premierleague_probabilidad.xlsx. ",
         "Guárdalo en la misma carpeta que este .Rmd.")
  }
  archivo <- encontrados[1]
}

pl <- read_excel(archivo)

basename(archivo)   # archivo utilizado
## [1] "premierleague_probabilidad.xlsx"
dim(pl)             # filas y columnas originales
## [1] 612  59

1.1 Depuración previa

El archivo original trae filas en blanco al final de la hoja y nombres de columna con espacios. Antes de analizar se eliminan los registros vacíos y se renombran las variables de interés al español.

pl <- pl %>%
  filter(!is.na(Name), !is.na(Club), !is.na(Position)) %>%   # quita filas vacías
  rename(
    jugador      = Name,
    club         = Club,
    posicion     = Position,
    nacionalidad = Nationality,
    edad         = Age,
    partidos     = Appearances,
    goles        = Goals,
    asistencias  = Assists,
    pases        = Passes,
    amarillas    = `Yellow cards`
  ) %>%
  mutate(
    club = gsub("-", " ", club),                              # nombres legibles
    posicion = recode(posicion,
                      "Goalkeeper" = "Portero",
                      "Defender"   = "Defensa",
                      "Midfielder" = "Mediocampista",
                      "Forward"    = "Delantero"),
    posicion = factor(posicion,
                      levels = c("Portero", "Defensa", "Mediocampista", "Delantero"))
  )

n <- nrow(pl)
n                # tamaño final de la muestra
## [1] 571

Después de la depuración se trabaja con 571 jugadores.

1.2 Variables seleccionadas

Variable Tipo Escala de medición
posicion Cualitativa Nominal (4 categorías)
club Cualitativa Nominal (20 categorías)
edad Cuantitativa discreta Razón (años cumplidos)
partidos Cuantitativa discreta Razón (conteo de partidos)

2 Funciones auxiliares

Para los indicadores de forma y la moda se definen tres funciones sencillas, ya que R base no las incluye.

# Moda: valor (o valores) con mayor frecuencia
moda <- function(x) {
  x <- x[!is.na(x)]
  tab <- table(x)
  as.numeric(names(tab)[tab == max(tab)])
}

# Coeficiente de asimetría de Fisher
asimetria <- function(x) {
  x <- x[!is.na(x)]
  mean((x - mean(x))^3) / (sd(x) * sqrt((length(x) - 1) / length(x)))^3
}

# Curtosis en exceso (0 = distribución mesocúrtica)
curtosis <- function(x) {
  x <- x[!is.na(x)]
  mean((x - mean(x))^4) / (sd(x) * sqrt((length(x) - 1) / length(x)))^4 - 3
}

3 Análisis de variables cualitativas

3.1 Variable 1: posición en el campo

3.1.1 Tabla de frecuencias

fa <- table(pl$posicion)                     # frecuencia absoluta
fr <- prop.table(fa) * 100                   # frecuencia relativa (%)

tabla_pos <- data.frame(
  Posicion            = names(fa),
  Frec_absoluta       = as.numeric(fa),
  Frec_relativa       = round(as.numeric(fr), 2),
  Frec_abs_acumulada  = cumsum(as.numeric(fa)),
  Frec_rel_acumulada  = round(cumsum(as.numeric(fr)), 2)
)

kable(tabla_pos,
      col.names = c("Posición", "ni", "hi (%)", "Ni", "Hi (%)"),
      caption = "Tabla 1. Distribución de frecuencias de la posición de juego")
Tabla 1. Distribución de frecuencias de la posición de juego
Posición ni hi (%) Ni Hi (%)
Portero 69 12.08 69 12.08
Defensa 194 33.98 263 46.06
Mediocampista 198 34.68 461 80.74
Delantero 110 19.26 571 100.00

3.1.2 Gráfico

ggplot(pl, aes(x = posicion)) +
  geom_bar(fill = azul, width = 0.65) +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.4, size = 3.5) +
  labs(title = "Jugadores por posición de juego",
       x = NULL, y = "Número de jugadores") +
  theme_minimal(base_size = 12) +
  theme(panel.grid.major.x = element_blank())

3.1.3 Interpretación

La moda de la variable es Mediocampista, con 198 jugadores (34.7% del total). La distribución no es uniforme: mediocampistas y defensas concentran cerca de 68.7% de las plantillas, mientras que los porteros son apenas el 12.1%. Este resultado es coherente con la estructura táctica del fútbol: cada equipo alinea un solo portero pero cuatro o cinco jugadores por línea en defensa y medio campo, y mantiene además menos porteros suplentes en la nómina.

Al tratarse de una variable nominal no tiene sentido calcular media ni mediana; el único indicador de tendencia central válido es la moda.

3.2 Variable 2: club

3.2.1 Tabla de frecuencias

fa_club <- sort(table(pl$club), decreasing = TRUE)
fr_club <- prop.table(fa_club) * 100

tabla_club <- data.frame(
  Club          = names(fa_club),
  Frec_absoluta = as.numeric(fa_club),
  Frec_relativa = round(as.numeric(fr_club), 2)
)

kable(tabla_club,
      col.names = c("Club", "ni", "hi (%)"),
      caption = "Tabla 2. Número de jugadores registrados por club")
Tabla 2. Número de jugadores registrados por club
Club ni hi (%)
Liverpool 34 5.95
Brighton and Hove Albion 33 5.78
Tottenham Hotspur 32 5.60
Crystal Palace 31 5.43
Everton 31 5.43
Leicester City 31 5.43
Manchester United 31 5.43
Arsenal 30 5.25
Newcastle United 30 5.25
West Ham United 29 5.08
Aston Villa 28 4.90
Sheffield United 28 4.90
West Bromwich Albion 28 4.90
Chelsea 27 4.73
Manchester City 27 4.73
Leeds United 26 4.55
Fulham 25 4.38
Burnley 24 4.20
Southampton 24 4.20
Wolverhampton Wanderers 22 3.85

3.2.2 Gráfico

orden_club <- names(sort(table(pl$club)))   # de menor a mayor plantilla

ggplot(pl, aes(y = factor(club, levels = orden_club))) +
  geom_bar(fill = azul, width = 0.7) +
  labs(title = "Tamaño de la plantilla registrada por club",
       x = "Número de jugadores", y = NULL) +
  theme_minimal(base_size = 11) +
  theme(panel.grid.major.y = element_blank())

3.2.3 Interpretación

La base cubre los 20 clubes de la temporada. El club con mayor número de jugadores registrados es Liverpool (34 jugadores) y el de menor número es Wolverhampton Wanderers (22). El rango es estrecho —apenas 12 jugadores de diferencia entre el máximo y el mínimo—, de modo que la distribución es prácticamente equilibrada: ningún club está sobrerrepresentado y las comparaciones posteriores entre equipos no quedan sesgadas por el tamaño de la muestra.

3.3 Tabla de doble entrada

Cruzar las dos variables cualitativas permite verificar que la composición por posición se mantenga estable entre clubes.

tabla_doble <- table(pl$club, pl$posicion)

kable(round(prop.table(tabla_doble, margin = 1) * 100, 1),
      caption = "Tabla 3. Composición porcentual de la plantilla por club (% por fila)")
Tabla 3. Composición porcentual de la plantilla por club (% por fila)
Portero Defensa Mediocampista Delantero
Arsenal 10.0 33.3 33.3 23.3
Aston Villa 17.9 32.1 35.7 14.3
Brighton and Hove Albion 12.1 30.3 36.4 21.2
Burnley 12.5 41.7 29.2 16.7
Chelsea 7.4 40.7 37.0 14.8
Crystal Palace 9.7 38.7 32.3 19.4
Everton 9.7 25.8 32.3 32.3
Fulham 12.0 32.0 40.0 16.0
Leeds United 7.7 34.6 38.5 19.2
Leicester City 9.7 32.3 41.9 16.1
Liverpool 14.7 26.5 35.3 23.5
Manchester City 11.1 33.3 33.3 22.2
Manchester United 12.9 41.9 32.3 12.9
Newcastle United 13.3 30.0 40.0 16.7
Sheffield United 14.3 39.3 32.1 14.3
Southampton 16.7 33.3 33.3 16.7
Tottenham Hotspur 15.6 31.2 34.4 18.8
West Bromwich Albion 10.7 35.7 32.1 21.4
West Ham United 13.8 34.5 31.0 20.7
Wolverhampton Wanderers 9.1 36.4 31.8 22.7

Los porcentajes por fila confirman el patrón general: en casi todos los clubes los porteros representan entre el 10% y el 15% de la plantilla y las líneas de defensa y mediocampo superan conjuntamente el 60%.


4 Análisis de variables cuantitativas

4.1 Variable 3: edad

4.1.1 Indicadores estadísticos

edad <- pl$edad

ind_edad <- data.frame(
  Indicador = c("n", "Media", "Mediana", "Moda", "Desviación estándar",
                "Varianza", "Coef. de variación (%)", "Mínimo", "Máximo", "Rango",
                "Q1", "Q3", "Rango intercuartílico", "P10", "P90",
                "Asimetría (Fisher)", "Curtosis (exceso)"),
  Valor = round(c(
    sum(!is.na(edad)), mean(edad, na.rm = TRUE), median(edad, na.rm = TRUE),
    moda(edad)[1], sd(edad, na.rm = TRUE), var(edad, na.rm = TRUE),
    100 * sd(edad, na.rm = TRUE) / mean(edad, na.rm = TRUE),
    min(edad, na.rm = TRUE), max(edad, na.rm = TRUE),
    diff(range(edad, na.rm = TRUE)),
    quantile(edad, 0.25, na.rm = TRUE), quantile(edad, 0.75, na.rm = TRUE),
    IQR(edad, na.rm = TRUE),
    quantile(edad, 0.10, na.rm = TRUE), quantile(edad, 0.90, na.rm = TRUE),
    asimetria(edad), curtosis(edad)
  ), 2)
)

kable(ind_edad, caption = "Tabla 4. Indicadores descriptivos de la edad (años)")
Tabla 4. Indicadores descriptivos de la edad (años)
Indicador Valor
n 570.00
Media 25.79
Mediana 26.00
Moda 27.00
Desviación estándar 4.38
Varianza 19.16
Coef. de variación (%) 16.97
Mínimo 17.00
Máximo 38.00
Rango 21.00
Q1 22.00
Q3 29.00
Rango intercuartílico 7.00
P10 20.00
P90 32.00
Asimetría (Fisher) 0.15
Curtosis (exceso) -0.69

4.1.2 Tabla de frecuencias por intervalos

pl <- pl %>%
  mutate(edadR = cut(edad,
                     breaks = c(16, 20, 24, 28, 32, Inf),
                     labels = c("17 a 20 años", "21 a 24 años", "25 a 28 años",
                                "29 a 32 años", "33 años o más")))

fa_e <- table(pl$edadR)
fr_e <- prop.table(fa_e) * 100

tabla_edad <- data.frame(
  Intervalo = names(fa_e),
  ni = as.numeric(fa_e),
  hi = round(as.numeric(fr_e), 2),
  Ni = cumsum(as.numeric(fa_e)),
  Hi = round(cumsum(as.numeric(fr_e)), 2)
)

kable(tabla_edad,
      col.names = c("Intervalo de edad", "ni", "hi (%)", "Ni", "Hi (%)"),
      caption = "Tabla 5. Distribución de frecuencias de la edad agrupada")
Tabla 5. Distribución de frecuencias de la edad agrupada
Intervalo de edad ni hi (%) Ni Hi (%)
17 a 20 años 82 14.39 82 14.39
21 a 24 años 153 26.84 235 41.23
25 a 28 años 169 29.65 404 70.88
29 a 32 años 128 22.46 532 93.33
33 años o más 38 6.67 570 100.00

4.1.3 Gráficos

ggplot(pl, aes(x = edad)) +
  geom_histogram(binwidth = 2, fill = azul, color = "white") +
  geom_vline(xintercept = mean(edad, na.rm = TRUE),
             linetype = "dashed", color = "#C0392B") +
  annotate("text", x = mean(edad, na.rm = TRUE) + 0.4, y = Inf, vjust = 1.8,
           label = "Media", color = "#C0392B", size = 3.5, hjust = 0) +
  labs(title = "Distribución de la edad de los jugadores",
       x = "Edad (años)", y = "Frecuencia") +
  theme_minimal(base_size = 12)

ggplot(pl, aes(x = edad)) +
  geom_boxplot(fill = azul, alpha = 0.6, outlier.color = "#C0392B") +
  labs(title = "Diagrama de caja de la edad", x = "Edad (años)") +
  theme_minimal(base_size = 12) +
  theme(axis.text.y = element_blank())

4.1.4 Interpretación

La edad promedio de los jugadores es de 25.8 años y la mediana es de 26 años. La cercanía entre ambas medidas, junto con un coeficiente de asimetría de 0.15 —muy próximo a cero—, indica una distribución casi simétrica. La curtosis en exceso (-0.69) es negativa, es decir, la distribución es platicúrtica: más aplanada que la normal, con las edades repartidas de forma relativamente pareja en lugar de concentrarse en un solo pico.

La dispersión es baja: con una desviación estándar de 4.38 años, el coeficiente de variación es de apenas 17%, lo que confirma que se trata de un grupo homogéneo en edad. El 50% central de los jugadores se ubica entre los 22 y los 29 años (rango intercuartílico de 7 años), y el 90% tiene 32 años o menos. Los valores extremos —17 años el más joven y 38 el mayor— reflejan la convivencia entre juveniles recién promovidos y veteranos al final de su carrera.

4.2 Variable 4: partidos jugados

4.2.1 Indicadores estadísticos

part <- pl$partidos

ind_part <- data.frame(
  Indicador = c("n", "Media", "Mediana", "Moda", "Desviación estándar",
                "Varianza", "Coef. de variación (%)", "Mínimo", "Máximo", "Rango",
                "Q1", "Q3", "Rango intercuartílico", "P10", "P90",
                "Asimetría (Fisher)", "Curtosis (exceso)"),
  Valor = round(c(
    sum(!is.na(part)), mean(part, na.rm = TRUE), median(part, na.rm = TRUE),
    moda(part)[1], sd(part, na.rm = TRUE), var(part, na.rm = TRUE),
    100 * sd(part, na.rm = TRUE) / mean(part, na.rm = TRUE),
    min(part, na.rm = TRUE), max(part, na.rm = TRUE),
    diff(range(part, na.rm = TRUE)),
    quantile(part, 0.25, na.rm = TRUE), quantile(part, 0.75, na.rm = TRUE),
    IQR(part, na.rm = TRUE),
    quantile(part, 0.10, na.rm = TRUE), quantile(part, 0.90, na.rm = TRUE),
    asimetria(part), curtosis(part)
  ), 2)
)

kable(ind_part, caption = "Tabla 6. Indicadores descriptivos de los partidos jugados")
Tabla 6. Indicadores descriptivos de los partidos jugados
Indicador Valor
n 571.00
Media 68.82
Mediana 36.00
Moda 0.00
Desviación estándar 81.80
Varianza 6690.83
Coef. de variación (%) 118.85
Mínimo 0.00
Máximo 539.00
Rango 539.00
Q1 3.00
Q3 108.00
Rango intercuartílico 105.00
P10 0.00
P90 190.00
Asimetría (Fisher) 1.62
Curtosis (exceso) 2.95

4.2.2 Tabla de frecuencias por intervalos

pl <- pl %>%
  mutate(partidosR = cut(partidos,
                         breaks = c(-1, 0, 25, 50, 100, 200, Inf),
                         labels = c("Sin debutar (0)", "1 a 25", "26 a 50",
                                    "51 a 100", "101 a 200", "Más de 200")))

fa_p <- table(pl$partidosR)
fr_p <- prop.table(fa_p) * 100

tabla_part <- data.frame(
  Intervalo = names(fa_p),
  ni = as.numeric(fa_p),
  hi = round(as.numeric(fr_p), 2),
  Ni = cumsum(as.numeric(fa_p)),
  Hi = round(cumsum(as.numeric(fr_p)), 2)
)

kable(tabla_part,
      col.names = c("Partidos jugados", "ni", "hi (%)", "Ni", "Hi (%)"),
      caption = "Tabla 7. Distribución de frecuencias de los partidos jugados")
Tabla 7. Distribución de frecuencias de los partidos jugados
Partidos jugados ni hi (%) Ni Hi (%)
Sin debutar (0) 90 15.76 90 15.76
1 a 25 139 24.34 229 40.11
26 a 50 103 18.04 332 58.14
51 a 100 85 14.89 417 73.03
101 a 200 106 18.56 523 91.59
Más de 200 48 8.41 571 100.00

4.2.3 Gráficos

ggplot(pl, aes(x = partidos)) +
  geom_histogram(binwidth = 25, fill = azul, color = "white", boundary = 0) +
  labs(title = "Distribución de los partidos jugados",
       x = "Partidos jugados en la Premier League", y = "Frecuencia") +
  theme_minimal(base_size = 12)

ggplot(pl, aes(x = partidos)) +
  geom_boxplot(fill = azul, alpha = 0.6, outlier.color = "#C0392B") +
  labs(title = "Diagrama de caja de los partidos jugados", x = "Partidos") +
  theme_minimal(base_size = 12) +
  theme(axis.text.y = element_blank())

4.2.4 Interpretación

El comportamiento de esta variable es opuesto al de la edad. La media (68.8 partidos) casi duplica a la mediana (36 partidos) y la moda es 0, señal inequívoca de una distribución fuertemente asimétrica hacia la derecha: el coeficiente de asimetría es 1.62 y la curtosis en exceso 2.95, es decir, una distribución leptocúrtica con cola larga y valores atípicos.

El coeficiente de variación alcanza el 118.9%, muy superior al 100%, lo que revela una heterogeneidad extrema: conviven jugadores que aún no debutan con futbolistas que superan los 539 partidos. El 25% de los jugadores acumula 3 partidos o menos, mientras que el 10% más experimentado supera los 190.

Implicación metodológica: con esta forma de distribución la media no es una medida representativa. Para resumir la experiencia típica de un jugador conviene usar la mediana y el rango intercuartílico, que no se ven arrastrados por los valores extremos. En la edad, en cambio, la media sí es un buen resumen porque la distribución es simétrica y homogénea.


5 Análisis comparativo por posición

Al cruzar las variables cualitativas con las cuantitativas aparecen diferencias que el análisis univariado no muestra.

resumen <- pl %>%
  group_by(posicion) %>%
  summarise(
    n           = n(),
    Edad_media  = round(mean(edad, na.rm = TRUE), 1),
    Edad_mediana= median(edad, na.rm = TRUE),
    Part_media  = round(mean(partidos, na.rm = TRUE), 1),
    Part_mediana= median(partidos, na.rm = TRUE),
    Goles_media = round(mean(goles, na.rm = TRUE), 1),
    Goles_max   = max(goles, na.rm = TRUE),
    .groups = "drop"
  )

kable(resumen,
      col.names = c("Posición", "n", "Edad media", "Edad mediana",
                    "Partidos media", "Partidos mediana", "Goles media", "Goles máx."),
      caption = "Tabla 8. Indicadores por posición de juego")
Tabla 8. Indicadores por posición de juego
Posición n Edad media Edad mediana Partidos media Partidos mediana Goles media Goles máx.
Portero 69 28.2 28 51.8 7.0 0.0 0
Defensa 194 25.6 26 67.3 37.5 2.7 27
Mediocampista 198 25.4 25 71.5 40.0 6.6 61
Delantero 110 25.4 25 77.4 39.5 21.5 180
ggplot(pl, aes(x = posicion, y = edad)) +
  geom_boxplot(fill = azul, alpha = 0.6, outlier.color = "#C0392B") +
  labs(title = "Edad según la posición de juego",
       x = NULL, y = "Edad (años)") +
  theme_minimal(base_size = 12) +
  theme(panel.grid.major.x = element_blank())

5.0.1 Interpretación

Los porteros son claramente el grupo de mayor edad: su media (28.2 años) supera en cerca de 2.7 años a la del resto de posiciones, que son prácticamente indistinguibles entre sí (entre 25.4 y 25.6 años). Esto coincide con la idea de que la portería es una posición donde la experiencia pesa más que el rendimiento físico y donde las carreras se extienden por más tiempo.

En goles, la diferencia es la esperada por el rol táctico: los delanteros promedian 21.5 goles frente a 2.7 de los defensas, y los porteros registran 0.


6 Conclusiones

  1. Las dos variables cualitativas se comportan de forma distinta. La posición presenta una distribución claramente desigual, con moda en Mediocampista (34.7%), mientras que el club se reparte de manera casi uniforme entre los 20 equipos. En ambos casos la moda es el único indicador de tendencia central aplicable por tratarse de escalas nominales.

  2. La edad es una variable homogénea y casi simétrica. Media (25.8) y mediana (26) coinciden, la asimetría es cercana a cero y el coeficiente de variación (17%) es bajo: la media resume bien al conjunto.

  3. Los partidos jugados son una variable muy heterogénea y asimétrica. Con un coeficiente de variación del 118.9% y una asimetría de 1.62, la media sobreestima la experiencia del jugador típico; la mediana y el rango intercuartílico son las medidas adecuadas.

  4. La elección del indicador depende de la forma de la distribución. Este es el aprendizaje central de la unidad: calcular la media sin revisar antes la simetría y la dispersión puede llevar a conclusiones equivocadas sobre los mismos datos.

  5. Limitaciones. El análisis es exclusivamente descriptivo: describe y resume lo observado en esta base, pero no permite establecer relaciones causales ni generalizar a otras ligas o temporadas. Además, las estadísticas son acumuladas de carrera, por lo que favorecen a los jugadores con más años en la liga.


Base de datos: estadísticas de jugadores de la Premier League (Actividad 112). Procesamiento: R y RStudio.