代码之家  ›  专栏  ›  技术社区  ›  Han

是否有一个Perl统计数据包不能让我一次加载整个数据集?

  •  9
  • Han  · 技术社区  · 16 年前

    我正在为Perl寻找一个统计数据包(CPAN很好),它允许我增量添加数据,而不必传递整个数据数组。

    只有平均值、中位数、stdev、max和min是必要的,没有什么太复杂的。

    这是因为我的数据集太大,无法装入内存。数据源在一个MySQL数据库中,所以现在我只是查询数据的一个子集,并为它们计算统计信息,然后再组合所有可管理的子集。

    如果你对如何克服这个问题有其他想法,我将不胜感激!

    7 回复  |  直到 8 年前
        1
  •  4
  •   Adam Bellaire    16 年前

    Statistics::Descriptive::Discrete 允许您以类似于Statistics::Descriptive的方式执行此操作,但已针对大型数据集进行了优化。(例如,文档报告内存使用率提高了两个数量级(100倍)。

        2
  •  5
  •   DVK    16 年前

    你不能做一个准确的stdev和中位数,除非你把整个事情都保存在内存中。 运行两次数据。

    更新 虽然您不能在一次传递中执行准确的stdev,但有一个近似的一次传递算法,链接在对该答案的注释中。

    剩下的部分在3-5行Perl中是完全微不足道的(不需要模块)。 stdev/median也可以在2次传球中完成(我刚刚推出了一个完全按照您描述的方式完成的脚本,但出于IP原因,我很确定不允许将其作为示例发布给您,抱歉)

    样例代码:

    my ($min, $max)
    my $sum = 0;
    my $count = 0;
    while (<>) {
        chomp;
        my $current_value = $_; #assume input is 1 value/line for simplicity sake
        $sum += $current_value;
        $count++;
        $min = $current_value if (!defined $min || $min > $current_value);
        $max = $current_value if (!defined $max || $max < $current_value);
    }
    my $mean = $sum * 1.0 / $count;
    my $sum_mean_diffs_2 = 0;
    
    while (<>) { # Second pass to compute stddev (use for median too)
        chomp;
        my $current_value = $_; 
        $sum_mean_diffs += ($current_value - $mean) * ($current_value - $mean);
    }
    my $std_dev = sqrt($sum_mean_diffs / $count);
    # Median is left as excercise for the reader.
    
        3
  •  4
  •   Community CDub    8 年前

    为什么不直接向数据库询问要计算的值呢?

    除此之外,mysql的功能 GROUP BY (Aggregate) functions . 对于缺失的功能,您只需要 a little SQL .

        4
  •  4
  •   Community CDub    8 年前

    PDL 可能提供一个可能的解决方案:

    看看前面的这个问题,答案是 means, std dev, etc .

    下面是代码的相关部分:

    use strict;
    use warnings;
    use PDL;
    
    my $figs = pdl [
        [0.01, 0.01, 0.02, 0.04, 0.03],
        [0.00, 0.02, 0.02, 0.03, 0.02],
        [0.01, 0.02, 0.02, 0.03, 0.02],
        [0.01, 0.00, 0.01, 0.05, 0.03],
    ];
    
    my ( $mean, $prms, $median, $min, $max, $adev, $rms ) = statsover( $figs );
    
        5
  •  1
  •   mabraham Frahm    15 年前

    @dvk:这里计算平均值和标准偏差的一次通过算法 http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm 不是近似值,并且比您给出的示例更具数值鲁棒性。参见该页的参考文献。

        6
  •  0
  •   tsee    16 年前

    这在很大程度上是未经测试的,所以小心使用。因为我的记忆力不好,我检查了算法 Wikipedia . 我不知道从数字流中计算中位数的算法,但这并不意味着没有。

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    use MooseX::Declare;
    
    class SimpleStats {
      has 'min'       => (is => 'rw', isa => 'Num', default => 9**9**9);
      has 'max'       => (is => 'rw', isa => 'Num', default => -9**9**9);
      has 'A'         => (is => 'rw', isa => 'Num', default => 0);
      has 'Q'         => (is => 'rw', isa => 'Num', default => 0);
      has 'n'         => (is => 'rw', isa => 'Int', default => 0);
      has 'n_nonzero' => (is => 'rw', isa => 'Int', default => 0);
      has 'sum_w'     => (is => 'rw', isa => 'Int', default => 0);
    
      method add (Num $x, Num $w = 1) {
        $self->min($x) if $x < $self->min;
        $self->max($x) if $x > $self->max;
        my $n = $self->n;
        if ($n == 0) {
          $self->A($x);
          $self->sum_w($w);
        }
        else {
          my $A = $self->A;
          my $Q = $self->Q;
          my $sum_w_before = $self->sum_w;
          $self->sum_w($sum_w_before+$w);
          $self->A($A + ($x-$A) * $w/$self->sum_w);
          $self->Q($Q + $w*($x-$A)*($x-$self->A));
        }
        $self->n($n+1);
        $self->n_nonzero($self->n_nonzero+1) if $w != 0;
        return();
      }
    
      method mean () { $self->A }
    
      method sample_variance () {
        $self->Q * $self->n_nonzero() /
        ( ($self->n_nonzero-1) * $self->sum_w )
      }
    
      method std_variance () { $self->Q / $self->sum_w }
      method std_dev      () { sqrt($self->std_variance) }
    
      # slightly evil. Just don't reuse objects
      method reset () { %$self = %{__PACKAGE__->new()} }
    }
    
    package main;
    
    my $stats = SimpleStats->new;
    
    while (<STDIN>) {
      s/^\s+//;
      s/\s+$//;
      my ($x, $w) = split /\s+/, $_;
      if (defined $w) {
        $stats->add($x, $w);
      } else {
        $stats->add($x);
      }
    }
    
    print "Mean:        ", $stats->mean, "\n";
    print "Sample var:  ", $stats->sample_variance, "\n";
    print "Std var:     ", $stats->std_variance, "\n";
    print "Std dev:     ", $stats->std_dev, "\n";
    print "Entries:     ", $stats->n, "\n";
    print "Min:         ", $stats->min, "\n";
    print "Max:         ", $stats->max, "\n";
    
        7
  •  0
  •   Dallaylaen    12 年前

    我知道这是4年前的事了,但如果有人感兴趣,现在 module 为了节省内存 近似 统计样本分析。它的接口通常遵循统计学的接口::描述性和协同。

    它将样本划分为对数间隔,并且只保留命中计数。因此,引入了一个固定的相对误差(精度可以在new()中进行调整),但是在不使用大量内存的情况下,可以处理大量数据。