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
10 from numpy
import arange
13 import matplotlib
.pyplot
as plt
15 # Essentiellement, la fonction qui trace la courbe, mais on y trouve également les fonctions d'extrapolation.
16 # Ainsi que les calculs additionnels.
19 def cree_figure(conf
,l_jours
,l_poids
,typedonnee
,liste_extracalculs
, liste_err
, enfants_add
= []):
20 """ conf est le dictionnaire de config. l_jours et l_poids les listes des temps (en jours) et de données
21 (donc pas forcément du poids)
22 typedonnee est le type de données (voir CONFIG["liste_typedonnees"]
23 liste_err la liste des erreurs à compléter (voir gestion_erreurs))
24 Renvoie la figure tracée, et les calculs additionnels sont mis sous forme de chaîne dans la liste
27 Les enfants en plus sont dans la liste enfants_add. Pour chaque item de la liste, il faut prendre
28 item[typed] pour avoir accès au nom, ljours, et ldonnees
31 debug("debut de cree_figure. Config : "+str(conf
)+". Nombre d'enfants additionnels : "+str(len(enfants_add
)),liste_err
)
33 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
)
35 erreur("bug avec liste data labels",liste_err
)
38 ######################## Gestion des bornes #############################
39 # y a-t-il un maxi saisi par l'utilisateur ?
41 # Est-ce qu'on a donné un maxi quand même (car même échelle) ?
42 if conf
["non_sauve"].get("maxi",0) == 0:
43 jour_maxi
= calcule_max_graphique(l_jours
)
45 jour_maxi
= conf
["non_sauve"]["maxi"]+1
47 jour_maxi
= conf
["maxi"]+1
49 # Si on cherche à extrapoler au-delà
50 if conf
["non_sauve"]["calculextradata_type"] == typedonnee
and conf
["non_sauve"]["calculextradata_age"]>jour_maxi
:
51 jour_maxi
= int(conf
["non_sauve"]["calculextradata_age"]) +1
53 # On s'assure que c'est bien compris dans les bornes
54 jour_maxi
= max(CONFIG
["jours_mini_courbe"],min(jour_maxi
,CONFIG
["jours_maxi_courbe"]))
55 debug("cree_figure : gestion du jour max : "+str(jour_maxi
),liste_err
)
57 ##################### Gestion des unités ###############################
58 # si l'unité n'est pas précisée, ni en "non sauvé" ni par l'utilisateur
59 if conf
["unite"] == "" and conf
["non_sauve"].get("unite","") == "":
60 unite
= u
.choix_unite(jour_maxi
)
61 debug("Unité non précisée, on choisit "+unite
,liste_err
)
62 elif conf
["unite"] != "":
65 unite
= conf
["non_sauve"]["unite"]
67 ##################### Gestion de la prématurité #######################"
68 prema
= int(convertit_jours_vers_python(conf
["prematurite"],liste_err
))
69 ## Gestion des prémas, deux cas :
70 # Si agecorrige est oui, alors on veut juste soustraire la valeur de préma
71 # à toutes les données.
72 # Si agecorrige est non, alors on veut ajouter la valeur de préma aux courbes de référence.
73 debug("Prématurité : "+str(prema
)+" age corrigé : "+conf
["agecorrige"],liste_err
)
74 if prema
>0 and conf
["agecorrige"] == "oui":
75 l_jours
= [j
-prema
for j
in l_jours
]
76 jour_maxi
= jour_maxi
- prema
79 ###################### Conversion des unités ###########################""
80 l_jours_conv
= u
.convertit_tableau(l_jours
,unite
,liste_err
)
81 # Attention, comme les jours commencent à partir de 0, faut enlever 1 pour avoir la borne...
82 age_maxi
= u
.convertitunite(jour_maxi
-1,unite
,liste_err
)
84 debug("cree_figure : conversion des unités ok : "+str(l_jours_conv
),liste_err
)
86 #####################" Courbes OMS et titre ######################################"
87 titre
= "Courbe de "+typedonnee
+" OMS"
88 if conf
["typecourbe"] == "P":
90 liste_data_labels
= liste_data_labels_p
91 if conf
["sexe"] == "M":
92 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["perc_garcon"]#f_poids_perc_garcon
93 titre
+= " (percentiles, garçon)"
94 elif conf
["sexe"] == "F":
95 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["perc_fille"]
96 titre
+= " (percentiles, fille)"
98 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["perc_mixte"]
99 titre
+= " (percentiles)"
100 elif conf
["typecourbe"] == "Z":
101 liste_data_labels
= liste_data_labels_z
102 if conf
["sexe"] == "M":
103 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_garcon"]
104 titre
+= " (moyenne et écarts-types, garçon)"
105 elif conf
["sexe"] == "F":
106 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_fille"]
107 titre
+= " (moyenne et écarts-types, fille)"
109 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_mixte"]
110 titre
+= " (moyenne et écarts-types)"
112 erreur("Type de courbe invalide"+conf
["typecourbe"],liste_err
)
115 ## On finira le titre plus tard quand on aura su qui est concerné
117 #debug("cree_figure : géré le type de courbe ok. Liste des data labels : "+str(liste_data_labels),liste_err)
118 debug("Fichier d'où extraire les données : "+fichier_oms
,liste_err
)
121 #### On extrait les données des courbes, et on convertit les jours dans l'unité voulues
123 t
= oms
.lire_fichier_csv(fichier_oms
)
125 erreur("cree_figure : Impossible d'ouvrir le fichier "+fichier_oms
, liste_err
)
128 debug("cree_figure : Conversion des données OMS à la bonne unité",liste_err
)
130 coljour
= (oms
.extraire_colonne(t
,0,jour_maxi
))
131 if prema
>0 and conf
["agecorrige"] != "oui":
132 coljour
= [j
+ prema
for j
in coljour
]
133 coljour
= u
.convertit_tableau(coljour
,unite
,liste_err
)
135 erreur("Problème à la conversion du tableau OMS. jour_maxi = "+str(jour_maxi
)+" unite = "+unite
,liste_err
)
138 ##################### Création de la figure et du graphique ###################
139 debug("cree_figure : prête à créer la figure",liste_err
)
140 #### La figure, params
142 fig
= plt
.figure(num
=None, figsize
=(conf
["largeur"], conf
["hauteur"]), dpi
=100, facecolor
=conf
["couleurs"]["fond"])
143 plt
.rcParams
['axes.facecolor'] = conf
["couleurs"]["fond"]
144 plt
.rcParams
['axes.edgecolor']= conf
["couleurs"]["cadretxt"]
145 plt
.rcParams
['xtick.color'] = conf
["couleurs"]["cadretxt"]
146 plt
.rcParams
['ytick.color'] = conf
["couleurs"]["cadretxt"]
147 plt
.rcParams
['grid.color'] = conf
["couleurs"]["grille"]
148 plt
.rcParams
['legend.edgecolor'] = conf
["couleurs"]["grille"]
151 ###################### Tracé des différentes courbes
152 #Tracé des courbes OMS
153 for (i
,label
,couleur
) in liste_data_labels
:
154 ax
.plot(coljour
,oms
.extraire_colonne(t
,i
,jour_maxi
),label
=label
,color
=couleur
)
156 debug("cree_figure : tracé des courbes OMS ok",liste_err
)
158 ### Tracé pour de bon
160 print(conf
["symbole"])
161 ax
.plot(l_jours_conv
,l_poids
,label
=conf
["nom"],color
=conf
["couleurs"]["courbeenfant"],marker
=conf
["symbole"])
162 debug("Tracé de la courbe enfant, avec les jours "+str(l_jours_conv
),liste_err
)
166 if conf
["nom"] != "": # Ajouter le nom de l'enfant
167 listenoms
.append(conf
["nom"])
169 # if enfants_add != []:
170 # debug("Il y a des enfants en plus à tracer. Données : "+str(enfants_add), liste_err)
171 for enfant
in enfants_add
: # Enfants additionnels éventuels
172 conf_add
, lj
, ld
= enfant
[typedonnee
] # On récuère les données
173 if lj
!= []: # pas la peine de tracer si y'a rien à tracer
174 # Ajouter le nom de cet enfant-là
175 listenoms
.append(conf_add
["nom"])
176 lj_conv
= u
.convertit_tableau(lj
,unite
,liste_err
)
177 debug("Tracé de la courbe additionnelle de "+conf_add
["nom"]+" config : "+str(conf_add
), liste_err
)
178 ax
.plot(lj_conv
, ld
, label
=conf_add
["nom"], color
=conf_add
["couleurcourbe"],marker
=conf_add
["symbole"])
180 if conf
["sexe"] != conf_add
["sexe"]:
181 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
)
184 # Si y'a un nom on met "courbe de machin"
186 titre
+= " de " +", ".join(listenoms
)
189 titre
+= ", préma de "+conf
["prematurite"]
190 if conf
["agecorrige"] == "oui":
191 titre
+=" (courbe en âge corrigé)"
193 titre
+=" (courbe en âge réel, données OMS décalées)"
195 #### extrapolatios éventuelles
196 # a-t-on demndé des calculs ?
197 jextrapole
= conf
["non_sauve"]["prolongercourbes"] == "oui"
198 # Est-ce qu'on a demandé un calcul sur cette donnée ?
200 for calextra
in CONFIG
["extradata"]:
201 jextrapole
= jextrapole
or conf
["non_sauve"][calextra
+"_type"] == typedonnee
204 ############################## Là où on extrapole ################################
207 debug("Il faut extrapoler les courbes !", liste_err
)
208 # Prendre l'ensemble des dates "source"
209 # print(conf["non_sauve"]["nbdataextra"])
210 if conf
["non_sauve"]["nbextradata"] == 0:
211 sources_extrap
= l_jours
212 sources_extrap_data
= l_poids
214 sources_extrap
= l_jours
[-conf
["non_sauve"]["nbextradata"]:] # les derniers jours
215 sources_extrap_data
= l_poids
[-conf
["non_sauve"]["nbextradata"]:]
217 debug("On extrapole sur les jours : "+str(sources_extrap
), liste_err
)
219 # On récupère toutes les données extrapolées
220 dates_extrapole
, donnees_extrapole
= prolongecourbe(t
, sources_extrap
, sources_extrap_data
, conf
["typecourbe"], liste_err
)
221 debug("données extrapolées !", liste_err
)
222 #debug(str(dates_extrapole[0:10])+str(donnees_extrapole[0:10]), liste_err)
224 # QUe veut-on maintenant sur ces données extrapolées ?
226 if conf
["non_sauve"]["prolongercourbes"] == "oui":
227 # On va prendre les extrapolations de la dernière donnée jusqu'à l fin du graphe
228 debut_extr
= int(l_jours
[-conf
["non_sauve"]["nbextradata"]])
229 i_debut_extr
= dates_extrapole
.index(debut_extr
)
230 i_fin_extr
= dates_extrapole
.index(jour_maxi
)
231 # Voilà ce qu'on veut tracer
232 dates_extrapole_trace
= dates_extrapole
[i_debut_extr
:i_fin_extr
+1]
233 donnees_extrapole_trace
= donnees_extrapole
[i_debut_extr
:i_fin_extr
+1]
234 dates_extrapole_trace
= u
.convertit_tableau(dates_extrapole_trace
,unite
,liste_err
)
236 # tracé des données extrapolées
237 plt
.plot(dates_extrapole_trace
, donnees_extrapole_trace
,color
=conf
["couleurs"]["cadretxt"], linestyle
=(0, (5,7)), marker
=None)
238 debug("Tracé de la courbe extrapolée ok", liste_err
)
240 ### Calculer une donnée à l'âge x
241 if conf
["non_sauve"]["calculextradata_type"] == typedonnee
:
242 # On essaie l'interpolation
243 r
= interpole_lineaire(l_jours
,l_poids
,conf
["non_sauve"]["calculextradata_age"], liste_err
)
245 # ça sera donc une extrapolation
246 r
= calcule_donnee_extrapolee(dates_extrapole
, donnees_extrapole
, conf
["non_sauve"]["calculextradata_age"], liste_err
)
247 message
=formate_extrapole(conf
["non_sauve"]["nbextradata"])
249 message
=formate_interpole()
251 texte
= formate_resultat_donnee(conf
["non_sauve"]["calculextradata_age"], r
, typedonnee
, message
, liste_err
)
252 debug("calcul de la donnée extrapolée : "+texte
, liste_err
)
254 liste_extracalculs
.append(texte
)
255 print(liste_extracalculs
)
257 if conf
["non_sauve"]["calculextradata_trace"] == "oui":
258 dessine_guides(conf
["non_sauve"]["calculextradata_age"], r
, conf
["couleurs"]["cadretxt"], unite
, ax
, liste_err
)
260 ### Calculer un âge où on atteint cette donnée
261 if conf
["non_sauve"]["calculextratemps_type"] == typedonnee
:
263 r
= interpole_lineaire_ordonnee(l_jours
,l_poids
,conf
["non_sauve"]["calculextratemps_val"], liste_err
)
265 # ça sera donc une extrapolation
266 r
= calcule_age_extrapole(dates_extrapole
, donnees_extrapole
, conf
["non_sauve"]["calculextratemps_val"], liste_err
)
267 message
=formate_extrapole(conf
["non_sauve"]["nbextradata"])
269 message
=formate_interpole()
271 texte
= formate_resultat_age(r
, conf
["non_sauve"]["calculextratemps_val"], typedonnee
, message
, liste_err
)
273 #r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], typedonnee, liste_err)
275 liste_extracalculs
.append(texte
)
276 print(liste_extracalculs
)
278 if conf
["non_sauve"]["calculextratemps_trace"]:
279 dessine_guides(r
, conf
["non_sauve"]["calculextratemps_val"], conf
["couleurs"]["cadretxt"], unite
, ax
, liste_err
)
282 warning("Des problèmes pour extrapoler...", liste_err
)
285 debug("On ne trace pas de courbe enfant", liste_err
)
287 ###################" Gestion de l'échelle #####################
288 debug("Courbes tracées. Il n'y a plus qu'à gérer l'échelle", liste_err
)
291 # On extrait la valeur min et la valeur max des poids des courbes OMS et des données
292 (colonne_min
,_
,_
) = liste_data_labels
[-1]
293 (colonne_max
,_
,_
) = liste_data_labels
[0]
296 poids_min
= min(oms
.extraire_colonne(t
,colonne_min
,jour_maxi
))
297 poids_max
= max(oms
.extraire_colonne(t
,colonne_max
,jour_maxi
))
299 poids_min
= min(min(l_poids
),poids_min
)
300 # Pour le poids max, voir la dernière valeur du tableau
302 while i
<len(l_jours
) and l_jours
[i
]<jour_maxi
:
304 poids_max
= max(max(l_poids
[0:i
+1]),poids_max
)
305 # On ajuste un peu ces min et max
306 # min : valeur min -1kg
307 poids_min
= max(0,poids_min
-1)
309 poids_max
= poids_max
* 1.05
312 ### Repères additionnels éventuels.
313 #reperes est une liste qui contient des dictionnaires avec "typed" (type de donnée : âge, etc), "donnee" :
314 #la donnée (en jours pour l'âge), et "texte": le texte à mettre sur le repère en question.
315 #Pour tracer des repères verticaux (horizontaux plus tard) sur la courbe.
316 for rep
in conf
["liste_reperes"]:
317 if rep
.get("trace", "") == "oui": # SI on veut tracer
318 agec
= u
.convertitunite(rep
["donnee"], unite
, liste_err
)
319 # Tracé de la ligne verticale
320 ax
.vlines(agec
, poids_min
, poids_max
, linestyles
="dashed", color
=conf
["couleurs"]["cadretxt"])
321 # Tracé éventuel du texte
322 if rep
["texte"] != "":
323 ax
.text(agec
, poids_min
, " "+rep
["texte"], rotation
=90, verticalalignment
='bottom', horizontalalignment
='right', color
=conf
["couleurs"]["cadretxt"])
330 if conf
["non_sauve"]["grilleamelio"] == "oui":
331 debug("On a choisi la grille plus jolie", liste_err
)
332 pas
=u
.choix_echelle_data(typedonnee
, poids_max
)
334 minechelle
= int(poids_min
/pas
[0])*pas
[0]
336 debug("pas choisis pour l'échelle en y : "+str(pas
), liste_err
)
337 echellemajeure
= arange(minechelle
, poids_max
, pas
[0])
340 echellemineure
= arange(minechelle
, poids_max
, pas
[1])
344 ax
.set_yticks(echellemajeure
, minor
=False)
345 ax
.set_yticks(echellemineure
, minor
=True)
348 pas
=u
.choix_echelle_temps(unite
, age_maxi
)
349 debug("pas choisis pour l'échelle en x : "+str(pas
), liste_err
)
351 echellemajeure
= arange(0,age_maxi
, pas
[0])
353 echellemineure
= arange(0,age_maxi
, pas
[1])
356 ax
.set_xticks(echellemajeure
, minor
=False)
357 ax
.set_xticks(echellemineure
, minor
=True)
359 ################################# Aspect du graphique
361 debug("On commende la déco du graphique", liste_err
)
364 ax
.grid(conf
["grille"]=="oui")
365 ax
.grid(conf
["grille"] == "oui", which
="minor", linestyle
="--")
368 plt
.xlabel("Âge en "+unite
,color
=conf
["couleurs"]["cadretxt"])
369 plt
.ylabel(typedonnee
.capitalize()+" en "+CONFIG
["unites_typedonnees"][typedonnee
],color
=conf
["couleurs"]["cadretxt"])
370 plt
.title(titre
,color
=conf
["couleurs"]["cadretxt"])
371 if l_jours_conv
== []:
372 plt
.axis([0,age_maxi
, poids_min
, poids_max
])
374 plt
.axis([min(0,l_jours_conv
[0]),age_maxi
,poids_min
,poids_max
])
377 if conf
['legende']=="oui":
378 legende
= plt
.legend(loc
=conf
['positionlegende'])
379 plt
.setp(legende
.get_texts(), color
=conf
["couleurs"]["cadretxt"])
384 debug("Fin de cree_figure, tout va bien.", liste_err
)
390 ######################################## Pour extrapoler la courbe
392 def prolongecourbe(tableauOMS
, dates
, donnees
, typecourbe
, liste_err
):
393 """ tableauOMS est le ableau des données OMS. dates et donnees sont les dates (jours)
394 et les données d'où on extrapole. On calcule toutes les dates comme des sauvages.
395 On renvoie la liste des jours totale et la liste des data_totales
397 typecourbe est P ou Z. Pour P il faut commencer à regarder à l'indice 4, pour Z
399 On renvoie [],[] si pas pu extrapoler. """
400 # les lignes OMS correspondant aux dates données
401 lignesoms
= [tableauOMS
[int(date
)] for date
in dates
]
402 debug("prolongecourbe : Lignes OMS :"+str(lignesoms
)+" valeur de données : "+str(donnees
), liste_err
)
406 # Principe : on cherche dans quel intervalle de "colonnes" on se situe.
407 # On va donc regarder pour chaque donnée entre quels i on se situe,et après
408 # prendre le plus grand intervalle.
409 # Numéros de colonnes d'où on part. Pour la fin c'est forcément longueur-1
410 if typecourbe
== "P":
416 for k
in range(len(dates
)):
419 while i
<len(ligne
) and ligne
[i
]<donnees
[k
]:
421 debug("prolongecourbe : on a trouvé la valeur de i : "+str(i
),liste_err
)
423 warning("prolongation de courbe : pas réussi... donnée trop haute !", liste_err
)
426 warning("prolongation de courbe : pas réussi... donnée trop basse !", liste_err
)
428 liste_indices
.append(i
)
429 imin
=min(liste_indices
) -1
430 imax
=max(liste_indices
)
431 debug("Les données se situent dans les indices : "+str(imin
)+", "+str(imax
),liste_err
)
432 # Maintenant on doit trouver les coeffs : on se situe en coeff * l[imin]+ (1-coeff)*ligne[imax]
433 # Et faire la moyenne de ces coeffs
435 for k
in range(len(dates
)):
438 total
+= (donnee
- ligne
[imax
])/(ligne
[imin
] - ligne
[imax
])
440 coeff_moyen
= total
/len(dates
)
442 debug("Coeff moyen calculé : "+str(coeff_moyen
), liste_err
)
444 # On utilisera la même chose pour les nouvelle donnee
447 nouvdates
=oms
.extraire_colonne(tableauOMS
,0) # On sort tout.
451 ligne2
= tableauOMS
[int(j
)]
452 nouvdonnees
.append(coeff_moyen
*ligne2
[imin
]+ (1-coeff_moyen
)*ligne2
[imax
])
454 return nouvdates
,nouvdonnees
457 def dessine_guides(t
, data
, couleur
, unite
, ax
, liste_err
):
458 """ dessine deux lignes, horizontales et verticales qui vont "vers" la courbe
459 jusqu'aux points (t, data). En pointillés et avec un point dessus."""
460 debug("Début de dessine_guides"+str(t
)+", "+str(data
), liste_err
)
461 t_conv
= u
.convertitunite(t
,unite
,liste_err
)
462 ax
.vlines(t_conv
, 0, data
, colors
=couleur
, linestyles
="dashed")
463 ax
.hlines(data
, 0, t_conv
, color
=couleur
, linestyles
="dashed")
464 ax
.plot([t_conv
], [data
], color
=couleur
, marker
="*", ms
=13)