Die fünf Tabellen, ihre zwei Quellen, die tatsächliche Abdeckung jeder Anreicherungsspalte und wie Sie das Ergebnis abfragen.
Fünf Tabellen, exportiert als pipe-getrenntes UTF-8-CSV und Apache Parquet mit identischen Spalten:
position_index.Zwei Quellen, ein dokumentierter Join. URL, Download-Zeitstempel und SHA-256 jeder Quelldatei sind in der mitgelieferten build_report.json festgehalten.
categories_tags, rohe Zutatenlisten) unter der Open Database License (ODbL).NULL, niemals ein Platzhalter.is_common_allergen. Die US-amerikanische FDA hat ihre MoCRA-Liste der Duftstoffallergene noch nicht veröffentlicht, daher enthält dieser Datensatz keine US-Markierung.rating_source und sind überall sonst NULL.Auf zwei Arten angegeben, weil sich beide Werte um eine Größenordnung unterscheiden und die Angabe nur eines davon irreführend wäre:
functions: 10,9% der eindeutigen Namen; 81,5% der Vorkommen auf Etiketten.cas_number: 8,3% der eindeutigen Namen; 76,4% der Vorkommen auf Etiketten.chemical_description: 8,5% der eindeutigen Namen, 3.965 eindeutige Werte.Ein Etikettenkorpus enthält weit mehr eindeutige Zeichenketten — botanische Varianten, mehrsprachige Schreibweisen, Marketing-Begriffe — als jedes regulatorische Verzeichnis führt; deshalb ist die erste Zahl jedes Paares niedrig. Die Positionen auf einem realen Etikett werden jedoch ganz überwiegend von Inhaltsstoffen belegt, die CosIng abdeckt; deshalb ist die zweite hoch. Keine der beiden Zahlen allein beschreibt die Daten.
Rohe Etikett-Tokens werden durch ein explizites, geordnetes Verfahren kanonischen INCI-Namen zugeordnet — Entity-Dekodierung, Entfernen von Zeichen, Zusammenfassen von Leerraum, Großschreibung, dann exakter Treffer, bereinigter Treffer, eine kleine belegte Tippfehlertabelle, Entfernen von Prozentangaben, Entfernen von Klammern, eine Synonymtabelle, Aufteilen von Schrägstrich-Varianten und Aufteilen zusammengeschriebener Tokens nur dann, wenn beide Teile im Verzeichnis gefunden werden. Was an allen Schritten scheitert, bleibt wörtlich erhalten und wird als unresolved erfasst, niemals geraten. Die verwendete Methode wird pro Zeile in ingredient_name_map.method gespeichert, sodass jede Entscheidung prüfbar und umkehrbar ist.
import pyarrow.parquet as pq
ingredients = pq.read_table('ingredients.parquet').to_pandas()
links = pq.read_table('product_ingredients.parquet').to_pandas()
# Coverage the way it actually matters: weighted by label occurrence.
merged = links.merge(ingredients, on='ingredient_id', how='left')
print(merged['functions'].notna().mean())
# EU Annex III fragrance allergens
allergens = ingredients[ingredients['is_common_allergen'] == 1]
import duckdb
duckdb.query("""
SELECT f.function, COUNT(*) AS label_slots
FROM 'product_ingredients.parquet' pi
JOIN 'ingredients.parquet' i USING (ingredient_id)
CROSS JOIN UNNEST(string_split(i.functions, ';')) AS f(function)
WHERE i.functions IS NOT NULL
GROUP BY 1 ORDER BY 2 DESC LIMIT 10;
""").show()
# Audit the canonicalisation itself
duckdb.query("""
SELECT method, COUNT(*) AS n
FROM 'ingredient_name_map.parquet'
GROUP BY 1 ORDER BY 2 DESC;
""").show()