我正在使用具有概率密度函数的广义正态分布(维基百科条目)来模拟植物扩散。
其中是行进距离,是比例参数,是形状参数。平均行驶距离由以下分布的标准偏差给出:
这是方便的,因为它允许以指数形式时,高斯形状时,并且对于尖峰厚尾分布时。这种分布在植物传播文献中经常出现,尽管通常它很少见,因此很难找到有关的信息。
最有趣的参数是和平均分散距离。
我正在尝试使用MCMC 估算和,但是我正在努力想出一种有效的方法来对提案价值进行抽样。到目前为止,我已经使用Metropolis-Hastings,并且从和均匀分布中得出,并且我得到的后平均分散距离约为200-400米,这确实具有生物学意义。但是,收敛确实很慢,并且我不确信它正在探索整个参数空间。
为和分配更好的提案分配是棘手的,因为它们彼此依赖,而没有太多意义。平均分散距离确实具有明确的生物学意义,但是给定的平均分散距离可以用和许多组合来解释。因此,和在后面相关。
到目前为止,我已经使用了Metropolis Hastings,但是我对在这里可以使用的其他算法持开放态度。
问题:有人可以建议一种更有效的方法来绘制和投标值吗?
编辑:关于系统的其他信息:我正在研究沿山谷的植物种群。目的是确定花粉在供体植物和它们授粉的植物之间传播的距离分布。我的数据是:
- 每个可能的花粉供体的位置和DNA
- 从已经生长并进行基因分型的60种母本植物(即花粉接受者)的样品中收集的种子。
- 每个母本植物的位置和DNA。
我不知道供体植物的身份,但是可以通过确定哪些供体是每株幼苗的父亲,从遗传数据中推论得出。假设此信息包含在概率矩阵G中,每个后代都有一行,每个候选供体都有一列,这仅根据遗传数据就可以得出每个候选者是每个后代的父亲的概率。G需要大约3秒钟的时间来计算,并且每次迭代都需要重新计算,这大大降低了速度。
由于我们通常期望更接近的候选人捐赠者更有可能是父亲,因此,如果您共同推断父子关系和分散父子关系,则父子关系推断会更准确。矩阵D具有与G相同的维度,并且仅基于母体与候选者之间的距离和某些参数向量的函数包含父系概率。在给定遗传和空间数据的情况下,D和G中的相乘元素给出了父权的联合概率。乘积值的乘积给出了离散模型的可能性。
如上所述,我一直在使用GND来建模色散。实际上,我实际上使用了GND和均匀分布的混合体,以允许非常遥远的候选者仅由于偶然性(遗传杂乱)而具有较高的亲子关系可能性(如果忽略,这会使GND的明显尾部膨胀)。因此,扩散距离的概率为:
其中是到GND的散布距离的概率,N是候选者的数量,而()确定GND对散布的贡献。
因此,还有两个额外的考虑因素会增加计算负担:
- 色散距离是未知的,但必须在每次迭代中进行推断,而创建G来做到这一点非常昂贵。
- 有第三个参数进行积分。
由于这些原因,在我看来,执行网格插值过于复杂,但我很高兴被说服。
例
这是我使用的python代码的简化示例。我简化了从遗传数据中对亲子关系的估计,因为这将涉及很多额外的代码,并将其替换为0到1之间的值矩阵。
首先,定义函数以计算GND:
import numpy as np
from scipy.special import gamma
def generalised_normal_PDF(x, a, b, gamma_b=None):
"""
Calculate the PDF of the generalised normal distribution.
Parameters
----------
x: vector
Vector of deviates from the mean.
a: float
Scale parameter.
b: float
Shape parameter
gamma_b: float, optional
To speed up calculations, values for Euler's gamma for 1/b
can be calculated ahead of time and included as a vector.
"""
xv = np.copy(x)
if gamma_b:
return (b/(2 * a * gamma_b )) * np.exp(-(xv/a)**b)
else:
return (b/(2 * a * gamma(1.0/b) )) * np.exp(-(xv/a)**b)
def dispersal_GND(x, a, b, c):
"""
Calculate a probability that each candidate is a sire
assuming assuming he is either drawn at random form the
population, or from a generalised normal function of his
distance from each mother. The relative contribution of the
two distributions is controlled by mixture parameter c.
Parameters
----------
x: vector
Vector of deviates from the mean.
a: float
Scale parameter.
b: float
Shape parameter
c: float between 0 and 1.
The proportion of probability mass assigned to the
generalised normal function.
"""
prob_GND = generalised_normal_PDF(x, a, b)
prob_GND = prob_GND / prob_GND.sum(axis=1)[:, np.newaxis]
prob_drawn = (prob_GND * c) + ((1-c) / x.shape[1])
prob_drawn = np.log(prob_drawn)
return prob_drawn
接下来模拟2000个候选对象和800个后代。还模拟后代母亲与候选父亲之间的距离列表以及虚拟G矩阵。
n_candidates = 2000 # Number of candidates in the population
n_offspring = 800 # Number of offspring sampled.
# Create (log) matrix G.
# These are just random values between 0 and 1 as an example, but must be inferred in reality.
g_matrix = np.random.uniform(0,1, size=n_candidates*n_offspring)
g_matrix = g_matrix.reshape([n_offspring, n_candidates])
g_matrix = np.log(g_matrix)
# simulate distances to ecah candidate father
distances = np.random.uniform(0,1000, 2000)[np.newaxis]
设置初始参数值:
# number of iterations to run
niter= 100
# set intitial values for a, b, and c.
a_current = np.random.uniform(0.001,500, 1)
b_current = np.random.uniform(0.01, 3, 1)
c_current = np.random.uniform(0.001, 1, 1)
# set initial likelihood to a very small number
lik_current = -10e12
依次更新a,b和c,然后计算都市比率。
# number of iterations to run
niter= 100
# set intitial values for a, b, and c.
# When values are very small, this can cause the Gamma function to break, so the limit is set to >0.
a_current = np.random.uniform(0.001,500, 1)
b_current = np.random.uniform(0.01, 3, 1)
c_current = np.random.uniform(0.001, 1, 1)
# set initial likelihood to a very small number
lik_current = -10e12
# empty array to store parameters
store_params = np.zeros([niter, 3])
for i in range(niter):
a_proposed = np.random.uniform(0.001,500, 1)
b_proposed = np.random.uniform(0.01,3, 1)
c_proposed = np.random.uniform(0.001,1, 1)
# Update likelihood with new value for a
prob_dispersal = dispersal_GND(distances, a=a_proposed, b=b_current, c=c_current)
lik_proposed = (g_matrix + prob_dispersal).sum() # lg likelihood of the proposed value
# Metropolis acceptance ration for a
accept = bool(np.random.binomial(1, np.min([1, np.exp(lik_proposed - lik_current)])))
if accept:
a_current = a_proposed
lik_current = lik_proposed
store_params[i,0] = a_current
# Update likelihood with new value for b
prob_dispersal = dispersal_GND(distances, a=a_current, b=b_proposed, c=c_current)
lik_proposed = (g_matrix + prob_dispersal).sum() # log likelihood of the proposed value
# Metropolis acceptance ratio for b
accept = bool(np.random.binomial(1, np.min([1, np.exp(lik_proposed - lik_current)])))
if accept:
b_current = b_proposed
lik_current = lik_proposed
store_params[i,1] = b_current
# Update likelihood with new value for c
prob_dispersal = dispersal_GND(distances, a=a_current, b=b_current, c=c_proposed)
lik_proposed = (g_matrix + prob_dispersal).sum() # lg likelihood of the proposed value
# Metropolis acceptance ratio for c
accept = bool(np.random.binomial(1, np.min([1, np.exp(lik_proposed - lik_current)])))
if accept:
c_current = c_proposed
lik_current = lik_proposed
store_params[i,2] = c_current