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
| Eigenschaft | Beschreibung |
|---|---|
| Läuft auf | FoodProductFlowNode (ohne Unterteilungen) |
| Abhängigkeiten | LocationGapFillingWorker, AddClientNodesGapFillingWorker, InventoryConnectorGapFillingWorker, MatchProductNameGapFillingWorker, LinkTermToActivityNodeGapFillingWorker, IngredientAmountEstimatorGapFillingWorker |
| Schlüsseleingabe | Küchenstandort (Land), gematchte Produkt-FoodEx2-Begriffe, FAO-Handelsdaten |
| Ausgabe | Herkunftsaufteilung mit länderspezifischen Prozentsätzen, Standorteigenschaften |
| Datenquelle | FAO STAT Detaillierte Handelsmatrix, FAO-Produktionsstatistiken |
Wann es läuft
Das Modul wird ausgelöst, wenn:
- Der Knoten ein
FoodProductFlowNodeist (keine Unterteilung) - Keine Herkunft bereits angegeben ist, ODER mehrere Herkünfte verarbeitet werden müssen
- Alle abhängigen GFMs abgeschlossen sind
- 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:
| Szenario | Behandlung |
|---|---|
| Export ≥ Inland + Import | "Unbekannte Herkunft" setzen |
| Keine Inlandsproduktionsdaten | Nur Importstatistiken verwenden |
| Keine Handelsdaten für Produkt | Auf 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änderpaarProduction_Crops_Livestock_E_All_Data_NOFLAG.csv- Inlandsproduktion nach LandTrade_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-Code | Name | Beschreibung |
|---|---|---|
100000 | Lokale Produktion | Produkte wie Wasser, die lokal bezogen werden |
200000 | Standardproduktion | Produkte ohne spezifische FAO-Zuordnungen |
300000 | Unbekannte Fischproduktion | Fischprodukte ohne Handelsdaten |
400000 | Fehlgeschlagene Produktion | Fallback 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:
| System | Beschreibung | Beispiel |
|---|---|---|
| M49 | UN-Zahlencodes | 756 (Schweiz) |
| ISO 3166-1 alpha-2 | Zwei-Buchstaben-Codes | CH |
| ISO 3166-1 alpha-3 | Drei-Buchstaben-Codes | CHE |
| GADM | Geografische Datenbank-Codes | CHE.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):
| Datenpunkt | Wert (Tonnen) |
|---|---|
| Inlandsproduktion | 45.000 |
| Gesamtimporte | 75.000 |
| Gesamtexporte | 5.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:
| Land | Import (Tonnen) | % des Imports | Endanteil |
|---|---|---|---|
| Spanien | 35.000 | 46,7% | 30,4% |
| Italien | 20.000 | 26,7% | 17,4% |
| Niederlande | 10.000 | 13,3% | 8,7% |
| Marokko | 5.000 | 6,7% | 4,4% |
| Sonstige | 5.000 | 6,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
-
FAO-Statistikabteilung. FAOSTAT-Datenbank. Ernährungs- und Landwirtschaftsorganisation der Vereinten Nationen.
-
FAO Detaillierte Handelsmatrix. Handelsdaten. Bilaterale Handelsströme für Agrarrohstoffe.
-
BACI-Datenbank (Alternative). CEPII BACI. Internationale Handelsdatenbank mit verbesserter Herkunftsverfolgung.
-
GADM-Datenbank. Global Administrative Areas. Geografische Grenzen und Verwaltungsregionen.
-
FoodEx2-Klassifikation. EFSA FoodEx2. Europäisches Lebensmittelklassifikationssystem der Europäischen Behörde für Lebensmittelsicherheit.