【问题标题】:How to calculate variance in perl?如何计算perl中的方差?
【发布时间】:2014-04-19 06:04:15
【问题描述】:

我想计算 perl 中每一行数字的方差。我写了这个子程序:

################################################################
# variance
#
#
# A subroutine to compute the variance of an array
# division by n-1 i s used
#
sub var{
    my ($data) = @_;
    if (@$data ==1) {
        return 0;
    }
    my $mean = mean ($data);
    my $sqtotal = 0;
    foreach (@$data) {
        $sqtotal += ($_ - $mean) ** 2
    }
    my $var = $sqtotal / (scalar @$data - 1);
    return $var;
}

如果我给它这个包含 58 个相同数量元素的数组

[0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98, 0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98, 0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98,0.98]

计算结果是 1.25421964097639e-30。

我还尝试使用 Statistics::Descriptive 模块 (http://metacpan.org/pod/Statistics::Descriptive),它给了我 2.11916254524942e-15。

我也试过这个网站 (http://www.alcula.com/calculators/statistics/variance/),结果是 2.2438191655582E-15。

为什么结果不一样...

我本可以只使用该模块,但不知何故它对我的文件来说非常占用内存,该文件基本上由 58 个数字组成的百万行。我不确定为什么它会占用这么多内存。

有人能告诉我为什么我的计算给出了与模块不同的数字,以及如何使模块以更少的内存工作吗?内存密集型只是该模块的固有缺点吗?一些帖子似乎暗示了这一点。

谢谢!

【问题讨论】:

  • 所有这些数字本质上都是零模浮点运算。而你自己的尝试似乎是最精确的。它看起来也会进行正确的计算,如果它也使用更少的内存,我建议你可以使用自己的子。编辑:哦,对不起,我现在才意识到我参加聚会有多晚。 ^^

标签: perl statistics variance


【解决方案1】:

我一直在等待有人发布有关浮点运算 (ty mob) 的帖子,因为这最终是答案。

但是,您链接到的 Statistics::Descriptive 模块是 version 2.6,而当前是 version 3.0607

如果您查看 2.6 的源代码,它会使用一些奇怪的数学来计算均值和方差:

sub add_data {
  ....

  ##Calculate new mean, pseudo-variance, min and max;
  foreach ( @{ $aref } ) {
    $oldmean = $self->{mean};
    $self->{sum} += $_;
    $self->{count}++;

    ....

    $self->{mean} += ($_ - $oldmean) / $self->{count};
    $self->{pseudo_variance} += ($_ - $oldmean) * ($_ - $self->{mean});
  }

现在这种计算运行平均值的方法在数学上是准确的,与Sum(A1..An)/n 相同。但是,由于浮点运算,您会看到与直接从总和/计数中计算平均值相比有所不同。此外,我怀疑这种运行方差在数学上可能是相同的(没有费心做纸质证明),但您也会看到由于浮点运算导致的细微差异。

该模块的最新版本确实使用了更简单的计算均值和方差的方法,并且通过使用 List::Util 等模块也提高了一些效率。因此,如果您对 2.6 版的链接不是站外链接,那么我建议您升级到最新版本。

【讨论】:

    【解决方案2】:

    定点算术

    您的数据是否使用 0.01 (1/100) 精度?您提供的示例是我的建议。

    YES => 您可以使用定点算术而不是浮点算术来减少舍入误差的累积。使用 1/(100**2)=1/10_000 比例因子。

    https://en.wikipedia.org/wiki/Fixed-point_arithmetic


    sub var4{
        my ($data) = @_;
        if (@$data ==1) {
            return 0;
        }
        my $totalD2 = 0;
        foreach (@$data) {
           $totalD2 += $_*100
        }
        my $meanD2 = $totalD2 / (scalar @$data);
    
        my $sqtotalD4 = 0;
        foreach (@$data) {
            $sqtotalD4 += ($_*100 - $meanD2) ** 2;
        }
        my $varD4 = $sqtotalD4 / (scalar @$data - 1);
        return $varD4/10_000; # convert from fixed point to floating point
    }
    

    【讨论】:

      【解决方案3】:

      常数序列的方差为零,因此您的计算或多或少都是正确的,也或多或少是相同的。

      您得到的结果与零略有不同,因为您正在使用有限精度浮点数执行许多操作。我们来看看这段代码:

       $z = 0;
       $z += 0.98 for 1..58;
       $mean = $z / 58;
       printf "%.20f", $mean;
      

      使用这段代码,我们将数字 0.98 的 58 个实例相加,然后将总和除以 58。这段代码将打印出 0.98000000000000000000 是有道理的,对吧?不,我实际上得到的是

       0.97999999999999887201
      

      (您的结果可能会有所不同)。

      规范的What Every Programmer Should Know About Floating-Point Arithmetic可以为你解释血淋淋的细节。

      【讨论】:

        猜你喜欢
        • 2013-09-02
        • 2020-02-05
        • 2012-10-30
        • 1970-01-01
        • 2020-07-25
        • 2017-04-04
        • 1970-01-01
        • 2022-01-15
        相关资源
        最近更新 更多