Metadane i mapy metadanych¶
Tłumaczenie wspomagane przez AI - dowiedz się więcej i zasugeruj ulepszenia
W każdej analizie naukowej rzadko pracujemy wyłącznie z surowymi plikami danych. Każdy plik niesie ze sobą dodatkowe informacje: czym jest, skąd pochodzi i co go wyróżnia. Te dodatkowe informacje nazywamy metadanymi.
Metadane to dane opisujące inne dane. Śledzą ważne szczegóły dotyczące plików i warunków eksperymentalnych, a także pomagają dostosować analizy do unikalnych cech każdego zestawu danych.
Wyobraź sobie katalog biblioteczny: podczas gdy książki zawierają właściwą treść (surowe dane), karty katalogowe dostarczają kluczowych informacji o każdej książce — kiedy została wydana, kto ją napisał, gdzie ją znaleźć (metadane). W pipeline'ach Nextflow metadane można wykorzystać do:
- Śledzenia informacji specyficznych dla pliku w trakcie całego workflow'u
- Konfigurowania procesów na podstawie właściwości pliku
- Grupowania powiązanych plików do wspólnej analizy
Cele szkolenia¶
W tym zadaniu pobocznym zbadamy, jak obsługiwać metadane w workflow'ach. Zaczynając od prostego arkusza danych (w bioinformatyce często nazywanego samplesheet), zawierającego podstawowe informacje o plikach, nauczysz się:
- Odczytywać i parsować metadane plików z plików CSV
- Tworzyć mapy metadanych i nimi manipulować
- Dodawać nowe pola metadanych podczas wykonywania workflow'u
- Używać metadanych do dostosowywania zachowania procesów
Te umiejętności pomogą Ci budować bardziej niezawodne i elastyczne pipeline'y, zdolne do obsługi złożonych relacji między plikami i wymagań przetwarzania.
Wymagania wstępne¶
Przed przystąpieniem do tego zadania pobocznego powinieneś/powinnaś:
- Ukończyć samouczek Hello Nextflow lub równoważny kurs dla początkujących.
- Swobodnie posługiwać się podstawowymi konceptami i mechanizmami Nextflow (procesy, kanały, operatory).
0. Pierwsze kroki¶
Otwórz środowisko szkoleniowe¶
Jeśli jeszcze tego nie zrobiłeś/zrobiłaś, otwórz środowisko szkoleniowe zgodnie z opisem w sekcji Konfiguracja środowiska.
Przejdź do katalogu projektu¶
Przejdźmy do katalogu, w którym znajdują się pliki tego samouczka.
Możesz ustawić VSCode tak, aby skupiał się na tym katalogu:
Przejrzyj materiały¶
Znajdziesz tu główny plik workflow'u oraz katalog data zawierający arkusz danych i kilka plików z danymi.
Zawartość katalogu
Workflow w pliku main.nf to szkielet, który będziesz stopniowo rozbudowywać w pełni działający workflow.
Arkusz danych zawiera ścieżki do plików z danymi oraz powiązane metadane, zorganizowane w 3 kolumnach:
id: oczywiste — identyfikator nadany plikowicharacter: nazwa postaci, której użyjemy później do rysowania różnych stworzeńdata: ścieżki do plików.txtzawierających pozdrowienia w różnych językach
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
Każdy plik z danymi zawiera tekst pozdrowienia w jednym z pięciu języków (fr: francuski, de: niemiecki, es: hiszpański, it: włoski, en: angielski).
Udostępniamy Ci również skonteneryzowane narzędzie do analizy języka o nazwie langid.
Zapoznaj się z zadaniem¶
Twoim wyzwaniem jest napisanie workflow'u Nextflow, który:
- Zidentyfikuje język w każdym pliku automatycznie
- Pogrupuje pliki według rodziny językowej (języki germańskie vs romańskie)
- Dostosuje przetwarzanie każdego pliku na podstawie jego języka i metadanych
- Zorganizuje wyniki według grupy językowej
To typowy wzorzec workflow'u, w którym metadane specyficzne dla pliku sterują decyzjami przetwarzania — dokładnie ten rodzaj problemu, który mapy metadanych rozwiązują elegancko.
Lista kontrolna gotowości¶
Myślisz, że jesteś gotowy/gotowa?
- Rozumiem cel tego kursu i jego wymagania wstępne
- Moje środowisko jest uruchomione i działa
- Ustawiłem/ustawiłam odpowiedni katalog roboczy
- Rozumiem zadanie
Jeśli możesz zaznaczyć wszystkie pola, możesz zaczynać.
1. Wczytaj metadane z arkusza danych¶
Otwórz plik workflow'u main.nf, aby przejrzeć szkielet, który dajemy Ci jako punkt startowy.
| main.nf | |
|---|---|
Widać, że skonfigurowaliśmy podstawową fabrykę kanałów do wczytania przykładowego arkusza danych jako pliku, ale nie odczyta ona jeszcze zawartości tego pliku. Zacznijmy od dodania tej funkcjonalności.
1.1. Wczytaj zawartość za pomocą splitCsv¶
Musimy wybrać operator, który odpowiednio sparsuje zawartość pliku przy minimalnym nakładzie pracy z naszej strony.
Ponieważ nasz arkusz danych jest w formacie CSV, do tego zadania nadaje się operator splitCsv, który wczytuje każdy wiersz pliku jako element kanału.
Wprowadź następujące zmiany, aby dodać operację splitCsv() do kodu budującego kanał, a także operację view() do sprawdzenia, czy zawartość pliku jest poprawnie wczytywana do kanału.
Zwróć uwagę, że używamy opcji header: true, aby poinformować Nextflow, że pierwszy wiersz pliku CSV ma być traktowany jako wiersz nagłówkowy.
Zobaczmy, co z tego wyjdzie. Uruchom workflow:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
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]
Widać, że operator zbudował mapę par klucz-wartość dla każdego wiersza pliku CSV, używając nagłówków kolumn jako kluczy dla odpowiadających im wartości.
Każdy wpis mapy odpowiada kolumnie w naszym arkuszu danych:
idcharacterrecording
Świetnie! Dzięki temu łatwo jest uzyskać dostęp do konkretnych pól każdego pliku.
Na przykład możemy uzyskać dostęp do identyfikatora pliku przez id lub do ścieżki pliku txt przez recording.
(Opcjonalnie) Więcej o mapach
W Groovy, języku programowania, na którym zbudowany jest Nextflow, mapa to struktura danych klucz-wartość podobna do słowników w Pythonie, obiektów w JavaScript czy haszy w Ruby.
Oto uruchamialny skrypt pokazujący, jak w praktyce zdefiniować mapę i uzyskać dostęp do jej zawartości:
#!/usr/bin/env nextflow
// Utwórz prostą mapę
def my_map = [id:'sampleA', character:'squirrel']
// Wydrukuj całą mapę
println "map: ${my_map}"
// Uzyskaj dostęp do poszczególnych wartości za pomocą notacji kropkowej
println "id: ${my_map.id}"
println "character: ${my_map.character}"
Mimo że nie ma właściwego bloku workflow, Nextflow może uruchomić ten skrypt jak workflow:
Oto czego możesz się spodziewać w wynikach:
1.2. Wybierz konkretne pola za pomocą map¶
Powiedzmy, że chcemy uzyskać dostęp do kolumny character z arkusza danych i ją wydrukować.
Możemy użyć operatora map Nextflow, aby iterować po każdym elemencie kanału i wybrać konkretnie wpis character z obiektu mapy.
Wprowadź następujące zmiany w workflow:
Uruchom workflow ponownie:
Wyjście polecenia
Sukces! Wykorzystaliśmy strukturę mapy wywodzącej się z naszego arkusza danych, aby uzyskać dostęp do wartości z poszczególnych kolumn dla każdego wiersza.
Teraz, gdy pomyślnie wczytaliśmy arkusz danych i mamy dostęp do danych w każdym wierszu, możemy zacząć implementować logikę naszego pipeline'u.
1.3. Zorganizuj metadane w 'mapę meta'¶
W obecnym stanie workflow'u pliki wejściowe (pod kluczem recording) i powiązane metadane (id, character) są na równorzędnej pozycji — jakby wszystko było w jednym dużym worku.
Praktyczną konsekwencją jest to, że każdy proces konsumujący ten kanał musiałby być skonfigurowany z uwzględnieniem tej struktury:
To jest w porządku, dopóki liczba kolumn w arkuszu danych się nie zmienia. Jednak dodanie nawet jednej kolumny do arkusza spowoduje, że kształt kanału przestanie odpowiadać temu, czego oczekuje proces, a workflow zacznie generować błędy. Utrudnia to też udostępnianie procesu innym osobom, które mogą mieć nieco inne dane wejściowe, i może prowadzić do konieczności zakodowania na stałe zmiennych w procesie, które nie są potrzebne w bloku skryptu.
Aby uniknąć tego problemu, musimy znaleźć sposób na utrzymanie spójnej struktury kanału niezależnie od liczby kolumn w arkuszu danych.
Możemy to zrobić, zbierając wszystkie metadane w jeden element krotki, który nazwiemy mapą metadanych, lub krócej — 'mapą meta'.
Wprowadź następujące zmiany w operacji map:
Przestrukturyzowaliśmy elementy kanału w krotkę składającą się z dwóch elementów: mapy meta i odpowiadającego jej obiektu pliku.
Uruchommy workflow:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
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]
Teraz każdy element kanału zawiera najpierw mapę meta, a następnie odpowiadający jej obiekt pliku:
[
[id:sampleA, character:squirrel],
/workspaces/training/side-quests/metadata/data/bonjour.txt
]
W rezultacie dodanie kolejnych kolumn do arkusza danych udostępni więcej metadanych w mapie meta, ale nie zmieni kształtu kanału.
Pozwala nam to pisać procesy konsumujące kanał bez konieczności zakodowywania na stałe elementów metadanych w specyfikacji wejścia:
Jest to powszechnie stosowana konwencja organizowania metadanych w workflow'ach Nextflow.
Podsumowanie¶
W tej sekcji nauczyłeś/nauczyłaś się:
- Dlaczego metadane są ważne: Przechowywanie metadanych razem z danymi zachowuje ważne informacje o plikach przez cały czas trwania workflow'u.
- Jak wczytywać arkusze danych: Używanie
splitCsvdo odczytu plików CSV z informacjami nagłówkowymi i przekształcania wierszy w ustrukturyzowane dane. - Jak tworzyć mapę meta: Oddzielanie metadanych od danych plikowych przy użyciu struktury krotki
[ [id:wartość, ...], plik ].
2. Manipulowanie metadanymi¶
Teraz, gdy mamy wczytane metadane, zróbmy z nimi coś pożytecznego!
Użyjemy narzędzia o nazwie langid do identyfikacji języka zawartego w pliku nagrania każdej postaci.
Narzędzie jest wstępnie wytrenowane na zestawie języków i dla podanego fragmentu tekstu zwróci przewidywanie języka oraz powiązany wynik prawdopodobieństwa, oba na stdout.
2.1. Zaimportuj proces i przejrzyj kod¶
Dostarczamy Ci gotowy moduł procesu o nazwie IDENTIFY_LANGUAGE, który opakowuje narzędzie langid — wystarczy dodać instrukcję include przed blokiem workflow.
Wprowadź następującą zmianę w workflow:
Możesz otworzyć plik modułu, aby przejrzeć jego kod:
Jak widać, definicja wejścia używa tej samej struktury tuple val(meta), path(file), którą właśnie zastosowaliśmy w naszym kanale wejściowym.
Definicja wyjścia jest zbudowana jako krotka o podobnej strukturze do wejścia, z tą różnicą, że zawiera również stdout jako trzeci element.
Ten wzorzec tuple val(meta), path(file), <wyjście> utrzymuje metadane powiązane zarówno z danymi wejściowymi, jak i wyjściowymi podczas przepływu przez pipeline.
Zwróć uwagę, że używamy tu kwalifikatora wyjścia stdout Nextflow, ponieważ narzędzie drukuje swoje wyniki bezpośrednio na konsolę zamiast zapisywać do pliku. Używamy też sed w wierszu poleceń, aby usunąć wynik prawdopodobieństwa, oczyścić string przez usunięcie znaków nowej linii i zwrócić tylko przewidywanie języka.
2.2. Dodaj wywołanie IDENTIFY_LANGUAGE¶
Teraz, gdy proces jest dostępny dla workflow'u, możemy dodać wywołanie procesu IDENTIFY_LANGUAGE, aby uruchomić go na kanale danych.
Wprowadź następujące zmiany w workflow:
| main.nf | |
|---|---|
Zwróć uwagę, że usunęliśmy oryginalną operację .view() z kodu budującego kanał.
Możemy teraz uruchomić workflow:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [voluminous_mcnulty] DSL2 - revision: f9bcfebabb
executor > local (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]
Doskonale! Mamy teraz przewidywanie języka, którym posługuje się każda postać.
Jak wspomniano wcześniej, w wynikach uwzględniliśmy też plik wejściowy i mapę meta, co oznacza, że oba pozostają powiązane z nowo wygenerowanymi informacjami. Przyda się to w następnym kroku.
Uwaga
Ogólnie rzecz biorąc, ten wzorzec utrzymywania mapy meta powiązanej z wynikami ułatwia kojarzenie powiązanych wyników, które mają te same identyfikatory.
Jak już wiesz, nie można polegać na kolejności elementów w kanałach przy kojarzeniu wyników między nimi. Zamiast tego należy używać kluczy do prawidłowego kojarzenia danych, a mapy meta zapewniają do tego idealną strukturę.
Ten przypadek użycia szczegółowo omawiamy w zadaniu pobocznym Splitting & Grouping.
2.3. Wzbogać metadane o wyniki procesu¶
Ponieważ wyniki, które właśnie uzyskaliśmy, są same w sobie formą metadanych o zawartości plików, warto byłoby dodać je do naszej mapy meta.
Nie chcemy jednak modyfikować istniejącej mapy meta w miejscu. Z technicznego punktu widzenia jest to możliwe, ale niebezpieczne.
Zamiast tego utworzymy nową mapę meta zawierającą zawartość istniejącej mapy meta plus nową parę klucz-wartość lang: lang_id przechowującą nowe informacje, używając operatora + (funkcja Groovy).
Połączymy to z operacją map, aby zastąpić starą mapę nową.
Oto zmiany, które należy wprowadzić w workflow:
Jeśli nie znasz jeszcze operatora + lub jeśli coś jest niejasne, poświęć kilka minut na przeczytanie szczegółowego wyjaśnienia poniżej.
Tworzenie nowej mapy meta za pomocą operatora +
Po pierwsze, musisz wiedzieć, że możemy scalić zawartość dwóch map za pomocą operatora Groovy +.
Powiedzmy, że mamy następujące mapy:
Możemy je scalić w ten sposób:
Zawartość new_map będzie następująca:
Świetnie!
Ale co, jeśli chcesz dodać pole, które nie jest jeszcze częścią żadnej mapy?
Powiedzmy, że zaczynasz ponownie od map1, ale przewidywanie języka nie jest w swojej własnej mapie (nie ma map2).
Zamiast tego jest przechowywane w zmiennej o nazwie lang_id, a Ty wiesz, że chcesz zapisać jej wartość ('fr') pod kluczem lang.
Możesz zrobić następująco:
Tutaj [lang: new_info] tworzy nową anonimową mapę w locie, a map1 + scala map1 z nową anonimową mapą, dając tę samą zawartość new_map co poprzednio.
Eleganckie, prawda?
Teraz przełóżmy to na kontekst operacji channel.map() w Nextflow.
Kod przyjmuje postać:
Robi to następujące rzeczy:
map1, lang_id ->pobiera dwa elementy z krotki[map1 + [lang: lang_id]]tworzy nową mapę zgodnie z opisem powyżej
Wynikiem jest pojedyncza anonimowa mapa o tej samej zawartości co new_map w naszym przykładzie.
Efektywnie przekształciliśmy:
w:
Mam nadzieję, że widzisz, że jeśli zmienimy map1 na meta, to właściwie wszystko, czego potrzebujemy, aby dodać przewidywanie języka do naszej mapy meta w workflow.
Jest jednak jedna rzecz do uwzględnienia!
W przypadku naszego workflow'u musimy też wziąć pod uwagę obecność obiektu file w krotce, która składa się z meta, file, lang_id.
Kod przyjmuje więc postać:
Jeśli trudno Ci śledzić, dlaczego file wydaje się przemieszczać w operacji map, wyobraź sobie, że zamiast [meta + [lang: lang_id], file] ten wiersz brzmi [new_map, file].
Powinno to wyjaśnić, że po prostu zostawiamy file na jego oryginalnym miejscu na drugiej pozycji w krotce. Wzięliśmy wartość new_info i wbudowaliśmy ją w mapę na pierwszej pozycji.
I to prowadzi nas z powrotem do struktury kanału tuple val(meta), path(file)!
Gdy już rozumiesz, co robi ten kod, uruchom workflow, aby sprawdzić, czy zadziałał:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [cheeky_fermat] DSL2 - revision: d096281ee4
[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]
Tak, to się zgadza!
Starannie przeorganizowaliśmy wynik procesu z meta, file, lang_id tak, że lang_id jest teraz jednym z kluczy w mapie meta, a krotki kanału ponownie pasują do modelu meta, file.
2.4. Przypisz grupę językową za pomocą instrukcji warunkowych¶
Teraz, gdy mamy przewidywania języków, użyjmy tych informacji do przypisania nowych grupowań.
W naszych przykładowych danych języki używane przez nasze postacie można podzielić na języki germańskie (angielski, niemiecki) i romańskie (francuski, hiszpański, włoski). Przydatne byłoby posiadanie tej klasyfikacji w łatwo dostępnym miejscu w dalszej części pipeline'u, więc dodajmy tę informację do mapy meta.
I dobra wiadomość — to kolejny przypadek, który doskonale nadaje się do użycia operatora map!
Konkretnie zdefiniujemy zmienną o nazwie lang_group i użyjemy prostej logiki warunkowej, aby określić, jaką wartość przypisać do lang_group dla każdego elementu danych.
Ogólna składnia będzie wyglądać tak:
.map { meta, file ->
// tutaj umieszczamy logikę warunkową definiującą lang_group
[meta + [lang_group: lang_group], file]
}
Widać, że jest to bardzo podobne do operacji scalania map w locie, której użyliśmy w poprzednim kroku. Musimy tylko napisać instrukcje warunkowe.
Oto logika warunkowa, którą chcemy zastosować:
- Zdefiniuj zmienną o nazwie
lang_groupz domyślną wartością'unknown'. - Jeśli
langto język niemiecki ('de') lub angielski ('en'), zmieńlang_groupnagermanic. - W przeciwnym razie, jeśli
langnależy do listy zawierającej francuski ('fr'), hiszpański ('es') i włoski ('it'), zmieńlang_groupnaromance.
Spróbuj napisać to samodzielnie, jeśli już wiesz, jak pisać instrukcje warunkowe w Nextflow.
Wskazówka
Dostęp do wartości lang w operacji map uzyskasz przez meta.lang.
Powinieneś/powinnaś wprowadzić następujące zmiany w workflow:
Oto kluczowe punkty:
- Używamy
def lang_group = "unknown", aby utworzyć zmiennąlang_groupz domyślną wartościąunknown. - Używamy struktury
if {} else if {}dla logiki warunkowej, z alternatywnymi testami.equals()dla dwóch języków germańskich i testem przynależności do listy dla trzech języków romańskich. - Używamy operacji scalania
meta + [lang_group:lang_group]jak poprzednio, aby wygenerować zaktualizowaną mapę meta.
Gdy wszystko jest jasne, uruchom workflow ponownie, aby zobaczyć wynik:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [wise_almeida] DSL2 - revision: 46778c3cd0
[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]
Jak widać, elementy kanału zachowują strukturę [meta, file], ale mapa meta zawiera teraz tę nową klasyfikację.
Podsumowanie¶
W tej sekcji nauczyłeś/nauczyłaś się:
- Stosować metadane wejściowe do kanałów wyjściowych: Kopiowanie metadanych w ten sposób pozwala nam później kojarzyć wyniki na podstawie ich zawartości.
- Tworzyć własne klucze: Dodałeś/dodałaś dwa nowe klucze do mapy meta, scalając je za pomocą
meta + [nowy_klucz:wartość]z istniejącą mapą meta — jeden oparty na wartości obliczonej przez proces, drugi na warunku ustawionym w operatorzemap.
Pozwala to kojarzyć nowe i istniejące metadane z plikami w miarę postępu przez pipeline. Nawet jeśli nie używasz metadanych bezpośrednio w procesie, utrzymywanie mapy meta powiązanej z danymi ułatwia przechowywanie wszystkich istotnych informacji razem.
3. Używanie informacji z mapy meta w procesie¶
Teraz, gdy wiesz, jak tworzyć i aktualizować mapę meta, możemy przejść do naprawdę ciekawej części: faktycznego używania metadanych w procesie.
Konkretnie dodamy drugi krok do naszego workflow'u, który narysuje każde zwierzę jako ASCII art i sprawi, że wypowie nagrany tekst w dymku.
Zrobimy to za pomocą narzędzia o nazwie cowpy.
Co robi cowpy?
cowpy to narzędzie wiersza poleceń generujące ASCII art do wyświetlania dowolnych tekstów w zabawny sposób.
Jest to implementacja w Pythonie klasycznego narzędzia cowsay autorstwa Tony'ego Monroe'a.
______________________________________________________
< Hello Nextflow >
------------------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Opcjonalnie możesz wybrać postać (lub 'cowacter') zamiast domyślnej krowy.
Jeśli przerabiałeś/przerabiałaś kurs Hello Nextflow, widziałeś/widziałaś już to narzędzie w akcji. Jeśli nie — nie martw się; omówimy wszystko, co musisz wiedzieć, w miarę postępu.
3.1. Zaimportuj proces i przejrzyj kod¶
Dostarczamy Ci gotowy moduł procesu o nazwie COWPY, który opakowuje narzędzie cowpy — wystarczy dodać instrukcję include przed blokiem workflow.
Wprowadź następującą zmianę w workflow:
Możesz otworzyć plik modułu, aby przejrzeć jego kod:
Jak widać, ten proces jest obecnie zaprojektowany tak, aby przyjmować plik wejściowy (zawierający tekst do wyświetlenia) i wartość określającą postać, która ma być narysowana jako ASCII art — zazwyczaj podawaną na poziomie workflow'u przez parametr wiersza poleceń.
3.2. Przekaż pole mapy meta jako wejście¶
Gdy używaliśmy narzędzia cowpy w kursie Hello Nextflow, używaliśmy parametru wiersza poleceń do określenia, jakiej postaci użyć do narysowania końcowego obrazu.
Miało to sens, ponieważ generowaliśmy tylko jeden obraz na uruchomienie pipeline'u.
Jednak w tym samouczku chcemy wygenerować odpowiedni obraz dla każdego przetwarzanego podmiotu, więc użycie parametru wiersza poleceń byłoby zbyt ograniczające.
Dobra wiadomość: mamy kolumnę character w naszym arkuszu danych, a co za tym idzie — w naszej mapie meta.
Użyjmy jej do ustawienia postaci, której proces powinien użyć dla każdego wpisu.
W tym celu musimy zrobić trzy rzeczy:
- Nadać nazwę kanałowi wyjściowemu poprzedniego procesu, aby wygodniej na nim operować.
- Określić, jak uzyskać dostęp do interesujących nas informacji.
- Dodać wywołanie drugiego procesu i odpowiednio przekazać informacje.
Zaczynajmy.
3.2.1. Nadaj nazwę poprzedniemu kanałowi wyjściowemu¶
Poprzednie manipulacje stosowaliśmy bezpośrednio na kanale wyjściowym pierwszego procesu, IDENTIFY_LANGUAGE.out.
Aby przekazać zawartość tego kanału do następnego procesu (i zrobić to w sposób czytelny i przejrzysty), chcemy nadać mu własną nazwę: ch_languages.
Możemy to zrobić za pomocą operatora set.
W głównym workflow zastąp operator .view() przez .set { ch_languages } i dodaj wiersz sprawdzający, czy możemy odwoływać się do kanału po nazwie.
Uruchommy to:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
Launching `./main.nf` [friendly_austin] DSL2 - revision: 3dbe460fd6
[36/cca6a7] IDENTIFY_LANGUAGE (7) | 7 of 7 ✔
[[id:sampleB, character:tux, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/work/e2/6db2402d83cf72081bcd2d11784714/guten_tag.txt]
[[id:sampleA, character:squirrel, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/work/6c/114c818317d169457d6e7336d5d55b/bonjour.txt]
[[id:sampleC, character:sheep, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/work/55/68c69c5efb527f3604ddb3daab8057/hallo.txt]
[[id:sampleD, character:turkey, lang:en, lang_group:germanic], /workspaces/training/side-quests/metadata/work/2a/4752055ccb5d1370b0ef9da41d3993/hello.txt]
[[id:sampleE, character:stegosaurus, lang:es, lang_group:romance], /workspaces/training/side-quests/metadata/work/f4/fcd3186dc666d5d239ffa6c37d125d/hola.txt]
[[id:sampleF, character:moose, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/work/c3/3b2627f733f278a7088332a5806108/salut.txt]
[[id:sampleG, character:turtle, lang:it, lang_group:romance], /workspaces/training/side-quests/metadata/work/36/cca6a7dbfa26ac24f9329787a32e9d/ciao.txt]
Potwierdza to, że możemy teraz odwoływać się do kanału po nazwie.
3.2.2. Uzyskaj dostęp do pliku i metadanych postaci¶
Z kodu modułu wiemy, że proces COWPY oczekuje pliku tekstowego i wartości character.
Aby napisać wywołanie procesu COWPY, musimy tylko wiedzieć, jak wyodrębnić odpowiedni obiekt pliku i metadane z każdego elementu kanału.
Jak to często bywa, najprostszym sposobem jest użycie operacji map.
Nasz kanał zawiera krotki o strukturze [meta, file], więc możemy uzyskać dostęp do obiektu file bezpośrednio, a do wartości character przechowywanej w mapie meta — przez meta.character.
W głównym workflow wprowadź następujące zmiany:
Zwróć uwagę, że używamy domknięć (takich jak { file -> "File: " + file }) aby wyniki operacji .view były bardziej czytelne.
Uruchommy to:
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
Launching `./main.nf` [cheesy_cantor] DSL2 - revision: 15af9c1ec7
[43/05df08] IDENTIFY_LANGUAGE (7) [100%] 7 of 7, cached: 7 ✔
Character: squirrel
File: /workspaces/training/side-quests/metadata/work/8d/4b9498bbccb7a74f04e41877cdc3e5/bonjour.txt
File: /workspaces/training/side-quests/metadata/work/d3/604274985406e40d79021dea658e60/guten_tag.txt
Character: tux
Character: turkey
File: /workspaces/training/side-quests/metadata/work/d4/fafcc9415b61d2b0fea872e6a05e8a/hello.txt
File: /workspaces/training/side-quests/metadata/work/02/468ac9efb27f636715e8144b37e9a7/hallo.txt
Character: sheep
Character: moose
Character: stegosaurus
File: /workspaces/training/side-quests/metadata/work/d4/61a7e1188b4f2742bc72004e226eca/salut.txt
File: /workspaces/training/side-quests/metadata/work/ae/68364be238c11149c588bf6fc858b1/hola.txt
File: /workspaces/training/side-quests/metadata/work/43/05df081af5d879ab52e5828fa0357e/ciao.txt
Character: turtle
Ścieżki plików i wartości postaci mogą pojawić się w innej kolejności w Twoich wynikach.
Potwierdza to, że możemy uzyskać dostęp do pliku i postaci dla każdego elementu kanału.
3.2.3. Wywołaj proces COWPY¶
Teraz złóżmy to wszystko razem i faktycznie wywołajmy proces COWPY na kanale ch_languages.
W głównym workflow wprowadź następujące zmiany:
Widać, że po prostu kopiujemy dwie operacje map (bez instrukcji .view()) jako wejścia do wywołania procesu.
Tylko nie zapomnij o przecinku między nimi!
To trochę niezgrabne, ale w następnej sekcji zobaczymy, jak to poprawić.
Uruchommy to:
Wyjście polecenia
Jeśli zajrzysz do katalogu wyników, powinieneś/powinnaś zobaczyć poszczególne pliki zawierające ASCII art każdego pozdrowienia wypowiadanego przez odpowiednią postać.
Zawartość katalogu i przykładowego pliku
Pokazuje to, że byliśmy w stanie użyć informacji z mapy meta do sparametryzowania polecenia w drugim kroku pipeline'u.
Jak jednak wspomniano, część kodu była nieco niezgrabna, ponieważ musieliśmy rozpakowywać metadane jeszcze w kontekście ciała workflow'u. To podejście sprawdza się dobrze przy używaniu niewielkiej liczby pól z mapy meta, ale słabo skaluje się, gdy chcemy używać ich znacznie więcej.
Istnieje inny operator o nazwie multiMap(), który pozwala nieco to usprawnić, ale nawet wtedy nie jest to idealne rozwiązanie.
(Opcjonalnie) Alternatywna wersja z multiMap()
Możesz się zastanawiać, dlaczego nie możemy napisać jednej operacji map(), która zwraca zarówno file, jak i character — otóż zwróciłaby je jako krotkę.
Musieliśmy napisać dwie oddzielne operacje map(), aby przekazać elementy file i character do procesu osobno.
Technicznie istnieje inny sposób na zrobienie tego przez jedną operację mapowania, używając operatora multiMap(), który jest w stanie emitować wiele kanałów.
Na przykład możesz zastąpić wywołanie COWPY powyżej następującym kodem:
Daje to dokładnie ten sam wynik.
W obu przypadkach jest to trochę niezręczne, że musimy wykonywać część rozpakowywania na poziomie workflow'u.
Lepiej byłoby móc przekazać całą mapę meta do procesu i wybrać to, czego potrzebujemy, już wewnątrz niego.
3.3. Przekaż i użyj całej mapy meta¶
Celem mapy meta jest przecież przekazywanie wszystkich metadanych razem jako pakietu. Jedynym powodem, dla którego nie mogliśmy tego zrobić powyżej, jest to, że proces nie jest skonfigurowany do przyjmowania mapy meta. Ale ponieważ kontrolujemy kod procesu, możemy to zmienić.
Zmodyfikujmy proces COWPY, aby przyjmował strukturę krotki [meta, file], której użyliśmy w pierwszym procesie, co pozwoli nam uprościć workflow.
W tym celu musimy zrobić trzy rzeczy:
- Zmodyfikować definicje wejść modułu procesu
COWPY - Zaktualizować polecenie procesu, aby używało mapy meta
- Zaktualizować wywołanie procesu w ciele workflow'u
Gotowy/gotowa? Zaczynamy!
3.3.1. Zmodyfikuj wejście modułu COWPY¶
Wprowadź następujące zmiany w pliku modułu cowpy.nf:
Pozwala nam to używać struktury krotki [meta, file] omówionej wcześniej w samouczku.
Zwróć uwagę, że nie zaktualizowaliśmy definicji wyjścia procesu, aby zwracała mapę meta — w celu uproszczenia samouczka. Możesz jednak zrobić to samodzielnie jako ćwiczenie, wzorując się na procesie IDENTIFY_LANGUAGE.
3.3.2. Zaktualizuj polecenie, aby używało pola mapy meta¶
Cała mapa meta jest teraz dostępna wewnątrz procesu, więc możemy odwoływać się do zawartych w niej informacji bezpośrednio z bloku polecenia.
Wprowadź następujące zmiany w pliku modułu cowpy.nf:
Zastąpiliśmy odwołanie do wartości character przekazywanej wcześniej jako samodzielne wejście wartością przechowywaną w mapie meta, do której odwołujemy się przez meta.character.
Teraz zaktualizujmy odpowiednio wywołanie procesu.
3.3.3. Zaktualizuj wywołanie procesu i uruchom go¶
Proces oczekuje teraz wejścia w strukturze krotki [meta, file], co jest dokładnie tym, co zwraca poprzedni proces — możemy więc po prostu przekazać cały kanał ch_languages do procesu COWPY.
Wprowadź następujące zmiany w głównym workflow:
To znacznie upraszcza wywołanie!
Usuńmy wyniki poprzedniego uruchomienia i uruchommy workflow:
Wyjście polecenia
Jeśli zajrzysz do katalogu wyników, powinieneś/powinnaś zobaczyć te same wyniki co poprzednio — czyli poszczególne pliki zawierające ASCII art każdego pozdrowienia wypowiadanego przez odpowiednią postać.
Zawartość katalogu
Wyniki są więc takie same jak poprzednio, ale kod jest prostszy.
Oczywiście zakłada to, że możesz modyfikować kod procesu.
W niektórych przypadkach możesz być zmuszony/zmuszona do korzystania z istniejących procesów, których nie możesz modyfikować, co ogranicza Twoje możliwości.
Dobra wiadomość dla tych, którzy planują używać modułów z projektu nf-core, jest taka, że moduły nf-core są wszystkie skonfigurowane do używania struktury krotki [meta, file] jako standardu.
3.4. Rozwiązywanie problemów z brakującymi wymaganymi wejściami¶
Wartość character jest wymagana, aby proces COWPY działał poprawnie.
Jeśli nie ustawimy dla niej wartości domyślnej w pliku konfiguracyjnym, MUSIMY podać ją w arkuszu danych.
Co się stanie, jeśli tego nie zrobimy? Zależy to od zawartości arkusza danych i wersji workflow'u, której używamy.
3.4.1. Kolumna character istnieje, ale jest pusta¶
Powiedzmy, że usuwamy wartość character dla jednego z wpisów w naszym arkuszu danych, symulując błąd zbierania danych:
W obu wersjach workflow'u, których używaliśmy powyżej, klucz character zostanie utworzony dla wszystkich wpisów podczas wczytywania arkusza danych, ale dla sampleA wartość będzie pustym stringiem.
Spowoduje to błąd.
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
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 > cowpy-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
Gdy Nextflow uruchamia wiersz poleceń cowpy dla tej próbki, ${meta.character} jest wypełniane pustym stringiem, więc narzędzie cowpy zgłasza błąd informujący, że nie podano żadnej wartości dla argumentu -c.
3.4.2. Kolumna character nie istnieje w arkuszu danych¶
Teraz powiedzmy, że całkowicie usuwamy kolumnę character z naszego arkusza danych:
W tym przypadku klucz character w ogóle nie zostanie utworzony podczas wczytywania arkusza danych.
3.4.2.1. Wartość dostępna na poziomie workflow'u¶
Jeśli używamy wersji kodu z sekcji 3.2, Nextflow spróbuje uzyskać dostęp do klucza character w mapie meta PRZED wywołaniem procesu COWPY.
Nie znajdzie żadnych elementów pasujących do instrukcji, więc w ogóle nie uruchomi COWPY.
Wyjście polecenia
Z punktu widzenia Nextflow ten workflow zakończył się pomyślnie! Jednak żadne z oczekiwanych wyników nie zostaną wygenerowane.
3.4.2.2. Wartość dostępna na poziomie procesu¶
Jeśli używamy wersji z sekcji 3.3, Nextflow przekaże całą mapę meta do procesu COWPY i spróbuje uruchomić polecenie.
Spowoduje to błąd, ale inny niż w pierwszym przypadku.
Wyjście polecenia
N E X T F L O W ~ version 25.10.2
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 > cowpy-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
Dzieje się tak, ponieważ meta.character nie istnieje, więc próba uzyskania do niego dostępu zwraca null. W rezultacie Nextflow dosłownie wstawia null do wiersza poleceń, co oczywiście nie jest rozpoznawane przez narzędzie cowpy.
3.4.3. Rozwiązania¶
Poza ustawieniem wartości domyślnej w konfiguracji workflow'u, możemy zrobić dwie rzeczy, aby obsłużyć to bardziej niezawodnie:
-
Zaimplementować walidację wejść w workflow'u, aby upewnić się, że arkusz danych zawiera wszystkie wymagane informacje. Możesz znaleźć wprowadzenie do walidacji wejść w kursie szkoleniowym Hello nf-core.
-
Jeśli chcesz mieć pewność, że każdy, kto używa Twojego modułu procesu, może natychmiast zidentyfikować wymagane wejścia, możesz też uczynić wymaganą właściwość metadanych jawnym wejściem.
Oto przykład, jak by to działało.
Po pierwsze, na poziomie procesu zaktualizuj definicję wejścia w następujący sposób:
Następnie na poziomie workflow'u użyj operacji mapowania, aby wyodrębnić właściwość character z metadanych i uczynić ją jawnym składnikiem krotki wejściowej:
To podejście ma tę zaletę, że jawnie pokazuje, że character jest wymagany, i ułatwia ponowne wdrożenie procesu w innych kontekstach.
Podkreśla to ważną zasadę projektowania:
Używaj mapy meta dla opcjonalnych, opisowych informacji, ale wyodrębniaj wymagane wartości jako jawne wejścia.
Mapa meta doskonale nadaje się do utrzymywania czystych struktur kanałów i zapobiegania arbitralnym strukturom, ale dla obowiązkowych elementów bezpośrednio przywoływanych w procesie wyodrębnienie ich jako jawnych wejść tworzy bardziej niezawodny i łatwy w utrzymaniu kod.
Podsumowanie¶
W tej sekcji nauczyłeś/nauczyłaś się, jak wykorzystywać metadane do dostosowywania wykonania procesu, uzyskując do nich dostęp zarówno na poziomie workflow'u, jak i na poziomie procesu.
Ćwiczenie uzupełniające¶
Jeśli chcesz poćwiczyć używanie informacji z mapy meta wewnątrz procesu, spróbuj użyć innych informacji z mapy meta, takich jak lang i lang_group, aby dostosować nazwy i/lub organizację wyników.
Na przykład spróbuj zmodyfikować kod, aby uzyskać następujący wynik:
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
Podsumowanie¶
W tym zadaniu pobocznym zbadałeś/zbadałaś, jak efektywnie pracować z metadanymi w workflow'ach Nextflow.
Ten wzorzec jawnego przechowywania metadanych razem z danymi jest podstawową dobrą praktyką w Nextflow, oferującą kilka zalet w porównaniu z zakodowywaniem informacji o plikach na stałe:
- Metadane pliku pozostają powiązane z plikami przez cały czas trwania workflow'u
- Zachowanie procesu można dostosować dla każdego pliku
- Organizacja wyników może odzwierciedlać metadane pliku
- Informacje o plikach można rozszerzać podczas wykonywania pipeline'u
Stosowanie tego wzorca we własnej pracy pozwoli Ci budować niezawodne, łatwe w utrzymaniu workflow'y bioinformatyczne.
Kluczowe wzorce¶
-
Odczytywanie i strukturyzowanie metadanych: Odczytywanie plików CSV i tworzenie zorganizowanych map metadanych, które pozostają powiązane z plikami danych.
-
Rozszerzanie metadanych podczas workflow'u: Dodawanie nowych informacji do metadanych w miarę postępu pipeline'u przez dodawanie wyników procesów i wyprowadzanie wartości przez logikę warunkową.
- Dodawanie nowych kluczy na podstawie wyników procesu
- Dodawanie nowych kluczy za pomocą klauzuli warunkowej
-
Dostosowywanie zachowania procesu: Używanie metadanych wewnątrz procesu.
Dodatkowe zasoby¶
Co dalej?¶
Wróć do menu zadań pobocznych lub kliknij przycisk w prawym dolnym rogu strony, aby przejść do następnego tematu na liście.