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