Como extrair vários bits de informação que aparecem em linhas diferentes dentro do mesmo arquivo de texto

8

Estou tentando extrair o ID da sequência e o número do cluster que ocorrem em linhas diferentes no mesmo arquivo de texto.

A entrada parece

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

A saída desejada é a ID da sequência em uma coluna e o número do cluster correspondente na 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

Alguém pode ajudar com isso?

    
por Tim 26.03.2018 / 08:02

4 respostas

13

com o awk:

awk -F '[. ]*' 'NF == 2 {id = $2; next} {print $3, id}' input-file
  • dividimos campos em espaços ou períodos com -F '[. ]*'
  • com linhas de dois campos (o >Cluster linhas), salve o segundo campo como o ID e passe para a próxima linha
  • com outras linhas, imprima o terceiro campo e o ID salvo
por muru 26.03.2018 / 08:15
5

Você pode usar awk para isso:

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

A primeira instrução de bloco está capturando o ID do cluster. A segunda instrução de bloco (a padrão) é extrair os dados desejados e imprimi-los.

    
por oliv 26.03.2018 / 08:15
3

Aqui está uma alternativa com Ruby como one-liner:

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

ou espalhados em várias linhas:

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

Eu acho que é apenas mais legível que a versão awk se você conhece Ruby e regexen. Como bônus, esse código pode ser um pouco mais robusto do que simplesmente dividir as linhas, porque ele procura o texto ao redor.

    
por Eric Duminil 26.03.2018 / 14:04
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

Explicação

  • perl -ne : leia o arquivo de entrada linha a linha ( -n ) e aplique o script fornecido por -e em cada linha.
  • if(/^>.*?(\d+)/){$n=$1;} : se essa linha começar com > , localize o maior número de números no final da linha e salve como $n .
  • else{ s/.*(>[^.]+).*/$1 $n/; print : se a linha não começar com > , substitua tudo pelo trecho mais longo de caracteres que não sejam . após > ( >[^.]+ ), ou seja, o nome da sequência ( $1 porque tenha capturado a correspondência de regex) e o valor atual de $n .

Ou, para uma abordagem mais parecida com o awk:

$ 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 é apenas uma maneira um pouco mais complicada de fazer a mesma ideia básica que as várias abordagens awk . Estou incluindo para completar e para os fãs do Perl. Se você precisar de uma explicação, basta usar as soluções do awk:).

    
por terdon 26.03.2018 / 20:00