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