使用Perl基于两个列值选择文件中的行

时间:2014-07-14 12:39:12

标签: perl

在我的数据集中有2列,第一列是“参考”列,第二列是“值”列。参考值可以重复多次,但每个参考值都有不同的值。

e.g。

ref1  0.0234
ref1  2.8951
ref1  1.4565
ref2  20.346
ref2  24.345

我需要创建一个脚本,该脚本将包含每个引用ID的最高值的行,并将其放在一个新文件中。对于我的示例,新文件看起来像:

ref1  2.8951
ref2  24.345

5 个答案:

答案 0 :(得分:1)

我会使用awk执行此任务:

$ awk '$2+0>a[$1]{a[$1]=$2;b[$1]=$0}END{for (i in a) {print b[i]}}' infile                           
ref1 2.8951
ref2 24.345

Perl非常有用,但在我看来,对于这个简单的任务,你无法击败awk简单。

这是内部,当第二列大于为该引用存储的先前值($ 1)时,我们保留/更新两个结构(哈希):

  • a:更大的价值和参考
  • b:line ref。

在END部分,我们负责显示结果,因此我们将最大参考值与其线条相关联。

答案 1 :(得分:1)

这是一种方法:

$ perl -anle '
    $h{$F[0]} = $F[1] > $h{$F[0]} 
              ? $F[1]
              : $h{$F[0]}
              ;
    END {print "$_ $h{$_}" for sort keys %h}
' file
ref1 2.8951
ref2 24.345

<强>解释

  • 在阅读文件时,我们创建了一个哈希%h
    • Key是参考ID
    • 价值是应对价值
  • 如果当前id的值大于hash %h中id的值,我们将值更新为hash,否则保持当前值。
  • 完成阅读文件后,我们会在%h中打印键,值对,按键顺序排序。

答案 2 :(得分:1)

klashxx的解决方案很接近,但额外的阵列确实没有必要。

awk '$2+0 > a[$1] {
         a[$1] = $2
     }
     END {
         for (i in a) {
             print i, a[i]
         }
     }' ref

在我看来,awk是这项工作的“正确”工具,因为它是为处理这样​​的记录中的数据而构建的。虽然我建议您阅读awk以获得该计划的全部要点,但这里有一些解释,以便了解正在发生的事情。

awk遍历文件中的所有行并对其进行操作。花括号中的第一个块包含仅在第二个字段($ 2)大于该数组中该参考值的已保存值的值的行上的操作。 +0强制转换为数值。在此块中,然后将$ 2值分配给数组a,其索引为参考值。

只有在读取文件中的所有行后,才会调用END块。它遍历您拥有的数组,并从数组中打印索引和值。

答案 3 :(得分:0)

你可以这样做:

perl -alne '
     unless (defined($h{$F[0]})){ 
        $h{$F[0]}=0
     } 
     if($h{$F[0]} <= $F[1] ){
         $h{$F[0]}=$F[1]
     }END{
         foreach( keys %h){print "$_ $h{$_}"}
     }' file

ref1 2.8951
ref2 24.345

全部在一行:

perl -alne 'unless (defined($h{$F[0]})){ $h{$F[0]}=0}; if($h{$F[0]} <= $F[1] ){$h{$F[0]}=$F[1];}END{foreach( keys %h){print "$_ $h{$_}"}}' file
ref1 2.8951
ref2 24.345

答案 4 :(得分:0)

即使它已被回答,我也会肛门并按照信中的说明制作一个实际的perl脚本:

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

unless ($ARGV[0]) { die "Missing source file as first arguement\n" }
unless ($ARGV[1]) { die "Missing destination file as second arguement\n" }

unless (-e $ARGV[0]) { die "Filename $ARGV[0] not found\n" }
if (-e $ARGV[1]) { die "Cowardly refusing to overwrite $ARGV[1]\n" }

open OLD,"<$ARGV[0]" or die "Couldn't open file: $!";
my %refs;
while (<OLD>)
{
        my ($ref, $value) = split(/\s+/,$_);
        if ($refs{$ref})
        {
                if ($value > $refs{$ref}) { $refs{$ref} = $value }
        } else { $refs{$ref} = $value }
}

open NEW,">>$ARGV[1]";
foreach my $ref (keys(%refs))
{
        print "$ref\t$refs{$ref}\n";
        print NEW "$ref\t$refs{$ref}\n";
}
  • %refs跟踪每个ref的最高值,因为while - 循环遍历文件。
  • 对于每一行,它会根据%refs中存储的值检查值,如果更高则更新。
  • 最后,它会将数据打印到新文件,并打印结果。

不像其他一些建议一样干净和紧凑,但对于不熟悉perl的人来说应该易于阅读和编辑。