【问题标题】:How to print columns that have values satisfying a range?如何打印具有满足范围的值的列?
【发布时间】:2018-03-06 16:07:31
【问题描述】:

我有一张包含数千列和行的大表。但为了简化起见,假设我有一个包含 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 的命令会更好,但我不知道它是否最适合这种提取。

请告诉我如何做到这一点。任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 可能会两次传递文件。第一次传递,根据给定条件为每一列构建具有真/假值的数组。下一次传递,打印所需的列…你必须自己尝试一些事情并添加到问题中。有关两个文件处理的示例,请参见backreference.org/2010/02/10/idiomatic-awk
  • 你的输出是任意的:它不符合条件 "0.80 >= value >= 0.70"
  • @RomanPerekhrest 我认为这意味着 0.7-0.8... col1 具有 0.74 和 col4 具有 0.75/0.72/0.78 之间的值

标签: awk multiple-columns extraction


【解决方案1】:

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

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

【讨论】:

  • 天哪,你是救生员!它可以工作。由于我的实际数据表很大,我不得不在一个相对较小的表上对其进行测试,它确实有效。我目前正在排队在实际数据集上运行。我很肯定它会起作用。谢谢!
【解决方案2】:

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

我的第一个想法是给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 脚本有两个输入;首先,它从标准输入 (-) 读取列号列表并用它们填充一个数组。然后它逐步遍历输入文件,打印编号在数组中的列。

【讨论】:

    【解决方案3】:
    $ 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-20
      • 1970-01-01
      • 1970-01-01
      • 2011-08-18
      • 2019-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多