Python 使用scipy.stats库或其他方法生成数据遵循特定边界中的分布

Python 使用scipy.stats库或其他方法生成数据遵循特定边界中的分布,python,numpy,scipy,Python,Numpy,Scipy,我想使用scipy.stats库进行采样,使用采样数据的上下边界。我有兴趣使用scipy.stats.lognorm和scipy.stats.expon并将约束设置为低 我最终可以编写两类prior,它们还可以根据给定的分布在给定的限制下对数据进行采样。我用这种方法对数据进行采样。我的课程如下: import os, sys import logging import scipy.stats from numpy import exp, sqrt, log, isfinite, inf, pi

我想使用scipy.stats库进行采样,使用采样数据的上下边界。我有兴趣使用scipy.stats.lognorm和scipy.stats.expon并将约束设置为低
我最终可以编写两类prior,它们还可以根据给定的分布在给定的限制下对数据进行采样。我用这种方法对数据进行采样。我的课程如下:

import os, sys
import logging
import scipy.stats
from numpy import exp, sqrt, log, isfinite, inf, pi
import scipy.special
import scipy.optimize
class LogPrior(object):
    def eval(self, value):
        return 0.
    def __call__(self, value):
        return self.eval(value)
    def sample(self, n=None):
        """ Sample from this prior. The returned array axis=0 is the
            sample axis.

            Parameters
            ----------
            n : int (optional)
                Number of samples to draw
        """
        raise ValueError("Cannot sample from a LogPrior object.")
    def __str__(self):
        return "<LogPrior>"
    def __repr__(self):
        return self.__str__()
class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
更新:对数正态分布类别:

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
指数分布类

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res

您的实现中存在几个问题

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
1,您的pdf无法在x=0时计算

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
2,-log1./sqrt2*pi/self.sigma*exp-0.5*logvalue-self.mu/self.sigma**2应该是:-log1./sqrt2*pi/self.sigma/value*exp-0.5*logvalue-self.mu/self.sigma**2

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
而且可能还有更多

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
另一个需要考虑的问题是,您可能希望保持参数化与scipy相同,以避免将来的混淆

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
因此,最低限度的实现:

class ExponentialPrior(LogPrior):
    """
    Exponential distribution

    Parameters
    ----------
    lam : float
        lam > 0
        rate or inverse scale
    """
    def __init__(self, lam, *args, **kwargs):
        super(ExponentialPrior, self).__init__(*args, **kwargs)
        self.lam = lam
        self.mean = 1. / lam
        self.median = self.mean * log(2)
        self.mode = 0
        self.variance = lam ** -2
    def logp(self, value, limits=None):
        if limits:
           lower,upper=limits
           """Log of lognormal prior probability with hard limits."""
           if value >= lower and value <= upper:
          return -log(self.lam)+self.lam*value
           else:
              return -inf
        else:
              """Log of normal prior probability."""
              return -log(self.lam)+self.lam*value
    def cdf(self, value):
        """Cumulative distribution function lognormal function""" 
        return (1-exp(-self.lam*value))
    #sampling data with the given distribution    
    def sample(self, n, limits=None):
        res=np.empty(n)
        if limits:
           lower,upper=limits 
           j=0
           while (j<n):
               def f(x):
           return self.cdf(x)-np.random.uniform(low=0,high=1,size=1)
           s=scipy.optimize.brenth(f,0,100)
           if s >= lower and s <= upper:
          res[j]=s
              j+=1
    else:
       r=np.random.uniform(low=0,high=1,size=n)
       for j in range(n):
               def f(x):
           return self.cdf(x)-r[j]
           s=scipy.optimize.brenth(f,0,100)
           res[j]=s
        return res
In [112]:
import scipy.stats as ss
import scipy.optimize as so
import numpy as np

class bounded_distr(object):
    def __init__(self, parent_dist):
        self.parent = parent_dist
    def bnd_lpdf(self, x, limits=None, *args, **kwargs):
        if limits and np.diff(limits)<=0:
            return -np.inf #nan may be better idea
        else:
            _v = -log(self.parent.pdf(x, *args, **kwargs))
            _v[x<=limits[0]] = -np.inf
            _v[x>=limits[1]] = -np.inf
            return _v
    def bnd_cdf(self, x, limits=None, *args, **kwargs):
        if limits and np.diff(limits)<=0:
            return 0 #nan may be better idea
        elif limits:
            _v1 = self.parent.cdf(x, *args, **kwargs)
            _v2 = self.parent.cdf(limits[0], *args, **kwargs)
            _v3 = self.parent.cdf(limits[1], *args, **kwargs)
            _v4 = (_v1-_v2)/(_v3-_v2)
            _v4[_v4<0] = np.nan
            _v4[_v4>1] = np.nan
            return _v4
        else:
            return self.parent.cdf(x, *args, **kwargs)
    def bnd_rvs(self, size, limits=None, *args, **kwargs):
        if limits and np.diff(limits)<=0:
            return np.repeat(np.nan, size) #nan may be better idea
        elif limits:
            low, high = limits
            rnd_cdf = np.random.uniform(self.parent.cdf(x=low, *args, **kwargs),
                                        self.parent.cdf(x=high, *args, **kwargs),
                                        size=size)
            return self.parent.ppf(q=rnd_cdf, *args, **kwargs)
        else:
            return self.parent.rvs(size=size, *args, **kwargs)
In [113]:

bnd_logn = bounded_distr(ss.lognorm)
In [114]:

bnd_logn.bnd_rvs(10, limits=(0.1, 0.9), s=1, loc=0)
Out[114]:
array([ 0.23167598,  0.43185726,  0.34763109,  0.71020467,  0.5216074 ,
        0.60883528,  0.34353607,  0.84530444,  0.64145739,  0.82082447])
In [115]:

bnd_logn.bnd_lpdf(np.linspace(0,1,10), limits=(0.1, 0.9), s=1, loc=0)
Out[115]:
array([        inf,  1.13561188,  0.54598554,  0.42380072,  0.43681222,
        0.50389845,  0.5956744 ,  0.69920358,  0.80809192,  0.91893853])
In [116]:

bnd_logn.bnd_cdf(np.linspace(0,1,10), limits=(0.1, 0.9), s=1, loc=0)
Out[116]:
array([        nan,  0.00749028,  0.12434152,  0.28010562,  0.44267888,
        0.59832448,  0.74188947,  0.87201574,  0.98899161,         nan])

我想在对数正态分布的特定边界上生成随机数。我知道我可以在特定的范围内绘制它。这些当然是有用的,但事实上它们提供了更多关于OP的信息,而不是回答它。您可能希望编辑OP并合并这些代码行。干杯@谢谢你指出我在回答中的几个错误。然而,我更新了我的答案。我发现很难用scipy.stats库设置采样数据的边界,这是我编写自己的类的动机。当我试图在给定的限制下估计logpdf时,它无法返回。例如,这个有界的分布.expon.bnd_lpdf5.6e12,limits=1e13,1e16,scale=1e15给出了34.544376394910685。它无法识别限制。抱歉,忘了在bnd_lpdf方法中添加限制。请参见编辑。