【问题标题】:Calculating distances with awk用 awk 计算距离
【发布时间】:2012-12-03 14:30:15
【问题描述】:

我有如下所示的文本文件。

 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

我想计算原子坐标之间的距离。例如,我想读取文件夹中的所有文件并计算第一个和第二个原子之间的距离,第一个和第三个,第一个和第四个等。然后,第二个和第三个,第二个和第四个,第二个和第五个等。公式是 SQRT ((X1-X2)^2 + (Y1-Y2)^2 + (Z1-Z2)^2)。我想用输入文件的名称将每个文件的输出保存到另一个文件夹中。如何使用 awk 做到这一点?

想要的输出

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

您的帮助将不胜感激!

【问题讨论】:

  • 也许从学习 awk 开始。或者您希望我们提供解决方案?如果您有特定的编程相关问题,请说出它。

标签: awk


【解决方案1】:

这是使用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

【讨论】:

    【解决方案2】:

    如果 atom 是包含您的数据的文件

    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 原子,因此很难说输出中的哪个是哪个

    【讨论】:

      【解决方案3】:

      这样的事情听起来像你想要的,但显然这两个结果都不符合你所说的它们应该澄清你的算法:

      $ 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
      

      【讨论】:

        【解决方案4】:

        下面是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
        > 
        

        【讨论】:

          【解决方案5】:

          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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-19
            • 2014-08-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多