获取多维数组中每列的最大值

时间:2014-12-01 20:25:15

标签: arrays perl sorting data-structures

我试图获得A,B和C列的最高值,并将这些值与它们出现的日期和时间联系起来。要获得最大值,我只需要使用List :: Util模块,但我不知道如何将其指向数组中的特定列,然后引用日期和时间。

DATE    TIME    A   B   C
11/22/14    21:00:00    5,854   2,105   1,290
11/22/14    21:02:35    7,692   2,593   2,649
11/22/14    21:05:10    1,639   458 444
11/22/14    21:07:00    1,032   487 434
11/22/14    21:08:15    4,707   1,352   646
11/22/14    21:10:22    351 46  162
11/22/14    21:10:55    5,507   1,943   957
11/22/14    21:11:00    1,703   647 516
11/22/14    21:12:00    2,359   751 785
11/22/14    21:14:05    67  25  44
11/22/14    21:16:25    4,072   1,596   1,050
11/22/14    21:17:48    5,060   2,131   1,996
11/22/14    21:19:00    341 42  137
11/22/14    21:23:00    1,308   71  634

3 个答案:

答案 0 :(得分:1)

这可能类似于Schwartzian变换,以减少线路分裂造成的一些开销,并使用reduce()核心模块中的List::Util来选择一条具有最大值的线,

use strict;
use warnings;
use List::Util 'reduce';

(undef, my @tmp) = map { tr/,/./; [ $_, split ] } <DATA>;

my ($max_a) = 
  map $_->[0],
  reduce {
    $a->[3] > $b->[3] ? $a : $b
  }
  @tmp;

my ($max_b) = 
  map $_->[0],
  reduce {
    $a->[4] > $b->[4] ? $a : $b
  }
  @tmp;

my ($max_c) = 
  map $_->[0],
  reduce {
    $a->[5] > $b->[5] ? $a : $b
  }
  @tmp;

print
  "maxA: ", $max_a,
  "maxB: ", $max_b,
  "maxC: ", $max_c;

__DATA__
DATE    TIME    A   B   C
11/22/14    21:00:00    5,854   2,105   1,290
11/22/14    21:02:35    7,692   2,593   2,649
11/22/14    21:05:10    1,639   458 444
11/22/14    21:07:00    1,032   487 434
11/22/14    21:08:15    4,707   1,352   646
11/22/14    21:10:22    351 46  162
11/22/14    21:10:55    5,507   1,943   957
11/22/14    21:11:00    1,703   647 516
11/22/14    21:12:00    2,359   751 785
11/22/14    21:14:05    67  25  44
11/22/14    21:16:25    4,072   1,596   1,050
11/22/14    21:17:48    5,060   2,131   1,996
11/22/14    21:19:00    341 42  137
11/22/14    21:23:00    1,308   71  634

输出

maxA: 11/22/14    21:10:22    351 46  162
maxB: 11/22/14    21:12:00    2.359   751 785
maxC: 11/22/14    21:10:55    5.507   1.943   957

一些重构,

sub get_max {
  my ($pos, $r) = @_;

  return map $_->[0],
    reduce {
      $a->[$pos] > $b->[$pos] ? $a : $b
    }
    @$r;
}

my ($max_a) = get_max(3, \@tmp);
my ($max_b) = get_max(4, \@tmp);
my ($max_c) = get_max(5, \@tmp);

答案 1 :(得分:1)

我的理解:

最后你想要三个数据:

  • A列中编号最高的日期和时间
  • B列中编号最高的日期和时间
  • C列中编号最高的日期和时间

您不想对数据进行排序。您无需存储任何其他内容。

你没有指定任何代码,所以我不打算给你一个完整的程序。你需要自己尝试一下。相反,我会给你一些提示:

您应该阅读References以及它们的工作原理。我们会将您的数据存储在参考文献中:

my %data;
$data{A}->{time} = "xxxxx";   # Time of highest item in column "A"
$data{A}->{date} = "xxxxx";   # Date of highest item in column "A"
$data{A}->{value} = "xxxx";   # Value of highest item in column "A"

与其他两列相同。

你可以遍历你的数据(它是一个文件吗?你没有解释)

my %data;
while ( my $line .... ) {
    my ( $date, $time, a_value, b_value, c_value ) = split /\s*/, $line;
    if ( not exists $data{A}->{value} or $a_value > $data{A}->{value} ) {
        $data{A}->{value} = $a_value;
        $data{A}->{date}  = $date;
        $data{A}->{time}  = $time;
    }
    ...    # Same for B and C

在这个循环中,如果该值高于我之前存储的值,我将$data{A}->{value}设置为我刚刚读入的值。这是寻找最高价值的常用方式。如果该值尚不存在,我还需要存储它。因此,我检查是否存在$data{A}->{value}。如果没有,我还是需要存储该值。 (我本可以做if ( not exists $data{A} or $a_value > $data{A}->{value} ))。

在while循环之后,%data将包含每列的最高值以及这些值的日期和时间。有很多代码重复。我本可以为每一列添加一个内部循环,但只用三列就不值得。

另外,请记住不要在数据中包含标题列。

如果$data{A}->{value}让您感到困惑,可以使用三个单独的哈希值:一个用于存储日期,一个用于存储时间,另一个用于存储值。

my %times;
my %dates
my %values;
while ( my $line .... ) {
    my ( $date, $time, a_value, b_value, c_value ) = split /\s*/, $line;
    if ( not exists $values{A} or $a_value > $values{A} ) {
        $values{A} = $a_value;
        $dates{A}  = $date;
        $times{A}  = $time;
    }
    ...    # Same for B and C

答案 2 :(得分:1)

这是一种机械的,程序性的,比“婴儿perl”方法更先进。该剧本非常简单,并且坚持使用已知的perl“习语”。

  • 首先,我们使用DATA将所有@lines放入map - 一个数组数组 - 我们可以在这里使用while (<DATA>){...}循环。我们在此时删除逗号(tr/,//d

  • 然后我们使用每列临时数组(@atmp@btmp,...)并使用sort @lines直接填充它们,访问$a->[n] ...操作的内部匿名数组(sort)中的相关列:这样我们就不使用模块并避免使用map

  • 一旦我们按列(反向)对数组进行排序,我们就可以打印第一个元素以获得每列的最高值。要打印,我们 dereference 第一个元素(例如 @{$atmp[0]}因为它是一个匿名数组)才能回来整条线 - 这样我们就将“最高价值”与其他列保持在一起。

NB 突出显示柱状排序我更改了原始数据,以便不同的行显示为每列的最大值。在原始数据中,第二行具有所有三列的最高值。我只使用tr|,||d代替tr/,//d来破坏SO语法高亮显示。

use v5.16;   # adds strict and warnings

my @lines = map { tr|,||d; [ split ] } <DATA> ; 
shift @lines;   # removes header

my @atmp = sort{ $b->[2]<=>$a->[2] } @lines;     
my @btmp = sort{ $b->[3]<=>$a->[3] } @lines;     
my @ctmp = sort{ $b->[4]<=>$a->[4] } @lines ;   

print "\t DATE     TIME    A    B    C \n";
print "Max A: @{$atmp[0]}\nMax B: @{$btmp[0]}\nMax C: @{$ctmp[0]}\n" ;  

__DATA__    
DATE    TIME    A   B   C     
11/22/14    21:00:00    5,854   2,105   1,290    
11/22/14    21:02:35    7,692   2,593   2,649    
11/22/14    21:05:10    1,639   458 444  
11/22/14    21:07:00    1,032   487 434
11/22/14    21:08:15    4,707   1,352   6460 
11/22/14    21:10:22    351 46  162 
11/22/14    21:10:55    5,507   9,943   957  
11/22/14    21:11:00    1,703   647 516  
11/22/14    21:12:00    2,359   751 785      
11/22/14    21:14:05    67  25  44          
11/22/14    21:16:25    4,072   1,596   1,050 
11/22/14    21:17:48    5,060   2,131   1,996    
11/22/14    21:19:00    341 42  137 
11/22/14    21:23:00    1,308   71  634  

<强>输出

 
~/$ perl sort_by_column.pl

         DATE     TIME    A    B    C 
Max A: 11/22/14 21:02:35 7692 2593 2649
Max B: 11/22/14 21:10:55 5507 9943 957
Max C: 11/22/14 21:08:15 4707 1352 6460

  • 为了更好地理解perl use DDP;后跟p @linesp @atmp 等创建的数据结构,可以成为“可视化”的有用工具。有关详细信息,请参阅Data::Printer