我有2个数据文件:file01
和file02
。在两个数据集中,字段是:(i)标识符; (ii)数字参考; (iii)经度; (iv)纬度。
对于file01
中的每一行,我想使用相同的数字引用在file02
中搜索数据,然后在file02
中找到最接近{{1}中的标识符的标识符}。
如果我使用以下代码手动将值从file01
传递到awk程序,我可以得到这个:
file01
如您所见,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
语句中的date
,lo1
和la1
是BEGIN
第一行中的值(有关数据,请参见下文)文件)。我的问题是我是否可以立即执行此操作,因此每次我在file01
中读取一行时,我都会获得最近的标识符和距离,并附加到file01
中的行数据。我不知道是否有一些shell命令可以更容易地实现这一点,也许使用管道。
这两个数据文件和所需输出的示例是:
=== file01 ===
file01
=== file02 ===
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
===输出文件===
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
编辑#1:考虑到@Eran的建议,我写了以下代码:
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
此脚本的输出为:
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
}
}'
编辑#2 :使用@Dennis的建议(经过一些修改)我得到了所需的输出。 awk脚本如下:
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
答案 0 :(得分:2)
将file01中的值读入一个或多个数组。您可以在getline
块中使用BEGIN
,或者规范的方法是使用FNR == NR
循环作为主要块之一。
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++
对其进行索引,而不是使用in
for (i=0; i<c; i++)
进行迭代,而不是使用字段值对数组建立索引:
当然,您应该选择有意义的数组名称。
答案 1 :(得分: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”距离与您的建议距离不同。
答案 2 :(得分: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}}'
答案 3 :(得分:0)
TXR:
@(do
(defvar pi 3.1415926535)
(defvar earth-radius 6378.7)
(defun rad (deg) (/ (* deg pi) 180))
(defun sphere-distance (lat0 lon0 lat1 lon1)
(let ((lat0 (rad lat0)) (lat1 (rad lat1))
(lon0 (rad lon0)) (lon1 (rad lon1)))
(* earth-radius (acos (+ (* (sin lat0) (sin lat1))
(* (cos lat0) (cos lat1) (cos (- lon1 lon0)))))))))
@(next "file01")
@(collect)
@id @ref @lon0 @lat0
@ (filter :tonumber lon0 lat0)
@ (next "file02")
@ (bind (min-dist ws) (1e99 nil))
@ (collect)
@ws1 @ref @lon1 @lat1
@ (filter :tonumber lon1 lat1)
@ (do (let ((d (sphere-distance lat0 lon0 lat1 lon1)))
(cond ((< d min-dist)
(set min-dist d)
(set ws ws1)))))
@ (end)
@ (do (format t "~a ~a ~0,2f ~0,2f ~a ~0,3f\n" id ref lon0 lat0 ws min-dist))
@(end)
执行命令
$ txr dist.txr
A 1001 -1.20 30.31 ws4 67.308
A 1002 -1.20 30.31 ws2 35.783
B 1002 -1.80 30.82 ws4 55.387
B 1003 -1.80 30.82 ws4 55.387
C 1001 -2.10 28.55 ws1 93.361