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"""
29 debug("debut de cree_figure. Config : "+str(conf
)+". Nombre d'enfants additionnels : "+str(len(enfants_add
)),liste_err
)
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
)
33 erreur("bug avec liste data labels",liste_err
)
36 ######################## Gestion des bornes #############################
37 # y a-t-il un maxi saisi par l'utilisateur ?
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
)
43 jour_maxi
= conf
["non_sauve"]["maxi"]+1
45 jour_maxi
= conf
["maxi"]+1
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
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
)
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"] != "":
63 unite
= conf
["non_sauve"]["unite"]
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
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
)
82 debug("cree_figure : conversion des unités ok : "+str(l_jours_conv
),liste_err
)
84 #####################" Courbes OMS et titre ######################################"
85 titre
= "Courbe de "+typedonnee
+" OMS"
86 if conf
["typecourbe"] == "P":
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)"
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)"
107 fichier_oms
= CONFIG
["fichiersOMS"][typedonnee
]["z_mixte"]
108 titre
+= " (moyenne et écarts-types)"
110 erreur("Type de courbe invalide"+conf
["typecourbe"],liste_err
)
113 ## On finira le titre plus tard quand on aura su qui est concerné
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
)
119 #### On extrait les données des courbes, et on convertit les jours dans l'unité voulues
121 t
= oms
.lire_fichier_csv(fichier_oms
)
123 erreur("cree_figure : Impossible d'ouvrir le fichier "+fichier_oms
, liste_err
)
126 debug("cree_figure : Conversion des données OMS à la bonne unité",liste_err
)
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
)
133 erreur("Problème à la conversion du tableau OMS. jour_maxi = "+str(jour_maxi
)+" unite = "+unite
,liste_err
)
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
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"]
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
)
154 debug("cree_figure : tracé des courbes OMS ok",liste_err
)
156 ### Tracé pour de bon
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
)
164 if conf
["nom"] != "": # Ajouter le nom de l'enfant
165 listenoms
.append(conf
["nom"])
167 # if enfants_add != []:
168 # debug("Il y a des enfants en plus à tracer. Données : "+str(enfants_add), liste_err)
169 for enfant
in enfants_add
: # Enfants additionnels éventuels
170 conf_add
, lj
, ld
= enfant
[typedonnee
] # On récuère les données
171 if lj
!= []: # pas la peine de tracer si y'a rien à tracer
172 # Ajouter le nom de cet enfant-là
173 listenoms
.append(conf_add
["nom"])
174 lj_conv
= u
.convertit_tableau(lj
,unite
,liste_err
)
175 debug("Tracé de la courbe additionnelle de "+conf_add
["nom"]+" config : "+str(conf_add
), liste_err
)
176 ax
.plot(lj_conv
, ld
, label
=conf_add
["nom"], color
=conf_add
["couleurcourbe"],marker
=conf_add
["symbole"])
178 if conf
["sexe"] != conf_add
["sexe"]:
179 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
)
182 # Si y'a un nom on met "courbe de machin"
184 titre
+= " de " +", ".join(listenoms
)
187 titre
+= ", préma de "+conf
["prematurite"]
188 if conf
["agecorrige"] == "oui":
189 titre
+=" (courbe en âge corrigé)"
191 titre
+=" (courbe en âge réel, données OMS décalées)"
194 #### extrapolatios éventuelles
195 # a-t-on demndé des calculs ?
196 jextrapole
= conf
["non_sauve"]["prolongercourbes"] == "oui"
197 # Est-ce qu'on a demandé un calcul sur cette donnée ?
199 for calextra
in CONFIG
["extradata"]:
200 jextrapole
= jextrapole
or conf
["non_sauve"][calextra
+"_type"] == typedonnee
203 ############################## Là où on extrapole ################################
206 debug("Il faut extrapoler les courbes !", liste_err
)
207 # Prendre l'ensemble des dates "source"
208 # print(conf["non_sauve"]["nbdataextra"])
209 if conf
["non_sauve"]["nbextradata"] == 0:
210 sources_extrap
= l_jours
211 sources_extrap_data
= l_poids
213 sources_extrap
= l_jours
[-conf
["non_sauve"]["nbextradata"]:] # les derniers jours
214 sources_extrap_data
= l_poids
[-conf
["non_sauve"]["nbextradata"]:]
216 debug("On extrapole sur les jours : "+str(sources_extrap
), liste_err
)
218 # On récupère toutes les données extrapolées
219 dates_extrapole
, donnees_extrapole
= prolongecourbe(t
, sources_extrap
, sources_extrap_data
, conf
["typecourbe"], liste_err
)
220 debug("données extrapolées !", liste_err
)
221 #debug(str(dates_extrapole[0:10])+str(donnees_extrapole[0:10]), liste_err)
223 # QUe veut-on maintenant sur ces données extrapolées ?
225 if conf
["non_sauve"]["prolongercourbes"] == "oui":
226 # On va prendre les extrapolations de la dernière donnée jusqu'à l fin du graphe
227 debut_extr
= int(l_jours
[-conf
["non_sauve"]["nbextradata"]])
228 i_debut_extr
= dates_extrapole
.index(debut_extr
)
229 i_fin_extr
= dates_extrapole
.index(jour_maxi
)
230 # Voilà ce qu'on veut tracer
231 dates_extrapole_trace
= dates_extrapole
[i_debut_extr
:i_fin_extr
+1]
232 donnees_extrapole_trace
= donnees_extrapole
[i_debut_extr
:i_fin_extr
+1]
233 dates_extrapole_trace
= u
.convertit_tableau(dates_extrapole_trace
,unite
,liste_err
)
235 # tracé des données extrapolées
236 plt
.plot(dates_extrapole_trace
, donnees_extrapole_trace
,color
=conf
["couleurs"]["cadretxt"], linestyle
=(0, (5,7)), marker
=None)
237 debug("Tracé de la courbe extrapolée ok", liste_err
)
239 ### Calculer une donnée à l'âge x
240 if conf
["non_sauve"]["calculextradata_type"] == typedonnee
:
241 # On essaie l'interpolation
242 r
= interpole_lineaire(l_jours
,l_poids
,conf
["non_sauve"]["calculextradata_age"], liste_err
)
244 # ça sera donc une extrapolation
245 r
= calcule_donnee_extrapolee(dates_extrapole
, donnees_extrapole
, conf
["non_sauve"]["calculextradata_age"], liste_err
)
246 message
=formate_extrapole(conf
["non_sauve"]["nbextradata"])
248 # message+="l'ensemble des données"
250 # message+="les "+str(conf["non_sauve"]["nbextradata"])+" dernière"+met_s(conf["non_sauve"]["nbextradata"])+" données"
252 message
=formate_interpole()
254 texte
= formate_resultat_donnee(conf
["non_sauve"]["calculextradata_age"], r
, typedonnee
, message
, liste_err
)
255 debug("calcul de la donnée extrapolée : "+texte
, liste_err
)
257 liste_extracalculs
.append(texte
)
258 print(liste_extracalculs
)
260 if conf
["non_sauve"]["calculextradata_trace"] == "oui":
261 dessine_guides(conf
["non_sauve"]["calculextradata_age"], r
, conf
["couleurs"]["cadretxt"], unite
, ax
, liste_err
)
263 ### Calculer un âge où on atteint cette donnée
264 if conf
["non_sauve"]["calculextratemps_type"] == typedonnee
:
266 r
= interpole_lineaire_ordonnee(l_jours
,l_poids
,conf
["non_sauve"]["calculextratemps_val"], liste_err
)
268 # ça sera donc une extrapolation
269 r
= calcule_age_extrapole(dates_extrapole
, donnees_extrapole
, conf
["non_sauve"]["calculextratemps_val"], liste_err
)
270 message
=formate_extrapole(conf
["non_sauve"]["nbextradata"])
272 message
=formate_interpole()
274 texte
= formate_resultat_age(r
, conf
["non_sauve"]["calculextratemps_val"], typedonnee
, message
, liste_err
)
276 #r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], typedonnee, liste_err)
278 liste_extracalculs
.append(texte
)
279 print(liste_extracalculs
)
281 if conf
["non_sauve"]["calculextratemps_trace"]:
282 dessine_guides(r
, conf
["non_sauve"]["calculextratemps_val"], conf
["couleurs"]["cadretxt"], unite
, ax
, liste_err
)
285 warning("Des problèmes pour extrapoler...", liste_err
)
288 debug("On ne trace pas de courbe enfant", liste_err
)
290 ###################" Gestion de l'échelle #####################
291 debug("Courbes tracées. Il n'y a plus qu'à gérer l'échelle", liste_err
)
294 # On extrait la valeur min et la valeur max des poids des courbes OMS et des données
295 (colonne_min
,_
,_
) = liste_data_labels
[-1]
296 (colonne_max
,_
,_
) = liste_data_labels
[0]
299 poids_min
= min(oms
.extraire_colonne(t
,colonne_min
,jour_maxi
))
300 poids_max
= max(oms
.extraire_colonne(t
,colonne_max
,jour_maxi
))
302 poids_min
= min(min(l_poids
),poids_min
)
303 # Pour le poids max, voir la dernière valeur du tableau
305 while i
<len(l_jours
) and l_jours
[i
]<jour_maxi
:
307 poids_max
= max(max(l_poids
[0:i
+1]),poids_max
)
308 # On ajuste un peu ces min et max
309 # min : valeur min -1kg
310 poids_min
= max(0,poids_min
-1)
312 poids_max
= poids_max
* 1.05
316 if conf
["non_sauve"]["grilleamelio"] == "oui":
317 debug("On a choisi la grille plus jolie", liste_err
)
318 pas
=u
.choix_echelle_data(typedonnee
, poids_max
)
320 minechelle
= int(poids_min
/pas
[0])*pas
[0]
322 debug("pas choisis pour l'échelle en y : "+str(pas
), liste_err
)
323 echellemajeure
= arange(minechelle
, poids_max
, pas
[0])
326 echellemineure
= arange(minechelle
, poids_max
, pas
[1])
330 ax
.set_yticks(echellemajeure
, minor
=False)
331 ax
.set_yticks(echellemineure
, minor
=True)
334 pas
=u
.choix_echelle_temps(unite
, age_maxi
)
335 debug("pas choisis pour l'échelle en x : "+str(pas
), liste_err
)
337 echellemajeure
= arange(0,age_maxi
, pas
[0])
339 echellemineure
= arange(0,age_maxi
, pas
[1])
342 ax
.set_xticks(echellemajeure
, minor
=False)
343 ax
.set_xticks(echellemineure
, minor
=True)
345 ################################# Aspect du graphique
347 debug("On commende la déco du graphique", liste_err
)
350 ax
.grid(conf
["grille"]=="oui")
351 ax
.grid(conf
["grille"] == "oui", which
="minor", linestyle
="--")
354 plt
.xlabel("Âge en "+unite
,color
=conf
["couleurs"]["cadretxt"])
355 plt
.ylabel(typedonnee
.capitalize()+" en "+CONFIG
["unites_typedonnees"][typedonnee
],color
=conf
["couleurs"]["cadretxt"])
356 plt
.title(titre
,color
=conf
["couleurs"]["cadretxt"])
357 if l_jours_conv
== []:
358 plt
.axis([0,age_maxi
, poids_min
, poids_max
])
360 plt
.axis([min(0,l_jours_conv
[0]),age_maxi
,poids_min
,poids_max
])
363 if conf
['legende']=="oui":
364 legende
= plt
.legend(loc
=conf
['positionlegende'])
365 plt
.setp(legende
.get_texts(), color
=conf
["couleurs"]["cadretxt"])
370 debug("Fin de cree_figure, tout va bien.", liste_err
)
376 ######################################## Pour extrapoler la courbe
378 def prolongecourbe(tableauOMS
, dates
, donnees
, typecourbe
, liste_err
):
379 """ tableauOMS est le ableau des données OMS. dates et donnees sont les dates (jours)
380 et les données d'où on extrapole. On calcule toutes les dates comme des sauvages.
381 On renvoie la liste des jours totale et la liste des data_totales
383 typecourbe est P ou Z. Pour P il faut commencer à regarder à l'indice 4, pour Z
385 On renvoie [],[] si pas pu extrapoler. """
386 # les lignes OMS correspondant aux dates données
387 lignesoms
= [tableauOMS
[int(date
)] for date
in dates
]
388 debug("prolongecourbe : Lignes OMS :"+str(lignesoms
)+" valeur de données : "+str(donnees
), liste_err
)
392 # Principe : on cherche dans quel intervalle de "colonnes" on se situe.
393 # On va donc regarder pour chaque donnée entre quels i on se situe,et après
394 # prendre le plus grand intervalle.
395 # Numéros de colonnes d'où on part. Pour la fin c'est forcément longueur-1
396 if typecourbe
== "P":
402 for k
in range(len(dates
)):
405 while i
<len(ligne
) and ligne
[i
]<donnees
[k
]:
407 debug("prolongecourbe : on a trouvé la valeur de i : "+str(i
),liste_err
)
409 warning("prolongation de courbe : pas réussi... donnée trop haute !", liste_err
)
412 warning("prolongation de courbe : pas réussi... donnée trop basse !", liste_err
)
414 liste_indices
.append(i
)
415 imin
=min(liste_indices
) -1
416 imax
=max(liste_indices
)
417 debug("Les données se situent dans les indices : "+str(imin
)+", "+str(imax
),liste_err
)
418 # Maintenant on doit trouver les coeffs : on se situe en coeff * l[imin]+ (1-coeff)*ligne[imax]
419 # Et faire la moyenne de ces coeffs
421 for k
in range(len(dates
)):
424 total
+= (donnee
- ligne
[imax
])/(ligne
[imin
] - ligne
[imax
])
426 coeff_moyen
= total
/len(dates
)
428 debug("Coeff moyen calculé : "+str(coeff_moyen
), liste_err
)
430 # On utilisera la même chose pour les nouvelle donnee
433 nouvdates
=oms
.extraire_colonne(tableauOMS
,0) # On sort tout.
437 ligne2
= tableauOMS
[int(j
)]
438 nouvdonnees
.append(coeff_moyen
*ligne2
[imin
]+ (1-coeff_moyen
)*ligne2
[imax
])
440 return nouvdates
,nouvdonnees
443 def dessine_guides(t
, data
, couleur
, unite
, ax
, liste_err
):
444 """ dessine deux lignes, horizontales et verticales qui vont "vers" la courbe
445 jusqu'aux points (t, data). En pointillés et avec un point dessus."""
446 debug("Début de dessine_guides"+str(t
)+", "+str(data
), liste_err
)
447 t_conv
= u
.convertitunite(t
,unite
,liste_err
)
448 ax
.vlines(t_conv
, 0, data
, colors
=couleur
, linestyles
="dashed")
449 ax
.hlines(data
, 0, t_conv
, color
=couleur
, linestyles
="dashed")
450 ax
.plot([t_conv
], [data
], color
=couleur
, marker
="*", ms
=13)