Come estrarre più bit di informazioni che appaiono su linee diverse all'interno dello stesso file di testo


8

Sto cercando di estrarre l'ID sequenza e il numero del cluster che si verificano su righe diverse all'interno dello stesso file di testo.

L'ingresso sembra

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

L'output desiderato è l'ID sequenza in una colonna e il numero cluster corrispondente nella seconda.

>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

Qualcuno può aiutare con questo?


L'ID sequenza sarà sempre il campo separato da spazi 3D su linee che non iniziano con >? Inoltre, potresti essere interessato al nostro sito gemello , Bioinformatica .
terdon,

Risposte:


13

Con awk:

awk -F '[. ]*' 'NF == 2 {id = $2; next} {print $3, id}' input-file
  • dividiamo i campi su spazi o punti con -F '[. ]*'
  • con righe di due campi, (le >Clusterrighe), salva il secondo campo come ID e passa alla riga successiva
  • con altre righe, stampa il terzo campo e l'ID salvato

Invece di cancellare il numero di campi, potrebbe essere meglio cercare esplicitamente $1 == ">Cluster"invece di NF == 2, a seconda di cos'altro potrebbe essere nel file.
Monty Harder,

5

Puoi usare awkper questo:

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

La prima istruzione di blocco sta acquisendo l'ID del cluster. La seconda istruzione di blocco (quella predefinita) sta estraendo i dati desiderati e stampandoli.


Non è necessario dare " "come argomento a print. Basta usare una virgola per separare gli argomenti e utilizzerà OFS, spazio predefinito, per separare gli argomenti.
muru,

4

Ecco un'alternativa con Ruby come one-liner:

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

o distribuito su più righe:

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

Immagino sia più leggibile della awkversione se conosci Ruby e regexen. Come bonus, questo codice potrebbe essere un po 'più robusto della semplice divisione delle linee, perché cerca il testo circostante.


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

Spiegazione

  • perl -ne: leggi il file di input riga per riga ( -n) e applica lo script fornito da -eciascuna riga.
  • if(/^>.*?(\d+)/){$n=$1;}: se questa riga inizia con a >, trova il tratto di numero più lungo alla fine della riga e salvalo come $n.
  • else{ s/.*(>[^.]+).*/$1 $n/; print: se la linea non inizia con >, sostituisci tutto con il tratto più lungo di non .caratteri che seguono un >( >[^.]+), ovvero il nome della sequenza ( $1perché abbiamo catturato la corrispondenza regex) e il valore corrente di $n.

Oppure, per un approccio più strano:

$ 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

Questo è solo un modo leggermente più ingombrante di fare la stessa idea di base dei vari awkapprocci. Lo sto includendo per il completamento e per i fan del Perl. Se hai bisogno di una spiegazione, usa le soluzioni awk :).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.