Metadaten und Meta-Maps¶
KI-gestützte Übersetzung - mehr erfahren & Verbesserungen vorschlagen
In jeder wissenschaftlichen Analyse arbeiten wir selten nur mit den rohen Datendateien. Jede Datei bringt ihre eigenen zusätzlichen Informationen mit: was sie ist, woher sie stammt und was sie besonders macht. Diese zusätzlichen Informationen nennen wir Metadaten.
Metadaten sind Daten, die andere Daten beschreiben. Metadaten erfassen wichtige Details über Dateien und experimentelle Bedingungen und helfen dabei, Analysen an die einzigartigen Eigenschaften jedes Datensatzes anzupassen.
Stell dir das wie einen Bibliothekskatalog vor: Während Bücher den eigentlichen Inhalt enthalten (Rohdaten), liefern die Katalogkarten wesentliche Informationen über jedes Buch – wann es veröffentlicht wurde, wer es geschrieben hat, wo es zu finden ist (Metadaten). In Nextflow-Pipelines können Metadaten verwendet werden, um:
- Dateispezifische Informationen durch den gesamten Workflow zu verfolgen
- Prozesse basierend auf Dateieigenschaften zu konfigurieren
- Verwandte Dateien für gemeinsame Analysen zu gruppieren
Lernziele¶
In dieser Side Quest erkunden wir, wie man Metadaten in Workflows verarbeitet. Ausgehend von einem einfachen Datenblatt (in der Bioinformatik oft als Samplesheet bezeichnet), das grundlegende Dateiinformationen enthält, lernst du:
- Datei-Metadaten aus CSV-Dateien zu lesen und zu verarbeiten
- Zu verstehen, warum die „Meta-Map + Datendatei"-Schnittstelle eine weit verbreitete Konvention ist
- Neue Metadatenfelder während der Workflow-Ausführung hinzuzufügen
- Metadaten zur Anpassung des Prozessverhaltens und zur Organisation von Ausgaben zu nutzen
Diese Fähigkeiten helfen dir, robustere und flexiblere Pipelines zu entwickeln, die komplexe Dateibeziehungen und Verarbeitungsanforderungen bewältigen können.
Voraussetzungen¶
Bevor du diese Side Quest angehst, solltest du:
- Das Tutorial Hello Nextflow oder einen gleichwertigen Einsteigerkurs abgeschlossen haben.
- Mit grundlegenden Nextflow-Konzepten und -Mechanismen (Prozesse, Kanäle, Operatoren) vertraut sein.
0. Erste Schritte¶
Öffne den Training-Codespace¶
Falls du das noch nicht getan hast, öffne die Trainingsumgebung wie in der Umgebung einrichten-Anleitung beschrieben.
Wechsle in das Projektverzeichnis¶
Wechseln wir in das Verzeichnis, in dem sich die Dateien für dieses Tutorial befinden.
Du kannst VSCode so einstellen, dass es sich auf dieses Verzeichnis konzentriert:
Der Editor öffnet sich mit dem Projektverzeichnis im Fokus.
Schau dir die Materialien an¶
Du findest eine Haupt-Workflow-Datei und ein data-Verzeichnis mit einem Datenblatt und einigen Datendateien.
Verzeichnisinhalt
Der Workflow in der main.nf-Datei ist ein Grundgerüst, das du schrittweise zu einem vollständig funktionierenden Workflow ausbauen wirst.
Das Datenblatt listet die Pfade zu den Datendateien und einige zugehörige Metadaten auf, gegliedert in 3 Spalten:
id: selbsterklärend, eine ID für die Dateicharacter: ein Charaktername, den wir später verwenden, um verschiedene Figuren zu zeichnendata: Pfade zu.txt-Dateien, die Begrüßungen in verschiedenen Sprachen enthalten
id,character,recording
sampleA,squirrel,/workspaces/training/side-quests/metadata/data/bonjour.txt
sampleB,tux,/workspaces/training/side-quests/metadata/data/guten_tag.txt
sampleC,sheep,/workspaces/training/side-quests/metadata/data/hallo.txt
sampleD,turkey,/workspaces/training/side-quests/metadata/data/hello.txt
sampleE,stegosaurus,/workspaces/training/side-quests/metadata/data/hola.txt
sampleF,moose,/workspaces/training/side-quests/metadata/data/salut.txt
sampleG,turtle,/workspaces/training/side-quests/metadata/data/ciao.txt
Jede Datendatei enthält einen Begrüßungstext in einer von fünf Sprachen (fr: Französisch, de: Deutsch, es: Spanisch, it: Italienisch, en: Englisch).
Wir werden ein Tool namens COWPY verwenden, um ASCII-Kunst jeder Figur zu generieren, die ihre aufgezeichnete Begrüßung spricht.
Was macht COWPY?
COWPY ist ein Befehlszeilen-Tool, das ASCII-Kunst generiert, um beliebige Texteingaben auf unterhaltsame Weise darzustellen.
Es ist eine Python-Implementierung des klassischen cowsay-Tools von Tony Monroe.
______________________________________________________
< Hello Nextflow >
------------------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Optional kannst du eine Figur (oder 'cowacter') auswählen, die anstelle der Standard-Kuh verwendet wird.
Außerdem verwenden wir ein Sprachanalyse-Tool namens langid, um zu erkennen, welche Sprache jede Figur spricht, und die Ausgaben der Pipeline entsprechend zu organisieren.
Schau dir die Aufgabe an¶
Deine Aufgabe ist es, einen Nextflow-Workflow zu schreiben, der:
- ASCII-Kunst jeder Figur generiert
- Ausgaben nach Sprachfamilie organisiert (Germanisch vs. Romanisch)
Dies ist ein typisches Workflow-Muster, bei dem dateispezifische Metadaten Verarbeitungsentscheidungen steuern – genau die Art von Problem, die Meta-Maps elegant lösen.
Bereitschafts-Checkliste¶
Bereit zum Eintauchen?
- Ich verstehe das Ziel dieses Kurses und seine Voraussetzungen
- Mein Codespace läuft
- Ich habe mein Arbeitsverzeichnis entsprechend eingestellt
- Ich verstehe die Aufgabe
Wenn du alle Punkte abhaken kannst, kann es losgehen.
1. Grundlegende Möglichkeiten zum Laden und Verwenden von Metadaten¶
Öffne die main.nf-Workflow-Datei, um das Workflow-Grundgerüst zu untersuchen, das wir dir als Ausgangspunkt geben.
| main.nf | |
|---|---|
Der splitCsv-Operator liest jede Zeile der Datei als Element im Kanal.
Das ist derselbe Ansatz, den wir in Hello Nextflow, unserem Einsteigerkurs, zum Laden von CSV-Daten verwenden.
Schau dir diesen Abschnitt an, falls du eine Auffrischung brauchst.
Mit header: true wird die erste Zeile als Spaltenüberschriften behandelt, sodass jedes Element eine Map aus Schlüssel-Wert-Paaren wird, die nach Spaltennamen geordnet sind.
Da wir noch keine Prozesse auf den Daten ausführen, sind die publish- und output-Blöcke nur Platzhalter.
1.1. Den Workflow ausführen¶
Führe den Workflow aus, um zu sehen, wie der Kanalinhalt strukturiert ist, sobald alles geladen ist:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [exotic_albattani] DSL2 - revision: c0d03cec83
[id:sampleA, character:squirrel, recording:/workspaces/training/side-quests/metadata/data/bonjour.txt]
[id:sampleB, character:tux, recording:/workspaces/training/side-quests/metadata/data/guten_tag.txt]
[id:sampleC, character:sheep, recording:/workspaces/training/side-quests/metadata/data/hallo.txt]
[id:sampleD, character:turkey, recording:/workspaces/training/side-quests/metadata/data/hello.txt]
[id:sampleE, character:stegosaurus, recording:/workspaces/training/side-quests/metadata/data/hola.txt]
[id:sampleF, character:moose, recording:/workspaces/training/side-quests/metadata/data/salut.txt]
[id:sampleG, character:turtle, recording:/workspaces/training/side-quests/metadata/data/ciao.txt]
Wir sehen, dass der Operator für jede Zeile in der CSV-Datei eine Map aus Schlüssel-Wert-Paaren erstellt hat, wobei die Spaltenüberschriften als Schlüssel für die entsprechenden Werte dienen.
Jeder Map-Eintrag entspricht einer Spalte in unserem Datenblatt:
idcharacterrecording
Das macht es einfach, auf bestimmte Felder jeder Zeile zuzugreifen.
Zum Beispiel könnten wir mit id auf die Datei-ID oder mit recording auf den Pfad der txt-Datei zugreifen.
(Optional) Mehr über Groovy-Maps
In Groovy, der Programmiersprache, auf der Nextflow aufbaut, ist eine Map eine Schlüssel-Wert-Datenstruktur, ähnlich wie Dictionaries in Python, Objekte in JavaScript oder Hashes in Ruby.
Hier ist ein ausführbares Skript, das zeigt, wie du eine Map definieren und auf ihren Inhalt zugreifen kannst:
#!/usr/bin/env nextflow
// Eine einfache Map erstellen
def my_map = [id:'sampleA', character:'squirrel']
// Die gesamte Map ausgeben
println "map: ${my_map}"
// Auf einzelne Werte mit Punktnotation zugreifen
println "id: ${my_map.id}"
println "character: ${my_map.character}"
Obwohl es keinen richtigen workflow-Block hat, kann Nextflow dies wie einen Workflow ausführen:
Und das kannst du in der Ausgabe erwarten:
1.2. Ein bestimmtes Feld mit map auswählen¶
Wir verwenden den map-Operator, um über jedes Element in einem Kanal zu iterieren und gezielt das character-Feld auszuwählen, auf das wir per Punktnotation zugreifen können.
1.2.1. Die map-Operation hinzufügen¶
Um auf die character-Spalte zuzugreifen, füge die map-Operation vor der .view()-Operation wie folgt hinzu:
Diese Art, auf ein bestimmtes Feld zuzugreifen, wird in diesem Abschnitt von Hello Nextflow ausführlicher erklärt, falls du eine Auffrischung brauchst.
1.2.2. Den Workflow ausführen¶
Führe den Workflow aus, um zu überprüfen, ob du die extrahierten Charakternamen siehst.
Befehlsausgabe
Das zeigt, dass wir auf die Werte der character-Spalte für jede Zeile zugreifen können.
Jetzt machen wir etwas mit diesen Daten: Wir verwenden die Felder character und recording zusammen, um mit COWPY ASCII-Kunst zu generieren.
1.3. Sub-Kanäle mit multiMap ausgeben¶
Wir stellen dir ein vorgefertigtes COWPY-Prozessmodul zur Verfügung. Zuerst musst du die Eingabeanforderungen des Prozesses untersuchen.
Du kannst die Datei öffnen, um den Prozess zu sehen:
| modules/cowpy.nf | |
|---|---|
Wie du siehst, erwartet der Prozess zwei separate Eingaben: eine Aufnahmedatei und einen Charakternamen. Wir haben Werte für beides, aber sie sind derzeit in jedem Element des Kanals gebündelt.
Eine Möglichkeit, mehrere Felder in separate Kanäle zu extrahieren, ist der multiMap-Operator, der einen Kanal in mehrere benannte Sub-Kanäle in einer einzigen Operation aufteilt.
1.3.1. Die multiMap-Operation hinzufügen¶
Ersetze die map-Operation durch multiMap:
Der multiMap-Block definiert zwei benannte Sub-Kanäle (file und character) aus jeder Zeile, auf die wir als ch_datasheet.file und ch_datasheet.character zugreifen können.
1.3.2. COWPY auf den Sub-Kanälen aufrufen¶
Füge nun das COWPY-Prozessmodul ein und übergib jeden Sub-Kanal als separates Argument:
| main.nf | |
|---|---|
So können wir die zwei Felder separat übergeben, wie es COWPY erfordert.
1.3.3. Die Ausgabeveröffentlichung einrichten¶
Füge abschließend die Ausgabe von COWPY zum publish:-Block hinzu:
So können wir die vom Workflow produzierten Ausgaben leicht einsehen.
1.3.4. Den Workflow ausführen¶
Führe den Workflow aus, um zu überprüfen, dass COWPY mit den bereitgestellten Eingaben läuft:
Befehlsausgabe
Wie du siehst, hat COWPY jede Datei mit der richtigen Figur verarbeitet.
Results-Verzeichnis Inhalt
Inhalt von results/cowpy-guten_tag.txt
Dieser Ansatz funktioniert, hat aber eine Einschränkung: Wir mussten den Kanal in zwei separate Sub-Kanäle aufteilen. Wenn wir mehr Felder an den Prozess übergeben wollten, müssten wir sie in weitere Sub-Kanäle aufteilen. Das kann schnell unübersichtlich werden.
Gute Neuigkeiten: Es gibt einen einfacheren Weg.
1.4. Alles als einzelne Eingabe an den Prozess übergeben¶
Anstatt die Felder in separate Kanäle aufzuteilen, können wir den Prozess so aktualisieren, dass er alle Eingaben als einzelnes Tupel empfängt. Das vereinfacht den Prozessaufruf.
1.4.1. Den COWPY-Prozess aktualisieren¶
Aktualisiere COWPY, um ein Tupel zu akzeptieren, das den drei Elementen jeder Zeile entspricht:
| modules/cowpy.nf | |
|---|---|
| modules/cowpy.nf | |
|---|---|
Jetzt erwartet der Prozess nur noch eine Eingabe, die alles enthält, was wir ihm geben möchten.
1.4.2. map() verwenden, um das Eingabe-Tupel zu erstellen¶
Wir müssen noch eine Mapping-Operation verwenden, um die Elemente aufzuzählen, die wir im Tupel an den Prozess übergeben möchten:
Du fragst dich vielleicht, warum wir nicht einfach die gesamte Groovy-Map aus splitCsv direkt übergeben können.
Der Grund: Wir müssen Nextflow explizit mitteilen, dass die Aufnahmedatei als Pfad behandelt werden muss (d.h. sie muss korrekt bereitgestellt werden).
Das geschieht auf der Ebene der Eingabeschnittstelle von COWPY, wo das recording-Element explizit als path deklariert ist.
1.4.3. Den Prozessaufruf aktualisieren¶
Ersetzen wir abschließend die zwei separaten Eingaben im Prozessaufruf durch das einzelne Tupel, das wir gerade erstellt haben:
Das vereinfacht den Prozessaufruf etwas.
1.4.4. Den Workflow ausführen¶
Führe den Workflow aus, um zu überprüfen, dass COWPY die Daten weiterhin korrekt verarbeitet:
Befehlsausgabe
Die Ausgabe sind dieselben sieben cowpy-*.txt-Dateien wie zuvor, jetzt mit einem einfacheren Aufruf von COWPY.
Results-Verzeichnis Inhalt
Inhalt von results/cowpy-guten_tag.txt
Das ist eine leichte Verbesserung gegenüber dem multiMap-Ansatz.
Aber wir mussten die ursprüngliche Groovy-Map immer noch entpacken, um das Eingabe-Tupel zu erstellen, und es gibt eine enge Kopplung zwischen dem Prozess und dem Datenblatt: Die COWPY-Eingabedefinition referenziert jetzt direkt die Spaltennamen id, character und recording.
Wenn eine andere Person ein anders strukturiertes Datenblatt verwendet – mit zusätzlichen Spalten oder Spalten in einer anderen Reihenfolge – funktioniert dieser Prozess ohne Änderungen nicht. Das macht den Prozess fragil, weil seine Eingabestruktur an die genaue Zusammensetzung des Datenblatts gebunden ist.
Um das zu lösen, brauchen wir eine Möglichkeit, alle Metadaten als Paket zu übergeben, ohne ihre genaue Struktur in die Prozessschnittstelle fest einzuprogrammieren.
1.5. Eine Meta-Map + Datei-Schnittstelle verwenden¶
Die Lösung besteht darin, zwei verschiedene Aspekte im Kanal zu trennen: die Metadaten über eine Probe und die Datendatei selbst. Indem wir alle Metadaten in einer einzigen Map bündeln – der „Meta-Map" – erhalten wir ein konsistentes Tupel aus zwei Elementen, unabhängig davon, wie viele Metadatenspalten das Datenblatt enthält:
Das Hinzufügen oder Entfernen von Spalten im Datenblatt ändert den Inhalt von meta, aber die Tupelform [meta, file] bleibt konstant.
Prozesse, die diese Struktur akzeptieren, müssen nicht wissen oder sich darum kümmern, wie viele Metadatenfelder vorhanden sind.
1.5.1. Den Tupelinhalt in eine Meta-Map umstrukturieren¶
Lass uns die map-Operation umstrukturieren, um ein [meta, file]-Tupel zu erzeugen:
| main.nf | |
|---|---|
Du wirst bemerken, dass wir auch eine view()-Anweisung hinzugefügt, den COWPY-Aufruf auskommentiert und COWPY.out durch channel.empty() ersetzt haben, weil die Prozess-Eingabedefinition noch nicht zur neuen Struktur passt.
1.5.2. Den Workflow ausführen, um den umstrukturierten Inhalt zu prüfen¶
Führe den Workflow aus, um die neue Kanalform zu sehen:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [lethal_booth] DSL2 - revision: 0d8f844c07
[[id:sampleA, character:squirrel], /workspaces/training/side-quests/metadata/data/bonjour.txt]
[[id:sampleB, character:tux], /workspaces/training/side-quests/metadata/data/guten_tag.txt]
[[id:sampleC, character:sheep], /workspaces/training/side-quests/metadata/data/hallo.txt]
[[id:sampleD, character:turkey], /workspaces/training/side-quests/metadata/data/hello.txt]
[[id:sampleE, character:stegosaurus], /workspaces/training/side-quests/metadata/data/hola.txt]
[[id:sampleF, character:moose], /workspaces/training/side-quests/metadata/data/salut.txt]
[[id:sampleG, character:turtle], /workspaces/training/side-quests/metadata/data/ciao.txt]
Jedes Element im Kanal ist jetzt ein Tupel aus zwei Elementen: zuerst die Meta-Map, dann die Datei.
[
[id:sampleA, character:squirrel],
/workspaces/training/side-quests/metadata/data/bonjour.txt
]
Wenn wir später eine language-Spalte zum Datenblatt hinzufügen, wird sie als meta.language verfügbar, ohne dass Änderungen an der Prozess-Eingabedefinition erforderlich sind.
1.5.3. Den COWPY-Prozess aktualisieren, um die Meta-Map zu verwenden¶
Aktualisiere COWPY, um die [meta, file]-Tupelstruktur zu akzeptieren:
| modules/cowpy.nf | |
|---|---|
| modules/cowpy.nf | |
|---|---|
Im Script-Block greift meta.character auf das character-Feld der Meta-Map zu.
Auf jedes Feld in der Meta-Map kann auf dieselbe Weise zugegriffen werden.
1.5.4. Den Prozessaufruf aktualisieren¶
Stelle den COWPY-Aufruf wieder her und verbinde seine Ausgabe für die Veröffentlichung:
| main.nf | |
|---|---|
Wir haben auch die Ausgabeveröffentlichung wiederhergestellt.
1.5.5. Den Workflow ausführen¶
Führe den Workflow aus, um zu überprüfen, dass alles funktioniert:
Befehlsausgabe
Das Ergebnisverzeichnis enthält jetzt die ASCII-Kunst-Dateien.
Verzeichnisinhalt
Inhalt von results/cowpy-guten_tag.txt
Der Prozess empfängt jetzt alle Metadaten als Paket über meta, verwendet was er braucht (meta.character) und ignoriert den Rest.
Das ist die Standardschnittstelle, die alle nf-core-Module verwenden.
Das tuple val(meta), path(file)-Muster erscheint durchgängig in der nf-core-Modulbibliothek, weshalb Workflows, die diese Konvention übernehmen, nf-core-Module mit minimalem Aufwand einbinden können.
Fazit¶
In diesem Abschnitt hast du gelernt:
- Wie man Datenblätter einliest: Mit
splitCsvCSV-Dateien mit Kopfzeileninformationen lesen - Warum die Meta-Map-Konvention existiert: Das Trennen von Metadaten und Datendateien in
[meta, file]-Tupel hält die Kanalstruktur stabil, wenn sich das Datenblatt weiterentwickelt - Wie man Meta-Map-Felder innerhalb eines Prozesses verwendet: Auf jedes Feld in der Meta-Map kann per Punktnotation im Script-Block zugegriffen werden
2. Weitere Metadaten-Manipulationen¶
Jetzt, wo die Meta-Map-Schnittstelle eingerichtet ist, können wir sie anreichern, während Daten durch die Pipeline fließen.
Wir werden ein Tool namens langid verwenden, um die Sprache in jeder Aufnahmedatei zu identifizieren.
Bei einem Textausschnitt gibt es eine Sprachvorhersage und einen Wahrscheinlichkeitswert nach stdout aus.
2.1. Einen Spracherkennungsschritt hinzufügen¶
Wir stellen dir ein vorgefertigtes Prozessmodul namens IDENTIFY_LANGUAGE zur Verfügung, das das langid-Tool kapselt.
Öffne die Moduldatei, um den Code zu untersuchen:
Die Eingabedefinition verwendet dieselbe tuple val(meta), path(file)-Struktur, die wir in Abschnitt 1 aufgebaut haben, sodass ch_datasheet direkt in diesen Prozess einfließen kann.
Die Ausgabe fügt stdout als drittes Element hinzu: Das erfasst die Sprachvorhersage, die langid auf der Konsole ausgibt.
Der sed-Befehl entfernt den Wahrscheinlichkeitswert und den abschließenden Zeilenumbruch und lässt nur den zweistelligen Sprachcode übrig.
2.1.1. Einen Aufruf von IDENTIFY_LANGUAGE hinzufügen¶
Füge das IDENTIFY_LANGUAGE-Prozessmodul ein und rufe es auf dem Datenblatt-Kanal auf:
Die Hauptausgabe dieses Prozesses ist nur ein String, daher gibt es keine Ausgabedateien zu veröffentlichen.
Stattdessen verwenden wir IDENTIFY_LANGUAGE.out.view(), um die Ergebnisse der Operation anzuzeigen.
2.1.2. Den Workflow ausführen¶
Führe den Workflow aus, um die Sprachidentifikation zu erzeugen. Verwende -resume, um die COWPY-Aufgaben nicht erneut auszuführen:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [voluminous_mcnulty] DSL2 - revision: f9bcfebabb
executor > local (14)
[5d/dffd4e] COWPY (7) [100%] 7 of 7, cached: 7 ✔
[4e/f722fe] IDENTIFY_LANGUAGE (7) [100%] 7 of 7 ✔
[[id:sampleA, character:squirrel], /workspaces/training/side-quests/metadata/work/eb/f7148ebdd898fbe1136bec6a714acb/bonjour.txt, fr]
[[id:sampleB, character:tux], /workspaces/training/side-quests/metadata/work/16/71d72410952c22cd0086d9bca03680/guten_tag.txt, de]
[[id:sampleD, character:turkey], /workspaces/training/side-quests/metadata/work/c4/b7562adddc1cc0b7d414ec45d436eb/hello.txt, en]
[[id:sampleC, character:sheep], /workspaces/training/side-quests/metadata/work/ea/04f5d979429e4455e14b9242fb3b45/hallo.txt, de]
[[id:sampleF, character:moose], /workspaces/training/side-quests/metadata/work/5a/6c2b84bf8fadb98e28e216426be079/salut.txt, fr]
[[id:sampleE, character:stegosaurus], /workspaces/training/side-quests/metadata/work/af/ee7c69bcab891c40d0529305f6b9e7/hola.txt, es]
[[id:sampleG, character:turtle], /workspaces/training/side-quests/metadata/work/4e/f722fe47271ba7ebcd69afa42964ca/ciao.txt, it]
Wir haben jetzt eine Sprachvorhersage für jede Datei im Datensatz.
Das Ausgabe-Tupel besteht aus [meta, file, lang_id], d.h. die Meta-Map und die Datei werden zusammen mit dem neuen Ergebnis weitergegeben.
Hinweis
Dieses Muster, die Meta-Map mit den Ergebnissen verknüpft zu halten, macht es einfacher, Ergebnisse später kanalübergreifend zu verknüpfen. Man kann sich nicht auf die Reihenfolge der Elemente in Kanälen verlassen, um Daten korrekt zuzuordnen. Stattdessen müssen Schlüssel verwendet werden. Meta-Maps bieten dafür eine ideale Struktur.
Dieser Anwendungsfall wird ausführlich in der Side Quest Splitting & Grouping erkundet.
2.2. Metadaten mit Prozessausgaben anreichern¶
Die Sprachvorhersage ist selbst eine Form von Metadaten über den Inhalt der Datei. Anstatt sie als separates Element zu behalten, falten wir sie zurück in die Meta-Map.
2.2.1. Eine neue und erweiterte Meta-Map erstellen¶
Wir können eine neue Meta-Map erstellen, die die ursprüngliche ersetzt, indem wir den Groovy-+-Operator verwenden:
Das Herzstück dieser Operation ist meta + [lang: lang_id].
Dieser Code erstellt eine temporäre Map mit einem einzigen Schlüssel-Wert-Paar, das den Sprachcode enthält ([lang: lang_id]), und verwendet dann den Groovy-+-Operator, um sie mit der ursprünglichen meta-Map zu kombinieren, die die vorhandenen Metadaten enthält. Das ergibt eine neue, erweiterte Meta-Map.
Eine ausführlichere Erklärung findest du im Kasten unten.
Erstellung der neuen Meta-Map mit dem +-Operator
Zunächst musst du wissen, dass wir den Inhalt zweier Maps mit dem Groovy-Operator + zusammenführen können.
Angenommen, wir haben die folgenden Maps:
Wir können sie so zusammenführen:
Der Inhalt von new_map wird sein:
Toll!
Aber was, wenn du ein Feld hinzufügen musst, das noch nicht Teil einer Map ist?
Angenommen, du beginnst wieder mit map1, aber die Sprachvorhersage ist nicht in ihrer eigenen Map (es gibt kein map2).
Stattdessen ist sie in einer Variable namens lang_id gespeichert, und du weißt, dass du ihren Wert ('fr') mit dem Schlüssel lang speichern möchtest.
Du kannst tatsächlich Folgendes tun:
Hier erstellt [lang: lang_id] eine neue namenlose Map auf der Stelle, und map1 + führt map1 mit der neuen namenlosen Map zusammen, was denselben new_map-Inhalt wie zuvor erzeugt.
Praktisch, oder?
Jetzt übertragen wir das in den Kontext einer Nextflow channel.map()-Operation.
Der Code wird zu:
Das macht Folgendes:
map1, lang_id ->nimmt die zwei Elemente im Tupelmap1 + [lang: lang_id]erstellt die neue Map wie oben beschrieben
Die Ausgabe ist eine einzelne namenlose Map mit demselben Inhalt wie new_map in unserem Beispiel oben.
Wir haben also effektiv transformiert:
in:
Hoffentlich siehst du, dass wenn wir map1 durch meta ersetzen, das im Wesentlichen alles ist, was wir brauchen, um die Sprachvorhersage zu unserer Meta-Map in unserem Workflow hinzuzufügen.
Bis auf eine Sache!
In unserem Workflow müssen wir auch das file-Objekt im Tupel berücksichtigen, das aus meta, file, lang_id besteht.
Der Code wird also zu:
Falls es schwer nachzuvollziehen ist, warum sich file in der map-Operation scheinbar bewegt, stell dir vor, dass statt [meta + [lang: lang_id], file] diese Zeile [new_map, file] lautet.
Das sollte klarer machen, dass wir file einfach an seiner ursprünglichen zweiten Position im Tupel belassen. Wir haben nur den new_info-Wert in die Map an erster Position eingefaltet.
Und das bringt uns zurück zur tuple val(meta), path(file)-Kanalstruktur!
2.2.2. Den Workflow ausführen¶
Sobald du sicher bist, dass du verstehst, was dieser Code tut, führe den Workflow aus, um zu sehen, ob es funktioniert hat:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [cheeky_fermat] DSL2 - revision: d096281ee4
[5d/dffd4e] COWPY (7) [100%] 7 of 7, cached: 7 ✔
[4e/f722fe] IDENTIFY_LANGUAGE (7) [100%] 7 of 7, cached: 7 ✔
[[id:sampleA, character:squirrel, lang:fr], /workspaces/training/side-quests/metadata/work/eb/f7148ebdd898fbe1136bec6a714acb/bonjour.txt]
[[id:sampleB, character:tux, lang:de], /workspaces/training/side-quests/metadata/work/16/71d72410952c22cd0086d9bca03680/guten_tag.txt]
[[id:sampleC, character:sheep, lang:de], /workspaces/training/side-quests/metadata/work/ea/04f5d979429e4455e14b9242fb3b45/hallo.txt]
[[id:sampleD, character:turkey, lang:en], /workspaces/training/side-quests/metadata/work/c4/b7562adddc1cc0b7d414ec45d436eb/hello.txt]
[[id:sampleF, character:moose, lang:fr], /workspaces/training/side-quests/metadata/work/5a/6c2b84bf8fadb98e28e216426be079/salut.txt]
[[id:sampleE, character:stegosaurus, lang:es], /workspaces/training/side-quests/metadata/work/af/ee7c69bcab891c40d0529305f6b9e7/hola.txt]
[[id:sampleG, character:turtle, lang:it], /workspaces/training/side-quests/metadata/work/4e/f722fe47271ba7ebcd69afa42964ca/ciao.txt]
Ja, das stimmt!
Wir haben die Ausgabe des Prozesses sauber von meta, file, lang_id umstrukturiert, sodass lang_id jetzt einer der Schlüssel in der Meta-Map ist und die Tupel des Kanals wieder dem meta, file-Modell entsprechen.
Schlüssel aus einer Meta-Map entfernen
Du kannst einen Schlüssel aus einer Meta-Map mit der Groovy-Methode subMap entfernen, die eine neue Map zurückgibt, die nur die angegebenen Schlüssel enthält:
Das ist nützlich, wenn ein nachgelagerter Prozess oder ein Modul nicht alle Felder benötigt, die sich in der Meta-Map angesammelt haben.
2.3. Eine Sprachgruppe mit Bedingungen zuweisen¶
Mit der Sprachvorhersage in der Meta-Map können wir weitere Metadaten daraus ableiten.
Die Sprachen in unserem Datensatz lassen sich in zwei Familien einteilen: Germanisch (Englisch, Deutsch) und Romanisch (Französisch, Spanisch, Italienisch).
Das Hinzufügen eines lang_group-Felds macht diese Klassifizierung für nachgelagerte Schritte verfügbar.
2.3.1. Eine map-Operation mit der bedingten Logik hinzufügen¶
Wir verwenden eine zweite map-Operation mit bedingter Logik, um die Sprachfamilie zuzuweisen:
.map { meta, file ->
// Bedingte Logik zur Definition von lang_group kommt hier hin
[meta + [lang_group: lang_group], file]
}
Hier ist die anzuwendende Logik:
- Beginne mit
lang_group = 'unknown'als Standardwert. - Wenn
meta.lang'de'oder'en'ist, setzelang_groupauf'germanic'. - Sonst wenn
meta.langin['fr', 'es', 'it']enthalten ist, setzelang_groupauf'romance'.
Tipp
Du kannst auf den Wert von lang innerhalb der map-Operation mit meta.lang zugreifen.
Nimm die folgenden Änderungen am Workflow vor:
Wichtige Punkte:
def lang_group = "unknown"initialisiert die Variable mit einem sicheren Standardwert.- Die
if / else if-Struktur behandelt die zwei Sprachfamilien; alles andere bleibt'unknown'. .set { ch_languages }gibt dem resultierenden Kanal einen Namen für den nächsten Schritt.
2.3.2. Den Workflow ausführen¶
Führe den Workflow aus, um zu überprüfen, dass es funktioniert:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [wise_almeida] DSL2 - revision: 46778c3cd0
[5d/dffd4e] COWPY (7) [100%] 7 of 7, cached: 7 ✔
[da/652cc6] IDENTIFY_LANGUAGE (7) [100%] 7 of 7, cached: 7 ✔
[[id:sampleA, character:squirrel, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/data/bonjour.txt]
[[id:sampleB, character:tux, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/data/guten_tag.txt]
[[id:sampleC, character:sheep, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/data/hallo.txt]
[[id:sampleD, character:turkey, lang:en, lang_group:germanic], /workspaces/training/side-quests/metadata/data/hello.txt]
[[id:sampleE, character:stegosaurus, lang:es, lang_group:romance], /workspaces/training/side-quests/metadata/data/hola.txt]
[[id:sampleF, character:moose, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/data/salut.txt]
[[id:sampleG, character:turtle, lang:it, lang_group:romance], /workspaces/training/side-quests/metadata/data/ciao.txt]
Die Meta-Map enthält jetzt vier Felder: id, character, lang und lang_group.
Die Kanalstruktur ist weiterhin [meta, file].
2.4. Metadaten zur Benennung und Organisation von Ausgaben verwenden¶
Mit lang und lang_group in der Meta-Map können wir sie verwenden, um einen Sprachcode zu den Ausgabedateinamen hinzuzufügen und die Dateien in Unterverzeichnisse nach Sprachfamilie zu organisieren.
Dazu sind drei Änderungen erforderlich: den COWPY-Prozess aktualisieren, um seine Ausgabe umzubenennen und meta in der Ausgabe einzuschließen, den COWPY-Aufruf auf ch_languages umstellen und den Output-Block aktualisieren, um den Unterverzeichnispfad anzugeben.
2.4.1. Den COWPY-Prozess aktualisieren¶
Benenne die Ausgabedatei mit dem Sprachcode aus der Meta-Map um und füge meta zur Ausgabe hinzu, damit der Output-Block auf lang_group für die Unterverzeichnis-Zuordnung zugreifen kann:
Das zeigt, wie wir andere Metadatenfelder nutzen können, um das Verhalten eines Prozesses anzupassen, ohne die Eingabedefinition ändern zu müssen.
2.4.2. Den COWPY-Aufruf auf ch_languages umstellen¶
Ersetze COWPY(ch_datasheet) durch COWPY(ch_languages):
Wir entfernen auch die ch_languages.view()-Zeile, da wir den Kanalinhalt nicht mehr prüfen müssen.
2.4.3. Den Output-Block aktualisieren¶
Füge eine path-Closure zum output {}-Block hinzu, um jede Datei in ihr Sprachgruppen-Unterverzeichnis zu leiten:
Das zeigt, wie wir Metadaten verwenden können, um Ausgaben mit großer Flexibilität zu organisieren.
2.4.4. Die vollständige Pipeline ausführen¶
Lösche die vorherigen Ergebnisse und führe die vollständige Pipeline aus:
Befehlsausgabe
Das Ergebnisverzeichnis ist jetzt nach Sprachfamilie organisiert, wobei jede Datei nach der erkannten Sprache benannt ist:
results/
├── germanic
│ ├── de-guten_tag.txt
│ ├── de-hallo.txt
│ └── en-hello.txt
└── romance
├── es-hola.txt
├── fr-bonjour.txt
├── fr-salut.txt
└── it-ciao.txt
Die path-Closure im output {}-Block empfängt jedes [meta, file]-Tupel und gibt meta.lang_group als Unterverzeichnisnamen zurück.
Der Dateiname selbst kommt von der Prozessausgabe ("${meta.lang}-${input_file}").
Beide Metadaten (Sprachcode und Sprachgruppe) stammen aus der in diesem Abschnitt aufgebauten angereicherten Meta-Map.
Fazit¶
In diesem Abschnitt hast du gelernt:
- Wie man die Meta-Map mit Prozessausgaben anreichert: Das Hinzufügen neuer Schlüssel mit
meta + [key: value]erhält die[meta, file]-Kanalstruktur, während die Metadaten erweitert werden. - Wie man Metadaten aus Metadaten ableitet: Bedingte Logik innerhalb einer
map-Operation kann neue Felder aus bestehenden berechnen. - Wie man Metadaten zur Ausgabeorganisation verwendet: Die
path-Closure imoutput {}-Block kann aus der Meta-Map lesen, um Dateien in Unterverzeichnisse zu leiten.
3. Überlegungen zur Robustheit¶
Wenn Metadatenwerte das Prozessverhalten steuern, können fehlende oder unvollständige Daten Probleme verursachen, die schwer zu diagnostizieren sind. Hier erfährst du, was zu erwarten ist und wie du damit umgehst.
3.1. Was passiert, wenn ein erforderliches Metadatenfeld fehlt¶
Der character-Wert ist erforderlich, damit der COWPY-Prozess ein gültiges Ergebnis erzeugt.
Das Fehlverhalten hängt davon ab, ob die Spalte im Datenblatt vorhanden, aber leer ist, oder ob sie ganz fehlt.
3.1.1. Die Spalte existiert, aber ein Wert ist leer¶
Angenommen, ein Eintrag im Datenblatt hat ein leeres character-Feld:
| datasheet.csv | |
|---|---|
Der character-Schlüssel wird für alle Einträge beim Einlesen des Datenblatts erstellt, aber meta.character für sampleA ist ein leerer String.
Wenn Nextflow ${meta.character} in den Befehl einsetzt, erhält das COWPY-Tool ein leeres Argument für -c und schlägt fehl:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [marvelous_hirsch] DSL2 - revision: 0dfeee3cc1
executor > local (9)
[c1/c5dd4f] process > IDENTIFY_LANGUAGE (7) [ 85%] 6 of 7
[d3/b7c415] process > COWPY (2) [ 0%] 0 of 6
ERROR ~ Error executing process > 'COWPY (1)'
Caused by:
Process `COWPY (1)` terminated with an error exit status (2)
Command executed:
cat bonjour.txt | cowpy -c > fr-bonjour.txt
Command exit status:
2
Command output:
(empty)
Command error:
usage: cowpy [-h] [-l] [-L] [-t] [-u] [-e EYES] [-c COWACTER] [-E] [-r] [-x]
[-C]
[msg ...]
cowpy: error: argument -c/--cowacter: expected one argument
Work dir:
/workspaces/training/side-quests/metadata/work/ca/9d49796612a54dec5ed466063c809b
Container:
community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273
Tip: you can try to figure out what's wrong by changing to the process work dir and showing the script file named `.command.sh`
-- Check '.nextflow.log' file for details
Die Fehlermeldung (expected one argument) weist auf das leere -c-Flag hin.
Ein Blick in die .command.sh-Datei im work-Verzeichnis bestätigt, dass der Befehl mit einem leeren Wert ausgeführt wurde.
3.1.2. Die Spalte existiert nicht im Datenblatt¶
Wenn die character-Spalte vollständig fehlt:
| datasheet.csv | |
|---|---|
Der character-Schlüssel wird in der Meta-Map nie erstellt.
Wenn der Prozess-Script ${meta.character} auswertet, gibt der fehlende Schlüssel null zurück, und Nextflow setzt buchstäblich den String null in den Befehl ein:
Befehlsausgabe
N E X T F L O W ~ version 25.10.4
Launching `main.nf` [jovial_bohr] DSL2 - revision: eaaf375827
executor > local (9)
[0d/ada9db] process > IDENTIFY_LANGUAGE (5) [ 85%] 6 of 7
[06/28065f] process > COWPY (2) [ 0%] 0 of 6
ERROR ~ Error executing process > 'COWPY (2)'
Caused by:
Process `COWPY (2)` terminated with an error exit status (1)
Command executed:
cat guten_tag.txt | cowpy -c null > de-guten_tag.txt
Command exit status:
1
Command output:
(empty)
Command error:
Traceback (most recent call last):
File "/opt/conda/bin/cowpy", line 10, in <module>
sys.exit(main())
~~~~^^
File "/opt/conda/lib/python3.13/site-packages/cowpy/cow.py", line 1215, in main
print(cow(eyes=args.eyes,
~~~^^^^^^^^^^^^^^^^
tongue=args.tongue,
^^^^^^^^^^^^^^^^^^^
thoughts=args.thoughts
^^^^^^^^^^^^^^^^^^^^^^
).milk(msg)
^
TypeError: 'str' object is not callable
Work dir:
/workspaces/training/side-quests/metadata/work/06/28065f7d9fd7d22bba084aa941b6d6
Container:
community.wave.seqera.io/library/cowpy:1.1.5--3db457ae1977a273
Tip: you can replicate the issue by changing to the process work dir and entering the command `bash .command.run`
-- Check '.nextflow.log' file for details
Das cowpy -c null im ausgeführten Befehl ist der diagnostische Hinweis.
3.2. Strategien für den Umgang mit fehlenden Metadaten¶
Es gibt zwei sich ergänzende Ansätze, um Workflows robuster gegen fehlende Metadaten zu machen.
1. Eingabevalidierung
Die zuverlässigste Lösung ist, das Datenblatt zu validieren, bevor die Verarbeitung beginnt, damit Probleme frühzeitig mit einer klaren Fehlermeldung erkannt werden, anstatt als kryptischer Prozessfehler mitten in der Ausführung aufzutauchen. Das Hello nf-core-Training zeigt, wie man Eingabevalidierung mit dem nf-schema-Plugin hinzufügt.
2. Explizite Prozesseingaben für erforderliche Werte
Wenn die Prozessschnittstelle selbst kommunizieren soll, dass ein bestimmter Wert erforderlich ist, kannst du ihn als explizite Eingabe aus der Meta-Map extrahieren:
Dieser Ansatz macht character zu einem sichtbaren, erforderlichen Teil des Prozessvertrags.
Wer das Modul liest, sieht sofort, dass ein Charakterwert angegeben werden muss.
Fehlt das Feld, schlägt der Workflow klar auf Kanalebene fehl, bevor der Prozess überhaupt ausgeführt wird.
Das verdeutlicht ein nützliches Designprinzip:
Verwende die Meta-Map für optionale oder beschreibende Informationen; extrahiere erforderliche Werte als explizite Eingaben.
Die Meta-Map hält Kanalstrukturen sauber und stabil, aber für Werte, die von einem Prozess wirklich benötigt werden, verbessert das Herauslösen als benannte Eingaben die Klarheit und macht das Modul in anderen Kontexten einfacher korrekt zu verwenden.
Fazit¶
In diesem Abschnitt hast du gesehen:
- Wie sich fehlende Metadaten äußern: Ein leeres Feld erzeugt ein leeres Argument; ein fehlendes Feld erzeugt
null, das buchstäblich in den Befehl eingesetzt wird. - Zwei sich ergänzende Strategien: Eingabevalidierung, um Probleme frühzeitig zu erkennen, und explizite Prozesseingaben, um Anforderungen klar zu kommunizieren.
Zusammenfassung¶
In dieser Side Quest hast du erkundet, wie man effektiv mit Metadaten in Nextflow-Workflows arbeitet.
Das „Meta-Map + Datendatei"-Tupel-Muster ist eine zentrale Konvention in Nextflow und bietet mehrere Vorteile gegenüber der Übergabe von Metadaten als einzelne Werte:
- Die Kanalstruktur bleibt stabil, wenn sich das Datenblatt weiterentwickelt
- Das Prozessverhalten kann pro Probe angepasst werden, ohne Feldnamen fest einzuprogrammieren
- Metadaten sind während der gesamten Pipeline für Benennung, Gruppierung und Organisation von Ausgaben verfügbar
- Module, die für diese Schnittstelle geschrieben wurden, sind austauschbar – einschließlich nf-core-Module
Wichtige Muster¶
-
Metadaten lesen und strukturieren: Eine CSV-Datei einlesen und eine Meta-Map erstellen.
-
Metadaten während des Workflows erweitern: Neue Schlüssel aus Prozessausgaben oder abgeleiteter Logik hinzufügen.
// Aus einer Prozessausgabe .map { meta, file, lang -> [ meta + [lang: lang], file ] } // Aus bedingter Logik .map { meta, file -> def lang_group = "unknown" if (meta.lang in ["de", "en"]) { lang_group = "germanic" } else if (meta.lang in ["fr", "es", "it"]) { lang_group = "romance" } [ meta + [lang_group: lang_group], file ] } -
Metadaten innerhalb eines Prozesses verwenden: Auf jedes Feld per Punktnotation im Script-Block zugreifen.
-
Ausgaben nach Metadatenwert organisieren: Die
path-Closure imoutput {}-Block verwenden.
Weitere Ressourcen¶
Wie geht es weiter?¶
Kehre zum Menü der Side Quests zurück oder klicke auf die Schaltfläche unten rechts auf der Seite, um zum nächsten Thema in der Liste zu wechseln.