|
1 | 1 | import plotly.express as px |
2 | 2 | import plotly.graph_objects as go |
3 | 3 | import numpy as np |
| 4 | +import matplotlib |
| 5 | +matplotlib.use('Agg') |
| 6 | +import matplotlib.pyplot as plt |
| 7 | +from mplsoccer import Pitch |
4 | 8 |
|
5 | 9 | COLORS = { |
6 | 10 | "primary": "#DA291C", "secondary": "#FBE122", "accent": "#58a6ff", |
@@ -67,47 +71,38 @@ def graficar_radar(df_f, players_sel): |
67 | 71 | return fig_radar |
68 | 72 |
|
69 | 73 | def graficar_heatmap_zonas(player_row): |
70 | | - zones_y = ["Bloque Bajo", "Zona Construc.", "Zona Ataque", "Último Tercio"] |
71 | | - zones_x = ["Banda Izq.", "Semi-izq.", "Centro Izq.", "Centro Der.", "Semi-der.", "Banda Der."] |
72 | | - |
| 74 | + pitch = Pitch(pitch_type='statsbomb', pitch_color='#0d1117', line_color='#30363d') |
| 75 | + fig, ax = pitch.draw(figsize=(6, 4)) |
| 76 | + fig.patch.set_facecolor('#0d1117') |
| 77 | + |
73 | 78 | np.random.seed(int(hash(player_row["player"])) % 999) |
74 | | - base, vert = player_row["prog_passes"] * 3, player_row["vert_idx"] |
75 | | - heat = np.random.exponential(base, (4, 6)) |
76 | | - heat[2:, 2:4] *= (1 + vert * 2) |
77 | | - heat[:1, :] *= 0.4 |
78 | | - heat = np.clip(heat, 0, 35) |
79 | | - |
80 | | - fig_hm = go.Figure(go.Heatmap( |
81 | | - z=heat, x=zones_x, y=zones_y, |
82 | | - colorscale=[[0, "#0d1117"], [0.25, "#1a3a5c"], [0.5, "#1f6feb"], [0.75, "#DA291C"], [1.0, "#FBE122"]], |
83 | | - showscale=True, colorbar=dict(title="Intens.", thickness=14, bgcolor="rgba(0,0,0,0)"), |
84 | | - hoverongaps=False |
85 | | - )) |
86 | | - # Lineas tácticas |
87 | | - fig_hm.add_shape(type="rect", x0=-0.5, x1=5.5, y0=-0.5, y1=3.5, line=dict(color="#30363d", width=1)) |
88 | | - fig_hm.add_shape(type="line", x0=-0.5, x1=5.5, y0=0.5, y1=0.5, line=dict(color="#30363d", dash="dot", width=1)) |
89 | | - fig_hm.add_shape(type="line", x0=-0.5, x1=5.5, y0=1.5, y1=1.5, line=dict(color="#30363d", dash="dot", width=1)) |
90 | | - fig_hm.add_shape(type="line", x0=-0.5, x1=5.5, y0=2.5, y1=2.5, line=dict(color="#DA291C", dash="dash", width=1.5)) |
| 79 | + num_passes = int(player_row["prog_passes"] * 15) + 30 |
| 80 | + |
| 81 | + # Bias distribution based on metrics |
| 82 | + x = np.random.normal(50 + player_row["vert_idx"] * 25, 20, num_passes) |
| 83 | + y = np.random.normal(40, 25, num_passes) |
| 84 | + x = np.clip(x, 0, 120); y = np.clip(y, 0, 80) |
91 | 85 |
|
92 | | - fig_hm.update_layout(**PLOTLY_THEME, title=f"Pases progresivos — {player_row['player']}", height=400) |
93 | | - fig_hm.update_xaxes(side="top", gridcolor="rgba(0,0,0,0)"); fig_hm.update_yaxes(gridcolor="rgba(0,0,0,0)") |
94 | | - return fig_hm |
| 86 | + pitch.hexbin(x, y, ax=ax, edgecolors='#0d1117', gridsize=(8, 6), cmap='magma', alpha=0.8) |
| 87 | + ax.set_title(f"Distribución de Pases — {player_row['player']}", color="#e6edf3", size=12, pad=5) |
| 88 | + return fig |
95 | 89 |
|
96 | 90 | def graficar_heatmap_xt(player_row): |
97 | | - zones_y = ["Bloque Bajo", "Zona Construc.", "Zona Ataque", "Último Tercio"] |
98 | | - zones_x = ["Banda Izq.", "Semi-izq.", "Centro Izq.", "Centro Der.", "Semi-der.", "Banda Der."] |
99 | | - |
100 | | - xT_zones = np.random.exponential(player_row["xT_gen"] * 5, (4, 6)) |
101 | | - xT_zones[3, 2:4] *= 2.5 |
102 | | - |
103 | | - fig_xt = go.Figure(go.Heatmap( |
104 | | - z=xT_zones, x=zones_x, y=zones_y, |
105 | | - colorscale=[[0, "#161b22"], [0.4, "#3fb950"], [0.8, "#FBE122"], [1, "#DA291C"]], |
106 | | - colorbar=dict(title="xT", thickness=14, bgcolor="rgba(0,0,0,0)"), |
107 | | - )) |
108 | | - fig_xt.update_layout(**PLOTLY_THEME, height=360, title=f"xT Generado — {player_row['player']}") |
109 | | - fig_xt.update_xaxes(side="top", gridcolor="rgba(0,0,0,0)"); fig_xt.update_yaxes(gridcolor="rgba(0,0,0,0)") |
110 | | - return fig_xt |
| 91 | + pitch = Pitch(pitch_type='statsbomb', pitch_color='#0d1117', line_color='#30363d', half=True) |
| 92 | + fig, ax = pitch.draw(figsize=(6, 4)) |
| 93 | + fig.patch.set_facecolor('#0d1117') |
| 94 | + |
| 95 | + np.random.seed(int(hash(player_row["player"])) % 999 + 1) |
| 96 | + num_events = int(player_row["xT_gen"] * 120) + 5 |
| 97 | + |
| 98 | + x = np.random.normal(95, 12, num_events) |
| 99 | + y = np.random.normal(40, 25, num_events) |
| 100 | + x = np.clip(x, 60, 120); y = np.clip(y, 0, 80) |
| 101 | + |
| 102 | + sizes = np.random.uniform(30, 180, num_events) |
| 103 | + pitch.scatter(x, y, s=sizes, c='#DA291C', edgecolors='#0d1117', alpha=0.75, ax=ax) |
| 104 | + ax.set_title(f"Nodos de xT — {player_row['player']}", color="#e6edf3", size=12, pad=5) |
| 105 | + return fig |
111 | 106 |
|
112 | 107 | def graficar_red_pases(df): |
113 | 108 | positions_xy = { |
|
0 commit comments