Teil 2: Hello Channels¶
KI-gestützte Übersetzung - mehr erfahren & Verbesserungen vorschlagen
In Teil 1 dieses Kurses (Hello World) haben wir dir gezeigt, wie du einem process eine variable Eingabe übergibst, indem du die Eingabe direkt im process-Aufruf angibst: sayHello(params.input).
Das war ein bewusst vereinfachter Ansatz.
In der Praxis hat dieser Ansatz wesentliche Einschränkungen; er funktioniert nämlich nur für sehr einfache Fälle, bei denen wir den process nur einmal mit einem einzelnen Wert ausführen möchten.
In den meisten realistischen Workflow-Anwendungsfällen wollen wir mehrere Werte verarbeiten (z.B. experimentelle Daten für mehrere Proben), daher brauchen wir einen ausgefeilteren Weg, um Eingaben zu handhaben.
Dafür sind Nextflow channels da. Channels sind Warteschlangen, die entwickelt wurden, um Eingaben effizient zu handhaben und sie von einem Schritt zum nächsten in mehrstufigen Workflows zu transportieren, während sie eingebaute Parallelität und viele zusätzliche Vorteile bieten.
In diesem Teil des Kurses lernst du, wie du einen channel verwendest, um mehrere Eingaben aus verschiedenen Quellen zu handhaben. Du lernst auch, Operatoren zu verwenden, um channel-Inhalte nach Bedarf zu transformieren.
Wie du von diesem Abschnitt aus beginnen kannst
Dieser Abschnitt des Kurses setzt voraus, dass du Teil 1 des Hello Nextflow-Kurses abgeschlossen hast, aber wenn du mit den dort behandelten Grundlagen vertraut bist, kannst du von hier aus starten, ohne etwas Besonderes tun zu müssen.
0. Aufwärmübung: hello-channels.nf ausführen¶
Wir werden das Workflow-Skript hello-channels.nf als Ausgangspunkt verwenden.
Es entspricht dem Skript, das durch das Durcharbeiten von Teil 1 dieses Trainingskurses erstellt wurde, außer dass wir das Ausgabeziel geändert haben:
Um sicherzustellen, dass alles funktioniert, führe das Skript einmal aus, bevor du Änderungen vornimmst:
Befehlsausgabe
Wie zuvor findest du die Ausgabedatei output.txt im Verzeichnis results/hello_channels (wie im output-Block des Workflow-Skripts oben angegeben).
Verzeichnisinhalte
Wenn das bei dir funktioniert hat, bist du bereit, etwas über channels zu lernen.
1. Variable Eingaben explizit über einen channel bereitstellen¶
Wir werden einen channel erstellen, um die variable Eingabe an den sayHello()-process zu übergeben, anstatt uns auf die implizite Handhabung zu verlassen, die gewisse Einschränkungen hat.
1.1. Einen Eingabe-channel erstellen¶
Es gibt verschiedene channel factories, die wir verwenden können, um einen channel einzurichten.
Um die Dinge vorerst einfach zu halten, werden wir die grundlegendste channel factory namens channel.of verwenden, die einen channel mit einem einzelnen Wert erstellt.
Funktional wird dies ähnlich sein wie zuvor, aber anstatt Nextflow einen channel implizit erstellen zu lassen, tun wir dies jetzt explizit.
Dies ist die Codezeile, die wir verwenden werden:
Dies erstellt einen channel namens greeting_ch unter Verwendung der channel.of() channel factory, die einen einfachen queue channel einrichtet, und lädt den String 'Hello Channels!' als Begrüßungswert.
Hinweis
Wir wechseln vorübergehend zurück zu fest codierten Strings, anstatt einen CLI-Parameter zu verwenden, um die Lesbarkeit zu verbessern. Wir werden zurück zu CLI-Parametern wechseln, sobald wir behandelt haben, was auf Ebene des channels passiert.
Füge im workflow-Block den channel factory-Code hinzu:
Dies ist noch nicht funktionsfähig, da wir die Eingabe für den process-Aufruf noch nicht umgestellt haben.
1.2. Den channel als Eingabe zum process-Aufruf hinzufügen¶
Jetzt müssen wir unseren neu erstellten channel tatsächlich in den sayHello()-process-Aufruf einbinden und den CLI-Parameter ersetzen, den wir zuvor direkt bereitgestellt haben.
Nimm im workflow-Block die folgende Codeänderung vor:
Dies sagt Nextflow, den sayHello-process auf den Inhalt des greeting_ch-channels auszuführen.
Jetzt ist unser Workflow richtig funktionsfähig; es ist das explizite Äquivalent zum Schreiben von sayHello('Hello Channels!').
1.3. Den Workflow ausführen¶
Lass ihn laufen!
Befehlsausgabe
Wenn du beide Änderungen korrekt vorgenommen hast, solltest du eine erfolgreiche Ausführung erhalten. Du kannst das Ergebnisverzeichnis überprüfen, um dich zu vergewissern, dass das Ergebnis noch dasselbe ist wie zuvor.
Wir haben also die Flexibilität unseres Workflows erhöht, während wir dasselbe Endergebnis erzielen. Das mag so erscheinen, als würden wir mehr Code für keinen greifbaren Nutzen schreiben, aber der Wert wird deutlich, sobald wir anfangen, mehr Eingaben zu handhaben.
Als Vorschau darauf schauen wir uns noch eine Sache an, bevor wir weitermachen: einen kleinen, aber praktischen Vorteil der Verwendung eines expliziten channels zur Verwaltung von Dateneingaben.
1.4. view() verwenden, um channel-Inhalte zu inspizieren¶
Nextflow channels sind so aufgebaut, dass wir mit Operatoren auf ihre Inhalte einwirken können, was wir später in diesem Kapitel ausführlich behandeln werden.
Vorerst zeigen wir dir nur, wie du einen super einfachen Operator namens view() verwendest, um die Inhalte eines channels zu inspizieren.
Du kannst view() als Debugging-Werkzeug betrachten, wie eine print()-Anweisung in Python oder deren Äquivalent in anderen Sprachen.
Füge diese kleine Zeile zum workflow-Block hinzu:
Die genaue Anzahl der Leerzeichen spielt keine Rolle, solange es ein Vielfaches von 4 ist; wir zielen nur darauf ab, den Anfang der .view()-Anweisung mit dem .of()-Teil der channel-Konstruktion auszurichten.
Führe jetzt den Workflow erneut aus:
Befehlsausgabe
Wie du sehen kannst, gibt dies die channel-Inhalte in der Konsole aus. Hier haben wir nur ein Element, aber wenn wir im nächsten Abschnitt anfangen, mehrere Werte in den channel zu laden, wirst du sehen, dass dies so eingestellt ist, dass ein Element pro Zeile ausgegeben wird.
Zusammenfassung¶
Du weißt, wie du eine grundlegende channel factory verwendest, um eine Eingabe an einen process zu übergeben.
Was kommt als Nächstes?¶
Lerne, wie du channels verwendest, damit der Workflow über mehrere Eingabewerte iteriert.
2. Den Workflow so modifizieren, dass er mit mehreren Eingabewerten läuft¶
Workflows werden typischerweise auf Stapel von Eingaben ausgeführt, die in großen Mengen verarbeitet werden sollen, daher wollen wir den Workflow aufrüsten, um mehrere Eingabewerte zu akzeptieren.
2.1. Mehrere Begrüßungen in den Eingabe-channel laden¶
Praktischerweise ist die channel.of() channel factory, die wir verwendet haben, durchaus bereit, mehr als einen Wert zu akzeptieren, daher müssen wir das überhaupt nicht ändern.
Wir können einfach mehrere Werte in den channel laden.
Machen wir sie zu 'Hello', 'Bonjour' und 'Holà'.
Im Diagramm wird der channel in Grün dargestellt, und die Reihenfolge der Elemente wird wie Murmeln in einer Röhre dargestellt: das zuerst geladene ist rechts, dann das zweite in der Mitte, dann das dritte links.
2.1.1. Weitere Begrüßungen hinzufügen¶
Nimm vor dem workflow-Block die folgende Codeänderung vor:
| hello-channels.nf | |
|---|---|
| hello-channels.nf | |
|---|---|
Die Dokumentation sagt uns, dass das funktionieren sollte. Kann es wirklich so einfach sein?
2.1.2. Den Befehl ausführen und die Log-Ausgabe betrachten¶
Lass uns es versuchen.
Befehlsausgabe
Es scheint tatsächlich gut gelaufen zu sein.
Der Ausführungsmonitor zeigt, dass 3 von 3 Aufrufe für den sayHello-process gemacht wurden, und wir sehen die drei Begrüßungen, die durch die view()-Anweisung aufgelistet werden, eine pro Zeile wie versprochen.
Allerdings gibt es im Ergebnisverzeichnis immer noch nur eine Ausgabe:
Verzeichnisinhalte
Du solltest eine der drei Begrüßungen darin sehen, aber die, die du bekommen hast, könnte anders sein als hier gezeigt. Kannst du dir vorstellen, warum das so sein könnte?
Wenn wir zurück auf den Ausführungsmonitor schauen, hat er uns nur einen Unterverzeichnispfad gegeben (f4/c9962c).
Lass uns dort hineinschauen.
Verzeichnisinhalte
Das ist nicht einmal dieselbe Begrüßung, die wir im Ergebnisverzeichnis bekommen haben! Was geht hier vor?
An diesem Punkt müssen wir dir sagen, dass das ANSI-Logging-System standardmäßig das Logging von mehreren Aufrufen desselben process auf derselben Zeile schreibt. Also landen die Statusmeldungen von allen drei Aufrufen des sayHello()-process an derselben Stelle.
Glücklicherweise können wir dieses Verhalten deaktivieren, um die vollständige Liste der process-Aufrufe zu sehen.
2.1.3. Den Befehl erneut mit der Option -ansi-log false ausführen¶
Um das Logging zu erweitern und eine Zeile pro process-Aufruf anzuzeigen, füge -ansi-log false zum Befehl hinzu.
Befehlsausgabe
Diesmal sehen wir alle drei process-Ausführungen und ihre zugehörigen work-Unterverzeichnisse in der Ausgabe aufgelistet.
Das ist viel besser, zumindest für einen einfachen Workflow.
Für einen komplexen Workflow oder eine große Anzahl von Eingaben würde die vollständige Liste, die im Terminal ausgegeben wird, etwas überwältigend werden.
Deshalb ist -ansi-log false nicht das Standardverhalten.
Tipp
Die Art und Weise, wie der Status gemeldet wird, ist zwischen den beiden Logging-Modi etwas unterschiedlich. Im komprimierten Modus meldet Nextflow, ob Aufrufe erfolgreich abgeschlossen wurden oder nicht. In diesem erweiterten Modus wird nur gemeldet, dass sie eingereicht wurden.
Wie auch immer, jetzt da wir die Unterverzeichnisse jedes process-Aufrufs haben, können wir nach ihren Logs und Ausgaben suchen.
Verzeichnisinhalte
work/23/871c7ec3642a898ecd5e6090d21300/
├── .command.begin
├── .command.err
├── .command.log
├── .command.out
├── .command.run
├── .command.sh
├── .exitcode
└── output.txt
Dateiinhalte
Dies zeigt, dass alle drei processes erfolgreich ausgeführt wurden (juhu).
Allerdings haben wir immer noch das Problem, dass es nur eine Ausgabedatei im Ergebnisverzeichnis gibt.
Du erinnerst dich vielleicht, dass wir den Ausgabedateinamen für den sayHello-process fest codiert haben, sodass alle drei Aufrufe eine Datei namens output.txt produziert haben.
Solange die Ausgabedateien in den work-Unterverzeichnissen bleiben, isoliert von den anderen processes, ist das in Ordnung. Aber wenn sie in dasselbe Ergebnisverzeichnis veröffentlicht werden, wird die, die zuerst dort kopiert wurde, von der nächsten überschrieben, und so weiter.
2.2. Sicherstellen, dass die Ausgabedateinamen eindeutig sind¶
Wir können weiterhin alle Ausgaben in dasselbe Ergebnisverzeichnis veröffentlichen, aber wir müssen sicherstellen, dass sie eindeutige Namen haben werden. Genauer gesagt müssen wir den ersten process so modifizieren, dass er einen Dateinamen dynamisch generiert, sodass die endgültigen Dateinamen eindeutig sein werden.
Wie machen wir also die Dateinamen eindeutig? Ein gängiger Weg, das zu tun, ist, ein eindeutiges Stück Metadaten aus den Eingaben (die vom Eingabe-channel empfangen wurden) als Teil des Ausgabedateinamens zu verwenden. Hier werden wir der Einfachheit halber nur die Begrüßung selbst verwenden, da sie nur ein kurzer String ist, und sie dem Basis-Ausgabedateinamen voranstellen.
2.2.1. Einen dynamischen Ausgabedateinamen konstruieren¶
Nimm im process-Block die folgenden Codeänderungen vor:
Stelle sicher, dass du output.txt sowohl in der Ausgabedefinition als auch im script:-Befehlsblock ersetzt.
Tipp
In der Ausgabedefinition MUSST du doppelte Anführungszeichen um den Ausgabedateinamensausdruck verwenden (NICHT einfache Anführungszeichen), sonst schlägt es fehl.
Dies sollte jedes Mal einen eindeutigen Ausgabedateinamen produzieren, wenn der process aufgerufen wird, sodass er von den Ausgaben anderer Aufrufe desselben process im Ausgabeverzeichnis unterschieden werden kann.
2.2.2. Den Workflow ausführen¶
Lass ihn laufen. Beachte, dass wir wieder mit den Standard-ANSI-Log-Einstellungen ausführen.
Befehlsausgabe
Zurück in der Zusammenfassungsansicht wird die Ausgabe wieder auf einer Zeile zusammengefasst.
Schau in das results-Verzeichnis, um zu sehen, ob alle Ausgabebegrüßungen da sind.
Verzeichnisinhalte
Ja! Und sie haben jeweils die erwarteten Inhalte.
Erfolg! Jetzt können wir so viele Begrüßungen hinzufügen, wie wir möchten, ohne uns Sorgen zu machen, dass Ausgabedateien überschrieben werden.
Tipp
In der Praxis ist das Benennen von Dateien basierend auf den Eingabedaten selbst fast immer unpraktisch. Der bessere Weg, dynamische Dateinamen zu generieren, ist, Metadaten zusammen mit den Eingabedateien an einen process zu übergeben. Die Metadaten werden typischerweise über ein 'Sample Sheet' oder Äquivalente bereitgestellt. Du wirst später in deinem Nextflow-Training lernen, wie du das machst (siehe Metadaten-Side Quest).
Zusammenfassung¶
Du weißt, wie du mehrere Eingabeelemente durch einen channel führst.
Was kommt als Nächstes?¶
Lerne, einen Operator zu verwenden, um die Inhalte eines channels zu transformieren.
3. Mehrere Eingaben über ein Array bereitstellen¶
Wir haben dir gerade gezeigt, wie du mehrere Eingabeelemente handhabst, die direkt in der channel factory fest codiert waren. Was ist, wenn wir diese mehreren Eingaben auf eine andere Weise bereitstellen wollten?
Stell dir zum Beispiel vor, wir richten eine Eingabevariable ein, die ein Array von Elementen enthält wie dieses:
greetings_array = ['Hello','Bonjour','Holà']
Können wir das in unseren Ausgabe-channel laden und erwarten, dass es funktioniert?
Lass uns das herausfinden.
3.1. Ein Array von Werten als Eingabe für den channel bereitstellen¶
Der gesunde Menschenverstand legt nahe, dass wir einfach ein Array von Werten anstelle eines einzelnen Wertes übergeben können sollten. Lass uns es versuchen; wir müssen die Eingabevariable einrichten und sie in die channel factory laden.
3.1.1. Die Eingabevariable einrichten¶
Lass uns die greetings_array-Variable, die wir uns gerade vorgestellt haben, Realität werden lassen, indem wir sie zum workflow-Block hinzufügen:
| hello-channels.nf | |
|---|---|
Dies ist noch nicht funktionsfähig, wir haben nur eine Deklaration für das Array hinzugefügt.
3.1.2. Das Array von Begrüßungen als Eingabe für die channel factory setzen¶
Jetzt werden wir die Werte 'Hello','Bonjour','Holà', die derzeit in der channel factory fest codiert sind, durch das greetings_array ersetzen, das wir gerade erstellt haben.
Nimm im workflow-Block die folgende Änderung vor:
| hello-channels.nf | |
|---|---|
| hello-channels.nf | |
|---|---|
Dies sollte jetzt funktionsfähig sein.
3.1.3. Den Workflow ausführen¶
Lass uns versuchen, ihn auszuführen:
Befehlsausgabe
N E X T F L O W ~ version 25.10.2
Launching `hello-channels.nf` [friendly_koch] DSL2 - revision: 97256837a7
executor > local (1)
[a8/1f6ead] sayHello (1) | 0 of 1
[Hello, Bonjour, Holà]
ERROR ~ Error executing process > 'sayHello (1)'
Caused by:
Missing output file(s) `[Hello, Bonjour, Holà]-output.txt` expected by process `sayHello (1)`
Command executed:
echo '[Hello, Bonjour, Holà]' > '[Hello, Bonjour, Holà]-output.txt'
Command exit status:
0
Command output:
(empty)
Work dir:
/workspaces/training/hello-nextflow/work/a8/1f6ead5f3fa30a3c508e2e7cf83ffb
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
Oh nein! Es gibt einen Fehler!
Schau dir die Ausgabe von view() und die Fehlermeldungen an.
Es sieht so aus, als hätte Nextflow versucht, einen einzelnen process-Aufruf auszuführen, wobei [Hello, Bonjour, Holà] als Stringwert verwendet wurde, anstatt die drei Strings im Array als separate Werte zu verwenden.
Es ist also die 'Verpackung', die das Problem verursacht. Wie bringen wir Nextflow dazu, das Array zu entpacken und die einzelnen Strings in den channel zu laden?
3.2. Einen Operator verwenden, um channel-Inhalte zu transformieren¶
Hier kommen Operatoren ins Spiel.
Du hast bereits den .view()-Operator verwendet, der nur hineinschaut, was drin ist.
Jetzt werden wir uns Operatoren ansehen, die es uns ermöglichen, auf die Inhalte eines channels einzuwirken.
Wenn du durch die Liste der Operatoren in der Nextflow-Dokumentation blätterst, wirst du flatten() finden, das genau das tut, was wir brauchen: den Inhalt eines Arrays entpacken und sie als einzelne Elemente ausgeben.
3.2.1. Den flatten()-Operator hinzufügen¶
Um den flatten()-Operator auf unseren Eingabe-channel anzuwenden, hängen wir ihn an die channel factory-Deklaration an.
Nimm im workflow-Block die folgende Codeänderung vor:
| hello-channels.nf | |
|---|---|
| hello-channels.nf | |
|---|---|
Hier haben wir den Operator für die Lesbarkeit in der nächsten Zeile hinzugefügt, aber du kannst Operatoren auch auf derselben Zeile wie die channel factory hinzufügen, wenn du möchtest, so:
greeting_ch = channel.of(greetings_array).view().flatten()
3.2.2. Die view()-Anweisung(en) verfeinern¶
Wir könnten dies jetzt sofort ausführen, um zu testen, ob es funktioniert, aber während wir dabei sind, werden wir verfeinern, wie wir die channel-Inhalte inspizieren.
Wir wollen den Inhalt vor und nach der Anwendung des flatten()-Operators vergleichen können, also werden wir ein zweites hinzufügen, UND wir werden etwas Code hinzufügen, um sie in der Ausgabe deutlicher zu beschriften.
Nimm im workflow-Block die folgende Codeänderung vor:
| hello-channels.nf | |
|---|---|
Du siehst, wir haben eine zweite .view-Anweisung hinzugefügt, und für jede von ihnen haben wir die leeren Klammern (()) durch geschweifte Klammern mit etwas Code ersetzt, wie { greeting -> "Before flatten: $greeting" }.
Diese werden closures genannt. Der Code, den sie enthalten, wird für jedes Element im channel ausgeführt.
Wir definieren eine temporäre Variable für den inneren Wert, hier greeting genannt (aber es könnte ein beliebiger Name sein), die nur innerhalb des Gültigkeitsbereichs dieser closure verwendet wird.
In diesem Beispiel repräsentiert $greeting jedes einzelne Element, das in den channel geladen wurde.
Dies führt zu sauber beschrifteter Konsolenausgabe.
Info
In einigen Pipelines siehst du möglicherweise eine spezielle Variable namens $it, die innerhalb von Operator-closures verwendet wird.
Dies ist eine implizite Variable, die einen Kurzform-Zugriff auf die innere Variable ermöglicht,
ohne sie mit einem -> definieren zu müssen.
Wir bevorzugen die explizite Form, um die Klarheit des Codes zu unterstützen. Die $it-Syntax wird nicht empfohlen und wird langsam aus der Nextflow-Sprache auslaufen.
3.2.3. Den Workflow ausführen¶
Schließlich kannst du versuchen, den Workflow erneut auszuführen!
Befehlsausgabe
Diesmal funktioniert es UND gibt uns den zusätzlichen Einblick, wie die Inhalte des channels vor und nach der Ausführung des flatten()-Operators aussehen.
- Du siehst, dass wir eine einzelne
Before flatten:-Anweisung bekommen, weil zu diesem Zeitpunkt der channel ein Element enthält, das ursprüngliche Array. Dann bekommen wir drei separateAfter flatten:-Anweisungen, eine für jede Begrüßung, die jetzt einzelne Elemente im channel sind.
Wichtig ist, dass dies bedeutet, dass jedes Element jetzt separat vom Workflow verarbeitet werden kann.
Tipp
Es ist technisch möglich, dieselben Ergebnisse zu erzielen, indem man eine andere channel factory verwendet, channel.fromList, die einen impliziten Mapping-Schritt in ihrer Operation enthält.
Hier haben wir uns entschieden, das nicht zu verwenden, um die Verwendung eines Operators an einem einfachen Anwendungsfall zu demonstrieren.
Zusammenfassung¶
Du weißt, wie du einen Operator wie flatten() verwendest, um die Inhalte eines channels zu transformieren, und wie du den view()-Operator verwendest, um channel-Inhalte vor und nach der Anwendung eines Operators zu inspizieren.
Was kommt als Nächstes?¶
Lerne, wie du den Workflow so einrichtest, dass er eine Datei als Quelle von Eingabewerten verwendet.
4. Eingabewerte aus einer CSV-Datei lesen¶
Realistischerweise werden wir selten, wenn überhaupt, von einem Array von Werten ausgehen. Höchstwahrscheinlich haben wir eine oder mehrere Dateien, die die zu verarbeitenden Daten enthalten, in irgendeiner Art von strukturiertem Format.
Wir haben eine CSV-Datei namens greetings.csv vorbereitet, die mehrere Eingabebegrüßungen enthält und die Art von tabellarischen Daten nachahmt, die du in einer echten Datenanalyse verarbeiten möchtest, gespeichert unter data/.
(Die Zahlen sind nicht bedeutsam, sie sind nur zu Illustrationszwecken da.)
Unsere nächste Aufgabe ist es, unseren Workflow anzupassen, um die Werte aus dieser Datei einzulesen.
Lass uns sehen, wie wir das bewerkstelligen können.
4.1. Das Skript so modifizieren, dass es eine CSV-Datei als Quelle von Begrüßungen erwartet¶
Um loszulegen, müssen wir zwei wichtige Änderungen am Skript vornehmen:
- Den Eingabeparameter so umstellen, dass er auf die CSV-Datei zeigt
- Die channel factory auf eine umstellen, die für das Handhaben einer Datei ausgelegt ist
4.1.1. Den Eingabeparameter so umstellen, dass er auf die CSV-Datei zeigt¶
Erinnerst du dich an den params.input-Parameter, den wir in Teil 1 eingerichtet haben?
Wir werden ihn aktualisieren, um auf die CSV-Datei zu zeigen, die unsere Begrüßungen enthält.
Nimm die folgende Änderung an der Parameterdeklaration vor:
Dies setzt voraus, dass die Datei am selben Ort wie der Workflow-Code liegt. Wie du mit anderen Datenspeicherorten umgehst, lernst du in späteren Kursen.
4.1.2. Zu einer channel factory wechseln, die für das Handhaben einer Datei ausgelegt ist¶
Da wir jetzt eine Datei anstelle von einfachen Strings als Eingabe verwenden wollen, können wir die channel.of() channel factory von vorher nicht verwenden.
Wir müssen zu einer neuen channel factory wechseln, channel.fromPath(), die einige eingebaute Funktionalitäten für das Handhaben von Dateipfaden hat.
Nimm im workflow-Block die folgende Codeänderung vor:
Du wirst bemerken, dass wir die channel-Eingabe zurück zu param.input gewechselt und die greetings_array-Deklaration gelöscht haben, da wir sie nicht mehr brauchen werden.
Wir haben auch flatten() und die zweite view()-Anweisung auskommentiert.
4.1.3. Den Workflow ausführen¶
Lass uns versuchen, den Workflow mit der neuen channel factory und der Eingabedatei auszuführen.
Befehlsausgabe
N E X T F L O W ~ version 25.10.2
Launching `hello-channels.nf` [peaceful_poisson] DSL2 - revision: a286c08ad5
[- ] sayHello [ 0%] 0 of 1
Before flatten: /workspaces/training/hello-nextflow/data/greetings.csv
ERROR ~ Error executing process > 'sayHello (1)'
Caused by:
File `/workspaces/training/hello-nextflow/data/greetings.csv-output.txt` is outside the scope of the process work directory: /workspaces/training/hello-nextflow/work/30/e610cb4ea5ae8693f456ac3329c92f
Command executed:
echo '/workspaces/training/hello-nextflow/data/greetings.csv' > '/workspaces/training/hello-nextflow/data/greetings.csv-output.txt'
Command exit status:
-
Command output:
(empty)
Work dir:
/workspaces/training/hello-nextflow/work/30/e610cb4ea5ae8693f456ac3329c92f
Tip: when you have fixed the problem you can continue the execution adding the option `-resume` to the run command line
-- Check '.nextflow.log' file for details
Oh nein, es funktioniert nicht. Schau dir den Anfang der Konsolenausgabe und die Fehlermeldung an.
Der Teil Command executed: ist hier besonders hilfreich.
Das sieht vielleicht ein bisschen vertraut aus. Es sieht so aus, als hätte Nextflow versucht, einen einzelnen process-Aufruf auszuführen, wobei der Dateipfad selbst als Stringwert verwendet wurde. Also hat es den Dateipfad korrekt aufgelöst, aber es hat nicht wirklich seinen Inhalt geparst, was wir wollten.
Wie bringen wir Nextflow dazu, die Datei zu öffnen und ihren Inhalt in den channel zu laden?
Klingt so, als bräuchten wir einen weiteren Operator!
4.2. Den splitCsv()-Operator verwenden, um die Datei zu parsen¶
Wenn wir wieder durch die Liste der Operatoren schauen, finden wir splitCsv(), das dafür ausgelegt ist, CSV-formatierten Text zu parsen und zu teilen.
4.2.1. splitCsv() auf den channel anwenden¶
Um den Operator anzuwenden, hängen wir ihn wie zuvor an die channel factory-Zeile an.
Nimm im workflow-Block die folgende Codeänderung vor, um flatten() durch splitcsv() zu ersetzen (nicht auskommentiert):
Wie du sehen kannst, haben wir auch die vorher/nachher view()-Anweisungen aktualisiert.
Technisch hätten wir denselben Variablennamen (greeting) verwenden können, aber wir haben ihn auf etwas Passenderes (csv) aktualisiert, um den Code für andere lesbarer zu machen.
4.2.2. Den Workflow erneut ausführen¶
Lass uns versuchen, den Workflow mit der hinzugefügten CSV-Parsing-Logik auszuführen.
Befehlsausgabe
N E X T F L O W ~ version 25.10.2
Launching `hello-channels.nf` [insane_fermat] DSL2 - revision: 8e62fcbeb1
executor > local (3)
[24/76da2f] sayHello (2) [ 0%] 0 of 3 ✘
Before splitCsv: /workspaces/training/hello-nextflow/data/greetings.csv
After splitCsv: [Hello, English, 123]
After splitCsv: [Bonjour, French, 456]
After splitCsv: [Holà, Spanish, 789]
ERROR ~ Error executing process > 'sayHello (2)'
Caused by:
Missing output file(s) `[Bonjour, French, 456]-output.txt` expected by process `sayHello (2)`
Command executed:
echo '[Bonjour, French, 456]' > '[Bonjour, French, 456]-output.txt'
Command exit status:
0
Command output:
(empty)
Work dir:
/workspaces/training/hello-nextflow/work/24/76da2fcc4876b61632749f99e26a50
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
Interessanterweise schlägt dies auch fehl, aber mit einem anderen Fehler. Diesmal hat Nextflow den Inhalt der Datei geparst (juhu!), aber es hat jede Zeile als Array geladen, und jedes Array ist ein Element im channel.
Wir müssen ihm sagen, dass es nur die erste Spalte in jeder Zeile nehmen soll. Wie entpacken wir das also?
Wir haben zuvor flatten() verwendet, um die Inhalte eines channels zu entpacken, aber das würde hier nicht funktionieren, weil flatten alles entpackt (versuche es gerne, wenn du es selbst sehen möchtest).
Stattdessen werden wir einen anderen Operator namens map() verwenden, der wirklich nützlich ist und in Nextflow-Pipelines häufig vorkommt.
4.3. Den map()-Operator verwenden, um die Begrüßungen zu extrahieren¶
Der map()-Operator ist ein sehr praktisches kleines Werkzeug, das es uns ermöglicht, alle Arten von Mappings auf die Inhalte eines channels durchzuführen.
In diesem Fall werden wir ihn verwenden, um das eine Element zu extrahieren, das wir aus jeder Zeile in unserer Datendatei wollen. So sieht die Syntax aus:
Das bedeutet 'für jede Zeile im channel, nimm das 0. (erste) Element, das sie enthält'.
Also wenden wir das auf unser CSV-Parsing an.
4.3.1. map() auf den channel anwenden¶
Nimm im workflow-Block die folgende Codeänderung vor:
Du siehst, wir haben einen weiteren view()-Aufruf hinzugefügt, um zu bestätigen, dass der Operator das tut, was wir erwarten.
4.3.2. Den Workflow ausführen¶
Lass uns das noch einmal ausführen:
Befehlsausgabe
N E X T F L O W ~ version 25.10.2
Launching `hello-channels.nf` [focused_volhard] DSL2 - revision: de435e45be
executor > local (3)
[54/6eebe3] sayHello (3) [100%] 3 of 3 ✔
Before splitCsv: /workspaces/training/hello-nextflow/data/greetings.csv
After splitCsv: [Hello, English, 123]
After splitCsv: [Bonjour, French, 456]
After splitCsv: [Holà, Spanish, 789]
After map: Hello
After map: Bonjour
After map: Holà
Diesmal sollte es ohne Fehler laufen.
Wenn du dir die Ausgabe der view()-Anweisungen anschaust, siehst du Folgendes:
- Eine einzelne
Before splitCsv:-Anweisung: zu diesem Zeitpunkt enthält der channel ein Element, den ursprünglichen Dateipfad. - Drei separate
After splitCsv:-Anweisungen: eine für jede Begrüßung, aber jede ist in einem Array enthalten, das dieser Zeile in der Datei entspricht. - Drei separate
After map:-Anweisungen: eine für jede Begrüßung, die jetzt einzelne Elemente im channel sind.
Beachte, dass die Zeilen in deiner Ausgabe in einer anderen Reihenfolge erscheinen können.
Du kannst auch die Ausgabedateien ansehen, um zu überprüfen, dass jede Begrüßung korrekt extrahiert und durch den Workflow verarbeitet wurde.
Wir haben dasselbe Ergebnis wie zuvor erzielt, aber jetzt haben wir viel mehr Flexibilität, um weitere Elemente zum channel von Begrüßungen hinzuzufügen, die wir verarbeiten möchten, indem wir eine Eingabedatei modifizieren, ohne Code zu ändern. Du wirst in einem späteren Training anspruchsvollere Ansätze für das Handhaben komplexer Eingaben lernen.
Zusammenfassung¶
Du weißt, wie du den .fromPath() channel-Konstruktor und die Operatoren splitCsv() und map() verwendest, um eine Datei mit Eingabewerten einzulesen und sie angemessen zu handhaben.
Allgemeiner hast du ein grundlegendes Verständnis davon, wie Nextflow channels verwendet, um Eingaben für processes zu verwalten, und Operatoren, um ihre Inhalte zu transformieren.
Was kommt als Nächstes?¶
Mach eine große Pause, du hast in diesem Teil hart gearbeitet!
Wenn du bereit bist, gehe zu Teil 3: Hallo Workflow, um zu lernen, wie du weitere Schritte hinzufügst und sie zu einem richtigen Workflow verbindest.
Quiz¶
Was ist ein channel in Nextflow?
Wenn ein channel mehrere Werte enthält, wie handhabt Nextflow die process-Ausführung?
Was macht der flatten()-Operator?
Was ist der Zweck des view()-Operators?
Was macht splitCsv()?
Was ist der Zweck des map()-Operators?
Warum ist es wichtig, dynamische Ausgabedateinamen zu verwenden, wenn mehrere Eingaben verarbeitet werden?