私は、確率密度関数を持つ一般化された正規分布(wikipediaエントリー)を使用して、植物の分散をモデル化しています。
ここで、は移動距離、はスケールパラメーター、は形状パラメーターです。平均移動距離は、この分布の標準偏差によって与えられます。
これは、場合は指数関数型、場合はガウス型、場合はレプトリック分布を可能にするため便利です。この分布は、一般的には非常にまれであり、したがって情報を見つけるのが難しいにもかかわらず、植物散布に関する文献で定期的に発生します。
最も興味深いパラメータはと平均分散距離です。
私は推定しようとしていると MCMCを使用して、私はサンプルの提案値への効率的な方法を考え出すのに苦労しています。これまでのところ、私はMetropolis-Hastingsを使用し、および均一分布から描画しました。約200〜400メートルの後方平均分散距離が得られます。これは生物学的に意味があります。ただし、収束は非常に遅く、パラメーター空間全体を調査しているとは思いません。
とプロポーザル分布をより適切に作成するのは難しいことです。なぜなら、それらは、それ自体にはあまり意味がなく、互いに依存しているためです。平均分散距離は、明確な生物学的な意味を持っていますが、与えられた平均分散距離は無限に多くの組み合わせによって説明できると。このようにとの後方に相関しています。
これまでのところ、Metropolis Hastingsを使用しましたが、ここで機能する他のアルゴリズムを使用できます。
質問:と提案値を描画するより効率的な方法を誰かが提案できますか?
編集:システムに関する追加情報:谷沿いの植物の個体数を調査しています。目的は、ドナー植物とそれらが受粉する植物との間で花粉が移動する距離の分布を決定することです。私が持っているデータは:
- すべての可能な花粉ドナーの場所とDNA
- 成長し、遺伝子型が特定された60の母性植物(花粉レシーバー)のサンプルから収集された種子。
- 各母性植物の場所とDNA。
私はドナー植物の正体を知りませんが、これは遺伝学データから、どのドナーが各苗の父親であるかを決定することによって推測できます。この情報が、各子の行と各候補ドナーの列を持つ確率Gのマトリックスに含まれているとします。これにより、各候補が各子の父親である確率が遺伝データのみに基づいて得られます。Gは計算に約3秒かかり、反復ごとに再計算する必要があるため、処理速度が大幅に低下します。
私たちは通常、寄付者の候補者が近いほど父親である可能性が高いと予想しているため、父親と分散を一緒に推測すると、父親の推測はより正確になります。行列DはGと同じ次元を持ち、母と候補の間の距離の関数とパラメーターのいくつかのベクトルのみに基づく父子の確率を含みます。DとGの要素を乗算すると、遺伝的データと空間的データが与えられた場合の父親の同時確率が得られます。乗算された値の積は、分散モデルの可能性を与えます。
上記のように、私は分散をモデル化するためにGNDを使用しています。実際、私は実際にGNDと均一分布の混合を使用して、非常に遠い候補者が偶然のために父性の可能性が高い可能性を可能にしました(遺伝学は乱雑です)、無視するとGNDの見かけの尾部が膨らみます。したがって、分散距離確率は次のとおりです。
ここで、はGNDからの分散距離の確率、Nは候補の数、()はGNDが分散に与える貢献度を決定します。
したがって、計算負荷を増加させる2つの追加の考慮事項があります。
- 散布距離は不明ですが、反復ごとに推測する必要があります。これを行うためにGを作成するにはコストがかかります。
- 統合する3番目のパラメーターがあります。
これらの理由から、グリッド補間を実行するには少し複雑すぎるように思われましたが、それ以外の場合は確信しています。
例
以下は、私が使用した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