Jak wyodrębnić wiele bitów informacji, które pojawiają się w różnych liniach w tym samym pliku tekstowym

8

Próbuję wyodrębnić identyfikator sekwencji i numer klastra, które występują w różnych liniach w tym samym pliku tekstowym.

Wygląda jak wejście

>Cluster 72
0   319aa, >O311_01007... *
>Cluster 73
0   318aa, >1494_00753... *
1   318aa, >1621_00002... at 99.69%
2   318aa, >1622_00575... at 99.37%
3   318aa, >1633_00422... at 99.37%
4   318aa, >O136_00307... at 99.69%
>Cluster 74
0   318aa, >O139_01028... *
1   318aa, >O142_00961... at 99.69%
>Cluster 75
0   318aa, >O300_00856... *

Pożądanym wynikiem jest identyfikator sekwencji w jednej kolumnie i odpowiedni numer klastra w drugiej.

>O311_01007  72
>1494_00753  73
>1621_00002  73
>1622_00575  73
>1633_00422  73
>O136_00307  73
>O139_01028  74
>O142_00961  74
>O300_00856  75

Czy ktoś może w tym pomóc?

Tim
źródło
Czy identyfikator sekwencji zawsze będzie polem 3d oddzielonym spacją na liniach, które nie zaczynają się od >? Być może zainteresuje Cię także nasza siostrzana strona Bioinformatyka .
terdon

Odpowiedzi:

13

Z awk:

awk -F '[. ]*' 'NF == 2 {id = $2; next} {print $3, id}' input-file
  • dzielimy pola na spacje lub kropki za pomocą -F '[. ]*'
  • z liniami dwóch pól ( >Clusterliniami) zapisz drugie pole jako identyfikator i przejdź do następnej linii
  • z innymi wierszami wydrukuj trzecie pole i zapisany identyfikator
muru
źródło
Zamiast wpisywać liczbę pól, lepiej może $1 == ">Cluster"zamiast tego jawnie szukać NF == 2, w zależności od tego, co jeszcze może znajdować się w pliku.
Monty Harder
5

Możesz użyć awkdo tego:

awk '/>Cluster/{
      c=$2;
      next
    }{
      print substr($3,2,length($3)-4), c
    }' file

Pierwsza instrukcja bloku przechwytuje identyfikator klastra. Druga instrukcja blokowa (domyślna) wyodrębnia potrzebne dane i drukuje je.

oliv
źródło
Nie musisz podawać się " "za argument print. Wystarczy użyć przecinka, aby oddzielić argumenty, a on użyje OFS, domyślnej spacji, aby oddzielić argumenty.
muru
4

Oto alternatywa dla Ruby jako jednej linijki:

ruby -ne 'case $_; when /^>Cluster (\d+)/;id = $1;when /, (>\w{4}_\w{5})\.\.\./;puts "#{$1} #{id}";end' input_file

lub rozłożone na wiele linii:

ruby -ne 'case $_
when /^>Cluster (\d+)/
  id = $1
when /, (>\w{4}_\w{5})\.\.\./
  puts "#{$1} #{id}"
end' input_file

Myślę, że jest to bardziej czytelne niż awkwersja, jeśli znasz Ruby i regexen. Jako bonus, ten kod może być nieco bardziej niezawodny niż zwykłe dzielenie linii, ponieważ szuka otaczającego tekstu.

Eric Duminil
źródło
1

Perl:

$ perl -ne 'if(/^>.*?(\d+)/){$n=$1;}else{ s/.*(>[^.]+).*/$1 $n/; print}' file 
>O311_01007 72
>1494_00753 73
>1621_00002 73
>1622_00575 73
>1633_00422 73
>O136_00307 73
>O139_01028 74
>O142_00961 74
>O300_00856 75

Wyjaśnienie

  • perl -ne: przeczytaj plik wejściowy linia po linii ( -n) i zastosuj skrypt podany przez -edla każdej linii.
  • if(/^>.*?(\d+)/){$n=$1;}: jeśli ta linia zaczyna się od a >, znajdź najdłuższy odcinek liczb na końcu linii i zapisz go jako $n.
  • else{ s/.*(>[^.]+).*/$1 $n/; print: jeśli linia nie zaczyna się od >, zamień wszystko na najdłuższy odcinek .znaków niebędących znakami po >( >[^.]+), tj. nazwę sekwencji ( $1ponieważ przechwyciliśmy dopasowanie wyrażenia regularnego) i bieżącą wartość $n.

Lub, dla bardziej podejrzanego podejścia:

$ perl -lane 'if($#F==1){$n=$F[1]}else{$F[2]=~s/\.+$//; print "$F[2] $n"}' file 
>O311_01007 72
>1494_00753 73
>1621_00002 73
>1622_00575 73
>1633_00422 73
>O136_00307 73
>O139_01028 74
>O142_00961 74
>O300_00856 75

Jest to tylko nieco bardziej kłopotliwy sposób zrobienia tego samego podstawowego pomysłu, co różne awkpodejścia. Załączam to ze względu na ukończenie i dla fanów Perla. Jeśli potrzebujesz wyjaśnienia, skorzystaj z rozwiązań awk :).

terdon
źródło