Como extrair vários bits de informações que aparecem em linhas diferentes no mesmo arquivo de texto

8

Estou tentando extrair a identificação de 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 é o 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?

Tim
fonte
O ID da sequência sempre será o campo 3d separado por espaço nas linhas que não começam >? Além disso, você pode estar interessado em nosso site irmão, Bioinformática .
terdon

Respostas:

13

Com 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 (as >Clusterlinhas), salve o segundo campo como o ID e vá para a próxima linha
  • com outras linhas, imprima o terceiro campo e o ID salvo
muru
fonte
Em vez de digitar o número de campos, talvez seja melhor procurar explicitamente em $1 == ">Cluster"vez de NF == 2, dependendo do que mais possa estar no arquivo.
Monty Mais difícil
5

Você pode usar awkpara 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.

oliv
fonte
Você não precisa dar " "como argumento print. Basta usar uma vírgula para separar os argumentos e ele usará o OFS, espaço padrão, para separar os argumentos.
muru 26/03
4

Aqui está uma alternativa ao Ruby como uma linha:

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

ou espalhe 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 awkversão se você conhece Ruby e regexen. Como um bônus, esse código pode ser um pouco mais robusto do que simplesmente dividir as linhas, porque procura o texto ao redor.

Eric Duminil
fonte
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 por linha ( -n) e aplique o script fornecido por -ecada linha.
  • if(/^>.*?(\d+)/){$n=$1;}: se essa linha começar com a >, encontre o maior número de números no final da linha e salve-o como $n.
  • else{ s/.*(>[^.]+).*/$1 $n/; print: se a linha não começar >, substitua tudo pelo trecho mais longo de não- .caracteres seguindo a >( >[^.]+), ou seja, o nome da sequência ( $1porque capturamos a correspondência de regex) e o valor atual de $n.

Ou, para uma abordagem mais estranha:

$ 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

Essa é apenas uma maneira um pouco mais complicada de fazer a mesma idéia básica das várias awkabordagens. Estou incluindo isso por uma questão de conclusão e pelos fãs do Perl. Se você precisar de uma explicação, use as soluções awk :).

Terdon
fonte