Я моделюю розповсюдження рослин за допомогою узагальненого нормального розподілу ( запис у Вікіпедії ), який має функцію щільності ймовірності:
де - пройдена відстань, - параметр масштабу, а - параметр форми. Середня пройдена відстань задається стандартним відхиленням цього розподілу:
Це зручно, оскільки воно дозволяє експоненціальну форму, коли , гауссова форма, коли , і лептокуртичний розподіл, коли . Цей розподіл регулярно з'являється в літературі про розповсюдження рослин, хоча він взагалі досить рідкісний, і тому важко знайти інформацію про нього.
Найцікавіші параметри - і середня відстань розсіювання.
Я намагаюся оцінити і за допомогою MCMC, але я намагаюся придумати ефективний спосіб вибірки значень пропозиції. Поки що я використовував Метрополіс-Гастінгс і виводив з рівномірних розподілів та , і я отримую середні відстані заднього розсіювання приблизно 200-400 метрів, що має біологічний сенс. Однак конвергенція дійсно повільна, і я не переконаний, що вона вивчає повний простір параметрів.
Складно придумати кращий розподіл пропозицій для і , оскільки вони залежать один від одного, не маючи особливого значення самостійно. Середня відстань розсіювання має чітке біологічне значення, але задана середня відстань розсіювання може бути пояснена нескінченно багатьма комбінаціями і . Оскільки такі і співвідносяться в задній частині.
Поки я використовував Метрополіс Гастінгс, але я відкритий для будь-якого іншого алгоритму, який би працював тут.
Питання: Чи може хтось запропонувати більш ефективний спосіб скласти значення пропозицій для і ?
Редагувати: Додаткова інформація про систему: Я вивчаю популяцію рослин вздовж долини. Метою є визначення розподілу відстаней, пройдених між пилком між рослинами-донорами та рослинами, які вони запилюють. У мене є такі дані:
- Місцезнаходження та ДНК кожного можливого донора пилку
- Насіння зібрано із зразка 60 материнських рослин (тобто приймачів пилку), які вирощували та генотипували.
- Місцезнаходження та ДНК для кожної материнської рослини.
Я не знаю ідентичності рослин-донорів, але це можна зробити з генетичних даних, визначивши, які донори є батьками кожного саджанця. Скажімо, ця інформація міститься у матриці ймовірностей G із рядком для кожного потомства та стовпчиком для кожного донора-кандидата, що дає ймовірність того, що кожен кандидат є батьком кожного потомства на основі лише генетичних даних. Г обчислення потрібно близько 3 секунд, і його потрібно перераховувати при кожній ітерації, що значно сповільнює роботу.
Оскільки ми, як правило, очікуємо, що більш близькі донори-кандидати стануть батьками, висновок про батьківство буде більш точним, якщо ви спільно підходите до батьківства та розселення. Матриця D має ті ж розміри , що і G , і містить ймовірності батьківства, засновані лише на функції відстані між матір'ю та кандидатом та деякого вектора параметрів. Помноження елементів на D і G дає спільну ймовірність батьківства за даними генетичних та просторових даних. Добуток помножених значень дає ймовірність моделі розгону.
Як описано вище, я використовував GND для моделювання розповсюдження. Насправді я фактично використовував суміш GND та рівномірного розподілу, щоб передбачити можливість дуже віддалених кандидатів, які мають більшу ймовірність батьківства через випадковість (генетика безладна), що може роздути очевидний хвіст GND, якщо його нехтувати. Отже, ймовірність відстані розсіювання дорівнює:
де - вірогідність відстані розсіювання від GND, N - кількість кандидатів, а ( ) визначає, який внесок внесе ВНД у розповсюдження.
Отже, є два додаткові міркування, які збільшують обчислювальне навантаження:
- Відстань розгону не відома, але її слід робити під час кожної ітерації та створення G для цього потрібно дорого.
- Існує третій параметр, , для інтеграції.
З цих причин мені здавалося, що вона є дещо настільки складною, щоб виконувати інтерполяцію сітки, але я радий переконатися в іншому.
Приклад
Ось спрощений приклад застосованого мною коду python. Я спростив оцінку батьківства за генетичними даними, оскільки це передбачало б багато зайвого коду, і замінив його матрицею значень між 0 і 1.
Спочатку визначте функції для обчислення ВНД:
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 і обчисліть коефіцієнт Metropolis.
# 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