Commit 6498a955 authored by Lionel's avatar Lionel
Browse files

Resolve "Ajouter la notion de SAU productive et SAU peu productive"

parent 7106e486
......@@ -23,7 +23,8 @@ from crater.reglages import (
DOSSIER_SOURCES, DOSSIER_TERRITOIRES, DOSSIER_SURFACE_AGRICOLE_UTILE,
DOSSIER_POPULATION_AGRICOLE, DOSSIER_POLITIQUE_FONCIERE, DOSSIER_PRATIQUES_AGRICOLES, CHEMIN_SOURCE_SAU_BIO,
CHEMIN_CRATER_DATA_SOURCES,
DOSSIER_PARCEL, DOSSIER_CRATER, DOSSIER_PRODUCTION_BESOINS, DOSSIER_RPG
DOSSIER_PARCEL, DOSSIER_CRATER, DOSSIER_PRODUCTION_BESOINS, DOSSIER_RPG,
CHEMIN_SOURCE_CORRESPONDANCE_NOMENCLATURE_RPG_VERS_CRATER
)
from crater.surface_agricole_utile import collecteur_rpg, calculateur_surface_agricole_utile
from crater.territoires.calculateur_donnees_territoires import calculer_donnees_territoires, \
......@@ -83,6 +84,7 @@ def lancer_calcul_territoires():
CHEMIN_SOURCE_POPULATION_TOTALE,
CHEMIN_SOURCE_POPULATION_TOTALE_HISTORIQUE,
CHEMIN_OUTPUT_DATA / DOSSIER_SURFACE_AGRICOLE_UTILE / 'sau_par_commune_et_culture',
CHEMIN_SOURCE_CORRESPONDANCE_NOMENCLATURE_RPG_VERS_CRATER,
CHEMIN_CRATER_DATA_SOURCES / DOSSIER_PARCEL / 'correspondance_territoires_parcel_crater.csv',
CHEMIN_OUTPUT_DATA / DOSSIER_TERRITOIRES
)
......@@ -150,7 +152,7 @@ def lancer_calcul_production_besoins():
CHEMIN_CRATER_DATA_SOURCES / DOSSIER_PARCEL / 'produits_hors_elevage_vers_codes_cultures.csv',
CHEMIN_CRATER_DATA_SOURCES / DOSSIER_PARCEL / 'produits_elevage_vers_codes_cultures.csv',
CHEMIN_OUTPUT_DATA / DOSSIER_SURFACE_AGRICOLE_UTILE / 'sau_par_commune_et_culture',
CHEMIN_CRATER_DATA_SOURCES / DOSSIER_CRATER / 'codes_rpg_vers_codes_cultures.csv',
CHEMIN_SOURCE_CORRESPONDANCE_NOMENCLATURE_RPG_VERS_CRATER,
CHEMIN_OUTPUT_DATA / DOSSIER_PRODUCTION_BESOINS
)
......
......@@ -63,7 +63,7 @@ def calculer(
def calculer_donnees_politique_fonciere(referentiel_territoires, sources_politique_fonciere):
donnees_politique_fonciere: DictTerritoires = outils_territoires.copier_en_gardant_colonnes(
referentiel_territoires,
['sau_ha',
['sau_productive_ha',
'population_totale_2017'
]
)
......@@ -91,8 +91,7 @@ def calculer_donnees_politique_fonciere(referentiel_territoires, sources_politiq
'evolution_emplois_2011_2016',
'evolution_menages_emplois_2011_2016',
'superficie_ha'
],
"somme"
]
)
for t in donnees_politique_fonciere:
......
......@@ -2,6 +2,9 @@ import crater.outils as outils
import crater.territoires.outils as outils_territoires
from crater.crater_logger import log
# TODO : il faudrait améliorer la règle de calcul dans le cas d'une SAU productive nulle.
# Dans ce cas (div par 0), on obtient une valeur NaN pour l'indicateur d'artificialisation, et on affiche Données non disponible
# voir si on ne peut pas afficher un messager plus spécifique, ou calculer autrement l'indicateur
def calculer(territoires):
log.info(" => Indicateur rythme d'artificialisation de la sau")
......@@ -9,7 +12,7 @@ def calculer(territoires):
indicateur = outils_territoires.copier_en_gardant_colonnes(
territoires,
['artificialisation_2011_2016_ha',
'sau_ha'
'sau_productive_ha'
]
)
for t in indicateur:
......@@ -17,7 +20,7 @@ def calculer(territoires):
lambda x:
valeur(
x['artificialisation_2011_2016_ha'],
x['sau_ha']
x['sau_productive_ha']
),
axis=1
)
......@@ -39,10 +42,10 @@ def calculer(territoires):
return indicateur
def valeur(artificialisation_5ans_ha, sau_ha):
def valeur(artificialisation_5ans_ha, sau_productive_ha):
try:
x = round(
artificialisation_5ans_ha / 5 / sau_ha * 100,
artificialisation_5ans_ha / 5 / sau_productive_ha * 100,
2
)
except:
......
......@@ -8,7 +8,7 @@ def calculer(territoires):
indicateur = outils_territoires.copier_en_gardant_colonnes(
territoires,
['sau_ha',
['sau_productive_ha',
'population_totale_2017'
]
)
......@@ -16,7 +16,7 @@ def calculer(territoires):
indicateur[t]['sau_par_habitant_valeur_m2_par_hab'] = indicateur[t].apply(
lambda x:
valeur(
x['sau_ha'],
x['sau_productive_ha'],
x['population_totale_2017']
),
axis=1
......@@ -35,10 +35,10 @@ def calculer(territoires):
return indicateur
def valeur(sau_ha, population_totale):
def valeur(sau_productive_ha, population_totale):
try:
x = round(
sau_ha * 1e4 / population_totale
sau_productive_ha * 1e4 / population_totale
)
except:
x = ""
......
......@@ -40,7 +40,8 @@ def calculer_notes_messages(territoires):
lambda x:
message(
x['sau_par_habitant_valeur_m2_par_hab'],
x['artificialisation_2011_2016_ha']
x['artificialisation_2011_2016_ha'],
x['synthese_note']
),
axis=1
)
......@@ -59,8 +60,13 @@ def note(
def message(
sau_par_habitant_valeur_m2_par_hab,
artificialisation_2011_2016_ha
artificialisation_2011_2016_ha,
synthese_note
):
# Cas ou la sau_productive est nulle => la note est NaN, et le message "Données non disponibles" est plus approprié
if not np.isfinite(synthese_note):
return "Données non disponibles"
return outils.message_synthese(
"",
[
......
......@@ -168,7 +168,6 @@ def calculer_donnees_population_agricole(referentiel_territoires: DictTerritoire
['actifs_agricoles_permanents_2010',
'actifs_agricoles_permanents_1988'
],
"somme",
"Int64"
)
return donnees_population_agricole
......@@ -20,7 +20,7 @@ def calculer_pratiques_agricoles(
donnees_pratiques_agricoles = outils_territoires.assembler_dict_territoires_en_df_territoires(
outils_territoires.copier_en_gardant_colonnes(
referentiel_territoires,
['sau_ha']
['sau_productive_ha']
)
)
donnees_pratiques_agricoles = ajouter_indicateur_hvn(
......
......@@ -9,7 +9,7 @@ from crater.outils import (
supprimer_donnees_apres_annee_mouvement_commune2
)
from crater.pratiques_agricoles.gestionnaire_sources_pratiques_agricoles import importer_hvn
from crater.territoires.outils import calculer_donnees_supracommunales_par_moyenne_ponderee_sau_donnees_communales
from crater.territoires.outils import calculer_donnees_supracommunales_par_moyenne_ponderee_donnees_communales
def ajouter_indicateur_hvn(
......@@ -53,13 +53,18 @@ def _ajouter_donnees_hvn(
],
2018
)
donnees_pratiques_agricoles = calculer_donnees_supracommunales_par_moyenne_ponderee_sau_donnees_communales(
donnees_pratiques_agricoles,
['hvn_indice1',
'hvn_indice2',
'hvn_indice3',
'hvn_score']
)
donnees_pratiques_agricoles = calculer_donnees_supracommunales_par_moyenne_ponderee_donnees_communales(donnees_pratiques_agricoles,
'hvn_indice1',
'sau_productive_ha')
donnees_pratiques_agricoles = calculer_donnees_supracommunales_par_moyenne_ponderee_donnees_communales(donnees_pratiques_agricoles,
'hvn_indice2',
'sau_productive_ha')
donnees_pratiques_agricoles = calculer_donnees_supracommunales_par_moyenne_ponderee_donnees_communales(donnees_pratiques_agricoles,
'hvn_indice3',
'sau_productive_ha')
donnees_pratiques_agricoles = calculer_donnees_supracommunales_par_moyenne_ponderee_donnees_communales(donnees_pratiques_agricoles,
'hvn_score',
'sau_productive_ha')
return donnees_pratiques_agricoles
......
......@@ -11,7 +11,7 @@ from crater.outils import (
calculerNoteParInterpolation2Seuils
)
from crater.pratiques_agricoles.gestionnaire_sources_pratiques_agricoles import importer_surfaces_bio
from crater.territoires.outils import calculer_donnees_pays_par_somme_donnees_communales, ID_FRANCE
from crater.territoires.outils import calculer_donnees_pays_par_somme_donnees_regions, ID_FRANCE
def ajouter_indicateur_part_sau_bio(
......@@ -54,11 +54,13 @@ def _ajouter_donnees_surfaces_bio(
['surface_bio_ha'],
2020
)
donnees_pratiques_agricoles = calculer_donnees_pays_par_somme_donnees_communales(
donnees_pratiques_agricoles = calculer_donnees_pays_par_somme_donnees_regions(
donnees_pratiques_agricoles,
['surface_bio_ha']
'surface_bio_ha'
)
return donnees_pratiques_agricoles
......@@ -85,10 +87,13 @@ def _ajouter_valeurs_et_note(donnees_pratiques_agricoles: Territoires) -> Territ
def _calculer_part_sau_bio_pourcent(donnees_pratiques_agricoles):
donnees_pratiques_agricoles['part_sau_bio_valeur_pourcent'] = donnees_pratiques_agricoles['surface_bio_ha'] / \
donnees_pratiques_agricoles['sau_ha'] * 100
donnees_pratiques_agricoles['sau_productive_ha'] * 100
# TODO : voir ce qui est vraiment utile comme ajustement de données
# si la sau productive est nulle, on met l'indicateur à 0 (sinon dans certains cas il a la valeur NaN suite à division par 0)
# donnees_pratiques_agricoles.loc[donnees_pratiques_agricoles['sau_productive_ha'] == 0, 'part_sau_bio_valeur_pourcent'] = 0
# Ramener à 100 les valeurs supérieures (peut arriver suite a arrondis quand sau_bio = sau_productive),
donnees_pratiques_agricoles['part_sau_bio_valeur_pourcent'] = (
donnees_pratiques_agricoles['part_sau_bio_valeur_pourcent']
.mask(donnees_pratiques_agricoles['part_sau_bio_valeur_pourcent'] > 100, 100)
.replace([numpy.inf], numpy.nan)
)
return donnees_pratiques_agricoles
......@@ -42,6 +42,7 @@ CHEMIN_SOURCE_POPULATION_AGRICOLE = Path(
CHEMIN_CRATER_DATA_SOURCES /
"agreste/1970-2010/FDS_G_2005.zip"
)
CHEMIN_SOURCE_ARTIFICIALISATION = Path(
CHEMIN_CRATER_DATA_SOURCES /
"cerema/2009-2018/artificialisation metropole 2009_2018.csv"
......@@ -59,6 +60,11 @@ DOSSIER_SOURCE_IGN = Path("ign")
DOSSIER_PARCEL = Path("parcel")
DOSSIER_CRATER = Path("crater")
# Ajout de la categorie de sau dans le fichier, qui change de nom
# TODO : voir si pas pertinent de garder les fichiers de crater-data-source/crater sous git
# CHEMIN_SOURCE_CORRESPONDANCE_NOMENCLATURE_RPG_VERS_CRATER = CHEMIN_CRATER_DATA_SOURCES / DOSSIER_CRATER / 'codes_rpg_vers_codes_cultures.csv'
CHEMIN_SOURCE_CORRESPONDANCE_NOMENCLATURE_RPG_VERS_CRATER = CHEMIN_CRATER_DATA_SOURCES / DOSSIER_CRATER / 'correspondance_nomenclature_rpg_vers_crater_092021.csv'
CHEMIN_OUTPUT_DATA = Path("../crater-data-resultats")
DOSSIER_SOURCES = Path("sources")
......
......@@ -3,16 +3,19 @@ from pathlib import Path
import pandas
from pandas import DataFrame
from crater.outils import DictTerritoires
import crater.territoires.outils as outils_territoires
from crater.crater_logger import log
from crater.outils import DictTerritoires
from crater.surface_agricole_utile import chargeur_sau_par_commune_et_culture
from crater.territoires import chargeur_arrondissements, chargeur_mouvements_communes, \
chargeur_population_totale, chargeur_population_totale_historique, chargeur_territoires_parcel
from crater.crater_logger import log
from crater.territoires.calculateur_referentiel_territoires import charger_referentiel_territoires, \
ajouter_id_commune_de_arrondissement
from crater.territoires.chargeur_donnees_territoires import charger_donnees_territoires
CODE_CATEGORIE_SAU_PRODUCTIVE = 'PRODUCTIVE'
CODE_CATEGORIE_SAU_PEU_PRODUCTIVE = 'PEU_PRODUCTIVE'
def calculer_donnees_territoires(
territoires: DictTerritoires,
......@@ -21,6 +24,7 @@ def calculer_donnees_territoires(
chemin_fichier_population_totale: Path,
chemin_fichier_population_totale_historique: Path,
chemin_fichiers_sau: Path,
chemin_fichiers_correspondance_nomenclature_rpg_vers_crater: Path,
chemin_fichier_territoires_parcel: Path,
chemin_dossier_output: Path
) -> None:
......@@ -32,6 +36,7 @@ def calculer_donnees_territoires(
chemin_fichier_population_totale,
chemin_fichier_population_totale_historique,
chemin_fichiers_sau,
chemin_fichiers_correspondance_nomenclature_rpg_vers_crater,
chemin_fichier_territoires_parcel
)
......@@ -47,6 +52,8 @@ def calculer_donnees_territoires(
outils_territoires.copier_en_gardant_colonnes(
territoires_enrichi,
['sau_ha',
'sau_productive_ha',
'sau_peu_productive_ha',
'population_totale_2017',
'population_totale_2010',
'population_totale_1990',
......@@ -72,34 +79,40 @@ def _ajouter_colonnes_donnees_territoires(territoires: DictTerritoires,
sources: DictTerritoires) -> DictTerritoires:
log.info("##### AJOUT DES DONNEES COMPLEMENTAIRES DES TERRITOIRES #####")
_ajouter_sau(
_ajouter_colonnes_sau(
territoires,
sources['sau'],
sources['correspondance_nomenclature_rpg_vers_crater'],
source_mouvements_communes,
2019
2017
)
_ajouter_colonnes_population(territoires, source_arrondissements, source_mouvements_communes, sources)
_ajouter_colonne_id_territoire_parcel(
territoires,
sources['territoires_parcel']
)
_ajouter_population_totale(
return territoires
def _ajouter_colonnes_population(territoires, source_arrondissements, source_mouvements_communes, sources):
_ajouter_colonne_population_totale(
territoires,
source_arrondissements,
sources['population_totale'],
source_mouvements_communes,
2019
)
_ajouter_population_totale_historique(
_ajouter_colonne_population_totale_historique(
territoires,
sources['population_totale_historique'],
source_mouvements_communes,
2017
)
_ajouter_id_territoire_parcel(
territoires,
sources['territoires_parcel']
)
return territoires
def _ajouter_population_totale(
def _ajouter_colonne_population_totale(
territoires: DictTerritoires,
arrondissements: DataFrame,
population_totale: DataFrame,
......@@ -120,6 +133,7 @@ def _ajouter_population_totale(
)
outils_territoires.traiter_donnees_communes_remodelees(
population_totale_final,
['population_totale_2017'],
mouvements_communes,
annee
)
......@@ -134,12 +148,11 @@ def _ajouter_population_totale(
outils_territoires.ajouter_colonne_par_agregation(
territoires,
"population_totale_2017",
"somme",
"Int64"
)
def _ajouter_population_totale_historique(
def _ajouter_colonne_population_totale_historique(
territoires: DictTerritoires,
population_totale_historique: DataFrame,
mvts_communes: DataFrame,
......@@ -148,6 +161,7 @@ def _ajouter_population_totale_historique(
population_totale_historique_final = population_totale_historique.copy()
outils_territoires.traiter_donnees_communes_remodelees(
population_totale_historique_final,
['population_totale_2010', 'population_totale_1990'],
mvts_communes,
annee
)
......@@ -163,12 +177,11 @@ def _ajouter_population_totale_historique(
["population_totale_2010",
"population_totale_1990"
],
"somme",
"Int64"
)
def _ajouter_id_territoire_parcel(
def _ajouter_colonne_id_territoire_parcel(
territoires: DictTerritoires,
territoires_parcel: DataFrame
) -> None:
......@@ -205,34 +218,63 @@ def _ajouter_id_territoire_parcel(
)
def _ajouter_sau(
def _ajouter_colonnes_sau(
territoires: DictTerritoires,
sau: DataFrame,
sau_par_commune_et_code_rpg: DataFrame,
correspondance_nomenclature_rpg_vers_crater: DataFrame,
mouvements_communes: DataFrame,
annee: int
) -> None:
sau_final = sau.copy()
outils_territoires.traiter_donnees_communes_remodelees(
sau_final,
mouvements_communes,
annee
)
# TODO : voir si nécessaire de garder le copy() ici ? peut etre nécessaire pour donnees_communes_remodelees qui modifie le df ?
sau_par_commune_et_categorie = (sau_par_commune_et_code_rpg.copy()
.loc[:, ['id_commune', 'code_culture_rpg', 'sau_ha']]
.merge(
correspondance_nomenclature_rpg_vers_crater.loc[:, ['code_culture_rpg', 'categorie_sau']],
how='left',
on='code_culture_rpg')
)
# outils_territoires.traiter_donnees_communes_remodelees(
# sau_par_commune_et_categorie,
# list(sau_par_commune_et_categorie.columns).remove('id_commune'),
# mouvements_communes,
# annee
# )
sau_par_commune_et_categorie['sau_productive_ha'] = sau_par_commune_et_categorie.loc[
sau_par_commune_et_categorie['categorie_sau'] == CODE_CATEGORIE_SAU_PRODUCTIVE, 'sau_ha']
sau_par_commune_et_categorie['sau_peu_productive_ha'] = sau_par_commune_et_categorie.loc[
sau_par_commune_et_categorie['categorie_sau'] == CODE_CATEGORIE_SAU_PEU_PRODUCTIVE, 'sau_ha']
_ajouter_colonne_sau(territoires, sau_par_commune_et_categorie, 'sau_ha', mouvements_communes, annee)
_ajouter_colonne_sau(territoires, sau_par_commune_et_categorie, 'sau_productive_ha', mouvements_communes, annee)
_ajouter_colonne_sau(territoires, sau_par_commune_et_categorie, 'sau_peu_productive_ha', mouvements_communes, annee)
def _ajouter_colonne_sau(territoires, sau_enrichie_nomenclature_crater, nom_colonne_sau_resultat, mouvements_communes, annee):
sau_par_commune = sau_enrichie_nomenclature_crater.loc[:, ['id_commune', nom_colonne_sau_resultat]].groupby(
['id_commune'],
as_index=False
).sum()
territoires['communes'] = pandas.merge(
territoires['communes'],
sau_final.groupby(
['id_commune'],
as_index=False
).agg(
{'sau_ha': 'sum'}
),
sau_par_commune,
how='left',
on=['id_commune'],
suffixes=(False, False)
on='id_commune'
)
territoires['communes'][nom_colonne_sau_resultat] = territoires['communes'][nom_colonne_sau_resultat].fillna(0)
outils_territoires.traiter_donnees_communes_remodelees(
territoires['communes'],
[nom_colonne_sau_resultat],
mouvements_communes,
annee
)
outils_territoires.ajouter_colonne_par_agregation(
territoires,
"sau_ha",
"somme"
nom_colonne_sau_resultat,
)
......@@ -242,6 +284,7 @@ def _charger_sources_donnees_territoires(
chemin_fichier_population_totale: Path,
chemin_fichier_population_totale_historique: Path,
chemin_fichiers_sau: Path,
chemin_fichiers_correspondance_nomenclature_rpg_vers_crater: Path,
chemin_fichier_territoires_parcel: Path
) -> DictTerritoires:
log.info(
......@@ -253,6 +296,8 @@ def _charger_sources_donnees_territoires(
chargeur_mouvements_communes.traduire_code_insee_vers_id_crater(mouvements_communes)
sau = chargeur_sau_par_commune_et_culture.charger(chemin_fichiers_sau)
correspondance_nomenclature_rpg_vers_crater = pandas.read_csv(
chemin_fichiers_correspondance_nomenclature_rpg_vers_crater, sep=";", dtype='str')
population_totale = chargeur_population_totale.charger(chemin_fichier_population_totale)
population_totale_historique = chargeur_population_totale_historique.charger(
......@@ -269,5 +314,6 @@ def _charger_sources_donnees_territoires(
'population_totale': population_totale,
'population_totale_historique': population_totale_historique,
'sau': sau,
'correspondance_nomenclature_rpg_vers_crater': correspondance_nomenclature_rpg_vers_crater,
'territoires_parcel': territoires_parcel
}
......@@ -60,6 +60,7 @@ def compiler_territoires(
def traiter_donnees_communes_remodelees(
df: DataFrame,
colonnes_donnees_a_supprimer,
mvts_communes: DataFrame,
annee: int
) -> None:
......@@ -83,9 +84,7 @@ def traiter_donnees_communes_remodelees(
).drop_duplicates(
).sort_values(by=["id_commune"])
colonnes = list(df.columns)
colonnes.remove('id_commune')
for col in colonnes:
for col in colonnes_donnees_a_supprimer:
df.loc[df.id_commune.isin(mouvements_communes.id_commune), col] = np.nan
......@@ -159,66 +158,46 @@ def ajouter_colonne(
)
# TODO : a fusionner avec calculer_donnees_supracommunales_par_somme_donnees_communales; Les 2 fn font la meme chose, mais l'une travaille sur un DictTerritoires et l'autre sur un df Territoires
def ajouter_colonne_par_agregation(
territoires: DictTerritoires,
colonne: str,
fonction: str,
type: str = None,
option: str = "communes vers le reste"
) -> None:
if fonction == "somme":
fun = lambda x: pd.Series(np.sum(x[colonne]), index=[colonne])
elif fonction == "moyenne ponderee par la sau":
# on utilise pas np.avergae car cela ne calcule pas sinon lorsqu'il y a des lignes avec info dans la colonne mais pas de sau
fun = lambda x: pd.Series(np.sum(x[colonne] * x['sau_ha']) / np.sum(x['sau_ha']), index=[colonne])
else:
raise ValueError("ERREUR : argument 'fonction' incorrect !")
if option == "communes vers le reste":
territoires['epcis'] = pd.merge(
territoires['epcis'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_epci").apply(fun).reset_index(),
how='left',
on=['id_epci'],
suffixes=(False, False)
).astype(dtype={colonne: type})
territoires['departements'] = pd.merge(
territoires['departements'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_departement").apply(
fun).reset_index(),
how='left',
on=['id_departement'],
suffixes=(False, False)
).astype(dtype={colonne: type})
territoires['regions'] = pd.merge(
territoires['regions'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_region").apply(fun).reset_index(),
how='left',
on=['id_region'],
suffixes=(False, False)
).astype(dtype={colonne: type})
territoires['pays'] = pd.merge(
territoires['pays'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_pays").apply(fun).reset_index(),
how='left',
on=['id_pays'],
suffixes=(False, False)
).astype(dtype={colonne: type})
elif option == "regions vers pays":
territoires['pays'] = pd.merge(
territoires['pays'],
territoires['regions'].dropna(how='any', subset=[colonne]).groupby("id_pays").apply(fun).reset_index(),
how='left',
on=['id_pays'],
suffixes=(False, False)
)
else:
raise ValueError("ERREUR : argument 'option' incorrect !")
# TODO .astype(dtype={colonne: type}) ne fonctionne pas sur l'option "regions vers pays"
fun = lambda x: pd.Series(np.sum(x[colonne]), index=[colonne])
territoires['epcis'] = pd.merge(
territoires['epcis'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_epci").apply(fun).reset_index(),
how='left',
on=['id_epci'],
suffixes=(False, False)
).astype(dtype={colonne: type})
territoires['departements'] = pd.merge(
territoires['departements'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_departement").apply(
fun).reset_index(),
how='left',
on=['id_departement'],
suffixes=(False, False)
).astype(dtype={colonne: type})
territoires['regions'] = pd.merge(
territoires['regions'],
territoires['communes'].dropna(how='any', subset=[colonne]).groupby("id_region").apply(fun).reset_index(),
how='left',
on=['id_region'],
suffixes=(False, False)
).astype(dtype={colonne: type})
territoires['pays'] = pd.merge(
territoires['pays'],