awk:计算不同文件中数据的平均值

Posted

技术标签:

【中文标题】awk:计算不同文件中数据的平均值【英文标题】:awk: compute mean values for the data in distinct files 【发布时间】:2022-01-24 01:00:45 【问题描述】:

我正在使用 bash + awk 从目录中的日志文件中提取一些信息,并将摘要保存在单独的文件中。 在每个日志文件的底部,都有一个类似的表格:

mode |   affinity | dist from best mode
     | (kcal/mol) | rmsd l.b.| rmsd u.b.
-----+------------+----------+----------
   1       -6.961          0          0
   2       -6.797      2.908      4.673
   3       -6.639      27.93      30.19
   4       -6.204      2.949      6.422
   5       -6.111      24.92      28.55
   6       -6.058      2.836      7.608
   7       -5.986      6.448      10.53
   8        -5.95      19.32      23.99
   9       -5.927      27.63      30.04
  10       -5.916      27.17      31.29
  11       -5.895      25.88      30.23
  12       -5.835      26.24      30.36

因此,我需要关注位于第二列的(负)值。值得注意的是,我需要从第二列中获取 10 个第一个值(从 -6.961 到 -5.916)并计算它的平均值并将平均值与日志名称一起保存为新的ranking.log 中的一个字符串 所以对于 5 个处理过的日志,它应该是这样的:

# ranking_$output.log
log_name1 -X.XXX
log_name2 -X.XXX
log_name3 -X.XXX
log_name4 -X.XXX
log_name5 -X.XXX

其中 -X.XXX 是为每个日志计算的前 10 个位置的平均值。

这是我集成在 bash 函数中的 awk 代码,它从每个日志中提取第一个值(示例表中的 -6.961)(没有平均计算)。

 # take only the first line (lowest dG) from each log
take_the_first_value () 
    awk '$1=="1"sub(/.*\//,"",FILENAME); sub(/\.log/,"",FILENAME); printf("%s: %s\n", FILENAME, $2)' "$results"/*.log  > "$results"/ranking.csv
 

我可以修改 AWK 部分以添加 MEAN 值的计算,而不是始终采用位于表第一行的值吗?

【问题讨论】:

该表中是否总是有 10 个或更多目标行(其中 $2 为负数)?如果存在少于 10 行或输入文件为空,您有多个答案将失败。 【参考方案1】:

使用 GNU awk 处理 ENDFILE:

$ cat tst.sh
#!/usr/bin/env bash

awk '
    ($2+0) < 0 
        sum += $2
        if ( ++cnt == 10 ) 
            nextfile
        
    
    ENDFILE 
        print FILENAME, (cnt ? sum/cnt : 0)
        cnt = sum = 0
    
' "$@:--"

$ ./tst.sh file
file -6.2549

请注意,即使您的输入文件末尾少于 10 行(包括空文件),上述方法也可以使用。

【讨论】:

@M.NejatAydin 是的,他们应该这样做。我在之前的迭代中有过这个,但通过更新打破了它。再次修复不,谢谢。【参考方案2】:

我建议使用 GNU awk:

awk -v num=10 'BEGINFILE c=sum=0 
     $1~/^[0-9]+$/ && NF==4
       c++; sum=sum+$2;
       if(c==num)
         sub(/.*\//, "", FILENAME);
         print FILENAME, sum/num
       
     ' "$results"/*.log  >> "$results"/ranking.csv

我使用$1~/^[0-9]+$/ &amp;&amp; NF==4 来识别正确的行。

【讨论】:

谢谢!事实上,它只对 1 个日志文件进行计算,而我需要将它应用于位于同一目录中的许多日志,请参阅我在第一篇文章中的示例,其中 AWK 应用于位于同一文件夹中的所有日志“$结果"/*.log > "$results"/ranking.csv @HotJAMS:好的,我已经更新了我的答案。 它有效!只有一个问题,是否可以替换输出中每个日志的路径?它现在看起来像 /Users/jame/Documents/tutorials/dolce_docking/results/name_X7V_rep1.log -8.0069 我需要简单的 name_X7V_rep1 -8.0069,在使用 sub(/.*\//,"",FILENAME ); sub(/\.log/,"",FILENAME); @HotJAMS:是的,有两个正确的地方。我现在已将答案中的两个位置换成了一个变量 (num=10)。 @HotJAMS:是的,或者使用print FILENAME ":", sum/num【参考方案3】:

这会为您提供平均值。用于查找第一个值的模式是^---+--- 行,在下一行的第一个字段中紧跟[:digit:]。对于每个日志文件做

$ awk '$1~/[[:digit:]]/ && set==1 x+=$2; i++;
    gsub(/\/*.*\//,"", FILENAME);               
    if(i==10) set=0; print FILENAME, x/i; i=0; x=0   
    /^\-+\+\-+/ set=1 ' "$results"/*.log > "$results"/ranking.csv

【讨论】:

它只在一个日志上进行计算,而我需要应用任意数量日志的 AWK 部分并将所有内容保存在一个日志中,如我的示例 AWK 代码中所示; awk '$1=="1"sub(/.*\//,"",FILENAME);子(/\.log/,"",文件名); printf("%s: %s\n", FILENAME, $2)' "$results"/*.log > "$results"/ranking.csv @HotJAMS 我改变了它。现在应该适用于所有日志文件。

以上是关于awk:计算不同文件中数据的平均值的主要内容,如果未能解决你的问题,请参考以下文章

使用来自多个文件的awk计算文件中的平均值

用shell语言能计算csv文件中某一列数据的平均值吗?如何实现?

在linux 下编写awk脚本计算每个人的平均成绩及所有人的平均成绩

awk 表达式

如何合并两个不同的数据帧,时间戳略有不同

计算数组平均值