Mesclando várias linhas em uma única usando o awk

1

Estou trabalhando com um conjunto de dados muito grande no formato:

chr1 45162 . . C T 
chr1 45162 . . C T
chr1 45162 A . . T
chr1 45162 . . C T
chr1 45257 A . . T
chr1 45257 A . . T
chr1 45413 A . . T
chr1 46844 A . C .
chr1 72434 A G . .
chr1 72434 A G . .
chr1 72434 A G . .
chr1 72434 A G . .
chr1 72515 A . C .
chr1 72515 A . . T
chr1 77689 A G . .    

A saída que eu gostaria é esta:

chr1 45162 A . C T
chr1 45257 A . . T
chr1 45413 A . . T
chr1 46844 A . C .
chr1 72434 A G . .
chr1 72515 A . C T
chr1 77689 A G . .

Essencialmente, para cada valor único na coluna 2, preciso ver todos os atributos que ele possui, que podem ser armazenados em linhas separadas.

Então, se eu tivesse:

chr1 100 A . C .
chr1 100 . G . T

Eu gostaria que a linha na saída fosse:

chr1 100 A G C T

Eu tenho um script que acredito estar funcionando corretamente, mas é muito lento. Eu acho que deve haver uma maneira muito mais fácil de fazer isso em um simples script awk .

Minha versão atual está aqui: link Mas, como eu disse, acho que deveria haver uma versão de quase 1 liner usando apenas awk .

Todas as sugestões são bem-vindas.

(Se isso simplificar as coisas, posso voltar e modificar o script que gerou esse arquivo, de modo que os campos vazios sejam uma string vazia, em vez de um caractere ".").

    
por Cameron Shaw 20.11.2014 / 23:04

3 respostas

1

Aqui está uma abordagem:

$ awk '$2!=old && NR>1 {for (i=1;i<=NF;i++) printf a[old","i]" "; print"";} {old=$2;for (i=1;i<=NF;i++) {if (a[$2","i]=="." ||  a[$2","i]=="") a[$2","i]= $i}} END{for (i=1;i<=NF;i++) printf a[old","i]" "; print"";}' file 
chr1 45162 A . C T 
chr1 45257 A . . T 
chr1 45413 A . . T 
chr1 46844 A . C . 
chr1 72434 A G . . 
chr1 72515 A . C T 
chr1 77689 A G . . 

Como funciona

  • $2!=old && NR>1 {for (i=1;i<=NF;i++) printf a[old","i]" "; print"";}

    Após a primeira linha, para cada vez que encontrarmos um novo valor para a segunda coluna, imprima os resultados do valor anterior.

  • old=$2;for (i=1;i<=NF;i++) {if (a[$2","i]=="." || a[$2","i]=="") a[$2","i]= $i}

    Atualize a matriz a com o valor da linha atual.

    O GNU awk tem bons arrays 2D. Para compatibilidade, no entanto, estou usando o substituto compatível com POSIX.

  • END{for (i=1;i<=NF;i++) printf a[old","i]" "; print"";}

    Após a última linha, imprima as informações para a seção final.

por 20.11.2014 / 23:52
0

Mais uma variante para linhas não classificadas:

awk '{
k[$2]=$1;
for(i=3;i<7;i++){
  if(l[$2,i]=="." || l[$2,i]=="")
    l[$2,i]=$i;
  }
}
END{
for(n in k){
  printf("%s %s ",k[n],n);
  for(m=3;m<7;m++)
    printf("%s ", l[n,m]);
  print "";
  }
 }' file

Uma breve explicação:

Percorrer scripts de arquivo cria dois arrays de associativa: k com field#2 como índice e l com índice field#2,Next_fields_number . Quando todas as linhas no arquivo são passadas, o script inicia dois loops para imprimir o primeiro array + os campos dos segundos.

    
por 21.11.2014 / 00:14
0

Uma solução perl :

$ perl -anle '
  for (2..$#F) {
    $h{join(" ",@F[0..1])}->{$_} ||= $F[$_];
    $h{join(" ",@F[0..1])}->{$_} = $F[$_] if $F[$_] ne ".";
  }
  END { print "$_ @{$h{$_}}{sort keys %{$h{$_}}}" for sort keys %h }
' file
chr1 45162 A . C T
chr1 45257 A . . T
chr1 45413 A . . T
chr1 46844 A . C .
chr1 72434 A G . .
chr1 72515 A . C T
chr1 77689 A G . .
    
por 21.11.2014 / 04:18