【问题标题】:perl: sort numbers in column in orderperl:按顺序对列中的数字进行排序
【发布时间】:2016-01-14 20:36:42
【问题描述】:

我想对最后一列中的数字进行排序,$en 以相反的顺序(从最小值到最大值)。更多,我指定了我的数字。它们达到 0 并且只是负数。

我的脚本如下:

!/usr/bin/perl -w

use strict;

my $list=$ARGV[0];


open(LST,$list) or die;

my $time=0;

my @id_table;
my @nr_table;
my @energy;

open(GRO,">waters.gro") or die;

while(<LST>) {
  my $pdb_file=$_;
  chomp $pdb_file;
  my $pdb_id=substr $pdb_file,0,4;
  open(PDB,$pdb_file) or die;
  while(<PDB>) {
      my $line=$_;
      my ($w_id, $x, $y, $z, $en) = (split(/\s+/, $line))[1, 5, 6, 7, 8];
      next if $en >= 0;
      my @energy = sort {$b <=> $a} $en;
      print GRO "moja woda t=   $time \n";
      printf(GRO "%5d\n",1);
      printf(GRO "    1SOL     OW    1%8.3f%8.3f%8.3f%8.3f\n",$x/10.0,$y/10.0,$z/10.0,$en);
      print GRO " 20.0 20.0 20.0\n";
      $id_table[$time]=$pdb_id;
      $nr_table[$time]=$w_id;
      $time++;
  }
  close PDB;
}
close GRO;

很遗憾,我的排序想法行不通。我是 Perls 脚本的初学者。在我的输出中,我想按递增顺序排列值。

提前谢谢你。 玛塔

我的输入文件:

ATOM    367  OH2 HOH   367      -2.010   7.370  -7.369   -6.52
ATOM    491  OH2 HOH   491       0.990   8.370  -8.369   -2.24
ATOM    652  OH2 HOH   652       5.490  -6.130   2.631    2.98
ATOM    689  OH2 HOH   689       6.490 -15.130   8.631   -4.23
ATOM    738  OH2 HOH   738       7.490  19.870  -8.369    3.38
ATOM    793  OH2 HOH   793       8.990  -2.630 -22.869   -2.29
ATOM    857  OH2 HOH   857      10.490  13.370  -5.869   -1.31
.
.
.

操作后我得到了输出(没有排序线):

moja woda t=   0 
        1
        1SOL     OW    1  -0.344   0.437   0.633  -9.290
     20.0 20.0 20.0
    moja woda t=   1 
        1
        1SOL     OW    1  -0.194   0.537  -0.767  -2.990
     20.0 20.0 20.0
    moja woda t=   2 
        1
        1SOL     OW    1  -0.044   0.287   0.333   4.960
     20.0 20.0 20.0
    moja woda t=   3 
        1
        1SOL     OW    1   0.106   0.837  -0.817  -1.300
     20.0 20.0 20.0
    moja woda t=   4 
    .
    .
    .

我想按顺序排列我的数据(对于每一行,取决于 $en 列)。我想我应该创建一个新数组,但我不知道我应该如何准确地以及将排序放在脚本中的确切位置。

【问题讨论】:

  • 你能比“它不起作用”更具体吗?
  • @AKHolland 如果您查看代码,则 OP 的问题变得非常明显:my @energy = sort {$b &lt;=&gt; $a} $en;(不过,您是对的,它们绝对应该更具体。)
  • 您希望这些行在整个输出中排序,还是在列表文件中每个文件的数据中排序?
  • FWIW,从最小到最大将是升序排序,这是默认值。倒序将是降序排序,或从大到小。
  • 请为此问题创建minimal reproducible example(即包括示例数据)和edit您的帖子。如果您帮助我们不必从我们的...呃...耳朵中提取样本数据,那么帮助您解决问题会容易得多。

标签: perl sorting


【解决方案1】:

我已经修改了您的代码存在一些问题

您的排序不起作用的原因是该语句

my @energy = sort {$b <=> $a} $en

$en 的值进行排序,并将其放入@energy。只有一个值,显然无事可做。您必须一次性拥有所有要排序的数据

只要您的文件大小合理,通常的方法是将文件读入每个元素一条记录的数组中,然后对该数组进行排序。我已经在下面的程序中做到了。数组@pdb_data 的每个元素都包含对一个包含五个字段的数组的引用,这些字段按照您最初拥有的顺序排列

$time 值作为第六个字段添加到每条记录中,因为这必须在排序之前完成。最后,数组按$en 的升序排序——第五个字段。 (顺便说一下,标准的排序顺序是从较小的值到较大的值。这是您想要的正常排序,而不是颠倒的排序。)

数组中经过过滤和排序的数据可以在一个简单的for 循环中打印出来。任务

my ( $w_id, $x, $y, $z, $en, $time ) =  @$_

从每个数组元素中提取字段,就像它们最初一样,在末尾添加一个时间字段

我无法在没有任何数据的情况下对此进行测试,但程序确实可以编译

#!/usr/bin/perl

use strict;
use warnings 'all';

my ($list_file) = @ARGV;

open my $lst_fh, '<', $list_file or die qq{Unable to open "$list_file" for input: $!};

my $gro_file = 'waters.gro';
open my $gro_fh, '>', $gro_file or die qq{Unable to open "$gro_file" for input: $!};

while ( my $pdb_file = <$lst_fh> ) {

    chomp $pdb_file;
    open my $pdb_fh, '<', $pdb_file or die $!;

    my @pdb_data;
    my $time = 0;

    while ( <$pdb_fh> ) {

        my @record = ( split )[ 1, 5, 6, 7, 8 ];

        next unless $record[4] < 0;

        push @record, $time++;

        push @pdb_data, \@record;
    }

    @pdb_data = sort { $a->[4] <=> $b->[4] } @pdb_data;

    my $stdout = select $gro_fh;

    for ( @pdb_data ) {

        my ( $w_id, $x, $y, $z, $en, $time ) =  @$_;

        printf "moja woda t=   %d\n", $time;
        printf "%5d\n", 1;
        printf "    1SOL     OW    1%8.3f%8.3f%8.3f%8.3f\n", $x/10.0, $y/10.0, $z/10.0, $en;
        print  " 20.0 20.0 20.0\n";
    }

    select $stdout;
}

close $gro_fh or die $!;



更新

此变体在排序和打印之前从列表文件中的所有文件中读取所有数据。时间值按$ne字段的顺序应用

#!/usr/bin/perl

use strict;
use warnings 'all';

my ($list_file) = @ARGV;
my $gro_file = 'waters.gro';

open my $lst_fh, '<', $list_file or die qq{Unable to open "$list_file" for input: $!};

my @pdb_data;

while ( my $pdb_file = <$lst_fh> ) {

    chomp $pdb_file;
    open my $pdb_fh, '<', $pdb_file or die $!;

    while ( <$pdb_fh> ) {

        my @record = ( split )[ 1, 5, 6, 7, 8 ];

        next unless $record[4] < 0;

        push @pdb_data, \@record;
    }
}


@pdb_data = sort { $a->[4] <=> $b->[4] } @pdb_data;

open my $gro_fh, '>', $gro_file or die qq{Unable to open "$gro_file" for input: $!};
select $gro_fh;

my $time = 0;

for ( @pdb_data ) {

    my ( $w_id, $x, $y, $z, $en ) =  @$_;

    printf "moja woda t=   %d\n", $time++;
    printf "%5d\n", 1;
    printf "    1SOL     OW    1%8.3f%8.3f%8.3f%8.3f\n", $x/10.0, $y/10.0, $z/10.0, $en;
    print  " 20.0 20.0 20.0\n";
}

close or die $!;

输出

moja woda t=   0
    1
    1SOL     OW    1  -0.201   0.737  -0.737  -6.520
 20.0 20.0 20.0
moja woda t=   1
    1
    1SOL     OW    1   0.649  -1.513   0.863  -4.230
 20.0 20.0 20.0
moja woda t=   2
    1
    1SOL     OW    1   0.899  -0.263  -2.287  -2.290
 20.0 20.0 20.0
moja woda t=   3
    1
    1SOL     OW    1   0.099   0.837  -0.837  -2.240
 20.0 20.0 20.0
moja woda t=   4
    1
    1SOL     OW    1   1.049   1.337  -0.587  -1.310
 20.0 20.0 20.0

【讨论】:

  • 我的列表文件有几个 pdb 文件(大约 5-120 个文件 - 它取决于系列),每个 pdb 文件由几个数据行组成。我想对整体进行排序(列表文件中的一系列 pdb_file)。
  • 非常感谢您的回复!
  • @MartaWisniewska:好的,那么您希望 time 字段发生什么变化?它应该在所有文件中不断增加,还是应该为每个文件重置为零?
  • 好的。我想以递增的顺序对 $en 列进行排序,直到它达到 0,然后停止打印数据,对于 time= 行,我想将它从 0 编号到某处。 (对于最小的 $en 值 time=0 ,在下一个 $en 值 time=1.
猜你喜欢
  • 2020-09-29
  • 1970-01-01
  • 2020-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多