【问题标题】:AWK to get average of three columns over an IDAWK 在 ID 上获取三列的平均值
【发布时间】:2017-05-08 14:21:21
【问题描述】:

我想寻求帮助。这是文件格式:

contig001 10 1 10 0.0000 0.1234 0.1234
contig001 10 1 10 0.0000 0.1678 0.1987
contig001 10 1 10 0.0111 0.1211 0.1234
contig002 245 1 10 0.0000 0.1456 0.1670
contig002 245 1 10 0.1234 0.1111 0.1098
contig002 245 1 10 0.1113 0.1111 0.1099
contig002 300 1 10 0.1112 0.1871 0.1229
contig003 100 1 10 0.0000 0.1234 0.1234

如何修改此代码,以便它也可以获得具有相同 contig 编号和位置的条目的 $4、$5 和 $6 的平均值(在示例格式中分别以 $1 和 $2 表示):

$ awk '{sum[$2]+=$3; count[$2]++} 
    END{for(k in sum) printf "%s %.1f\n",  k, sum[k]/count[k]}' file

(来自https://stackoverflow.com/a/37012455/6696551

另外,如果“可折叠”条目的数量少于 5 个(不过,我在示例输入文件中只指出了 3 个),则不会计算平均值。

这是预期的 file.out(假设这里的值已经是平均值):

contig001 10 1 10 0.1982 0.1987 0.1223
contig002 245 1 10 0.0123 0.1324 0.1452

不是很相关(?)信息: 实际上,“重复项”来自 50 个文件的 cat-sort-uniq 命令组合(但是,我不知道如何删除少于 5 个的重复项)。最初,我会询问跨多个文件的 AWK-ing 以获得每列的平均值;但是,我认为折叠重复文件将比重复遍历每个文件更容易(尤其是 2 列 ID 复杂性)。

谢谢!

【问题讨论】:

    标签: awk


    【解决方案1】:

    给你...

     awk '{k=$1 FS $2; 
           for(i=3;i<=NF;i++) s[k,i]+=$i; 
           c[k]++} 
      END {for(k in c) 
             if(c[k]>4) 
               {printf "%s", k OFS; 
                for(i=3;i<=NF;i++) 
                  printf "%.1f%s", s[k,i]/c[k], (i==NF?ORS:OFS)}}' file 
    

    请注意,对于 qualifier!(n&lt;5) == n&gt;4,您的预期输出值也不正确,可能会混淆其他人。

    【讨论】:

      【解决方案2】:

      试试:

      awk -v mindup=3 '
        {
          i=$1 FS $2
        }
      
        !C[i]++ {
          for(j=3; j<=NF-4; j++)
            M[i]=M[i] FS $j
        }
        {
          for(j=1; j<=3; j++)
            A[i,j]+=$(NF-3+j)
        }
      
        END {
          for(i in C)
            if(C[i]>=mindup)
              print i M[i], A[i,1]/C[i], A[i,2]/C[i], A[i,3]/C[i]
        }
      ' file
      

      mindup 设置为 3 用于示例。为您的实际文件将其设置为 5。

      输入文件有 6 个字段和 7 个字段的行。这种方法考虑了这种可变性,并假设平均值是针对该行的最后三个字段。 M 数组包含索引字段和给定索引的最后三个字段之间的字段。

      --编辑--

      如果事实上,任何给定索引的剩余字段是否相同,我假设它们是相同的,那么它可以简化为:

      awk -v mindup=3 '
        {
          i=x
          for(j=1; j<=NF-4; j++) 
            i=i $j FS
          C[i]++
          for(j=1; j<=3; j++)
            A[i,j]+=$(NF-3+j)
        }
      
        END {
          for(i in C)
            if(C[i]>=mindup)
              print i A[i,1]/C[i], A[i,2]/C[i], A[i,3]/C[i]
        }
      ' file
      

      【讨论】:

        【解决方案3】:

        您可以像这样使用 awk 脚本:

        script.awk

            { k = sprintf("%s %s",$1, $2)
              s_4[ k ] += $4
              s_5[ k ] += $5
              s_6[ k ] += $6
              cnt[ k ] ++
            }
        
        END {
              for( k in cnt ) {
                  n = cnt[ k ]
                  printf("%s%s%f%s%f%s%f\n", 
                          k, OFS,
                          s_4[ k ] / n, OFS, 
                          s_5[ k ] / n, OFS, 
                          s_6[ k ] / n )
              }
            }
        

        你用这个命令 ling 执行它:awk -f script.awk yourfile

        该脚本将$4, ... , $6 字段的总和聚合到三个不同的数组s_4, ... , s_6 中,这些数组由k 索引,由$1$2 组成。

        END 中,平均值是使用cnt 中的计数和每个键的三个总和来计算的。

        您可能需要稍微采用该脚本:在您的示例输入中,每行的列数不相等,并且您没有指定如何处理列 $3(我跳过了它)。

        【讨论】:

        • 嗨@Lars Fischer!我忘了修改预期的输出文件(我现在已经编辑过了)。
        • 另外,我在计算开始的实际列之前有 5 列(即 $5、$6 和 $7)。我已经相应地调整了数组。我也想打印 $3 ... $5,所以我在 END 中添加了 zzz = sprintf("%s %s %s", $3, $4, $5) 和一些东西。它有效,但我不知道它在技术上是否正确。如何排除重复项少于 10 个的条目?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-01-30
        • 1970-01-01
        • 2015-08-01
        • 2015-09-11
        • 1970-01-01
        • 2020-05-08
        相关资源
        最近更新 更多