Python 使用scipy.stats库或其他方法生成数据遵循特定边界中的分布
我想使用scipy.stats库进行采样,使用采样数据的上下边界。我有兴趣使用scipy.stats.lognorm和scipy.stats.expon并将约束设置为低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
我最终可以编写两类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方法中添加限制。请参见编辑。