0c7a1d075151d55e584d09b1e76ba32b531c663b
[perso/Denise/oms.git] / trace_courbe.py
1 #!/usr/bin/env python3
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
9
10 from numpy import arange
11
12
13 import matplotlib.pyplot as plt
14
15 # Essentiellement, la fonction qui trace la courbe, mais on y trouve également les fonctions d'extrapolation.
16 # Ainsi que les calculs additionnels.
17
18
19 def cree_figure(conf,l_jours,l_poids,typedonnee,liste_extracalculs, liste_err):
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
25 liste_extracalculs"""
26 debug("debut de cree_figure. Config : "+str(conf),liste_err)
27 try:
28 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)
29 except:
30 erreur("bug avec liste data labels",liste_err)
31 return ""
32
33 ######################## Gestion des bornes #############################
34 # y a-t-il un maxi saisi par l'utilisateur ?
35 if conf["maxi"] ==0:
36 # Est-ce qu'on a donné un maxi quand même (car même échelle) ?
37 if conf["non_sauve"].get("maxi",0) == 0:
38 jour_maxi = calcule_max_graphique(l_jours)
39 else:
40 jour_maxi = conf["non_sauve"]["maxi"]+1
41 else:
42 jour_maxi = conf["maxi"]+1
43
44 # Si on cherche à extrapoler au-delà
45 if conf["non_sauve"]["calculextradata_type"] == typedonnee and conf["non_sauve"]["calculextradata_age"]>jour_maxi:
46 jour_maxi = int(conf["non_sauve"]["calculextradata_age"]) +1
47
48 # On s'assure que c'est bien compris dans les bornes
49 jour_maxi = max(CONFIG["jours_mini_courbe"],min(jour_maxi,CONFIG["jours_maxi_courbe"]))
50 debug("cree_figure : gestion du jour max : "+str(jour_maxi),liste_err)
51
52 ##################### Gestion des unités ###############################
53 # si l'unité n'est pas précisée, ni en "non sauvé" ni par l'utilisateur
54 if conf["unite"] == "" and conf["non_sauve"].get("unite","") == "":
55 unite = u.choix_unite(jour_maxi)
56 debug("Unité non précisée, on choisit "+unite,liste_err)
57 elif conf["unite"] != "":
58 unite = conf["unite"]
59 else:
60 unite = conf["non_sauve"]["unite"]
61
62 ##################### Gestion de la prématurité #######################"
63 prema = int(convertit_jours_vers_python(conf["prematurite"],liste_err))
64 ## Gestion des prémas, deux cas :
65 # Si agecorrige est oui, alors on veut juste soustraire la valeur de préma
66 # à toutes les données.
67 # Si agecorrige est non, alors on veut ajouter la valeur de préma aux courbes de référence.
68 debug("Prématurité : "+str(prema)+" age corrigé : "+conf["agecorrige"],liste_err)
69 if prema>0 and conf["agecorrige"] == "oui":
70 l_jours = [j-prema for j in l_jours]
71 jour_maxi = jour_maxi - prema
72
73
74 ###################### Conversion des unités ###########################""
75 l_jours_conv = u.convertit_tableau(l_jours,unite,liste_err)
76 # Attention, comme les jours commencent à partir de 0, faut enlever 1 pour avoir la borne...
77 age_maxi = u.convertitunite(jour_maxi-1,unite,liste_err)
78
79 debug("cree_figure : conversion des unités ok : "+str(l_jours_conv),liste_err)
80
81 #####################" Courbes OMS et titre ######################################"
82 titre = "Courbe de "+typedonnee+" OMS"
83 if conf["typecourbe"] == "P":
84 # percentiles
85 liste_data_labels = liste_data_labels_p
86 if conf["sexe"] == "M":
87 fichier_oms = CONFIG["fichiersOMS"][typedonnee]["perc_garcon"]#f_poids_perc_garcon
88 titre += " (percentiles, garçon)"
89 elif conf["sexe"] == "F":
90 fichier_oms = CONFIG["fichiersOMS"][typedonnee]["perc_fille"]
91 titre += " (percentiles, fille)"
92 else:
93 fichier_oms = CONFIG["fichiersOMS"][typedonnee]["perc_mixte"]
94 titre += " (percentiles)"
95 elif conf["typecourbe"] == "Z":
96 liste_data_labels = liste_data_labels_z
97 if conf["sexe"] == "M":
98 fichier_oms = CONFIG["fichiersOMS"][typedonnee]["z_garcon"]
99 titre += " (moyenne et écarts-types, garçon)"
100 elif conf["sexe"] == "F":
101 fichier_oms = CONFIG["fichiersOMS"][typedonnee]["z_fille"]
102 titre += " (moyenne et écarts-types, fille)"
103 else:
104 fichier_oms = CONFIG["fichiersOMS"][typedonnee]["z_mixte"]
105 titre += " (moyenne et écarts-types)"
106 else:
107 erreur("Type de courbe invalide"+conf["typecourbe"],liste_err)
108 return ""
109
110 # Si y'a un nom on met "courbe de machin"
111 if conf["nom"] !="":
112 titre += " de " +conf["nom"]
113
114 if prema>0:
115 titre+= ", préma de "+conf["prematurite"]
116 if conf["agecorrige"] == "oui":
117 titre+=" (courbe en âge corrigé)"
118 else:
119 titre+=" (courbe en âge réel, données OMS décalées)"
120
121 #debug("cree_figure : géré le type de courbe ok. Liste des data labels : "+str(liste_data_labels),liste_err)
122 debug("Fichier d'où extraire les données : "+fichier_oms,liste_err)
123
124
125 #### On extrait les données des courbes, et on convertit les jours dans l'unité voulues
126 try:
127 t = oms.lire_fichier_csv(fichier_oms)
128 except:
129 erreur("cree_figure : Impossible d'ouvrir le fichier "+fichier_oms, liste_err)
130 return ""
131
132 debug("cree_figure : Conversion des données OMS à la bonne unité",liste_err)
133 try:
134 coljour= (oms.extraire_colonne(t,0,jour_maxi))
135 if prema>0 and conf["agecorrige"] != "oui":
136 coljour = [j + prema for j in coljour]
137 coljour = u.convertit_tableau(coljour,unite,liste_err)
138 except:
139 erreur("Problème à la conversion du tableau OMS. jour_maxi = "+str(jour_maxi)+" unite = "+unite,liste_err)
140 return ""
141
142 ##################### Création de la figure et du graphique ###################
143 debug("cree_figure : prête à créer la figure",liste_err)
144 #### La figure, params
145
146 fig = plt.figure(num=None, figsize=(conf["largeur"], conf["hauteur"]), dpi=100, facecolor=conf["couleurs"]["fond"])
147 plt.rcParams['axes.facecolor'] = conf["couleurs"]["fond"]
148 plt.rcParams['axes.edgecolor']= conf["couleurs"]["cadretxt"]
149 plt.rcParams['xtick.color'] = conf["couleurs"]["cadretxt"]
150 plt.rcParams['ytick.color'] = conf["couleurs"]["cadretxt"]
151 plt.rcParams['grid.color'] = conf["couleurs"]["grille"]
152 plt.rcParams['legend.edgecolor'] = conf["couleurs"]["grille"]
153 ax = plt.axes()
154
155 ###################### Tracé des différentes courbes
156 #Tracé des courbes OMS
157 for (i,label,couleur) in liste_data_labels:
158 ax.plot(coljour,oms.extraire_colonne(t,i,jour_maxi),label=label,color=couleur)
159
160 debug("cree_figure : tracé des courbes OMS ok",liste_err)
161
162 ### Tracé pour de bon
163 if l_jours != []:
164 ax.plot(l_jours_conv,l_poids,label=conf["nom"],color=conf["couleurs"]["cadretxt"],marker='o')
165 debug("Tracé de la courbe enfant, avec les jours "+str(l_jours_conv),liste_err)
166
167 #### extrapolatios éventuelles
168 # a-t-on demndé des calculs ?
169 jextrapole = conf["non_sauve"]["prolongercourbes"] == "oui"
170 # Est-ce qu'on a demandé un calcul sur cette donnée ?
171 print()
172 for calextra in CONFIG["extradata"]:
173 jextrapole = jextrapole or conf["non_sauve"][calextra+"_type"] == typedonnee
174
175 #print(jextrapole)
176 ############################## Là où on extrapole ################################
177 if jextrapole:
178 try:
179 debug("Il faut extrapoler les courbes !", liste_err)
180 # Prendre l'ensemble des dates "source"
181 # print(conf["non_sauve"]["nbdataextra"])
182 if conf["non_sauve"]["nbextradata"] == 0:
183 sources_extrap = l_jours
184 sources_extrap_data = l_poids
185 else:
186 sources_extrap = l_jours[-conf["non_sauve"]["nbextradata"]:] # les derniers jours
187 sources_extrap_data = l_poids[-conf["non_sauve"]["nbextradata"]:]
188
189 debug("On extrapole sur les jours : "+str(sources_extrap), liste_err)
190
191 # On récupère toutes les données extrapolées
192 dates_extrapole, donnees_extrapole = prolongecourbe(t, sources_extrap, sources_extrap_data, conf["typecourbe"], liste_err)
193 debug("données extrapolées !", liste_err)
194 #debug(str(dates_extrapole[0:10])+str(donnees_extrapole[0:10]), liste_err)
195
196 # QUe veut-on maintenant sur ces données extrapolées ?
197 # Afficher la courbe
198 if conf["non_sauve"]["prolongercourbes"] == "oui":
199 # On va prendre les extrapolations de la dernière donnée jusqu'à l fin du graphe
200 debut_extr = int(l_jours[-conf["non_sauve"]["nbextradata"]])
201 i_debut_extr = dates_extrapole.index(debut_extr)
202 i_fin_extr = dates_extrapole.index(jour_maxi)
203 # Voilà ce qu'on veut tracer
204 dates_extrapole_trace = dates_extrapole[i_debut_extr:i_fin_extr+1]
205 donnees_extrapole_trace = donnees_extrapole[i_debut_extr:i_fin_extr+1]
206 dates_extrapole_trace = u.convertit_tableau(dates_extrapole_trace,unite,liste_err)
207
208 # tracé des données extrapolées
209 plt.plot(dates_extrapole_trace, donnees_extrapole_trace,color=conf["couleurs"]["cadretxt"], linestyle=(0, (5,7)), marker=None)
210 debug("Tracé de la courbe extrapolée ok", liste_err)
211
212 ### Calculer une donnée à l'âge x
213 if conf["non_sauve"]["calculextradata_type"] == typedonnee:
214 # On essaie l'interpolation
215 r = interpole_lineaire(l_jours,l_poids,conf["non_sauve"]["calculextradata_age"], liste_err)
216 if r==-1:
217 # ça sera donc une extrapolation
218 r = calcule_donnee_extrapolee(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextradata_age"], liste_err)
219 message=formate_extrapole(conf["non_sauve"]["nbextradata"])
220 # if == 0:
221 # message+="l'ensemble des données"
222 # else:
223 # message+="les "+str(conf["non_sauve"]["nbextradata"])+" dernière"+met_s(conf["non_sauve"]["nbextradata"])+" données"
224 else:
225 message=formate_interpole()
226
227 texte = formate_resultat_donnee(conf["non_sauve"]["calculextradata_age"], r, typedonnee, message, liste_err)
228 debug("calcul de la donnée extrapolée : "+texte, liste_err)
229 if texte!="":
230 liste_extracalculs.append(texte)
231 print(liste_extracalculs)
232 # Ajouter le trait ?
233 if conf["non_sauve"]["calculextradata_trace"] == "oui":
234 dessine_guides(conf["non_sauve"]["calculextradata_age"], r, conf["couleurs"]["cadretxt"], unite, ax, liste_err)
235
236 ### Calculer un âge où on atteint cette donnée
237 if conf["non_sauve"]["calculextratemps_type"] == typedonnee:
238 # interpolation
239 r = interpole_lineaire_ordonnee(l_jours,l_poids,conf["non_sauve"]["calculextratemps_val"], liste_err)
240 if r==-1:
241 # ça sera donc une extrapolation
242 r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], liste_err)
243 message=formate_extrapole(conf["non_sauve"]["nbextradata"])
244 else:
245 message=formate_interpole()
246
247 texte = formate_resultat_age(r, conf["non_sauve"]["calculextratemps_val"], typedonnee, message, liste_err)
248
249 #r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], typedonnee, liste_err)
250 if texte!="":
251 liste_extracalculs.append(texte)
252 print(liste_extracalculs)
253 # Ajouter le trait ?
254 if conf["non_sauve"]["calculextratemps_trace"]:
255 dessine_guides(r, conf["non_sauve"]["calculextratemps_val"], conf["couleurs"]["cadretxt"], unite, ax, liste_err)
256
257 except:
258 warning("Des problèmes pour extrapoler...", liste_err)
259
260 else:
261 debug("On ne trace pas de courbe enfant", liste_err)
262
263 ###################" Gestion de l'échelle #####################
264 debug("Courbes tracées. Il n'y a plus qu'à gérer l'échelle", liste_err)
265 ### échelle à régler
266
267 # On extrait la valeur min et la valeur max des poids des courbes OMS et des données
268 (colonne_min,_,_) = liste_data_labels[-1]
269 (colonne_max,_,_) = liste_data_labels[0]
270
271 # poids max OMS
272 poids_min = min(oms.extraire_colonne(t,colonne_min,jour_maxi))
273 poids_max = max(oms.extraire_colonne(t,colonne_max,jour_maxi))
274 if l_jours != []:
275 poids_min = min(min(l_poids),poids_min)
276 # Pour le poids max, voir la dernière valeur du tableau
277 i = 0
278 while i<len(l_jours) and l_jours[i]<jour_maxi:
279 i=i+1
280 poids_max = max(max(l_poids[0:i+1]),poids_max)
281 # On ajuste un peu ces min et max
282 # min : valeur min -1kg
283 poids_min = max(0,poids_min-1)
284 #max : +5%
285 poids_max = poids_max * 1.05
286
287
288 # Grille custom ?
289 if conf["non_sauve"]["grilleamelio"] == "oui":
290 debug("On a choisi la grille plus jolie", liste_err)
291 pas=u.choix_echelle_data(typedonnee, poids_max)
292 # data_min_arrondie
293 minechelle = int(poids_min/pas[0])*pas[0]
294
295 debug("pas choisis pour l'échelle en y : "+str(pas), liste_err)
296 echellemajeure = arange(minechelle, poids_max, pas[0])
297
298 if pas[1] >0:
299 echellemineure = arange(minechelle, poids_max, pas[1])
300 else:
301 echellemineure = []
302
303 ax.set_yticks(echellemajeure, minor=False)
304 ax.set_yticks(echellemineure, minor=True)
305
306 # échelle en temps
307 pas=u.choix_echelle_temps(unite, age_maxi)
308 debug("pas choisis pour l'échelle en x : "+str(pas), liste_err)
309
310 echellemajeure = arange(0,age_maxi, pas[0])
311 if pas[1] >0:
312 echellemineure = arange(0,age_maxi, pas[1])
313 else:
314 echellemineure = []
315 ax.set_xticks(echellemajeure, minor=False)
316 ax.set_xticks(echellemineure, minor=True)
317
318 ################################# Aspect du graphique
319
320 debug("On commende la déco du graphique", liste_err)
321
322 # La grille
323 ax.grid(conf["grille"]=="oui")
324 ax.grid(conf["grille"] == "oui", which="minor", linestyle="--")
325
326
327 plt.xlabel("Âge en "+unite,color=conf["couleurs"]["cadretxt"])
328 plt.ylabel(typedonnee.capitalize()+" en "+CONFIG["unites_typedonnees"][typedonnee],color=conf["couleurs"]["cadretxt"])
329 plt.title(titre,color=conf["couleurs"]["cadretxt"])
330 if l_jours_conv == []:
331 plt.axis([0,age_maxi, poids_min, poids_max])
332 else:
333 plt.axis([min(0,l_jours_conv[0]),age_maxi,poids_min,poids_max])
334
335
336 if conf['legende']=="oui":
337 legende = plt.legend(loc=conf['positionlegende'])
338 plt.setp(legende.get_texts(), color=conf["couleurs"]["cadretxt"])
339
340
341 fig.tight_layout()
342
343 debug("Fin de cree_figure, tout va bien.", liste_err)
344 return fig
345
346
347
348
349 ######################################## Pour extrapoler la courbe
350
351 def prolongecourbe(tableauOMS, dates, donnees, typecourbe, liste_err):
352 """ tableauOMS est le ableau des données OMS. dates et donnees sont les dates (jours)
353 et les données d'où on extrapole. On calcule toutes les dates comme des sauvages.
354 On renvoie la liste des jours totale et la liste des data_totales
355 (tableaux de jours)
356 typecourbe est P ou Z. Pour P il faut commencer à regarder à l'indice 4, pour Z
357 à l'indice 1
358 On renvoie [],[] si pas pu extrapoler. """
359 # les lignes OMS correspondant aux dates données
360 lignesoms = [tableauOMS[int(date)] for date in dates]
361 debug("prolongecourbe : Lignes OMS :"+str(lignesoms)+" valeur de données : "+str(donnees), liste_err)
362
363
364
365 # Principe : on cherche dans quel intervalle de "colonnes" on se situe.
366 # On va donc regarder pour chaque donnée entre quels i on se situe,et après
367 # prendre le plus grand intervalle.
368 # Numéros de colonnes d'où on part. Pour la fin c'est forcément longueur-1
369 if typecourbe == "P":
370 idep=4
371 else:
372 idep = 1
373
374 liste_indices = []
375 for k in range(len(dates)):
376 i= idep
377 ligne = lignesoms[k]
378 while i<len(ligne) and ligne[i]<donnees[k]:
379 i+=1
380 debug("prolongecourbe : on a trouvé la valeur de i : "+str(i),liste_err)
381 if i>=len(ligne):
382 warning("prolongation de courbe : pas réussi... donnée trop haute !", liste_err)
383 return [],[]
384 if i==idep:
385 warning("prolongation de courbe : pas réussi... donnée trop basse !", liste_err)
386 return [],[]
387 liste_indices.append(i)
388 imin=min(liste_indices) -1
389 imax=max(liste_indices)
390 debug("Les données se situent dans les indices : "+str(imin)+", "+str(imax),liste_err)
391 # Maintenant on doit trouver les coeffs : on se situe en coeff * l[imin]+ (1-coeff)*ligne[imax]
392 # Et faire la moyenne de ces coeffs
393 total = 0
394 for k in range(len(dates)):
395 ligne = lignesoms[k]
396 donnee = donnees[k]
397 total += (donnee - ligne[imax])/(ligne[imin] - ligne[imax])
398 #print(k)
399 coeff_moyen = total/len(dates)
400
401 debug("Coeff moyen calculé : "+str(coeff_moyen), liste_err)
402
403 # On utilisera la même chose pour les nouvelle donnee
404
405 # extrapolations
406 nouvdates =oms.extraire_colonne(tableauOMS,0) # On sort tout.
407 #print(nouvdates)
408 nouvdonnees = []
409 for j in nouvdates:
410 ligne2 = tableauOMS[int(j)]
411 nouvdonnees.append(coeff_moyen*ligne2[imin]+ (1-coeff_moyen)*ligne2[imax])
412
413 return nouvdates,nouvdonnees
414
415
416 def dessine_guides(t, data, couleur, unite, ax, liste_err):
417 """ dessine deux lignes, horizontales et verticales qui vont "vers" la courbe
418 jusqu'aux points (t, data). En pointillés et avec un point dessus."""
419 debug("Début de dessine_guides"+str(t)+", "+str(data), liste_err)
420 t_conv = u.convertitunite(t,unite,liste_err)
421 ax.vlines(t_conv, 0, data, colors=couleur, linestyles="dashed")
422 ax.hlines(data, 0, t_conv, color=couleur, linestyles="dashed")
423 ax.plot([t_conv], [data], color=couleur, marker="*", ms=13)