beezdiscounting 0.4.0
This is a large release (the first since 0.3.2, January 2025): mixed-effects and Bayesian discounting tiers, trial-level choice models, 21-item MCQ and PDQ scoring, Monte Carlo power analysis, and ten vignettes (all new since 0.3.2, which had none).
Bug fixes that can change results
-
check_unsystematic()now applies both Johnson & Bickel (2008) thresholds on theyscale (c1 * ll,c2 * ll). Previouslyllcancelled out, so amount-scale data (e.g.yin dollars withll = 100) were judged against the bare proportionsc1/c2and received wrong verdicts. Criterion 2 is now strict: a decline of exactlyc2 * llpasses.ll,c1andc2are validated. Verdicts on proportion data withll = 1change only for changes of exactlyc1orc2, which floating-point error previously judged either way (e.g.y = c(0.6, 0.8, 0.1)used to fail C1). -
check_unsystematic()andcalc_aucs()now error on missingy/xvalues and on duplicate delays within a subject, which previously gave a silentNA/TRUEverdict or a result that depended on row order.calc_aucs()documents thatauc_log10useslog10(x + 1)and therefore depends on the delay unit. - INN imputation (
score_mcq(),score_mcq27(),score_pdq()withimpute_method = "inn") now warns when items stay missing (neighbours disagree withrandom = FALSE, or a whole rank group is missing) instead of letting the resultingNAscores pass silently. -
score_dd(),ans_dd(),calc_dd(),score_pd(),ans_pd()andcalc_pd()now read numeric Qualtrics exports (choice codes 1/2) as well as text exports. In 0.3.2 only text exports were supported: a numeric export matched neither “now” nor “for sure”, so every response was scored as the larger-later (delay) or uncertain (probability) option. Numeric codes are read per item, because both 5.5-trial templates codeAttend-LLin reverse order (delay: 1 = “in 25 years”, 2 = “now”; probability: 1 = “with a 1% chance”, 2 = “for sure”). Text exports score as before. -
fit_dd_brms(equation = "rachlin")now samples on the normalised delayx / median(x)with thelogkintercept priornormal(0, 2.5)on that scale, and back-transforms the draws to data-unit k. Rachlin k scales as c^s under a change of delay unit, so the previous data-unit prior (normal(-log(median(x)), 2.5), independent of s) implied a different curve prior in days than in weeks whenever s was not 1. Rachlin posteriors change; the same data in any delay unit now give the same curve posterior. A user-supplied Rachlinlogkintercept prior is read on the normalised scale, and rawfit$brmsfitdraws ofb_logk_Interceptare normalised (coef(),confint(),subject_pars, EMMs and comparisons report data units).autoscale_priors = FALSEkeeps the old parameterisation. MCMC convergence checks now also cover the back-transformed log k. -
score_mcq(items = 21): a respondent who chooses the smaller-sooner reward on every item now gets the overall-ladder edge k of 0.1333 itself, as the Kaplan et al. (2014) 21-item Excel scorer assigns it, instead of the geometric mean of the last item’s k and the edge (0.1321). When that switch point ties with others, 0.1333 is its contribution to the geometric mean of the tied switch points, so the overall k also changes there. The 21-item item table (order, magnitude, k, delay) was checked against that workbook and matches. The 27-item scorer is unchanged: its workbook does average the last item with the edge (0.2494), andscore_mcq27()reproduces that. - The 5.5-trial delay and probability scorers (
score_dd(),ans_dd(),calc_dd(),score_pd(),ans_pd(),calc_pd()) now recognise only the template’s numeric codes and option texts. Previously any other value (a typo,"-99", a relabelled export, a blank cell) was silently scored as the delayed/uncertain choice with a real k or h. Blank cells are now treated as unanswered and dropped, and other unrecognised values raise an error naming the item and value. -
fit_dd_tmb()andfit_dd_choice()(both modes) no longer let a non-converged start displace a converged one on negative log-likelihood alone: the multi-start keeps the lowest-NLL converged start that passes the log-k sanity guard, falling back to other starts only when no start both converged and passed the guard (recorded in the newmulti_start_infoelement). A non-converged fit now raises a classedbeezdiscounting_convergence_warningat fit time regardless ofverbose, andtidy(),confint(),summary(),get_dd_param_emms()andget_dd_comparisons()repeat it. Previously such fits (e.g.tmb_control = list(iter_max = 3)) returned finite p-values and intervals with no warning. Non-positive-definite Hessian and failedsdreport()warnings are likewise no longer silenced byverbose = 0. -
simulate_dd_ip(family = "gaussian")no longer clamps draws to[0, 1], andfit_dd_tmb(family = "gaussian")/fit_dd_brms(family = "gaussian")keep responses as supplied: no clamping to[0, 1]and no automatic percent detection (data that look percent-scaled get a warning; an explicitresponse_scale = "percent"or"amount"still converts). The Gaussian likelihood is unbounded, so the clamp made the simulated data, and the data the model saw, differ from the model being fitted: near delay 0 about half the draws piled up at 1. Gaussian-family simulations andpower_discounting(family = "gaussian")results change; the SLT-beta family is unaffected.
Monte Carlo power analysis
-
power_discounting()estimates statistical power for detecting a between-subject difference in discount rate (delta_kon log k) by simulating withsimulate_dd_ip()and refitting each replicate withfit_dd_tmb(). Reports the power estimate with a Wilson Monte Carlo confidence interval, p-value and CI-exclusion hit rates, and convergence diagnostics; non-usable fits are excluded from the denominator and surfaced, never counted as misses. The Wald test uses a t reference with the design’s two-sample df (n - 2): an empirical small-sample calibration, not a model-derived df, chosen because it keeps the Type I error near nominal in the package’s calibration tests where the z-test runs high. -
find_n_discounting()searches for the smallest total N reaching a target power via bisection, adding replicates adaptively where the Monte Carlo verdict is ambiguous and re-confirming the selected N before reporting. - Type I error calibration, convergence handling, a closed-form benchmark against
pwr::pwr.t.test(), monotonicity, and seed reproducibility are verified in the test suite; seevignette("power-analysis")for scope and validity notes. Mirrorsbeezdemand::power_demand(); the within- vs between-subject asymmetry is intentional.
Probability Discounting Questionnaire (PDQ)
- New
score_pdq()scores the 30-item PDQ (Madden, Petry, & Johnson, 2009): per-block h under the hyperbolic odds-against model, consistency, and risky choice ratios, with the same strict input validation and imputation options asscore_mcq(). The implementation reproduces the Gray et al. (2016) scoring-syntax lookup tables exactly for all 3 x 1024 response patterns (verified in the test suite). Anoverall_hfrom a pooled 30-item ladder is also reported as a documented beezdiscounting extension (the published scoring defines no overall ladder;mean_his Gray et al.’s recommended composite). - New
prop_sc()(guaranteed-choice proportions by h rank) andpdq_to_choice()(trial-level choice frame including the odds against winning,theta), plus a bundledpdqexample dataset and a “Scoring the Probability Discounting Questionnaire” vignette. -
get_lookup_table()gains aninstrumentargument ("mcq27","mcq21","pdq");itemsremains as a back-compatible alias. Internally the MCQ registry is now instrument-keyed and the ladder-scoring core is shared across instruments; the refactor leaves 27- and 21-item MCQ results unchanged (pinned by golden-fixture regression tests). The separate 21-item all-smaller-sooner change is listed under “Bug fixes that can change results”.
21-item MCQ support
- New
score_mcq()scores both the 27-item (Kirby, Petry, & Bickel, 1999) and the original 21-item (Kirby & Marakovic, 1996) MCQ with the same consistency-maximization algorithm;score_mcq27()is now a wrapper and its output is unchanged. The 21-item item table (amounts, delays, k at indifference, ranks) was transcribed from Kirby & Marakovic (1996, Table 1) and cross-validated against the Kaplan et al. (2014) Excel Automated Scorer, including its ladder-edge conventions. -
prop_ss(), INN missing-data imputation (now defined over k-rank neighbor groups, identical results for the 27-item version), the newmcq_to_choice()(generalizingmcq27_to_choice()), andget_lookup_table()all supportitems = 21. - New
mcq21example dataset. - Stricter input validation in the scorer: duplicate, unknown, missing, or fractional/non-whole question ids and responses outside 0/1/NA now error instead of silently producing invalid scores.
mcq_to_choice()still accepts ragged input. - Character, factor, and logical responses to
score_mcq()/score_mcq27()are now normalized and score identically to numeric 0/1 (previously they either passed validation and then failed obscurely during scoring, or errored). - New
plot()method for 21-itemscore_mcq()output (plot.score_mcq_output()), matching the existing 27-item plot method. - Internal (unexported)
inn()gained aregparameter (inn(dat, reg, random, verbose)) to support both MCQ versions. This is a breaking signature change for any code callingbeezdiscounting:::inn()directly.
New vignettes
-
vignette("mcq27-scoring"): scoring the 27-item Monetary Choice Questionnaire (score_mcq27(),get_lookup_table(),prop_ss(),summarize_mcq(),mcq27_to_choice(), and the wide/long converters). -
vignette("delay-discounting-basics"): a getting-started walk-through of indifference-point screening (check_unsystematic()), curve fitting (fit_dd()/results_dd()), discount ratek, and AUC (calc_aucs()). -
vignette("fivetrial-task"): scoring the 5.5-trial delay and probability discounting tasks from the Qualtrics minute-discounting template. -
vignette("tmb-mixed-effects"): thefit_dd_tmb()mixed-effects workflow and its S3 methods, prediction, diagnostics, and group comparisons.
Bug fixes
tidy(),confint(), andsummary()on afit_dd_tmb()or structuralfit_dd_choice()fit withfactorsreported the intercept’s standard error for everybeta_kcoefficient (the log-k intercept and each condition contrast): the internal SE lookup collapsed the duplicatedbeta_kparameter names onto the first element. Condition-contrast Wald statistics, p-values, and confidence intervals were therefore anticonservative (thepower_discounting()Type I calibration battery measured a false-positive rate of 0.168 at nominal .05 before the fix). SEs are now aligned positionally with the coefficient vector in both accessors, and ambiguous legacy objects with duplicated names returnNASEs instead of silently misaligned values.check_unsystematic()andcalc_aucs()now compute results perid. Previously they computed a single result over the whole data frame and recycled it acrossunique(id), so multi-subject input returned the same verdict / AUC for every subject. Each now returns one correct row per subject (single-subject output is unchanged by this, apart from the threshold and input-validation changes under “Bug fixes that can change results”).check_unsystematic()orders points byxwhen that column is present, andcalc_aucs()orders byxwithin each subject. Rows with a missingidare dropped so they cannot contaminate other subjects’ results.prop_ss()now pools correctly across respondents. It previously dropped all but the first occurrence of eachquestionid(viamatch()) and divided by a fixed denominator of 3, so multi-respondent input was reduced to one respondent and could return values above 1. It now averages over all retained rows per k-rank and returnsNAfor a rank with no observed responses.
Subject-random s (GM/Rachlin curvature)
-
fit_dd_tmb(..., random_effects = k + s ~ 1)now fits a per-subject random intercept on the Green-Myerson / Rachlin curvatures, jointly withlog k, withcovariance_structure = "pdSymm"(correlated) or"pdDiag"(independent), for bothfamily = "sltb"and"gaussian". Per-subjects_iis soft-clamped toward(0.05, 20)(a C-infinity softplus map; see the de-hang note below).VarCorr()/ranef()/summary()surface the(k, s)covariance and per-subjects.predict()atlevel = "subject"uses each subject’s estimateds_i; response-SD standardization uses the population precisionphi.simulate_dd_ip()gainssigma_s/rho_ksto generate(log k, log s)bivariate-normal data for recovery testing. - The per-subject
srandom effect now uses a smooth (C-infinity) softplus soft clamp instead of a hard clamp, so a clamp-binding subject (e.g. a no-discounting or step subject) converges instead of hanging the Laplace inner solve.
Bayesian (brms) modeling tier
- New
fit_dd_brms(): Bayesian mixed-effects discounting via brms/Stan for all four TMB equations ("mazur","exponential","green-myerson","rachlin").family = "beta"(identity link with a differentiable squish) is an ordinary beta likelihood, not the TMB SLT-beta, and its estimates are not expected to matchfit_dd_tmb(family = "sltb")when responses sit at or near 0 or 1;family = "gaussian"matches the TMB gaussian likelihood wherever the TMB mu clamp does not bind (everywhere except extreme decay underflow). Exact 0/1 responses must be handled explicitly: the defaultboundary = "error"refuses to fit and reports the count;"squeeze"(Smithson-Verkuilen, rescales every response) and"zoib"(zero-one-inflated beta; changes the estimand) are opt-ins.summary()reports the exact- and near-boundary fractions.init = "tmb"(both brms fitters) seeds the chains from the TMB pre-fit only when that fit converged with finite estimates, and otherwise falls back to prior-center inits with a warning. - New
fit_dd_choice_brms(): the structural trial-level choice model underbernoulli("logit"), matchingfit_dd_choice(mode = "structural"), including between-subject designs onlog kviafactors/factor_interaction/continuous_covariates(rank-deficient designs are rejected before sampling;gammaandb0stay population-level). - New
default_dd_priors()/default_dd_choice_priors(): inspectable defaults with delay-unit-awarelogkanchoring (centersk * median(delay) = 1; for Rachlin,k * median(delay)^s = 1). -
fit_dd_brms(random_effects = k + phi ~ 1, family = "beta")adds a per-subject precision random effect, correlated with thelog kintercept (covariance_structure = "pdSymm", the default) or independent ("pdDiag").phibecomes a predicted distributional parameter on the log link;variance_componentsandsubject_parsgain the precision-RE SD, the(k, phi)correlation, and per-subject precision, mirroringfit_dd_tmb(random_effects = k + phi ~ 1). The TMB per-subject precision floor (0.1) is not replicated by the brms log-link RE. - S3 methods mirror the TMB tier’s contracts: the exact 8-column
tidy()table (withNAstatistic/p.value; estimates are posterior medians of report-space-transformed draws),glance()withelpd_loo/looicand MCMC diagnostics in place ofAIC/BIC,confint()quantile credible intervals,predict(),ranef()with per-subjectkposterior summaries for indifference-point fits,print()/summary(). -
get_dd_param_emms()andget_dd_comparisons()accept brms indifference-point and structural choice fits: draws-based marginal means and contrasts over the same reference grid as the TMB path, with quantile credible intervals andpost.prob(posterior probability of direction) in place of adjusted p-values. -
init = "tmb"is available in both Bayesian fitters (a quiet TMB pre-fit supplies the chain starting values, with prior-center fallback). - brms, posterior, and loo are Suggests-only.
- New vignette
vignette("bayesian-discounting")(precomputed output) walks throughfit_dd_brms()/fit_dd_choice_brms()and their S3 surface. - New vignette “Comparing discounting rates between groups” (
vignette("dd-group-comparisons")): factor designs on log k, estimated marginal means, and contrasts across both backends (TMB with Wald tests and Holm adjustment; brms with posterior draws andpost.prob), for indifference-point and trial-level choice models alike.
Modeling tiers and choice models
New
fit_dd_choice(mode = "structural")fits trial-level smaller-sooner vs larger-later choice as a binomial GLMM, estimating the discount ratekdirectly (scale-invariant value comparison, optional choice-bias intercept). It shares theget_dd_param_emms()/get_dd_comparisons()kcontract withfit_dd_tmb()and is validated by an IP-vs-choice tie-out.simulate_dd_choice()generates structural choice data.fit_dd_choice(mode = "descriptive")fits the Young (2018) descriptive choice model: a logistic mixed model on the log amount ratio and log delay with subject random slopes, returning logit-scale sensitivities rather than a discount rate. Shares the S3 surface (tidy(),summary(),predict(), …) with the structural mode; seevignette("choice-discounting").New
mcq27_to_choice()reshapes long-form 27-item Monetary Choice Questionnaire responses (subjectid/questionid/response) into the per-trialid/ss_amount/ll_amount/delay/choiceframe consumed byfit_dd_choice(), using the canonical Kirby, Petry, & Bickel (1999) item design.get_lookup_table()now returns that complete design (addsss_amount,ll_amount, anddelay).fit_dd_tmb()andsimulate_dd_ip()gain the two-parameter hyperboloid equations"green-myerson"(mu = (1 + k*x)^(-s)) and"rachlin"(mu = 1 / (1 + k*x^s)), with a single population nonlinearity exponents(reported bytidy()/summary()/confint()). Both reduce to"mazur"ats = 1.New
plot_qq()methods (re-exportingbeezdemand::plot_qq()) forfit_dd_tmb()andfit_dd_choice()fits: a normal QQ plot of the estimated (shrunken, empirical-Bayes) subject random-effect deviates against a normal reference, the standard check on the Gaussian random-effects assumption. Bayesian (fit_dd_brms()) fits are intentionally excluded; usebrms::pp_check()and MCMC diagnostics there instead.Mixed-effects discounting via TMB (
fit_dd_tmb()): fits the indifference-point (IP) family discounting model (Mazur hyperbolic or exponential mean with a subject random intercept onlog k) under either the scale-location-truncated beta (family = "sltb", default) or Gaussian (family = "gaussian") observation family. Between-subject factors and continuous covariates enter thelog kfixed-effect design.SLT-beta error distribution: assigns finite probability to indifference points at exactly 0 and 1, where ordinary beta regression is undefined. Means use an identity link on the discounting function; variance shrinks near the bounds and grows mid-range.
Estimated marginal means and contrasts:
get_dd_param_emms()returns the EMM ofkper factor level (computed on thelog kscale and back-transformed);get_dd_comparisons()returns pairwise or treatment-vs-control contrasts as ratios of discount rates, with multiplicity adjustment via anystats::p.adjustmethod.broom + base S3 surface on
beezdiscounting_tmbobjects:tidy(),glance()(backend"TMB_mixed"),augment(),coef(),fixef(),ranef(),confint(),predict(),summary(),logLik(),AIC(),BIC(),nobs(),print().
beezdiscounting 0.3.2
CRAN release: 2025-01-08
New Features
-
fit_dd():- Introduced a new function to fit delay-discounting models using specified equations (
"mazur"/"hyperbolic"or"exponential") and methods ("pooled","mean", or"two stage"). - Supports flexible data handling for aggregated and participant-specific modeling.
- Returns an object of class
"fit_dd"containing the fitted models, input data, and method details.
- Introduced a new function to fit delay-discounting models using specified equations (
-
plot_dd():- Added a function to visualize fitted delay-discounting models.
- Automatically adapts to different fitting methods, including aggregated and individual models.
- Provides customizable axis labels, title, and optional log-transformed x-axis for improved visualization of delay scales.
-
results_dd():- New utility to extract model parameter estimates, confidence intervals, and fit statistics from a
"fit_dd"object. - Supports both aggregated and participant-specific models.
- Outputs a tidy tibble with columns for terms, estimates, standard errors, t-statistics, p-values, R2, three different AUC metrics, and confidence bounds.
- New utility to extract model parameter estimates, confidence intervals, and fit statistics from a
-
check_unsystematic():- New utility function to check delay-discounting datasets for unsystematic data patterns according to Johnson & Bickel’s (2008) two criteria.
-
calc_aucs():- New utility function to calculate three different area under the curve (AUC) metrics for delay-discounting data according to Borges et al. (2016).
Improvements
- Confidence intervals are now computed using the
calc_conf_int()function, ensuring accurate estimation based on model degrees of freedom. - R2 values are calculated consistently using the
calc_r2()function, providing reliable fit metrics for all models.
Enhancements
- The package now supports robust delay-discounting workflows, from unsystematic identification (
check_unsystematic), model fitting (fit_dd), to visualization (plot_dd), to result extraction (results_dd). - Improved compatibility with delay-discounting datasets that require participant-level or aggregated modeling approaches.
beezdiscounting 0.3.1
CRAN release: 2023-11-16
Minor fix
- Correctly names output columns from
calc_pd()andscore_pd().ep50changed toetheta50and corrected calculation ofep50.
beezdiscounting 0.3.0
CRAN release: 2023-11-14
New features
- Add functions for scoring 5.5 trial probability discounting task (from the Qualtrics template) including:
calc_pd()(andscore_pd(),timing_pd(), andans_pd).
Minor fix
- Subsetting issue is fixed in
score_dd()that would unintentionally drop all rows if both conditions wereFALSE.
beezdiscounting 0.2.0
CRAN release: 2023-11-02
New features
score_mcq27()properly supports arguments:impute_method,random,return_data, andverbose. See documentation and theREADMEfor explanations.generate_data_mcq()can generate fake MCQ data, includingseedandprop_naarguments for reproducibility and specifying proportion ofNAs.long_to_wide*andwide_to_long*are helper functions to reshape data from/to different formats.
Minor fix
- When no imputation is specified and
NAs exist in the data,score_mcq27()returnsNAs for the scoring instead of 1.
