Analiza bioinformatyczna jest nierozerwalnym elementem całego procesu badania sekwencji DNA. Praca bioinformatyków jest więc kluczowa w realizacji usługi sekwencjonowania. Aby efektywnie przeprowadzić analizę bioinformatyczną potrzebne są odpowiednie narzędzia i rozwiązania. Jednym z takich rozwiązań jest potokowe przetwarzanie danych.
Czym jest przetwarzanie potokowe?
Potoki przetwarzania danych są sposobem na sekwencyjne przetwarzanie informacji. Zadania takie jak sortowanie, czy wyszukiwanie duplikatów , są podzielone na osobne bloki, które łączą się ze sobą szeregowo. Właśnie stąd wiemy jaka jest prawidłowa kolejność wykonania wszystkich zadań.
Do czego służy przetwarzanie potokowe?
Potokowe przetwarzanie danych w codziennej pracy bioinformatyka wykorzystywane jest przede wszystkim po to, aby zaoszczędzić czas i zwiększyć efektywność pracy. Przykładowo, jeśli chcemy wyświetlić tylko te linie z pliku, które rozpoczynają się od “>”, a następnie posortować je, nie musimy stosować komendy wyszukania odpowiednich linii i zapisywać je do osobnego pliku, a dopiero później sortować ten plik. Zamiast tego możemy jednorazowo wyszukać określone linie i skierować je potokiem bezpośrednio do komendy sortującej.
Jak używać potoków w konsoli?
Potok danych w systemach opartych o UNIX przekierowujemy w konsoli za pomocą znaku “|”. Jest on łącznikiem między dwoma blokami komend. Zamiast tworzyć plik pośredni:
grep "^>" file_in.txt > file_tmp.txt sort file_tmp.txt > file_out.txt
możemy przekazać dane potokiem:
grep "^>" file_in.txt | sort > file_out.txt
Tym sposobem nasze polecenie jest jednolinijkową komendą, z której jasno wynika przepływ danych, bez tworzenia pośrednich plików, o których należy pamiętać, aby je usunąć, gdy nie będą już potrzebne.
Komenda łączona za pomocą potoków może być dowolnie długa:
command1 | command2 | command3 | command3 | … | commandX
Zalety potokowego przetwarzania danych
- Przy przetwarzaniu potokowym dane są przechowywane w szybkiej pamięci RAM, dzięki temu unikamy tworzenia plików pośrednich – ich zapis na dysku i ponowny odczyt przez inny program powoduje spowolnienie przetwarzania ze względu na konieczność użycia dysków twardych, szczególnie przy przetwarzaniu dużych wolumenów danych.
- Zdecydowanie bardziej wygodne i szybsze jest sporządzanie całych skryptów i poleceń. Zajmują one często tylko jedną linię kodu.
- Takie rozwiązanie daje również większą kontrolę nad tworzonymi plikami i przepływem danych, a także sprawia wrażenie czystego i przejrzystego kod.
Oczywiście zdarzają się również sytuacje, w których potok danych nie jest możliwy. Takim przykładem może być konieczność obróbki danych przez program, który kilkukrotnie wczytuje dane wejściowe. W trakcie przetwarzania potokowego dane przekazywane są do programu tylko jeden raz, w czasie rzeczywistym.
Przykład wykorzystania przetwarzania potokowego
Jako przykład przedstawiamy analizę danych o taksonomii wpisów w bazie danych sekwencji genomowych NCBI. Z pliku https://ftp.ncbi.nlm.nih.gov/pub/taxonomy/taxcat_readme.txt możemy wyczytać, iż w bazie przechowywane są genomy organizmów przynależnych do następujących kategorii/królestw:
A = Archaea B = Bacteria E = Eukaryota V = Viruses and Viroids U = Unclassified O = Other
Pierwsze spojrzenie na dane: ściągamy plik z taksonomicznymi ID, rozpakowujemy go i wybieramy 5 losowych wierszy spośród pierwszych 5000.
URL="https://ftp.ncbi.nlm.nih.gov/pub/taxonomy/taxcat.zip" curl -s "$URL" | zcat | head -n 5000 | shuf | head -n 5 E 3095 3095 E 8737 8737 B 1066 1066 B 447 66973 E 8420 8420
Dane mają postać Kategoria SpeciesTaxonomicID TaxonomicID
. Jeżeli SpeciesTaxonomicID
jest równy TaxonomicID
to wpis dotyczy gatunku, w innym przypadku wpis dotyczy organizmu będącego podgatunkiem. Przykładowo, wiersz B 447 66973
dotyczy bakterii Legionella bozemanii serogroup 1 podgatunku bakterii gatunku Fluoribacter bozemanae.
Zliczymy teraz ile organizmów odnotowanych jest taksonomicznie w każdej z kategorii/w każdym królestwie. Podobnie jak poprzednio ściągamy plik, rozpakowujemy, przepisujemy tylko pierwszą kolumnę, sortujemy, zliczamy ile jest kolejnych takich samych wierszy i ostatecznie sortujemy w odwrotnej kolejności by wypisać począwszy od kategorii o największej liczbie organizmów. Wynik przekierowujemy jednocześnie na ekran i do pliku komendą tee
.
curl -s "$URL" | zcat \ | awk '{print $1}' | sort -T /dev/shm | uniq -c | sort -rn \ | tee all_stats.txt 1338805 E 495917 B 203340 V 15530 O 12701 A 958 U
Opcja -T /dev/shm
na systemach Linux powoduje, iż przy sortowaniu dużych plików powstające pliki tymczasowe umieszczane są w pamięci RAM – daje to mniejsze obciążenie dysku i szybsze sortowanie kosztem zużycia RAM.
Znane są już dane ilościowe gatunków wraz z podgatunkami, w następnym kroku chcielibyśmy zliczyć ile gatunków występuje w każdej kategorii. W tym celu możemy zastosować filtr grep -e '\s([0-9]+)\s+\1'
, pozostawi on jedynie te wiersze, dla których druga i trzecia kolumna są identyczne.
Dodatkowo, w poniższej komendzie używamy tee
(duplikacja strumienia) oraz przekierowania bash strumienia do kolejnego polecenia >(polecenie_potokowe|...)
curl -s "$URL" | zcat \ | tee >( awk '{print $1}' | sort -T /dev/shm | uniq -c | sort -rn \ | tee all_stats.txt >&2 ) \ | grep -e '\s\([0-9]\+\)\s\+\1' \ | awk '{print $1}' | sort -T /dev/shm | uniq -c | sort -rn \ | tee species_stats.txt 1338805 E 495917 B 203340 V 15530 O 12701 A 958 U 1302432 E 451723 B 34725 V 15530 O 12341 A 958 U
Wyniki zostały także zapisane w plikach all_stats.txt species_stats.txt
. Możemy zaobserwować, iż największa różnica w liczbie odnotowanych podgatunków względem liczby odnotowanych gatunków jest w domenie wirusów. Przydatna jest tu komenda tail
, która wypisuje zawartość plików wraz z ich nazwami.
tail -n +1 *stats.txt
==> all_stats.txt <== 1338805 E 495917 B 203340 V 15530 O 12701 A 958 U ==> species_stats.txt <== 1302432 E 451723 B 34725 V 15530 O 12341 A 958 U