Je modélise la dispersion des plantes en utilisant une distribution normale généralisée ( entrée wikipedia ), qui a la fonction de densité de probabilité:
où est la distance parcourue, est un paramètre d'échelle et est le paramètre de forme. La distance moyenne parcourue est donnée par l'écart type de cette distribution:
Ceci est pratique car il permet une forme exponentielle lorsque , une forme gaussienne lorsque et une distribution leptokurtique lorsque . Cette distribution revient régulièrement dans la littérature sur la dispersion des plantes, même si elle est assez rare en général, et donc difficile à trouver.
Les paramètres les plus intéressants sont et la distance de dispersion moyenne.
J'essaie d'estimer et aide de MCMC, mais j'ai du mal à trouver un moyen efficace d'échantillonner les valeurs de proposition. Jusqu'à présent, j'ai utilisé Metropolis-Hastings et tiré de distributions uniformes et , et j'obtiens des distances de dispersion moyennes postérieures d'environ 200 à 400 mètres, ce qui a un sens biologique. Cependant, la convergence est vraiment lente, et je ne suis pas convaincu qu'elle explore tout l'espace des paramètres.
Il est difficile de trouver une meilleure distribution de proposition pour et , car ils dépendent les uns des autres, sans avoir beaucoup de sens par eux-mêmes. La distance de dispersion moyenne a une signification biologique claire, mais une distance de dispersion moyenne donnée pourrait s'expliquer par une infinité de combinaisons de et . Ainsi, et sont corrélés dans la partie postérieure.
Jusqu'à présent, j'ai utilisé Metropolis Hastings, mais je suis ouvert à tout autre algorithme qui fonctionnerait ici.
Question: Quelqu'un peut-il suggérer un moyen plus efficace de dessiner des valeurs de proposition pour et ?
Edit: Informations supplémentaires sur le système: J'étudie une population de plantes le long d'une vallée. L'objectif est de déterminer la répartition des distances parcourues par le pollen entre les plantes donneuses et les plantes qu'elles pollinisent. Les données dont je dispose sont:
- L'emplacement et l'ADN de chaque donneur de pollen possible
- Graines recueillies auprès d'un échantillon de 60 plantes maternelles (c.-à-d. Récepteurs de pollen) qui ont été cultivées et génotypées.
- L'emplacement et l'ADN de chaque plante maternelle.
Je ne connais pas l'identité des plantes donneuses, mais cela peut être déduit des données génétiques en déterminant quels donneurs sont les pères de chaque semis. Disons que cette information est contenue dans une matrice de probabilités G avec une ligne pour chaque progéniture et une colonne pour chaque candidat donneur, qui donne la probabilité que chaque candidat soit le père de chaque progéniture sur la base de données génétiques uniquement. G prend environ 3 secondes à calculer et doit être recalculé à chaque itération, ce qui ralentit considérablement les choses.
Étant donné que nous nous attendons généralement à ce que les candidats donateurs plus proches soient plus susceptibles d'être des pères, l'inférence de paternité est plus précise si vous inférez conjointement la paternité et la dispersion. La matrice D a les mêmes dimensions que G et contient des probabilités de paternité basées uniquement sur une fonction des distances entre la mère et la candidate et un vecteur de paramètres. La multiplication des éléments en D et G donne la probabilité conjointe de paternité compte tenu des données génétiques et spatiales. Le produit de valeurs multipliées donne la probabilité d'un modèle de dispersion.
Comme décrit ci-dessus, j'ai utilisé le GND pour modéliser la dispersion. En fait, j'ai utilisé un mélange de GND et d'une distribution uniforme pour permettre à des candidats très éloignés d'avoir une probabilité de paternité plus élevée en raison du hasard seul (la génétique est en désordre), ce qui gonflerait la queue apparente du GND s'il était ignoré. La probabilité de distance de dispersion est donc:
où est la probabilité de distance de dispersion par rapport au GND, N est le nombre de candidats et ( ) détermine la contribution du GND à la dispersion.
Il y a donc deux considérations supplémentaires qui alourdissent la charge de calcul:
- La distance de dispersion n'est pas connue mais doit être déduite à chaque itération, et la création de G pour ce faire est coûteuse.
- Il y a un troisième paramètre, , à intégrer.
Pour ces raisons, il m'a semblé être un peu trop complexe pour effectuer une interpolation de grille, mais je suis heureux d'être convaincu du contraire.
Exemple
Voici un exemple simplifié du code python que j'ai utilisé. J'ai simplifié l'estimation de la paternité à partir des données génétiques, car cela impliquerait beaucoup de code supplémentaire, et je l'ai remplacé par une matrice de valeurs entre 0 et 1.
Tout d'abord, définissez les fonctions pour calculer le 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
Ensuite, simulez 2000 candidats et 800 descendants. Simulez également une liste de distances entre les mères de la progéniture et les pères candidats, ainsi qu'une matrice G factice .
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]
Définissez les valeurs initiales des paramètres:
# 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
Mettez à jour a, b et c tour à tour et calculez le ratio 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