Estou modelando a dispersão da planta usando uma distribuição normal generalizada ( entrada da Wikipedia ), que tem a função de densidade de probabilidade:
onde é a distância percorrida, é um parâmetro de escala é o parâmetro de forma. A distância média percorrida é dada pelo desvio padrão desta distribuição:
Isso é conveniente porque permite uma forma exponencial quando , uma forma gaussiana quando e uma distribuição leptocúrtica quando . Essa distribuição surge regularmente na literatura de dispersão de plantas, embora seja bastante rara em geral e, portanto, difícil de encontrar informações.
Os parâmetros mais interessantes são distância média da dispersão.
Eu estou tentando estimar e usando MCMC, mas eu estou lutando para chegar a uma forma eficiente de valores proposta de amostra. Até agora, usei Metropolis-Hastings, e extraí de distribuições uniformes e , e obtenho distâncias médias de dispersão posteriores de cerca de 200 a 400 metros, o que faz sentido biológico. No entanto, a convergência é realmente lenta e não estou convencido de que esteja explorando todo o espaço de parâmetros.
Sua complicado para chegar a uma melhor distribuição proposta de e , porque eles dependem um do outro, sem ter muito significado por conta própria. A distância média de dispersão tem um significado biológico claro, mas uma determinada distância média de dispersão pode ser explicada por infinitas combinações de e . Como tal e são correlacionados no posterior.
Até agora, usei o Metropolis Hastings, mas estou aberto a qualquer outro algoritmo que funcione aqui.
Pergunta: Alguém pode sugerir uma maneira mais eficiente de desenhar valores da proposta para e ?
Edit: Informações adicionais sobre o sistema: Estou estudando uma população de plantas ao longo de um vale. O objetivo é determinar a distribuição das distâncias percorridas pelo pólen entre as plantas doadoras e as plantas que polinizam. Os dados que tenho são:
- A localização e o DNA de todos os possíveis doadores de pólen
- Sementes coletadas de uma amostra de 60 plantas maternas (ou seja, receptores de pólen) que foram cultivadas e genotipadas.
- A localização e o DNA de cada planta materna.
Não conheço a identidade das plantas doadoras, mas isso pode ser inferido a partir dos dados genéticos, determinando quais doadores são os pais de cada muda. Digamos que essa informação esteja contida em uma matriz de probabilidades G com uma linha para cada descendência e uma coluna para cada candidato a doador, que fornece a probabilidade de cada candidato ser o pai de cada descendente com base apenas em dados genéticos. G leva cerca de 3 segundos para calcular e precisa ser recalculado a cada iteração, o que torna as coisas consideravelmente mais lentas.
Como geralmente esperamos que os doadores candidatos mais próximos tenham maior probabilidade de serem pais, a inferência de paternidade é mais precisa se você deduzir em conjunto paternidade e dispersão. A matriz D tem as mesmas dimensões que G e contém probabilidades de paternidade baseadas apenas em função das distâncias entre mãe e candidato e algum vetor de parâmetros. A multiplicação de elementos em D e G fornece a probabilidade conjunta de paternidade, dados genéticos e espaciais. O produto de valores multiplicados fornece a probabilidade de modelo de dispersão.
Como descrito acima, tenho usado o GND para modelar a dispersão. Na verdade, usei uma mistura de um GND e uma distribuição uniforme para permitir a possibilidade de candidatos muito distantes terem uma maior probabilidade de paternidade devido apenas ao acaso (a genética é uma bagunça) que inflaria a cauda aparente do GND se ignorado. Portanto, a probabilidade da distância de dispersão é:
onde é a probabilidade de distância de dispersão do GND, N é o número de candidatos e ( ) determina quanta contribuição o GND faz para a dispersão.
Há, portanto, duas considerações adicionais que aumentam a carga computacional:
- A distância de dispersão não é conhecida, mas deve ser inferida a cada iteração, e criar G para fazer isso é caro.
- Há um terceiro parâmetro, , para integrar novamente.
Por esses motivos, pareceu-me ser um pouco complexo demais para executar a interpolação da grade, mas estou feliz por estar convencido do contrário.
Exemplo
Aqui está um exemplo simplificado do código python que eu usei. Simplifiquei a estimativa de paternidade a partir de dados genéticos, pois isso envolveria muito código extra e o substituí por uma matriz de valores entre 0 e 1.
Primeiro, defina funções para calcular o 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
Em seguida, simule 2000 candidatos e 800 filhos. Simule também uma lista de distâncias entre as mães dos filhos e os pais candidatos e uma matriz G fictícia .
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]
Defina os valores iniciais dos parâmetros:
# 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
Atualize a, bec, por sua vez, e calcule a proporção 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