Simulating bouts¶
This follows the simulation of mixed Poisson distributions in Luque & Guinet (2007), and the comparison of models for characterizing such distributions.
Set up the environment.
# Set up
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import skdiveMove.bouts as skbouts
# For figure sizes
_FIG3X1 = (9, 12)
pd.set_option("display.precision", 3)
np.set_printoptions(precision=3, sign="+")
%matplotlib inline
Generate two-process mixture¶
For a mixed distribution of two random Poisson processes with a mixing parameter \(p=0.7\), and density parameters \(\lambda_f=0.05\), and \(\lambda_s=0.005\), we can set up the following function to generate samples:
def genx(n, p, lda0, lda1):
chooser = np.random.uniform(size=n)
# We need to convert from scale to rate parameter
proc1 = np.random.exponential(1 / lda0, size=n)
proc2 = np.random.exponential(1 / lda1, size=n)
proc_mix = np.where(chooser < p, proc1, proc2)
return(proc_mix)
Define the true values described above, grouping the parameters into a Series to simplify further operations.
p_true = 0.7
lda0_true = 0.05
lda1_true = 0.005
pars_true = pd.Series({"lambda0": lda0_true,
"lambda1": lda1_true,
"p": p_true})
Declare the number of simulations and the number of samples to generate:
# Number of simulations
nsims = 500
# Size of each sample
nsamp = 1000
Set up variables to accumulate simulations:
# Set up NLS simulations
coefs_nls = []
# Set up MLE simulations
coefs_mle = []
# Fixed bounds fit 1
p_bnd = (-2, None)
lda0_bnd = (-5, None)
lda1_bnd = (-10, None)
opts1 = dict(method="L-BFGS-B",
bounds=(p_bnd, lda0_bnd, lda1_bnd))
# Fixed bounds fit 2
p_bnd = (1e-1, None)
lda0_bnd = (1e-3, None)
lda1_bnd = (1e-6, None)
opts2 = dict(method="L-BFGS-B",
bounds=(p_bnd, lda0_bnd, lda1_bnd))
Perform the simulations in a loop, fitting the nonlinear least squares (NLS) model, and the alternative maximum likelihood (MLE) model at each iteration.
# Estimate parameters `nsims` times
for i in range(nsims):
x = genx(nsamp, pars_true["p"], pars_true["lambda0"],
pars_true["lambda1"])
# NLS
xbouts = skbouts.BoutsNLS(x, 5)
init_pars = xbouts.init_pars([80], plot=False)
coefs, _ = xbouts.fit(init_pars)
p_i = skbouts.bouts.calc_p(coefs)[0][0] # only one here
coefs_i = coefs.loc["lambda"].append(pd.Series({"p": p_i}))
coefs_nls.append(coefs_i.to_numpy())
# MLE
xbouts = skbouts.BoutsMLE(x, 5)
init_pars = xbouts.init_pars([80], plot=False)
fit1, fit2 = xbouts.fit(init_pars, fit1_opts=opts1,
fit2_opts=opts2)
coefs_mle.append(np.roll(fit2.x, -1))
Non-linear least squares (NLS)¶
Collect and display NLS results from the simulations:
nls_coefs = pd.DataFrame(np.row_stack(coefs_nls),
columns=["lambda0", "lambda1", "p"])
# Centrality and variance
nls_coefs.describe()
lambda0 | lambda1 | p | |
---|---|---|---|
count | 500.000 | 5.000e+02 | 500.000 |
mean | 0.047 | 4.011e-03 | 0.728 |
std | 0.005 | 4.296e-04 | 0.021 |
min | 0.028 | 2.777e-03 | 0.658 |
25% | 0.044 | 3.739e-03 | 0.714 |
50% | 0.047 | 4.012e-03 | 0.727 |
75% | 0.050 | 4.285e-03 | 0.744 |
max | 0.066 | 5.485e-03 | 0.791 |
Maximum likelihood estimation (MLE)¶
Collect and display MLE results from the simulations:
mle_coefs = pd.DataFrame(np.row_stack(coefs_mle),
columns=["lambda0", "lambda1", "p"])
# Centrality and variance
mle_coefs.describe()
lambda0 | lambda1 | p | |
---|---|---|---|
count | 500.000 | 5.000e+02 | 500.000 |
mean | 0.050 | 5.029e-03 | 0.698 |
std | 0.003 | 3.839e-04 | 0.024 |
min | 0.043 | 3.958e-03 | 0.622 |
25% | 0.048 | 4.772e-03 | 0.682 |
50% | 0.050 | 5.017e-03 | 0.697 |
75% | 0.052 | 5.253e-03 | 0.714 |
max | 0.065 | 6.210e-03 | 0.777 |
Comparing NLS vs MLE¶
The bias relative to the true values of the mixed distribution can be readily assessed for NLS:
nls_coefs.mean() - pars_true
lambda0 -2.992e-03
lambda1 -9.894e-04
p 2.761e-02
dtype: float64
and for MLE:
mle_coefs.mean() - pars_true
lambda0 1.939e-04
lambda1 2.915e-05
p -1.956e-03
dtype: float64
To visualize the estimates obtained throughout the simulations, we can compare density plots, along with the true parameter values:

Feel free to download a copy of this demo (boutsimuldemo.py).