Qué es un convex hull (envolvente convexa)

Es el polígono más pequeño y sin entrantes que contiene todos los puntos de un grupo. Sirve para ver de un vistazo cuánto espacio ocupa cada grupo y si dos grupos se solapan.

Aquí: un punto = una temporada de un jugador. Eje X = % de victorias en tierra batida. Eje Y = % de victorias en pista dura. Solo temporadas con al menos 10 partidos en cada superficie.

Datos

Fuente: TML-Database (estadísticas ATP por partido), https://github.com/Tennismylife/TML-Database. Elaboración propia.

# El CSV debe estar en la misma carpeta que este .Rmd
d <- read.csv("tenis_convex_hull_TML.csv", encoding = "UTF-8")
head(d)
##   jugador temporada pct_tierra pct_dura partidos_tierra partidos_dura
## 1 Federer      2000       30.0     60.0              10            40
## 2 Federer      2001       64.3     70.0              14            30
## 3 Federer      2002       75.0     73.8              16            42
## 4 Federer      2003       80.0     80.7              20            57
## 5 Federer      2004       88.9     92.2              18            51
## 6 Federer      2005       88.2     98.0              17            51
table(d$jugador)
## 
## Djokovic  Federer    Nadal 
##       20       17       18

Gráfico

jug  <- c("Federer", "Nadal", "Djokovic")
cols <- c(Federer = "#1f6fb4", Nadal = "#d9534f", Djokovic = "#2a9d5c")

plot(d$pct_tierra, d$pct_dura, type = "n",
     xlim = range(d$pct_tierra) + c(-3, 3), ylim = range(d$pct_dura) + c(-3, 3),
     xlab = "% victorias en tierra batida", ylab = "% victorias en pista dura",
     main = "Convex hull por jugador (cada punto = una temporada)")
grid()

for (j in jug) {
  s   <- d[d$jugador == j, ]
  idx <- chull(s$pct_tierra, s$pct_dura)   # puntos que forman el borde
  polygon(s$pct_tierra[idx], s$pct_dura[idx],
          col = adjustcolor(cols[j], alpha.f = 0.25), border = cols[j], lwd = 2)
  points(s$pct_tierra, s$pct_dura, pch = 19, col = cols[j])
}
legend("bottomright", legend = jug, fill = adjustcolor(cols[jug], 0.4),
       border = cols[jug], bty = "n")

Área de cada envolvente

area <- function(x, y) {            # fórmula del polígono (shoelace)
  n <- length(x)
  abs(sum(x * c(y[-1], y[1]) - c(x[-1], x[1]) * y)) / 2
}
res <- t(sapply(jug, function(j) {
  s <- d[d$jugador == j, ]; i <- chull(s$pct_tierra, s$pct_dura)
  c(temporadas = nrow(s), puntos_en_borde = length(i),
    area = round(area(s$pct_tierra[i], s$pct_dura[i]), 1))
}))
res
##          temporadas puntos_en_borde  area
## Federer          17               8 738.4
## Nadal            18               6 380.8
## Djokovic         20               5 362.5

Lectura