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 sur | FoodProductFlowNode (hors subdivisions) |
| Dépendances | LocationGapFillingWorker, AddClientNodesGapFillingWorker, InventoryConnectorGapFillingWorker, MatchProductNameGapFillingWorker, LinkTermToActivityNodeGapFillingWorker, IngredientAmountEstimatorGapFillingWorker |
| Entrée clé | Lieu de cuisine (pays), termes FoodEx2 correspondants, données commerciales FAO |
| Sortie | Répartition des origines avec pourcentages par pays, propriétés de localisation |
| Source de données | Matrice commerciale détaillée FAO STAT, Statistiques de production FAO |
Conditions d'exécution
Le module se déclenche lorsque :
- Le noeud est un
FoodProductFlowNode(pas une subdivision) - Aucune origine n'est déjà spécifiée, OU plusieurs origines doivent être traitées
- Tous les GFM dépendants ont terminé leur exécution
- 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énario | Traitement |
|---|---|
| Export ≥ Production nationale + Import | Définir « origine inconnue » |
| Pas de données de production nationale | Utiliser uniquement les statistiques d'importation |
| Pas de données commerciales pour le produit | Revenir aux statistiques FAO par défaut |
| Offre totale négative | Dé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 paysProduction_Crops_Livestock_E_All_Data_NOFLAG.csv- Production nationale par paysTrade_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 FAO | Nom | Description |
|---|---|---|
100000 | Production locale | Produits d'approvisionnement local comme l'eau |
200000 | Production par défaut | Produits sans correspondance FAO spécifique |
300000 | Production de poisson inconnue | Produits de la pêche sans données commerciales |
400000 | Production en échec | Repli 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ème | Description | Exemple |
|---|---|---|
| M49 | Codes numériques ONU | 756 (Suisse) |
| ISO 3166-1 alpha-2 | Codes à deux lettres | CH |
| ISO 3166-1 alpha-3 | Codes à trois lettres | CHE |
| GADM | Codes de base de données géographiques | CHE.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ées | Valeur (tonnes) |
|---|---|
| Production nationale | 45 000 |
| Importations totales | 75 000 |
| Exportations totales | 5 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 :
| Pays | Importation (tonnes) | % d'importation | Part finale |
|---|---|---|---|
| Espagne | 35 000 | 46,7 % | 30,4 % |
| Italie | 20 000 | 26,7 % | 17,4 % |
| Pays-Bas | 10 000 | 13,3 % | 8,7 % |
| Maroc | 5 000 | 6,7 % | 4,4 % |
| Autres | 5 000 | 6,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
-
Division de statistique de la FAO. Base de données FAOSTAT. Organisation des Nations Unies pour l'alimentation et l'agriculture.
-
Matrice commerciale détaillée FAO. Données commerciales. Flux commerciaux bilatéraux pour les produits agricoles.
-
Base de données BACI (Alternative). CEPII BACI. Base de données commerciale internationale avec suivi amélioré de l'origine.
-
Base de données GADM. Zones administratives mondiales. Limites géographiques et régions administratives.
-
Classification FoodEx2. EFSA FoodEx2. Système de classification alimentaire de l'Autorité européenne de sécurité des aliments.