ゼロサムのある列を削除

ゼロサムのある列を削除

数値テーブルがあります。つまり、すべてのセルに数字があります。数値ではなく、ヘッダーと行名を持つタブ区切りファイル。合計がゼロになるすべての列を削除する必要があります。最初の列(行名)と削除されていない残りの列のヘッダーを保持したいと思います。

入力する

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

出力

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

同様の質問ですが、次の行があります。合計がゼロの行を削除

アッ解決策は良いでしょう。 Rから大容量ファイルをロードするのを避けたいです。

答え1

削除する

奇妙な:

 { 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" ;
  }
}

どこ

  • { for(i=1;i<=NF;i++) { line[NR][i]=$i ; col[i]+=$i ;} }すべての行(列名を含む)を保存します。
  • ENDcount != 0 の場合、句はすべての列を印刷します。
  • すべてのデータはメモリに保存されます。

テスト:

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

ラインソリューションの場合...

努力する

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

どこ

  • NR==1 {print }タイトル印刷
  • NR>1 { s=0 ; for(i=1;i<=NF;i++) s+=$i ; if (s) print ;}0かどうかテストし、そうでなければ印刷
  • i=2最初の列が行名であれば、それから始めることができます。
  • 浮動小数点数に注意してください。その合計がゼロではない可能性があります。

これにより、元のファイルから行を削除するのではなく、その行が出力されます。

答え2

perl間隔を維持するには、次の方が簡単です。

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

これにより、ファイル全体がメモリにロードされます。これを防ぐには、ファイルに2回渡す必要があります。

awkこれは以下を組み合わせて行うことができます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

awksed合計がゼロの列を削除するスクリプトを生成します。このコマンドは、他の列の間隔を維持しながらその列を削除しますが、高価でパフォーマンスが問題の場合はs/[^[:blank:]]\{1,\}[[:blank:]]*//3 sed削除する必要があります。perl

行の場合ははるかに簡単です。

perl -MList::Util=sum -lane 'print if $. == 1 or sum @F'

答え3

値は常に整数なので、次のことができます。

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

これはファイルを2回読みます。awk合計がゼロ以外の列番号を取得し、それを使用して目的の列のみをcut印刷します。

関連情報