projects 13.06.26 · 9 MIN

Mundial 2026 — ¿Quién levanta la copa? 2 millones de simulaciones en la GPU

Ya rueda el balón. Con los primeros resultados reales capturados y el resto del torneo rellenado por nuestro modelo bayesiano, simulé el Mundial completo —fase de grupos, los 8 mejores terceros, y todo el cuadro de eliminatorias hasta la final— 2 millones de veces sobre la GPU. Aquí están las probabilidades de cada selección de llegar a cada ronda y de levantar la copa.

Cómo funciona, en corto:

  • Resultados reales fijos. Los partidos ya jugados quedan clavados a su marcador; solo se simula lo que falta.
  • Goles del modelo bayesiano. Cada partido pendiente muestrea goles (Poisson jerárquico con ataque/defensa por selección y altitud de sede). Cada simulación toma una muestra distinta del posterior, así que la incertidumbre del modelo viaja hasta el resultado final.
  • Bracket oficial FIFA. Cableado real del cuadro (R32→final) + la tabla oficial de 495 combinaciones que asigna los 8 mejores terceros.
  • 2 millones de torneos en ~3 s en una RTX 4080. A ese volumen el error de muestreo es ~0.02 pp: los decimales que ves ya están clavados.

Los favoritos

Probabilidad de ser campeón del mundo. Argentina y España se despegan; detrás, un pelotón de cinco.

@d3
const data = [{"t": "Argentina", "v": 12.87}, {"t": "España", "v": 11.35}, {"t": "Brasil", "v": 7.81}, {"t": "Inglaterra", "v": 6.5}, {"t": "Marruecos", "v": 5.67}, {"t": "Colombia", "v": 5.52}, {"t": "Portugal", "v": 4.47}, {"t": "Japón", "v": 4.46}, {"t": "Francia", "v": 4.38}, {"t": "Uruguay", "v": 3.72}, {"t": "Alemania", "v": 3.3}, {"t": "Ecuador", "v": 2.55}, {"t": "Australia", "v": 2.37}, {"t": "P. Bajos", "v": 2.29}, {"t": "México", "v": 2.19}, {"t": "Bélgica", "v": 1.94}];
const W = container.clientWidth || 1000;
const m = {top:8,right:64,left:120,bottom:28};
const rowH = 30, H = data.length*rowH + m.top + m.bottom;
const svg = d3.select(container).append('svg').attr('width',W).attr('height',H)
  .attr('font-family','JetBrains Mono, system-ui, sans-serif');
const x = d3.scaleLinear().domain([0, d3.max(data,d=>d.v)*1.08]).range([m.left, W-m.right]);
const y = d3.scaleBand().domain(data.map(d=>d.t)).range([m.top, H-m.bottom]).padding(0.45);
const color = d3.scaleSequential(d3.interpolateTurbo).domain([d3.max(data,d=>d.v)*1.25, 0]);
svg.append('g').attr('transform',`translate(0,${H-m.bottom})`)
  .call(d3.axisBottom(x).ticks(6).tickFormat(d=>d+'%'))
  .call(g=>g.select('.domain').remove())
  .call(g=>g.selectAll('.tick line').attr('stroke','#33332e'))
  .attr('color','#9a9a8e').attr('font-size','11px');
const row = svg.selectAll('g.row').data(data).join('g');
row.append('line').attr('x1',x(0)).attr('x2',d=>x(d.v))
  .attr('y1',d=>y(d.t)+y.bandwidth()/2).attr('y2',d=>y(d.t)+y.bandwidth()/2)
  .attr('stroke',d=>color(d.v)).attr('stroke-width',3).attr('opacity',0.5);
row.append('circle').attr('cx',d=>x(d.v)).attr('cy',d=>y(d.t)+y.bandwidth()/2)
  .attr('r',7).attr('fill',d=>color(d.v));
row.append('text').attr('x',m.left-10).attr('y',d=>y(d.t)+y.bandwidth()/2)
  .attr('dy','0.35em').attr('text-anchor','end').attr('font-size','13px')
  .attr('fill','#d4d4c8').text(d=>d.t);
row.append('text').attr('x',d=>x(d.v)+12).attr('y',d=>y(d.t)+y.bandwidth()/2)
  .attr('dy','0.35em').attr('font-size','12px').attr('font-weight','600')
  .attr('fill',d=>color(d.v)).text(d=>d.v.toFixed(1)+'%');

El camino a la gloria

Cada celda es la probabilidad de alcanzar esa ronda. Se lee de izquierda (superar la fase de grupos) a derecha (campeón); pasa el cursor para el valor exacto. Útil para ver quién tiene un cuadro amable y quién se desinfla pronto.

@plotly
{"data": [{"type": "heatmap", "z": [[79.2, 46.1, 23.0, 9.9, 4.5, 1.9], [98.5, 57.4, 28.7, 12.2, 5.2, 2.2], [81.9, 38.4, 20.0, 10.6, 5.0, 2.3], [81.6, 47.2, 25.0, 11.1, 5.3, 2.4], [82.6, 45.1, 23.6, 11.7, 5.6, 2.5], [88.6, 51.7, 29.2, 14.8, 7.1, 3.3], [86.4, 44.2, 25.4, 14.3, 7.5, 3.7], [86.4, 53.3, 28.4, 16.2, 8.5, 4.4], [88.3, 46.7, 27.4, 16.0, 8.4, 4.5], [83.7, 52.6, 30.1, 16.4, 8.8, 4.5], [87.2, 56.4, 33.7, 18.9, 10.4, 5.5], [91.3, 53.7, 32.2, 19.2, 10.6, 5.7], [94.9, 58.5, 34.7, 20.5, 11.6, 6.5], [94.4, 59.4, 37.6, 24.0, 13.9, 7.8], [96.4, 63.5, 42.6, 29.8, 18.9, 11.3], [96.0, 62.4, 45.5, 31.2, 20.1, 12.9]], "x": ["Avanza", "Octavos", "Cuartos", "Semis", "Final", "Campeón"], "y": ["Bélgica", "México", "P. Bajos", "Australia", "Ecuador", "Alemania", "Uruguay", "Francia", "Japón", "Portugal", "Colombia", "Marruecos", "Inglaterra", "Brasil", "España", "Argentina"], "colorscale": "YlGnBu", "reversescale": false, "zmin": 0, "zmax": 100, "xgap": 2, "ygap": 2, "hovertemplate": "%{y} — %{x}: %{z:.1f}%<extra></extra>", "colorbar": {"ticksuffix": "%", "outlinewidth": 0, "tickfont": {"color": "#d4d4c8"}, "thickness": 14}}], "layout": {"height": 520, "margin": {"l": 110, "r": 20, "t": 30, "b": 30}, "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)", "font": {"family": "JetBrains Mono, system-ui, sans-serif", "size": 12, "color": "#d4d4c8"}, "xaxis": {"side": "top", "tickfont": {"color": "#d4d4c8"}, "fixedrange": true}, "yaxis": {"tickfont": {"color": "#d4d4c8"}, "fixedrange": true}}}

¿Quién sobrevive a los grupos?

Con 48 equipos y solo 32 plazas (12 primeros, 12 segundos y los 8 mejores terceros), pasar de ronda no es trivial. México lidera —ya ganó su debut y juega en casa, en altura— seguido de los pesos pesados.

@vega-lite
{"$schema": "https://vega.github.io/schema/vega-lite/v5.json", "width": "container", "height": 420, "background": "transparent", "data": {"values": [{"team": "México", "p": 98.5}, {"team": "España", "p": 96.4}, {"team": "EE.UU.", "p": 96.2}, {"team": "Argentina", "p": 96.0}, {"team": "Corea del Sur", "p": 95.5}, {"team": "Inglaterra", "p": 94.9}, {"team": "Brasil", "p": 94.4}, {"team": "Marruecos", "p": 91.3}, {"team": "Suiza", "p": 91.3}, {"team": "Alemania", "p": 88.6}, {"team": "Japón", "p": 88.3}, {"team": "Colombia", "p": 87.2}, {"team": "Francia", "p": 86.4}, {"team": "Uruguay", "p": 86.4}, {"team": "Portugal", "p": 83.7}, {"team": "Ecuador", "p": 82.6}, {"team": "P. Bajos", "p": 81.9}, {"team": "Australia", "p": 81.6}, {"team": "Canadá", "p": 81.2}, {"team": "Croacia", "p": 81.0}]}, "mark": {"type": "bar", "cornerRadiusEnd": 3}, "encoding": {"y": {"field": "team", "type": "nominal", "sort": "-x", "title": null, "axis": {"labelFontSize": 12, "labelColor": "#d4d4c8"}}, "x": {"field": "p", "type": "quantitative", "title": "Probabilidad de avanzar (%)", "scale": {"domain": [0, 100]}, "axis": {"labelColor": "#d4d4c8", "titleColor": "#d4d4c8", "gridColor": "#2a2a26"}}, "color": {"field": "p", "type": "quantitative", "scale": {"scheme": "tealblues"}, "legend": null}, "tooltip": [{"field": "team", "title": "Selección"}, {"field": "p", "title": "% avanza"}]}, "config": {"view": {"stroke": null}, "background": "transparent", "axis": {"domainColor": "#2a2a26"}}}

El factor altitud

Tres partidos de eliminatorias se juegan en altura: dos en Ciudad de México (2240 m) y uno en Guadalupe (540 m). El modelo penaliza al visitante que sube de golpe, así que las selecciones acostumbradas a la altura ganan terreno cuando el cuadro pasa por México. México sube de 25% a 29% de llegar a cuartos; rivales de nivel del mar como Inglaterra ceden algo. El efecto global es chico (pocos partidos en altura) pero apunta en la dirección correcta.

@plotly
{"data": [{"type": "bar", "orientation": "h", "y": ["Inglaterra", "Ecuador", "Colombia", "México"], "x": [36.7, 23.1, 33.2, 25.4], "name": "Sin altitud (nivel del mar)", "marker": {"color": "#6b6b63"}}, {"type": "bar", "orientation": "h", "y": ["Inglaterra", "Ecuador", "Colombia", "México"], "x": [34.7, 23.6, 33.7, 28.7], "name": "Con altitud de sede", "marker": {"color": "#2dd4a8"}}], "layout": {"barmode": "group", "height": 300, "margin": {"l": 90, "r": 20, "t": 10, "b": 40}, "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)", "font": {"family": "JetBrains Mono, system-ui, sans-serif", "size": 12, "color": "#d4d4c8"}, "xaxis": {"title": "Probabilidad de llegar a cuartos (%)", "ticksuffix": "%", "gridcolor": "#2a2a26", "tickfont": {"color": "#d4d4c8"}}, "yaxis": {"tickfont": {"color": "#d4d4c8"}}, "legend": {"orientation": "h", "y": -0.25, "font": {"color": "#d4d4c8"}}}}

Tabla completa (top 24)

# Selección Gr Avanza Octavos Cuartos Semis Final Campeón
1 Argentina J 96% 62% 45% 31% 20% 12.9%
2 España H 96% 64% 43% 30% 19% 11.3%
3 Brasil C 94% 59% 38% 24% 14% 7.8%
4 Inglaterra L 95% 59% 35% 20% 12% 6.5%
5 Marruecos C 91% 54% 32% 19% 11% 5.7%
6 Colombia K 87% 56% 34% 19% 10% 5.5%
7 Portugal K 84% 53% 30% 16% 9% 4.5%
8 Japón F 88% 47% 27% 16% 8% 4.5%
9 Francia I 86% 53% 28% 16% 8% 4.4%
10 Uruguay H 86% 44% 25% 14% 7% 3.7%
11 Alemania E 89% 52% 29% 15% 7% 3.3%
12 Ecuador E 83% 45% 24% 12% 6% 2.5%
13 Australia D 82% 47% 25% 11% 5% 2.4%
14 P. Bajos F 82% 38% 20% 11% 5% 2.3%
15 México A 99% 57% 29% 12% 5% 2.2%
16 Bélgica G 79% 46% 23% 10% 4% 1.9%
17 Senegal I 73% 38% 18% 9% 4% 1.7%
18 Suiza B 91% 50% 21% 9% 4% 1.6%
19 Noruega I 73% 38% 17% 8% 4% 1.6%
20 Corea del Sur A 96% 52% 24% 10% 4% 1.5%
21 Croacia L 81% 37% 17% 8% 3% 1.3%
22 Canadá B 81% 43% 19% 8% 3% 1.3%
23 EE.UU. D 96% 50% 23% 8% 3% 1.2%
24 Irán G 72% 39% 18% 7% 3% 1.2%

Las 48 selecciones están en el CSV del proyecto.


Cómo se hizo

Motor de simulación propio en PyTorch/CUDA: todo el torneo vectorizado como tensores y resuelto en paralelo para los 2 millones de simulaciones. Desempates de grupo por el reglamento FIFA (puntos, diferencia de gol, goles a favor, sorteo); empates en eliminatorias resueltos por penales (50/50). Los goles salen del posterior bayesiano jerárquico (mismo modelo que alimenta las predicciones partido a partido de este blog). Validado: en cada simulación hay exactamente un campeón, 2 finalistas, 4 semifinalistas, 8 en cuartos y 32 que pasan de grupos.

Disclaimer

Esto es un ejercicio de modelado estadístico, no consejo de apuestas. Las probabilidades reflejan el modelo y los resultados ya ocurridos, no certezas. El fútbol, afortunadamente, se juega.