我已经进行了一些搜索,但似乎无法找到一种合理的方法来从截断的正态分布中进行采样。
我没有被截断:
samples = [np.random.normal(loc=x,scale=d) for (x,d) in zip(X,D)]
X
和D
是浮点列表。
目前,我正在像这样实现截断:
def truncnorm(loc,scale,bounds):
s = np.random.normal(loc,scale)
if s > bounds[1]:
return bounds[1]
elif s < bounds[0]:
return bounds[0]
return s
samples = [truncnorm(loc=x,scale=d,bounds=b) for (x,d,b) in zip(X,D,bounds)]
bounds
是元组列表(min,max)
这种方法有点尴尬,所以我想知道是否有更好的方法?
答案 0 :(得分:1)
返回它们之外的样本的边界值,将导致过多的样本落在边界上。这并不代表实际的分布。边界上的值需要被拒绝,并用新的样本替换。这样的代码可能是:
def test_truncnorm(loc, scale, bounds):
while True:
s = np.random.normal(loc, scale)
if bounds[0] <= s <= bounds[1]:
break
return s
在狭窄范围内,这可能会非常慢。 Scipy的truncnorm更有效地处理了这种情况。出乎意料的是,边界以标准法线的功能表示,因此您的调用将是:
s = scipy.stats.truncnorm.rvs((bounds[0]-loc)/scale, (bounds[1]-loc)/scale, loc=loc, scale=scale)
请注意,使用numpy的vectorization and broadcasting时scipy的工作速度更快。一旦您习惯了这种记号,它的书写和阅读也将变得更加简单。所有样本可以一口气计算出来:
X = np.array(X)
D = np.array(D)
bounds = np.array(bounds)
samples = scipy.stats.truncnorm.rvs((bounds[:, 0] - X) / D, (bounds[:, 1] - X) / D, loc=X, scale=D)