使用Perl查找中位数、众数和标准差?

11

我有一个数字数组。最简单的方法是如何计算数据集的中位数、众数和标准差?

3个回答

30

为什么该模块中没有 Statistics::Basic::MaxStatistics::Basic::Min - Agostino
2
因为它们在List::Util中,而List::Util又属于核心模块。 - Dave Jacoby

16
#!/usr/bin/perl
#
# stdev - figure N, min, max, median, mode, mean, & std deviation
#
# pull out all the real numbers in the input
# stream and run standard calculations on them.
# they may be intermixed with other test, need
# not be on the same or different lines, and 
# can be in scientific notion (avagadro=6.02e23).
# they also admit a leading + or -.
#
# Tom Christiansen
# tchrist@perl.com

use strict;
use warnings;

use List::Util qw< min max >;

sub by_number {
    if ($a < $b){ -1 } elsif ($a > $b) { 1 } else { 0 }
}


#
my $number_rx = qr{

  # leading sign, positive or negative
    (?: [+-] ? )

  # mantissa
    (?= [0123456789.] )
    (?: 
        # "N" or "N." or "N.N"
        (?:
            (?: [0123456789] +     )
            (?:
                (?: [.] )
                (?: [0123456789] * )
            ) ?
      |
        # ".N", no leading digits
            (?:
                (?: [.] )
                (?: [0123456789] + )
            ) 
        )
    )

  # abscissa
    (?:
        (?: [Ee] )
        (?:
            (?: [+-] ? )
            (?: [0123456789] + )
        )
        |
    )
}x;

my $n = 0;
my $sum = 0;
my @values = ();

my %seen = ();

while (<>) {
    while (/($number_rx)/g) {
        $n++;
        my $num = 0 + $1;  # 0+ is so numbers in alternate form count as same
        $sum += $num;
        push @values, $num;
        $seen{$num}++;
    } 
} 

die "no values" if $n == 0;

my $mean = $sum / $n;

my $sqsum = 0;
for (@values) {
    $sqsum += ( $_ ** 2 );
} 
$sqsum /= $n;
$sqsum -= ( $mean ** 2 );
my $stdev = sqrt($sqsum);

my $max_seen_count = max values %seen;
my @modes = grep { $seen{$_} == $max_seen_count } keys %seen;

my $mode = @modes == 1 
            ? $modes[0] 
            : "(" . join(", ", @modes) . ")";
$mode .= ' @ ' . $max_seen_count;

my $median;
my $mid = int @values/2;
my @sorted_values = sort by_number @values;
if (@values % 2) {
    $median = $sorted_values[ $mid ];
} else {
    $median = ($sorted_values[$mid-1] + $sorted_values[$mid])/2;
} 

my $min = min @values;
my $max = max @values;

printf "n is %d, min is %g, max is %g\n", $n, $min, $max;
printf "mode is %s, median is %g, mean is %g, stdev is %g\n", 
    $mode, $median, $mean, $stdev;

这比sub by_number { return $a <=> $b }更好吗,对于某种“更好”的定义? - Jim Davis
2
my @sorted_values = sort {$a <=> $b} @values;有什么问题吗? - hepcat72
@hepcat72 这是一个很好的问题。我已经不记得为什么这样做了。 - tchrist
如果您已经对数据进行了排序,那么调用min和max函数是否有任何价值呢? - Duncan

15

如果你只需要简单的数学功能,erickb的回答可能适用。然而,如果你需要使用Perl进行数字处理的话,可以使用PDL。你可以使用pdl函数创建一个piddle对象(包含你的数据)。从那里,你可以使用这个页面上的操作来执行所需的统计工作。

编辑:我找到了两个函数调用,它们恰好能够满足你的需求。statsover对piddle的一个维度进行统计,而stats会对整个piddle执行相同操作。

my $piddle = pdl @data;
my ($mean,$prms,$median,$min,$max,$adev,$rms) = statsover $piddle;

1
我之前其实没听说过PDL,可能是因为我做的大部分工作都是系统管理员工具开发之类的。这确实非常酷,谢谢你的提示。 - BadFileMagic
1
@BadFileMagic,我看了一场关于NumPy/SciPy的演讲后感到非常失望,认为这意味着我必须离开Perl转向Python。然后我发现了PDL。一切都好了。 - Joel Berger
1
以前从未听说过这个。看起来太棒了! - Leonardo Herrera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接