Visuturf 22 avril 2025
Code Python pour l'analyse de courses hippiques
Ce code utilise Python et des librairies scientifiques (pandas, matplotlib, numpy) pour traiter des données de courses et générer un rapport PDF. Il ne peut pas être exécuté directement dans cette page web.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from matplotlib.patches import Wedge, Rectangle, Patch
import matplotlib.patheffects as pe
import re, math
# Dataset for 22 April 2025
data = [
(1, "FAN D'APPLE'S", 72, "1h 3h 7h 9h 8s 6s 2s 5s 1s 13h 1h 9h", 8.4),
(2, "GILOU CAT", 72, "1s 5s 1h 2h 2s 5h 2s 1s 6h 7h 3h 3h", 11.6),
(3, "DENTOR DES OBEAUX", 72, "1h 5s 2s 8h 8s 4s 2s 6h 9h 1s 6s 4h", 9.5),
(4, "BOJAK", 71, "3h Ah 11h 6h 2h 6h 4h 6h 6h 2h 11h 1h", 5.9),
(5, "HOSAVILLE", 71, "1h 11h 7s 7s 6s 5s 3s 5s 4s 1h 2h 9s", 10.1),
(6, "JAZZ ROQUE", 70.5, "1s 1h Ts 3h 7s 1s Ah 3h 2h 2h 1h Ah", 11.9),
(7, "JALWAY", 70, "2h 5h 7h 1h 3h 4h 7h 1h 5h 3h 4h 6p", 60.5),
(8, "KREMLIN", 70, "2h 2h 2h 2h 2p 4p 1p 2p", 5.9),
(9, "HOOKER DE BRENUS", 70, "4h Th 7h Dh 7h 1h 5h 1h 2h 2h 3h 3p", 9.4),
(10, "BOOM BOOM CIAO", 70, "6s As Ts 7s As 7h 5h As 3s Ts 1s 5s", 80.3),
(11, "CHOEUR ETINCELLE", 69.5, "5h 10h 1h 4h 9p Ah 7h Ah 8h 6h 5h 4h", 104.7),
(12, "JARDIN SECRET", 69, "7h 2h 3h Th 11h 5s 2h 1h 3s 3h 2h 1s", 19.2),
(13, "HILL OF BAMBOO", 69, "2s Ah 5h 11s 7h 1h 8h 15h 7s 5s 2s 3s", 18.2),
(14, "ROI K'ALIN", 69, "1h 8h Ah 3h 6h 2h 6h Th 8s 6s 6h 6s", 45.4),
(15, "RAFFLES LIAISON", 69, "Dh 4h 2h Ah 4h 5s 5s 2s 1s 5s 4s 8s", 57.5),
(16, "ASTALAVISTA", 68.5, "5s 4s 4s 6h Ah 6s 6s 15h 1h 2h 7h 3h", 38.2),
(17, "KUFLOWER DE COUELY",68, "12s 6s 1s 2s 4h Ts 8h Ts Ts 5s 5s 2h", 55.0),
]
df = pd.DataFrame(data, columns=["N°","Cheval","Poids","Musique","Cote"])
# Compute simple form index
def form_idx(music):
parts = [int(m.group(1)) for t in music.split() if (m := re.match(r'^(\d+)', t))]
return sum(1 for r in parts[:8] if r <= 3)
df["Forme"] = df["Musique"].apply(form_idx)
# Categorize
def category(c):
if c <= 6: return "Base"
if c <= 12: return "Bon coup"
if c <= 30: return "Outsider"
return "Longshot"
df["Catégorie"] = df["Cote"].apply(category)
# Placeholders
df["Distance fétiche"] = "1800‑2100 m"
df["Terrain préféré"] = "Souple"
# Colors
cat_color = {"Base":"#66CC33","Bon coup":"#FFFF33","Outsider":"#3399FF","Longshot":"#FF6633"}
# Generate PDF with improved visibility
pdf_path = "/mnt/data/Visuturf_22-04-2025_visibility_fixed.pdf"
with PdfPages(pdf_path) as pdf:
# Dynamic x-axis with padding
x_max = df["Cote"].max() + 10
x_min = 0
y_min, y_max = df["Poids"].min()-1, df["Poids"].max()+1
# Page 1: Visuturf scatter
fig, ax = plt.subplots(figsize=(12,7), facecolor='black')
ax.set_facecolor('black')
# Grid & zones behind
for x in np.arange(x_min, x_max+1, 5):
ax.axvline(x, color='grey', lw=0.3, alpha=0.25, zorder=0)
for y in np.arange(math.floor(y_min), math.ceil(y_max)+1):
ax.axhline(y, color='grey', lw=0.3, alpha=0.25, zorder=0)
ax.add_patch(Wedge((x_min, y_max), y_max-y_min, -90, 0, facecolor='#FFFF33', alpha=0.25, zorder=0))
ax.add_patch(Wedge((x_min, y_max), y_max-y_min, 0, 90, facecolor='#66CC33', alpha=0.25, zorder=0))
ax.add_patch(Rectangle((x_min, y_min), x_max, (y_max-y_min)*0.2, facecolor='#990000', alpha=0.25, zorder=0))
# Plot points & labels on top
for _, row in df.iterrows():
size = 900 if row["Catégorie"] == "Base" else 550
ax.scatter(row["Cote"], row["Poids"], s=size,
edgecolor='white', facecolor=cat_color[row["Catégorie"]],
linewidth=1.2, alpha=0.9, zorder=5)
txt = ax.text(row["Cote"], row["Poids"], str(int(row["N°"])),
color='white', ha='center', va='center',
fontsize=14 if row["Catégorie"]=="Base" else 12,
weight='bold', zorder=6)
txt.set_path_effects([pe.Stroke(linewidth=2, foreground='black'), pe.Normal()])
# Axes styling
ax.set_xlim(x_max, x_min)
ax.set_ylim(y_min, y_max)
ax.set_xlabel("Cote PMU (petit = favori)", color='white')
ax.set_ylabel("Poids (kg)", color='white')
ax.tick_params(colors='white')
for sp in ax.spines.values():
sp.set_edgecolor('white')
ax.set_title("Visuturf – Quinté+ 22/04/2025 (Cote vs Poids)", color='white')
# Legend above
handles = [Patch(facecolor=col, edgecolor='white', label=cat) for cat, col in cat_color.items()]
ax.legend(handles=handles, title="Catégories", loc='lower center',
bbox_to_anchor=(0.5, 1.02), ncol=4,
facecolor='black', framealpha=0.5, edgecolor='white', fontsize=10)
pdf.savefig(fig, facecolor=fig.get_facecolor(), bbox_inches='tight')
plt.close(fig)
# Pages 2+: all detail sheets
for _, row in df.sort_values("N°").iterrows():
fig2, ax2 = plt.subplots(figsize=(8.3,5.8), facecolor='black')
ax2.axis('off')
ax2.set_title(f"Fiche Visuturf – {row['Catégorie']} {int(row['N°'])} {row['Cheval']}",
color='white', pad=15)
info = [
f"Cote : {row['Cote']}",
f"Poids : {row['Poids']} kg",
f"Indice de forme : {row['Forme']} / 8",
f"Distance fétiche : {row['Distance fétiche']}",
f"Terrain préféré : {row['Terrain préféré']}",
f"Musique : {row['Musique']}"
]
y = 0.9
for line in info:
ax2.text(0.05, y, line, color='white', fontsize=12, va='top')
y -= 0.12
pdf.savefig(fig2, facecolor=fig2.get_facecolor(), bbox_inches='tight')
plt.close(fig2)
#pdf_path # This line just returns the path, doesn't execute
Ajouter un commentaire