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:

_images/boutsimuldemo_10_0.png

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