如何计算perl的方差?

时间:2014-03-14 05:14:05

标签: perl statistics variance

我想计算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个数字组成。我不知道为什么它耗尽了这么多内存。

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

谢谢!

3 个答案:

答案 0 :(得分:5)

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

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

 $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可以向你解释血淋淋的细节。

答案 1 :(得分:1)

定点运算

您的数据使用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
}

答案 2 :(得分: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版本只是一个过分,那么我建议您升级到最新版本。