构造具体的分布
下一个例子展示了如何建立你自己的分布。更多的例子见分布用法以及统计检验
创建一个连续分布,继承rv_continuous
类
创建连续分布是非常简单的.
>>> from scipy import stats
>>> class deterministic_gen(stats.rv_continuous):
... def _cdf(self, x):
... return np.where(x < 0, 0., 1.)
... def _stats(self):
... return 0., 0., 0., 0.
>>> deterministic = deterministic_gen(name="deterministic")
>>> deterministic.cdf(np.arange(-3, 3, 0.5))
array([ 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1.])
令人高兴的是,pdf也能被自动计算出来:
>>>
>>> deterministic.pdf(np.arange(-3, 3, 0.5))
array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
5.83333333e+04, 4.16333634e-12, 4.16333634e-12,
4.16333634e-12, 4.16333634e-12, 4.16333634e-12])
注意这种用法的性能问题,参见“性能问题与注意事项”一节。这种缺乏信息的通用计算可能非常慢。
而且再看看下面这个准确性的例子:
>>> from scipy.integrate import quad
>>> quad(deterministic.pdf, -1e-1, 1e-1)
(4.163336342344337e-13, 0.0)
但这并不是对pdf积分的正确的结果,实际上结果应为1.让我们将积分变得更小一些。
>>> quad(deterministic.pdf, -1e-3, 1e-3) # warning removed
(1.000076872229173, 0.0010625571718182458)
这样看上去好多了,然而,问题本身来源于pdf不是来自包给定的类的定义。
继承rv_discrete
类
在之后我们使用stats.rv_discrete产生一个离散分布,其有一个整数区间截断概率。
通用信息
通用信息可以从 rv_discrete的 docstring中得到。
>>> from scipy.stats import rv_discrete
>>> help(rv_discrete)
从中我们得知:
“你可以构建任意一个像P(X=xk)=pk一样形式的离散rv,通过传递(xk,pk)元组序列给
rv_discrete初始化方法(通过values=keyword方式),但其不能有0概率值。”
接下来,还有一些进一步的要求:
- keyword必须给出。
- Xk必须是整数
- 小数的有效位数应当被给出。
事实上,如果最后两个要求没有被满足,一个异常将被抛出或者导致一个错误的数值。
一个例子
让我们开始办,首先
>>> npoints = 20 # number of integer support points of the distribution minus 1
>>> npointsh = npoints / 2
>>> npointsf = float(npoints)
>>> nbound = 4 # bounds for the truncated normal
>>> normbound = (1+1/npointsf) * nbound # actual bounds of truncated normal
>>> grid = np.arange(-npointsh, npointsh+2, 1) # integer grid
>>> gridlimitsnorm = (grid-0.5) / npointsh * nbound # bin limits for the truncnorm
>>> gridlimits = grid - 0.5 # used later in the analysis
>>> grid = grid[:-1]
>>> probs = np.diff(stats.truncnorm.cdf(gridlimitsnorm, -normbound, normbound))
>>> gridint = grid
然后我们可以继承rv_discrete类
>>> normdiscrete = stats.rv_discrete(values=(gridint,
... np.round(probs, decimals=7)), name='normdiscrete')
现在我们已经定义了这个分布,我们可以调用其所有常规的离散分布方法。
>>> print 'mean = %6.4f, variance = %6.4f, skew = %6.4f, kurtosis = %6.4f'% \
... normdiscrete.stats(moments = 'mvsk')
mean = -0.0000, variance = 6.3302, skew = 0.0000, kurtosis = -0.0076
>>> nd_std = np.sqrt(normdiscrete.stats(moments='v'))
测试上面的结果
让我们产生一个随机样本并且比较连续概率的情况。
>>> n_sample = 500
>>> np.random.seed(87655678) # fix the seed for replicability
>>> rvs = normdiscrete.rvs(size=n_sample)
>>> rvsnd = rvs
>>> f, l = np.histogram(rvs, bins=gridlimits)
>>> sfreq = np.vstack([gridint, f, probs*n_sample]).T
>>> print sfreq
[[ -1.00000000e+01 0.00000000e+00 2.95019349e-02]
[ -9.00000000e+00 0.00000000e+00 1.32294142e-01]
[ -8.00000000e+00 0.00000000e+00 5.06497902e-01]
[ -7.00000000e+00 2.00000000e+00 1.65568919e+00]
[ -6.00000000e+00 1.00000000e+00 4.62125309e+00]
[ -5.00000000e+00 9.00000000e+00 1.10137298e+01]
[ -4.00000000e+00 2.60000000e+01 2.24137683e+01]
[ -3.00000000e+00 3.70000000e+01 3.89503370e+01]
[ -2.00000000e+00 5.10000000e+01 5.78004747e+01]
[ -1.00000000e+00 7.10000000e+01 7.32455414e+01]
[ 0.00000000e+00 7.40000000e+01 7.92618251e+01]
[ 1.00000000e+00 8.90000000e+01 7.32455414e+01]
[ 2.00000000e+00 5.50000000e+01 5.78004747e+01]
[ 3.00000000e+00 5.00000000e+01 3.89503370e+01]
[ 4.00000000e+00 1.70000000e+01 2.24137683e+01]
[ 5.00000000e+00 1.10000000e+01 1.10137298e+01]
[ 6.00000000e+00 4.00000000e+00 4.62125309e+00]
[ 7.00000000e+00 3.00000000e+00 1.65568919e+00]
[ 8.00000000e+00 0.00000000e+00 5.06497902e-01]
[ 9.00000000e+00 0.00000000e+00 1.32294142e-01]
[ 1.00000000e+01 0.00000000e+00 2.95019349e-02]]
接下来,我们可以测试,是否我们的样本取自于一个normdiscrete分布。这也是在验证
是否随机数是以正确的方式产生的。
卡方测试要求起码在每个子区间(bin)里具有最小数目的观测值。我们组合末端子区间进大子区间
所以它们现在包含了足够数量的观测值。
>>> f2 = np.hstack([f[:5].sum(), f[5:-5], f[-5:].sum()])
>>> p2 = np.hstack([probs[:5].sum(), probs[5:-5], probs[-5:].sum()])
>>> ch2, pval = stats.chisquare(f2, p2*n_sample)
>>> print 'chisquare for normdiscrete: chi2 = %6.3f pvalue = %6.4f' % (ch2, pval)
chisquare for normdiscrete: chi2 = 12.466 pvalue = 0.4090
P值在这个情况下是不显著地,所以我们可以断言我们的随机样本的确是由此分布产生的。