找到最接近的前 5 个,而不仅仅是最接近的?
Find the top 5 closest rather than just the closest?
如何在一列数字中找到最接近 $VariableNumber
的 5 个数字?
例如,如果 $VariableNumber
= 30 那么:
输入文件示例:
50
100
70
40
20
10
65
41
92
示例输出:
20
40
41
10
50
之前有人在其他地方发布过一个答案,在特定行的特定列中找到与给定值最接近的数字匹配,如下所示:
awk -v col_num="3" -v value="$Number" '
func abs(x) { return (x<0) ? -x : x }
{
distance = abs($col_num - value)
}
NR==1 || distance<shortest_distance {
shortest_distance = distance
nearest_value = $col_num
}
END {
print nearest_value
}
'
但是一直没能适应
我只是按到 n 的距离对条目进行排序,select 第一个像:
awk -v n=30 '
function abs(x) {return x < 0 ? -x : x}
{print abs([=10=] - n) "\t" [=10=]}' < file |
sort -n |
head -n 5 |
cut -f 2-
一如既往,很好;
简单明了。
但是,如果您真的 真的 想完全在 awk
内完成,
并且你有 GNU awk
(a.k.a. gawk
),你可以这样做:
awk -v t="$VariableNumber" '
{
d = - t
if (d < 0) d = -d
e = d "#"
if (NR <= 5) {
a[NR] = e
} else {
a[5+1] = e
asort(a, a, "@val_num_asc")
delete a[5+1]
}
}
END {
print "---"
if (NR <= 5) asort(a, a, "@val_num_asc")
for (i in a) { gsub(".*#", "", a[i]); print a[i]; }
}
'
对于每个输入值,
这将 d
计算为绝对 d 差异
在该值和 t 目标值之间,
t
(在命令行上设置为 $VariableNumber
的值,
根据您的示例,这可能是 30)。
然后构造数组entry,e
,
组成的差异,
与 #
和原始数字连接。
然后将此数组条目添加到 array a
。
前五个输入值简单地放入数组元素 1 到 5。
之后,将每个数字追加到数组中
通过放入元素 6。然后对数组进行排序。
由于数组条目以 d 差异值开头,
接近 t 目标的数字
(d差异值较低)
被排序到数组的开头,
和远离 target 的数字
被排序到数组的末尾。
(指定 "@val_num_asc"
将值排序为 数字 而不是字符串。
如果没有这个,10
和 20
的差异将排序在 3
和 4
之下。)
然后删除第6个元素(距离目标最远的那个)。
最后(到达数据的末尾),我们
- 检查记录数是否≤5。
如果是,则对数组进行排序,
因为它仍然是输入数据的顺序。
(可以说,这一步是可选的。)
- 对于每个元素都是数组,
去掉差异和
#
通过搜索正则表达式 .*#
并替换 (gsub
) 什么。
然后打印原始值。
显然,如果您想查看第一列以外的列,
您可以更改脚本中出现的所有 </code>。
(您在问题中显示的脚本
演示了如何允许在 运行 时指定列号。)
而且,如果你想要一些不是最接近的五个数字,
只需更改 <code>5
的所有外观即可。
(我本可以在第 9 行和第 11 行中提到 a[6]
;
我写 a[5+1]
是为了方便简单的参数化。)
另一个,适用于所有 awk(使用 gawk、mawk、Debian 的原始 awk 和 Busybox awk 测试):
$ awk -v v=30 -v n=5 ' # v is the central value,
function abs(d) { # n is the number of wanted values
return (d<0?-d:d) # d is distance, c array index, va value array
} # da distance array, max is greatest of smallest
((d=abs(v-))<max) && c==n { # if we find distance < max of top n smallest
da[maxc]=d # replace max in distance array
va[maxc]= # and in value array
max=0 # set max to min to find new max distance
for(ct in da)
if(da[ct]>max) { # find the new max in the top n smallest
max=da[ct]
maxc=ct
}
if(max==0) # if max is 0, all are 0s so might as well exit
exit
next
}
c<n { # we need n first distances
da[++c]=d # fill distance array with them
va[c]= # related values to value array
if(d>max) { # look for max
max=d
maxc=c
}
}
END { # in the end or exit
for(c in va) # get all values in value array
print va[c] # and output them
}' file
输出(排名不分先后,数组实现相关):
50
10
41
40
20
执行时间是线性的,最坏的情况是值数组的大小乘以记录数,所以仍然是线性的(对吗?:)。
如何在一列数字中找到最接近 $VariableNumber
的 5 个数字?
例如,如果 $VariableNumber
= 30 那么:
输入文件示例:
50
100
70
40
20
10
65
41
92
示例输出:
20
40
41
10
50
之前有人在其他地方发布过一个答案,在特定行的特定列中找到与给定值最接近的数字匹配,如下所示:
awk -v col_num="3" -v value="$Number" '
func abs(x) { return (x<0) ? -x : x }
{
distance = abs($col_num - value)
}
NR==1 || distance<shortest_distance {
shortest_distance = distance
nearest_value = $col_num
}
END {
print nearest_value
}
'
但是一直没能适应
我只是按到 n 的距离对条目进行排序,select 第一个像:
awk -v n=30 '
function abs(x) {return x < 0 ? -x : x}
{print abs([=10=] - n) "\t" [=10=]}' < file |
sort -n |
head -n 5 |
cut -f 2-
一如既往,awk
内完成,
并且你有 GNU awk
(a.k.a. gawk
),你可以这样做:
awk -v t="$VariableNumber" '
{
d = - t
if (d < 0) d = -d
e = d "#"
if (NR <= 5) {
a[NR] = e
} else {
a[5+1] = e
asort(a, a, "@val_num_asc")
delete a[5+1]
}
}
END {
print "---"
if (NR <= 5) asort(a, a, "@val_num_asc")
for (i in a) { gsub(".*#", "", a[i]); print a[i]; }
}
'
对于每个输入值,
这将 d
计算为绝对 d 差异
在该值和 t 目标值之间,
t
(在命令行上设置为 $VariableNumber
的值,
根据您的示例,这可能是 30)。
然后构造数组entry,e
,
组成的差异,
与 #
和原始数字连接。
然后将此数组条目添加到 array a
。
前五个输入值简单地放入数组元素 1 到 5。
之后,将每个数字追加到数组中
通过放入元素 6。然后对数组进行排序。
由于数组条目以 d 差异值开头,
接近 t 目标的数字
(d差异值较低)
被排序到数组的开头,
和远离 target 的数字
被排序到数组的末尾。
(指定 "@val_num_asc"
将值排序为 数字 而不是字符串。
如果没有这个,10
和 20
的差异将排序在 3
和 4
之下。)
然后删除第6个元素(距离目标最远的那个)。
最后(到达数据的末尾),我们
- 检查记录数是否≤5。 如果是,则对数组进行排序, 因为它仍然是输入数据的顺序。 (可以说,这一步是可选的。)
- 对于每个元素都是数组,
去掉差异和
#
通过搜索正则表达式.*#
并替换 (gsub
) 什么。 然后打印原始值。
显然,如果您想查看第一列以外的列,
您可以更改脚本中出现的所有 </code>。
(您在问题中显示的脚本
演示了如何允许在 运行 时指定列号。)
而且,如果你想要一些不是最接近的五个数字,
只需更改 <code>5
的所有外观即可。
(我本可以在第 9 行和第 11 行中提到 a[6]
;
我写 a[5+1]
是为了方便简单的参数化。)
另一个,适用于所有 awk(使用 gawk、mawk、Debian 的原始 awk 和 Busybox awk 测试):
$ awk -v v=30 -v n=5 ' # v is the central value,
function abs(d) { # n is the number of wanted values
return (d<0?-d:d) # d is distance, c array index, va value array
} # da distance array, max is greatest of smallest
((d=abs(v-))<max) && c==n { # if we find distance < max of top n smallest
da[maxc]=d # replace max in distance array
va[maxc]= # and in value array
max=0 # set max to min to find new max distance
for(ct in da)
if(da[ct]>max) { # find the new max in the top n smallest
max=da[ct]
maxc=ct
}
if(max==0) # if max is 0, all are 0s so might as well exit
exit
next
}
c<n { # we need n first distances
da[++c]=d # fill distance array with them
va[c]= # related values to value array
if(d>max) { # look for max
max=d
maxc=c
}
}
END { # in the end or exit
for(c in va) # get all values in value array
print va[c] # and output them
}' file
输出(排名不分先后,数组实现相关):
50
10
41
40
20
执行时间是线性的,最坏的情况是值数组的大小乘以记录数,所以仍然是线性的(对吗?:)。