构造具体的分布

下一个例子展示了如何建立你自己的分布。更多的例子见分布用法以及统计检验

创建一个连续分布,继承rv_continuous

创建连续分布是非常简单的.

  1. >>> from scipy import stats
  2. >>> class deterministic_gen(stats.rv_continuous):
  3. ... def _cdf(self, x):
  4. ... return np.where(x < 0, 0., 1.)
  5. ... def _stats(self):
  6. ... return 0., 0., 0., 0.
  7. >>> deterministic = deterministic_gen(name="deterministic")
  8. >>> deterministic.cdf(np.arange(-3, 3, 0.5))
  9. array([ 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1.])

令人高兴的是,pdf也能被自动计算出来:

  1. >>>
  2. >>> deterministic.pdf(np.arange(-3, 3, 0.5))
  3. array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
  4. 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
  5. 5.83333333e+04, 4.16333634e-12, 4.16333634e-12,
  6. 4.16333634e-12, 4.16333634e-12, 4.16333634e-12])

注意这种用法的性能问题,参见“性能问题与注意事项”一节。这种缺乏信息的通用计算可能非常慢。
而且再看看下面这个准确性的例子:

  1. >>> from scipy.integrate import quad
  2. >>> quad(deterministic.pdf, -1e-1, 1e-1)
  3. (4.163336342344337e-13, 0.0)

但这并不是对pdf积分的正确的结果,实际上结果应为1.让我们将积分变得更小一些。

  1. >>> quad(deterministic.pdf, -1e-3, 1e-3) # warning removed
  2. (1.000076872229173, 0.0010625571718182458)

这样看上去好多了,然而,问题本身来源于pdf不是来自包给定的类的定义。

继承rv_discrete

在之后我们使用stats.rv_discrete产生一个离散分布,其有一个整数区间截断概率。

通用信息

通用信息可以从 rv_discrete的 docstring中得到。

  1. >>> from scipy.stats import rv_discrete
  2. >>> help(rv_discrete)

从中我们得知:

“你可以构建任意一个像P(X=xk)=pk一样形式的离散rv,通过传递(xk,pk)元组序列给
rv_discrete初始化方法(通过values=keyword方式),但其不能有0概率值。”

接下来,还有一些进一步的要求:

  • keyword必须给出。
  • Xk必须是整数
  • 小数的有效位数应当被给出。

事实上,如果最后两个要求没有被满足,一个异常将被抛出或者导致一个错误的数值。

一个例子

让我们开始办,首先

  1. >>> npoints = 20 # number of integer support points of the distribution minus 1
  2. >>> npointsh = npoints / 2
  3. >>> npointsf = float(npoints)
  4. >>> nbound = 4 # bounds for the truncated normal
  5. >>> normbound = (1+1/npointsf) * nbound # actual bounds of truncated normal
  6. >>> grid = np.arange(-npointsh, npointsh+2, 1) # integer grid
  7. >>> gridlimitsnorm = (grid-0.5) / npointsh * nbound # bin limits for the truncnorm
  8. >>> gridlimits = grid - 0.5 # used later in the analysis
  9. >>> grid = grid[:-1]
  10. >>> probs = np.diff(stats.truncnorm.cdf(gridlimitsnorm, -normbound, normbound))
  11. >>> gridint = grid

然后我们可以继承rv_discrete类

  1. >>> normdiscrete = stats.rv_discrete(values=(gridint,
  2. ... np.round(probs, decimals=7)), name='normdiscrete')

现在我们已经定义了这个分布,我们可以调用其所有常规的离散分布方法。

  1. >>> print 'mean = %6.4f, variance = %6.4f, skew = %6.4f, kurtosis = %6.4f'% \
  2. ... normdiscrete.stats(moments = 'mvsk')
  3. mean = -0.0000, variance = 6.3302, skew = 0.0000, kurtosis = -0.0076
  4. >>> nd_std = np.sqrt(normdiscrete.stats(moments='v'))

测试上面的结果

让我们产生一个随机样本并且比较连续概率的情况。

  1. >>> n_sample = 500
  2. >>> np.random.seed(87655678) # fix the seed for replicability
  3. >>> rvs = normdiscrete.rvs(size=n_sample)
  4. >>> rvsnd = rvs
  5. >>> f, l = np.histogram(rvs, bins=gridlimits)
  6. >>> sfreq = np.vstack([gridint, f, probs*n_sample]).T
  7. >>> print sfreq
  8. [[ -1.00000000e+01 0.00000000e+00 2.95019349e-02]
  9. [ -9.00000000e+00 0.00000000e+00 1.32294142e-01]
  10. [ -8.00000000e+00 0.00000000e+00 5.06497902e-01]
  11. [ -7.00000000e+00 2.00000000e+00 1.65568919e+00]
  12. [ -6.00000000e+00 1.00000000e+00 4.62125309e+00]
  13. [ -5.00000000e+00 9.00000000e+00 1.10137298e+01]
  14. [ -4.00000000e+00 2.60000000e+01 2.24137683e+01]
  15. [ -3.00000000e+00 3.70000000e+01 3.89503370e+01]
  16. [ -2.00000000e+00 5.10000000e+01 5.78004747e+01]
  17. [ -1.00000000e+00 7.10000000e+01 7.32455414e+01]
  18. [ 0.00000000e+00 7.40000000e+01 7.92618251e+01]
  19. [ 1.00000000e+00 8.90000000e+01 7.32455414e+01]
  20. [ 2.00000000e+00 5.50000000e+01 5.78004747e+01]
  21. [ 3.00000000e+00 5.00000000e+01 3.89503370e+01]
  22. [ 4.00000000e+00 1.70000000e+01 2.24137683e+01]
  23. [ 5.00000000e+00 1.10000000e+01 1.10137298e+01]
  24. [ 6.00000000e+00 4.00000000e+00 4.62125309e+00]
  25. [ 7.00000000e+00 3.00000000e+00 1.65568919e+00]
  26. [ 8.00000000e+00 0.00000000e+00 5.06497902e-01]
  27. [ 9.00000000e+00 0.00000000e+00 1.32294142e-01]
  28. [ 1.00000000e+01 0.00000000e+00 2.95019349e-02]]

构造具体的分布 - 图1
构造具体的分布 - 图2

接下来,我们可以测试,是否我们的样本取自于一个normdiscrete分布。这也是在验证
是否随机数是以正确的方式产生的。

卡方测试要求起码在每个子区间(bin)里具有最小数目的观测值。我们组合末端子区间进大子区间
所以它们现在包含了足够数量的观测值。

  1. >>> f2 = np.hstack([f[:5].sum(), f[5:-5], f[-5:].sum()])
  2. >>> p2 = np.hstack([probs[:5].sum(), probs[5:-5], probs[-5:].sum()])
  3. >>> ch2, pval = stats.chisquare(f2, p2*n_sample)
  1. >>> print 'chisquare for normdiscrete: chi2 = %6.3f pvalue = %6.4f' % (ch2, pval)
  2. chisquare for normdiscrete: chi2 = 12.466 pvalue = 0.4090

P值在这个情况下是不显著地,所以我们可以断言我们的随机样本的确是由此分布产生的。