![使用 awk 的标准差](https://linux22.com/image/6761/%E4%BD%BF%E7%94%A8%20awk%20%E7%9A%84%E6%A0%87%E5%87%86%E5%B7%AE%20.png)
我使用下面的命令来获取文件 A 名称的标准偏差
1 2 3 平均 23.3107 20.0372 21.7236 21.6905
awk '{x[NR]=$0 ;} END{a=$4; for (i in x){ss += (x[i]-a)^2} sd = sqrt(ss/n);打印 $5 = sd}'
变得致命:尝试除以零
将上面的命令修改为
awk '{x[NR]=$0 ;} END{a=$4; for (i in x){if (a == 0) $6 ="N/A";否则 ss += (x[i]-a)^2} sd = sqrt(ss/n);打印 $5 = sd}'
但错误仍然存在?谢谢 cas 理解我的追求?
答案1
“n”在哪里?
你写:
sd = sqrt(ss/n)
但是你在代码中的哪里分配了变量“n”?从表面awk
上看,“n”为零。
另外,第 5 列在哪里a=$5
(第三个问题,为什么这个作业在这个END
部分)?您的示例仅包含 4 列。
答案2
你想做这样的事情吗?这是我能想到的理解你的剧本的唯一方法。
awk -v OFS=$'\t' '
FNR == 1 { $5 = "sdev" ; print }
FNR > 1 { a = $4 # field 4 is 'avg'
n = NF-1 # exclude the 'avg' field from the ss calculations.
for (i=1; i <= n; i++) { ss += ($i - a)^2 }
$5 = sqrt(ss/n)
print
}' inputfile
注意:$i
线上for
不是指 的值i
,而是指编号的输入字段i
- 即它循环遍历$1
、$2
和$3
。这对于 shell 或用户来说可能并不明显,perl
因为(标量)变量通常以 为前缀$
。
NF
是一行中的字段数,并且FNR
是当前输入文件的记录(行)号(因此此awk
脚本支持多个输入文件,每个文件都有自己的标题行。如果在某一处只有一个输入文件时间,您可以使用)NR
代替FNR
。
示例输出:
1 2 3 avg sdev
23.3107 20.0372 21.7236 21.6905 1.33661
这是另一个版本,适用于每行任意数量的字段。它假设最后的一行的字段包含该行上所有先前字段的平均值。
$NF
指的是最后一个字段的值(即“avg”)并$new
指的是(最后一个字段+ 1),即为其分配一个值会在行尾添加一个新字段。
awk -v OFS=$'\t' '
FNR == 1 { new = NF+1 # number of new field to add
$new = "sdev"
print
}
FNR > 1 { a = $NF # last field is 'avg'
n = NF-1 # exclude the 'avg' field from the ss calculations.
for (i=1; i <= n; i++) { ss += ($i - a)^2 }
$new = sqrt(ss/n)
print
}' inputfile
每个输入行有 5 个值加上平均值的示例输出:
1 2 3 4 5 avg sdev
23.3107 20.0372 21.7236 20.5328 21.2016 21.3611 1.13107