Excluir colunas que somam zero

3

Eu tenho uma tabela numérica, ou seja, todas as células têm números. É um arquivo delimitado por tabulação, com cabeçalhos e nomes de linha não numéricos. Eu preciso excluir todas as colunas que somam zero. Gostaria de manter a primeira coluna (nomes de linha) e os cabeçalhos das demais colunas que não foram removidas.

Entrada

a  b  c  d
e  1  2  0
f  3  4  0
g  5  6  0

Saída

a  b  c
e  1  2
f  3  4
g  5  6

Problema analógico, mas com linhas: exclui linhas que somam zero

A solução

awk seria incrível; Eu quero evitar o carregamento de arquivos enormes no R.

    
por fibar 31.08.2016 / 12:27

3 respostas

2

para remover a coluna

c.awk:

 { for(i=1;i<=NF;i++) { line[NR][i]=$i ; col[i]+=$i ;} }
END {
 for ( l=1 ; l<=NR ; l++ )
  {
    printf line[l][1]   "\t" ;
    for (c=2;c<=NF;c++) if (col[c]) printf line[l][c]  "\t" ;
    printf "\n" ;
  }
}

onde

  • { for(i=1;i<=NF;i++) { line[NR][i]=$i ; col[i]+=$i ;} } armazena toda a linha (incluindo nomes de col).
  • END cláusula print all column if count! = 0.
  • cuidado para que todos os dados sejam mantidos na memória.

teste:

awk -f c.awk a
a   b       c
e   1       2
f   3       4
g   5       6

para solução de linha ...

tente

 awk 'NR==1 {print } NR>1 { s=0 ; for(i=1;i<=NF;i++) s+=$i ; if (s) print ;}'

onde

  • NR==1 {print } cabeçalho de impressão
  • NR>1 { s=0 ; for(i=1;i<=NF;i++) s+=$i ; if (s) print ;} teste para 0 e imprime se não
  • você pode começar com i=2 se a primeira coluna for o nome da linha.
  • cuidado com números de ponto flutuante, eles podem não somar 0.

observe que isso gerará linhas, e não excluirá do arquivo original.

    
por 31.08.2016 / 13:08
1

Pode ser mais fácil com perl se você deseja manter o espaçamento:

perl -lne '
   $i = 0;
   for (/\S+\s*/g) {
      $cell[$.][$i] = $_;
      $sum[$i++] += $_
   }
   END{
     @keep=(0, grep {$sum[$_]} (1..$#sum));
     print((@{$cell[$_]})[@keep]) for (1..$.)
   }'

Isso carrega o arquivo inteiro na memória. Para evitar isso, você precisaria de duas passagens no arquivo.

Isso pode ser feito com uma combinação de awk e sed :

awk '
  NR>1{for (i=2; i<=NF; i++) sum[i]+=$i; if (NF>n) n = NF}
  END {
    for (;n>1;n--)
      if (!sum[n])
        print "s/[^[:blank:]]\{1,\}[[:blank:]]*//" n
  }' < file | sed -f - file

awk gerando o script sed para remover as colunas cuja soma é 0. Os comandos s/[^[:blank:]]\{1,\}[[:blank:]]*//3 sed removeriam as colunas enquanto preservavam o espaçamento das outras colunas, mas seriam muito caras, você pode deseja fazer isso retirando perl se o desempenho for um problema.

Para linhas, é muito mais fácil:

perl -MList::Util=sum -lane 'print if $. == 1 or sum @F'
    
por 31.08.2016 / 15:54
0

Como esses valores são sempre inteiros, você pode fazer algo como:

cut $(awk 'NR>1{for(i=2;i<=NF;i++) s[i]+=$i}END{printf("%s", "-f 1");
for (i=2;i<=NF;i++) {if (s[i]) printf(",%s", i)}}' infile) infile

isto lê o arquivo duas vezes: awk obtém os números da coluna onde a soma não é zero; estes são então usados com cut para imprimir apenas as colunas desejadas.

    
por 31.08.2016 / 13:54