| Title: | Behavioral Economic Easy Discounting |
|---|---|
| Description: | Facilitates some of the analyses performed in studies of behavioral economic discounting. The package supports scoring of the 21- and 27-Item Monetary Choice Questionnaires (see Kaplan et al., 2016; <doi:10.1007/s40614-016-0070-9>), scoring of the 30-Item Probability Discounting Questionnaire (see Madden et al., 2009; <doi:10.1037/a0016806>), calculating k values (Mazur's simple hyperbolic and exponential) using nonlinear regression, calculating various Area Under the Curve (AUC) measures, plotting regression curves for both fit-to-group and two-stage approaches, checking for unsystematic discounting (Johnson & Bickel, 2008; <doi:10.1037/1064-1297.16.3.264>) and scoring of the minute discounting task (see Koffarnus & Bickel, 2014; <doi:10.1037/a0035973>) using the Qualtrics 5-trial discounting template (see the Qualtrics Minute Discounting User Guide; <doi:10.13140/RG.2.2.26495.79527>), which is also available as a .qsf file in this package. The package also fits mixed-effects (hierarchical) discounting models with a scale-location-truncated beta likelihood that admits indifference points at exactly 0 and 1 (Kim, Kaplan, Koffarnus, and Franck, 2025; <arXiv:2509.13167>). The package also implements the linearized Mazur estimator, random-effects model, and F-test of Hinds et al. (2026; <doi:10.1016/j.jmp.2026.103006>). |
| Authors: | Brent A. Kaplan [aut, cre, cph] (ORCID: <https://orcid.org/0000-0002-3758-6776>) |
| Maintainer: | Brent A. Kaplan <[email protected]> |
| License: | GPL (>= 2) |
| Version: | 0.4.0.9000 |
| Built: | 2026-10-17 10:54:52 UTC |
| Source: | https://github.com/brentkaplan/beezdiscounting |
The paper's F-test is exact when the random-effects variance estimate
g-hat > 0 (Prop. 3.5's assumption); when g-hat = 0 the statistic is still
reported, with a warning.
## S3 method for class 'beezdiscounting_linear' anova(object, hypothesis = NULL, pairwise = FALSE, ...)## S3 method for class 'beezdiscounting_linear' anova(object, hypothesis = NULL, pairwise = FALSE, ...)
object |
A |
hypothesis |
|
pairwise |
If |
... |
Unused; present for S3 generic consistency. |
Tibble of class beezdiscounting_linear_anova.
beezdiscounting_linear-methods for the other S3 methods.
Converts answers from 5.5 trial delay discounting from Qualtrics template
ans_dd(df)ans_dd(df)
df |
A dataframe containing all the columns |
A dataframe with the ResponseId, index, and response (ss or ll).
ans_dd(five.fivetrial_dd)ans_dd(five.fivetrial_dd)
Converts answers from 5.5 trial probability discounting from Qualtrics template
ans_pd(df)ans_pd(df)
df |
A dataframe containing all the columns |
A dataframe with the ResponseId, index, and response (sc or lu).
ans_pd(five.fivetrial_pd)ans_pd(five.fivetrial_pd)
Returns the data used for fitting (or newdata) as a tibble with three
diagnostic columns appended:
.fitted: subject-conditional fitted choice probability P(LL).
.resid: raw (response) residual choice - .fitted.
.std_resid: Pearson residual .resid / sqrt(p * (1 - p)).
## S3 method for class 'beezdiscounting_choice' augment(x, newdata = NULL, ...)## S3 method for class 'beezdiscounting_choice' augment(x, newdata = NULL, ...)
x |
A |
newdata |
Optional data frame. |
... |
Unused. |
A tibble with the same rows as the data plus .fitted, .resid,
and .std_resid.
Returns the data used for fitting (or newdata) as a tibble with three
diagnostic columns appended:
.fitted: subject-conditional fitted indifference proportion (clamped to
(0, 1)).
.resid: raw residual y - .fitted on the response scale.
.std_resid: Pearson (standardized) residual, i.e., .resid divided by the
per-row response SD. For family = "gaussian" the SD is the constant
sigma_e; for family = "sltb" it is the delta-method SLT SD
sqrt(mu * (1 - mu) / (phi + 1)).
## S3 method for class 'beezdiscounting_tmb' augment(x, newdata = NULL, ...)## S3 method for class 'beezdiscounting_tmb' augment(x, newdata = NULL, ...)
x |
A |
newdata |
Optional data frame. |
... |
Unused. |
A tibble with the same rows as the data plus .fitted, .resid,
and .std_resid.
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, verbose = 0) head(augment(fit))dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, verbose = 0) head(augment(fit))
S3 methods for objects of class beezdiscounting_linear returned by
fit_dd_linear().
All quantities are closed form; nothing is re-optimized.
## S3 method for class 'beezdiscounting_linear' print(x, ...) ## S3 method for class 'beezdiscounting_linear' summary(object, ...) ## S3 method for class 'summary.beezdiscounting_linear' print(x, ...) ## S3 method for class 'beezdiscounting_linear' tidy(x, effects = c("subject", "population"), ...) ## S3 method for class 'beezdiscounting_linear' glance(x, ...) ## S3 method for class 'beezdiscounting_linear' coef(object, ...) ## S3 method for class 'beezdiscounting_linear' confint(object, parm = c("population", "subject"), level = NULL, ...) ## S3 method for class 'beezdiscounting_linear' augment(x, ...) ## S3 method for class 'beezdiscounting_linear' predict(object, type = "parameters", ...) ## S3 method for class 'beezdiscounting_linear' nobs(object, ...) ## S3 method for class 'beezdiscounting_linear' logLik( object, level = c("population", "subject"), scale = c("raw", "transformed"), ... )## S3 method for class 'beezdiscounting_linear' print(x, ...) ## S3 method for class 'beezdiscounting_linear' summary(object, ...) ## S3 method for class 'summary.beezdiscounting_linear' print(x, ...) ## S3 method for class 'beezdiscounting_linear' tidy(x, effects = c("subject", "population"), ...) ## S3 method for class 'beezdiscounting_linear' glance(x, ...) ## S3 method for class 'beezdiscounting_linear' coef(object, ...) ## S3 method for class 'beezdiscounting_linear' confint(object, parm = c("population", "subject"), level = NULL, ...) ## S3 method for class 'beezdiscounting_linear' augment(x, ...) ## S3 method for class 'beezdiscounting_linear' predict(object, type = "parameters", ...) ## S3 method for class 'beezdiscounting_linear' nobs(object, ...) ## S3 method for class 'beezdiscounting_linear' logLik( object, level = c("population", "subject"), scale = c("raw", "transformed"), ... )
... |
Unused; present for S3 generic consistency. |
object, x
|
A |
effects |
For |
parm |
Which interval |
level |
For |
type |
For |
scale |
For |
confint() uses parm to SELECT WHICH INTERVAL is returned, not to filter
parameter names as in stats::confint().
logLik() reports the raw-scale (Jacobian-corrected) log-likelihood by
default, which makes it comparable
with this package's Gaussian NLS and TMB log-likelihoods.
print() and print.summary() return their input invisibly;
summary() returns an object of class summary.beezdiscounting_linear;
tidy(), glance(), augment() and predict() return tibbles.
augment() adds .fitted and .resid on the raw indifference-point
scale and .std_resid, the transformed-scale residual y_lin - ln k_i
divided by the model's error standard deviation (sqrt(sigma2) from the
random-effects fit, or the pooled within-subject mean square when that
component is unavailable); .std_resid is NA for points dropped under
boundary = "drop". coef() returns the
named vector of condition means mu; confint() returns a two-column
matrix of lower/upper bounds; nobs() returns the number of usable
transformed observations; logLik() returns a "logLik" object with df
and nobs attributes. Methods needing the random-effects component error
when the design was unbalanced and re is NULL.
fit_dd_linear(),
anova.beezdiscounting_linear(),
plot.beezdiscounting_linear()
Calculates three area-under-the-curve (AUC) metrics for delay-discounting data:
regular AUC (using raw delays), log10 AUC (using logarithmically scaled delays), and ordinal AUC (using ordinally scaled delays).
The three differ only in how the delays are scaled before the area is computed.
Metrics are computed separately for each id, so a data frame with several
subjects returns one row per subject.
calc_aucs(dat)calc_aucs(dat)
dat |
A data frame containing delay discounting data. It must include the following columns:
|
A tibble with the following columns:
id: The participant or group identifier.
auc_regular: The regular AUC, calculated using the raw delay values.
auc_log10: The log10 AUC, calculated using logarithmically transformed delay values.
auc_ord: The ordinal AUC, calculated using ordinally scaled delay values.
# Example data data <- data.frame( id = rep("P1", 6), x = c(1, 7, 30, 90, 180, 365), y = c(0.8, 0.5, 0.3, 0.2, 0.1, 0.05) ) # Calculate AUC metrics for a single participant calc_aucs(data)# Example data data <- data.frame( id = rep("P1", 6), x = c(1, 7, 30, 90, 180, 365), y = c(0.8, 0.5, 0.3, 0.2, 0.1, 0.05) ) # Calculate AUC metrics for a single participant calc_aucs(data)
Computes the lower and upper bounds of a t-based confidence interval for a parameter estimate from its standard error, a specified significance level, and the degrees of freedom from the model.
calc_conf_int(estimate, std_error, model, alpha = 0.05)calc_conf_int(estimate, std_error, model, alpha = 0.05)
estimate |
A numeric value representing the parameter estimate. |
std_error |
A numeric value representing the standard error of the parameter estimate. |
model |
A fitted model object that provides the residual degrees of freedom via |
alpha |
A numeric value representing the significance level. Default is 0.05 (95% confidence interval). |
A numeric vector of length two:
First element: Lower bound of the confidence interval.
Second element: Upper bound of the confidence interval.
# Example using a linear model data <- data.frame(x = 1:10, y = c(2.3, 2.1, 3.7, 4.5, 5.1, 6.8, 7.3, 7.9, 9.2, 10.1)) lm_model <- lm(y ~ x, data = data) calc_conf_int(estimate = 0.5, std_error = 0.1, model = lm_model, alpha = 0.05)# Example using a linear model data <- data.frame(x = 1:10, y = c(2.3, 2.1, 3.7, 4.5, 5.1, 6.8, 7.3, 7.9, 9.2, 10.1)) lm_model <- lm(y ~ x, data = data) calc_conf_int(estimate = 0.5, std_error = 0.1, model = lm_model, alpha = 0.05)
Calculate scores, answers, and timing for 5.5 trial delay discounting from Qualtrics template
calc_dd(df)calc_dd(df)
df |
A dataframe containing all the columns from the template. |
A dataframe with k/ed50 values, answers, timing
calc_dd(five.fivetrial_dd)calc_dd(five.fivetrial_dd)
Calculate scores, answers, and timing for 5.5 trial probability discounting from Qualtrics template
calc_pd(df)calc_pd(df)
df |
A dataframe containing all the columns from the template. |
A dataframe with h/ep50 values, answers, timing
calc_pd(five.fivetrial_pd)calc_pd(five.fivetrial_pd)
Calculates the coefficient of determination () for a fitted model as one minus the ratio of the sum of squared errors (SSE)
to the total sum of squares (SST).
calc_r2(model)calc_r2(model)
model |
A fitted model object. The model must have |
A numeric value representing the value of the model. Returns NA if the model is NULL.
# Example using a simple linear model data <- data.frame(x = 1:10, y = c(1, 2, 3, 4, 5, 6, 7, 9, 10, 11)) lm_model <- lm(y ~ x, data = data) calc_r2(lm_model)# Example using a simple linear model data <- data.frame(x = 1:10, y = c(1, 2, 3, 4, 5, 6, 7, 9, 10, 11)) lm_model <- lm(y ~ x, data = data) calc_r2(lm_model)
Checks a data set against the two Johnson and Bickel (2008) criteria for unsystematic delay-discounting data:
Criterion 1: Any subsequent value of y exceeds the previous value by more than a specified proportion of the larger later reward (ll).
Criterion 2: The last value of y is not at least a specified proportion less than the first value of y.
check_unsystematic(dat, ll = 1, c1 = 0.2, c2 = 0.1)check_unsystematic(dat, ll = 1, c1 = 0.2, c2 = 0.1)
dat |
A data frame containing the delay-discounting data. It must have at least two columns:
|
ll |
A numeric value representing the larger later reward. Default is 1. |
c1 |
A numeric value for the threshold proportion for Criterion 1. Default is 0.2. |
c2 |
A numeric value for the threshold proportion for Criterion 2. Default is 0.1. |
The criteria are applied separately to each id, so a data frame containing
several subjects returns one row per subject. When an x (delay) column is
present, each subject's points are ordered by delay before the criteria are applied.
A tibble with the following columns:
id: The unique identifier for the data set.
c1_pass: Logical value indicating whether Criterion 1 was passed.
c2_pass: Logical value indicating whether Criterion 2 was passed.
data <- tibble::tibble( id = c(rep("P1", 6)), x = c(1, 7, 30, 90, 180, 365), # delays y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05) # indifference points ) check_unsystematic(data, ll = 1, c1 = 0.2, c2 = 0.1)data <- tibble::tibble( id = c(rep("P1", 6)), x = c(1, 7, 30, 90, 180, 365), # delays y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05) # indifference points ) check_unsystematic(data, ll = 1, c1 = 0.2, c2 = 0.1)
Returns the optimizer's flat named parameter vector: beta_k (one entry per
fixed-effect design column, on the log-k scale), log_sigma_u, log_gamma,
and beta0 (only when intercept = TRUE). This is the numeric escape hatch
consumed by tooling such as car::deltaMethod.
## S3 method for class 'beezdiscounting_choice' coef(object, ...)## S3 method for class 'beezdiscounting_choice' coef(object, ...)
object |
A |
... |
Unused. |
Named numeric vector.
Returns the optimizer's flat named parameter vector: beta_k (one entry per
fixed-effect design column, on the log-k scale), log_sigma_u, and the
auxiliary parameter (log_phi for family = "sltb", log_sigma_e for
family = "gaussian"). This is the numeric escape hatch consumed by tooling
such as car::deltaMethod.
## S3 method for class 'beezdiscounting_tmb' coef(object, ...)## S3 method for class 'beezdiscounting_tmb' coef(object, ...)
object |
A |
... |
Unused. |
Named numeric vector.
Equal-tailed quantile credible intervals on the report-space-transformed
draws. Columns match confint.beezdiscounting_tmb(): term,
estimate, conf.low, conf.high, level.
## S3 method for class 'beezdiscounting_brms' confint( object, parm = NULL, level = 0.95, report_space = c("natural", "log10", "internal", "log"), ... )## S3 method for class 'beezdiscounting_brms' confint( object, parm = NULL, level = 0.95, report_space = c("natural", "log10", "internal", "log"), ... )
object |
A |
parm |
Optional terms (display, e.g. |
level |
Credible level (default 0.95). |
report_space |
Reporting scale. |
... |
Unused. |
A tibble.
Wald (Hessian-based) confidence intervals: estimate +/- z * se on the
internal scale, then back-transformed to report_space.
## S3 method for class 'beezdiscounting_choice' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), ... )## S3 method for class 'beezdiscounting_choice' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), ... )
object |
A |
parm |
Optional character vector for filtering. Accepts display names
( |
level |
Confidence level (default |
report_space |
|
... |
Unused. |
A tibble with columns term, estimate, conf.low, conf.high,
level.
Quantile credible intervals on the report-space-transformed draws;
columns term, estimate, conf.low, conf.high, level. beta0
stays on the identity scale across report spaces, matching
tidy.beezdiscounting_choice().
## S3 method for class 'beezdiscounting_choice_brms' confint( object, parm = NULL, level = 0.95, report_space = c("natural", "log10", "internal", "log"), ... )## S3 method for class 'beezdiscounting_choice_brms' confint( object, parm = NULL, level = 0.95, report_space = c("natural", "log10", "internal", "log"), ... )
object |
A |
parm |
Optional terms: display names ( |
level |
Credible level (default 0.95). |
report_space |
Reporting scale. |
... |
Unused. |
A tibble.
Wald (Hessian-based) confidence intervals: estimate +/- z * se on the
internal (log-k) scale, then back-transformed to report_space.
## S3 method for class 'beezdiscounting_tmb' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), ... )## S3 method for class 'beezdiscounting_tmb' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), ... )
object |
A |
parm |
Optional character vector for filtering. Accepts display names
( |
level |
Confidence level (default |
report_space |
|
... |
Unused. |
A tibble with columns term, estimate, conf.low, conf.high,
level.
For a 2-RE fit the cor_re and log_sd_re rows are reported on their
internal (atanh / log) scales and are NOT back-transformed by
report_space = "natural" (only beta_k/log_s rows are); use
VarCorr() to obtain the correlation and natural-log SDs.
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) confint(fit) confint(fit, parm = "k:(Intercept)", report_space = "natural")dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) confint(fit) confint(fit, parm = "k:(Intercept)", report_space = "natural")
A dataset containing a set of fake delay discounting responses
dd_ipdd_ip
A data frame with 600 rows and 3 columns:
Subject identifier.
Delay until the larger, later reward (in days).
Indifference point, expressed as a proportion of the larger, later reward (these synthetic values are near, but not strictly within, the unit interval).
loggamma ~ normal(1, 1) (choice sensitivity, typically 1-20) and the
optional logit-scale bias b0 ~ normal(0, 1.5); logk defaults as in
default_dd_priors() (autoscaled to the median delay when data is
supplied; delay_var names the delay column).
default_dd_choice_priors( equation = c("mazur", "exponential"), intercept = FALSE, data = NULL, delay_var = "delay", factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, autoscale = !is.null(data) )default_dd_choice_priors( equation = c("mazur", "exponential"), intercept = FALSE, data = NULL, delay_var = "delay", factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, autoscale = !is.null(data) )
equation |
|
intercept |
Include the |
data |
Optional data frame used for |
delay_var |
Delay column name in |
factors, factor_interaction, continuous_covariates
|
Fixed-effect
design on |
autoscale |
Logical; defaults to |
A brmsprior data frame.
Returns the default prior table used by fit_dd_brms(), so the defaults
can be inspected, modified row-wise, and passed back via the fitter's
prior argument. The logk location is the principled fix for k's
delay-unit dependence: with autoscale = TRUE (the default whenever
data is supplied) it centers k * median(delay) = 1 (the delay at
which the Mazur curve crosses 0.5) via normal(-log(median(x)), 2.5);
the static fallback is normal(-4.5, 2.5). The anchors used are
attached as attr(, "autoscale_info"); numeric values are formatted
with format(x, digits = 6, scientific = FALSE).
default_dd_priors( equation = c("mazur", "exponential", "green-myerson", "rachlin"), family = c("beta", "gaussian"), data = NULL, y_var = "y", x_var = "x", factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, autoscale = !is.null(data), random_effects = k ~ 1, covariance_structure = c("pdSymm", "pdDiag") )default_dd_priors( equation = c("mazur", "exponential", "green-myerson", "rachlin"), family = c("beta", "gaussian"), data = NULL, y_var = "y", x_var = "x", factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, autoscale = !is.null(data), random_effects = k ~ 1, covariance_structure = c("pdSymm", "pdDiag") )
equation |
Discounting equation (TMB-tier vocabulary). |
family |
|
data |
Optional data frame used for autoscaling. |
y_var, x_var
|
Column names in |
factors, factor_interaction, continuous_covariates
|
Fixed-effect
design on |
autoscale |
Logical; defaults to |
random_effects |
|
covariance_structure |
|
Other defaults: logs ~ normal(0, 0.5) (two-parameter equations; s is
near 1 a priori), sd(logk) ~ student_t(3, 0, 1),
phi ~ gamma(2, 0.1) (Beta precision; mean 20, far from brms's
near-improper gamma(0.01, 0.01)), and
sigma ~ student_t(3, 0, 0.25) for the Gaussian family (y is a
proportion in the unit interval, so sd(y) <= 0.5).
A brmsprior data frame, with attr(, "autoscale_info") when
autoscaling was used.
Bisection search over n_subjects for the smallest total N whose Monte
Carlo power estimate from power_discounting() reaches target_power.
The search accounts for Monte Carlo noise: at each evaluated N,
replicates are added in batches (up to n_sim_max) until the Wilson
interval for power lies wholly above or below the target; if it still
straddles the target at the cap, the decision falls back to the point
estimate and the result is flagged uncertain. The selected N and its
lower neighbor are then re-evaluated with fresh replicates before
minimality is claimed.
The returned n is an estimated minimum under Monte Carlo uncertainty
rather than an exact bound. For grant-quality reporting, rerun
power_discounting() at the returned n with a large n_sim (2000+)
and report that estimate with its Monte Carlo confidence interval.
Monotonicity assumption. Bisection presumes that power is
non-decreasing in n_subjects. Because every evaluated N is judged from
its own independent replicates (and a convergence-conditioned
denominator), a Monte Carlo fluctuation can make a lower N read "below"
when its true power is above the target, so the search may step past a
lower crossing that it never revisits. Evaluated N that contradict the
assumption (a lower N reading "above" the selected N) demote the status to
"uncertain"; N that were never evaluated cannot be checked. Widen
n_sim/n_sim_max when the reported n matters. When the target is
already met at n_range[1], that bound is likewise re-evaluated with
fresh replicates before "at_lower_bound" is reported; if the second look
does not clear the target the bound is treated as below and the bisection
proceeds upward.
find_n_discounting( target_power = 0.8, effect = list(delta_k = NULL), design = list(), n_range = c(6, 200), n_sim = 200, n_sim_max = 4 * n_sim, alpha = 0.05, df = NULL, seed = NULL, equation = c("mazur", "exponential"), family = c("sltb", "gaussian"), random_effects = k ~ 1, multi_start = FALSE, verbose = TRUE, ... )find_n_discounting( target_power = 0.8, effect = list(delta_k = NULL), design = list(), n_range = c(6, 200), n_sim = 200, n_sim_max = 4 * n_sim, alpha = 0.05, df = NULL, seed = NULL, equation = c("mazur", "exponential"), family = c("sltb", "gaussian"), random_effects = k ~ 1, multi_start = FALSE, verbose = TRUE, ... )
target_power |
Target power in (0, 1). |
effect |
Named list supplying |
design |
Named list of data-generating settings, merged over the
simulator defaults: |
n_range |
Integer bracket |
n_sim |
Replicates per evaluation batch. Smaller than the
|
n_sim_max |
Maximum replicates per evaluated N (default |
alpha |
Nominal two-sided test level. |
df |
Degrees of freedom for the Wald test's t reference. |
seed |
Optional integer seed; identical seeds give identical results. The caller's RNG state is restored on exit. |
equation |
Discounting function used for BOTH simulation and
refitting (they are always matched): |
family |
Observation family used for both simulation and refitting:
|
random_effects |
Random-effects formula passed to |
multi_start |
Passed to |
verbose |
Logical; report each evaluation. |
... |
Additional arguments passed to |
An object of class beezdiscounting_power_n: a list with
Estimated smallest total n_subjects reaching
target_power; NA when the confirmation pass contradicted the
search (status = "unresolved").
As supplied.
"confirmed" (selected N re-confirmed above target and
N - 1 below), "uncertain" (a decision relied on a point estimate,
N - 1 also cleared the target on reconfirmation, or an evaluated
lower N read above the target, so the returned N may not be
minimal), "unresolved" (the selected N failed its
own reconfirmation; n is NA), or "at_lower_bound" (the target
was already met at n_range[1] on two independent looks; smaller N
was not explored; widen n_range downward if that matters). These
labels describe a heuristic Monte Carlo decision rule (repeated
looks at ordinary Wilson intervals across several N) rather than a
formal sequential error guarantee.
Logical; TRUE when any search decision was made on
a point estimate rather than a conclusive Wilson interval, or the
status is not "confirmed"/"at_lower_bound".
Tibble of every evaluation: n_subjects,
n_sim_total, n_used, usable_fraction, power, ci_lower,
ci_upper, decision. A warning fires when any evaluation had
fewer than 95% usable fits.
Echoed inputs and effective settings.
power_discounting() for the Monte Carlo engine.
Other power-analysis:
power_discounting()
# Small search for demonstration (use larger n_sim for real planning) res <- find_n_discounting( target_power = 0.8, effect = list(delta_k = log(8)), n_range = c(6, 20), n_sim = 4, n_sim_max = 4, seed = 1, verbose = FALSE ) print(res)# Small search for demonstration (use larger n_sim for real planning) res <- find_n_discounting( target_power = 0.8, effect = list(delta_k = log(8)), n_range = c(6, 20), n_sim = 4, n_sim_max = 4, seed = 1, verbose = FALSE ) print(res)
Fits a delay-discounting model to a data set with the specified equation and fitting method.
fit_dd(dat, equation, method)fit_dd(dat, equation, method)
dat |
A data frame containing delay ( |
equation |
A character string specifying the delay-discounting equation to use. Options include:
|
method |
A character string specifying the method for fitting the model. Options include:
|
A list object of class "fit_dd", containing:
The fitted model(s).
The original dataset (dat).
The specified method (method).
data <- data.frame( id = rep(1:2, each = 6), x = rep(c(1, 7, 30, 90, 180, 365), 2), y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05, 0.85, 0.55, 0.35, 0.15, 0.1, 0.05) ) fit_dd(data, equation = "mazur", method = "two stage")data <- data.frame( id = rep(1:2, each = 6), x = rep(c(1, 7, 30, 90, 180, 365), 2), y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05, 0.85, 0.55, 0.35, 0.15, 0.1, 0.05) ) fit_dd(data, equation = "mazur", method = "two stage")
Fits the four discounting equations of fit_dd_tmb() ("mazur",
"exponential", "green-myerson", "rachlin") as Bayesian nonlinear
mixed-effects models, with the discount rate estimated on the natural-log
scale (logk, subject random intercept; k ~ 1 by default, or
k + phi ~ 1 to add a per-subject precision random effect) and the
shape exponent (logs) population-level for the two-parameter equations.
fit_dd_brms( data, y_var = "y", x_var = "x", id_var = "id", equation = c("mazur", "exponential", "green-myerson", "rachlin"), family = c("beta", "gaussian"), boundary = c("squeeze", "zoib", "error"), random_effects = k ~ 1, covariance_structure = c("pdSymm", "pdDiag"), factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, ll = NULL, response_scale = c("proportion", "percent", "amount"), prior = NULL, autoscale_priors = TRUE, chains = 4, iter = 2000, warmup = floor(iter/2), thin = 1, cores = getOption("mc.cores", 1L), seed = NA, backend = getOption("brms.backend", "rstan"), control = list(adapt_delta = 0.95), init = c("prior_center", "tmb", "random", "0"), sample_prior = "no", loo = TRUE, file = NULL, file_refit = getOption("brms.file_refit", "on_change"), verbose = 1, ... )fit_dd_brms( data, y_var = "y", x_var = "x", id_var = "id", equation = c("mazur", "exponential", "green-myerson", "rachlin"), family = c("beta", "gaussian"), boundary = c("squeeze", "zoib", "error"), random_effects = k ~ 1, covariance_structure = c("pdSymm", "pdDiag"), factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, ll = NULL, response_scale = c("proportion", "percent", "amount"), prior = NULL, autoscale_priors = TRUE, chains = 4, iter = 2000, warmup = floor(iter/2), thin = 1, cores = getOption("mc.cores", 1L), seed = NA, backend = getOption("brms.backend", "rstan"), control = list(adapt_delta = 0.95), init = c("prior_center", "tmb", "random", "0"), sample_prior = "no", loo = TRUE, file = NULL, file_refit = getOption("brms.file_refit", "on_change"), verbose = 1, ... )
data |
Long-format data frame (one row per subject-delay). |
y_var, x_var, id_var
|
Column names (canonical defaults), as in
|
equation |
Discounting equation. |
family |
|
boundary |
Boundary handling for the beta family (see Details). |
random_effects |
|
covariance_structure |
For |
factors, factor_interaction, continuous_covariates
|
Fixed-effect
design on |
ll, response_scale
|
Response coercion, as in |
prior |
Optional |
autoscale_priors |
Anchor the |
chains, iter, warmup, thin, cores, seed, backend, control, sample_prior
|
MCMC settings passed to |
init |
|
loo |
Compute and store |
file, file_refit
|
Passed to |
verbose |
0 (silent), 1 (messages), 2 (full Stan output). |
... |
Passed through to |
family = "beta" (default) uses Beta(link = "identity") with the mean
squished into (1e-6, 1 - 1e-6), the closest brms analog of the TMB
SLT-beta (family = "sltb" has no brms equivalent and errors with this
pointer). Boundary observations (y exactly 0 or 1) are handled per
boundary: "squeeze" (default) applies the Smithson-Verkuilen
transform y* = (y (N - 1) + 0.5) / N to all responses (message reports
the boundary count); "zoib" switches to zero_one_inflated_beta
(statistically more honest, but it changes the estimand: k then describes
interior responses only); "error" refuses to fit.
family = "gaussian" matches fit_dd_tmb(family = "gaussian")
wherever the TMB template's mu clamp into [1e-6, 1 - 1e-6] does not
bind (everywhere except extreme decay underflow).
An object of class beezdiscounting_brms: model (posterior
medians/SDs on the estimation scale under TMB names beta_k/log_s,
plus variance_components), brmsfit, subject_pars
(id, k, k_lower, k_upper, plus phi/phi_lower/phi_upper
for k + phi ~ 1), converged (Rhat < 1.01, no
divergences, bulk ESS >= 400), mcmc_info, loo, data,
param_info, priors, autoscale_info.
fit_dd_tmb(); default_dd_priors().
Fits a trial-level binary choice model from two complementary perspectives.
The structural model estimates the discount rate k directly
from choices via the scale-invariant relative-value comparison
logit P(LL) = beta0 + gamma * ((ll/ss) * D(k, delay) - 1),
k = exp(X beta_k + sigma_u u). The descriptive model (Young 2018) forgoes a
discount function and instead characterises choices via separate magnitude and
delay sensitivities with optional correlated per-subject random slopes.
fit_dd_choice( data, mode = c("structural", "descriptive"), id_var = "id", ss_var = "ss_amount", ll_var = "ll_amount", delay_var = "delay", choice_var = "choice", equation = c("mazur", "exponential"), intercept = FALSE, predictors = NULL, random_slopes = TRUE, factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, start_values = NULL, tmb_control = list(iter_max = 1000, eval_max = 2000), multi_start = TRUE, verbose = 1, ... )fit_dd_choice( data, mode = c("structural", "descriptive"), id_var = "id", ss_var = "ss_amount", ll_var = "ll_amount", delay_var = "delay", choice_var = "choice", equation = c("mazur", "exponential"), intercept = FALSE, predictors = NULL, random_slopes = TRUE, factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, start_values = NULL, tmb_control = list(iter_max = 1000, eval_max = 2000), multi_start = TRUE, verbose = 1, ... )
data |
Trial-level data frame (see the |
mode |
|
id_var, ss_var, ll_var, delay_var, choice_var
|
Column names. |
equation |
|
intercept |
Logical; include the choice-bias |
predictors |
Descriptive ( |
random_slopes |
Descriptive only: logical; |
factors, factor_interaction, continuous_covariates
|
Between-subject design
on |
start_values, tmb_control, multi_start, verbose, ...
|
As in |
The structural model (mode = "structural") parameterises choices
through a classical discount function (Mazur hyperbolic or exponential) and
shares the IP family's k/emmeans contract: k estimates and estimated
marginal means are accessible via get_dd_param_emms() and
get_dd_comparisons(). The descriptive model (mode = "descriptive")
follows Young (2018): it regresses binary choice on
log(ll_amount / ss_amount) (magnitude sensitivity) and log(delay + 1)
(delay sensitivity) with no assumed discount function. When
random_slopes = TRUE (default) each subject receives correlated random
slopes on these two predictors; the primary inferential targets are the
random-effect covariance (via nlme::VarCorr()) and per-subject slopes
(via nlme::ranef()), not emmeans.
An object of class beezdiscounting_choice.
simulate_dd_choice() for data generation; nlme::VarCorr() and
nlme::ranef() for descriptive-mode random-effect output;
get_dd_param_emms() and get_dd_comparisons() for structural-mode
emmeans.
# Structural model: estimate a discount rate k from binary choices sim_s <- simulate_dd_choice(n_subjects = 30, mode = "structural", seed = 1) fit_s <- fit_dd_choice(sim_s, mode = "structural", equation = "mazur") summary(fit_s) # Descriptive (Young 2018) model: correlated per-subject magnitude/delay slopes sim_d <- simulate_dd_choice(n_subjects = 30, mode = "descriptive", seed = 1) fit_d <- fit_dd_choice(sim_d, mode = "descriptive") summary(fit_d) nlme::VarCorr(fit_d)# Structural model: estimate a discount rate k from binary choices sim_s <- simulate_dd_choice(n_subjects = 30, mode = "structural", seed = 1) fit_s <- fit_dd_choice(sim_s, mode = "structural", equation = "mazur") summary(fit_s) # Descriptive (Young 2018) model: correlated per-subject magnitude/delay slopes sim_d <- simulate_dd_choice(n_subjects = 30, mode = "descriptive", seed = 1) fit_d <- fit_dd_choice(sim_d, mode = "descriptive") summary(fit_d) nlme::VarCorr(fit_d)
The TMB structural likelihood of fit_dd_choice():
logit P(LL) = [b0] + gamma * ((ll/ss) * D(k, delay) - 1) with choice
sensitivity gamma = exp(loggamma) and the discount rate k = exp(logk) carrying the subject random intercept. With
bernoulli("logit") the brms nonlinear formula IS the logit, so the
likelihood matches TMB exactly.
fit_dd_choice_brms( data, mode = c("structural", "descriptive"), id_var = "id", ss_var = "ss_amount", ll_var = "ll_amount", delay_var = "delay", choice_var = "choice", equation = c("mazur", "exponential"), intercept = FALSE, factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, prior = NULL, autoscale_priors = TRUE, chains = 4, iter = 2000, warmup = floor(iter/2), thin = 1, cores = getOption("mc.cores", 1L), seed = NA, backend = getOption("brms.backend", "rstan"), control = list(adapt_delta = 0.95), init = c("prior_center", "tmb", "random", "0"), sample_prior = "no", loo = TRUE, file = NULL, file_refit = getOption("brms.file_refit", "on_change"), verbose = 1, ... )fit_dd_choice_brms( data, mode = c("structural", "descriptive"), id_var = "id", ss_var = "ss_amount", ll_var = "ll_amount", delay_var = "delay", choice_var = "choice", equation = c("mazur", "exponential"), intercept = FALSE, factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, prior = NULL, autoscale_priors = TRUE, chains = 4, iter = 2000, warmup = floor(iter/2), thin = 1, cores = getOption("mc.cores", 1L), seed = NA, backend = getOption("brms.backend", "rstan"), control = list(adapt_delta = 0.95), init = c("prior_center", "tmb", "random", "0"), sample_prior = "no", loo = TRUE, file = NULL, file_refit = getOption("brms.file_refit", "on_change"), verbose = 1, ... )
data |
Long-format trial-level data (one row per choice). |
mode |
|
id_var, ss_var, ll_var, delay_var, choice_var
|
Column names, as in
|
equation |
|
intercept |
Include the logit-scale bias term |
factors, factor_interaction, continuous_covariates
|
Between-subject
fixed-effect design on |
prior |
Optional |
autoscale_priors |
Anchor the |
chains, iter, warmup, thin, cores, seed, backend, control, sample_prior
|
MCMC settings passed to |
init |
|
loo |
Compute and store |
file, file_refit
|
Passed to |
verbose |
0 (silent), 1 (messages), 2 (full Stan output). |
... |
Passed through to |
v1 implements mode = "structural" only: the descriptive (Young 2018)
model is a plain logistic GLMM expressible directly with
brms::brm().
An object of class beezdiscounting_choice_brms. Coefficients
are posterior medians on the estimation scale under the TMB names
(one beta_k per design column, log_gamma, and beta0 when
intercept = TRUE).
fit_dd_choice(); default_dd_choice_priors();
get_dd_param_emms() and get_dd_comparisons() for draws-based
marginal means and contrasts of k.
Linearizes D = 1/(1 + k t) to ln(1/D - 1) - ln(t) = ln(k) + e so that
ln(k) has a closed-form per-subject estimator (the geometric mean of
(1/D - 1)/t), a one-way random-effects model on the transformed scale has
closed-form MLEs, and condition means can be compared with an exact F-test
(anova.beezdiscounting_linear());
the test is exact when the random-effects variance estimate g-hat > 0, and
when g-hat = 0 the statistic is still reported, with a warning. No
numerical optimization is involved.
fit_dd_linear( data, y_var = "y", x_var = "x", id_var = "id", factors = NULL, response_scale = c("proportion", "percent", "amount"), ll = NULL, boundary = c("clamp", "drop", "error"), eps = NULL, conf_level = 0.95 )fit_dd_linear( data, y_var = "y", x_var = "x", id_var = "id", factors = NULL, response_scale = c("proportion", "percent", "amount"), ll = NULL, boundary = c("clamp", "drop", "error"), eps = NULL, conf_level = 0.95 )
data |
Long data frame with subject id, delay, and indifference point columns. |
y_var, x_var, id_var
|
Column names for indifference point, delay, subject. |
factors |
Optional name of ONE categorical column giving the experimental condition. Must be between-subject: every id appears under exactly one level; frames with an id under several levels are rejected because the exact F-test assumes independent units per condition. |
response_scale, ll
|
As in |
boundary |
How to treat |
eps |
Where exact 0 / 1 are placed under |
conf_level |
Confidence level for per-subject ln(k) intervals (a single
number strictly between 0 and 1); also the default |
Indifference points at exactly 0 or 1 are undefined under the transform;
by default they are moved to eps and 1 - eps respectively
(boundary = "clamp"). Only exact 0 and 1 are touched — interior points,
however close to a bound, are used as observed. This is a package decision;
the paper does not address boundary values. Use boundary = "drop" or
"error" when you would rather not impute them.
An object of class beezdiscounting_linear: a list with subjects
(per-unit tibble of ln k estimates, intervals and log-likelihoods), re
(closed-form random-effects MLEs mu, sigma2, g, or NULL if the
design is unbalanced), design (factor name and levels), transform
(boundary bookkeeping; the counts refer to the retained units), data
(the long frame: id, x, y, the factor, and the derived columns
condition, unit, y_lin, d_used, log_jac; factors may not name
one of the derived columns, and other user columns are not carried),
conf_level, and call.
Hinds, D., Tegge, A. N., Stein, J. S., LaConte, S. M., McClure, S. M., & Ferreira, M. A. R. (2026). To linearize or not to linearize: That is the Mazur delay discounting question. Journal of Mathematical Psychology, 130, 103006. doi:10.1016/j.jmp.2026.103006
beezdiscounting_linear-methods for the S3 surface,
plot.beezdiscounting_linear(), anova.beezdiscounting_linear(),
simulate_dd_linear(), and vignette("linearized-mazur").
fit <- fit_dd_linear(dd_ip) fitfit <- fit_dd_linear(dd_ip) fit
Fits a 1-parameter discounting model (Mazur hyperbolic or exponential) 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.
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"), ... )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"), ... )
data |
Long data frame with subject id, delay, and indifference proportion columns. |
y_var, x_var, id_var
|
Column names (defaults |
equation |
One of |
family |
Observation family: |
random_effects |
RE formula: |
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 |
response_scale |
One of |
start_values |
Optional named list overriding defaults. |
tmb_control |
Optimizer control list. |
multi_start |
Logical; if |
verbose |
Integer verbosity (0 silent, 1 progress, 2 debug). |
covariance_structure |
Covariance for a 2-D random effect
( |
... |
Reserved. |
An object of class beezdiscounting_tmb with components:
The matched call.
Normalized optimizer result (par, objective,
convergence, message).
List of coefficients, se, and variance_components.
TMB sdreport object (or NULL if SE computation failed).
Logical positive-definiteness of the Hessian.
Model metadata (equation, family, dimensions, factor spec, parsed random effects).
Fixed-effect design (X, rhs, contrasts).
Data frame of subject-level parameters. For a 1-RE fit
(k ~ 1) the columns are id, u_i, k; for a phi-target 2-RE fit
(k + phi ~ 1) they are id, re_k, re_phi, k, phi; for an s-target
2-RE fit (k + s ~ 1, GM/Rachlin) they are id, re_k, re_s, k, s
where s is soft-clamped toward (0.05, 20).
Fit statistics.
Convergence / SE-availability flags.
Character vector of optimizer warnings.
The single filtered model frame (id/x/y + retained design columns), row-aligned with the design matrix.
The validated frame before complete-casing.
Scale-coercion/clamping audit list.
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.
# 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# 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
Fitted values for a beezdiscounting_choice fit
## S3 method for class 'beezdiscounting_choice' fitted(object, level = c("subject", "population"), ...)## S3 method for class 'beezdiscounting_choice' fitted(object, level = c("subject", "population"), ...)
object |
A |
level |
|
... |
Unused. |
Numeric vector of fitted choice probabilities, length nobs(object).
Fitted values for a beezdiscounting_tmb fit
## S3 method for class 'beezdiscounting_tmb' fitted(object, level = c("subject", "population"), ...)## S3 method for class 'beezdiscounting_tmb' fitted(object, level = c("subject", "population"), ...)
object |
A |
level |
|
... |
Unused. |
Numeric vector of fitted indifference proportions, length nobs(object).
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, verbose = 0) head(fitted(fit))dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, verbose = 0) head(fitted(fit))
An example dataset containing four participants' data (two typical discounting patterns and two patterns suggesting potential misattention to the task).
five.fivetrial_ddfive.fivetrial_dd
Example Qualtrics output
An example dataset containing four participants' data.
five.fivetrial_pdfive.fivetrial_pd
Example Qualtrics output
Extract fixed effects from a structural choice model
## S3 method for class 'beezdiscounting_choice' fixef(object, ...)## S3 method for class 'beezdiscounting_choice' fixef(object, ...)
object |
A |
... |
Unused. |
Named numeric vector (identical to coef()).
Extract fixed effects from a TMB discounting model
## S3 method for class 'beezdiscounting_tmb' fixef(object, ...)## S3 method for class 'beezdiscounting_tmb' fixef(object, ...)
object |
A |
... |
Unused. |
Named numeric vector (identical to coef()).
Generate fake MCQ data
generate_data_mcq(n_ids = 100, n_items = 27, seed = 1234, prop_na = 0)generate_data_mcq(n_ids = 100, n_items = 27, seed = 1234, prop_na = 0)
n_ids |
Number of subjectids |
n_items |
Number of trials |
seed |
Random seed; identical seeds give identical results. The caller's RNG state is restored on exit. |
prop_na |
Proportion of NAs in the entire data set |
Dataframe of subjectid, questionid, and response
generate_data_mcq(n_ids = 2, n_items = 27, prop_na = .01)generate_data_mcq(n_ids = 2, n_items = 27, prop_na = .01)
k
Computes factor-level contrasts of the discount rate k from a fitted
beezdiscounting_tmb model (or a structural
beezdiscounting_choice fit). k is linear in the fixed-effect
coefficients on the natural-log scale (log k = X beta_k), so each
contrast is a linear combination of beta_k with a Wald standard error
from the beta_k block of TMB::sdreport()'s fixed-effect
covariance. Contrasts are reported on the log10 scale and,
optionally, as multiplicative ratios (ratio = exp(est_log)). The
returned container mirrors the beezdemand beezdemand_comparison shape,
so tidy.beezdiscounting_comparison() gives a flat, cross-backend frame.
Bayesian fits (beezdiscounting_brms,
beezdiscounting_choice_brms) compute per-draw contrasts over the
same reference grid: posterior medians with quantile credible intervals
and post.prob in place of adjusted p-values (no multiplicity
adjustment; the joint posterior already encodes contrast dependence).
get_dd_comparisons( fit, compare_specs = NULL, contrast_type = c("pairwise", "trt.vs.ctrl"), contrast_by = NULL, adjust = "holm", at = NULL, ci_level = 0.95, report_ratios = TRUE, ... )get_dd_comparisons( fit, compare_specs = NULL, contrast_type = c("pairwise", "trt.vs.ctrl"), contrast_by = NULL, adjust = "holm", at = NULL, ci_level = 0.95, report_ratios = TRUE, ... )
fit |
A |
compare_specs |
Optional one-sided formula naming the factor subset to
contrast (e.g. |
contrast_type |
Character. |
contrast_by |
Optional |
adjust |
Character. P-value adjustment method; must be one of
|
at |
Named list specifying factor levels and/or continuous-covariate
values to condition on, as in |
ci_level |
Numeric. Confidence level for intervals (default 0.95). |
report_ratios |
Logical. If |
... |
Additional arguments (reserved; |
A beezdiscounting_comparison object: a list with a single
element k, itself a list with emmeans (the EMM tibble from
get_dd_param_emms()), contrasts_log10 (columns contrast,
estimate, std.error, statistic, df,
conf.low, conf.high, p.value), and (if
report_ratios) contrasts_ratio (columns contrast,
ratio, conf.low, conf.high, p.value). When
contrast_by is active, the contrast tables gain leading by-column(s)
before contrast. Attributes backend, adjustment_method,
compare_specs_used, contrast_type_used, and
contrast_by_used describe the call.
tidy.beezdiscounting_comparison() for the cross-backend frame.
sim <- simulate_dd_ip( n_subjects = 30, n_conditions = 2, delta_k = c(0, log(3)), seed = 1 ) fit <- fit_dd_tmb(sim, factors = "condition", verbose = 0) get_dd_comparisons(fit, contrast_type = "pairwise")sim <- simulate_dd_ip( n_subjects = 30, n_conditions = 2, delta_k = c(0, log(3)), seed = 1 ) fit <- fit_dd_tmb(sim, factors = "condition", verbose = 0) get_dd_comparisons(fit, contrast_type = "pairwise")
k
Computes estimated marginal means (EMMs) of the discount rate k from a
fitted beezdiscounting_tmb model (or a structural
beezdiscounting_choice fit, which shares the log k design).
EMMs are computed on the
log k scale (linear in the fixed-effect coefficients) using the
averaging-matrix reference grid, then back-transformed with exp() so
that k = exp(k_log). Standard errors use the beta_k block of
TMB::sdreport()'s fixed-effect covariance; intervals are Wald on the
log scale and exponentiated. Bayesian fits (beezdiscounting_brms,
beezdiscounting_choice_brms) use the same reference grid with
draws-based summaries: posterior medians and equal-tailed quantile
intervals, no delta method.
get_dd_param_emms(fit, factors_in_emm = NULL, at = NULL, ci_level = 0.95, ...)get_dd_param_emms(fit, factors_in_emm = NULL, at = NULL, ci_level = 0.95, ...)
fit |
A |
factors_in_emm |
Character vector of factors to retain in the EMM
reference grid. A strict subset marginalizes the omitted factors with equal
weights across the full crossing of their levels (emmeans' default
|
at |
Named list specifying factor levels and/or continuous-covariate
values for conditional EMMs (one numeric per covariate; multiple values
warn and use the first). |
ci_level |
Numeric confidence level for intervals (default 0.95). |
... |
Additional arguments (currently unused). |
A tibble with columns level, k, k_log,
std.error, conf.low, conf.high. k_log is the
marginal mean on the log k scale; k = exp(k_log);
std.error is the SE of k_log; the intervals are on the
k (natural) scale.
sim <- simulate_dd_ip( n_subjects = 30, n_conditions = 2, delta_k = c(0, log(3)), seed = 1 ) fit <- fit_dd_tmb(sim, factors = "condition", verbose = 0) get_dd_param_emms(fit)sim <- simulate_dd_ip( n_subjects = 30, n_conditions = 2, delta_k = c(0, log(3)), seed = 1 ) fit <- fit_dd_tmb(sim, factors = "condition", verbose = 0) get_dd_param_emms(fit)
Get an internal instrument design table
get_lookup_table(items = NULL, instrument = NULL)get_lookup_table(items = NULL, instrument = NULL)
items |
Deprecated. Number of MCQ items (27 or 21), kept as a
back-compatible alias for |
instrument |
Instrument key: |
Data frame with the complete item design. MCQ tables:
questionid, magnitude, kindiff, k_rank, ss_amount, ll_amount, delay
(days). PDQ table: questionid, block, h_rank, overall_rank (the
item's position in the pooled overall ladder used by the
overall_h extension in score_pdq()), sc_amount, lu_amount,
prob, theta (odds against), hindiff.
get_lookup_table() get_lookup_table(items = 21) get_lookup_table(instrument = "pdq")get_lookup_table() get_lookup_table(items = 21) get_lookup_table(instrument = "pdq")
The dd glance columns with backend = "brms" and
logLik/AIC/BIC = NA_real_ by design, plus the Bayesian comparison
and diagnostic currency (elpd_loo, p_loo, looic, rhat_max,
ess_bulk_min, num_divergent).
## S3 method for class 'beezdiscounting_brms' glance(x, ...)## S3 method for class 'beezdiscounting_brms' glance(x, ...)
x |
A |
... |
Unused. |
A one-row tibble.
Returns a one-row tibble of model-level summary statistics suitable for use
with broom::glance() workflows and multi-model comparison tables.
## S3 method for class 'beezdiscounting_choice' glance(x, ...)## S3 method for class 'beezdiscounting_choice' glance(x, ...)
x |
A |
... |
Unused. |
A one-row tibble with columns model_class, backend, mode,
equation, nobs, n_subjects, n_random_effects, converged,
logLik, AIC, BIC.
Glance at a beezdiscounting_choice_brms model
## S3 method for class 'beezdiscounting_choice_brms' glance(x, ...)## S3 method for class 'beezdiscounting_choice_brms' glance(x, ...)
x |
A |
... |
Unused. |
A one-row tibble.
Returns a one-row tibble of model-level summary statistics suitable for use
with broom::glance() workflows and multi-model comparison tables.
## S3 method for class 'beezdiscounting_tmb' glance(x, ...)## S3 method for class 'beezdiscounting_tmb' glance(x, ...)
x |
A |
... |
Unused. |
A one-row tibble with columns model_class, backend, equation,
family, nobs, n_subjects, n_random_effects, converged, logLik,
AIC, BIC.
Reshape MCQ data long to wide
long_to_wide_mcq(dat, q_col = "questionid", ans_col = "response")long_to_wide_mcq(dat, q_col = "questionid", ans_col = "response")
dat |
Long format MCQ |
q_col |
Name of the question column (default is "questionid") |
ans_col |
Name of the answer column (default is "response") |
Wide format data frame
long_to_wide_mcq(mcq27)long_to_wide_mcq(mcq27)
Reshape MCQ data from long to wide (as used in the 21- and 27-Item Monetary Choice Questionnaire Automated Scorer)
long_to_wide_mcq_excel(dat, subj_col = "subjectid", ans_col = "response")long_to_wide_mcq_excel(dat, subj_col = "subjectid", ans_col = "response")
dat |
Long format MCQ data |
subj_col |
Character column name of subject ids |
ans_col |
Character column name of responses |
Wide format MCQ data that can be used in the Excel Automated Scorers
long_to_wide_mcq_excel(generate_data_mcq(2))long_to_wide_mcq_excel(generate_data_mcq(2))
Reshapes long-form Monetary Choice Questionnaire (MCQ) responses into
the per-trial smaller-sooner versus larger-later choice frame consumed by
fit_dd_choice(), joining each questionid to the canonical item design
(immediate amount, delayed amount, delay) bundled in the lookup table (see
get_lookup_table(); Kirby, Petry, & Bickel (1999) for the 27-item MCQ,
or Kirby & Maraković (1996, Table 1) for the 21-item MCQ).
mcq_to_choice( responses, items = 27, id_var = "subjectid", question_var = "questionid", response_var = "response" )mcq_to_choice( responses, items = 27, id_var = "subjectid", question_var = "questionid", response_var = "response" )
responses |
Long-form data frame with one row per MCQ item per subject,
holding the columns named by |
items |
Number of MCQ items: 27 (Kirby, Petry, & Bickel, 1999) or 21 (Kirby & Maraković, 1996). Default is 27. |
id_var, question_var, response_var
|
Column names in |
Unknown or non-coercible question ids raise an error rather than
silently producing unmatched rows. Question ids 1-21 are valid in both
the 21- and 27-item designs, so passing the wrong items does not
error; it silently returns the wrong amounts/delays for those ids.
Make sure items matches the instrument actually administered. Ragged
input is allowed (subjects need not have all items) and NA
responses are preserved (they are complete-cased by fit_dd_choice()).
For the strict scorer see score_mcq().
A tibble with columns id (character), ss_amount,
ll_amount, delay (days), and choice (0/1, 1 = chose LL), in the
input row order. Ready to pass to fit_dd_choice().
fit_dd_choice(), score_mcq(), get_lookup_table(),
mcq27_to_choice()
ch <- mcq_to_choice(mcq27, items = 27) head(ch) # feeds directly into the structural choice model (requires TMB): # fit_dd_choice(ch, mode = "structural", equation = "mazur")ch <- mcq_to_choice(mcq27, items = 27) head(ch) # feeds directly into the structural choice model (requires TMB): # fit_dd_choice(ch, mode = "structural", equation = "mazur")
A dataset containing two illustrative participants' 21-item MCQ (Kirby & Marakovic, 1996) responses: subject 1 responds like the median subject in Kirby & Marakovic (1996) (switching between the eighth and ninth ladder items, overall k of about 0.007) and subject 2 always chooses the larger delayed reward. These responses are constructed for illustration only, not drawn from an empirical sample.
mcq21mcq21
Long-form data.frame with columns: subjectid, questionid, response.
Constructed in data-raw/mcq21_data.R.
A dataset containing two participants' data (same data as in the paper by Kaplan et al., 2016)
mcq27mcq27
Long-form data.frame with columns: subjectid, questionid, response.
Reshapes long-form Monetary Choice Questionnaire (MCQ) responses into
the per-trial smaller-sooner versus larger-later choice frame consumed by
fit_dd_choice(), joining each questionid to the canonical item design
(immediate amount, delayed amount, delay) bundled in the lookup table (see
get_lookup_table(); Kirby, Petry, & Bickel (1999) for the 27-item MCQ,
or Kirby & Maraković (1996, Table 1) for the 21-item MCQ).
mcq27_to_choice( responses, id_var = "subjectid", question_var = "questionid", response_var = "response" )mcq27_to_choice( responses, id_var = "subjectid", question_var = "questionid", response_var = "response" )
responses |
Long-form data frame with one row per MCQ item per subject,
holding the columns named by |
id_var, question_var, response_var
|
Column names in |
Unknown or non-coercible question ids raise an error rather than
silently producing unmatched rows. Question ids 1-21 are valid in both
the 21- and 27-item designs, so passing the wrong items does not
error; it silently returns the wrong amounts/delays for those ids.
Make sure items matches the instrument actually administered. Ragged
input is allowed (subjects need not have all items) and NA
responses are preserved (they are complete-cased by fit_dd_choice()).
For the strict scorer see score_mcq().
A tibble with columns id (character), ss_amount,
ll_amount, delay (days), and choice (0/1, 1 = chose LL), in the
input row order. Ready to pass to fit_dd_choice().
fit_dd_choice(), score_mcq27(), get_lookup_table(),
mcq_to_choice()
ch <- mcq27_to_choice(mcq27) head(ch) # feeds directly into the structural choice model (requires TMB): # fit_dd_choice(ch, mode = "structural", equation = "mazur")ch <- mcq27_to_choice(mcq27) head(ch) # feeds directly into the structural choice model (requires TMB): # fit_dd_choice(ch, mode = "structural", equation = "mazur")
A dataset containing two illustrative participants' Probability Discounting Questionnaire (Madden, Petry, & Johnson, 2009) responses: subject 1 chooses the guaranteed reward through the fifth h rank of every block and the risky reward after (block h values of about 1.22), and subject 2 always chooses the risky reward. These responses are constructed for illustration only, not drawn from an empirical sample.
pdqpdq
Long-form data.frame with columns: subjectid, questionid, response (0 = guaranteed, 1 = risky).
Constructed in data-raw/pdq_data.R.
Reshapes long-form Probability Discounting Questionnaire (PDQ) responses
into a per-trial certain-versus-risky choice frame, joining each
questionid to the canonical item design (certain amount, probabilistic
amount, probability, and odds against theta = (1 - p)/p) from
Madden, Petry, & Johnson (2009; see get_lookup_table()).
pdq_to_choice( responses, id_var = "subjectid", question_var = "questionid", response_var = "response" )pdq_to_choice( responses, id_var = "subjectid", question_var = "questionid", response_var = "response" )
responses |
Long-form data frame with one row per PDQ item per
subject, holding the columns named by |
id_var, question_var, response_var
|
Column names in |
Unknown or non-coercible question ids raise an error rather
than silently producing unmatched rows. Ragged input is allowed
(subjects need not have all items) and NA responses are preserved.
For the strict scorer see score_pdq().
A tibble with columns id (character),
sc_amount, lu_amount, prob, theta (odds against winning), and
choice (0/1, 1 = chose the risky reward), in the input row
order.
score_pdq(), get_lookup_table(), mcq_to_choice()
ch <- pdq_to_choice(pdq) head(ch)ch <- pdq_to_choice(pdq) head(ch)
Plots the delay-discounting data with the fitted model overlaid.
plot_dd( fit_dd_object, xlabel = "Delay", ylabel = "Indifference Point", title = "", logx = TRUE )plot_dd( fit_dd_object, xlabel = "Delay", ylabel = "Indifference Point", title = "", logx = TRUE )
fit_dd_object |
A fitted delay-discounting model object of class |
xlabel |
A character string specifying the label for the x-axis. Default is |
ylabel |
A character string specifying the label for the y-axis. Default is |
title |
A character string specifying the plot title. Default is |
logx |
Logical. If |
A ggplot object representing the fitted model and data.
data <- data.frame( id = rep(1:2, each = 6), x = rep(c(1, 7, 30, 90, 180, 365), 2), y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05, 0.85, 0.55, 0.35, 0.15, 0.1, 0.05) ) fit <- fit_dd(data, equation = "mazur", method = "mean") plot_dd(fit)data <- data.frame( id = rep(1:2, each = 6), x = rep(c(1, 7, 30, 90, 180, 365), 2), y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05, 0.85, 0.55, 0.35, 0.15, 0.1, 0.05) ) fit <- fit_dd(data, equation = "mazur", method = "mean") plot_dd(fit)
QQ plot of the estimated subject random-effect deviates against a normal
reference (the standard check on the Gaussian random-effects assumption).
Methods exist for the TMB indifference-point (fit_dd_tmb()) and choice
(fit_dd_choice()) models. Bayesian fits are intentionally excluded; use
brms::pp_check() and MCMC diagnostics there instead. The plotted deviates
are shrunken (empirical-Bayes) estimates, so the normal reference is
approximate.
## S3 method for class 'beezdiscounting_tmb' plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdiscounting_choice' plot_qq(object, which = NULL, ...)## S3 method for class 'beezdiscounting_tmb' plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdiscounting_choice' plot_qq(object, which = NULL, ...)
object |
A fitted |
which |
Optional character vector selecting random-effect terms to show. |
... |
Unused. |
A ggplot2::ggplot object.
Visualize a fitted fit_dd_brms() model. type = "population" (default)
draws the posterior-median discount curve with a credible band for the
population conditional mean (random effects set to zero; a credible band
rather than a posterior-predictive band) over the observed (boundary-squeezed)
indifference points; type = "individual" adds per-subject median curves;
type = "parameters" shows the posterior subject-k caterpillar;
type = "resid" plots standardized residuals against fitted values.
## S3 method for class 'beezdiscounting_brms' plot( x, type = c("population", "individual", "parameters", "resid"), ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )## S3 method for class 'beezdiscounting_brms' plot( x, type = c("population", "individual", "parameters", "resid"), ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )
x |
A |
type |
One of |
ids |
Optional subset of subject ids for |
at |
Optional named list conditioning the population curve on factor levels / covariate values. |
n_points |
Number of delay points in the curve grid. |
x_trans |
Delay-axis scale: |
show_observed |
Overlay the observed indifference points. |
... |
Unused. |
A ggplot2::ggplot object.
Visualize a fitted fit_dd_choice() model. For a structural fit,
type = "population" (default) draws the implied discount curve from the
estimated discount rate, type = "individual" adds the per-subject curves,
and type = "parameters" shows the subject-k distribution. For both
structural and descriptive (Young 2018) fits, type = "calibration" plots
the observed choice proportion against the fitted P(choose LL), binned by
fitted probability (a likelihood-geometry-agnostic goodness-of-fit view).
Descriptive fits have no structural discount rate, so the curve/parameter
types are unavailable.
## S3 method for class 'beezdiscounting_choice' plot( x, type = NULL, ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )## S3 method for class 'beezdiscounting_choice' plot( x, type = NULL, ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )
x |
A |
type |
One of |
ids |
Optional subset of subject ids for |
at |
Optional named list conditioning the curve on factor levels / covariate values. |
n_points |
Number of delay points in the curve grid. |
x_trans |
Delay-axis scale: |
show_observed |
Unused for the implied discount curve (kept for a consistent signature across tiers). |
... |
Unused. |
A ggplot2::ggplot object.
ch <- simulate_dd_choice(n_subjects = 12, seed = 1) fit <- fit_dd_choice(ch, equation = "mazur") plot(fit) plot(fit, type = "calibration")ch <- simulate_dd_choice(n_subjects = 12, seed = 1) fit <- fit_dd_choice(ch, equation = "mazur") plot(fit) plot(fit, type = "calibration")
Visualize a fitted fit_dd_choice_brms() model. type = "population"
(default) draws the posterior implied discount curve with a credible band;
type = "individual" adds per-subject (posterior-median) curves;
type = "parameters" shows the posterior subject-k caterpillar; and
type = "calibration" plots observed choice proportion against fitted
P(choose LL), binned by fitted probability.
## S3 method for class 'beezdiscounting_choice_brms' plot( x, type = c("population", "individual", "calibration", "parameters"), ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )## S3 method for class 'beezdiscounting_choice_brms' plot( x, type = c("population", "individual", "calibration", "parameters"), ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )
x |
A |
type |
One of |
ids |
Optional subset of subject ids for |
at |
Optional named list conditioning the population curve on factor levels / covariate values. |
n_points |
Number of delay points in the curve grid. |
x_trans |
Delay-axis scale: |
show_observed |
Unused for the implied discount curve. |
... |
Unused. |
A ggplot2::ggplot object.
Forest plot of the pairwise (or treatment-vs-control) contrasts from
get_dd_comparisons(). type = "ratio" (default) shows each k ratio on a
log axis with a reference line at 1; type = "difference" shows the
difference in log10 k with a reference line at 0. A contrast is flagged when
its interval excludes the null (a backend-agnostic rule that applies to
both the frequentist (TMB) and Bayesian (brms) backends).
## S3 method for class 'beezdiscounting_comparison' plot(x, type = c("ratio", "difference"), ...)## S3 method for class 'beezdiscounting_comparison' plot(x, type = c("ratio", "difference"), ...)
x |
A |
type |
|
... |
Unused. |
A ggplot2::ggplot object.
sim <- simulate_dd_ip(n_subjects = 30, n_conditions = 2, delta_k = c(0, 0.5), seed = 1) fit <- fit_dd_tmb(sim, equation = "mazur", factors = "condition") cmp <- get_dd_comparisons(fit) plot(cmp)sim <- simulate_dd_ip(n_subjects = 30, n_conditions = 2, delta_k = c(0, 0.5), seed = 1) fit <- fit_dd_tmb(sim, equation = "mazur", factors = "condition") cmp <- get_dd_comparisons(fit) plot(cmp)
Visualize a fitted fit_dd_linear() model. type = "population" (default)
draws the hyperbola implied by each condition's geometric-mean discount rate
exp(mu) over the observed indifference points (one curve per condition when
the fit has a factor); type = "individual" adds each subject's own
closed-form hyperbola 1 / (1 + k_i x) as thin lines; type = "transformed" shows the linearization itself, ln(1/D - 1) against
ln(delay) per subject with a slope-1 line at that subject's ln k;
type = "parameters" shows the subject ln k estimates with their
t-intervals (by condition, with the condition mean mu and its interval
from confint() overlaid, when the fit has a factor; otherwise ordered as a
caterpillar), or the same quantities as k on a log10 axis with
k_scale = "log10"; type = "resid" plots the standardized residual on the
transformed scale against ln(delay).
## S3 method for class 'beezdiscounting_linear' plot( x, type = c("population", "individual", "transformed", "parameters", "resid"), ids = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, k_scale = c("ln", "log10"), ... )## S3 method for class 'beezdiscounting_linear' plot( x, type = c("population", "individual", "transformed", "parameters", "resid"), ids = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, k_scale = c("ln", "log10"), ... )
x |
A |
type |
One of |
ids |
Optional subset of subject ids for |
n_points |
Number of delay points in the curve grid. |
x_trans |
Delay-axis scale: |
show_observed |
Overlay the observed indifference points. |
k_scale |
For |
... |
Unused. |
The method shares its core arguments (type, ids, n_points, x_trans,
show_observed) with plot.beezdiscounting_tmb(); it adds k_scale and
omits at (a linearized fit has no covariates or reference grid, so there
is nothing to condition on). x_trans, n_points and show_observed apply to the
"population" and "individual" curves only; ids applies to
"individual" (default: every subject) and "transformed" (default: the
first 12 subjects, with a message when the fit has more).
Unlike the mixed-model tiers there is no shrinkage: every subject's curve is
its own closed-form estimate, and the per-subject pictures do not need the
random-effects component. "population" requires it (the condition means
mu); when the design was unbalanced and re is NULL, "population"
errors, "individual" draws the subject curves only, and "parameters"
omits the condition means with a message.
Two pictures deliberately differ from the mixed-model tiers because this
model is fitted on the linearized scale. "parameters" defaults to ln k
(the scale of coef(), confint() and anova()'s effect size);
k_scale = "log10" gives the sibling tiers' picture, k on a log10 axis.
"resid" plots the transformed-scale residual against ln(delay), the
model's regressor with its slope fixed at 1, so a trend across delay may
indicate delay-dependent lack of fit; the sibling tiers plot
residuals against fitted values instead. k_scale affects only
"parameters".
The "transformed" abscissa is ln(delay) by construction (a log10 axis
would break the slope-1 reference), so x_trans is ignored there.
Indifference points at exactly 0 or 1 are shown at their observed value on
the raw scale, and at their boundary-handled value d_used on the
transformed scale; points dropped under boundary = "drop" do not appear on
the transformed scale. "resid" uses augment()'s .std_resid, the
transformed-scale residual divided by the model's error standard deviation.
A ggplot2::ggplot object.
fit_dd_linear(), beezdiscounting_linear-methods,
vignette("linearized-mazur").
sim <- simulate_dd_linear( n_subjects = 10, delays = c(7, 30, 90, 365), mu = c(A = -6, B = -4.5), sigma2 = 2, g = 8, seed = 1 ) fit <- fit_dd_linear(sim, factors = "condition") plot(fit) plot(fit, type = "individual") plot(fit, type = "transformed", ids = c("A_1", "B_1")) plot(fit, type = "parameters")sim <- simulate_dd_linear( n_subjects = 10, delays = c(7, 30, 90, 365), mu = c(A = -6, B = -4.5), sigma2 = 2, g = 8, seed = 1 ) fit <- fit_dd_linear(sim, factors = "condition") plot(fit) plot(fit, type = "individual") plot(fit, type = "transformed", ids = c("A_1", "B_1")) plot(fit, type = "parameters")
Visualize a fitted fit_dd_tmb() model. type = "population" draws the
population (random-effects-at-zero) discount curve over the observed
indifference points; type = "individual" adds the per-subject curves (the
shrinkage picture). For a fit with factors the population curve is drawn once
per factor-level combination; use at to condition on specific levels or
covariate values. type = "parameters" shows the distribution of the
subject-specific discount rate k (log scale); type = "resid" plots
standardized (Pearson) residuals against fitted values.
## S3 method for class 'beezdiscounting_tmb' plot( x, type = c("population", "individual", "parameters", "resid"), ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )## S3 method for class 'beezdiscounting_tmb' plot( x, type = c("population", "individual", "parameters", "resid"), ids = NULL, at = NULL, n_points = 200, x_trans = c("log10", "linear"), show_observed = TRUE, ... )
x |
A |
type |
One of |
ids |
Optional subset of subject ids for |
at |
Optional named list conditioning the population curve on factor
levels / covariate values (e.g. |
n_points |
Number of delay points in the curve grid. |
x_trans |
Delay-axis scale: |
show_observed |
Overlay the observed indifference points. |
... |
Unused. |
A ggplot2::ggplot object.
sim <- simulate_dd_ip(n_subjects = 12, seed = 1) fit <- fit_dd_tmb(sim, equation = "mazur") plot(fit) plot(fit, type = "individual")sim <- simulate_dd_ip(n_subjects = 12, seed = 1) fit <- fit_dd_tmb(sim, equation = "mazur") plot(fit) plot(fit, type = "individual")
Plot Proportion of Guaranteed Choices by h Rank
## S3 method for class 'prop_sc_output' plot( x, ..., pt_shape = 21, pt_fill = "white", pt_size = 3, title = "Proportion of guaranteed choices by h rank", xlab = "h value rank", ylab = "Proportion of guaranteed choices" )## S3 method for class 'prop_sc_output' plot( x, ..., pt_shape = 21, pt_fill = "white", pt_size = 3, title = "Proportion of guaranteed choices by h rank", xlab = "h value rank", ylab = "Proportion of guaranteed choices" )
x |
Output from the |
... |
Additional arguments passed to |
pt_shape |
Shape of the points in the plot. Default is 21. |
pt_fill |
Fill color of the points in the plot. Default is "white". |
pt_size |
Size of the points in the plot. Default is 3. |
title |
Title of the plot. |
xlab |
Label for the x-axis. Default is "h value rank". |
ylab |
Label for the y-axis. |
A ggplot object.
plot(prop_sc(pdq))plot(prop_sc(pdq))
Plots the proportion of SIR/SS
choices by k value using the output of the prop_ss function.
## S3 method for class 'prop_ss_output' plot( x, ..., pt_shape = 21, pt_fill = "white", pt_size = 3, title = "Proportion of SIR/SS choices by k value", xlab = "k value rank", ylab = "Proportion of SS choices" )## S3 method for class 'prop_ss_output' plot( x, ..., pt_shape = 21, pt_fill = "white", pt_size = 3, title = "Proportion of SIR/SS choices by k value", xlab = "k value rank", ylab = "Proportion of SS choices" )
x |
Output from the |
... |
Additional arguments passed to |
pt_shape |
Shape of the points in the plot. Default is 21. |
pt_fill |
Fill color of the points in the plot. Default is "white". |
pt_size |
Size of the points in the plot. Default is 3. |
title |
Title of the plot. Default is "Proportion of SIR/SS choices by k value". |
xlab |
Label for the x-axis. Default is "k value rank". |
ylab |
Label for the y-axis. Default is "Proportion of SS choices". |
A ggplot object.
plot(prop_ss(mcq27))plot(prop_ss(mcq27))
Boxplots the MCQ scores for
different metrics (small_k, medium_k, large_k, geomean_k, overall_k).
Log-transformed k values (trans = "log"/"ln") are detected
and the y-axis label adjusted accordingly.
## S3 method for class 'score_mcq_output' plot(x, ..., xlab = "Metric", alpha = 0.3)## S3 method for class 'score_mcq_output' plot(x, ..., xlab = "Metric", alpha = 0.3)
x |
A data frame returned by the |
... |
Additional arguments passed to methods. |
xlab |
Label for the x-axis. Default is "Metric". |
alpha |
Transparency of the points in the plot. Default is 0.3. |
A ggplot object showing the boxplot of MCQ scores.
dat21 <- data.frame(subjectid = 1:5, questionid = rep(1:21, 5), response = 1) plot(score_mcq(dat21, items = 21))dat21 <- data.frame(subjectid = 1:5, questionid = rep(1:21, 5), response = 1) plot(score_mcq(dat21, items = 21))
Boxplots the MCQ-27 scores for
different metrics (small_k, medium_k, large_k, geomean_k, overall_k).
Log-transformed k values (trans = "log"/"ln") are detected
and the y-axis label adjusted accordingly.
## S3 method for class 'score_mcq27_output' plot(x, ..., xlab = "Metric", alpha = 0.3)## S3 method for class 'score_mcq27_output' plot(x, ..., xlab = "Metric", alpha = 0.3)
x |
A data frame returned by the |
... |
Additional arguments passed to methods. |
xlab |
Label for the x-axis. Default is "Metric". |
alpha |
Transparency of the points in the plot. Default is 0.3. |
A ggplot object showing the boxplot of MCQ-27 scores.
plot(score_mcq27(mcq27))plot(score_mcq27(mcq27))
Boxplot of PDQ h metrics (block1_h, block2_h, block3_h, mean_h,
geomean_h, and the pooled overall_h extension), handling the same log
transformations as plot.score_mcq_output().
## S3 method for class 'score_pdq_output' plot(x, ..., xlab = "Metric", alpha = 0.3)## S3 method for class 'score_pdq_output' plot(x, ..., xlab = "Metric", alpha = 0.3)
x |
A data frame returned by the |
... |
Additional arguments passed to methods. |
xlab |
Label for the x-axis. Default is "Metric". |
alpha |
Transparency of the points in the plot. Default is 0.3. |
A ggplot object showing the boxplot of PDQ scores.
plot(score_pdq(pdq))plot(score_pdq(pdq))
Estimates statistical power to detect a between-subject difference in the
discount rate (a shift delta_k on natural-log k) by simulation: each
replicate (1) simulates a two-condition between-subject indifference-point
dataset with simulate_dd_ip() under assumed population parameters plus
the effect, (2) refits it with fit_dd_tmb(), and (3) tests the log-k
condition contrast with a Wald test at level alpha, referred to a t
distribution with df degrees of freedom (see the df argument). Power
is the proportion of usable fits (converged, positive-definite Hessian,
finite standard error) that reject.
Because the power estimate is a proportion from finitely many replicates,
it is reported with a Wilson score confidence interval (power_mc_ci).
Both a p-value verdict (p < alpha) and a confidence-interval verdict
(Wald CI excludes 0) are recorded per replicate; they use the same
standard error and reference distribution, so they coincide by
construction, and both rates are returned.
This mirrors beezdemand::power_demand(). The design asymmetry is
intentional: this package's simulator models a between-subject condition
(subjects are split across conditions round-robin), while beezdemand's
models a within-subject condition.
power_discounting( n_subjects, effect = list(delta_k = NULL), design = list(), n_sim = 500, alpha = 0.05, df = NULL, seed = NULL, equation = c("mazur", "exponential"), family = c("sltb", "gaussian"), random_effects = k ~ 1, multi_start = FALSE, verbose = TRUE, ... )power_discounting( n_subjects, effect = list(delta_k = NULL), design = list(), n_sim = 500, alpha = 0.05, df = NULL, seed = NULL, equation = c("mazur", "exponential"), family = c("sltb", "gaussian"), random_effects = k ~ 1, multi_start = FALSE, verbose = TRUE, ... )
n_subjects |
Total number of simulated subjects per replicate, split across the two conditions round-robin (even numbers give equal groups). |
effect |
Named list supplying |
design |
Named list of data-generating settings, merged over the
simulator defaults: |
n_sim |
Number of Monte Carlo replicates. 500 (default) is suitable
for interactive exploration; use 2000+ for grant-quality precision (see
|
alpha |
Nominal two-sided test level. |
df |
Degrees of freedom for the Wald test's t reference
distribution. |
seed |
Optional integer seed; identical seeds give identical results. The caller's RNG state is restored on exit. |
equation |
Discounting function used for BOTH simulation and
refitting (they are always matched): |
family |
Observation family used for both simulation and refitting:
|
random_effects |
Random-effects formula passed to |
multi_start |
Passed to |
verbose |
Logical; show a progress bar. |
... |
Additional arguments passed to |
A replicate whose fit fails (non-convergence, non-positive-definite
Hessian, unusable standard error, or an error) is excluded from the power
denominator and reported through the n_* counts and
$replicates$status. It is never counted as "no effect detected", which
would bias power in an unpredictable direction. A warning is issued when
fewer than 95% of replicates are usable.
The v1 scope is a single fixed-effect delta_k under the package's
existing between-subject simulator. Effects on s or phi, power for
derived measures (ED50, AUC), and arbitrary designs are out of scope; see
vignette("power-analysis").
An object of class beezdiscounting_power: a list with
Estimated power: proportion of usable replicates whose
Wald CI excludes 0 (equal to hit_rate_ci). NA if no replicate
was usable.
Wilson 95% confidence interval on power,
reflecting Monte Carlo uncertainty from n_used replicates.
Proportion of usable replicates with p < alpha.
Proportion of usable replicates whose Wald CI
excludes 0 (the same decision rule as hit_rate_p, since both use
the same SE and t reference; both reported).
Total replicates attempted.
Replicates whose fit converged.
Replicates with a positive-definite Hessian.
Replicates entering the power denominator (converged, positive-definite Hessian, finite SE).
Nominal test level.
Degrees of freedom of the t reference distribution actually
used (n_subjects - 2 unless overridden).
The validated effect specification (name and delta).
The tested coefficient ("k:conditionC2").
The merged design list actually used.
As supplied.
Tibble with one row per replicate: sim, status
("ok", "nonconverged", "hessian_not_pd", "se_unusable",
"error"), converged, hessian_pd, estimate, se,
statistic, p_value, ci_lower, ci_upper, hit_p, hit_ci,
and message (error text, if any). Estimates are on the
natural-log-k scale of the simulated delta_k.
As supplied.
List of equation, family, multi_start, and the
deparsed random-effects formula.
The matched call.
find_n_discounting() to search for the smallest adequate
sample size; fit_dd_tmb() for the model being refit;
simulate_dd_ip() for the data-generating process.
Other power-analysis:
find_n_discounting()
# Quick exploratory run (use n_sim >= 500 for real planning) res <- power_discounting( n_subjects = 8, effect = list(delta_k = log(2)), n_sim = 4, seed = 1, verbose = FALSE ) print(res)# Quick exploratory run (use n_sim >= 500 for real planning) res <- power_discounting( n_subjects = 8, effect = list(delta_k = log(2)), n_sim = 4, seed = 1, verbose = FALSE ) print(res)
type = "response" summarizes brms::posterior_epred() draws (the
indifference-proportion mean) to the posterior median with equal-tailed
intervals; level = "subject" conditions on the subject random effects,
"population" sets them to zero. type = "parameters" returns the
per-subject k summaries.
## S3 method for class 'beezdiscounting_brms' predict( object, newdata = NULL, type = c("response", "parameters"), level = "subject", probs = c(0.025, 0.975), draws = FALSE, ... )## S3 method for class 'beezdiscounting_brms' predict( object, newdata = NULL, type = c("response", "parameters"), level = "subject", probs = c(0.025, 0.975), draws = FALSE, ... )
object |
A |
newdata |
Optional data frame (defaults to the model frame). The
Rachlin guard columns ( |
type |
|
level |
|
probs |
Interval probabilities. |
draws |
If |
... |
Unused. |
A tibble (or draws matrix).
Predict from a structural choice discounting model
## S3 method for class 'beezdiscounting_choice' predict( object, newdata = NULL, type = c("prob", "parameters"), level = "subject", ... )## S3 method for class 'beezdiscounting_choice' predict( object, newdata = NULL, type = c("prob", "parameters"), level = "subject", ... )
object |
A |
newdata |
Optional data frame. |
type |
|
level |
For |
... |
Unused. |
type = "parameters": the subject-parameter tibble (one row per subject).
type = "prob": newdata as a tibble plus a .prob column of fitted
choice probabilities.
Predict P(LL) from a beezdiscounting_choice_brms model
## S3 method for class 'beezdiscounting_choice_brms' predict( object, newdata = NULL, type = c("response", "parameters"), level = "subject", probs = c(0.025, 0.975), draws = FALSE, ... )## S3 method for class 'beezdiscounting_choice_brms' predict( object, newdata = NULL, type = c("response", "parameters"), level = "subject", probs = c(0.025, 0.975), draws = FALSE, ... )
object |
A |
newdata |
Optional trial-level data (needs |
type |
|
level |
|
probs |
Interval probabilities. |
draws |
Return the draws matrix instead. |
... |
Unused. |
A tibble (or draws matrix).
Predict from a TMB mixed-effects discounting model
## S3 method for class 'beezdiscounting_tmb' predict( object, newdata = NULL, type = c("response", "parameters"), level = "subject", ... )## S3 method for class 'beezdiscounting_tmb' predict( object, newdata = NULL, type = c("response", "parameters"), level = "subject", ... )
object |
A |
newdata |
Optional data frame. |
type |
|
level |
For |
... |
Unused. |
type = "parameters": the subject-parameter tibble (one row per subject).
type = "response", level = "subject" only: newdata as a tibble plus
a .fitted column.
type = "response", "population" requested: newdata plus
predict.fixed (and predict.id when "subject" is also requested),
matching the nlme::predict.lme(level = 0:1) column-name convention so
nlme-based plotting code runs unchanged.
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) # Subject-conditional fitted values head(predict(fit, type = "response")) # Population-mean curve at specific delays (no id column needed) nd <- data.frame(x = c(7, 30, 180, 365)) predict(fit, newdata = nd, level = "population") # Per-subject parameters head(predict(fit, type = "parameters"))dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) # Subject-conditional fitted values head(predict(fit, type = "response")) # Population-mean curve at specific delays (no id column needed) nd <- data.frame(x = c(7, 30, 180, 365)) predict(fit, newdata = nd, level = "population") # Per-subject parameters head(predict(fit, type = "parameters"))
Print a structural choice discounting fit
## S3 method for class 'beezdiscounting_choice' print(x, ...)## S3 method for class 'beezdiscounting_choice' print(x, ...)
x |
A |
... |
Unused. |
Invisibly returns x.
Print a TMB mixed-effects discounting fit
## S3 method for class 'beezdiscounting_tmb' print(x, ...)## S3 method for class 'beezdiscounting_tmb' print(x, ...)
x |
A |
... |
Unused. |
Invisibly returns x.
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) print(fit)dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) print(fit)
Print a structural choice discounting model summary
## S3 method for class 'summary.beezdiscounting_choice' print(x, digits = 4, ...)## S3 method for class 'summary.beezdiscounting_choice' print(x, digits = 4, ...)
x |
A |
digits |
Number of significant digits for rounding. |
... |
Unused. |
Invisibly returns x.
Print a TMB discounting model summary
## S3 method for class 'summary.beezdiscounting_tmb' print(x, digits = 4, ...)## S3 method for class 'summary.beezdiscounting_tmb' print(x, digits = 4, ...)
x |
A |
digits |
Number of significant digits for rounding. |
... |
Unused. |
Invisibly returns x.
The PDQ analog of prop_ss(): pools all subjects' responses and
reports, for each of the 10 h ranks (one item per block per rank), the
proportion choosing the smaller guaranteed reward. 1 - prop_sc is the
risky-choice proportion at that rank.
prop_sc(dat)prop_sc(dat)
dat |
Dataframe (longform) with subjectid, questionid (1-30), and response (0 for the guaranteed reward and 1 for the risky reward) |
Dataframe with proportion of guaranteed choices at each h rank
prop_sc(pdq)prop_sc(pdq)
Calculate proportion of SIR/SS responses at each k value
prop_ss(dat, items = 27)prop_ss(dat, items = 27)
dat |
Dataframe (longform) with subjectid, questionid, and response (0 for SIR/SS and 1 for LDR/LL) |
items |
Number of MCQ items (27 or 21) |
items must match the instrument actually administered.
Question ids 1-21 are valid in both the 21- and 27-item designs, so
passing the wrong items does not error; it silently pools responses
into the wrong k-rank rows. If the observed question ids do not exactly
match the requested design, prop_ss() warns.
Dataframe with proportion of SIR/SS responses at each k rank
prop_ss(mcq27) dat21 <- data.frame(subjectid = 1, questionid = 1:21, response = 1) prop_ss(dat21, items = 21)prop_ss(mcq27) dat21 <- data.frame(subjectid = 1, questionid = 1:21, response = 1) prop_ss(dat21, items = 21)
Structural: id, the standardized random-intercept deviate u_i (such that
log k_i = X beta + sigma_u * u_i), and the resolved per-subject discount
rate k. Descriptive (Young 2018): id plus the per-subject magnitude and
delay random slopes (b_mag, b_delay); pooled (random_slopes = FALSE)
returns an id-only frame.
## S3 method for class 'beezdiscounting_choice' ranef(object, ...)## S3 method for class 'beezdiscounting_choice' ranef(object, ...)
object |
A |
... |
Unused. |
Data frame: structural id/u_i/k; descriptive id/b_mag/
b_delay (or id-only when pooled).
Extract subject-level random effects from a TMB discounting model
## S3 method for class 'beezdiscounting_tmb' ranef(object, ...)## S3 method for class 'beezdiscounting_tmb' ranef(object, ...)
object |
A |
... |
Unused. |
Data frame keyed by id. For a 1-RE fit (k ~ 1): the standardized
random-intercept deviate u_i (such that log k_i = X beta + sigma_u * u_i) and the resolved per-subject discount rate k (no phi or s
column; both are population-level). For a phi-target 2-RE fit
(k + phi ~ 1): (re_k, re_phi) offsets plus per-subject k and phi.
For an s-target 2-RE fit (k + s ~ 1, GM/Rachlin only): (re_k, re_s)
offsets plus per-subject k and s (soft-clamped toward (0.05, 20)).
Residuals for a beezdiscounting_choice fit
## S3 method for class 'beezdiscounting_choice' residuals( object, type = c("response", "pearson"), level = c("subject", "population"), ... )## S3 method for class 'beezdiscounting_choice' residuals( object, type = c("response", "pearson"), level = c("subject", "population"), ... )
object |
A |
type |
|
level |
|
... |
Unused. |
Numeric vector of residuals, length nobs(object).
Residuals for a beezdiscounting_tmb fit
## S3 method for class 'beezdiscounting_tmb' residuals( object, type = c("response", "pearson"), level = c("subject", "population"), ... )## S3 method for class 'beezdiscounting_tmb' residuals( object, type = c("response", "pearson"), level = c("subject", "population"), ... )
object |
A |
type |
|
level |
|
... |
Unused. |
Numeric vector of residuals, length nobs(object).
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, verbose = 0) head(residuals(fit)) head(residuals(fit, type = "pearson"))dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, verbose = 0) head(residuals(fit)) head(residuals(fit, type = "pearson"))
Extracts parameter estimates, fit statistics, and confidence intervals from a fitted delay-discounting model.
results_dd(fit_dd_object)results_dd(fit_dd_object)
fit_dd_object |
A fitted delay-discounting model object of class |
A tibble containing the following columns:
id: The participant or group ID (if applicable).
term: The model parameter (e.g., k).
estimate: The estimated value of the parameter.
std.error: The standard error of the parameter estimate.
statistic: The t-statistic for the parameter estimate.
p.value: The p-value for the parameter estimate.
conf_low: The lower bound of the 95% confidence interval.
conf_high: The upper bound of the 95% confidence interval.
R2: The coefficient of determination ().
data <- data.frame( id = rep(1:2, each = 6), x = rep(c(1, 7, 30, 90, 180, 365), 2), y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05, 0.85, 0.55, 0.35, 0.15, 0.1, 0.05) ) fit <- fit_dd(data, equation = "mazur", method = "two stage") results_dd(fit)data <- data.frame( id = rep(1:2, each = 6), x = rep(c(1, 7, 30, 90, 180, 365), 2), y = c(0.9, 0.5, 0.3, 0.2, 0.1, 0.05, 0.85, 0.55, 0.35, 0.15, 0.1, 0.05) ) fit <- fit_dd(data, equation = "mazur", method = "two stage") results_dd(fit)
Score 5.5 trial delay discounting from Qualtrics template
score_dd(df)score_dd(df)
df |
A dataframe containing all the columns |
Currently assumes the attending questions are present and labeled "Attend-LL" and "Attend-SS"
A dataframe with id, indexes, response, k value, and effective delay 50.
score_dd(five.fivetrial_dd)score_dd(five.fivetrial_dd)
Score an MCQ (27- or 21-item)
score_mcq( dat = dat, items = 27, impute_method = "none", round = 6, random = FALSE, trans = "none", return_data = FALSE, verbose = FALSE )score_mcq( dat = dat, items = 27, impute_method = "none", round = 6, random = FALSE, trans = "none", return_data = FALSE, verbose = FALSE )
dat |
Dataframe (longform) with subjectid, questionid, and response (0 for SIR/SS and 1 for LDR/LL) |
items |
Number of MCQ items: 27 (Kirby, Petry, & Bickel, 1999) or 21 (Kirby & Marakovic, 1996). Default is 27. |
impute_method |
One of: "none", "ggm", "GGM", "inn", "INN" |
round |
Numeric specifying number of decimal places
(passed to |
random |
Boolean whether to insert a random draw (0 or 1) for NAs. Default is FALSE |
trans |
Transformation to apply to k values: "none", "log", or "ln". Default is "none" |
return_data |
Boolean whether to return the original data and new imputed responses. Default is FALSE. |
verbose |
Boolean whether to print subject and question ids pertaining to missing data. Default is FALSE. |
Each subject's data must satisfy a strict contract: exactly one
row per canonical question id (items of them; no duplicates, no unknown
ids, none missing) and responses coded 0, 1, or NA (numeric, logical, or
character/factor values that coerce to 0/1). Malformed input errors
rather than silently mis-scoring. Contrast with mcq_to_choice()'s
lenient, ragged contract, which accepts partial per-subject coverage.
If return_data = FALSE (default), a summary data frame with one
row per subject. If return_data = TRUE, a list with results (that
summary data frame) and data (the input data plus a newresponse
column reflecting any imputation).
score_mcq(mcq27, items = 27) dat21 <- data.frame(subjectid = 1, questionid = 1:21, response = 1) score_mcq(dat21, items = 21)score_mcq(mcq27, items = 27) dat21 <- data.frame(subjectid = 1, questionid = 1:21, response = 1) score_mcq(dat21, items = 21)
Score 27-item MCQ
score_mcq27( dat = dat, impute_method = "none", round = 6, random = FALSE, trans = "none", return_data = FALSE, verbose = FALSE )score_mcq27( dat = dat, impute_method = "none", round = 6, random = FALSE, trans = "none", return_data = FALSE, verbose = FALSE )
dat |
Dataframe (longform) with subjectid, questionid, and response (0 for SIR/SS and 1 for LDR/LL) |
impute_method |
One of: "none", "ggm", "GGM", "inn", "INN" |
round |
Numeric specifying number of decimal places
(passed to |
random |
Boolean whether to insert a random draw (0 or 1) for NAs. Default is FALSE |
trans |
Transformation to apply to k values: "none", "log", or "ln". Default is "none" |
return_data |
Boolean whether to return the original data and new imputed responses. Default is FALSE. |
verbose |
Boolean whether to print subject and question ids pertaining to missing data. Default is FALSE. |
The subject's data must satisfy a strict contract: exactly one
row per canonical question id (27 of them; no duplicates, no unknown
ids, none missing) and responses coded 0, 1, or NA (numeric, logical, or
character/factor values that coerce to 0/1). Malformed input errors
rather than silently mis-scoring. Contrast with mcq27_to_choice()'s
lenient, ragged contract, which accepts partial per-subject coverage.
If return_data = FALSE (default), a summary data frame with one
row per subject. If return_data = TRUE, a list with results (that
summary data frame) and data (the input data plus a newresponse
column reflecting any imputation).
score_mcq27(mcq27)score_mcq27(mcq27)
Score 5.5 trial probability discounting from Qualtrics template
score_pd(df)score_pd(df)
df |
A dataframe containing all the columns |
Currently assumes the attending questions are present and labeled "Attend-LL" and "Attend-SS"
A dataframe with id, indexes, response, h value, and effective probability 50.
score_pd(five.fivetrial_pd)score_pd(five.fivetrial_pd)
Scores the Madden, Petry, & Johnson (2009) PDQ: three 10-item blocks
(block 1: $20 for sure vs. a chance of $80; block 2: $40 vs. $100;
block 3: $40 vs. $60), each an ascending ladder of h values at
indifference under the hyperbolic odds model V = A / (1 + h * theta),
theta = (1 - p) / p (Rachlin, Raineri, & Cross, 1991). Each block is
scored independently by the same consistency-maximization algorithm as
the MCQ scorers; this implementation reproduces the Gray et al. (2016)
scoring syntax lookup tables for every possible response pattern.
score_pdq( dat = dat, impute_method = "none", round = 6, random = FALSE, trans = "none", return_data = FALSE, verbose = FALSE )score_pdq( dat = dat, impute_method = "none", round = 6, random = FALSE, trans = "none", return_data = FALSE, verbose = FALSE )
dat |
Dataframe (longform) with subjectid, questionid (1-30), and response (0 for the smaller guaranteed reward, 1 for the larger risky reward) |
impute_method |
One of: "none", "ggm", "GGM", "inn", "INN" |
round |
Numeric specifying number of decimal places
(passed to |
random |
Boolean whether to insert a random draw (0 or 1) for NAs. Default is FALSE |
trans |
Transformation to apply to h values: "none", "log", or "ln". Default is "none" |
return_data |
Boolean whether to return the original data and new imputed responses. Default is FALSE. |
verbose |
Boolean whether to print subject and question ids pertaining to missing data. Default is FALSE. |
Each subject's data must satisfy a strict contract: exactly one
row per canonical question id (30 of them; no duplicates, no unknown
ids, none missing) and responses coded 0, 1, or NA (numeric, logical,
or character/factor values that coerce to 0/1). Malformed input errors
rather than silently mis-scoring. Contrast with pdq_to_choice()'s
lenient, ragged contract.
The published scoring (Madden et al., 2009; Gray et al., 2016) has no
overall 30-item ladder; blocks are scored separately, and mean_h is
Gray et al.'s recommended composite. overall_h and
overall_consistency are a beezdiscounting extension: all 30 items are
pooled into a single ascending ladder (exact-rational h order, ties
broken by question id; four item pairs tie exactly) and scored by the
same consistency-maximization algorithm with the repeat-last edge.
INN imputation groups items sharing an h rank (one item per block).
Ladders with remaining NA responses score NA; Gray et al. recommend
excluding subjects below 80% consistency on any block.
If return_data = FALSE (default), a summary data frame with one
row per subject: pooled overall_h (see Details), per-block h
(block1_h, block2_h, block3_h), their arithmetic mean (mean_h;
Gray et al.'s recommended composite) and geometric mean (geomean_h),
pooled and per-block consistency plus their mean
(composite_consistency), and pooled plus per-block proportions of
risky choices (block*_proportion is Gray et al.'s risky choice ratio).
If return_data = TRUE, a list with results and data (the input plus
a newresponse column reflecting any imputation).
Madden, G. J., Petry, N. M., & Johnson, P. S. (2009). Pathological gamblers discount probabilistic rewards less steeply than matched controls. Experimental and Clinical Psychopharmacology, 17(5), 283-290. doi:10.1037/a0016806
Gray, J. C., Amlung, M. T., Palmer, A. A., & MacKillop, J. (2016). Syntax for calculation of discounting indices from the monetary choice questionnaire and probability discounting questionnaire. Journal of the Experimental Analysis of Behavior, 106(2), 156-163. doi:10.1002/jeab.221
score_pdq(pdq)score_pdq(pdq)
Generates binary choices from either the structural or descriptive choice
model. When mode = "structural" (default) choices are drawn from the
structural model: P(LL) = plogis(beta0 + gamma * ((ll/ss) * D(k, delay) - 1)), k_i = exp(log_k_pop + sigma_u * u_i). When mode = "descriptive"
choices follow Young's (2018) correlated random-slope logistic model: each
subject receives per-subject slopes drawn from a bivariate normal with mean
theta and covariance Sigma, and P(LL) = plogis((theta[1] + b_i[1]) * log(ll/ss) + (theta[2] + b_i[2]) * log(delay + 1)). The descriptive branch
is the known-truth generator used by recovery tests.
simulate_dd_choice( n_subjects = 50, ss_amount = c(40, 55, 31, 14, 47, 25, 78, 40, 11, 67), ll_amount = c(65, 75, 85, 25, 60, 30, 80, 65, 30, 75), delay = c(25, 61, 14, 19, 160, 7, 4, 62, 7, 119), mode = c("structural", "descriptive"), log_k_pop = log(0.02), sigma_u = 0.5, gamma = 4, beta0 = 0, equation = c("mazur", "exponential"), theta = c(1.5, -0.4), re_sd = c(0.5, 0.3), re_cor = -0.2, return_truth = FALSE, seed = NULL )simulate_dd_choice( n_subjects = 50, ss_amount = c(40, 55, 31, 14, 47, 25, 78, 40, 11, 67), ll_amount = c(65, 75, 85, 25, 60, 30, 80, 65, 30, 75), delay = c(25, 61, 14, 19, 160, 7, 4, 62, 7, 119), mode = c("structural", "descriptive"), log_k_pop = log(0.02), sigma_u = 0.5, gamma = 4, beta0 = 0, equation = c("mazur", "exponential"), theta = c(1.5, -0.4), re_sd = c(0.5, 0.3), re_cor = -0.2, return_truth = FALSE, seed = NULL )
n_subjects |
Integer number of subjects. |
ss_amount, ll_amount, delay
|
Numeric trial-design vectors (same length); each subject is presented all trials. Defaults are a built-in grid. |
mode |
|
log_k_pop, sigma_u, gamma, beta0
|
Structural truth (used only when
|
equation |
|
theta |
Length-2 numeric vector of population-level slopes for the
descriptive model: |
re_sd |
Length-2 numeric vector of random-effect standard deviations (descriptive mode only). |
re_cor |
Correlation between the two random slopes (descriptive mode only). |
return_truth |
Logical. If |
seed |
Optional integer seed for reproducibility. The caller's RNG state is restored on exit. |
A tibble with columns id, ss_amount,
ll_amount, delay, choice (0/1, 1 = LL chosen). When
mode = "descriptive" and return_truth = TRUE, the tibble also carries
subject_slopes and Sigma attributes.
# Structural (default) sim <- simulate_dd_choice(n_subjects = 20, seed = 1) head(sim) # Descriptive (Young 2018 correlated random-slope model) sim_desc <- simulate_dd_choice(n_subjects = 20, mode = "descriptive", theta = c(1.5, -0.4), seed = 42) head(sim_desc)# Structural (default) sim <- simulate_dd_choice(n_subjects = 20, seed = 1) head(sim) # Descriptive (Young 2018 correlated random-slope model) sim_desc <- simulate_dd_choice(n_subjects = 20, mode = "descriptive", theta = c(1.5, -0.4), seed = 42) head(sim_desc)
Generates long-format indifference-point data from the mixed-effects
discounting model used by fit_dd_tmb(). Each subject i has a random
discount rate log k_i = log_k_pop + delta_k[condition] + u_i,
u_i ~ N(0, sigma_u^2). The mean indifference proportion at delay x is the
discounting function mu (Mazur hyperbola, exponential, Green-Myerson, or
Rachlin), and observed y
is drawn from the scale-location-truncated beta (family = "sltb") via the
inverse-CDF on the truncated beta, or from a clamped Gaussian
(family = "gaussian").
simulate_dd_ip( n_subjects = 60, delays = c(7, 30, 180, 365, 730, 1460, 2920), log_k_pop = log(0.01), sigma_u = 0.6, phi = 10, sigma_e = 0.1, family = c("sltb", "gaussian"), equation = c("mazur", "exponential", "green-myerson", "rachlin"), s = 1, n_conditions = 1, delta_k = NULL, seed = NULL, sigma_phi = 0, rho_kphi = 0, attach_truth = FALSE, sigma_s = 0, rho_ks = 0 )simulate_dd_ip( n_subjects = 60, delays = c(7, 30, 180, 365, 730, 1460, 2920), log_k_pop = log(0.01), sigma_u = 0.6, phi = 10, sigma_e = 0.1, family = c("sltb", "gaussian"), equation = c("mazur", "exponential", "green-myerson", "rachlin"), s = 1, n_conditions = 1, delta_k = NULL, seed = NULL, sigma_phi = 0, rho_kphi = 0, attach_truth = FALSE, sigma_s = 0, rho_ks = 0 )
n_subjects |
Integer; number of subjects. |
delays |
Numeric vector of delays (days) each subject is observed at. |
log_k_pop |
Numeric; population intercept on |
sigma_u |
Numeric; SD of the subject random intercept on |
phi |
Numeric; SLT-beta precision ( |
sigma_e |
Numeric; residual SD on |
family |
One of |
equation |
One of |
s |
Numeric nonlinearity exponent for the 2-parameter equations
(Green-Myerson / Rachlin); ignored by |
n_conditions |
Integer; number of between-subject condition levels. When
|
delta_k |
Numeric vector of length |
seed |
Optional integer seed; identical seeds give identical results. The caller's RNG state is restored on exit. |
sigma_phi |
Numeric; SD of the subject random intercept on |
rho_kphi |
Numeric in |
attach_truth |
Logical; when |
sigma_s |
Numeric; SD of the subject random intercept on |
rho_ks |
Numeric in |
The SLT draw uses the same constants as the C++ template and the verified
reference density: s_slt = 1.0000001, l = 1e-8, with
a = mu * phi, b = (1 - mu) * phi, and
y = (qbeta(U, a, b) - l) * s_slt where
U ~ Uniform(pbeta(l, a, b), pbeta(1/s_slt + l, a, b)).
A tibble with columns id (factor),
condition (factor; only when n_conditions > 1), x (delay), and
y (indifference proportion in [0, 1]). When attach_truth = TRUE, an
additional phi column is appended with the subject-specific SLT-beta
precision (equal to the constant phi when sigma_phi = 0). When
attach_truth = TRUE and sigma_s > 0, an additional s column is
appended with the subject-specific curvature exponent.
# A small SLT-beta mixed-effects discounting dataset (id, x, y) sim <- simulate_dd_ip(n_subjects = 8, seed = 1) head(sim) # Two between-subject conditions with a log-k shift on the second level sim2 <- simulate_dd_ip( n_subjects = 8, n_conditions = 2, delta_k = c(0, log(3)), seed = 2 )# A small SLT-beta mixed-effects discounting dataset (id, x, y) sim <- simulate_dd_ip(n_subjects = 8, seed = 1) head(sim) # Two between-subject conditions with a log-k shift on the second level sim2 <- simulate_dd_ip( n_subjects = 8, n_conditions = 2, delta_k = c(0, log(3)), seed = 2 )
Draws theta_ic ~ N(mu_c, g sigma2 / T), y_ijc ~ N(theta_ic, sigma2) and
returns D = 1 / (1 + exp(y) t) (Hinds et al., 2026, Sec. 4.1), which is
always strictly inside (0, 1).
simulate_dd_linear( n_subjects, delays, mu, sigma2, g, seed = NULL, attach_truth = FALSE )simulate_dd_linear( n_subjects, delays, mu, sigma2, g, seed = NULL, attach_truth = FALSE )
n_subjects |
Integer, scalar (recycled) or one per condition. |
delays |
Positive numeric vector of delays ( |
mu |
Numeric vector of population mean ln k per condition; names become condition labels. |
sigma2 |
Measurement-error variance on the transformed scale. |
g |
Random-effect variance multiplier: |
seed |
Optional seed; the global RNG state is restored afterwards. |
attach_truth |
If |
Long data.frame(id, condition, x, y).
s <- simulate_dd_linear(30, c(7, 30, 180, 365), mu = c(A = -6, B = -5), sigma2 = 2, g = 10, seed = 1) anova(fit_dd_linear(s, factors = "condition"))s <- simulate_dd_linear(30, c(7, 30, 180, 365), mu = c(A = -6, B = -5), sigma2 = 2, g = 10, seed = 1) anova(fit_dd_linear(s, factors = "condition"))
Provide a summary of the results from the MCQ output table.
summarize_mcq(res, na.rm = TRUE)summarize_mcq(res, na.rm = TRUE)
res |
Dataframe with MCQ results (output from |
na.rm |
Boolean whether to remove NAs from the calculation |
Dataframe with summary statistics
summarize_mcq(score_mcq27(mcq27))summarize_mcq(score_mcq27(mcq27))
Summarize a beezdiscounting_brms model
## S3 method for class 'beezdiscounting_brms' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)## S3 method for class 'beezdiscounting_brms' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)
object |
A |
report_space |
Reporting scale for the coefficient table. |
... |
Unused. |
A summary.beezdiscounting_brms list.
Summarize a structural choice discounting fit
## S3 method for class 'beezdiscounting_choice' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)## S3 method for class 'beezdiscounting_choice' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)
object |
A |
report_space |
Scale for the fixed-effect ( |
... |
Unused. |
An object of class "summary.beezdiscounting_choice" with components:
call, model_class, backend, mode, equation, coefficients,
variance_components, n_subjects, nobs, converged, logLik,
AIC, BIC, notes.
Summarize a beezdiscounting_choice_brms model
## S3 method for class 'beezdiscounting_choice_brms' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)## S3 method for class 'beezdiscounting_choice_brms' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)
object |
A |
report_space |
Reporting scale. |
... |
Unused. |
A summary.beezdiscounting_choice_brms list.
Summarize a TMB mixed-effects discounting fit
## S3 method for class 'beezdiscounting_tmb' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)## S3 method for class 'beezdiscounting_tmb' summary(object, report_space = c("natural", "log10", "internal", "log"), ...)
object |
A |
report_space |
Scale for the fixed-effect ( |
... |
Unused. |
An object of class "summary.beezdiscounting_tmb" with components:
call, model_class, backend, equation, family, coefficients,
variance_components, n_subjects, nobs, converged, logLik,
AIC, BIC, notes.
dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) summary(fit) summary(fit, report_space = "log10")dd <- simulate_dd_ip(n_subjects = 20, seed = 1) fit <- fit_dd_tmb(dd, equation = "mazur", family = "sltb", random_effects = k ~ 1, verbose = 0) summary(fit) summary(fit, report_space = "log10")
The exact 8-column dd coefficient contract (term, estimate,
std.error, statistic, p.value, component, estimate_scale,
term_display). Estimates are posterior medians and std.error
posterior SDs of the report-space-transformed draws (exact; no delta
method); statistic/p.value are NA; use confint() for interval
summaries. The shape row (s, two-parameter equations) carries
component = "shape"; variance rows (effects = "ran_pars") mirror the
TMB reporting convention (log10-scale k RE SD; natural phi/sigma).
## S3 method for class 'beezdiscounting_brms' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )## S3 method for class 'beezdiscounting_brms' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )
x |
A |
effects |
|
report_space |
|
... |
Unused. |
A tibble.
Returns fixed-effect rows (the log-k coefficients), the shape rows (the
choice-sensitivity gamma and, when present, the choice-bias beta0), and
the variance-component row, following the broom coefficient-table contract.
## S3 method for class 'beezdiscounting_choice' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )## S3 method for class 'beezdiscounting_choice' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )
x |
A |
effects |
Character vector: |
report_space |
Reporting scale for the fixed-effect
|
... |
Unused. |
estimate and std.error are reported on the report_space scale for the
fixed-effect (beta_k) rows and for gamma (which is transformed
EXPLICITLY since the param-space transformer keys only on k/s/phi). beta0
is on the identity (logit-intercept) scale and is NEVER transformed across
report spaces. statistic and p.value are always computed on the
estimation (internal) scale. Wald statistics are not recomputed after
back-transforming (broom convention).
A tibble with exactly 8 columns in this order: term, estimate,
std.error, statistic, p.value, component, estimate_scale,
term_display. Fixed-effect rows carry component == "fixed"; gamma/beta0
carry component == "shape"; variance rows carry component == "variance".
The 8-column dd contract; gamma (choice sensitivity) and k rows are
report-space transformed per draw; the optional beta0 bias stays on
the logit scale.
## S3 method for class 'beezdiscounting_choice_brms' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )## S3 method for class 'beezdiscounting_choice_brms' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )
x |
A |
effects |
|
report_space |
Reporting scale. |
... |
Unused. |
A tibble.
generics::tidy() method for beezdiscounting_comparison objects
(returned by get_dd_comparisons()). Produces a flat long tibble whose
column names and order match the beezdemand
beezdemand_comparison tidier, so downstream consumers can bind
rows from both backends into a single frame. Note that the contrast
label dialect differs: beezdiscounting emits native factor=level
labels (e.g. "condition=C1 - condition=C2"), while beezdemand emits
bare level labels (e.g. "C1 - C2"). Fully uniform emmeans-style
contrast labels across backends are a future cross-backend item.
The nested object keeps the native dialect (see get_dd_comparisons()).
## S3 method for class 'beezdiscounting_comparison' tidy(x, exponentiate = FALSE, ...)## S3 method for class 'beezdiscounting_comparison' tidy(x, exponentiate = FALSE, ...)
x |
A |
exponentiate |
Logical. If |
... |
Unused. |
A tibble with columns param, contrast,
estimate, std.error, statistic, df,
conf.low, conf.high, p.value (with leading by-column(s)
inserted before param when contrast_by is active). Estimates
and CIs are on the log10 scale (or ratios when exponentiate = TRUE).
statistic is an asymptotic z (df = Inf). On the
brms backend, statistic/df/p.value are NA
(posterior summaries), the intervals are quantile credible intervals,
and an additional post.prob column reports the posterior
probability of direction.
Returns fixed-effect rows (the log-k coefficients) and/or variance-component rows, following the broom coefficient-table contract.
## S3 method for class 'beezdiscounting_tmb' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )## S3 method for class 'beezdiscounting_tmb' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal", "log"), ... )
x |
A |
effects |
Character vector: |
report_space |
Reporting scale for fixed-effect |
... |
Unused. |
estimate and std.error are reported on the report_space scale for the
fixed-effect (beta_k) rows. statistic and p.value are always computed
on the estimation (log-k) scale. Wald statistics are not recomputed after
back-transforming (broom convention; see the summary() note for details).
Variance-component rows carry NA for statistic and p.value and are not
affected by report_space.
A tibble with exactly 8 columns in this order: term, estimate,
std.error, statistic, p.value, component, estimate_scale,
term_display. Fixed-effect rows carry component == "fixed"; the
shape-parameter row (2-parameter equations only) carries
component == "shape"; variance rows carry component == "variance".
Extract timing metrics from 5.5 trial delay discounting from Qualtrics template
timing_dd(df)timing_dd(df)
df |
A dataframe containing all the columns |
Currently assumes the attending questions are present and labeled "Attend-LL" and "Attend-SS"
A dataframe with ResponseId, indexes, values and timing
timing_dd(five.fivetrial_dd)timing_dd(five.fivetrial_dd)
Extract timing metrics from 5.5 trial probability discounting from Qualtrics template
timing_pd(df)timing_pd(df)
df |
A dataframe containing all the columns |
Currently assumes the attending questions are present and labeled "Attend-LL" and "Attend-SS"
A dataframe with ResponseId, indexes, values and timing
timing_pd(five.fivetrial_pd)timing_pd(five.fivetrial_pd)
Descriptive (Young 2018): the 2-RE slope SDs and their correlation (the
per-subject magnitude/delay sensitivities); pooled (random_slopes = FALSE)
returns a 0-row frame. Structural: the log-k random-intercept SD.
## S3 method for class 'beezdiscounting_choice' VarCorr(x, sigma = 1, ...)## S3 method for class 'beezdiscounting_choice' VarCorr(x, sigma = 1, ...)
x |
A |
sigma |
Ignored (present for |
... |
Unused. |
A data frame of variance components. Descriptive: Group, Term,
Variance, StdDev, Corr (one row per random slope; 0 rows when
pooled). Structural: Group, Term, StdDev, Scale.
For a 2-RE fit returns the 2x2 covariance with StdDev and the joint
correlation (a structural 0 for pdDiag; the first row's Corr is NA
by convention). The Term column reflects the fitted pair: (k, phi) for a
phi-target fit (k + phi ~ 1) or (k, s) for an s-target fit (k + s ~ 1,
GM/Rachlin only). For a 1-RE fit (k ~ 1) returns the single
random-intercept SD on the log-k scale.
## S3 method for class 'beezdiscounting_tmb' VarCorr(x, sigma = 1, ...)## S3 method for class 'beezdiscounting_tmb' VarCorr(x, sigma = 1, ...)
x |
A |
sigma |
Ignored (present for the |
... |
Unused. |
A data frame with Group, Term, Variance, StdDev, and (2-RE)
Corr.
Reshape MCQ data wide to long
wide_to_long_mcq(dat, items = 27)wide_to_long_mcq(dat, items = 27)
dat |
Wide format MCQ assuming subject id is in column 1 |
items |
Number of MCQ questions |
Long format data frame
wide_to_long_mcq(long_to_wide_mcq(mcq27))wide_to_long_mcq(long_to_wide_mcq(mcq27))
Reshape MCQ data from wide (as used in the 21- and 27-Item Monetary Choice Questionnaire Automated Scorer) to long
wide_to_long_mcq_excel(dat)wide_to_long_mcq_excel(dat)
dat |
Wide format MCQ data as used in the Excel Automated Scorers |
Long format data frame
wide_to_long_mcq_excel(long_to_wide_mcq_excel(generate_data_mcq(2)))wide_to_long_mcq_excel(long_to_wide_mcq_excel(generate_data_mcq(2)))