Modeluję rozproszenie roślin przy użyciu uogólnionego rozkładu normalnego ( wpis na Wikipedii ), który ma funkcję gęstości prawdopodobieństwa:
gdzie jest przebytą odległością, jest parametrem skali, a jest parametrem kształtu. Średnią przebytą odległość podaje standardowe odchylenie tego rozkładu:
Jest to wygodne, ponieważ pozwala na uzyskanie kształtu wykładniczego, gdy , kształtu Gaussa, gdy , i rozkładu lepeptycznego, gdy . Ta dystrybucja pojawia się regularnie w literaturze dotyczącej dyspersji roślin, chociaż ogólnie jest dość rzadka i dlatego trudno jest znaleźć informacje na jej temat.
Najbardziej interesującymi parametrami są i średnia odległość rozproszenia.
Próbuję oszacować i używając MCMC, ale walczę wymyślić skuteczny sposób, aby próbki wartości wniosku. Do tej pory korzystałem z Metropolis-Hastings i czerpałem z równomiernych rozkładów i , i otrzymuję średnie średnie odległości rozproszenia około 200-400 metrów, co ma sens biologiczny. Jednak konwergencja jest bardzo powolna i nie jestem przekonany, że bada pełną przestrzeń parametrów.
Jego trudne wymyślić lepszej dystrybucji Wniosek dotyczący i , ponieważ zależą one od siebie, bez większego znaczenia na własną rękę. Średnia odległość rozproszenia ma wyraźne znaczenie biologiczne, ale daną średnią odległość rozproszenia można wyjaśnić nieskończenie wieloma kombinacjami i . Jako taki i są skorelowane w tylnej.
Do tej pory korzystałem z Metropolis Hastings, ale jestem otwarty na każdy inny algorytm, który by tu działał.
Pytanie: Czy ktoś może zaproponować bardziej skuteczny sposób rysowania wartości propozycji dla i ?
Edycja: Dodatkowe informacje o systemie: Badam populację roślin wzdłuż doliny. Celem jest określenie rozkładu odległości między pyłkami między roślinami dawcami a roślinami, które zapylają. Dane, które posiadam to:
- Lokalizacja i DNA każdego możliwego dawcy pyłku
- Nasiona zebrane z próbki 60 roślin matczynych (tj. Biorców pyłku), które zostały wyhodowane i genotypowane.
- Lokalizacja i DNA każdej rośliny matczynej.
Nie znam tożsamości roślin dawcy, ale można to wywnioskować z danych genetycznych poprzez określenie, którzy dawcy są ojcami każdej sadzonki. Powiedzmy, że ta informacja jest zawarta w macierzy prawdopodobieństw G z wierszem dla każdego potomstwa i kolumną dla każdego kandydata na dawcę, co daje prawdopodobieństwo, że każdy kandydat będzie ojcem każdego potomstwa na podstawie wyłącznie danych genetycznych. Obliczenie G zajmuje około 3 sekund i musi być przeliczane przy każdej iteracji, co znacznie spowalnia.
Ponieważ ogólnie oczekujemy, że bliżsi kandydaci na dawców będą bardziej prawdopodobnie ojcami, wnioskowanie o ojcostwo jest dokładniejsze, jeśli wspólnie wnioskujesz o ojcostwie i rozproszeniu. Macierz D ma takie same wymiary jak G i zawiera prawdopodobieństwa ojcostwa oparte jedynie na funkcji odległości między matką a kandydatem i pewnym wektorze parametrów. Pomnożenie elementów w D i G daje wspólne prawdopodobieństwo ojcostwa na podstawie danych genetycznych i przestrzennych. Iloczyn pomnożonych wartości daje prawdopodobieństwo modelu rozproszenia.
Jak opisano powyżej, używam GND do modelowania rozproszenia. W rzeczywistości użyłem mieszanki GND i jednolitego rozkładu, aby umożliwić bardzo odległym kandydatom posiadanie większego prawdopodobieństwa ojcostwa z powodu samego przypadku (genetyka jest nieuporządkowana), który wzmógłby pozorny ogon GND, gdyby został zignorowany. Prawdopodobieństwo odległości rozproszenia wynosi:
gdzie jest prawdopodobieństwem odległości rozproszenia od GND, N jest liczbą kandydatów, a ( ) określa, jaki wkład GND ma w rozproszenie.
Istnieją zatem dwa dodatkowe czynniki, które zwiększają obciążenie obliczeniowe:
- Odległość rozproszenia nie jest znana, ale należy ją wywnioskować przy każdej iteracji, a utworzenie G w tym celu jest kosztowne.
- Istnieje trzeci parametr, , do zintegrowania.
Z tych powodów wydawało mi się, że jest to trochę zbyt skomplikowane, aby wykonać interpolację siatki, ale cieszę się, że jestem przekonany, że jest inaczej.
Przykład
Oto uproszczony przykład kodu python, którego użyłem. Uprościłem szacowanie ojcostwa na podstawie danych genetycznych, ponieważ wymagałoby to wielu dodatkowych kodów i zastąpiłem je macierzą wartości od 0 do 1.
Najpierw zdefiniuj funkcje do obliczania 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
Następnie symuluj 2000 kandydatów i 800 potomstwa. Symuluj również listę odległości między matkami potomstwa i ojcami kandydującymi oraz obojętną macierzą 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]
Ustaw początkowe wartości parametrów:
# 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
Zaktualizuj kolejno a, b i c, a następnie oblicz współczynnik 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