2012-12-03 166 views
1

我有如下所示的文本文件。使用awk计算距离

CA  21.660 -6.795 11.323 
    C  28.811 -9.801 16.262 
    O  23.221 -9.266 13.799 
    CB 33.528 -11.934 17.900 
    N  21.660 -6.795 11.323 
    O  32.410 -8.539 16.566 

我想计算原子坐标之间的距离。例如,我想读取文件夹中的所有文件,并计算第一个和第二个原子之间的距离,第一个和第三个,第一个和第四个等。然后,第二个和第三个,第二个和第四个,第二个和第五个等。是(S 1 -X 2)^ 2 +(Y 1 -Y 2)^ 2 +(Z 1 -Z 2)^ 2)。我想将每个文件的输出保存到具有输入文件名称的另一个文件夹中。我怎样才能用awk做到这一点?

所需的输出

CA-C 4.52 
CA-O 3.80 
CA-CB 5.68 
CA-N 8.94 
-- 
-- 
-- 
N-O 5.98 

你的帮助,将不胜感激!

+0

也许从学习awk开始。或者你希望我们提供解决方案?如果您有特定的与编程相关的问题,请将其命名。 – cxxl

回答

3

下面是使用GNU awk一个办法:你想要什么

awk 'FNR==NR { a[NR]=$0; next } { for (i=FNR+1;i<=NR-1;i++) { split(a[i],b); print $1 "-" b[1], sqrt(($2-b[2])^2 + ($3-b[3])^2 + ($4-b[4])^2) | "column -t" } NR--}' file file 

确实如此,但无论是您所提供的算法是不同的,你需要什么,或者,你的期望输出已被错误计算(我假设后者是问题)。总之,这里的结果:

CA-C 9.19601 
CA-O 3.83055 
CA-CB 14.5092 
CA-N 0 
CA-O 12.0869 
C-O 6.13194 
C-CB 5.42981 
C-N 9.19601 
C-O 3.82595 
O-CB 11.4092 
O-N 3.83055 
O-O 9.62406 
CB-N 14.5092 
CB-O 3.81517 
N-O 12.0869 

如果您需要在当前工作目录中对多个文件执行此,假设没有什么,但该目录中感兴趣的文件,你可以环绕awk语句for循环。很显然,你需要改变/path/to/folder/到您选择的路径,使其正常工作:

for i in *; do awk 'FNR==NR { a[NR]=$0; next } { for (i=FNR+1;i<=NR-1;i++) { split(a[i],b); print $1 "-" b[1], sqrt(($2-b[2])^2 + ($3-b[3])^2 + ($4-b[4])^2) | "column -t > /path/to/folder/" FILENAME } NR--}' "$i"{,}; done 
+0

太棒了!非常感谢!!! – mafugu

1

这样的事情听起来像你想要什么,但显然没有结果的匹配你说的话,他们应该这样澄清你的算法:

$ awk 'NR>1{print p[1]"-"$1,sqrt((p[2]-$2)^2 + (p[3]-$3)^2 + (p[4]-$4)^2)} {split($0,p) }' file 
CA-C 9.19601 
C-O 6.13194 
O-CB 11.4092 
CB-N 14.5092 
N-O 12.0869 

$ awk 'NR>1{print p[1]"-"$1,sqrt(($2-p[2])^2 + ($3-p[3])^2 + ($4-p[4])^2)} {split($0,p) }' file 
CA-C 9.19601 
C-O 6.13194 
O-CB 11.4092 
CB-N 14.5092 
N-O 12.0869 
2

如果原子是包含数据

awk '{ p[NR,0]=$1;p[NR,1]=$2;p[NR,2]=$3;p[NR,3]=$4; for (j=1;j<=NR-1;j++) print p[j,0]"-"$1,sqrt((p[NR,1]-p[j,1])^2+(p[NR,2]-p[j,2])^2+(p[NR,3]-p[j,3])^2) }' atoms 
CA-C 9.19601 
CA-O 3.83055 
C-O 6.13194 
CA-CB 14.5092 
C-CB 5.42981 
O-CB 11.4092 
CA-N 0 
C-N 9.19601 
O-N 3.83055 
CB-N 14.5092 
CA-O 12.0869 
C-O 3.82595 
O-O 9.62406 
CB-O 3.81517 
N-O 12.0869 
文件

存在问题:

  • 您的数据包含两个相同的O原子,因此它是很难说哪个是哪个输出
1

下面是AWK代码:

awk '{a[NR]=$0} 
    END 
    { 
     for(i=1;i<=NR;i++) 
     {split(a[i],k); 
     for(j=i+1;j<=NR;j++) 
      { 
      split(a[j],l); 
      dist=(k[2]-l[2])*(k[2]-l[2])+(k[3]-l[3])*(k[3]-l[3])+(k[4]-l[4])*(k[4]-l[4]); 
      print k[1]"-"l[1],sqrt(dist); 
      } 
     } 
    }' your_file 

并且在下面的测试:

> cat temp 
CA  21.660 -6.795 11.323 
    C  28.811 -9.801 16.262 
    O  23.221 -9.266 13.799 
    CB 33.528 -11.934 17.900 
    N  21.660 -6.795 11.323 
    O  32.410 -8.539 16.566 

执行:

> awk '{a[NR]=$0}END{for(i=1;i<=NR;i++){split(a[i],k);for(j=i+1;j<=NR;j++){split(a[j],l);dist=(k[2]-l[2])*(k[2]-l[2])+(k[3]-l[3])*(k[3]-l[3])+(k[4]-l[4])*(k[4]-l[4]);print k[1]"-"l[1],sqrt(dist);}}}' temp 
CA-C 9.19601 
CA-O 3.83055 
CA-CB 14.5092 
CA-N 0 
CA-O 12.0869 
C-O 6.13194 
C-CB 5.42981 
C-N 9.19601 
C-O 3.82595 
O-CB 11.4092 
O-N 3.83055 
O-O 9.62406 
CB-N 14.5092 
CB-O 3.81517 
N-O 12.0869 
> 
0

Perl解决方案可能是:

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

my @data = map [split], <DATA>; 

for (my $i = 0; $i < @data; $i++) { 
    for (my $j = $i+1; $j < @data; $j++) { 
     my $d = distance(@data[$i, $j]); 
     printf("%-6s%7.4f\n", 
      join("-", map $_->[0], @data[$i, $j]), $d) if $d <= 6; 
    } 
} 

sub distance { 
    my ($coord1, $coord2) = @_; 
    my $sum; 
    for my $k (1 .. $#$coord1) { 
     $sum += ($coord1->[$k] - $coord2->[$k])**2; 
    } 
    return sqrt $sum; 
} 

__DATA__ 
CA  21.660 -6.795 11.323 
C  28.811 -9.801 16.262 
O  23.221 -9.266 13.799 
CB 33.528 -11.934 17.900 
N  21.660 -6.795 11.323 
O  32.410 -8.539 16.566