(* ================================================================
graphiques.ml – Génération de graphiques SVG
Fonctions :
courbe_entrainement -> courbe(s) d'apprentissage
carte_chaleur -> heatmap pour les filtres convolutifs
nuage -> nuage de points 2D avec classes
barres -> diagramme en barres
================================================================ *)
(* ----------------------------------------------------------------- *)
(* Types *)
(* ----------------------------------------------------------------- *)
type courbe = { points : (float * float) list; legende : string }
(* ----------------------------------------------------------------- *)
(* Dimensions et couleurs *)
(* ----------------------------------------------------------------- *)
let largeur = 800
let hauteur = 600
let marge_g = 80
let marge_d = 40
let marge_h = 40
let marge_b = 60
let zone_l = largeur - marge_g - marge_d (* 680 *)
let zone_h = hauteur - marge_h - marge_b (* 500 *)
let palette = [| "#e41a1c"; "#377eb8"; "#4daf4a"; "#984ea3";
"#ff7f00"; "#ffff33"; "#a65628"; "#f781bf" |]
let couleur_val v =
let r = max 0 (min 255 (int_of_float (v *. 255.5))) in
Printf.sprintf "#%02x%02x%02x" r 0 (255 - r)
(* ----------------------------------------------------------------- *)
(* Primitives SVG *)
(* ----------------------------------------------------------------- *)
let balise oc =
Printf.fprintf oc
"\n"
let texte oc x y ancrage taille couleur s =
Printf.fprintf oc
"%s\n"
x y ancrage taille couleur s
let ligne oc x1 y1 x2 y2 epaisseur couleur =
Printf.fprintf oc "\n" x1 y1 x2 y2 couleur epaisseur
let cercle oc cx cy r couleur =
Printf.fprintf oc "\n" cx cy r couleur
let polygone oc points couleur =
Printf.fprintf oc "\n"
couleur couleur
let polyline oc pts couleur epaisseur =
Printf.fprintf oc "\n" couleur epaisseur
let rect oc x y w h remplissage =
Printf.fprintf oc
"\n"
x y w h remplissage
(* ----------------------------------------------------------------- *)
(* Axes *)
(* ----------------------------------------------------------------- *)
let echelle minv maxv =
if maxv = minv then (minv -. 0.5, minv +. 0.5, 0.5) else
let r = maxv -. minv in
let e = 10. ** floor (log10 r) in
let pas = if r /. e >= 5. then e else if r /. e >= 2. then e /. 2. else e /. 5. in
let debut = floor (minv /. pas) *. pas in
let fin = ceil (maxv /. pas) *. pas in
(debut, fin, pas)
let trace_axes oc xmin xmax ymin ymax xtitre ytitre =
let ax = float marge_g in
let bx = float (largeur - marge_d) in
let ay = float hauteur -. float marge_b in
let by = float marge_h in
let (xd, xf, xp) = echelle xmin xmax in
let (yd, yf, yp) = echelle ymin ymax in
let mx x = ax +. (x -. xd) /. (xf -. xd) *. (bx -. ax) in
let my y = ay -. (y -. yd) /. (yf -. yd) *. (ay -. by) in
(* cadre *)
ligne oc ax ay bx ay 1 "black";
ligne oc ax ay ax by 1 "black";
(* graduation x *)
let rec aux_x v =
if v > xf +. 1e-9 then () else
let px = mx v in
ligne oc px ay px (ay +. 6.) 1 "black";
texte oc px (ay +. 20.) "middle" 12 "black" (Printf.sprintf "%.1f" v);
aux_x (v +. xp)
in aux_x xd;
(* graduation y *)
let rec aux_y v =
if v > yf +. 1e-9 then () else
let py = my v in
ligne oc ax py (ax -. 6.) py 1 "black";
texte oc (ax -. 8.) (py +. 4.) "end" 12 "black" (Printf.sprintf "%.1f" v);
aux_y (v +. yp)
in aux_y yd;
(* titres *)
texte oc (ax +. (bx -. ax) /. 2.) (ay +. 45.) "middle" 14 "black" xtitre;
texte oc (ax -. 60.) (ay -. (ay -. by) /. 2.) "middle" 14 "black" ytitre;
(* rotation du titre y via transform *)
Printf.fprintf oc "%s\n"
(ax -. 55.) (ay -. (ay -. by) /. 2.) (ax -. 55.) (ay -. (ay -. by) /. 2.) ytitre;
(mx, my, xd, xf, yd, yf)
(* ----------------------------------------------------------------- *)
(* 1. Courbe d'entraînement *)
(* ----------------------------------------------------------------- *)
let courbe_entrainement ?(fichier = "courbe.svg") ?(titre = "")
?(xlab = "Itération") ?(ylab = "Erreur")
courbes =
let oc = open_out fichier in
balise oc;
let (mx, my, _, _, _, _) =
if courbes = [] then ((fun x -> 0.), (fun y -> 0.), 0., 1., 0., 1.) else
let xs = List.concat_map (fun c -> List.map fst c.points) courbes in
let ys = List.concat_map (fun c -> List.map snd c.points) courbes in
let xmin = List.fold_left min max_float xs in
let xmax = List.fold_left max (-.max_float) xs in
let ymin = List.fold_left min max_float ys in
let ymax = List.fold_left max (-.max_float) ys in
let xpad = (xmax -. xmin) *. 0.05 in
let ypad = (ymax -. ymin) *. 0.1 in
trace_axes oc (xmin -. xpad) (xmax +. xpad)
(max 0. (ymin -. ypad)) (ymax +. ypad)
xlab ylab
in
if titre <> "" then
texte oc (float largeur /. 2.) 25. "middle" 18 "black" titre;
List.iteri (fun i c ->
let pts = List.map (fun (x, y) -> (mx x, my y)) c.points in
polyline oc pts palette.(i mod Array.length palette) 2
) courbes;
let y_leg = float marge_h +. 10. in
List.iteri (fun i c ->
let x_leg = float (largeur - marge_d + 10) in
ligne oc x_leg (y_leg +. float i *. 20.) (x_leg +. 20.) (y_leg +. float i *. 20.)
2 palette.(i mod Array.length palette);
texte oc (x_leg +. 25.) (y_leg +. 5. +. float i *. 20.) "start" 12 "black" c.legende
) courbes;
fermer oc;
close_out oc;
Printf.printf " -> graphique sauvegardé : %s\n%!" fichier
(* ----------------------------------------------------------------- *)
(* 2. Carte de chaleur (heatmap) *)
(* ----------------------------------------------------------------- *)
let carte_chaleur ?(fichier = "heatmap.svg") ?(titre = "") matrice =
let oc = open_out fichier in
balise oc;
let n_lignes = List.length matrice in
let n_cols = if n_lignes = 0 then 0 else List.length (List.hd matrice) in
let cote = min (zone_l / max 1 n_cols) (zone_h / max 1 n_lignes) in
let cote = min cote 60 in
let larg = n_cols * cote in
let haut = n_lignes * cote in
let ox = float (marge_g + (zone_l - larg) / 2) in
let oy = float (marge_h + (zone_h - haut) / 2) in
if titre <> "" then
texte oc (float largeur /. 2.) 25. "middle" 18 "black" titre;
(* valeurs min/max *)
let toutes = List.flatten matrice in
let vmin = List.fold_left min max_float toutes in
let vmax = List.fold_left max (-.max_float) toutes in
let plage = if vmax = vmin then 1. else vmax -. vmin in
List.iteri (fun i ligne ->
List.iteri (fun j v ->
let vn = (v -. vmin) /. plage in
let r = int_of_float (vn *. 255.) in
let b = int_of_float ((1. -. vn) *. 255.) in
let col = Printf.sprintf "#%02x%02x%02x" r 0 b in
rect oc (ox +. float j *. float cote) (oy +. float i *. float cote)
(float cote) (float cote) col
) ligne
) matrice;
(* quadrillage *)
for i = 0 to n_lignes do
ligne oc ox (oy +. float i *. float cote) (ox +. float n_cols *. float cote)
(oy +. float i *. float cote) 1 "#ccc"
done;
for j = 0 to n_cols do
ligne oc (ox +. float j *. float cote) oy (ox +. float j *. float cote)
(oy +. float n_lignes *. float cote) 1 "#ccc"
done;
fermer oc;
close_out oc;
Printf.printf " -> graphique sauvegardé : %s\n%!" fichier
(* ----------------------------------------------------------------- *)
(* 3. Nuage de points 2D avec classes *)
(* ----------------------------------------------------------------- *)
let nuage ?(fichier = "nuage.svg") ?(titre = "") ?(xlab = "x") ?(ylab = "y")
points_par_classe =
let oc = open_out fichier in
balise oc;
let tous = List.concat points_par_classe in
let xs = List.map fst tous in
let ys = List.map snd tous in
let xmin = List.fold_left min max_float xs in
let xmax = List.fold_left max (-.max_float) xs in
let ymin = List.fold_left min max_float ys in
let ymax = List.fold_left max (-.max_float) ys in
let xpad = (xmax -. xmin) *. 0.1 in
let ypad = (ymax -. ymin) *. 0.1 in
let (mx, my, _, _, _, _) =
trace_axes oc (xmin -. xpad) (xmax +. xpad)
(ymin -. ypad) (ymax +. ypad)
xlab ylab
in
if titre <> "" then
texte oc (float largeur /. 2.) 25. "middle" 18 "black" titre;
List.iteri (fun i pts ->
let col = palette.(i mod Array.length palette) in
List.iter (fun (x, y) -> cercle oc (mx x) (my y) 4. col) pts
) points_par_classe;
(* légende *)
let y_leg = float marge_h +. 10. in
let x_leg = float (largeur - marge_d + 10) in
List.iteri (fun i _ ->
cercle oc (x_leg +. 5.) (y_leg +. 5. +. float i *. 22.) 4.
palette.(i mod Array.length palette);
texte oc (x_leg +. 15.) (y_leg +. 8. +. float i *. 22.) "start" 12 "black"
(Printf.sprintf "Classe %d" i)
) points_par_classe;
fermer oc;
close_out oc;
Printf.printf " -> graphique sauvegardé : %s\n%!" fichier
(* ----------------------------------------------------------------- *)
(* 4. Diagramme en barres *)
(* ----------------------------------------------------------------- *)
let barres ?(fichier = "barres.svg") ?(titre = "") ?(xlab = "") ?(ylab = "")
etiquettes valeurs =
let oc = open_out fichier in
balise oc;
let n = List.length valeurs in
let ymin = 0. in
let ymax = List.fold_left max (-.max_float) valeurs in
let ypad = ymax *. 0.1 in
let (mx, my, _, _, _, _) =
trace_axes oc (-0.5) (float (n - 1) +. 0.5) (ymin -. ypad) (ymax +. ypad)
xlab ylab
in
let ax = float marge_g in
let bx = float (largeur - marge_d) in
let larg_barre = (bx -. ax) /. float (max 1 n) *. 0.6 in
if titre <> "" then
texte oc (float largeur /. 2.) 25. "middle" 18 "black" titre;
List.iteri (fun i v ->
let cx = mx (float i) in
let cy = my v in
let ay = float hauteur -. float marge_b in
let col = palette.(i mod Array.length palette) in
rect oc (cx -. larg_barre /. 2.) cy larg_barre (ay -. cy) col;
texte oc cx (ay +. 18.) "middle" 11 "black"
(try List.nth etiquettes i with _ -> "")
) valeurs;
fermer oc;
close_out oc;
Printf.printf " -> graphique sauvegardé : %s\n%!" fichier
(* ----------------------------------------------------------------- *)
(* 5. Exemple : courbes depuis un fichier de données *)
(* ----------------------------------------------------------------- *)
(* Lit des paires (x, y) depuis un fichier, une paire par ligne *)
let lit_donnees fichier =
let ic = open_in fichier in
let rec aux acc =
try
let ligne = input_line ic in
match List.filter (fun s -> s <> "") (String.split_on_char ' ' ligne) with
| [x; y] -> aux ((float_of_string x, float_of_string y) :: acc)
| _ -> aux acc
with _ -> List.rev acc
in
let res = aux [] in
close_in ic;
res