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 3.979e-03 0.729
std 0.005 4.227e-04 0.020
min 0.032 2.576e-03 0.655
25% 0.044 3.715e-03 0.716
50% 0.047 3.957e-03 0.730
75% 0.050 4.245e-03 0.743
max 0.061 5.356e-03 0.777

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 4.996e-03 0.700
std 0.003 3.818e-04 0.024
min 0.042 4.108e-03 0.628
25% 0.048 4.726e-03 0.684
50% 0.050 4.967e-03 0.701
75% 0.052 5.259e-03 0.716
max 0.062 6.295e-03 0.760

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   -0.003
lambda1   -0.001
p          0.029
dtype: float64

and for MLE:

mle_coefs.mean() - pars_true
lambda0    2.183e-04
lambda1   -3.812e-06
p          1.466e-05
dtype: float64

To visualize the estimates obtained throughout the simulations, we can compare density plots, along with the true parameter values:

_images/boutsimuldemo_10_0.png

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