【问题标题】:awk: extracting columns based on column valuesawk:根据列值提取列
【发布时间】:2013-11-27 06:11:00
【问题描述】:

我有一个看起来有点像这样的文件:

C1 C2 C3 C4 C5
0 0 0 0 0
0 1 0 0 0
0 0 0 1 0
0 0 0 0 0

但要大得多...

我只想提取全为 0 的列,因此我的输出文件应如下所示:

C1 C3 C5
0 0 0
0 0 0
0 0 0
0 0 0

这可以通过简单的 awk 单行来完成吗(例如类似于 awk: print columns based on values of another column)?如果没有,还有其他方法可以使用 bash 有效地做到这一点吗?

【问题讨论】:

  • 越大越大越好?您的文件是否适合 ram?
  • 是的,没有那么大。几千行,50 到 100 列。

标签: bash awk multiple-columns


【解决方案1】:

尝试关注awk

awk 'NR==1 {next} NR==FNR { for(i=1;i<=NF;i++) sum[i]+=$i; next } { for(i=1;i<=NF;i++) if (sum[i]==0) printf " %s", $i; print "" }' file{,}

输出

 C1 C3 C5
 0 0 0
 0 0 0
 0 0 0
 0 0 0

这里的想法是迭代 file 两次。一旦它计算出所有列的总和,并且在下一次迭代中,它只打印总和等于 0 的列。

这假设所有列条目都有只有正数


另一种可能更好的方法是,如果列中的任何条目非零,则设置一个标志。然后只打印对应标志为零的列。

awk 'NR==1 {next} NR==FNR { for(i=1;i<=NF;i++) if ($i) flag[i]=1; next } { for(i=1;i<=NF;i++) if (!flag[i]) printf " %s", $i; print "" }' file{,}

这种方法允许正数和负数,并消除了任何限制。

或者按照 @fedorqui 在评论中的建议

awk 'NR==1 {next} NR==FNR { for(i=1;i<=NF;i++) if ($i) flag[i]=1; next } { for(i=1;i<=NF;i++) if (flag[i]) $i="" } 1' file{,}

【讨论】:

  • +1 的思维方式。也许您可以将 { for(i=1;i&lt;=NF;i++) if (flag[i]) $i=""}1 用于第二个块:只需清空带有标志的列,然后添加 1(或 Kent 的 7 :D)以使其打印该行。
  • @fedorqui 感谢您的建议。这也有效,但我将其称为使列为空而不是删除它的解决方法。它在列之间引入了额外的空间集。不是吗?
  • 是的,它在列之间引入了额外的空格。但是原始方法(谈论答案中建议的第二种解决方案)似乎没有给我输出文件中的列...我可以从标题中看到提取了正确的列,但它没有开始应该的新行(它在一行中提供整个输出)......知道为什么吗?
  • @Jotne 哎呀。对不起!我已经在 Windows 中对其进行了测试,并在最后一列中有\r,这将它带到了新行。我的错。用你的建议更新了ans。谢谢。
  • @Abdel 我已经用 Jotne 和 fedorqui 的建议更新了 ans
【解决方案2】:

这适用于带有负数或其他字符串的数据,例如“foo”或“bar

单行:

awk 'NR==1{next}NR==FNR{while(++i<=NF)if($i!="0")k[i];i=0;next}{while(++x<=NF)if(!(x in k))printf "%s ",$x;x=0;print ""}' file file

更具可读性:

awk 'NR==1{next}
     NR==FNR{while(++i<=NF)if($i!="0")k[i];i=0;next}
     {while(++x<=NF)
         if(!(x in k)) printf "%s ",$x
      x=0
      print ""}' file file

【讨论】:

    【解决方案3】:

    一个很长的解决方案。
    将列转换为行

    awk '{
           for (f = 1; f <= NF; f++) { a[NR, f] = $f }
         }
         NF > nf { nf = NF }
         END {
           for (f = 1; f <= nf; f++) {
               for (r = 1; r <= NR; r++) {
                   printf a[r, f] (r==NR ? RS : FS)
               }
           }
        }' file >tmp1
    

    只打印只有0的行

    awk '{for (i=2;i<=NF;i++) f+=$i} !f; {f=0}' tmp1 >tmp2
    

    转换回来

    awk '{
           for (f = 1; f <= NF; f++) { a[NR, f] = $f }
         }
         NF > nf { nf = NF }
         END {
           for (f = 1; f <= nf; f++) {
               for (r = 1; r <= NR; r++) {
                   printf a[r, f] (r==NR ? RS : FS)
               }
           }
        }' tmp2
    

    给予

    C1 C3 C5
    0 0 0
    0 0 0
    0 0 0
    0 0 0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多