8
8
from .dist_math import bound , factln , binomln , betaln , logpow
9
9
from .distribution import Discrete , draw_values , generate_samples , reshape_sampled
10
10
from pymc3 .math import tround
11
+ from ..math import logaddexp
11
12
12
13
__all__ = ['Binomial' , 'BetaBinomial' , 'Bernoulli' , 'DiscreteWeibull' ,
13
14
'Poisson' , 'NegativeBinomial' , 'ConstantDist' , 'Constant' ,
14
- 'ZeroInflatedPoisson' , 'ZeroInflatedNegativeBinomial' ,
15
+ 'ZeroInflatedPoisson' , 'ZeroInflatedBinomial' , ' ZeroInflatedNegativeBinomial' ,
15
16
'DiscreteUniform' , 'Geometric' , 'Categorical' ]
16
17
17
18
@@ -593,7 +594,7 @@ class ZeroInflatedPoisson(Discrete):
593
594
594
595
.. math::
595
596
596
- f(x \mid \theta , \psi ) = \left\{ \begin{array}{l}
597
+ f(x \mid \psi , \theta ) = \left\{ \begin{array}{l}
597
598
(1-\psi) + \psi e^{-\theta}, \text{if } x = 0 \\
598
599
\psi \frac{e^{-\theta}\theta^x}{x!}, \text{if } x=1,2,3,\ldots
599
600
\end{array} \right.
@@ -606,15 +607,14 @@ class ZeroInflatedPoisson(Discrete):
606
607
607
608
Parameters
608
609
----------
610
+ psi : float
611
+ Expected proportion of Poisson variates (0 < psi < 1)
609
612
theta : float
610
613
Expected number of occurrences during the given interval
611
614
(theta >= 0).
612
- psi : float
613
- Expected proportion of Poisson variates (0 < psi < 1)
614
-
615
615
"""
616
616
617
- def __init__ (self , theta , psi , * args , ** kwargs ):
617
+ def __init__ (self , psi , theta , * args , ** kwargs ):
618
618
super (ZeroInflatedPoisson , self ).__init__ (* args , ** kwargs )
619
619
self .theta = theta = tt .as_tensor_variable (theta )
620
620
self .psi = psi = tt .as_tensor_variable (psi )
@@ -630,9 +630,17 @@ def random(self, point=None, size=None, repeat=None):
630
630
return reshape_sampled (sampled , size , self .shape )
631
631
632
632
def logp (self , value ):
633
- return tt .switch (value > 0 ,
634
- tt .log (self .psi ) + self .pois .logp (value ),
635
- tt .log ((1. - self .psi ) + self .psi * tt .exp (- self .theta )))
633
+ psi = self .psi
634
+ theta = self .theta
635
+
636
+ logp_val = tt .switch (value > 0 ,
637
+ tt .log (psi ) + self .pois .logp (value ),
638
+ logaddexp (tt .log1p (- psi ), tt .log (psi ) - theta ))
639
+
640
+ return bound (logp_val ,
641
+ 0 <= value ,
642
+ 0 <= psi , psi <= 1 ,
643
+ 0 <= theta )
636
644
637
645
def _repr_latex_ (self , name = None , dist = None ):
638
646
if dist is None :
@@ -644,6 +652,76 @@ def _repr_latex_(self, name=None, dist=None):
644
652
get_variable_name (psi ))
645
653
646
654
655
+ class ZeroInflatedBinomial (Discrete ):
656
+ R"""
657
+ Zero-inflated Binomial log-likelihood.
658
+
659
+ .. math::
660
+
661
+ f(x \mid \psi, n, p) = \left\{ \begin{array}{l}
662
+ (1-\psi) + \psi (1-p)^{n}, \text{if } x = 0 \\
663
+ \psi {n \choose x} p^x (1-p)^{n-x}, \text{if } x=1,2,3,\ldots,n
664
+ \end{array} \right.
665
+
666
+ ======== ==========================
667
+ Support :math:`x \in \mathbb{N}_0`
668
+ Mean :math:`(1 - \psi) n p`
669
+ Variance :math:`(1-\psi) n p [1 - p(1 - \psi n)].`
670
+ ======== ==========================
671
+
672
+ Parameters
673
+ ----------
674
+ psi : float
675
+ Expected proportion of Binomial variates (0 < psi < 1)
676
+ n : int
677
+ Number of Bernoulli trials (n >= 0).
678
+ p : float
679
+ Probability of success in each trial (0 < p < 1).
680
+
681
+ """
682
+
683
+ def __init__ (self , psi , n , p , * args , ** kwargs ):
684
+ super (ZeroInflatedBinomial , self ).__init__ (* args , ** kwargs )
685
+ self .n = n = tt .as_tensor_variable (n )
686
+ self .p = p = tt .as_tensor_variable (p )
687
+ self .psi = psi = tt .as_tensor_variable (psi )
688
+ self .bin = Binomial .dist (n , p )
689
+ self .mode = self .bin .mode
690
+
691
+ def random (self , point = None , size = None , repeat = None ):
692
+ n , p , psi = draw_values ([self .n , self .p , self .psi ], point = point )
693
+ g = generate_samples (stats .binom .rvs , n , p ,
694
+ dist_shape = self .shape ,
695
+ size = size )
696
+ sampled = g * (np .random .random (np .squeeze (g .shape )) < psi )
697
+ return reshape_sampled (sampled , size , self .shape )
698
+
699
+ def logp (self , value ):
700
+ psi = self .psi
701
+ p = self .p
702
+ n = self .n
703
+
704
+ logp_val = tt .switch (value > 0 ,
705
+ tt .log (psi ) + self .bin .logp (value ),
706
+ logaddexp (tt .log1p (- psi ), tt .log (psi ) + n * tt .log1p (- p )))
707
+
708
+ return bound (logp_val ,
709
+ 0 <= value , value <= n ,
710
+ 0 <= psi , psi <= 1 ,
711
+ 0 <= p , p <= 1 )
712
+
713
+ def _repr_latex_ (self , name = None , dist = None ):
714
+ if dist is None :
715
+ dist = self
716
+ n = dist .n
717
+ p = dist .p
718
+ psi = dist .psi
719
+ return r'${} \sim \text{{ZeroInflatedBinomial}}(\mathit{{n}}={}, \mathit{{p}}={}, \mathit{{psi}}={})$' .format (name ,
720
+ get_variable_name (n ),
721
+ get_variable_name (p ),
722
+ get_variable_name (psi ))
723
+
724
+
647
725
class ZeroInflatedNegativeBinomial (Discrete ):
648
726
R"""
649
727
Zero-Inflated Negative binomial log-likelihood.
@@ -654,7 +732,7 @@ class ZeroInflatedNegativeBinomial(Discrete):
654
732
655
733
.. math::
656
734
657
- f(x \mid \mu , \alpha , \psi ) = \left\{ \begin{array}{l}
735
+ f(x \mid \psi , \mu , \alpha ) = \left\{ \begin{array}{l}
658
736
(1-\psi) + \psi \left (\frac{\alpha}{\alpha+\mu} \right) ^\alpha, \text{if } x = 0 \\
659
737
\psi \frac{\Gamma(x+\alpha)}{x! \Gamma(\alpha)} \left (\frac{\alpha}{\mu+\alpha} \right)^\alpha \left( \frac{\mu}{\mu+\alpha} \right)^x, \text{if } x=1,2,3,\ldots
660
738
\end{array} \right.
@@ -667,15 +745,16 @@ class ZeroInflatedNegativeBinomial(Discrete):
667
745
668
746
Parameters
669
747
----------
748
+ psi : float
749
+ Expected proportion of NegativeBinomial variates (0 < psi < 1)
670
750
mu : float
671
751
Poission distribution parameter (mu > 0).
672
752
alpha : float
673
753
Gamma distribution parameter (alpha > 0).
674
- psi : float
675
- Expected proportion of NegativeBinomial variates (0 < psi < 1)
754
+
676
755
"""
677
756
678
- def __init__ (self , mu , alpha , psi , * args , ** kwargs ):
757
+ def __init__ (self , psi , mu , alpha , * args , ** kwargs ):
679
758
super (ZeroInflatedNegativeBinomial , self ).__init__ (* args , ** kwargs )
680
759
self .mu = mu = tt .as_tensor_variable (mu )
681
760
self .alpha = alpha = tt .as_tensor_variable (alpha )
@@ -694,9 +773,18 @@ def random(self, point=None, size=None, repeat=None):
694
773
return reshape_sampled (sampled , size , self .shape )
695
774
696
775
def logp (self , value ):
697
- return tt .switch (value > 0 ,
698
- tt .log (self .psi ) + self .nb .logp (value ),
699
- tt .log ((1. - self .psi ) + self .psi * (self .alpha / (self .alpha + self .mu ))** self .alpha ))
776
+ alpha = self .alpha
777
+ mu = self .mu
778
+ psi = self .psi
779
+
780
+ logp_val = tt .switch (value > 0 ,
781
+ tt .log (psi ) + self .nb .logp (value ),
782
+ logaddexp (tt .log1p (- psi ), tt .log (psi ) + alpha * (tt .log (alpha ) - tt .log (alpha + mu ))))
783
+
784
+ return bound (logp_val ,
785
+ 0 <= value ,
786
+ 0 <= psi , psi <= 1 ,
787
+ mu > 0 , alpha > 0 )
700
788
701
789
def _repr_latex_ (self , name = None , dist = None ):
702
790
if dist is None :
0 commit comments