2

2つのデータファイルがあります:file01file02。両方のデータセットのフィールドは次のとおりです。(i)識別子。(ii)数値参照。(iii)経度; (iv)緯度。の各行について、同じ数値参照を使用しfile01てデータを検索し、の識別子に最も近い識別子を見つけます。file02file02file01

file01次のコードを使用してからawkプログラムに値を手動で渡すと、これを取得できます。

awk 'function acos(x) { return atan2(sqrt(1-x*x), x) }
BEGIN {pi=3.14159;
       ndist=999999999.1;
       date=1001;
       lo1=-1.20; lg1=lo1*(pi/180);
       la1=30.31; lt1=la1*(pi/180)
           }
{if($2==date) {ws=$1;
               lg2=$3*(pi/180);
               lt2=$4*(pi/180);
               dist= 6378.7 * acos( sin(lt1)*sin(lt2) + cos(lt1)*cos(lt2)*cos(lg2-lg1) );
               if(dist < ndist) {ndist=dist; ws0=ws}}}
END {print(ws0,ndist)}' file02

ご覧のとおり、ステートメントの、、およびはの1行目の値ですdatelo1データファイルについては以下を参照してください)。私の質問は、それを一度に実行できるかどうかです。したがって、の行を読み取るたびに、最も近い識別子と距離を取得し、の行データに追加します。いくつかのシェルコマンドが、おそらくパイプを使用して、これをより簡単な方法で達成できるかどうかはわかりません。 la1BEGINfile01file01file01

これら2つのデータファイルと目的の出力の例は次のとおりです。

=== file01 ===

A 1001 -1.2 30.31
A 1002 -1.2 30.31
B 1002 -1.8 30.82
B 1003 -1.8 30.82
C 1001 -2.1 28.55

=== file02 ===

ws1 1000 -1.3 29.01
ws1 1001 -1.3 29.01
ws1 1002 -1.3 29.01
ws1 1003 -1.3 29.01
ws1 1004 -1.3 29.01
ws1 1005 -1.3 29.01
ws2 1000 -1.5 30.12
ws2 1002 -1.5 30.12
ws2 1003 -1.5 30.12
ws2 1004 -1.5 30.12
ws2 1005 -1.5 30.12
ws3 1000 -1.7 29.55
ws3 1001 -1.7 29.55
ws3 1002 -1.7 29.55
ws3 1003 -1.7 29.55
ws3 1004 -1.7 29.55
ws3 1005 -1.7 29.55
ws4 1000 -1.9 30.33
ws4 1001 -1.9 30.33
ws4 1002 -1.9 30.33
ws4 1003 -1.9 30.33
ws4 1004 -1.9 30.33
ws4 1005 -1.9 30.33

===出力ファイル===

A 1001 -1.2 30.31 ws4 67.308
A 1002 -1.2 30.31 ws2 35.783
B 1002 -1.8 30.82 ws4 55.387
B 1003 -1.8 30.82 ws4 55.387
C 1001 -2.1 28.55 ws1 85.369

編集#1: @Eranによる提案を考慮して、私は次のコードを書きました:

join -j 2 < (sort -k 2,2 file01) < (sort -k 2,2 file02) |
awk 'function acos(x) { return atan2(sqrt(1-x*x), x) }
     BEGIN {pi=3.14159}

     {if (last != $1 $2)
         {print NR, id,r,lon,lat,ws0,ndist;
          last = $1 $2;
          ndist=999999999.1

         } else {

          lg1=$3*(pi/180);
          lt1=$4*(pi/180);
          lg2=$6*(pi/180);
          lt2=$7*(pi/180);
          dist= 6378.7 * acos( sin(lt1)*sin(lt2) + cos(lt1)*cos(lt2)*cos(lg2-lg1) );
          if(dist< ndist) {ndist=dist; ws0=$5}
          id=$2;r=$1;lon=$3;lat=$4

          }
     }'

このスクリプトからの出力は次のとおりです。

1      
4  A 1001 -1.2 30.31 ws4 67.3078
7  C 1001 -2.0 28.55 ws3 115.094
11 A 1002 -1.2 30.31 ws2 35.7827
15 B 1002 -1.8 30.82 ws4 55.387

編集#2:@Dennisの提案を使用して(いくつかの変更を加えて)、目的の出力が得られました。awkスクリプトは次のとおりです。


awk 'function acos(x) { return atan2(sqrt(1-x*x), x) }
     BEGIN {pi=3.14159}
     NR==FNR {c++; a1[c]=$1;a2[c]=$2;a3[c]=$3;a4[c]=$4; next}
             {d++; b1[d]=$1;b2[d]=$2;b3[d]=$3;b4[d]=$4}

     END {
     for(k=1;k<=c;k++) {
         lg1=a3[k]*(pi/180);
         lt1=a4[k]*(pi/180);
         ndist=999999999.1;
         for(l=1;l<=d;l++) {
             if(b2[l]==a2[k]) {kk=b2[l];
                lg2=b3[l]*(pi/180);
                lt2=b4[l]*(pi/180);
                dist= 6378.7 * acos( sin(lt1)*sin(lt2) + cos(lt1)*cos(lt2)*cos(lg2-lg1) );
                if(dist<ndist) {ndist=dist; ws0=b1[l]}
             }
         }
         print a1[k],a2[k],a3[k],a4[k],ws0,ndist
     }
    }' file01 file02
4

4 に答える 4

2

file01 から 1 つ以上の配列に値を読み取ります。getlineブロックで使用できます。BEGINまたは、標準的な方法はFNR == NR、メイン ブロックの 1 つとしてループを使用することです。

FNR == NR {array[$1] = $1; ...; next } # read file01 into some arrays
{ for (item in array) { ... }     # process each item in the array(s) against each line in file02

スクリプトは次のように呼び出されますawk '...' file01 file02

フィールド値で配列にインデックスを付ける代わりに、カウンターを使用して配列にインデックスを付け、 :array1[c] = $1; array2[c] = $2; c++を使用する代わりにカウンターを使用して反復することができます。infor (i=0; i<c; i++)

もちろん、意味のある配列名を選択する必要があります。

于 2012-05-09T16:04:23.843 に答える
1

一度に実行するには、実行します

join -j 2 <(sort -k 2,2 file01) <(sort -k 2,2 file02)

そして、それを awk にパイプして、参照が変更されるたびに計算を行います。

gawk '{if (last != $1 $2) {calc_nearest_on_array; last=$1 $2; add_point_to_array} else {add_point_to_array}}'
于 2012-05-10T05:13:14.577 に答える
1

面白い挑戦。最初に file02 を読み込んで情報をデータ構造に格納する必要があるため、私はまず Perl を使用します。

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

# see http://perldoc.perl.org/Math/Trig.html
use Math::Trig qw(great_circle_distance deg2rad);
sub NESW {deg2rad($_[0]), deg2rad(90-$_[1])}

# read file02
my %data;
my $file2 = 'file02';
open my $fid, '<', $file2 or die "can't read $file2: $!\n";
while (<$fid>) {
    my ($id, $ref, $long, $lat) = split;
    push @{$data{$ref}}, [$id, $long, $lat];
}
close $fid;

$, = " ";

# process file01
my $file1 = 'file01';
open $fid, '<', $file1 or die "can't read $file1: $!\n";
while (<$fid>) {
    my ($id, $ref, $long, $lat) = split;
    my @here = NESW($long, $lat);
    my $min = 99_999_999;
    my (@min_id, $dist);

    while (my ($key, $listref) = each %data) {
        next unless $key == $ref;

        foreach my $trioref (@$listref) {
            my ($other_id, $other_long, $other_lat) = @$trioref;
            my @there = NESW($other_long, $other_lat);
            $dist = great_circle_distance(@here, @there, 6378.7);
            if ($dist < $min) {
                $min = $dist;
                @min_id = @$trioref;
            }
        }
    }

    printf "%s %d %s %s %s %6.3f\n", $id, $ref, $long, $lat, $min_id, $min;
}
close $fid;

これは出力します

A 1001 -1.2 30.31 ws4 67.308
A 1002 -1.2 30.31 ws2 35.783
B 1002 -1.8 30.82 ws4 55.387
B 1003 -1.8 30.82 ws4 55.387
C 1001 -2.1 28.55 ws1 93.361

「C」の距離が、あなたが提案するものとは異なることに気付きました。

于 2012-05-09T16:47:06.057 に答える