使用awk选择两个列的元素,其差异小于某个给定值

时间:2014-05-05 08:17:10

标签: awk comparison

在进行数值分析的后处理时,我有以下选择数据的问题:

time_1     result_1              time_2       result_2
1          10                    1.1          10.1
2          20                    1.6          15.1
3          30                    2.1          20.1
4          40                    2.6          25.1
5          50                    3.1          30.1
6          60                    3.6          35.1
7          70                    4.1          40.1
8          80                    4.6          45.1
9          90                    5.1          50.1
10         100                   5.6          55.1
                                 6.1          60.1
                                 6.6          65.1
                                 7.1          70.1
                                 7.6          75.1
                                 8.1          80.1
                                 8.6          85.1
                                 9.1          90.1
                                 9.6          95.1
                                 10.1         100.1

此文件有4列,第一列(time_1)表示程序1的计算时刻,第二列(result_1)是为每个时刻计算的结果。

第三列(time_2)表示另一个程序的计算时刻,第四列(result_2)是为该程序2的每个时刻计算的结果。

现在我希望仅选择非常接近第一列(time_1)时刻的第三列(time_2)的时刻,允许的差异小于或等于0.1。例如:

对于time_1列的瞬间1,我希望选择time_2列的即时1.1,因为(1.1 - 1)= 0.1,我不想选择time_2列的其他时刻因为(1.6 - 1)> 0.1,或(2.1-1)> 0.1

对于time_1列的瞬间2,我希望选择time_2列的即时2.1,因为(2.1 - 2)= 0.1,我不想选择time_2列的其他时刻因为(2.6 - 1)> 0.1,或(3.1-1)> 0.1

最后,我想获得以下数据:

time_1     result_1              time_2       result_2
1          10                    1.1          10.1
2          20                    2.1          20.1
3          30                    3.1          30.1
4          40                    4.1          40.1
5          50                    5.1          50.1
6          60                    6.1          60.1
7          70                    7.1          70.1
8          80                    8.1          80.1
9          90                    9.1          90.1
10         100                   10.1         100.1

我希望使用awk,但我还没有熟悉这段代码。我不知道如何修复第一列的元素,然后将其与第三列的所有元素进行比较,以便选择第三列的正确值。如果我非常喜欢这样,我只能打印第一行:

{if (($3>=$1) && (($3-$1) <= 0.1)) {print  $2, $4}}

提前感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

您可以尝试以下perl脚本:

#! /usr/bin/perl

use strict;
use warnings;
use autodie;
use File::Slurp qw(read_file);

my @lines=read_file("file");

shift @lines; # skip first line

my @a;

for (@lines) {
    my @fld=split;
    if (@fld == 4) {
        push (@a,{id=>$fld[0], val=>$fld[1]});
    }
}

for (@lines) {
    my @fld=split;
    my $id; my $val;
    if (@fld == 4) {
        $id=$fld[2]; $val=$fld[3];
    } elsif (@fld == 2) {
        $id=$fld[0]; $val=$fld[1];
    }
    my $ind=checkId(\@a,$id);
    if ($ind>=0) {
        $a[$ind]->{sel}=[] if (! exists($a[$ind]->{sel}));
        push(@{$a[$ind]->{sel}},{id=>$id,val=>$val});
    }
}

for my $item (@a) {
    if (exists $item->{sel}) {
        my $s= $item->{sel};
        for (@$s) {
            print $item->{id}."\t".$item->{val}."\t";
            print $_->{id}."\t".$_->{val}."\n";
        }
    }
}


sub checkId { 
    my ($a,$id) = @_;

    my $dif=0.1+1e-10;

    for (my $i=0; $i<=$#$a; $i++) {
        return $i if (abs($a->[$i]->{id}-$id)<=$dif)
    }
    return -1;
}

答案 1 :(得分:0)

有一点需要注意:由于浮点数的变幻莫测,将值与0.1进行比较不太可能为您提供所需的结果:

awk 'BEGIN {x=1; y=x+0.1; printf "%.20f", y-x}'
0.10000000000000008882⏎            

此处y=x+0.1,但y-x > 0.1

因此,我们将差异视为diff = 10*y - 10x

此外,我将处理文件两次:一次获取所有time_1 / result_1值,第二次提取“匹配”time_2 / result_2值。

awk '
    NR==1   {print; next} 
    NR==FNR {if (NF==4) r1[$1]=$2; next} 
    FNR==1  {next}
    {
        if (NF == 4) {t2=$3; r2=$4} else {t2=$1; r2=$2}
        for (t1 in r1) {
            diff = 10*t1 - 10*t2; 
            if (-1 <= diff && diff <= 1) {
                print t1, r1[t1], t2, r2
                break
            }
        }
    }

' ~/tmp/timings.txt ~/tmp/timings.txt | column -t
time_1  result_1  time_2  result_2
1       10        1.1     10.1
2       20        2.1     20.1
3       30        3.1     30.1
4       40        4.1     40.1
5       50        5.1     50.1
6       60        6.1     60.1
7       70        7.1     70.1
8       80        8.1     80.1
9       90        9.1     90.1
10      100       10.1    100.1