如何打印具有满足范围值的列?

时间:2018-03-06 16:07:32

标签: awk multiple-columns extraction

我有一个包含数千列和行的大表。但是为了简化起见,假设我有一个包含11行和100列的表。表格单元格包含0到1之间的值。表格如下所示:

Sample1 Sample2 Sample3 Sample4
1   0   0.001   0.002   
0.74    0.52    0.654   0.75    
0.65    0.64    0.455   0.72    
0.24    0.51    0.512   0.78    
0.25    0.555   0.557   0.25    
0.003   0.454   0.532   0.23    
0.02    0.56    0.643   0.22    
1   0.495   0.555   0.99    
0.992   1   0.999   0.98    
0.12    0   0.968   1   

现在我想扫描所有单元格中所有在0.80> = value> = 0.70的特定范围内的值。任何包含这样的值的单元格,都会打印整列,包括标题。 预期的输出如下:

Sample1 Sample4
1   0.002   
0.74    0.75    
0.65    0.72    
0.24    0.78    
0.25    0.25    
0.003   0.23    
0.02    0.22    
1   0.99    
0.992   0.98    
0.12    1   

使用awk的命令会更好,但我不知道它是否最适合这种提取。

请告诉我如何做到这一点。任何帮助将非常感谢。谢谢。

3 个答案:

答案 0 :(得分:0)

awk救援!

$ awk 'NR==FNR && NR>1{for(i=1; i<=NF; i++) 
                         if(0.7<=$i && $i<=0.8) col[i]=1; next} 
                      {for(i=1 ;i<=NF; i++) 
                         if(col[i]) printf "%s", $i OFS; print ""}' file{,} | column -t

Sample1  Sample4
1        0.002
0.74     0.75
0.65     0.72
0.24     0.78
0.25     0.25
0.003    0.23
0.02     0.22
1        0.99
0.992    0.98
0.12     1

双扫描算法,在第一轮标记过滤后的列,并在第二轮打印。

答案 1 :(得分:0)

不确定这是否适用于您正在处理的表的大小,但任何解决方案都可能需要将某些内容存储在数组中,或者是多次传递。

我的第一个想法就是rotate.awk这样:

{
  for (i=1; i<=NF; i++) {
    d[i,NR]=$i
  }
}

END {
  for (i=1; i<=NF; i++) {
    tab=""
    for (j=1; j<=NR; j++) {
      printf "%s%s", tab, d[i,j]
      tab="\t"
    }
    printf "\n"
  }
}

您可以在两次旋转之间分析结果:

$ awk -f rotate.awk file.tsv | awk -v n=0.7 -v m=0.8 '{x=0; for (i=2; i<=NF; i++) if ($i >= n && $i <= m) x=1} x' | awk -f rotate.awk
Sample1 Sample4
1       0.002
0.74    0.75
0.65    0.72
0.24    0.78
0.25    0.25
0.003   0.23
0.02    0.22
1       0.99
0.992   0.98
0.12    1

同样,您可能会受到系统可以分配给awk的内存量的限制,以包含旋转所需的数组。

不使用大量内存来存储数组的替代方法是识别列号的多遍方法,然后将它们用作打印脚本的输入:

$ awk -v n=0.7 -v m=0.8 '{for (i=1; i<=NF; i++) if ($i >= n && $i <= m) print i}' file.tsv |
  awk 'NR==FNR{c[$1];next} {tab="";for (i=1; i<=NF; i++) if (i in c) {printf "%s%s",tab,$i;tab="\t"} printf "\n" }' - file.tsv

这里的想法是FIRST awk脚本选择要打印的列,并打印那些列号。 SECOND awk脚本有两个输入;首先,它从stdin(-)读取列号列表,并用它们填充数组。然后它逐步执行输入文件,打印数字在数组中的列。

答案 2 :(得分:0)

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==FNR {
    if (FNR > 1) {
        for (i=1; i<=NF; i++) {
            if ( ($i >= 0.7) && ($i <= 0.8) ) {
                good[i]
            }
        }
    }
    next
}
{
    c=0
    for (i=1; i<=NF; i++) {
        if (i in good) {
            printf "%s%s", (c++ ? OFS : ""), $i
        }
    }
    print ""
}

$ awk -f tst.awk file file
Sample1 Sample4
1       0.002
0.74    0.75
0.65    0.72
0.24    0.78
0.25    0.25
0.003   0.23
0.02    0.22
1       0.99
0.992   0.98
0.12    1