【问题标题】:Getting the maximum value per column in a multidimensional array获取多维数组中每列的最大值
【发布时间】:2014-12-01 20:25:15
【问题描述】:

我正在尝试获取 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

【问题讨论】:

  • 输入的是文件?
  • 在收集数据之前运行另一个命令,但是,我无法控制数据的呈现方式。
  • 请解释您想要的输出
  • sputnick,我没有任何数组代码,我也不是在寻找整个程序。我只是不知道如何设置一个允许我通过 3 个单独的列抓取数据的数组。当我确实使用 max() 函数时,我需要输出如下所示: 11/22/14 21:02:35 7,692 max for column A, 11/22/14 21:02:35 2,593 max for column B , 11/22/14 21:02:35 C 列最大 2,649

标签: arrays perl sorting data-structures


【解决方案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】:

    这可能类似于 Schwartzian 变换,以减少因行拆分而产生的一些开销,它使用来自 List::Util 核心模块的 reduce() 仅选择具有最大值的行,

    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);
    

    【讨论】:

    • 最大的“A”栏不是7,692吗?也许如果逗号被删除tr/,/./ ==> tr/,//d ?
    • @G.Cito 好点,OP 应该知道这些是千位还是小数位。
    • 它们是逗号,您是否使用reduce 在数字列表中进行A/B 比较?我打算使用 max(),因为我希望它只获取 A、B 和 C 列中的最高数字。
    • @Newbie_Mark 你不能只使用max,如果你想“将这些值与日期和时间联系起来”
    • @mpapec 出色的“重构”示例/练习 - 我们可以深入了解您的想法 :-) 我在尝试中稍微简化了一些事情(我可以​​获得排序的最大值!)并尝试捕获“编辑”中的变化,但我真的很喜欢重构演示 ++
    【解决方案3】:

    这是一种机械的、程序化的,比“baby perl”方法稍微先进一些。该脚本相当简单,并且遵循已知的 perl“习语”。

      1234563我们在这里删除逗号 (tr/,//d)
    • 然后我们使用每列临时数组 (@atmp, @btmp, ...) 并使用 @lines 的直接 sort 填充它们,访问内部匿名数组中的相关列($a-&gt;[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

    【讨论】:

    • NB 我删除了每个数字中的逗号以将值转换为整数,但由于某种原因,我得到了奇怪的结果(请参阅上面对 @mpapec 帖子的评论)。我将编辑以改进“优雅”打印完整结果并删除临时变量。
    • 尽管最大值丢失了相应的日期/时间,但您得到了正确的结果。 eval.in/229491
    • 只是快速尝试一下 - 我需要更多地考虑如何保持获取其余部分,但错误不断出现(参见我的 cmets 上面的帖子)。如果我不能修复它,我会删除它。
    • @mpapec 一开始就把它们放在一起解决了这个问题 :-) 谢谢。
    • @Сухой27 修复了它,但你是否与 mpapec 混淆了
    猜你喜欢
    • 2012-10-29
    • 2017-11-03
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 2017-10-16
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    相关资源
    最近更新 更多