Extrapolation améliorée + possibilité de voir le point voulu
authorDenise sur Lya <sekhmet@lya>
Sat, 17 Apr 2021 11:47:35 +0000 (13:47 +0200)
committerDenise sur Lya <sekhmet@lya>
Sat, 17 Apr 2021 11:47:35 +0000 (13:47 +0200)
calculs_extrapole.py
data/changelog_data.txt
gestion_donnees.py
templates/index.html
trace_courbe.py

index ab22502e855d7e74a5b15b943a4445a92a8dbe85..509402be0f4d226452d7ac562f8bd26744036b66 100644 (file)
@@ -10,8 +10,95 @@ from gestion_erreurs import warning, debug
 from configuration import CONFIG
 from gestion_donnees import convertit_age_vers_texte
 import gestion_unites as u
+from math import ceil
 
-#### Les fonctions qui permettent de faire les calculs additionnels une fois qu'on a l'extrapolation
+#### Les fonctions qui permettent de faire les calculs additionnels une fois qu'on a l'extrapolation (ou pas)
+
+def interpole_lineaire(dates, donnees, t, liste_err):
+    """ simple interpolation linéaire.
+    dates et donnees sont les dates et données de l'enfant.
+    t est le temps (jours) auquel on veut l'interpolation. On renvoie la donnée interpolée.
+    On interpole linéairement entre les deux données. Si ça
+    n'est pas possible parce que t n'est pas dans les bornes, on renvoie -1
+    """
+    
+    if t<dates[0] or t>dates[-1]:
+        return -1 # en dehors des bornes
+    
+    i=0 # on cherche l'intervalle : entre dates[i] et dates[i+1] on a la donnée t
+    while i<len(dates)-1 and dates[i+1]<t:
+        i+=1
+    
+    m = (donnees[i+1] - donnees[i])/(dates[i+1] - dates[i]) # pente de la courbe
+    valeur = m * (t - dates[i]) + donnees[i]
+    return valeur
+
+
+def interpole_lineaire_ordonnee(dates, donnees, val, liste_err):
+    """ interpolation linéaire des données.
+    dates et donnees sont les dates et données de l'enfant.
+    val est la valeur pour laquelle on veut trouver le temps.
+    On interpole linéairement entre les deux données. Si ça
+    n'est pas possible parce que t n'est pas dans les bornes, on renvoie -1
+    
+    Comportement potentiellement foireux si jamais les données ne sont pas
+    croissantes (poids qui chute, etc)..."""
+    
+    if val>max(donnees) or val<min(donnees):
+        return -1 # en dehors des maxi et mini
+    
+    i = 0 # On cherche dans quel intervalles de données on se situe (donnnes[i]
+    # et donnees [i+1])
+    while i<len(donnees)-1 and donnees[i+1]<val:
+        i+=1   
+        
+    m = (dates[i+1] - dates[i])/(donnees[i+1] - donnees[i])
+    temps = m * (val - donnees[i]) + dates[i]
+    
+    return ceil(temps)
+
+
+######################################
+
+
+            
+
+
+def calcule_donnee_extrapolee(dates_extrapole, donnees_extrapole, age_voulu, liste_err):
+    """ prend en argument les données extrapolées, l'âge voulu en jours.
+    Renvoie la donnée, ou -1 si ça n'a pas marché"""
+    age_voulu = int(age_voulu) # pour avoir les choses bien
+    debug("On veut la donnée à l'âge "+str(age_voulu), liste_err)
+    try:      
+        i_date_voulue = dates_extrapole.index(age_voulu)
+        donnee_voulue = donnees_extrapole[i_date_voulue]
+        
+        return donnee_voulue
+        #return formate_resultat_donnee(age_voulu, donnee_voulue, typedonnee, "", liste_err)
+    except:
+        warning("Impossible de calculer la donnée à l'âge "+age_voulu, liste_err)
+        return -1
+    
+    
+def calcule_age_extrapole(dates_extrapole, donnees_extrapole, donnee_voulue,  liste_err):
+    """ prend en argument les données extrapolées, la donnée voulue (dans l'unité adaptée)
+    Renvoie la donnée, ou -1 si ça n'a pas marché"""
+    
+    debug("On veut savoir à quel âge on a "+str(donnee_voulue), liste_err)   
+    try:
+        i=0
+        while i<len(donnees_extrapole) and donnees_extrapole[i]<donnee_voulue:
+            i+=1      
+
+        return dates_extrapole[i]
+        #return formate_resultat_age(dates_extrapole[i], donnee_voulue, typedonnee, "", liste_err)
+    except:
+        warning("Impossible de calculer l'âge pour la donnée "+str(donnee_voulue), liste_err)
+        return -1
+
+######################################
+### Formatage du résultat "joli"
 
 def met_s(chaine):
     """ renvoie un s si pluriel, rien sinon"""
@@ -27,7 +114,7 @@ def joliechaine_age(age):
     nombre = ""
     for char in age:
         if char=="a":
-            retour+=nombre+" année"+met_s(nombre)+", "
+            retour+=nombre+" an"+met_s(nombre)+", "
             nombre=""
         elif char=="m":
             retour+=nombre+" mois, "
@@ -42,48 +129,62 @@ def joliechaine_age(age):
             nombre+=char
     #print(retour)
     return retour[:-2]
-            
 
+def formate_resultat_donnee(age, donnee, typedonnee, extra, liste_err):
+    """ Formate le tout en une zolie phrase
+    age et donnee sont les données,
+    typedonnee est le type de donnée (poids, etc)
+    extra est un truc additionnel à mettre entre parenthèses"""
 
-def calcule_donnee_extrapolee(dates_extrapole, donnees_extrapole, age_voulu, typedonnee, liste_err):
-    """ prend en argument les données extrapolées, l'âge voulu en jours,
-    et le type de données qu'on veut afficher, et renvoie une jolie phrase
-    à afficher"""
-    age_voulu = int(age_voulu) # pour avoir les choses bien
-    debug("On veut la donnée à l'âge "+str(age_voulu), liste_err)
-    try:      
-        i_date_voulue = dates_extrapole.index(age_voulu)
-        donnee_voulue = donnees_extrapole[i_date_voulue]
-        donnee_arrondie = u.arrondit_donnee(donnee_voulue, typedonnee)
-        chaine = "À "+joliechaine_age(convertit_age_vers_texte(age_voulu))
-
-        if typedonnee == "poids":
-            return chaine+", l'enfant pèsera "+str(donnee_arrondie)+" kg."
-        elif typedonnee == "taille":
-            return chaine+", l'enfant mesurera "+str(donnee_arrondie)+" cm."
-        else: # phrase générique
-            return chaine+", la donnée sera : "+str(donnee_arrondie)+" "+CONFIG["unites_typedonnees"][typedonnee]+"."
-    except:
-        warning("Impossible de calculer la donnée à l'âge "+age_voulu, liste_err)
-        return ""
-    
-def calcule_age_extrapole(dates_extrapole, donnees_extrapole, donnee_voulue, typedonnee, liste_err):
-    """ prend en argument les données extrapolées, la donnée voulue (dans l'unité adaptée)
-    et son type, et renvoie la jolie phrase à afficher"""
+    donnee_arrondie = u.arrondit_donnee(donnee, typedonnee)
+    chaine = "À "+joliechaine_age(convertit_age_vers_texte(age))
+
+    if typedonnee == "poids":
+        chaine+= ", l'enfant pèsera "+str(donnee_arrondie)+" kg"
+    elif typedonnee == "taille":
+        chaine+= ", l'enfant mesurera "+str(donnee_arrondie)+" cm"
+    else: # phrase générique
+        chaine+= ", la donnée sera : "+str(donnee_arrondie)+" "+CONFIG["unites_typedonnees"][typedonnee]
+
+    if extra!="":
+        ajout=" ("+extra+")"
+    else:
+        ajout=""
+
+    chaine+=ajout+"."
+    return chaine
+
+def formate_resultat_age(age, donnee, typedonnee, extra, liste_err):
+    """ formate les données en une zolie phrase
+    age et donnee sont les données
+    typedonnee est le type de donnée (poids, etc)
+    extra est un truc additionnel à mettre entre parenthèses"""
+    age_joli = joliechaine_age(convertit_age_vers_texte(age))
+    if typedonnee=="poids":
+        chaine= "L'enfant atteindra un poids de "+str(donnee)+" kg à l'âge de "+age_joli
+    elif typedonnee=="taille":
+        chaine= "L'enfant atteindra la taille "+str(donnee)+ "cm à l'âge de "+age_joli
+    else:# phrase générique
+        chaine= "L'enfant atteindra la donnée "+typedonnee+" "+str(donnee)+" à l'âge de "+age_joli
     
-    debug("On veut savoir à quel âge on a "+str(donnee_voulue), liste_err)   
-    try:
-        i=0
-        while i<len(donnees_extrapole) and donnees_extrapole[i]<donnee_voulue:
-            i+=1
-        
-        age_joli = joliechaine_age(convertit_age_vers_texte(dates_extrapole[i]))
-        if typedonnee=="poids":
-            return "L'enfant atteindra un poids de "+str(donnee_voulue)+" kg à l'âge de "+age_joli+"."
-        elif typedonnee=="taille":
-            return "L'enfant atteindra la taille "+str(donnee_voulue)+ "cm à l'âge de "+age_joli+"."
-        else:# phrase générique
-            return "L'enfant atteindra la donnée "+typedonnee+" "+str(donnee_voulue)+" à l'âge de "+age_joli+"."
-    except:
-        warning("Impossible de calculer l'âge pour la donnée "+str(donnee_voulue), liste_err)
-        return ""
+    if extra!="":
+        ajout=" ("+extra+")"
+    else:
+        ajout=""
+
+    chaine+=ajout+"."
+    return chaine
+
+def formate_extrapole(nb_extra):
+    """ Renvoie une chaîne qui dit sur cb on a extrapolé"""
+    message="extrapolation sur "
+    if nb_extra== 0:
+        message+="l'ensemble des données"
+    elif nb_extra==1:
+        message+="la donnée la plus récente"
+    else:
+        message+="les "+str(nb_extra)+" dernières données"
+    return message
+
+def formate_interpole():
+    return "interpolation sur les données existantes"
\ No newline at end of file
index 9d48c19e5dbaf1b2508f635d11f8723b75e2bfb2..6ba20dda4e25180227be3f65d7572640ddb66fd1 100644 (file)
@@ -1,3 +1,11 @@
+"Version 2.31","17/04/2021","<p>Bêta : Les calculs de données sont améliorés :
+<ul>
+<li>Si l'âge demandé (ou la donnée) est entre les données saisies, c'est une simple interpolation linéaires entre les données existantes,</li>
+<li>Si c'est en dehors, alors on fait un calcul d'extrapolation comme avant</li>
+</ul>
+En plus, on peut décider de marquer sur le graphique, ou pas, le point qu'on cherche.
+</p>"
+
 "Version 2.3001","13/03/2021","<p>Petit changement : on peut maintenant mettre le poids en grammes. Si le poids saisi est supérieur à 500 il sera automatiquement converti.</p>"
 
 "Version 2.3","26/02/2021","<p>Nouveauté : extrapolation de la courbe améliorée, ainsi que la possibilité de faire des ""calculs"" sur la courbe. Il y a aussi deux-trois petits changements cosmétiques.</p>"
index 25830070a0650b5e01f13db6160311a79dcc35e4..2153a55f814cf0cb6660d71ca4842e475b3a5544 100644 (file)
@@ -333,6 +333,11 @@ def gere_configuration(data,liste_err):
     else:
         configuration["non_sauve"]["calculextratemps_type"] = ""
     
+    # Tracer les calculs sur la grille
+    configuration["non_sauve"]["calculextradata_trace"] = gere_checkbox(data.get("calculextradata_trace"))
+    configuration["non_sauve"]["calculextratemps_trace"] = gere_checkbox(data.get("calculextratemps_trace"))
+
+    
     return configuration   
  
     
index 64fb9e153236a9bc8778bf397b6e1355231d2a1e..32c45e1e1fc9e12ff4a3ea5d0936089836b65322 100644 (file)
 <ul id="extra">
        <li>Calculer le prolongement de la courbe à partir des <input type="text" name="nbextradata" size="4" value="1"> dernières données (mettre 0 pour "toutes").</li>
        <li><input type="checkbox" name="prolongercourbes">Tracer l'extrapolation sur le graphique.</li>
+       
        <li>Calculer <select name="calculextradata_type">
        <option value="">Choisir la donnée</option>
        {% for val in CONFIG.liste_typedonnees %}
        <option value="{{val}}">{{val}}</option>
        {% endfor %}
-       </select> à l'âge <input type="text" name="calculextradata_age" value="6m"></li>
+       </select> à l'âge <input type="text" name="calculextradata_age" value="6m">
+       <input type="checkbox" name="calculextradata_trace"> ... et le voir sur le graphique.</li>
+       
        <li>Calculer l'âge auquel l'enfant aura <input type="text" name="calculextratemps_val">
        <select name="calculextratemps_type">
        <option value="">Choisir la donnée</option>
        {% for val in CONFIG.liste_typedonnees %}
        <option value="{{val}}">{{ CONFIG.unites_typedonnees[val]}} ({{val}})</option>
        {% endfor %}    
-       </select></li>
+       </select>
+       <input type="checkbox" name="calculextratemps_trace"> ... et le voir sur le graphique.</li>
        
 </ul>
 
index e72560782e47f9268694400bcab791cadcd0c40e..28ee5b4c29b99ca523e900223f9dff7d0448a653 100644 (file)
@@ -5,8 +5,10 @@ import gestionOMS as oms
 import gestion_unites as u
 from gestion_donnees import calcule_max_graphique, convertit_jours_vers_python, convertit_age_vers_texte
 from gestion_erreurs import debug, erreur, warning
+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
+
 from numpy import arange
-from calculs_extrapole import calcule_donnee_extrapolee, calcule_age_extrapole
+
 
 import matplotlib.pyplot as plt
 
@@ -167,6 +169,7 @@ def cree_figure(conf,l_jours,l_poids,typedonnee,liste_extracalculs, liste_err):
             jextrapole = jextrapole or conf["non_sauve"][calextra+"_type"] == typedonnee
         
         #print(jextrapole)
+        ############################## Là où on extrapole ################################
         if jextrapole:
             try:
                 debug("Il faut extrapoler les courbes !", liste_err)
@@ -181,6 +184,7 @@ def cree_figure(conf,l_jours,l_poids,typedonnee,liste_extracalculs, liste_err):
                 
                 debug("On extrapole sur les jours : "+str(sources_extrap), liste_err)
                
+                # On récupère toutes les données extrapolées
                 dates_extrapole, donnees_extrapole =  prolongecourbe(t, sources_extrap, sources_extrap_data, conf["typecourbe"], liste_err)
                 debug("données extrapolées !", liste_err)
                 #debug(str(dates_extrapole[0:10])+str(donnees_extrapole[0:10]), liste_err)            
@@ -201,19 +205,50 @@ def cree_figure(conf,l_jours,l_poids,typedonnee,liste_extracalculs, liste_err):
                     plt.plot(dates_extrapole_trace, donnees_extrapole_trace,color=conf["couleurs"]["cadretxt"], linestyle=(0, (5,7)), marker=None)
                     debug("Tracé de la courbe extrapolée ok", liste_err)
                 
-                # Calculer une donnée à l'âge x
+                ### Calculer une donnée à l'âge x
                 if conf["non_sauve"]["calculextradata_type"] == typedonnee:
-                    r = calcule_donnee_extrapolee(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextradata_age"], typedonnee, liste_err)
-                    if r!="":
-                        liste_extracalculs.append(r)
+                    # On essaie l'interpolation
+                    r = interpole_lineaire(l_jours,l_poids,conf["non_sauve"]["calculextradata_age"], liste_err)
+                    if r==-1:
+                        # ça sera donc une extrapolation
+                        r = calcule_donnee_extrapolee(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextradata_age"], liste_err)          
+                        message=formate_extrapole(conf["non_sauve"]["nbextradata"])
+#                        if  == 0:
+#                            message+="l'ensemble des données"
+#                        else:
+#                            message+="les "+str(conf["non_sauve"]["nbextradata"])+" dernière"+met_s(conf["non_sauve"]["nbextradata"])+" données"
+                    else:
+                        message=formate_interpole()
+                    
+                    texte = formate_resultat_donnee(conf["non_sauve"]["calculextradata_age"], r, typedonnee, message, liste_err)
+                    debug("calcul de la donnée extrapolée : "+texte, liste_err)
+                    if texte!="":
+                        liste_extracalculs.append(texte)
                         print(liste_extracalculs)
+                        # Ajouter le trait ?
+                        if conf["non_sauve"]["calculextradata_trace"] == "oui":
+                            dessine_guides(conf["non_sauve"]["calculextradata_age"], r, conf["couleurs"]["cadretxt"], unite, ax, liste_err)
                         
-                # Calculer un âge où on atteint cette donnée
+                ### Calculer un âge où on atteint cette donnée
                 if conf["non_sauve"]["calculextratemps_type"] == typedonnee:
-                    r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], typedonnee, liste_err)
-                    if r!="":
-                        liste_extracalculs.append(r)
+                    # interpolation
+                    r = interpole_lineaire_ordonnee(l_jours,l_poids,conf["non_sauve"]["calculextratemps_val"], liste_err)
+                    if r==-1:
+                        # ça sera donc une extrapolation
+                        r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], liste_err)          
+                        message=formate_extrapole(conf["non_sauve"]["nbextradata"])
+                    else:
+                        message=formate_interpole()
+                    
+                    texte = formate_resultat_age(r, conf["non_sauve"]["calculextratemps_val"], typedonnee, message, liste_err)
+                    
+                    #r = calcule_age_extrapole(dates_extrapole, donnees_extrapole, conf["non_sauve"]["calculextratemps_val"], typedonnee, liste_err)
+                    if texte!="":
+                        liste_extracalculs.append(texte)
                         print(liste_extracalculs)
+                        # Ajouter le trait ?
+                        if conf["non_sauve"]["calculextratemps_trace"]:
+                            dessine_guides(r, conf["non_sauve"]["calculextratemps_val"], conf["couleurs"]["cadretxt"], unite, ax, liste_err)
                     
             except: 
                 warning("Des problèmes pour extrapoler...", liste_err)
@@ -371,4 +406,14 @@ def prolongecourbe(tableauOMS, dates, donnees, typecourbe, liste_err):
         ligne2 = tableauOMS[int(j)]
         nouvdonnees.append(coeff_moyen*ligne2[imin]+ (1-coeff_moyen)*ligne2[imax])
 
-    return nouvdates,nouvdonnees
\ No newline at end of file
+    return nouvdates,nouvdonnees
+
+
+def dessine_guides(t, data, couleur, unite, ax, liste_err):
+    """ dessine deux lignes, horizontales et verticales qui vont "vers" la courbe
+    jusqu'aux points (t, data). En pointillés et avec un point dessus."""
+    debug("Début de dessine_guides"+str(t)+", "+str(data), liste_err)
+    t_conv = u.convertitunite(t,unite,liste_err)
+    ax.vlines(t_conv, 0, data, colors=couleur, linestyles="dashed")
+    ax.hlines(data, 0, t_conv, color=couleur, linestyles="dashed")
+    ax.plot([t_conv], [data], color=couleur, marker="*", ms=13)