Zum Hauptinhalt springen

Herkunft GFM

Das Herkunft Gap Filling Module bestimmt die geografische Herkunft von Lebensmittelprodukten, wenn diese nicht explizit angegeben ist. Unter Verwendung von FAO-Handelsstatistiken (Ernährungs- und Landwirtschaftsorganisation) berechnet es Importanteile und Inlandsproduktionsquoten, um zu schätzen, woher Zutaten am wahrscheinlichsten stammen, basierend auf dem "Küchenstandort" (Verbrauchsland).

Kurzreferenz

EigenschaftBeschreibung
Läuft aufFoodProductFlowNode (ohne Unterteilungen)
AbhängigkeitenLocationGapFillingWorker, AddClientNodesGapFillingWorker, InventoryConnectorGapFillingWorker, MatchProductNameGapFillingWorker, LinkTermToActivityNodeGapFillingWorker, IngredientAmountEstimatorGapFillingWorker
SchlüsseleingabeKüchenstandort (Land), gematchte Produkt-FoodEx2-Begriffe, FAO-Handelsdaten
AusgabeHerkunftsaufteilung mit länderspezifischen Prozentsätzen, Standorteigenschaften
DatenquelleFAO STAT Detaillierte Handelsmatrix, FAO-Produktionsstatistiken

Wann es läuft

Das Modul wird ausgelöst, wenn:

  1. Der Knoten ein FoodProductFlowNode ist (keine Unterteilung)
  2. Keine Herkunft bereits angegeben ist, ODER mehrere Herkünfte verarbeitet werden müssen
  3. Alle abhängigen GFMs abgeschlossen sind
  4. Unteraktivitätsknoten Produktionsmengen berechnet haben

Schlüsselausgabe

Das Modul modifiziert den Berechnungsgraph durch:

  • Herkunftsaufteilung: Erstellen mehrerer Produktflussknoten, jeder mit einem spezifischen Herkunftsland
  • Prozentuale Zuweisung: Verteilung der Mengen basierend auf Importstatistiken und Inlandsproduktion
  • Standorteigenschaften: Setzen von Ländercodes, Koordinaten und GADM-Begriffen

Wissenschaftliche Methodik

Das Herkunftsmodell berechnet die Wahrscheinlichkeitsverteilung, woher ein Produkt stammt, basierend auf:

Gesamtangebot = Inlandsproduktion + Importe - Exporte

Für jedes Herkunftsland wird der Anteil berechnet als:

Inlandsanteil = (Inlandsproduktion - Exporte) / Gesamtangebot
Auslandsanteil = 1 - Inlandsanteil
Importanteil pro Land = Auslandsanteil * (Länderimport / Gesamtimport)

Handelsbilanzmodell

Das Modell verwendet FAO STAT-Daten zur Berechnung von Herkunftsverteilungen:

Inlandsproduktionsanteil

domestic_import_export_total = domestic_production + import_value - export_value

domestic_origin_share = (domestic_production - export_value) / domestic_import_export_total

Wenn die Inlandsproduktion die Exporte übersteigt, wird angenommen, dass ein Teil des Verbrauchs inländisch ist. Der verbleibende Anteil wird den Importländern zugeordnet.

Importländerverteilung

Für ausländische Herkünfte identifiziert das Modul die größten beitragenden Länder:

# Länder, die 90% der Gesamtimporte abdecken
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

Der 90%-Schwellenwert reduziert die Rechenkomplexität bei gleichzeitiger Beibehaltung der Genauigkeit. Für Produkte ohne spezifische FAO-Codes (Standardstatistiken) werden nur die Top-10-Länder verwendet.

Inkonsistenzbehandlung

FAO-Handelsdaten können Inkonsistenzen aufweisen. Das Modul behandelt diese Fälle:

SzenarioBehandlung
Export ≥ Inland + Import"Unbekannte Herkunft" setzen
Keine InlandsproduktionsdatenNur Importstatistiken verwenden
Keine Handelsdaten für ProduktAuf Standard-FAO-Statistiken zurückgreifen
Negatives Gesamtangebot"Unbekannte Herkunft" setzen

Implementierungsdetails

FAO-Datenkonfiguration

Das Modul verwendet zwei primäre FAO-Datensätze:

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"

Extrahierte Datendateien:

  • Trade_DetailedTradeMatrix_E_All_Data_NOFLAG.csv - Import/Export-Mengen nach Länderpaar
  • Production_Crops_Livestock_E_All_Data_NOFLAG.csv - Inlandsproduktion nach Land
  • Trade_DetailedTradeMatrix_E_ItemCodes.csv - FAO-Produktcode-Definitionen

Jahresauswahl

Das Modul unterstützt primäre und sekundäre Jahre für Datenverfügbarkeit:

# Wenn Primärjahresdaten fehlen, auf Sekundärjahr zurückgreifen
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]

Spezielle FAO-Codes

Benutzerdefinierte FAO-Codes behandeln Sonderfälle:

FAO-CodeNameBeschreibung
100000Lokale ProduktionProdukte wie Wasser, die lokal bezogen werden
200000StandardproduktionProdukte ohne spezifische FAO-Zuordnungen
300000Unbekannte FischproduktionFischprodukte ohne Handelsdaten
400000Fehlgeschlagene ProduktionFallback wenn Herkunftsschätzung fehlschlägt

Verarbeitungsmodi

Monoprodukt-Verarbeitung

Für Ein-Zutaten-Produkte:

async def monoproduct_origin_processing(self, calc_graph: "CalcGraph") -> None:
# 1. Prüfen, ob Herkunft bereits angegeben
origins_list = await self.parse_current_origins()

if len(origins_list) == 1:
return # Hat bereits Herkunft

# 2. Küchenstandort aus vererbtem Ländercode holen
kitchen_country_code = get_inherited_country_code(self.node)
kitchen_country_m49_code = iso_to_m49_mapping.get(kitchen_country_code)

# 3. FAO-Code für Produkt über FoodEx2-Zuordnung finden
fao_code_term = linked_fao_code_terms.get(foodex2_term_uids)

# 4. Inlands- + Importanteile berechnen
# 5. Herkunftsaufteilung auf Graph anwenden

Kombiniertes Produkt-Verarbeitung

Für Produkte mit mehreren Zutaten:

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

# Gleichmäßige Verteilung unter angegebenen Herkünften
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)

Graph-Mutation der Herkunftsaufteilung

Wenn mehrere Herkünfte bestimmt werden, wird der Graph modifiziert:

async def apply_origin_split_to_graph(
self,
calc_graph: "CalcGraph",
country_total_percentage_share: dict,
is_origin_from_fao: bool = False,
...
) -> None:
# 1. Kante zwischen Produkt und Unteraktivität entfernen
remove_edge_mutation = RemoveEdgeMutation(...)

# 2. Neuen Herkunfts-Aufteilungs-Aktivitätsknoten hinzufügen
activity_node = FoodProcessingActivityNode.model_construct(...)
calc_graph.apply_mutation(AddNodeMutation(...))

# 3. Herkunftsspezifische Flussknoten erstellen
for country_iso_code, percentage in country_total_percentage_share.items():
# Originalknoten mit neuer Menge duplizieren
calc_graph.apply_mutation(DuplicateNodeMutation(...))

# Proportionale Menge setzen
new_amount = percentage * activity_node.production_amount.value

# Standorteigenschaft setzen
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,
)

Behandlung regionaler Herkünfte

Wenn eine regionale Herkunft (wie "Europa") angegeben ist, wird sie zu Mitgliedsländern erweitert:

# Regionale Begriffe behandeln
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,
))

Datenquellen

FAO STAT-Datenbank

Die primäre Datenquelle ist die FAO-Statistikabteilung:

Handelsdaten: FAO Detaillierte Handelsmatrix

  • Enthält bilaterale Handelsströme zwischen Ländern
  • Mengen in Tonnen
  • Elemente: Importmenge, Exportmenge, Importwert, Exportwert

Produktionsdaten: FAO-Produktionsstatistiken

  • Enthält Inlandsproduktion nach Land und Produkt
  • Mengen in Tonnen

Ländercode-Zuordnungen

Das Modul verwendet mehrere Ländercode-Systeme:

SystemBeschreibungBeispiel
M49UN-Zahlencodes756 (Schweiz)
ISO 3166-1 alpha-2Zwei-Buchstaben-CodesCH
ISO 3166-1 alpha-3Drei-Buchstaben-CodesCHE
GADMGeografische Datenbank-CodesCHE.1.2 (Unterregionen)

FoodEx2-zu-FAO-Zuordnung

Produkte werden über den Glossardienst verknüpft:

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

# Ordnet FoodEx2-Term-UIDs -> FAO-Code-Term-UID zu
for link in fao_glossary_links:
linked_fao_code_terms[frozenset(link.term_uids)] = link.linked_term_uid

Berechnungsbeispiel

Szenario: Herkunft für 1 kg Tomaten bestimmen, die in der Schweiz (CH) verzehrt werden

Schritt 1: Produkt identifizieren

  • FoodEx2-Begriff gematcht: A0DMX (Tomaten)
  • FAO-Code über Glossarlink: 388 (Tomaten)
  • Küchenland: Schweiz (M49: 756)

Schritt 2: Handelsstatistiken abfragen

FAO-Daten für Schweiz und Tomaten (FAO-Code 388):

DatenpunktWert (Tonnen)
Inlandsproduktion45.000
Gesamtimporte75.000
Gesamtexporte5.000

Schritt 3: Anteile berechnen

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%)

Schritt 4: Importanteil verteilen

Top-Importländer für Schweizer Tomatenimporte:

LandImport (Tonnen)% des ImportsEndanteil
Spanien35.00046,7%30,4%
Italien20.00026,7%17,4%
Niederlande10.00013,3%8,7%
Marokko5.0006,7%4,4%
Sonstige5.0006,7%4,4%
Schweiz (Inland)--34,8%

Schritt 5: Graph-Modifikation

Das Modul erstellt Herkunftsaufteilungsknoten:

Original:
Tomaten (1 kg) -> Aktivität -> ...

Nach Herkunft-GFM:
Tomaten (1 kg) -> Herkunfts-Aufteilungs-Aktivität
|-> Tomaten (0,348 kg, CH) -> Aktivität (Kopie) -> ...
|-> Tomaten (0,304 kg, ES) -> Aktivität (Kopie) -> ...
|-> Tomaten (0,174 kg, IT) -> Aktivität (Kopie) -> ...
|-> Tomaten (0,087 kg, NL) -> Aktivität (Kopie) -> ...
|-> Tomaten (0,044 kg, MA) -> Aktivität (Kopie) -> ...
|-> Tomaten (0,044 kg, SONSTIGE) -> Aktivität (Original) -> ...

Jeder herkunftsspezifische Fluss erhält dann nachgelagert entsprechende Transportberechnungen.


Caching-System

Import/Export-Cache

Handelsdaten werden für Leistung im MessagePack-Format gecacht:

class ImportExportCache:
def import_export_value(self, m49_code: int, fao_code: int, year_column: str) -> tuple[int, int]:
"""Gibt Import- und Exportsummen aus Cache zurück."""
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:
"""Gibt relativen Importanteil zwischen Ländern zurück, die Top 90% abdecken."""
return self.import_export_data["top90"][(m49_code, fao_code)][year_column]

Cache-Dateistruktur

temp_data/origin_gfm/
├── domestic_df_{suffix}.hdf5 # Inlandsproduktion (HDF5)
├── import_export_cache_{suffix}.msgpack # Voraggregierte Handelsdaten
├── item_codes_{suffix}.csv # FAO-Produktcodes
├── import_export_{version}.zip # Quellarchiv
└── domestic_{version}.zip # Quellarchiv

Google Drive-Integration

Cache-Dateien werden mit Google Drive für gemeinsamen Zugriff synchronisiert:

def download_file_from_google_drive(self, drive_service: Resource, filename: str, file_id: str):
"""Cache-Datei von Google Drive herunterladen, wenn lokal nicht verfügbar."""

def upload_cache_file_to_google_drive(self, drive_service: Resource, filename: str):
"""Regenerierte Cache-Datei zu Google Drive hochladen."""

Bekannte Einschränkungen

Datenqualität

  • Rotterdam-Effekt: Über Handelszentren (Niederlande, Belgien) reexportierte Waren können als aus diesen Ländern stammend erscheinen statt aus den tatsächlichen Herkünften
  • FAO-Datenlücken: Einige Produkte haben keine Handelsstatistiken und greifen auf Standard-Herkunftsschätzung zurück
  • Inkonsistente Bilanzen: Einige Land/Produkt-Kombinationen haben Exporte, die Produktion + Importe übersteigen
  • Zeitliche Verzögerung: FAO-Datenveröffentlichung liegt typischerweise 1-2 Jahre hinter dem aktuellen Datum

Abdeckungslücken

  • Fischprodukte: Begrenzte FAO-Handelsdaten für Fisch; verwendet "unbekannte Herkunft" mit Standardtransport
  • Verarbeitete Produkte: Komplexe verarbeitete Lebensmittel haben möglicherweise keine direkten FAO-Zuordnungen
  • Regionale Produkte: Produkte wie lokales Wasser oder Spezialitäten verwenden "lokale Produktion"-Code

Modellannahmen

  • Gleichverteilung: Wenn mehrere Herkünfte vom Benutzer ohne Prozentsätze angegeben werden, erhalten sie gleiche Anteile
  • Küchenstandort erforderlich: Herkunftsschätzung erfordert ein bekanntes Verbrauchsland
  • Top-10-Länder-Limit: Standard-FAO-Statistiken verwenden nur Top-10-Importländer zur Begrenzung der Graphkomplexität
  • 90%-Abdeckungsschwelle: Kleinere Importquellen unter 10% kumulativem Anteil werden ausgeschlossen

Verarbeitungsbeschränkungen

  • Nicht-Lebensmittel-Ausschluss: Produkte, die mit Nicht-Lebensmittel-FoodEx2-Begriffen (EAT-0002, EAT-0000) gematcht werden, werden übersprungen
  • Unterteilungsbehandlung: Unterteilungsknoten werden anders verarbeitet (vom Herkunft-GFM übersprungen)
  • Kombinierte Produkte: Regionale Herkünfte bei kombinierten Produkten werden nicht erweitert, um Graph-Explosion zu vermeiden

Referenzen

  1. FAO-Statistikabteilung. FAOSTAT-Datenbank. Ernährungs- und Landwirtschaftsorganisation der Vereinten Nationen.

  2. FAO Detaillierte Handelsmatrix. Handelsdaten. Bilaterale Handelsströme für Agrarrohstoffe.

  3. BACI-Datenbank (Alternative). CEPII BACI. Internationale Handelsdatenbank mit verbesserter Herkunftsverfolgung.

  4. GADM-Datenbank. Global Administrative Areas. Geografische Grenzen und Verwaltungsregionen.

  5. FoodEx2-Klassifikation. EFSA FoodEx2. Europäisches Lebensmittelklassifikationssystem der Europäischen Behörde für Lebensmittelsicherheit.