Aller au contenu principal

GFM Origine

Le Gap Filling Module Origine détermine l'origine géographique des produits alimentaires lorsqu'elle n'est pas explicitement spécifiée. En utilisant les statistiques commerciales de la FAO (Organisation des Nations Unies pour l'alimentation et l'agriculture), il calcule les parts d'importation et les ratios de production nationale pour estimer la provenance la plus probable des ingrédients en fonction du « lieu de cuisine » (pays de consommation).

Référence rapide

PropriétéDescription
S'exécute surFoodProductFlowNode (hors subdivisions)
DépendancesLocationGapFillingWorker, AddClientNodesGapFillingWorker, InventoryConnectorGapFillingWorker, MatchProductNameGapFillingWorker, LinkTermToActivityNodeGapFillingWorker, IngredientAmountEstimatorGapFillingWorker
Entrée cléLieu de cuisine (pays), termes FoodEx2 correspondants, données commerciales FAO
SortieRépartition des origines avec pourcentages par pays, propriétés de localisation
Source de donnéesMatrice commerciale détaillée FAO STAT, Statistiques de production FAO

Conditions d'exécution

Le module se déclenche lorsque :

  1. Le noeud est un FoodProductFlowNode (pas une subdivision)
  2. Aucune origine n'est déjà spécifiée, OU plusieurs origines doivent être traitées
  3. Tous les GFM dépendants ont terminé leur exécution
  4. Les noeuds de sous-activité ont leurs quantités de production calculées

Sortie clé

Le module modifie le graphe de calcul par :

  • Répartition des origines : Création de plusieurs noeuds de flux de produits, chacun avec un pays d'origine spécifique
  • Allocation des pourcentages : Distribution des quantités basée sur les statistiques d'importation et la production nationale
  • Propriétés de localisation : Définition des codes pays, coordonnées et termes GADM

Méthodologie scientifique

Le modèle d'origine calcule la distribution de probabilité de la provenance d'un produit en fonction de :

Offre totale = Production nationale + Importations - Exportations

Pour chaque pays d'origine, la part est calculée comme suit :

Part nationale = (Production nationale - Exportations) / Offre totale
Part étrangère = 1 - Part nationale
Part d'importation par pays = Part étrangère * (Importation du pays / Importation totale)

Modèle de balance commerciale

Le modèle utilise les données FAO STAT pour calculer les distributions d'origine :

Part de production nationale

domestic_import_export_total = domestic_production + import_value - export_value

domestic_origin_share = (domestic_production - export_value) / domestic_import_export_total

Si la production nationale dépasse les exportations, une partie de la consommation est supposée nationale. La partie restante est allouée aux pays importateurs.

Distribution par pays d'importation

Pour les origines étrangères, le module identifie les principaux pays contributeurs :

# Obtenir les pays couvrant 90 % des importations totales
m49_country_share = import_export_cache.import_countries_top90percent(
kitchen_country_m49_code, fao_code, year_column
)

for country, value in country_share.items():
country_total_percentage_share[country] = foreign_origin_share * value

Le seuil de 90 % réduit la complexité de calcul tout en maintenant la précision. Pour les produits sans codes FAO spécifiques (statistiques par défaut), seuls les 10 premiers pays sont utilisés.

Gestion des incohérences

Les données commerciales FAO peuvent présenter des incohérences. Le module gère ces cas :

ScénarioTraitement
Export ≥ Production nationale + ImportDéfinir « origine inconnue »
Pas de données de production nationaleUtiliser uniquement les statistiques d'importation
Pas de données commerciales pour le produitRevenir aux statistiques FAO par défaut
Offre totale négativeDéfinir « origine inconnue »

Détails d'implémentation

Configuration des données FAO

Le module utilise deux jeux de données FAO principaux :

FAO_IMPORT_EXPORT_ZIP_URL = "https://bulks-faostat.fao.org/production/Trade_DetailedTradeMatrix_E_All_Data.zip"
FAO_DOMESTIC_ZIP_URL = "https://bulks-faostat.fao.org/production/Production_Crops_Livestock_E_All_Data.zip"

Fichiers de données extraits :

  • Trade_DetailedTradeMatrix_E_All_Data_NOFLAG.csv - Quantités import/export par paire de pays
  • Production_Crops_Livestock_E_All_Data_NOFLAG.csv - Production nationale par pays
  • Trade_DetailedTradeMatrix_E_ItemCodes.csv - Définitions des codes produits FAO

Sélection de l'année

Le module prend en charge des années primaires et secondaires pour la disponibilité des données :

# Si les données de l'année primaire sont manquantes, revenir à l'année secondaire
if pd.isnull(domestic_production_value.values[0]):
stats_data_year_column = self.gfm_factory.secondary_year
domestic_production_value = domestic_production_row[stats_data_year_column]

Codes FAO spéciaux

Des codes FAO personnalisés gèrent les cas particuliers :

Code FAONomDescription
100000Production localeProduits d'approvisionnement local comme l'eau
200000Production par défautProduits sans correspondance FAO spécifique
300000Production de poisson inconnueProduits de la pêche sans données commerciales
400000Production en échecRepli lorsque l'estimation d'origine échoue

Modes de traitement

Traitement monoproduit

Pour les produits à ingrédient unique :

async def monoproduct_origin_processing(self, calc_graph: "CalcGraph") -> None:
# 1. Vérifier si l'origine est déjà spécifiée
origins_list = await self.parse_current_origins()

if len(origins_list) == 1:
return # A déjà une origine

# 2. Obtenir le lieu de cuisine à partir du code pays hérité
kitchen_country_code = get_inherited_country_code(self.node)
kitchen_country_m49_code = iso_to_m49_mapping.get(kitchen_country_code)

# 3. Trouver le code FAO du produit via la correspondance FoodEx2
fao_code_term = linked_fao_code_terms.get(foodex2_term_uids)

# 4. Calculer les parts nationales + importées
# 5. Appliquer la répartition d'origine au graphe

Traitement produit combiné

Pour les produits à plusieurs ingrédients :

async def combined_product_origin_processing(self, calc_graph: "CalcGraph") -> None:
origins_list = await self.parse_current_origins(combined_product_origin_processing=True)

# Distribution égale entre les origines spécifiées
country_total_percentage_share = {
origin.country_code: 1 / len(origins_list)
for origin in origins_list
}

await self.apply_origin_split_to_graph(calc_graph, country_total_percentage_share, is_combined_product=True)

Mutation du graphe pour la répartition des origines

Lorsque plusieurs origines sont déterminées, le graphe est modifié :

async def apply_origin_split_to_graph(
self,
calc_graph: "CalcGraph",
country_total_percentage_share: dict,
is_origin_from_fao: bool = False,
...
) -> None:
# 1. Supprimer l'arête entre le produit et la sous-activité
remove_edge_mutation = RemoveEdgeMutation(...)

# 2. Ajouter un nouveau noeud Origin-Split-Activity
activity_node = FoodProcessingActivityNode.model_construct(...)
calc_graph.apply_mutation(AddNodeMutation(...))

# 3. Créer des noeuds de flux spécifiques à l'origine
for country_iso_code, percentage in country_total_percentage_share.items():
# Dupliquer le noeud original avec une nouvelle quantité
calc_graph.apply_mutation(DuplicateNodeMutation(...))

# Définir la quantité proportionnelle
new_amount = percentage * activity_node.production_amount.value

# Définir la propriété de localisation
origin_location = LocationProp.unvalidated_construct(
address=gadm_term.name,
country_code=country_iso_code,
term_uid=gadm_term.uid,
source=LocationSourceEnum.fao_stat if is_origin_from_fao else LocationSourceEnum.gadm,
)

Gestion des origines régionales

Lorsqu'une origine régionale (comme « Europe ») est spécifiée, elle est développée en pays composants :

# Gérer les termes régionaux
if term_xid in location_to_regional_term_xid_map.values():
for region in regional_term_xid_to_region_gadm_codes_map.get(term_xid, []):
if region not in already_listed_regions:
country_code = iso_3166_map_3_to_2_letter.get(region.split(".")[0])
locations.append(LocationProp.unvalidated_construct(
country_code=country_code,
term_uid=gadm_term.uid,
location_qualifier=location_qualifier,
))

Sources de données

Base de données FAO STAT

La source de données principale est la Division de statistique de la FAO :

Données commerciales : Matrice commerciale détaillée FAO

  • Contient les flux commerciaux bilatéraux entre pays
  • Quantités en tonnes
  • Éléments : Quantité d'importation, Quantité d'exportation, Valeur d'importation, Valeur d'exportation

Données de production : Statistiques de production FAO

  • Contient la production nationale par pays et produit
  • Quantités en tonnes

Correspondances de codes pays

Le module utilise plusieurs systèmes de codes pays :

SystèmeDescriptionExemple
M49Codes numériques ONU756 (Suisse)
ISO 3166-1 alpha-2Codes à deux lettresCH
ISO 3166-1 alpha-3Codes à trois lettresCHE
GADMCodes de base de données géographiquesCHE.1.2 (sous-régions)

Correspondance FoodEx2 vers FAO

Les produits sont liés via le service de glossaire :

fao_glossary_links = await glossary_link_service.get_glossary_links_by_gfm(
gap_filling_module="FAO"
)

# Associe les UID de termes FoodEx2 -> UID de terme de code FAO
for link in fao_glossary_links:
linked_fao_code_terms[frozenset(link.term_uids)] = link.linked_term_uid

Exemple de calcul

Scénario : Déterminer l'origine pour 1 kg de tomates consommées en Suisse (CH)

Étape 1 : Identifier le produit

  • Terme FoodEx2 correspondant : A0DMX (Tomates)
  • Code FAO via lien de glossaire : 388 (Tomates)
  • Pays de cuisine : Suisse (M49 : 756)

Étape 2 : Interroger les statistiques commerciales

Données FAO pour la Suisse et les tomates (code FAO 388) :

Point de donnéesValeur (tonnes)
Production nationale45 000
Importations totales75 000
Exportations totales5 000

Étape 3 : Calculer les parts

total_supply = 45 000 + 75 000 - 5 000 = 115 000

domestic_share = (45 000 - 5 000) / 115 000 = 0,348 (34,8 %)
foreign_share = 1 - 0,348 = 0,652 (65,2 %)

Étape 4 : Distribuer la part d'importation

Principaux pays d'importation pour les tomates suisses :

PaysImportation (tonnes)% d'importationPart finale
Espagne35 00046,7 %30,4 %
Italie20 00026,7 %17,4 %
Pays-Bas10 00013,3 %8,7 %
Maroc5 0006,7 %4,4 %
Autres5 0006,7 %4,4 %
Suisse (national)--34,8 %

Étape 5 : Modification du graphe

Le module crée des noeuds de répartition d'origine :

Original :
Tomates (1 kg) -> Activité -> ...

Après GFM Origine :
Tomates (1 kg) -> Origin-Split-Activity
|-> Tomates (0,348 kg, CH) -> Activité (copie) -> ...
|-> Tomates (0,304 kg, ES) -> Activité (copie) -> ...
|-> Tomates (0,174 kg, IT) -> Activité (copie) -> ...
|-> Tomates (0,087 kg, NL) -> Activité (copie) -> ...
|-> Tomates (0,044 kg, MA) -> Activité (copie) -> ...
|-> Tomates (0,044 kg, AUTRE) -> Activité (originale) -> ...

Chaque flux spécifique à l'origine reçoit ensuite les calculs de transport appropriés en aval.


Système de mise en cache

Cache Import/Export

Les données commerciales sont mises en cache au format MessagePack pour les performances :

class ImportExportCache:
def import_export_value(self, m49_code: int, fao_code: int, year_column: str) -> tuple[int, int]:
"""Retourne les sommes d'import et d'export depuis le cache."""
return self.import_export_data[(m49_code, fao_code)][year_column]

def import_countries_top90percent(self, m49_code: int, fao_code: int, year_column: str) -> dict:
"""Retourne la part relative d'importation entre les pays couvrant les 90 premiers pourcents."""
return self.import_export_data["top90"][(m49_code, fao_code)][year_column]

Structure des fichiers de cache

temp_data/origin_gfm/
├── domestic_df_{suffix}.hdf5 # Production nationale (HDF5)
├── import_export_cache_{suffix}.msgpack # Données commerciales pré-agrégées
├── item_codes_{suffix}.csv # Codes produits FAO
├── import_export_{version}.zip # Archive source
└── domestic_{version}.zip # Archive source

Intégration Google Drive

Les fichiers de cache sont synchronisés avec Google Drive pour un accès partagé :

def download_file_from_google_drive(self, drive_service: Resource, filename: str, file_id: str):
"""Télécharge le fichier de cache depuis Google Drive s'il n'est pas disponible localement."""

def upload_cache_file_to_google_drive(self, drive_service: Resource, filename: str):
"""Charge le fichier de cache régénéré vers Google Drive."""

Limitations connues

Qualité des données

  • Effet Rotterdam : Les marchandises réexportées via des plateformes commerciales (Pays-Bas, Belgique) peuvent apparaître comme provenant de ces pays plutôt que de leurs véritables origines
  • Lacunes des données FAO : Certains produits n'ont pas de statistiques commerciales, revenant à l'estimation d'origine par défaut
  • Balances incohérentes : Certaines combinaisons pays/produit ont des exportations dépassant la production + importations
  • Décalage temporel : La publication des données FAO est généralement en retard de 1-2 ans par rapport à la date actuelle

Lacunes de couverture

  • Produits de la pêche : Données commerciales FAO limitées pour le poisson ; utilise « origine inconnue » avec transport par défaut
  • Produits transformés : Les aliments transformés complexes peuvent ne pas avoir de correspondances FAO directes
  • Produits régionaux : Les produits comme l'eau locale ou les spécialités utilisent le code « production locale »

Hypothèses du modèle

  • Distribution égale : Lorsque plusieurs origines sont spécifiées par l'utilisateur sans pourcentages, elles reçoivent des parts égales
  • Lieu de cuisine requis : L'estimation d'origine nécessite un pays de consommation connu
  • Limite des 10 premiers pays : Les statistiques FAO par défaut n'utilisent que les 10 premiers pays d'importation pour limiter la complexité du graphe
  • Seuil de couverture de 90 % : Les sources d'importation mineures en dessous de 10 % de part cumulative sont exclues

Contraintes de traitement

  • Exclusion des non-alimentaires : Les produits correspondant à des termes FoodEx2 non alimentaires (EAT-0002, EAT-0000) sont ignorés
  • Gestion des subdivisions : Les noeuds de subdivision sont traités différemment (ignorés par le GFM Origine)
  • Produits combinés : Les origines régionales sur les produits combinés ne sont pas développées pour éviter l'explosion du graphe

Références

  1. Division de statistique de la FAO. Base de données FAOSTAT. Organisation des Nations Unies pour l'alimentation et l'agriculture.

  2. Matrice commerciale détaillée FAO. Données commerciales. Flux commerciaux bilatéraux pour les produits agricoles.

  3. Base de données BACI (Alternative). CEPII BACI. Base de données commerciale internationale avec suivi amélioré de l'origine.

  4. Base de données GADM. Zones administratives mondiales. Limites géographiques et régions administratives.

  5. Classification FoodEx2. EFSA FoodEx2. Système de classification alimentaire de l'Autorité européenne de sécurité des aliments.