So extrahieren Sie mehrere Informationen, die in verschiedenen Zeilen derselben Textdatei angezeigt werden

8

Ich versuche, die Sequenz-ID und die Clusternummer zu extrahieren, die in verschiedenen Zeilen innerhalb derselben Textdatei vorkommen.

Die Eingabe sieht aus wie

>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... *

Die gewünschte Ausgabe ist die Sequenz-ID in einer Spalte und die entsprechende Clusternummer in der zweiten.

>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

Kann mir jemand dabei helfen?

Tim
quelle
Wird die Sequenz-ID immer das durch 3D-Leerzeichen getrennte Feld in Zeilen sein, mit denen nicht begonnen wird >? Vielleicht interessiert Sie auch unsere Schwesterseite Bioinformatik .
Terdon

Antworten:

13

Mit awk:

awk -F '[. ]*' 'NF == 2 {id = $2; next} {print $3, id}' input-file
  • Wir teilen Felder auf Räume oder Punkte mit -F '[. ]*'
  • >ClusterSpeichern Sie mit Zeilen aus zwei Feldern (den Zeilen) das zweite Feld als ID und wechseln Sie zur nächsten Zeile
  • Drucken Sie mit anderen Zeilen das dritte Feld und die gespeicherte ID
muru
quelle
Anstatt die Anzahl der Felder einzugeben, ist es möglicherweise besser, explizit nach zu suchen, $1 == ">Cluster"anstatt danach NF == 2, je nachdem, was sich sonst noch in der Datei befindet.
Monty Harder
5

Sie können dafür verwenden awk:

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

Die erste Blockanweisung erfasst die Cluster-ID. Die zweite Blockanweisung (die Standardanweisung) extrahiert die gewünschten Daten und druckt sie aus.

oliv
quelle
Sie müssen nicht " "als Argument angeben print. Verwenden Sie einfach ein Komma, um die Argumente zu trennen, und es wird das OFS (Standardbereich) verwendet, um die Argumente zu trennen.
Muru
4

Hier ist eine Alternative mit Ruby als Einzeiler:

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

oder auf mehrere Zeilen verteilt:

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

Ich denke, es ist nur dann besser lesbar als die awkVersion, wenn Sie Ruby und Regexen kennen. Als Bonus ist dieser Code möglicherweise etwas robuster als das einfache Teilen der Zeilen, da er nach dem umgebenden Text sucht.

Eric Duminil
quelle
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

Erläuterung

  • perl -ne: Lesen Sie die Eingabedatei Zeile für Zeile ( -n) und wenden Sie das von -ejeder Zeile angegebene Skript an.
  • if(/^>.*?(\d+)/){$n=$1;}: Wenn diese Zeile mit a beginnt >, suchen Sie die längste Zahlenstrecke am Ende der Zeile und speichern Sie diese als $n.
  • else{ s/.*(>[^.]+).*/$1 $n/; print: Wenn die Zeile nicht mit beginnt >, ersetzen Sie alles durch die längste Strecke von Nichtzeichen .nach a >( >[^.]+), dh den Sequenznamen ( $1weil wir die Regex-Übereinstimmung erfasst haben ) und den aktuellen Wert von $n.

Oder für einen eher awk-ähnlichen Ansatz:

$ 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

Dies ist nur eine etwas umständlichere Methode, um dieselbe Grundidee wie die verschiedenen awkAnsätze zu verwirklichen. Ich schließe es der Vollständigkeit halber und für die Perl-Fans ein. Wenn Sie eine Erklärung benötigen, verwenden Sie einfach die awk-Lösungen :).

Terdon
quelle