如何用Scipy类正确定义多元对数正态分布PDF并实现采样?
正确实现多元对数正态分布采样的方法
你的核心问题在于:仅重定义_logpdf并不会改变原multivar_normal_gen的采样逻辑——它的_rvs方法依然生成多元正态分布的样本(包含负值),和你修改的PDF没有关联。要得到符合要求的正域样本,必须同时修改采样逻辑,或采用更直接的转换方式。
方法一:直接利用正态分布与对数正态的转换(推荐)
对数正态分布的本质是正态分布变量的指数变换,无需自定义类,直接用Scipy现有工具即可实现:
import numpy as np from scipy.stats import multivariate_normal # 定义对应潜在正态分布的参数 mu = np.array([1, 2]) # 正态分布的均值向量 cov = np.array([[1, 0.5], [0.5, 2]]) # 正态分布的协方差矩阵 # 1. 采样多元正态样本 normal_samples = multivariate_normal.rvs(mean=mu, cov=cov, size=1000, random_state=42) # 2. 指数转换为对数正态样本(确保所有值>0) lognormal_samples = np.exp(normal_samples) # 验证样本范围 print(np.all(lognormal_samples > 0)) # 输出 True
方法二:自定义MultivariateLognormal类(继承multivar_normal_gen)
如果一定要通过继承multivar_normal_gen实现,必须重定义_rvs方法(采样逻辑),同时配套重定义_pdf和_logpdf以保证分布一致性:
import numpy as np from scipy.stats import multivar_normal_gen class MultivariateLognormalGen(multivar_normal_gen): def _rvs(self, mean=None, cov=None, size=1, random_state=None): # 先采样潜在正态分布,再指数转换 normal_samples = super()._rvs(mean=mean, cov=cov, size=size, random_state=random_state) return np.exp(normal_samples) def _logpdf(self, x, mean=None, cov=None): # 对数正态的对数PDF公式:-sum(log(x)) + 潜在正态分布在log(x)处的logpdf if np.any(x <= 0, axis=-1): return -np.inf log_x = np.log(x) normal_logpdf = super()._logpdf(log_x, mean=mean, cov=cov) return normal_logpdf - np.sum(np.log(x), axis=-1) def _pdf(self, x, mean=None, cov=None): # 基于logpdf计算pdf return np.exp(self._logpdf(x, mean=mean, cov=cov)) # 使用自定义分布 mu = np.array([1, 2]) cov = np.array([[1, 0.5], [0.5, 2]]) mln_dist = MultivariateLognormalGen()(mean=mu, cov=cov) samples = mln_dist.rvs(size=1000, random_state=42) # 验证样本范围 print(np.all(samples > 0)) # 输出 True
关键说明
- Scipy的分布类中,
_rvs(采样)、_pdf(概率密度)、_logpdf(对数概率密度)是独立实现的,仅修改其中一个会导致逻辑不一致。 - 对数正态分布的参数对应的是潜在正态分布的均值和协方差,而非对数正态本身的均值/协方差,注意参数含义不要混淆。
内容的提问来源于stack exchange,提问作者Antonio Alvarez
相关产品推荐
相关产品推荐

