Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 在一列数字中,找到最接近某个目标值的值_Bash_Awk - Fatal编程技术网

Bash 在一列数字中,找到最接近某个目标值的值

Bash 在一列数字中,找到最接近某个目标值的值,bash,awk,Bash,Awk,假设我有一些列中的数字数据,比如 11.100000 36.829657 6.101642 11.400000 36.402069 5.731998 11.700000 35.953025 5.372652 12.000000 35.482082 5.023737 12.300000 34.988528 4.685519 12.600000 34.471490 4.358360 12.900000 33.930061 4.042693 13.200000 33.363428 3.738985 1

假设我有一些列中的数字数据,比如

11.100000 36.829657 6.101642
11.400000 36.402069 5.731998
11.700000 35.953025 5.372652
12.000000 35.482082 5.023737
12.300000 34.988528 4.685519
12.600000 34.471490 4.358360
12.900000 33.930061 4.042693
13.200000 33.363428 3.738985
13.500000 32.770990 3.447709
13.800000 32.152473 3.169312
我还有一个目标值和一个列索引。给定这组数据,我希望在具有指定索引的列中找到与目标值最接近的值

例如,如果我的目标值是列
1
中的
11.6
,那么脚本应该输出
11.7
。如果有两个数字距离目标值相等,则应输出较高的值

我觉得awk有必要的功能来实现这一点,但是任何在bash脚本中工作的解决方案都是受欢迎的。

试试这个:

awk -v c=2 -v t=35 'NR==1{d=$c-t;d=d<0?-d:d;v=$c;next}{m=$c-t;m=m<0?-m:m}m<d{d=m;v=$c}END{print v}' file
编辑

如果有两个数字与目标值等距,则应输出较高的值

上述代码未检查此要求。。。。下面的一个应该有效:

awk -v c=1 -v t=11.6 '{a[NR]=$c}END{
        asort(a);d=a[NR]-t;d=d<0?-d:d;v = a[NR]
        for(i=NR-1;i>=1;i--){
                m=a[i]-t;m=m<0?-m:m
                if(m<d){
                    d=m;v=a[i]
                }
        }
        print v
}' file
awk-vc=1-vt=11.6'{a[NR]=$c}END{
asort(a);d=a[NR]-t;d=d=1;i--){

m=a[i]-t;m=m让我们试试另一种方法,尽管肯特的答案必须更短更清晰:)

Perl解决方案:

#!/usr/bin/perl
use warnings;
use strict;

@ARGV == 2 or die "Usage: closest column value < input\n";
my ($column, $target) = (shift, shift);
my $closest;
while (<>) {
    my $value = (split)[$column - 1];
    if ($. == 1
        or abs($closest - $target) >  abs($target - $value)
        or abs($closest - $target) == abs($target - $value)
           && $value > $closest) {
        $closest = $value;
    }
}
print $closest, "\n";
!/usr/bin/perl
使用警告;
严格使用;
@ARGV==2或die“用法:最接近的列值<输入\n”;
我的($column,$target)=(shift,shift);
我的美元最接近;
而(){
我的$value=(拆分)[$column-1];
如果($。==1
或abs($target-$target)>abs($target-$value)
或资产负债表($最近-$目标)=资产负债表($目标-$价值)
&&$value>$closest){
$closest=$value;
}
}
打印$closest,“\n”;

注意,使用float==float可能不起作用()。您可能需要类似于
abs(abs($closest-$target)-abs($target-$value))的东西<1e-14

我想你的逻辑可能有一些小问题……因为如果你测试
c=2v=35
,我得到了
35.482…
,但我认为
34.988528
将是答案。因为0.482..>0.114..`我没有仔细阅读你的答案,但你为什么要做
v-$1
?应该是
v-$c
,对吗?而且魔法数字
99
对所有情况都不安全。而且
等距
案例也没有被检查。我在编辑我的答案之前也忽略了它。哦,是的,这是一个输入错误@Kent。当我开始检查col 1案例时,我忘记了更新。我检查了
等距
案例,应该与
d+1一起工作。干得好,我给了我的ha小费t to you Kent;)
awk
在macOS上缺少内置的
asort()
函数,因此我只需省略该部分并在使用前对输入数据进行排序。这对于查找出生时间(即创建日期)最接近给定纪元时间的文件非常方便。
kent$  awk -v c=1 -v t=11.6 '{a[NR]=$c}END{
        asort(a);d=a[NR]-t;d=d<0?-d:d;v = a[NR]
        for(i=NR-1;i>=1;i--){
                m=a[i]-t;m=m<0?-m:m
                if(m<d){
                    d=m;v=a[i]
                }
        }
        print v
}' f
11.700000
awk -vc=1 -vv=13.6 '
    BEGIN{l=$c; ld=99}
    {d=($c-v>=0) ? ($c-v) : v-$c; if (d <= ld) {ld=d; l=$c}}
    END{print l}' file
$ awk -vc=2 -vv=13.6 'BEGIN{l=$c; ld=99} {d=($c-v>=0) ? ($c-v) : v-$c; if (d <= ld) {ld=d; l=$c}} END{print l}' file
32.152473
$ awk -vc=3 -vv=10.6 'BEGIN{l=$c; ld=99} {d=($c-v>=0) ? ($c-v) : v-$c; if (d <= ld) {ld=d; l=$c}} END{print l}' file
3.169312
#!/usr/bin/perl
use warnings;
use strict;

@ARGV == 2 or die "Usage: closest column value < input\n";
my ($column, $target) = (shift, shift);
my $closest;
while (<>) {
    my $value = (split)[$column - 1];
    if ($. == 1
        or abs($closest - $target) >  abs($target - $value)
        or abs($closest - $target) == abs($target - $value)
           && $value > $closest) {
        $closest = $value;
    }
}
print $closest, "\n";