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]+$/ && 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:计算不同文件中数据的平均值的主要内容,如果未能解决你的问题,请参考以下文章
用shell语言能计算csv文件中某一列数据的平均值吗?如何实现?