找到最接近的前5名而不是最近的?

时间:2016-07-16 16:55:34

标签: text-processing

如何在最接近$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
    }
'

但我无法适应它

3 个答案:

答案 0 :(得分:5)

我只是按距离对n进行排序,然后选择第一个:

awk -v n=30 '
  function abs(x) {return x < 0 ? -x : x}
  {print abs($0 - n) "\t" $0}' < file |
  sort -n |
  head -n 5 |
  cut -f 2-

答案 1 :(得分:2)

像往常一样,Stéphane’s answer非常好; 简单明了。 但是,如果你真的真的希望完全在awk中完成, 你有GNU awk(a.k.a。gawk),你可以这样做:

awk -v t="$VariableNumber" '
       {
            d = $1 - t
            if (d < 0) d = -d
            e = d "#" $1
            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 ifference 在该值与 t arget值之间, t(在命令行上设置为$VariableNumber的值, 根据你的例子,它可能是30)。 然后它构造一个数组 e ntry,e, 由差异组成, 与#和原始数字连接在一起。 然后将此数组条目添加到 a rray a

前五个输入值只是放入数组元素1到5中。

之后,每个数字都附加到数组中 通过放入元素6.然后对数组进行排序。 由于数组条目以 d ifference值开头, 接近 t arget的数字 ( d ifference值为低) 被排序到数组的开头, 和远离 t arget的数字 被排序到数组的末尾。 (指定"@val_num_asc" 将值排序为数字而不是字符串。 如果不这样做,1020的差异将排在34之下。) 然后删除第6个元素(距离目标最远的元素)。

最后(在到达数据的END时),我们

  • 检查记录数是否≤5。 如果是,请对数组进行排序, 因为它仍然是输入数据的顺序。 (可以说,这一步是可选的。)
  • 每个元素都是数组, 消除差异和# 通过搜索正则表达式.*# 并代替(gsub)什么都没有。 然后打印原始值。

显然,如果你想查看第一个列, 您可以更改脚本中$1的所有匹配项。 (您在问题中显示的脚本 演示了如何在运行时指定列号。) 并且,如果你想要一些除最近的五个之外的数字, 只需更改5的所有外观。 (我本可以在第9和第11行提到a[6]; 我写了a[5+1]来促进简单的参数化。)

答案 2 :(得分:0)

另一个,对于所有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-$1))<max) && c==n {        # if we find distance < max of top n smallest
    da[maxc]=d                       # replace max in distance array
    va[maxc]=$1                      # 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]=$1                         # 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

执行时间是线性的,最坏的情况是值数组的大小乘以记录计数,因此仍是线性的(对吗?:)。