Commit 6b8c6afd authored by Lionel's avatar Lionel
Browse files

Vectorization du calcul du message de synthese

parent a4f9d7c8
......@@ -90,6 +90,38 @@ def message_synthese(
return message
def discretiser_indicateur(indicateur:Series, liste_seuils, liste_valeurs_classes, type_classes, valeur_classe_defaut):
valeurs_discretisees = pd.cut(
indicateur,
bins=[-np.inf, *liste_seuils, np.inf],
labels=liste_valeurs_classes,
right=False,include_lowest=True,
ordered=False
)
return valeurs_discretisees.astype(type_classes).fillna(valeur_classe_defaut)
def construire_message(introduction_globale, indicateurs1:Series, intro1: str, liste_seuils1, liste_messages1, valeurs_messages1,
indicateurs2: Series, intro2: str, liste_seuils2, liste_messages2, valeurs_messages2):
df = pd.DataFrame([indicateurs1, indicateurs2], index=['indicateur1', 'indicateur2']).transpose()
df['message1'] = intro1 + '<strong>' + discretiser_indicateur(df['indicateur1'], liste_seuils1, liste_messages1, pd.StringDtype(), "") + '</strong>'
df['valeur_message1'] = discretiser_indicateur(df['indicateur1'], liste_seuils1, valeurs_messages1, pd.Int64Dtype(), 0)
df['message2'] = intro2 + '<strong>' + discretiser_indicateur(df['indicateur2'], liste_seuils2, liste_messages2, pd.StringDtype(), "") + '</strong>'
df['valeur_message2'] = discretiser_indicateur(df['indicateur2'], liste_seuils2, valeurs_messages2, pd.Int64Dtype(), 0)
mask_messages_a_inverser = df['valeur_message1'].abs() < df['valeur_message2'].abs()
df.loc[mask_messages_a_inverser, ['message1', 'valeur_message1', 'message2', 'valeur_message2']] = (
df.loc[mask_messages_a_inverser, ['message2', 'valeur_message2', 'message1', 'valeur_message1']].values)
df['mot_liaison'] = ' et '
mask_messages_en_opposition = df['valeur_message1'] * df['valeur_message2'] < 0
df.loc[mask_messages_en_opposition, 'mot_liaison'] = ' mais '
df['message'] = introduction_globale + df['message1'] + df['mot_liaison'] + df['message2']
df.loc[df['indicateur1'].isnull() | df['indicateur2'].isnull(), 'message'] = 'Données non disponibles'
df['message'] = df['message'].str.capitalize()
return df['message']
#TODO fonction à tester
def interpolation(x, xa, ya, xb, yb):
......
......@@ -19,18 +19,25 @@ def ajouter_colonne_synthese_population_agricole(population_agricole: DataFrame)
# (pour l'instant Int64 et Float6' sont encore expérimentaux)
).round(0).clip(lower=0, upper=10).astype('Int64')
part_population_agricole_2010_valeur_pourcent_FRANCE= population_agricole.query(f"id_territoire=='{outils_territoires.ID_FRANCE}'")[
'part_population_agricole_2010_valeur_pourcent'][0]
population_agricole.loc[:, 'synthese_message'] = outils.construire_message(
'population agricole ',
population_agricole['part_population_agricole_2010_valeur_pourcent'],
'',
[0.8 * part_population_agricole_2010_valeur_pourcent_FRANCE,
1.2 * part_population_agricole_2010_valeur_pourcent_FRANCE],
["en proportion plus faible que la moyenne française",
"en proportion proche de la moyenne française",
"en proportion plus élevée que la moyenne française"],
[-2, 0, 2],
population_agricole['evolution_part_population_agricole_valeur_pt_de_pourcentage'],
"",
[-0.5, 0.5],
["en déclin", "stable", "en croissance"],
[-1, 0, 1]
population_agricole.loc[:, 'synthese_message'] = (
population_agricole.apply(
lambda x:
_message(
population_agricole.query(f"id_territoire=='{outils_territoires.ID_FRANCE}'")[
'part_population_agricole_2010_valeur_pourcent'][0],
x['part_population_agricole_2010_valeur_pourcent'],
x['evolution_part_population_agricole_valeur_pt_de_pourcentage']
),
axis=1
)
)
return population_agricole
......
id_territoire;nom_territoire;categorie_territoire;population_totale_2010;population_totale_1990;nb_exploitations_2010;actifs_agricoles_permanents_2010;nb_exploitations_1988;actifs_agricoles_permanents_1988;nb_exploitations_par_age_chef_exploitation.moins_40_ans;nb_exploitations_par_age_chef_exploitation.40_a_49_ans;nb_exploitations_par_age_chef_exploitation.50_a_59_ans;nb_exploitations_par_age_chef_exploitation.plus_60_ans;nb_exploitations_par_sau.moins_20_ha;nb_exploitations_par_sau.20_a_50_ha;nb_exploitations_par_sau.50_a_100_ha;nb_exploitations_par_sau.100_a_200_ha;nb_exploitations_par_sau.plus_200_ha;part_population_agricole_2010_valeur_pourcent;part_population_agricole_2010_note;part_population_agricole_1988_valeur_pourcent;part_population_agricole_1988_note;evolution_part_population_agricole_valeur_pt_de_pourcentage;evolution_part_population_agricole_note;synthese_note;synthese_message
P-FR;France;PAYS;26494;8421;514694;102;1067156;344;84293;138636;159384;107664;211406;87915;97440;72622;20594;0.38;5.00;4.09;5.00;-3.70;5.00;5;Population agricole <strong>en proportion proche de la moyenne française</strong> mais <strong>en déclin</strong>
P-FR;France;PAYS;26494;8421;514694;102;1067156;344;84293;138636;159384;107664;211406;87915;97440;72622;20594;0.38;5.00;4.09;5.00;-3.70;5.00;5;Population agricole <strong>en déclin</strong> et <strong>en proportion proche de la moyenne française</strong>
R-11;Île-de-France;REGION;20000;2000;;;;;;;;;;;;;;;;;;;;;Données non disponibles
R-75;Nouvelle-Aquitaine;REGION;6445;6370;;;;;;;;;;;;;;;;;;;;;Données non disponibles
R-76;Occitanie;REGION;49;51;;;;;;;;;;;;;;;;;;;;;Données non disponibles
......@@ -8,14 +8,14 @@ D-40;Landes;DEPARTEMENT;6445;6370;;;;;;;;;;;;;;;;;;;;;Données non disponibles
D-75;Paris;DEPARTEMENT;20000;2000;;;;;;;;;;;;;;;;;;;;;Données non disponibles
PAT-1;Le PAT monocommunal du Gée-Rivière;REGROUPEMENT_COMMUNES;49;51;9;24;44;87;0;0;0;0;0;0;0;0;0;48.98;10.00;170.59;10.00;-121.61;0.00;8;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
PAT-2;Le PAT interdepartemental D-32 (3 communes) D-40 (2 communes);REGROUPEMENT_COMMUNES;6494;6421;54;102;184;344;14;23;26;21;46;17;15;0;0;1.57;10.00;5.36;6.55;-3.79;4.88;10;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
SCOT-1;Le SCOT monocommunal de Paris;REGROUPEMENT_COMMUNES;20000;2000;0;0;0;0;0;0;0;0;0;0;0;0;0;0.00;0.00;0.00;0.00;0.00;10.00;2;Population agricole <strong>stable</strong> mais <strong>en proportion plus faible que la moyenne française</strong>
SCOT-1;Le SCOT monocommunal de Paris;REGROUPEMENT_COMMUNES;20000;2000;0;0;0;0;0;0;0;0;0;0;0;0;0;0.00;0.00;0.00;0.00;0.00;10.00;2;Population agricole <strong>en proportion plus faible que la moyenne française</strong> et <strong>stable</strong>
SCOT-2;Le SCOT monocommunal de Gée-Rivière;REGROUPEMENT_COMMUNES;49;51;9;24;44;87;0;0;0;0;0;0;0;0;0;48.98;10.00;170.59;10.00;-121.61;0.00;8;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
SCOT-3;Le SCOT interdepartemental D-32 (3 communes) D-40 (2 communes);REGROUPEMENT_COMMUNES;6494;6421;54;102;184;344;14;23;26;21;46;17;15;0;0;1.57;10.00;5.36;6.55;-3.79;4.88;10;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
E-200030435;CC d'Aire-sur-l'Adour;EPCI;6494;6421;54;102;184;344;14;23;26;21;46;17;15;0;0;1.57;10.00;5.36;6.55;-3.79;4.88;10;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
E-200054781;Métropole du Grand Paris;EPCI;20000;2000;0;0;0;0;0;0;0;0;0;0;0;0;0;0.00;0.00;0.00;0.00;0.00;10.00;2;Population agricole <strong>stable</strong> mais <strong>en proportion plus faible que la moyenne française</strong>
E-200054781;Métropole du Grand Paris;EPCI;20000;2000;0;0;0;0;0;0;0;0;0;0;0;0;0;0.00;0.00;0.00;0.00;0.00;10.00;2;Population agricole <strong>en proportion plus faible que la moyenne française</strong> et <strong>stable</strong>
C-32145;Gée-Rivière;COMMUNE;49;51;9;24;44;87;0;0;0;0;0;0;0;0;0;48.98;10.00;170.59;10.00;-121.61;0.00;8;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
C-32333;Projan(scission2020);COMMUNE;;;;;;;;;;;;;;;;;;;;;;;Données non disponibles
C-32424;Ségos(fusion2017);COMMUNE;;;;;;;;;;;;;;;;;;;;;;;Données non disponibles
C-40001;Aire-sur-l'Adour;COMMUNE;6279;6205;9;14;38;71;14;23;26;21;46;17;15;0;0;0.22;2.89;1.14;1.39;-0.92;8.76;5;Population agricole <strong>en proportion plus faible que la moyenne française</strong> et <strong>en déclin</strong>
C-40146;Latrille;COMMUNE;166;165;36;64;102;186;0;0;0;0;0;0;0;0;0;38.55;10.00;112.73;10.00;-74.17;0.00;8;Population agricole <strong>en proportion plus élevée que la moyenne française</strong> mais <strong>en déclin</strong>
C-75056;Paris;COMMUNE;20000;2000;0;0;0;0;0;0;0;0;0;0;0;0;0;0.00;0.00;0.00;0.00;0.00;10.00;2;Population agricole <strong>stable</strong> mais <strong>en proportion plus faible que la moyenne française</strong>
C-75056;Paris;COMMUNE;20000;2000;0;0;0;0;0;0;0;0;0;0;0;0;0;0.00;0.00;0.00;0.00;0.00;10.00;2;Population agricole <strong>en proportion plus faible que la moyenne française</strong> et <strong>stable</strong>
......@@ -2,8 +2,10 @@ import unittest
import numpy as np
import pandas as pd
from pandas import Series
from crater.outils import calculerNoteParInterpolation2Seuils, calculer_note_par_interpolation_2_seuils
from crater.outils import calculerNoteParInterpolation2Seuils, calculer_note_par_interpolation_2_seuils, \
message_synthese, discretiser_indicateur, construire_message
from tests.test_outils import augmenter_nombre_colonnes_affichees_pandas
augmenter_nombre_colonnes_affichees_pandas()
......@@ -72,7 +74,93 @@ class TestUnitaireOutils(unittest.TestCase):
# then
pd.testing.assert_series_equal(df['note'], df_attendu['note'], check_names=False)
def test_discretiser_indicateur_vers_classes_string(self):
# given
df = pd.DataFrame(
columns=['indicateur'],
data=[0, 2, 4, 1, 3, np.nan])
# when
df['message'] = discretiser_indicateur(df['indicateur'], [1, 3],
["Inferieur à 1", "Entre 1 et 3", "Superieur a 3"],
pd.StringDtype(), "Données non disponible")
# then
resultat_attendu = pd.Series(
data=
["Inferieur à 1",
"Entre 1 et 3",
"Superieur a 3",
"Entre 1 et 3",
"Superieur a 3",
"Données non disponible"
],
dtype=pd.StringDtype())
self.assertEqual(resultat_attendu.tolist(), df['message'].tolist())
self.assertEqual(resultat_attendu.dtype, df['message'].dtype)
def test_discretiser_indicateur_vers_classes_int64(self):
# given
df = pd.DataFrame(
columns=['indicateur'],
data=[0, 2, 4, 1, 3, np.nan])
# when
df['valeur_message'] = discretiser_indicateur(df['indicateur'], [1, 3], [0, 6, 10], pd.Int64Dtype(), -1)
# then
resultat_attendu = pd.Series(
data=
[0,
6,
10,
6,
10,
-1],
dtype=pd.Int64Dtype())
self.assertEqual(resultat_attendu.tolist(), df['valeur_message'].tolist())
self.assertEqual(resultat_attendu.dtype, df['valeur_message'].dtype)
def test_construire_message_synthese(self):
df = pd.DataFrame(
columns=['indicateur1', 'indicateur2'],
data=[
[1, 40],
[5, 40],
[8, 40],
[1, 60],
[5, 60],
[8, 60],
[np.nan, 40],
[1, np.nan],
])
# given
df_attendu = pd.DataFrame(
columns=['message'],
data=[
['Intro intro 2 <strong>très mauvais</strong> et intro 1 <strong>mauvais</strong>'],
['Intro intro 2 <strong>très mauvais</strong> et intro 1 <strong>neutre</strong>'],
['Intro intro 1 <strong>très bien</strong> mais intro 2 <strong>très mauvais</strong>'],
['Intro intro 2 <strong>bien</strong> mais intro 1 <strong>mauvais</strong>'],
['Intro intro 2 <strong>bien</strong> et intro 1 <strong>neutre</strong>'],
['Intro intro 1 <strong>très bien</strong> et intro 2 <strong>bien</strong>'],
['Données non disponibles'],
['Données non disponibles'],
]
)
# when
df['message'] = construire_message("Intro ", df['indicateur1'], "intro 1 ", [4, 6], ["mauvais", "neutre", "très bien"], [-1, 0, 10],
df['indicateur2'], "intro 2 ", [50], ["très mauvais", "bien"], [-2, 8])
# then
pd.testing.assert_series_equal(df['message'], df_attendu['message'].astype(pd.StringDtype()), check_names=False)
# TODO à supprimer quand on aura basculé sur le nouveau fonctionnement
# Fonctions helper pour encapsuler les apply
def calculer_note_par_interpolation_sur_df(df, nom_colonne_indicateur, seuil1, seuil2, note_seuil1, note_seuil2):
return df.apply(
lambda x:
......@@ -84,6 +172,41 @@ def calculer_note_par_interpolation_sur_df(df, nom_colonne_indicateur, seuil1, s
axis=1
)
def calculer_message_df(df):
return (
df.apply(
lambda x:
_message(
x['indicateur1'],
x['indicateur2']
),
axis=1
)
)
def _message(
indicateur1,
indicateur2
):
return message_synthese(
"Intro ",
[
[indicateur1,
"intro 1 ",
"croissant",
[4, 6],
["mauvais", "neutre", "très bien"],
[-1, 0, 10]
],
[indicateur2,
"intro 2 ",
"croissant",
[50],
["très mauvais", "bien"],
[-2, 8]
]
]
)
if __name__ == '__main__':
unittest.main()
......@@ -88,12 +88,12 @@ class TestUnitairePopulationAgricole(unittest.TestCase):
]).set_index('id_territoire')
resultat_attendu = pd.DataFrame(
columns=['synthese_note','synthese_message'],
data=[[5, 'Population agricole <strong>en proportion proche de la moyenne française</strong> mais <strong>en déclin</strong>'],
data=[[5, 'Population agricole <strong>en déclin</strong> et <strong>en proportion proche de la moyenne française</strong>'],
[np.nan, 'Données non disponibles'],
[0, 'Population agricole <strong>en proportion plus faible que la moyenne française</strong> et <strong>en déclin</strong>'],
[10, 'Population agricole <strong>en proportion plus élevée que la moyenne française</strong> et <strong>en croissance</strong>']
]
).astype({'synthese_note': 'Int64'})
).astype({'synthese_note': 'Int64', 'synthese_message': pd.StringDtype()})
# when
df_resultat = ajouter_colonne_synthese_population_agricole(df_population_agricole)
df_resultat = df_resultat.reset_index()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment