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.