2 # -*- coding: utf-8 -*-
3 from configuration
import CONFIG
4 import gestionOMS
as oms
5 import gestion_unites
as u
6 from gestion_donnees
import calcule_max_graphique
, convertit_jours_vers_python
7 from gestion_erreurs
import debug
, erreur
, warning
8 from calculs_extrapole
import calcule_donnee_extrapolee
, calcule_age_extrapole
, interpole_lineaire
, interpole_lineaire_ordonnee
, formate_resultat_donnee
, formate_resultat_age
, formate_interpole
, formate_extrapole
11 from numpy
import arange
14 import matplotlib
.pyplot
as plt
16 # Essentiellement, la fonction qui trace la courbe, mais on y trouve également les fonctions d'extrapolation.
17 # Ainsi que les calculs additionnels.
20 def cree_figure(conf
,l_jours
,l_poids
,typedonnee
,liste_extracalculs
, liste_err
, enfants_add
= []):
21 """ conf est le dictionnaire de config. l_jours et l_poids les listes des temps (en jours) et de données
22 (donc pas forcément du poids)
23 typedonnee est le type de données (voir CONFIG["liste_typedonnees"]
24 liste_err la liste des erreurs à compléter (voir gestion_erreurs))
25 Renvoie la figure tracée, et les calculs additionnels sont mis sous forme de chaîne dans la liste
28 Les enfants en plus sont dans la liste enfants_add. Pour chaque item de la liste, il faut prendre
29 item[typed] pour avoir accès au nom, ljours, et ldonnees
32 debug("debut de cree_figure. Config : "+str(conf
)+". Nombre d'enfants additionnels : "+str(len(enfants_add
)),liste_err
)
34 liste_data_labels_p
,liste_data_labels_z
= oms
.renvoie_liste_labels(conf
,CONFIG
["liste_data_choisie_p"],CONFIG
["liste_data_choisie_z"],liste_err
)
36 erreur("bug avec liste data labels",liste_err
)
39 ######################## Gestion des bornes #############################
40 # y a-t-il un maxi saisi par l'utilisateur ?
42 # Est-ce qu'on a donné un maxi quand même (car même échelle) ?
43 if conf
["non_sauve"].get("maxi",0) == 0:
44 jour_maxi
= calcule_max_graphique(l_jours
)
46 jour_maxi
= conf
["non_sauve"]["maxi"]+1
48 jour_maxi
= conf
["maxi"]+1
50 # Si on cherche à extrapoler au-delà
51 if conf
["non_sauve"]["calculextradata_type"] == typedonnee
and conf
["non_sauve"]["calculextradata_age"]>jour_maxi
:
52 jour_maxi
= int(conf
["non_sauve"]["calculextradata_age"]) +1
54 # On s'assure que c'est bien compris dans les bornes
55 jour_maxi
= max(CONFIG
["jours_mini_courbe"],min(jour_maxi
,CONFIG
["jours_maxi_courbe"]))
56 debug("cree_figure : gestion du jour max : "+str(jour_maxi
),liste_err
)
58 ##################### Gestion des unités ###############################
59 # si l'unité n'est pas précisée, ni en "non sauvé" ni par l'utilisateur
60 if conf
["unite"] == "" and conf
["non_sauve"].get("unite","") == "":
61 unite
= u
.choix_unite(jour_maxi
)
62 debug("Unité non précisée, on choisit "+unite
,liste_err
)
63 elif conf
["unite"] != "":
66 unite
= conf
["non_sauve"]["unite"]
68 ##################### Gestion de la prématurité #######################"
69 prema
= int(convertit_jours_vers_python(conf
["prematurite"],liste_err
))
70 ## Gestion des prémas, deux cas :
71 # Si agecorrige est oui, alors on veut juste soustraire la valeur de préma
72 # à toutes les données.
73 # Si agecorrige est non, alors on veut ajouter la valeur de préma aux courbes de référence.
74 debug("Prématurité : "+str(prema
)+" age corrigé : "+conf
["agecorrige"],liste_err
)
75 if prema
>0 and conf
["agecorrige"] == "oui":
76 l_jours
= [j
-prema
for j
in l_jours
]
77 jour_maxi
= jour_maxi
- prema
80 ###################### Conversion des unités ###########################""
81 l_jours_conv
= u
.convertit_tableau(l_jours
,unite
,liste_err
)
82 # Attention, comme les jours commencent à partir de 0, faut enlever 1 pour avoir la borne...
83 age_maxi
= u
.convertitunite(jour_maxi
-1,unite
,liste_err
)
85 debug("cree_figure : conversion des unités ok : "+str(l_jours_conv
),liste_err
)
87 #####################" Courbes OMS et titre ######################################"
88 titre
= "Courbe de "+typedonnee
+" OMS"
89 if conf
["typecourbe"] == "P":
91 liste_data_labels
= liste_data_labels_p
92 if conf
["sexe"] == "M":
93 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["perc_garcon"]#f_poids_perc_garcon
94 titre
+= " (percentiles, garçon)"
95 elif conf
["sexe"] == "F":
96 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["perc_fille"]
97 titre
+= " (percentiles, fille)"
99 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["perc_mixte"]
100 titre
+= " (percentiles)"
101 elif conf
["typecourbe"] == "Z":
102 liste_data_labels
= liste_data_labels_z
103 if conf
["sexe"] == "M":
104 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_garcon"]
105 titre
+= " (moyenne et écarts-types, garçon)"
106 elif conf
["sexe"] == "F":
107 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_fille"]
108 titre
+= " (moyenne et écarts-types, fille)"
110 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_mixte"]
111 titre
+= " (moyenne et écarts-types)"
113 erreur("Type de courbe invalide"+conf
["typecourbe"],liste_err
)
116 ## On finira le titre plus tard quand on aura su qui est concerné
118 #debug("cree_figure : géré le type de courbe ok. Liste des data labels : "+str(liste_data_labels),liste_err)
119 debug("Fichier d'où extraire les données : "+fichier_oms
,liste_err
)
122 #### On extrait les données des courbes, et on convertit les jours dans l'unité voulues
124 t
= oms
.lire_fichier_csv(fichier_oms
)
126 erreur("cree_figure : Impossible d'ouvrir le fichier "+fichier_oms
, liste_err
)
129 debug("cree_figure : Conversion des données OMS à la bonne unité",liste_err
)
131 coljour
= (oms
.extraire_colonne(t
,0,jour_maxi
))
132 if prema
>0 and conf
["agecorrige"] != "oui":
133 coljour
= [j
+ prema
for j
in coljour
]
134 coljour
= u
.convertit_tableau(coljour
,unite
,liste_err
)
136 erreur("Problème à la conversion du tableau OMS. jour_maxi = "+str(jour_maxi
)+" unite = "+unite
,liste_err
)
139 ##################### Création de la figure et du graphique ###################
140 debug("cree_figure : prête à créer la figure",liste_err
)
141 #### La figure, params
143 fig
= plt
.figure(num
=None, figsize
=(conf
["largeur"], conf
["hauteur"]), dpi
=100, facecolor
=conf
["couleurs"]["fond"])
144 plt
.rcParams
['axes.facecolor'] = conf
["couleurs"]["fond"]
145 plt
.rcParams
['axes.edgecolor']= conf
["couleurs"]["cadretxt"]
146 plt
.rcParams
['xtick.color'] = conf
["couleurs"]["cadretxt"]
147 plt
.rcParams
['ytick.color'] = conf
["couleurs"]["cadretxt"]
148 plt
.rcParams
['grid.color'] = conf
["couleurs"]["grille"]
149 plt
.rcParams
['legend.edgecolor'] = conf
["couleurs"]["grille"]
152 ###################### Tracé des différentes courbes
153 #Tracé des courbes OMS
154 for (i
,label
,couleur
) in liste_data_labels
:
155 ax
.plot(coljour
,oms
.extraire_colonne(t
,i
,jour_maxi
),label
=label
,color
=couleur
)
157 debug("cree_figure : tracé des courbes OMS ok",liste_err
)
159 ### Tracé pour de bon
161 print(conf
["symbole"])
162 ax
.plot(l_jours_conv
,l_poids
,label
=conf
["nom"],color
=conf
["couleurs"]["courbeenfant"],marker
=conf
["symbole"])
163 debug("Tracé de la courbe enfant, avec les jours "+str(l_jours_conv
),liste_err
)
167 if conf
["nom"] != "": # Ajouter le nom de l'enfant
168 listenoms
.append(conf
["nom"])
170 # if enfants_add != []:
171 # debug("Il y a des enfants en plus à tracer. Données : "+str(enfants_add), liste_err)
172 for enfant
in enfants_add
: # Enfants additionnels éventuels
173 conf_add
, lj
, ld
= enfant
[typedonnee
] # On récuère les données
174 if lj
!= []: # pas la peine de tracer si y'a rien à tracer
175 # Ajouter le nom de cet enfant-là
176 listenoms
.append(conf_add
["nom"])
177 lj_conv
= u
.convertit_tableau(lj
,unite
,liste_err
)
178 debug("Tracé de la courbe additionnelle de "+conf_add
["nom"]+" config : "+str(conf_add
), liste_err
)
179 ax
.plot(lj_conv
, ld
, label
=conf_add
["nom"], color
=conf_add
["couleurcourbe"],marker
=conf_add
["symbole"])
181 if conf
["sexe"] != conf_add
["sexe"]:
182 warning("Attention, tous les enfants n'ont pas le même sexe. La courbe de référence est celle de "+conf
["nom"]+" et ne sera pas forcément pertinente pour les autres. Vous pouvez éventuellement essayer la courbe neutre. Remarque : cette alerte s'affichera quand même.", liste_err
)
185 # Si y'a un nom on met "courbe de machin"
187 titre
+= " de " +", ".join(listenoms
)
190 titre
+= ", préma de "+conf
["prematurite"]
191 if conf
["agecorrige"] == "oui":
192 titre
+=" (courbe en âge corrigé)"
194 titre
+=" (courbe en âge réel, données OMS décalées)"
196 #### extrapolatios éventuelles
197 # a-t-on demndé des calculs ?
198 jextrapole
= conf
["non_sauve"]["prolongercourbes"] == "oui"
199 # Est-ce qu'on a demandé un calcul sur cette donnée ?
201 for calextra
in CONFIG
["extradata"]:
202 jextrapole
= jextrapole
or conf
["non_sauve"][calextra
+"_type"] == typedonnee
205 ############################## Là où on extrapole ################################
208 debug("Il faut extrapoler les courbes !", liste_err
)
209 # Prendre l'ensemble des dates "source"
210 # print(conf["non_sauve"]["nbdataextra"])
211 if conf
["non_sauve"]["nbextradata"] == 0:
212 sources_extrap
= l_jours
213 sources_extrap_data
= l_poids
215 sources_extrap
= l_jours
[-conf
["non_sauve"]["nbextradata"]:] # les derniers jours
216 sources_extrap_data
= l_poids
[-conf
["non_sauve"]["nbextradata"]:]
218 debug("On extrapole sur les jours : "+str(sources_extrap
), liste_err
)
220 # On récupère toutes les données extrapolées
221 dates_extrapole
, donnees_extrapole
= prolongecourbe(t
, sources_extrap
, sources_extrap_data
, conf
["typecourbe"], liste_err
)
222 debug("données extrapolées !", liste_err
)
223 #debug(str(dates_extrapole[0:10])+str(donnees_extrapole[0:10]), liste_err)
225 # QUe veut-on maintenant sur ces données extrapolées ?
227 if conf
["non_sauve"]["prolongercourbes"] == "oui":
228 # On va prendre les extrapolations de la dernière donnée jusqu'à l fin du graphe
229 debut_extr
= int(l_jours
[-conf
["non_sauve"]["nbextradata"]])
230 i_debut_extr
= dates_extrapole
.index(debut_extr
)
231 if jour_maxi
>= dates_extrapole
[-1]:
232 i_fin_extr
= len(dates_extrapole
) -1
234 i_fin_extr
= dates_extrapole
.index(jour_maxi
)
235 print("bla", i_debut_extr
, i_fin_extr
)
236 # Voilà ce qu'on veut tracer
237 dates_extrapole_trace
= dates_extrapole
[i_debut_extr
:i_fin_extr
+1]
238 donnees_extrapole_trace
= donnees_extrapole
[i_debut_extr
:i_fin_extr
+1]
239 dates_extrapole_trace
= u
.convertit_tableau(dates_extrapole_trace
,unite
,liste_err
)
241 # tracé des données extrapolées
242 plt
.plot(dates_extrapole_trace
, donnees_extrapole_trace
,color
=conf
["couleurs"]["cadretxt"], linestyle
=(0, (5,7)), marker
=None)
243 debug("Tracé de la courbe extrapolée ok", liste_err
)
245 ### Calculer une donnée à l'âge x
246 if conf
["non_sauve"]["calculextradata_type"] == typedonnee
:
247 # On essaie l'interpolation
248 r
= interpole_lineaire(l_jours
,l_poids
,conf
["non_sauve"]["calculextradata_age"], liste_err
)
250 # ça sera donc une extrapolation
251 r
= calcule_donnee_extrapolee(dates_extrapole
, donnees_extrapole
, conf
["non_sauve"]["calculextradata_age"], liste_err
)
252 message
=formate_extrapole(conf
["non_sauve"]["nbextradata"])
254 message
=formate_interpole()
256 texte
= formate_resultat_donnee(conf
["non_sauve"]["calculextradata_age"], conf
["non_sauve"]["calculextradata_date"], r
, typedonnee
, message
, liste_err
)
257 debug("calcul de la donnée extrapolée : "+texte
, liste_err
)
259 liste_extracalculs
.append(texte
)
260 #print(liste_extracalculs)
262 if conf
["non_sauve"]["calculextradata_trace"] == "oui":
263 dessine_guides(conf
["non_sauve"]["calculextradata_age"], r
, conf
["couleurs"]["cadretxt"], unite
, ax
, liste_err
)
265 ### Calculer un âge où on atteint cette donnée
266 if conf
["non_sauve"]["calculextratemps_type"] == typedonnee
:
268 r
= interpole_lineaire_ordonnee(l_jours
,l_poids
,conf
["non_sauve"]["calculextratemps_val"], liste_err
)
269 if type(conf
["naissance"]) == datetime
.date
:
270 rdate
= conf
["naissance"] + datetime
.timedelta(days
=r
)
275 # ça sera donc une extrapolation
276 r
= calcule_age_extrapole(dates_extrapole
, donnees_extrapole
, conf
["non_sauve"]["calculextratemps_val"], liste_err
)
277 if type(conf
["naissance"]) == datetime
.date
:
278 rdate
= conf
["naissance"] + datetime
.timedelta(days
=round(r
))
281 message
=formate_extrapole(conf
["non_sauve"]["nbextradata"])
283 message
=formate_interpole()
285 texte
= formate_resultat_age(r
, rdate
, conf
["non_sauve"]["calculextratemps_val"], typedonnee
, message
, liste_err
)
287 #r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], typedonnee, liste_err)
289 liste_extracalculs
.append(texte
)
290 print(liste_extracalculs
)
292 if conf
["non_sauve"]["calculextratemps_trace"]:
293 dessine_guides(r
, conf
["non_sauve"]["calculextratemps_val"], conf
["couleurs"]["cadretxt"], unite
, ax
, liste_err
)
296 warning("Des problèmes pour extrapoler...", liste_err
)
299 debug("On ne trace pas de courbe enfant", liste_err
)
301 ###################" Gestion de l'échelle #####################
302 debug("Courbes tracées. Il n'y a plus qu'à gérer l'échelle", liste_err
)
305 # On extrait la valeur min et la valeur max des poids des courbes OMS et des données
306 (colonne_min
,_
,_
) = liste_data_labels
[-1]
307 (colonne_max
,_
,_
) = liste_data_labels
[0]
310 poids_min
= min(oms
.extraire_colonne(t
,colonne_min
,jour_maxi
))
311 poids_max
= max(oms
.extraire_colonne(t
,colonne_max
,jour_maxi
))
313 poids_min
= min(min(l_poids
),poids_min
)
314 # Pour le poids max, voir la dernière valeur du tableau
316 while i
<len(l_jours
) and l_jours
[i
]<jour_maxi
:
318 poids_max
= max(max(l_poids
[0:i
+1]),poids_max
)
319 # On ajuste un peu ces min et max
320 # min : valeur min -1kg
321 poids_min
= max(0,poids_min
-1)
323 poids_max
= poids_max
* 1.05
326 ### Repères additionnels éventuels.
327 #reperes est une liste qui contient des dictionnaires avec "typed" (type de donnée : âge, etc), "donnee" :
328 #la donnée (en jours pour l'âge), et "texte": le texte à mettre sur le repère en question.
329 #Pour tracer des repères verticaux (horizontaux plus tard) sur la courbe.
330 for rep
in conf
["liste_reperes"]:
331 if rep
.get("trace", "") == "oui": # SI on veut tracer
332 agec
= u
.convertitunite(rep
["donnee"], unite
, liste_err
)
333 # Tracé de la ligne verticale
334 ax
.vlines(agec
, poids_min
, poids_max
, linestyles
="dashed", color
=conf
["couleurs"]["cadretxt"])
336 if rep
["affichedate"] == "oui" and rep
["date"] != "":
337 ax
.text(agec
, poids_min
,rep
["date"]+" ", rotation
=90, verticalalignment
='top', horizontalalignment
='center', color
=conf
["couleurs"]["cadretxt"], fontstyle
="italic")
338 # Si y'a un texte à afficher
339 if rep
["texte"] != "":
340 ax
.text(agec
, poids_min
, " "+rep
["texte"], rotation
=90, verticalalignment
='bottom', horizontalalignment
='right', color
=conf
["couleurs"]["cadretxt"])
347 if conf
["non_sauve"]["grilleamelio"] == "oui":
348 debug("On a choisi la grille plus jolie", liste_err
)
349 pas
=u
.choix_echelle_data(typedonnee
, poids_max
)
351 minechelle
= int(poids_min
/pas
[0])*pas
[0]
353 debug("pas choisis pour l'échelle en y : "+str(pas
), liste_err
)
354 echellemajeure
= arange(minechelle
, poids_max
, pas
[0])
357 echellemineure
= arange(minechelle
, poids_max
, pas
[1])
361 ax
.set_yticks(echellemajeure
, minor
=False)
362 ax
.set_yticks(echellemineure
, minor
=True)
365 pas
=u
.choix_echelle_temps(unite
, age_maxi
)
366 debug("pas choisis pour l'échelle en x : "+str(pas
), liste_err
)
368 echellemajeure
= arange(0,age_maxi
, pas
[0])
370 echellemineure
= arange(0,age_maxi
, pas
[1])
373 ax
.set_xticks(echellemajeure
, minor
=False)
374 ax
.set_xticks(echellemineure
, minor
=True)
376 ################################# Aspect du graphique
378 debug("On commende la déco du graphique", liste_err
)
381 ax
.grid(conf
["grille"]=="oui")
382 ax
.grid(conf
["grille"] == "oui", which
="minor", linestyle
="--")
385 plt
.xlabel("Âge en "+unite
,color
=conf
["couleurs"]["cadretxt"])
386 plt
.ylabel(typedonnee
.capitalize()+" en "+CONFIG
["unites_typedonnees"][typedonnee
],color
=conf
["couleurs"]["cadretxt"])
387 plt
.title(titre
,color
=conf
["couleurs"]["cadretxt"])
388 if l_jours_conv
== []:
389 plt
.axis([0,age_maxi
, poids_min
, poids_max
])
391 plt
.axis([min(0,l_jours_conv
[0]),age_maxi
,poids_min
,poids_max
])
394 if conf
['legende']=="oui":
395 legende
= plt
.legend(loc
=conf
['positionlegende'])
396 plt
.setp(legende
.get_texts(), color
=conf
["couleurs"]["cadretxt"])
401 debug("Fin de cree_figure, tout va bien.", liste_err
)
407 ######################################## Pour extrapoler la courbe
409 def prolongecourbe(tableauOMS
, dates
, donnees
, typecourbe
, liste_err
):
410 """ tableauOMS est le ableau des données OMS. dates et donnees sont les dates (jours)
411 et les données d'où on extrapole. On calcule toutes les dates comme des sauvages.
412 On renvoie la liste des jours totale et la liste des data_totales
414 typecourbe est P ou Z. Pour P il faut commencer à regarder à l'indice 4, pour Z
416 On renvoie [],[] si pas pu extrapoler. """
417 # les lignes OMS correspondant aux dates données
418 lignesoms
= [tableauOMS
[int(date
)] for date
in dates
]
419 debug("prolongecourbe : Lignes OMS :"+str(lignesoms
)+" valeur de données : "+str(donnees
), liste_err
)
423 # Principe : on cherche dans quel intervalle de "colonnes" on se situe.
424 # On va donc regarder pour chaque donnée entre quels i on se situe,et après
425 # prendre le plus grand intervalle.
426 # Numéros de colonnes d'où on part. Pour la fin c'est forcément longueur-1
427 if typecourbe
== "P":
433 for k
in range(len(dates
)):
436 while i
<len(ligne
) and ligne
[i
]<donnees
[k
]:
438 debug("prolongecourbe : on a trouvé la valeur de i : "+str(i
),liste_err
)
440 warning("prolongation de courbe : pas réussi... donnée trop haute !", liste_err
)
443 warning("prolongation de courbe : pas réussi... donnée trop basse !", liste_err
)
445 liste_indices
.append(i
)
446 imin
=min(liste_indices
) -1
447 imax
=max(liste_indices
)
448 debug("Les données se situent dans les indices : "+str(imin
)+", "+str(imax
),liste_err
)
449 # Maintenant on doit trouver les coeffs : on se situe en coeff * l[imin]+ (1-coeff)*ligne[imax]
450 # Et faire la moyenne de ces coeffs
452 for k
in range(len(dates
)):
455 total
+= (donnee
- ligne
[imax
])/(ligne
[imin
] - ligne
[imax
])
457 coeff_moyen
= total
/len(dates
)
459 debug("Coeff moyen calculé : "+str(coeff_moyen
), liste_err
)
461 # On utilisera la même chose pour les nouvelle donnee
464 nouvdates
=oms
.extraire_colonne(tableauOMS
,0) # On sort tout.
468 ligne2
= tableauOMS
[int(j
)]
469 nouvdonnees
.append(coeff_moyen
*ligne2
[imin
]+ (1-coeff_moyen
)*ligne2
[imax
])
471 return nouvdates
,nouvdonnees
474 def dessine_guides(t
, data
, couleur
, unite
, ax
, liste_err
):
475 """ dessine deux lignes, horizontales et verticales qui vont "vers" la courbe
476 jusqu'aux points (t, data). En pointillés et avec un point dessus."""
477 debug("Début de dessine_guides"+str(t
)+", "+str(data
), liste_err
)
478 t_conv
= u
.convertitunite(t
,unite
,liste_err
)
479 ax
.vlines(t_conv
, 0, data
, colors
=couleur
, linestyles
="dashed")
480 ax
.hlines(data
, 0, t_conv
, color
=couleur
, linestyles
="dashed")
481 ax
.plot([t_conv
], [data
], color
=couleur
, marker
="*", ms
=13)