删除总和为零的列

删除总和为零的列

我有一个数字表,即所有单元格都有数字。它是一个制表符分隔的文件,具有非数字标题和行名称。我需要删除所有加起来为零的列。我想保留第一列(行名称)以及未删除的其余列的标题。

输入

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

类似的问题,但有行:删除总和为零的行

awk解决方案会很棒;我想避免在 R 中加载大文件。

答案1

去除柱子

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

在哪里

  • { for(i=1;i<=NF;i++) { line[NR][i]=$i ; col[i]+=$i ;} }存储所有行(包括列名称)。
  • END如果 count != 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如果第一列是行名称,您可以从开始。
  • 小心浮点数,它们的和可能不等于 0。

请注意,这将输出行,而不是从原始文件中删除。

答案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..$.)
   }'

这会将整个文件加载到内存中。为了避免这种情况,您需要在文件中传递两次。

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

awk生成sed脚本以删除总和为 0 的列。这些命令将删除这些列,同时保留其他列的间距,但这会非常昂贵,如果性能是一个问题,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

这会读取文件两次:awk获取总和不为零的列号;然后使用它们来cut仅打印所需的列。

相关内容