Fits a discounting model (Mazur hyperbolic, exponential, or the
two-parameter Green-Myerson / Rachlin hyperboloids) with a random intercept
on log k, between-subject fixed effects, and either an SLT-beta or
Gaussian observation family, using Template Model Builder for exact AD +
Laplace approximation.
Usage
fit_dd_tmb(
data,
y_var = "y",
x_var = "x",
id_var = "id",
equation = c("mazur", "exponential", "green-myerson", "rachlin"),
family = c("sltb", "gaussian"),
random_effects = k ~ 1,
factors = NULL,
factor_interaction = FALSE,
continuous_covariates = NULL,
ll = NULL,
response_scale = c("proportion", "percent", "amount"),
start_values = NULL,
tmb_control = list(iter_max = 1000, eval_max = 2000),
multi_start = TRUE,
verbose = 1,
covariance_structure = c("pdSymm", "pdDiag"),
...
)Arguments
- data
Long data frame with subject id, delay, and indifference proportion columns.
- y_var, x_var, id_var
Column names (defaults
"y","x","id").- equation
One of
"mazur","exponential","green-myerson", or"rachlin". The two 2-parameter (hyperboloid) forms add a single population nonlinearity exponents(estimated on the log scale) and reduce to"mazur"ats = 1.- family
Observation family:
"sltb"(default) or"gaussian". For"sltb", responses outside[0, 1]after scaling are clamped (with a warning); for"gaussian", whose likelihood is unbounded, they are kept as observed and percent-scaled data are not detected automatically (setresponse_scale = "percent").- random_effects
RE formula:
k ~ 1(single random intercept onlog k),k + phi ~ 1(a joint 2-D random intercept on(log k, log phi), SLT-beta only), ork + s ~ 1(a joint 2-D random intercept on(log k, log s), Green-Myerson and Rachlin only).- factors
Character vector of between-subject factor names.
- factor_interaction
Logical; include a pairwise factor interaction.
- continuous_covariates
Character vector of covariate names.
- ll
Optional larger-later reward for
amount-scale coercion.- response_scale
One of
"proportion","percent","amount".- start_values
Optional named list overriding defaults.
- tmb_control
Optimizer control list.
- multi_start
Logical; if
TRUE(default), run the 3-set guarded multi-start. Converged starts are preferred over non-converged ones, then the lowest negative log-likelihood wins; the Hessian is checked on the kept fit only.- verbose
Integer verbosity (0 silent, 1 progress, 2 debug).
- covariance_structure
Covariance for a 2-D random effect (
k + phi ~ 1ork + s ~ 1):"pdSymm"(default; correlated random intercepts) or"pdDiag"(independent, correlation fixed at 0). Ignored fork ~ 1.- ...
Reserved.
Value
An object of class beezdiscounting_tmb with components:
- call
The matched call.
- opt
Normalized optimizer result (
par,objective,convergence,message).- model
List of
coefficients,se, andvariance_components.- sdr
TMB
sdreportobject (orNULLif SE computation failed).- hessian_pd
Logical positive-definiteness of the Hessian.
- param_info
Model metadata (equation, family, dimensions, factor spec, parsed random effects).
- formula_details
Fixed-effect design (
X,rhs,contrasts).- subject_pars
Data frame of subject-level parameters. For a 1-RE fit (
k ~ 1) the columns areid, u_i, k; for a phi-target 2-RE fit (k + phi ~ 1) they areid, re_k, re_phi, k, phi, phi_latent(phifloored at 0.1,phi_latentunfloored); for an s-target 2-RE fit (k + s ~ 1, GM/Rachlin) they areid, re_k, re_s, k, s, s_latentwheresis soft-clamped toward(0.05, 20)ands_latent = exp(log_s + re_s).- guard_info
Guard / clamp / floor activity at the fitted values (see "Scales, guards and floors"):
n_rows,mu_guard_lower,mu_guard_upper, plusn_s_clamped_lower/n_s_clamped_upper(k + s ~ 1) orn_phi_floor(k + phi ~ 1).NULLif the computation failed.- loglik, AIC, BIC
Fit statistics.
- converged, se_available
Convergence / SE-availability flags. A non-converged fit raises a
beezdiscounting_convergence_warningat fit time (regardless ofverbose) and again fromtidy(),confint(),summary(),get_dd_param_emms()andget_dd_comparisons().- multi_start_info
List recording the start selection:
n_starts,n_finite,n_converged,selected_start,tier(1 = converged and passing the log-k sanity guard, 2 = passing the guard but not converged, 3 = neither), andlower_nll_nonconverged(TRUEwhen a non-converged start reached a lower NLL than the kept one).- opt_warnings
Character vector of optimizer warnings.
- data
The single filtered model frame (id/x/y + retained design columns), row-aligned with the design matrix.
- data_all
The validated frame before complete-casing.
- coercion_info
Scale-coercion/clamping audit list.
Scales, guards and floors
Random-effect scales.
nlme::VarCorr()reports the subject SD oflog kon the natural-log scale (Term = "k"means log k);tidy()andsummary()convert it to the log10 scale and label it so.ranef()returns the standardised deviateu_ifor ak ~ 1fit but natural-log offsets (re_k,re_phi/re_s) for a two-random-effect fit.Mean guard. The fitted mean is held inside
[1e-6, 1 - 1e-6]. For the exponential equation this binds oncek * delayexceeds about 13.8 (fork = 0.01, delays beyond roughly 1,400 days); such observations carry no information aboutk, so very steep discounters measured at long delays lose curvature. Mazur needsk * delaynear1e6to reach the guard; Green-Myerson and Rachlin can reach it with a larges(e.g. Green-Myerson withk = 0.1,s = 3at 1,460 days).fit$guard_info$mu_guard_lower/$mu_guard_uppercount the positive-delay observations whose subject-level fitted mean falls outside the guard, andsummary()adds a note when either is non-zero.Shape
s. The reportedsis the unclamped population valueexp(log_s). Withk + s ~ 1each subject's effectivesis soft-clamped into(0.05, 20), and theVarCorr()SD ofsis on the latent (pre-clamp) log scale.subject_parsholds both the effectivesand the latents_latent = exp(log_s + re_s);fit$guard_info$n_s_clamped_lower/$n_s_clamped_uppercount the subjects whose two values differ by more than 1% on the log scale (noted bysummary()).Precision floor. SLT-beta precision is bounded below at
phi = 0.1. Fork ~ 1this is an optimizer bound thattmb_control$lowercan relax; withk + phi ~ 1each subject'sphiis floored at 0.1 inside the likelihood and cannot be relaxed.subject_pars$phi_latentis the unfloored value andfit$guard_info$n_phi_floorcounts the subjects below the floor (noted bysummary()).
These counts are computed at the fitted values and are reporting only: they do not change any estimate.
Two-parameter equations (Green-Myerson, Rachlin)
k and s trade off along a ridge, and with the few delays of a typical
titration task (about 7) the pair is much less stable than a one-parameter
k. The pair is particularly sensitive to how responses near zero are
recorded. In the SLT-beta log-density the response enters through a
(mu * phi - 1) * log(y) term, so a recorded 0 (treated as about 1e-8)
carries far more leverage than a recorded 0.001; the direction of the pull
depends on mu * phi. In simulation with identical true values (Green-
Myerson, log k = -4.61, s = 1.4, 300 subjects, 6 delays), the same
draws gave (log k, s) of (-4.79, 1.58) as simulated, (-5.06, 1.91)
after rounding y to 3 decimals, and (-4.37, 1.20) after flooring y
at 0.001; Mazur fits to the same data moved by less than 0.03. These are
sensitivity results for that design, not a general bias. Report how the
indifference points were recorded (resolution, how zeros were coded, the
count of y below 0.001), keep the convention identical across groups
being compared, and consider a sensitivity refit under an alternative
convention.
References
Young, M. E. (2017). Discounting: A practical guide to multilevel analysis of indifference data. Journal of the Experimental Analysis of Behavior, 108(1), 97-112. doi:10.1002/jeab.265
Kim, M., Koffarnus, M. N., & Franck, C. T. (2024). Thinking inside the bounds: Improved error distributions for indifference point data analysis and simulation via beta regression using common discounting functions. arXiv preprint arXiv:2404.18000.
Kim, M., Kaplan, B. A., Koffarnus, M. N., & Franck, C. T. (2025). Scale-location-truncated beta regression: Expanding beta regression to accommodate 0 and 1. arXiv preprint arXiv:2509.13167.
Examples
# \donttest{
# Small two-subject long-format indifference-point data frame.
dd <- data.frame(
id = rep(c("s1", "s2"), each = 5),
x = rep(c(7, 30, 180, 365, 730), times = 2),
y = c(0.95, 0.80, 0.45, 0.30, 0.15,
0.90, 0.70, 0.40, 0.25, 0.10)
)
fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", verbose = 0)
exp(fit$model$coefficients[["beta_k"]]) # population k
#> [1] 0.008690582
# }
