Cómo extraer múltiples bits de información que aparecen en diferentes líneas dentro del mismo archivo de texto

8

Estoy tratando de extraer la ID de secuencia y el número de clúster que ocurren en diferentes líneas dentro del mismo archivo de texto.

La entrada se parece a

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

La salida deseada es la ID de secuencia en una columna y el número de clúster correspondiente en la segunda.

>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

¿Alguien puede ayudarme con esto?

Tim
fuente
¿El ID de secuencia siempre será el campo separado por espacios en 3D en líneas que no comienzan con >? Además, puede estar interesado en nuestro sitio hermano, Bioinformática .
terdon

Respuestas:

13

Con awk:

awk -F '[. ]*' 'NF == 2 {id = $2; next} {print $3, id}' input-file
  • dividimos campos en espacios o períodos con -F '[. ]*'
  • con líneas de dos campos, (las >Clusterlíneas), guarde el segundo campo como ID y pase a la siguiente línea
  • con otras líneas, imprima el tercer campo y la ID guardada
muru
fuente
En lugar de eliminar la cantidad de campos, podría ser mejor buscar explícitamente en $1 == ">Cluster"lugar de NF == 2, dependiendo de qué otra cosa podría haber en el archivo.
Monty Harder
5

Puedes usar awkpara esto:

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

La primera instrucción de bloque captura la ID del clúster. La segunda instrucción de bloque (la predeterminada) extrae los datos deseados e imprime.

oliv
fuente
No es necesario dar " "como argumento para print. Simplemente use una coma para separar los argumentos y usará el OFS, espacio predeterminado, para separar los argumentos.
muru
4

Aquí hay una alternativa con Ruby como una frase:

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

o se extendió en varias líneas:

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

Supongo que solo es más legible que la awkversión si conoces Ruby y regexen. Como beneficio adicional, este código podría ser un poco más robusto que simplemente dividir las líneas, ya que busca el texto circundante.

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

Explicación

  • perl -ne: lea el archivo de entrada línea por línea ( -n) y aplique la secuencia de comandos dada por -ea cada línea.
  • if(/^>.*?(\d+)/){$n=$1;}: si esta línea comienza con a >, encuentre el tramo más largo de números al final de la línea y guárdelo como $n.
  • else{ s/.*(>[^.]+).*/$1 $n/; print: si la línea no comienza >, reemplace todo con el tramo más largo de .caracteres que no siguen a >( >[^.]+), es decir, el nombre de la secuencia ( $1porque hemos capturado la coincidencia de expresiones regulares) y el valor actual de $n.

O, para un enfoque más awk-like:

$ 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

Esta es solo una forma un poco más engorrosa de hacer la misma idea básica que los diversos awkenfoques. Lo incluyo para completarlo y para los fanáticos de Perl. Si necesita una explicación, simplemente use las soluciones awk :).

terdon
fuente