Perl 中是否有一个包允许您计算每个给定点的概率分布高度。例如,这可以在 R 中以这种方式完成:
> dnorm(0, mean=4,sd=10)
> 0.03682701
即点 x=0 落入正态分布的概率为 0.0368,mean=4,sd=10。我查看了Statistics::Distribution但它并没有提供这样做的功能。
Perl 中是否有一个包允许您计算每个给定点的概率分布高度。例如,这可以在 R 中以这种方式完成:
> dnorm(0, mean=4,sd=10)
> 0.03682701
即点 x=0 落入正态分布的概率为 0.0368,mean=4,sd=10。我查看了Statistics::Distribution但它并没有提供这样做的功能。
dnorm(0, mean=4, sd=10) 不会为您提供此类点发生的概率。引用维基百科的概率密度函数
在概率论中,随机变量的概率密度函数 (pdf)(通常称为概率分布函数1 )或密度是描述样本空间中每个点的概率密度的函数。随机变量落入给定集合的概率由其密度在集合上的积分给出。
你提到的概率是
R> pnorm(0, 4, 10)
[1] 0.3446
或从 N(4, 10) 分布中获得等于或小于 0 的值的概率为 34.46%。
至于您的 Perl 问题:如果您知道如何在 R 中执行此操作,但需要从 Perl 中获取它,那么您可能需要基于 R 的 libRmath(在 Debian 中由软件包 r-mathlib 提供)编写一个 Perl 扩展来获取这些功能珀尔?这不需要 R 解释器。
否则,您可以尝试使用 GNU GSL 或 Cephes 库来访问这些特殊功能。
为什么不按照这些思路(我正在用 R 编写,但可以在 perl 中使用 Statistics::Distribution 完成):
dn <- function(x=0 # value
,mean=0 # mean
,sd=1 # sd
,sc=10000 ## scale the precision
) {
res <- (pnorm(x+1/sc, mean=mean, sd=sd)-pnorm(x, mean=mean, sd=sd))*sc
res
}
> dn(0,4,10,10000)
0.03682709
> dn(2.02,2,.24)
1.656498
[edit:1] 我应该提一下,这种近似在远尾会变得非常可怕。根据您的应用程序,它可能会或可能不会重要。
[edit:2] @foolishbrat 把代码变成了一个函数。结果应该总是积极的。也许您忘记了在 perl 模块中您提到函数返回上概率 1-F,而 R 返回 F?
[编辑:3] 修复了复制和粘贴错误。
如果你真的想要密度函数,为什么不直接使用它:
$pi = 3.141593;
$x = 2.02;
$mean = 2;
$sd = .24;
print 1/($sd * sqrt(2*$pi)) * exp(-($x-$mean)**2 / (2 * $sd**2));
它给出的 1.65649768474891 与 R 中的 dnorm 大致相同。
我不认为 Jouni 是完全正确的。这似乎给出了 PDF 的合理版本(如果您只想要一个特定的 xy 点,请提取循环的中间部分):
!/usr/bin/perl
use strict;
use Getopt::Std;
use POSIX qw(ceil floor);
# Usage
# Outputs normal density function given a mean and sd
# -s standard deviation
# -m mean
# -n normalization factor (multiply result by this amount), optional
my %para = ();
getopts('s:m:n:', \%para);
if (!exists ($para{'s'}) || !exists ($para{'m'})) {
die ("mean and standard deviation required");
}
my $norm = 1.0;
if (exists ($para{'n'})) {
$norm = $para{'n'};
}
my $sd = $para{'s'};
my $mean = $para{'m'};
my $start = floor($mean - ($sd * 5));
my $end = ceil($mean + ($sd * 5));
my $pi = 3.141593;
my $var = $sd**2;
for (my $x = $start; $x < $end; $x+=0.1) {
my $e = exp( -1 * (($x-$mean)**2) / (2*$var));
my $d = sqrt($var) * sqrt(2*$pi);
my $y = 1.0/$d*$e * $norm;
printf ("%5.5f %5.5f\n", $x, $y);
}
正如其他人指出的那样,您可能需要累积分布函数。这可以通过标准数学库中存在的误差函数(按均值移动并按正态分布的标准差缩放)获得,并且可以通过Math::Libm在 Perl 中访问。
使用 Perl 的 Statistics::Distributions,您可以通过以下方式实现:
#!/usr/bin/perl
use strict; use warnings;
use Statistics::Distributions qw(uprob);
my $x = 0;
my $mean = 4;
my $stdev = 10;
print "Height of probablility distribution at point $x = "
. (1-uprob(($x-$mean)/$stdev))."\n";
“点 0 的概率分布高度 = 0.34458”的结果
以下是如何使用CPAN中的Math::SymbolicX::Statistics::Distributions模块在 Perl 中使用 R 执行相同的操作:
use strict; use warnings;
use Math::SymbolicX::Statistics::Distributions qw/normal_distribution/;
my $norm = normal_distribution(qw/mean sd/);
print $norm->value(mean => 4, sd => 10, x => 0), "\n";
# curry it with the parameter values
$norm->implement(mean => 4, sd => 10);
print $norm->value(x => 0),"\n"; # prints the same as above
该模块中的 normal_distribution() 函数是函数的生成器。$norm 将是一个可以修改的Math::Symbolic (::Operator) 对象。例如implement,在上面的例子中,它用常量替换了两个参数变量。
但是请注意,正如 Dirk 指出的那样,您可能需要正态分布的累积函数。或者更一般地说,是某个范围内的积分。
不幸的是,Math::Symbolic 不能以符号方式进行积分。因此,您必须借助Math::Integral::Romberg之类的数值积分。(或者,在 CPAN 中搜索错误函数的实现。)这可能很慢,但仍然很容易做到。将此添加到上面的代码段:
use Math::Integral::Romberg 'integral';
my ($int_sub) = $norm->to_sub(); # compile to a faster Perl sub
print $int_sub->(0),"\n"; # same number as above
print "p=" . integral($int_sub, -100., 0) . "\n";
# -100 is an arbitrary, small number
这应该给你从德克的回答〜0.344578258389676。