Sto modellando la dispersione delle piante usando una distribuzione normale generalizzata ( voce di Wikipedia ), che ha la funzione di densità di probabilità:
dove è la distanza percorsa, è un parametro di scala e è il parametro di forma. La distanza media percorsa è data dalla deviazione standard di questa distribuzione:
Ciò è utile perché consente una forma esponenziale quando , una forma gaussiana quando e una distribuzione leptokurtic quando . Questa distribuzione cresce regolarmente nella letteratura sulla dispersione delle piante, anche se è piuttosto rara in generale, e quindi è difficile trovare informazioni su.
La maggior parte dei parametri interessanti sono e la distanza media di dispersione.
Sto cercando di stimare e utilizzando MCMC, ma sto lottando per trovare un modo efficace per valori proposta dei campioni. Finora ho usato Metropolis-Hastings e tratto da distribuzioni uniformi e , e ottengo distanze di dispersione medie posteriori di circa 200-400 metri, il che ha un senso biologico. Tuttavia, la convergenza è molto lenta e non sono convinto che stia esplorando l'intero spazio dei parametri.
La sua difficile da venire con una migliore distribuzione proposta di e , perché dipendono l'uno dall'altro, senza avere molto senso per conto proprio. La distanza di dispersione media ha un chiaro significato biologico, ma una data distanza di dispersione media potrebbe essere spiegata da infinite combinazioni di e . Come tale e sono correlati in quella posteriore.
Finora ho usato Metropolis Hastings, ma sono aperto a qualsiasi altro algoritmo che funzionerebbe qui.
Domanda: qualcuno può suggerire un modo più efficiente per disegnare i valori della proposta per e ?
Modifica: Ulteriori informazioni sul sistema: sto studiando una popolazione di piante lungo una valle. L'obiettivo è determinare la distribuzione delle distanze percorse dal polline tra le piante donatrici e le piante che impollinano. I dati che ho sono:
- La posizione e il DNA di ogni possibile donatore di polline
- Semi raccolti da un campione di 60 piante materne (cioè ricevitori di polline) che sono state coltivate e genotipizzate.
- La posizione e il DNA di ogni pianta materna.
Non conosco l'identità delle piante donatrici, ma ciò può essere desunto dai dati genetici determinando quali donatori sono i padri di ogni piantina. Supponiamo che queste informazioni siano contenute in una matrice di probabilità G con una riga per ogni prole e una colonna per ciascun donatore candidato, che dà la probabilità che ciascun candidato sia il padre di ogni prole in base esclusivamente a dati genetici. G richiede circa 3 secondi per il calcolo e deve essere ricalcolato ad ogni iterazione, il che rallenta notevolmente le cose.
Dal momento che generalmente ci aspettiamo che i donatori candidati più vicini abbiano più probabilità di essere padri, l'inferenza della paternità è più accurata se si deducono congiuntamente paternità e dispersione. La matrice D ha le stesse dimensioni di G e contiene probabilità di paternità basate solo su una funzione delle distanze tra madre e candidato e alcuni vettori di parametri. Moltiplicare gli elementi in D e G fornisce la probabilità congiunta di paternità dati genetici e spaziali dati. Il prodotto di valori moltiplicati dà la probabilità del modello di dispersione.
Come descritto sopra, ho usato GND per modellare la dispersione. In realtà ho usato una miscela di un GND e una distribuzione uniforme per consentire la possibilità che candidati molto distanti avessero una maggiore probabilità di paternità a causa del solo caso (la genetica è disordinata) che gonfierebbe la coda apparente del GND se ignorata. Quindi la probabilità della distanza di dispersione è:
dove è la probabilità di distanza di dispersione dal GND, N è il numero di candidati e ( ) determina la quantità di contributo che il GND fornisce alla dispersione.
Vi sono quindi due ulteriori considerazioni che aumentano l'onere computazionale:
- La distanza di dispersione non è nota ma deve essere dedotta ad ogni iterazione e creare G per fare ciò è costoso.
- C'è un terzo parametro, , da integrare.
Per questi motivi mi è sembrato un po 'troppo complesso per eseguire l'interpolazione della griglia, ma sono felice di essere convinto altrimenti.
Esempio
Ecco un esempio semplificato del codice Python che ho usato. Ho semplificato la stima della paternità dai dati genetici, poiché ciò implicherebbe un sacco di codice extra e lo ha sostituito con una matrice di valori tra 0 e 1.
Innanzitutto, definire le funzioni per calcolare il 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
Successivamente simula 2000 candidati e 800 prole. Simula anche un elenco di distanze tra le madri della prole e i padri candidati e una matrice G fittizia .
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]
Imposta i valori dei parametri iniziali:
# 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
Aggiorna a, b e c a sua volta e calcola il rapporto 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