| Title: | Behavioral Economic Easy Demand |
|---|---|
| Description: | Facilitates many of the analyses performed in studies of behavioral economic demand. The package supports commonly-used options for modeling operant demand including (1) data screening proposed by Stein, Koffarnus, Snider, Quisenberry, & Bickel (2015; <doi:10.1037/pha0000020>), (2) fitting models of demand such as linear (Hursh, Raslear, Bauman, & Black, 1989, <doi:10.1007/978-94-009-2470-3_22>), exponential (Hursh & Silberberg, 2008, <doi:10.1037/0033-295X.115.1.186>) and modified exponential (Koffarnus, Franck, Stein, & Bickel, 2015, <doi:10.1037/pha0000045>), (3) calculating numerous measures relevant to applied behavioral economists (Intensity, Pmax, Omax), (4) nonlinear mixed-effects demand models with subject-level random effects, fit either with 'nlme' or with a 'TMB' backend that also provides a two-part (hurdle) model for zero-inflated consumption, and (5) Monte Carlo power analysis for condition differences in demand parameters. Also supports plotting and comparing data. |
| Authors: | Brent Kaplan [aut, cre, cph]
|
| Maintainer: | Brent Kaplan <[email protected]> |
| License: | GPL (>= 2) |
| Version: | 0.3.0 |
| Built: | 2026-09-22 00:20:29 UTC |
| Source: | https://github.com/brentkaplan/beezdemand |
AIC for Hurdle Demand Model
## S3 method for class 'beezdemand_hurdle' AIC(object, ..., k = 2)## S3 method for class 'beezdemand_hurdle' AIC(object, ..., k = 2)
object |
A |
... |
Additional arguments (unused). |
k |
Penalty per parameter. Default is 2 (standard AIC). |
A numeric AIC value.
Creates annotation layer
annotation_logticks2( base = 10, sides = "bl", scaled = TRUE, short = unit(0.1, "cm"), mid = unit(0.2, "cm"), long = unit(0.3, "cm"), colour = "black", size = 0.5, linetype = 1, alpha = 1, data = data.frame(x = NA), color = NULL, ... )annotation_logticks2( base = 10, sides = "bl", scaled = TRUE, short = unit(0.1, "cm"), mid = unit(0.2, "cm"), long = unit(0.3, "cm"), colour = "black", size = 0.5, linetype = 1, alpha = 1, data = data.frame(x = NA), color = NULL, ... )
base |
base for drawing in scale |
sides |
sides to draw, by default bottom and left |
scaled |
true by default |
short |
short tick settings |
mid |
mid tick settings |
long |
long tick settings |
colour |
default to black colour |
size |
size for labels |
linetype |
default linetype |
alpha |
default alpha level |
data |
data to include |
color |
colors to include |
... |
additional arguments |
Inherit and extend layer for use in ggplot draw
ggplot2 layer
Shawn Gilroy [email protected]
Compare nested hurdle demand models using likelihood ratio tests.
## S3 method for class 'beezdemand_hurdle' anova(object, ...)## S3 method for class 'beezdemand_hurdle' anova(object, ...)
object |
A |
... |
Additional |
All models must be fit to the same data. Models are ordered by degrees of freedom, and sequential likelihood ratio tests are performed.
An object of class anova.beezdemand containing:
Data frame with model comparison statistics
Likelihood ratio test results
data(apt) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) anova(fit2, fit3)data(apt) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) anova(fit2, fit3)
Compare nested NLME demand models using likelihood ratio tests.
## S3 method for class 'beezdemand_nlme' anova(object, ...)## S3 method for class 'beezdemand_nlme' anova(object, ...)
object |
A |
... |
Additional |
For NLME models, this method delegates to nlme::anova.lme() on the
underlying model objects when possible.
An object of class anova.beezdemand containing model comparison statistics.
data(ko) fit1 <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben", random_effects = Q0 ~ 1) fit2 <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben", random_effects = Q0 + alpha ~ 1) anova(fit1, fit2)data(ko) fit1 <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben", random_effects = Q0 ~ 1) fit2 <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben", random_effects = Q0 + alpha ~ 1) anova(fit1, fit2)
For a single fit, computes joint Wald-chi-square tests on grouped
fixed-effect coefficients. For multiple fits passed via ..., performs
sequential likelihood-ratio tests on nested models.
## S3 method for class 'beezdemand_tmb' anova( object, ..., test = c("Wald", "LRT", "AIC"), terms = NULL, group_by = c("auto", "parameter", "term") )## S3 method for class 'beezdemand_tmb' anova( object, ..., test = c("Wald", "LRT", "AIC"), terms = NULL, group_by = c("auto", "parameter", "term") )
object |
A |
... |
Additional |
test |
One of |
terms |
|
group_by |
One of |
The Wald statistic for a coefficient block is
, asymptotically
on length(beta_g) df. An exactly rank-deficient
(perfectly collinear) block has a singular and triggers
an explicit error. A near-collinear block is not detected:
stays invertible and becomes large and unstable,
so such a value should be interpreted with caution. For multiple fits,
the likelihood-ratio test screens for detectable non-nesting (equal or
decreasing degrees of freedom, or a larger model with lower
log-likelihood) but cannot prove nesting from log-likelihood and df
alone. Pass genuinely nested models. Rows of the multiple-fit table
are ordered by ascending degrees of freedom, and the Model
column labels them Model1, Model2, ... in that order.
For a single fit, a tibble with Group, Chisq, df, p.value.
For multiple fits, a tibble with Model, df, AIC, Chisq,
`Pr(>Chisq)`.
anova.beezdemand_nlme(), confint.beezdemand_tmb().
data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) anova(fit) anova(fit, group_by = "parameter")data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) anova(fit) anova(fit, group_by = "parameter")
A dataset containing alcohol purchase task data for a small number of participants
aptapt
Long-form data.frame with columns: id, x, y. Participants were asked how many standard sized alcoholic beverages they would buy at various prices.
A larger dataset containing alcohol purchase task data with demographic covariates. Suitable for testing hurdle models and mixed-effects models with covariates.
apt_fullapt_full
A data frame with 18,700 rows and 8 columns:
Unique participant identifier (1-1100)
Participant gender (Male/Female)
Participant age in years
Number of binge drinking episodes
Total number of drinks consumed
Total hours spent drinking
Price point for the purchase task
Number of drinks participant would purchase at price x
data(apt_full) # Use a subset for quick demonstration apt_sub <- apt_full[apt_full$id %in% unique(apt_full$id)[1:20], ] fit <- fit_demand_hurdle(apt_sub, y_var = "y", x_var = "x", id_var = "id") summary(fit)data(apt_full) # Use a subset for quick demonstration apt_sub <- apt_full[apt_full$id %in% unique(apt_full$id)[1:20], ] fit <- fit_demand_hurdle(apt_sub, y_var = "y", x_var = "x", id_var = "id") summary(fit)
Returns the original data with fitted values and residuals from individual demand curve fits. This enables easy model diagnostics and visualization with the tidyverse.
## S3 method for class 'beezdemand_fixed' augment(x, newdata = NULL, ...)## S3 method for class 'beezdemand_fixed' augment(x, newdata = NULL, ...)
x |
An object of class |
newdata |
Optional data frame of new data for prediction. If NULL, uses the original data from the model. |
... |
Additional arguments (currently unused). |
For "hs" equation models where fitting is done on the log10 scale, fitted values are back-transformed to the natural scale.
A tibble containing the original data plus:
Fitted demand values on the response scale
Residuals (observed - fitted)
data(apt) fit <- fit_demand_fixed(apt, y_var = "y", x_var = "x", id_var = "id") augmented <- augment(fit) # Plot residuals by subject library(ggplot2) ggplot(augmented, aes(x = .fitted, y = .resid)) + geom_point(alpha = 0.5) + facet_wrap(~id) + geom_hline(yintercept = 0, linetype = "dashed")data(apt) fit <- fit_demand_fixed(apt, y_var = "y", x_var = "x", id_var = "id") augmented <- augment(fit) # Plot residuals by subject library(ggplot2) ggplot(augmented, aes(x = .fitted, y = .resid)) + geom_point(alpha = 0.5) + facet_wrap(~id) + geom_hline(yintercept = 0, linetype = "dashed")
Returns the original data with fitted values, residuals, and predictions from a hurdle demand model, in the format expected by tidyverse plotting and diagnostic tools.
## S3 method for class 'beezdemand_hurdle' augment(x, newdata = NULL, component = c("combined", "continuous"), ...)## S3 method for class 'beezdemand_hurdle' augment(x, newdata = NULL, component = c("combined", "continuous"), ...)
x |
An object of class |
newdata |
Optional data frame of new data for prediction. If NULL, uses the original data from the model. |
component |
Character. Which residuals to compute:
|
... |
Additional arguments (currently unused). |
The hurdle model has two components, each requiring different diagnostic approaches:
Continuous residuals (component = "continuous"): Standard log-scale
residuals log(y) - mu for observations where y > 0. Zeros are excluded
(NA). Assesses whether the lognormal conditional distribution is well-
specified.
Randomized quantile residuals (component = "combined", default):
Following Dunn & Smyth (1996), maps each observation through the fitted
hurdle CDF and then the standard normal quantile function. If the model is
correctly specified, these residuals are exactly N(0,1) regardless of which
component generated the observation. For zeros, a uniform random variate
within [0, P(zero)] breaks ties that would otherwise create a spike in
the QQ plot.
A tibble containing the original data plus:
Fitted demand values (natural scale)
Fitted values on log scale (Part II mean)
Predicted probability of consumption (1 - P(zero))
Residuals (type depends on component; see above)
Residuals on response scale (y - .fitted)
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") augmented <- augment(fit) # Plot residuals library(ggplot2) ggplot(augmented, aes(x = .fitted, y = .resid)) + geom_point(alpha = 0.5) + geom_hline(yintercept = 0, linetype = "dashed")data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") augmented <- augment(fit) # Plot residuals library(ggplot2) ggplot(augmented, aes(x = .fitted, y = .resid)) + geom_point(alpha = 0.5) + geom_hline(yintercept = 0, linetype = "dashed")
Returns the original data with fitted values and residuals from a nonlinear mixed-effects demand model. This enables easy model diagnostics and visualization with the tidyverse.
## S3 method for class 'beezdemand_nlme' augment(x, newdata = NULL, ...)## S3 method for class 'beezdemand_nlme' augment(x, newdata = NULL, ...)
x |
An object of class |
newdata |
Optional data frame of new data for prediction. If NULL, uses the original data from the model. |
... |
Additional arguments (currently unused). |
The fitted values and residuals are on the same scale as the response variable
used in the model. For equation_form = "zben", this is the LL4-transformed
scale. For equation_form = "simplified" or "exponentiated", this is the natural
consumption scale.
To back-transform predictions to the natural scale for "zben" models, use:
ll4_inv(augmented$.fitted)
A tibble containing the original data plus:
Fitted values on the model scale (may be transformed, e.g., LL4)
Residuals on the model scale
Fitted values from fixed effects only (population-level)
data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") augmented <- augment(fit) # Plot residuals library(ggplot2) ggplot(augmented, aes(x = .fitted, y = .resid)) + geom_point(alpha = 0.5) + geom_hline(yintercept = 0, linetype = "dashed")data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") augmented <- augment(fit) # Plot residuals library(ggplot2) ggplot(augmented, aes(x = .fitted, y = .resid)) + geom_point(alpha = 0.5) + geom_hline(yintercept = 0, linetype = "dashed")
Augment a beezdemand_tmb Model
## S3 method for class 'beezdemand_tmb' augment(x, newdata = NULL, ...)## S3 method for class 'beezdemand_tmb' augment(x, newdata = NULL, ...)
x |
A |
newdata |
Optional data frame. |
... |
Additional arguments. |
A tibble with original data plus .fitted, .resid, and
.std_resid columns. Residuals are computed on the model's native scale
(log scale for "exponential", natural/LL4 scale for others) to match the
C++ likelihood.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(augment(fit))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(augment(fit))
Augment a Cross-Price Demand Model (Linear)
## S3 method for class 'cp_model_lm' augment(x, ...)## S3 method for class 'cp_model_lm' augment(x, ...)
x |
A |
... |
Additional arguments (unused). |
A tibble with the original modelling data and added .fitted and
.resid columns.
Augment a Cross-Price Demand Model (Mixed-Effects)
## S3 method for class 'cp_model_lmer' augment(x, ...)## S3 method for class 'cp_model_lmer' augment(x, ...)
x |
A |
... |
Additional arguments (unused). |
A tibble with the original modelling data and added .fitted,
.resid, and .fixed (population-level prediction with random effects
set to zero) columns.
Augment a Cross-Price Demand Model (Nonlinear)
## S3 method for class 'cp_model_nls' augment(x, ...)## S3 method for class 'cp_model_nls' augment(x, ...)
x |
A |
... |
Additional arguments (unused). |
A tibble with the original modelling data and added .fitted and
.resid columns. Both are on the model's response scale: raw y for
equation = "exponentiated" / "additive", and log10(y) for
equation = "exponential" (which is fit to log10(y)).
Methods for printing, summarizing, and visualizing objects of class
beezdemand_descriptive created by get_descriptive_summary().
## S3 method for class 'beezdemand_descriptive' print(x, ...) ## S3 method for class 'beezdemand_descriptive' summary(object, ...) ## S3 method for class 'beezdemand_descriptive' plot(x, x_trans = "identity", y_trans = "identity", show_zeros = FALSE, ...)## S3 method for class 'beezdemand_descriptive' print(x, ...) ## S3 method for class 'beezdemand_descriptive' summary(object, ...) ## S3 method for class 'beezdemand_descriptive' plot(x, x_trans = "identity", y_trans = "identity", show_zeros = FALSE, ...)
x, object
|
A |
... |
Additional arguments (currently unused) |
x_trans |
Character string specifying x-axis transformation. Options:
"identity" (default), "log10", "log", "sqrt". See |
y_trans |
Character string specifying y-axis transformation. Options: "identity" (default), "log10", "log", "sqrt", "pseudo_log" (signed log). |
show_zeros |
Logical indicating whether to show proportion of zeros as labels on the boxplot (default: FALSE) |
Displays a compact summary showing the number of subjects and prices analyzed, plus a preview of the statistics table.
Provides extended information including:
Data summary (subjects, prices analyzed)
Distribution of means across prices (min, median, max)
Proportion of zeros by price (range)
Missing data summary
Creates a boxplot showing the distribution of consumption at each price point. Features:
Red cross markers indicate means
Boxes show median and quartiles
Whiskers extend to 1.5 * IQR
Supports axis transformations (log, sqrt, etc.)
Uses modern beezdemand styling via theme_apa()
print() - Returns the object invisibly (called for side effects)
summary() - Returns a list with extended summary information
plot() - Returns a ggplot2 object
data(apt, package = "beezdemand") desc <- get_descriptive_summary(apt) # Print compact summary print(desc) # Extended summary summary(desc) # Default boxplot plot(desc) # With log-transformed y-axis plot(desc, y_trans = "log10") # With pseudo-log y-axis (defined at zero) plot(desc, y_trans = "pseudo_log")data(apt, package = "beezdemand") desc <- get_descriptive_summary(apt) # Print compact summary print(desc) # Extended summary summary(desc) # Default boxplot plot(desc) # With log-transformed y-axis plot(desc, y_trans = "log10") # With pseudo-log y-axis (defined at zero) plot(desc, y_trans = "pseudo_log")
Methods for printing, summarizing, and visualizing objects of class
beezdemand_empirical created by get_empirical_measures().
## S3 method for class 'beezdemand_empirical' print(x, ...) ## S3 method for class 'beezdemand_empirical' summary(object, ...) ## S3 method for class 'beezdemand_empirical' plot(x, type = "histogram", ...)## S3 method for class 'beezdemand_empirical' print(x, ...) ## S3 method for class 'beezdemand_empirical' summary(object, ...) ## S3 method for class 'beezdemand_empirical' plot(x, type = "histogram", ...)
x, object
|
A |
... |
Additional arguments passed to plotting functions |
type |
Character string specifying plot type. Options:
|
Displays a compact summary showing the number of subjects analyzed and a preview of the empirical measures table.
Provides extended information including:
Data summary (subjects, zero consumption patterns, completeness)
Descriptive statistics for each empirical measure (min, median, mean, max, SD)
Missing data patterns
Creates visualizations of empirical measures across subjects.
Histogram type (default):
Six-panel faceted plot showing distribution of each measure
Helps identify central tendencies and outliers
Matrix type:
Scatterplot matrix (pairs plot) showing relationships between measures
Useful for identifying correlated demand metrics
Lower triangle: scatterplots with smoothed trend lines
Diagonal: density plots
Upper triangle: correlation coefficients
print() - Returns the object invisibly (called for side effects)
summary() - Returns a list with extended summary information
plot() - Returns a ggplot2 object
data(apt, package = "beezdemand") emp <- get_empirical_measures(apt) # Print compact summary print(emp) # Extended summary summary(emp) # Histogram of measure distributions plot(emp) # Scatterplot matrix plot(emp, type = "matrix")data(apt, package = "beezdemand") emp <- get_empirical_measures(apt) # Print compact summary print(emp) # Extended summary summary(emp) # Histogram of measure distributions plot(emp) # Scatterplot matrix plot(emp, type = "matrix")
BIC for Hurdle Demand Model
## S3 method for class 'beezdemand_hurdle' BIC(object, ...)## S3 method for class 'beezdemand_hurdle' BIC(object, ...)
object |
A |
... |
Additional arguments (unused). |
A numeric BIC value.
Computes confidence intervals on derived demand metrics (Pmax, Omax, Qmax,
EV, elasticity-at-Pmax) for a TMB mixed-effects demand fit, via a parametric
bootstrap. Draws of the fixed-effect parameter vector are taken from the
asymptotic Gaussian sampling approximation
(the distribution Wald intervals assume; no prior is involved), mapped to
per-condition through the model's fixed-effect design,
passed through the canonical Pmax/Omax engine, and summarized by empirical
quantiles.
For a factor-expanded fit, one CI is returned per factor cell (e.g. one row
per gender level); for an intercept-only fit, one row per statistic with
condition = NA. The per-cell point estimate reproduces
calc_group_metrics(fit, at = cell); the bootstrap supplies only
the interval.
boot_demand( fit, statistics = c("Pmax", "Omax", "EV"), method = c("parametric"), R = 1000L, ci_level = 0.95, at = NULL, seed = NULL, ... )boot_demand( fit, statistics = c("Pmax", "Omax", "EV"), method = c("parametric"), R = 1000L, ci_level = 0.95, at = NULL, seed = NULL, ... )
fit |
A |
statistics |
Character vector; any of
|
method |
Resampling scheme. Only |
R |
Integer number of bootstrap draws; minimum 100, default 1000. |
ci_level |
Confidence level for the empirical-quantile interval (default 0.95). |
at |
Optional named list of factor-level filters / continuous-covariate
value overrides, with the same shape as the |
seed |
Optional integer seed for reproducible draws. The caller's RNG state is left unperturbed. |
... |
Reserved for future extension; must be empty. Unknown arguments
(e.g. a misspelled |
The parametric bootstrap is asymptotically equivalent to the delta method but avoids its linearization, so it is preferable for the strongly nonlinear derived metrics (Pmax/Omax via Lambert-W). Draws are fixed-effect-only (population / per-condition metrics); per-subject metric CIs would require random-effect-aware draws and are out of scope for now.
When k is estimated, its uncertainty is propagated (the log_k column is in
the draw matrix); when k is fixed, the fixed value is used. The point
estimate always uses the point k, matching calc_group_metrics().
Note that percentile intervals of a nonlinear transform are not guaranteed to
bracket the point estimate; conf.low <= conf.high always holds, but
conf.low <= estimate <= conf.high may not at boundary cases.
Some draws can leave a metric's domain (e.g. Pmax is undefined when a drawn
k falls below the Lambert-W threshold). Such non-finite draws are excluded
from the quantiles; the per-row count of excluded draws is recorded in
attr(x, "n_nonfinite") (so the realized draw count is R minus that). If
every draw of a requested metric/condition is non-finite, an error is
raised because the interval is undefined.
A tibble with one row per (statistic, condition):
Metric name.
Factor-cell label (e.g. "gender=Male"); NA when
the fit has no factors.
Point estimate (from the coefficient vector and point k).
Empirical-quantile interval bounds.
The confidence level used.
The bootstrap settings are attached as attributes "method", "R", and
"seed", plus "n_nonfinite" (a per-row count of draws excluded as
non-finite).
calc_group_metrics, fit_demand_tmb,
confint.beezdemand_tmb
data(apt, package = "beezdemand") fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) boot_demand(fit, statistics = c("Pmax", "Omax", "EV"), R = 500, seed = 1)data(apt, package = "beezdemand") fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) boot_demand(fit, statistics = c("Pmax", "Omax", "EV"), R = 500, seed = 1)
Calculates group-level (population) Omax and Pmax from a fitted hurdle demand model.
calc_group_metrics(object, ...)calc_group_metrics(object, ...)
object |
A fitted |
... |
Method-specific arguments. The |
A named list with group-level Pmax, Omax, and Qmax.
calc_omax_pmax, fit_demand_hurdle
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") calc_group_metrics(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") calc_group_metrics(fit)
Calculate Population-Level Demand Metrics for TMB Model
## S3 method for class 'beezdemand_tmb' calc_group_metrics(object, at = NULL, ...)## S3 method for class 'beezdemand_tmb' calc_group_metrics(object, at = NULL, ...)
object |
A |
at |
Named list of factor-level filters or continuous-covariate
value overrides (e.g. |
... |
Additional arguments (currently unused). |
A list with Pmax, Omax, Qmax, elasticity_at_pmax,
method, and conditioned_on describing the reference point used.
The conditioned_on field reports the actual conditioning applied
(covariate values used, factor treatment per factor) so programmatic
consumers do not have to re-derive it.
For derived metrics (Pmax/Omax/Qmax) that depend nonlinearly on Q0
and alpha jointly, this function marginalizes parameters first then
derives metrics:
Compute log-Q0 and log-alpha EMMs at each cell of the reference
grid produced by .tmb_build_emm_ref_grid().
Marginalize each parameter across factor cells with equal weights (matches the emmeans default).
Derive Pmax/Omax/Qmax from the marginalized log-parameters at the user-supplied (or training-mean default) covariate point.
The result is "metrics evaluated at the average parameter values" rather
than "average metrics across cells". The two answers differ for nonlinear
transforms. The convention matches the parameter-level marginalization
used by get_demand_param_emms().
This method averages the log-scale linear predictors with equal weight
over the factor cells observed in the fitting data and then
exponentiates. The NLME method
(calc_group_metrics.beezdemand_nlme()) follows the same policy:
per-cell EMMs from emmeans, rows for unobserved cells dropped,
equal-weight geometric mean over the remaining cells. Cells of the full
factorial crossing with no subjects are never averaged over by either
backend. The two agree for any design fit in log space and can differ
only when the NLME fit uses param_space = "natural". In both
backends continuous covariates are held at the training mean unless
at supplies a single value; a multi-value continuous at
entry warns and uses its first value.
fit_demand_tmb,
get_demand_param_emms.beezdemand_tmb
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) calc_group_metrics(fit) # Conditioned at a specific covariate value: # calc_group_metrics(fit_with_cov, at = list(FTND_z = 1))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) calc_group_metrics(fit) # Conditioned at a specific covariate value: # calc_group_metrics(fit_with_cov, at = list(FTND_z = 1))
Calculate Observed Pmax/Omax Grouped by ID
calc_observed_pmax_omax( data, id_var = "id", price_var = "x", consumption_var = "y" )calc_observed_pmax_omax( data, id_var = "id", price_var = "x", consumption_var = "y" )
data |
Data frame with id, price, and consumption columns |
id_var |
Name of ID column |
price_var |
Name of price column |
consumption_var |
Name of consumption column |
Data frame with observed pmax/omax for each subject
data(apt, package = "beezdemand") calc_observed_pmax_omax(apt, id_var = "id", price_var = "x", consumption_var = "y")data(apt, package = "beezdemand") calc_observed_pmax_omax(apt, id_var = "id", price_var = "x", consumption_var = "y")
Calculates the maximum expenditure (Omax) and the price at maximum expenditure (Pmax) for the exponential demand model used in the two-part hurdle model.
calc_omax_pmax(Q0, k, alpha, price_range = NULL)calc_omax_pmax(Q0, k, alpha, price_range = NULL)
Q0 |
Intensity parameter (consumption at price 0). |
k |
Scaling parameter for the exponential decay. |
alpha |
Elasticity parameter (rate of decay). |
price_range |
Numeric vector of length 2 specifying the price range
to search for Pmax. Default is |
For the demand function:
Expenditure is E(p) = p * Q(p). Omax is the maximum of E(p) and Pmax is the price at which this maximum occurs. These are found numerically.
The search range is automatically adjusted based on alpha to ensure the maximum is found. For small alpha values, Pmax can be quite large.
A named list with:
Price at maximum expenditure
Maximum expenditure (price * quantity)
Quantity at Pmax
calc_group_metrics, fit_demand_hurdle
# Calculate for group-level parameters calc_omax_pmax(Q0 = 10, k = 2, alpha = 0.5) # With k > e (~2.718), a strict interior maximum exists calc_omax_pmax(Q0 = 10, k = 3, alpha = 0.5)# Calculate for group-level parameters calc_omax_pmax(Q0 = 10, k = 2, alpha = 0.5) # With k > e (~2.718), a strict interior maximum exists calc_omax_pmax(Q0 = 10, k = 3, alpha = 0.5)
Calculates Amplitude and Persistence latent factors from demand metrics for various beezdemand model objects.
calculate_amplitude_persistence( fit, amplitude = c("Intensity", "Q0d", "Q0"), persistence = c("BP0", "Pmaxe", "Omaxe", "Alpha"), use_inv_alpha = TRUE, strict = TRUE, min_persistence_components = 2L, empirical_y_var = NULL, basis_means = NULL, basis_sds = NULL, ... )calculate_amplitude_persistence( fit, amplitude = c("Intensity", "Q0d", "Q0"), persistence = c("BP0", "Pmaxe", "Omaxe", "Alpha"), use_inv_alpha = TRUE, strict = TRUE, min_persistence_components = 2L, empirical_y_var = NULL, basis_means = NULL, basis_sds = NULL, ... )
fit |
An object of class |
amplitude |
Character vector of column names to consider for the Amplitude factor.
The function will use the first column found in the data. Default is
|
persistence |
Character vector of column names to include in the Persistence factor.
Default is |
use_inv_alpha |
Logical. If "Alpha" (or a variation) is present in |
strict |
Logical. If |
min_persistence_components |
Integer. Minimum number of non-missing standardized
persistence components required to compute |
empirical_y_var |
For |
basis_means |
Optional named numeric vector of means to use for Z-score standardization.
Names must match the columns used (e.g., |
basis_sds |
Optional named numeric vector of standard deviations to use for Z-score standardization. Names must match the columns used. If NULL (default), the sample SDs are used. |
... |
Additional arguments passed to methods. |
This function calculates Amplitude and Persistence by:
Extracting the relevant demand metrics from the fit object.
Resolving requested metric columns (case-insensitive, with limited synonym support for common beezdemand outputs).
Inverting Alpha if requested (1/Alpha).
Standardizing (Z-scoring) the metrics. If basis_means and basis_sds are provided,
they are used; otherwise, the current sample's statistics are used.
Aggregating the Z-scores into the two latent factors.
Amplitude is defined by the variable specified in amplitude (typically Intensity/Q0).
Persistence is defined as the mean of the standardized values of the variables
specified in persistence (typically Breakpoint, Pmax, Omax, and 1/Alpha).
A data frame with the original ID and calculated Amplitude and Persistence scores, along with the standardized (Z-scored) constituent metrics.
beezdemand_fixed: Extracts metrics from fit$results.
beezdemand_hurdle: Extracts metrics from fit$subject_pars.
beezdemand_nlme: Calculates subject-specific parameters from fixed and random effects.
Parameters Q0 and Alpha are assumed to be on log10 scale for zben and simplified
equations and are converted to linear scale. Omax and Pmax are calculated empirically
from predictions. Breakpoint is calculated empirically from the raw data.
data(apt, package = "beezdemand") fit <- FitCurves(apt, "hs", k = "share") calculate_amplitude_persistence(fit)data(apt, package = "beezdemand") fit <- FitCurves(apt, "hs", k = "share") calculate_amplitude_persistence(fit)
Cross-price style data with cannabis and cigarette context.
cannabisCigarettescannabisCigarettes
A data frame with columns including: id, x, y, commodity, and auxiliary fields (Q1035, CigPrice, CanPrice, variable, value).
# data(cannabisCigarettes)# data(cannabisCigarettes)
Changes demand data
ChangeData( dat, nrepl = 1, replnum = 0.01, rem0 = FALSE, remq0e = FALSE, replfree = NULL, xcol = "x", ycol = "y", idcol = "id" )ChangeData( dat, nrepl = 1, replnum = 0.01, rem0 = FALSE, remq0e = FALSE, replfree = NULL, xcol = "x", ycol = "y", idcol = "id" )
dat |
A long form dataframe |
nrepl |
Number of zeros to replace with replacement value (replnum). Can accept either a number or "all" if all zeros should be replaced. Default is to replace the first zero only |
replnum |
Value to replace zeros. Default is .01 |
rem0 |
If TRUE, removes all 0s in consumption data prior to analysis. Default value is FALSE |
remq0e |
If TRUE, removes consumption and price where price == 0. Default value is FALSE |
replfree |
Optionally replaces price == 0 with specified value. |
xcol |
Column name in dataframe that signifies x values (usually price or the IV) |
ycol |
Column name in dataframe that signifies y values (usually consumption or the DV) |
idcol |
Column name in dataframe that signifies identifying id grouping |
Change demand data in various ways. Ways include replacing any number of 0 values with a replacement number (or remove them completely), removing price and consumption at free, replacing free with some number. This will soon replace ReplaceZeros and certain arguments in FitCurves.
Long form dataframe resembling the originally provided dataframe
Brent Kaplan [email protected]
## Change just the first instance of 0 within each unique value of id with .1 ChangeData(apt, nrepl = 1, replnum = .1)## Change just the first instance of 0 within each unique value of id with .1 ChangeData(apt, nrepl = 1, replnum = .1)
Performs diagnostic checks on fitted demand models, returning information about convergence, boundary conditions, and residual patterns.
check_demand_model(object, ...) ## S3 method for class 'beezdemand_hurdle' check_demand_model(object, ...) ## S3 method for class 'beezdemand_nlme' check_demand_model(object, ...) ## S3 method for class 'beezdemand_fixed' check_demand_model(object, ...) ## S3 method for class 'beezdemand_tmb' check_demand_model(object, ...)check_demand_model(object, ...) ## S3 method for class 'beezdemand_hurdle' check_demand_model(object, ...) ## S3 method for class 'beezdemand_nlme' check_demand_model(object, ...) ## S3 method for class 'beezdemand_fixed' check_demand_model(object, ...) ## S3 method for class 'beezdemand_tmb' check_demand_model(object, ...)
object |
A fitted model object of class |
... |
Additional arguments passed to methods. |
The function checks for:
Convergence status and optimization messages
Parameters at or near boundaries
Residual patterns (heteroscedasticity, outliers)
Random effect variance estimates near zero
For fit_demand_tmb() fits with estimate_k = TRUE, whether the
fit shows the signature of an unidentified k: an implausible k, a decay
exponent alpha * Q0 * price that never leaves its linear regime over
the observed prices, a degenerate alpha, or log_k resting on a
user-supplied bound. This is a screen, not a formal test: no flag means
nothing was detected, not that k is identified.
Correlation matrices near singularity
An object of class beezdemand_diagnostics containing:
List with convergence status and messages
List with boundary condition warnings. For
beezdemand_tmb fits it also carries k_identification: NULL unless
k was estimated as a free parameter, otherwise the screen described in
Details.
Summary statistics for residuals
Summary of random effects (if applicable).
variances holds random-effect variances: on the log10 scale for
beezdemand_tmb fits, and as reported by nlme::VarCorr() for
beezdemand_nlme fits. beezdemand_tmb fits also carry sd_log10
(the corresponding standard deviations, matching
summary(fit)$variance_components) and sd_internal_log (the raw
natural-log-scale standard deviations used by the near-zero
degeneracy check).
Character vector of identified issues
Character vector of recommendations
This function is named check_demand_model() to avoid potential conflicts
with performance::check_model() from the performance package.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") diagnostics <- check_demand_model(fit) print(diagnostics)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") diagnostics <- check_demand_model(fit) print(diagnostics)
Modern interface for screening cross-price data with standardized output
vocabulary aligned with check_systematic_demand().
check_systematic_cp( data, trend_threshold = 0.025, bounce_threshold_down = 0.1, bounce_threshold_up = 0.1, bounce_threshold_none = 0.1, consecutive_zeros = 2, consecutive_nonzeros = 2, expected_down = FALSE, x_var = "x", y_var = "y", id_var = "id", by = NULL )check_systematic_cp( data, trend_threshold = 0.025, bounce_threshold_down = 0.1, bounce_threshold_up = 0.1, bounce_threshold_none = 0.1, consecutive_zeros = 2, consecutive_nonzeros = 2, expected_down = FALSE, x_var = "x", y_var = "y", id_var = "id", by = NULL )
data |
Data frame with columns: |
trend_threshold |
Numeric. Threshold for trend detection. Default |
bounce_threshold_down |
Numeric. Bounce threshold for upward trends. Default |
bounce_threshold_up |
Numeric. Bounce threshold for downward trends. Default |
bounce_threshold_none |
Numeric. Bounce threshold when no trend. Default |
consecutive_zeros |
Integer. Zeros for reversal detection. Default |
consecutive_nonzeros |
Integer. Non-zeros for return detection. Default |
expected_down |
Logical. Suppress reversal detection if TRUE. Default |
x_var |
Character. Name of the price column. Default |
y_var |
Character. Name of the consumption column. Default |
id_var |
Character. Name of the subject identifier column. Default |
by |
Optional character vector of column names to group by.
When supplied, the check is run separately within each unique
combination of the |
If the data contains an id column (or column specified by id_var), each
unique ID is checked separately. Otherwise, the entire dataset is treated
as a single pattern.
For cross-price data, the wrapper preserves the legacy meaning of
check_unsystematic_cp():
trend_direction and bounce_direction are taken directly from the legacy
function outputs.
trend_pass is set to NA because cross-price systematicity does not use a
separate trend “pass/fail” criterion in the same way as purchase-task
screening; instead, trend classification determines which bounce rule
applies.
bounce_stat is reported as the proportion relevant to the legacy bounce
rule for the detected trend_direction (or expected_down case), computed
from the legacy bounce counts and the number of price steps.
An object of class beezdemand_systematicity with the same structure
as check_systematic_demand(), with type = "cp".
data(etm) check <- check_systematic_cp(etm)data(etm) check <- check_systematic_cp(etm)
Modern interface for screening purchase task data using Stein et al. (2015)
criteria. Returns a structured object with standardized output vocabulary
that is consistent with check_systematic_cp().
check_systematic_demand( data, trend_threshold = 0.025, bounce_threshold = 0.1, max_reversals = 0, consecutive_zeros = 2, x_var = "x", y_var = "y", id_var = "id", by = NULL )check_systematic_demand( data, trend_threshold = 0.025, bounce_threshold = 0.1, max_reversals = 0, consecutive_zeros = 2, x_var = "x", y_var = "y", id_var = "id", by = NULL )
data |
Data frame in long format with columns: |
trend_threshold |
Numeric. Threshold for trend detection (log-log slope).
Default |
bounce_threshold |
Numeric. Threshold for bounce proportion. Default |
max_reversals |
Integer. Maximum allowed reversals from zero. Default |
consecutive_zeros |
Integer. Consecutive zeros required for reversal detection.
Default |
x_var |
Character. Name of the price column. Default |
y_var |
Character. Name of the consumption column. Default |
id_var |
Character. Name of the subject identifier column. Default |
by |
Optional character vector of column names to group by.
When supplied, the check is run separately within each unique
combination of the |
The results tibble contains standardized columns for both demand and
cross-price systematicity checks:
Subject identifier
"demand" for this function
DeltaQ statistic (log-log slope)
Threshold used
"down", "up", or "none"
Logical: passed trend criterion
Bounce proportion
Threshold used
"significant" or "none"
Logical: passed bounce criterion
Count of reversals from zero
Logical: passed reversals criterion
NA for demand (CP-specific)
Count of positive values
Logical: passed all criteria
An object of class beezdemand_systematicity with components:
Tibble with one row per subject containing systematicity metrics
"demand"
The original function call
Total number of subjects
Number of subjects passing all criteria
Number of subjects failing at least one criterion
data(apt) check <- check_systematic_demand(apt) print(check) summary(check) tidy(check) # Grouped check: results include group column data(apt_full) check_g <- check_systematic_demand(apt_full, by = "gender") check_g$resultsdata(apt) check <- check_systematic_demand(apt) print(check) summary(check) tidy(check) # Grouped check: results include group column data(apt_full) check_g <- check_systematic_demand(apt_full, by = "gender") check_g$results
Analyzes whether consumption data shows systematic trends or unsystematic patterns ("bounces") with respect to price. Includes detection of zero-value reversal/return sequences and allows flexible output based on the level of detail requested. See Rzeszutek et al. (in press) for more details.
check_unsystematic_cp( data, delta_threshold = 0.025, bounce_down_threshold = 0.1, bounce_up_threshold = 0.1, bounce_none_threshold = 0.1, rev_zeroes = 2, ret_nums = 2, expected_down = FALSE, verbose = FALSE, detailed = FALSE )check_unsystematic_cp( data, delta_threshold = 0.025, bounce_down_threshold = 0.1, bounce_up_threshold = 0.1, bounce_none_threshold = 0.1, rev_zeroes = 2, ret_nums = 2, expected_down = FALSE, verbose = FALSE, detailed = FALSE )
data |
A data frame with columns 'x' and 'y', where 'x' is price and 'y' is consumption. |
delta_threshold |
Numeric. Threshold for detecting log-scale trends (default 0.025). |
bounce_down_threshold |
Numeric. Minimum downward bounce proportion to count as significant in upward trends. |
bounce_up_threshold |
Numeric. Minimum upward bounce proportion to count as significant in downward trends. |
bounce_none_threshold |
Numeric. Minimum bounce proportion to count as significant in no-trend cases. |
rev_zeroes |
Integer. Length of zero sequences to detect reversals (default 2). |
ret_nums |
Integer. Length of non-zero sequences to detect returns (default 2). |
expected_down |
Logical. If TRUE, suppress reversal detection. |
verbose |
Logical. If TRUE, print intermediate values (default FALSE). |
detailed |
Logical. If TRUE, return additional columns including all trend/bounce flags. |
A data frame of class cp_unsystematic with core results:
Character: 'down', 'up', or 'none'.
Character: 'up', 'down', 'significant', or 'none'.
Logical. TRUE if any bounce pattern detected.
Integer. Number of upward changes meeting threshold.
Integer. Number of downward changes meeting threshold.
Integer. Detected reversals from 0 to non-0.
Integer. Detected returns from non-0 to 0.
If detailed = TRUE, returns additional columns:
Logical. Significant downward trend.
Logical. Significant upward trend.
Logical. No significant trend.
Logical. Significant bounce up in a downward trend.
Logical. Significant bounce down in an upward trend.
Logical. Significant bounces in no-trend data.
x_seq <- 10^(seq(-2, 2, length.out = 10)) pattern <- data.frame(x = x_seq, y = c(10, 5, 10, 9, 10, 13, 10, 10, 7, 9)) check_unsystematic_cp(pattern)x_seq <- 10^(seq(-2, 2, length.out = 10)) pattern <- data.frame(x = x_seq, y = c(10, 5, 10, 9, 10, 13, 10, 10, 7, 9)) check_unsystematic_cp(pattern)
Checks to ensure column names are specified
CheckCols(dat, xcol, ycol, idcol, groupcol = NULL)CheckCols(dat, xcol, ycol, idcol, groupcol = NULL)
dat |
Dataframe |
xcol |
Name of x column |
ycol |
Name of y column |
idcol |
Name of id column |
groupcol |
Name of group column |
Check column names
Dataframe
Brent Kaplan [email protected]
dat <- data.frame(price = 1:5, quantity = c(10, 8, 5, 2, 0), subj = rep(1, 5)) CheckCols(dat, xcol = "price", ycol = "quantity", idcol = "subj")dat <- data.frame(price = 1:5, quantity = c(10, 8, 5, 2, 0), subj = rep(1, 5)) CheckCols(dat, xcol = "price", ycol = "quantity", idcol = "subj")
Applies Stein, Koffarnus, Snider, Quisenberry, & Bickel's (2015) criteria for identification of nonsystematic purchase task data.
CheckUnsystematic(dat, deltaq = 0.025, bounce = 0.1, reversals = 0, ncons0 = 2)CheckUnsystematic(dat, deltaq = 0.025, bounce = 0.1, reversals = 0, ncons0 = 2)
dat |
Dataframe in long form. Columns are id, x, y. |
deltaq |
Numeric vector of length equal to one. The criterion by which the relative change in quantity purchased will be compared. Relative changes in quantity purchased below this criterion will be flagged. Default value is 0.025. |
bounce |
Numeric vector of length equal to one. The criterion by which the number of price-to-price increases in consumption that exceed 25% of initial consumption at the lowest price, expressed relative to the total number of price increments, will be compared. The relative number of price-to-price increases above this criterion will be flagged. Default value is 0.10. |
reversals |
Numeric vector of length equal to one. The criterion by which the number of reversals from number of consecutive (see ncons0) 0s will be compared. Number of reversals above this criterion will be flagged. Default value is 0. |
ncons0 |
Numer of consecutive 0s prior to a positive value is used to flag for a reversal. Value can be either 1 (relatively more conservative) or 2 (default; as recommended by Stein et al., (2015). |
This function applies the 3 criteria proposed by Stein et al., (2015) for identification of nonsystematic purchase task data. The three criteria include trend (deltaq), bounce, and reversals from 0. Also reports number of positive consumption values.
Dataframe
Brent Kaplan [email protected]
## Using all default values CheckUnsystematic(apt, deltaq = 0.025, bounce = 0.10, reversals = 0, ncons0 = 2) ## Specifying just 1 zero to flag as reversal CheckUnsystematic(apt, deltaq = 0.025, bounce = 0.10, reversals = 0, ncons0 = 1)## Using all default values CheckUnsystematic(apt, deltaq = 0.025, bounce = 0.10, reversals = 0, ncons0 = 2) ## Specifying just 1 zero to flag as reversal CheckUnsystematic(apt, deltaq = 0.025, bounce = 0.10, reversals = 0, ncons0 = 1)
Methods to extract coefficients from various cross-price demand model objects.
## S3 method for class 'cp_model_nls' coef(object, ...) ## S3 method for class 'cp_model_lm' coef(object, ...) ## S3 method for class 'cp_model_lmer' coef(object, fixed_only = FALSE, combine = TRUE, ...)## S3 method for class 'cp_model_nls' coef(object, ...) ## S3 method for class 'cp_model_lm' coef(object, ...) ## S3 method for class 'cp_model_lmer' coef(object, fixed_only = FALSE, combine = TRUE, ...)
object |
A cp_model_lm object |
... |
Additional arguments (not used). |
fixed_only |
Logical; if TRUE, returns only fixed effects. Default is FALSE. |
combine |
Logical; if TRUE and fixed_only=FALSE, returns fixed + random effects combined. Default is TRUE. |
Named vector of coefficients
A named numeric vector of model coefficients.
coef(cp_model_nls): Extract coefficients from a nonlinear cross-price model
coef(cp_model_lm): Extract coefficients from a linear cross-price model
coef(cp_model_lmer): Extract coefficients from a mixed-effects cross-price model
Extract Coefficients from Fixed-Effect Demand Fit
## S3 method for class 'beezdemand_fixed' coef(object, report_space = c("internal", "natural", "log10"), ...)## S3 method for class 'beezdemand_fixed' coef(object, report_space = c("internal", "natural", "log10"), ...)
object |
A |
report_space |
One of |
... |
Unused. |
A tibble with columns id, term, estimate, estimate_scale, term_display.
Extract Coefficients from Grouped Fixed-Effect Demand Fit
## S3 method for class 'beezdemand_fixed_grouped' coef(object, ...)## S3 method for class 'beezdemand_fixed_grouped' coef(object, ...)
object |
A beezdemand_fixed_grouped object |
... |
Additional arguments passed to |
A tibble with group columns prepended
Extract Coefficients from Hurdle Demand Model
## S3 method for class 'beezdemand_hurdle' coef(object, report_space = c("natural", "log10", "internal"), ...)## S3 method for class 'beezdemand_hurdle' coef(object, report_space = c("natural", "log10", "internal"), ...)
object |
An object of class |
report_space |
Character. One of |
... |
Additional arguments (currently unused). |
Named numeric vector of fixed effect coefficients.
Provides methods to extract fixed effects, random effects, or subject-specific
(combined fixed + random) coefficients from a beezdemand_nlme object.
This is an S3 method for the generic coef function.
## S3 method for class 'beezdemand_nlme' coef( object, type = "combined", report_space = c("internal", "natural", "log10"), ... )## S3 method for class 'beezdemand_nlme' coef( object, type = "combined", report_space = c("internal", "natural", "log10"), ... )
object |
A |
type |
Character, type of coefficients to extract. One of:
|
report_space |
Character. One of |
... |
Additional arguments passed to the underlying |
Depending on type:
type="fixed": A named numeric vector of fixed-effect coefficients.
type="random": A data frame (or list of data frames if multiple levels of grouping)
of random effects, as returned by ranef.nlme().
type="combined": A data frame where rows are subjects (from id_var)
and columns are the Q0 and alpha parameters, representing subject-specific
estimates (on the log10 scale).
fixef.beezdemand_nlme, ranef.beezdemand_nlme
data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") coef(fit, type = "fixed") coef(fit, type = "random") coef(fit, type = "combined")data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") coef(fit, type = "fixed") coef(fit, type = "random") coef(fit, type = "combined")
Extract coefficients from a fitted beezdemand_tmb model. The
type argument selects the return shape. The default,
"internal", is unchanged: a named numeric vector of the
optimizer's flat parameterization (entries include beta_q0,
beta_alpha, logsigma*, and any covariance
hyperparameters; intercepts are on the log scale because the optimizer
works in unconstrained space). This is the numeric-vector escape hatch
consumed by tooling such as car::deltaMethod and
multcomp::glht.
## S3 method for class 'beezdemand_tmb' coef(object, type = c("internal", "subject", "combined", "fixed"), ...)## S3 method for class 'beezdemand_tmb' coef(object, type = c("internal", "subject", "combined", "fixed"), ...)
object |
A |
type |
One of |
... |
Additional arguments (currently unused; supplying
|
type = "subject" (alias "combined") returns the
per-subject parameter tibble from get_subject_pars (with
expanded = NULL, so within-id factor expansion is auto-detected).
This is concept-parity with coef.beezdemand_nlme(type = "combined")
but not column-identical: it returns resolved per-subject parameters
(Q0, alpha, ...), not a per-design-term coefficient
matrix. type = "fixed" returns a one-row tibble of the
fixed-effect coefficients only (the beta_q0 / beta_alpha
block on the internal parameterization), excluding log_k,
logsigma*, and rho*.
Scale conversion is not performed here: supplying report_space
through ... is an error. Use get_subject_pars or
predict.beezdemand_tmb for natural-scale parameters.
For type = "internal", a named numeric vector. For
type = "subject"/"combined", a tibble with one row per
subject (or one row per subject-by-within-id-factor-level cell when
the fit has within-id factor variation). For type = "fixed", a
one-row tibble of fixed-effect coefficients.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) coef(fit) # raw optimizer vector (default, "internal") coef(fit, type = "subject") # per-subject parameter tibble coef(fit, type = "fixed") # fixed-effect coefficientsdata(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) coef(fit) # raw optimizer vector (default, "internal") coef(fit, type = "subject") # per-subject parameter tibble coef(fit, type = "fixed") # fixed-effect coefficients
Performs a likelihood ratio test comparing two nested hurdle demand models. Typically used to test whether adding the random effect on alpha (c_i) significantly improves model fit (3-RE vs 2-RE models).
compare_hurdle_models(model_full, model_reduced)compare_hurdle_models(model_full, model_reduced)
model_full |
A |
model_reduced |
A |
Invisibly returns a list with:
Likelihood ratio test statistic
Degrees of freedom
P-value from chi-squared distribution
Data frame with model comparison statistics
data(apt) fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) compare_hurdle_models(fit3, fit2)data(apt) fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) compare_hurdle_models(fit3, fit2)
Compare multiple demand models using information criteria and likelihood ratio tests (when applicable). Works with all beezdemand model classes.
compare_models(..., test = c("auto", "lrt", "none"))compare_models(..., test = c("auto", "lrt", "none"))
... |
Two or more model objects of class |
test |
Character; type of statistical test. One of:
|
Models are compared using AIC and BIC. For models from the same statistical backend (e.g., two hurdle models or two NLME models), likelihood ratio tests can be performed if the models are nested.
When comparing models from different backends (e.g., hurdle vs NLME), only information criteria comparisons are possible since the likelihoods are not directly comparable for LRT purposes.
| Backend 1 | Backend 2 | LRT Possible? |
| hurdle | hurdle | Yes (if nested) |
| nlme | nlme | Yes (if nested) |
| fixed | fixed | No (no likelihood) |
| hurdle | nlme | No |
| hurdle | fixed | No |
| nlme | fixed | No |
An object of class beezdemand_model_comparison containing:
Data frame with model fit statistics
Type of test performed
LRT results if performed (NULL otherwise)
Index of best model by BIC
Character vector of notes/warnings
Logical; always FALSE since nesting is not automatically verified. Users must ensure models are properly nested for valid LRT interpretation.
The likelihood ratio test (LRT) assumes that:
The models are nested (the reduced model is a special case of the full model obtained by constraining parameters).
Both models are fit to identical data.
Under the null hypothesis, the LR statistic follows a chi-square distribution with degrees of freedom equal to the difference in the number of parameters.
Important caveat for mixed-effects models: When variance components are tested at the boundary (e.g., testing whether a random effect variance is zero), the standard chi-square distribution is not appropriate. The correct null distribution is a mixture of chi-squares (Stram & Lee, 1994). The p-values reported here use the standard chi-square approximation, which is conservative (p-values are too large) for boundary tests.
This function does not automatically verify that models are nested. Users should ensure models are properly nested before interpreting LRT p-values.
Stram, D. O., & Lee, J. W. (1994). Variance components testing in the longitudinal mixed effects model. Biometrics, 50(4), 1171-1177.
compare_hurdle_models() for the legacy hurdle-specific comparison
data(apt) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) compare_models(fit2, fit3)data(apt) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) compare_models(fit2, fit3)
Computes confidence intervals for Q0, alpha, and k parameters from individual demand curve fits. Uses asymptotic normal approximation based on standard errors when available.
## S3 method for class 'beezdemand_fixed' confint(object, parm = NULL, level = 0.95, ...)## S3 method for class 'beezdemand_fixed' confint(object, parm = NULL, level = 0.95, ...)
object |
A |
parm |
Character vector of parameter names to compute CIs for. Default includes all available parameters. |
level |
Confidence level (default 0.95). |
... |
Additional arguments (ignored). |
For beezdemand_fixed objects, confidence intervals are computed using
the asymptotic normal approximation: estimate +/- z * SE. If standard errors
are not available for a parameter, the confidence bounds will be NA.
When the underlying NLS fit objects are available (from detailed = TRUE),
this method attempts to use nlstools::confint2() for more accurate
profile-based intervals.
A tibble with columns: id, term, estimate, conf.low,
conf.high, level.
fit <- fit_demand_fixed(apt, equation = "hs", k = 2) confint(fit) confint(fit, level = 0.90) confint(fit, parm = "Q0")fit <- fit_demand_fixed(apt, equation = "hs", k = 2) confint(fit) confint(fit, level = 0.90) confint(fit, parm = "Q0")
Computes confidence intervals for fixed effect parameters from a TMB-based hurdle demand model using the asymptotic normal approximation.
## S3 method for class 'beezdemand_hurdle' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), ... )## S3 method for class 'beezdemand_hurdle' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), ... )
object |
A |
parm |
Character vector of parameter names to compute CIs for. Default includes all fixed effect parameters. |
level |
Confidence level (default 0.95). |
report_space |
Character. Reporting space for parameters:
|
... |
Additional arguments (ignored). |
Confidence intervals are computed using the asymptotic normal approximation
based on standard errors from TMB::sdreport(). For parameters estimated
on the log scale (Q0, alpha, k), intervals can be back-transformed to the
natural scale using report_space = "natural".
The transformation uses:
For log-scale parameters: exp(estimate +/- z * SE)
A tibble with columns: term, estimate, conf.low, conf.high,
level, component, estimate_scale.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") confint(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") confint(fit)
Computes confidence intervals for fixed effect parameters from an NLME-based mixed-effects demand model.
## S3 method for class 'beezdemand_nlme' confint(object, parm = NULL, level = 0.95, method = c("wald", "profile"), ...)## S3 method for class 'beezdemand_nlme' confint(object, parm = NULL, level = 0.95, method = c("wald", "profile"), ...)
object |
A |
parm |
Character vector of parameter names to compute CIs for. Default includes all fixed effect parameters. |
level |
Confidence level (default 0.95). |
method |
Character. Method for computing intervals:
|
... |
Additional arguments passed to |
For Wald intervals, confidence bounds are computed as estimate ± z * SE using standard errors from the model summary.
For profile intervals, nlme::intervals() is called on the underlying
nlme model object. This method provides more accurate intervals but can be
computationally intensive for complex models.
A tibble with columns: term, estimate, conf.low, conf.high,
level, component.
data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") confint(fit)data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") confint(fit)
Confidence Intervals for TMB Model Parameters
## S3 method for class 'beezdemand_tmb' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), method = c("wald", "simulate"), R = 1000L, seed = NULL, ... )## S3 method for class 'beezdemand_tmb' confint( object, parm = NULL, level = 0.95, report_space = c("internal", "natural"), method = c("wald", "simulate"), R = 1000L, seed = NULL, ... )
object |
A |
parm |
Character vector of parameter names. |
level |
Confidence level (default 0.95). |
report_space |
Character. |
method |
Character. |
R |
Integer. Number of Monte Carlo draws for |
seed |
Optional integer seed for |
... |
Additional arguments. |
method = "simulate" is Monte Carlo simulation from the
asymptotic Gaussian sampling approximation to the MLE (neither a
Bayesian posterior, nor a data-resampling bootstrap, nor
a profile-likelihood interval). Because the sampled distribution is
the same Gaussian that Wald assumes, the simulated per-coefficient
quantiles converge to the Wald intervals as R -> Inf; the method does
not improve on Wald at boundary cases and offers no positivity
guarantee on the internal scale (logsigma_* intervals can be
negative). Its value is (a) a diagnostic side-by-side check on the
Gaussian approximation, and (b) a shared draw primitive
(.tmb_parametric_draws()) for derived-metric confidence intervals.
A tibble with term, estimate, conf.low, conf.high, level.
confint.beezdemand_nlme(), vcov.beezdemand_tmb().
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) confint(fit) confint(fit, report_space = "natural") # Diagnostic Monte Carlo intervals (asymptotically Wald-equivalent): confint(fit, method = "simulate", R = 1000, seed = 42)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) confint(fit) confint(fit, report_space = "natural") # Diagnostic Monte Carlo intervals (asymptotically Wald-equivalent): confint(fit, method = "simulate", R = 1000, seed = 42)
Wald confidence intervals via stats::confint.default().
## S3 method for class 'cp_model_lm' confint(object, parm = NULL, level = 0.95, ...)## S3 method for class 'cp_model_lm' confint(object, parm = NULL, level = 0.95, ...)
object |
A |
parm |
Optional character vector of parameter names. |
level |
Confidence level (default |
... |
Additional arguments (unused). |
A tibble with columns term, estimate, conf.low, conf.high,
level, method.
Wraps lme4::confint.merMod(). Defaults to Wald intervals; method = "profile"
or method = "boot" use the corresponding lme4 backends and are slower.
## S3 method for class 'cp_model_lmer' confint( object, parm = NULL, level = 0.95, method = c("Wald", "profile", "boot"), ... )## S3 method for class 'cp_model_lmer' confint( object, parm = NULL, level = 0.95, method = c("Wald", "profile", "boot"), ... )
object |
A |
parm |
Optional character vector of parameter names. |
level |
Confidence level (default |
method |
One of |
... |
Additional arguments passed to |
A tibble with columns term, estimate, conf.low, conf.high,
level, method.
Computes confidence intervals for parameters from a nonlinear cross-price
demand model using nlstools::confint2().
## S3 method for class 'cp_model_nls' confint( object, parm = NULL, level = 0.95, method = c("asymptotic", "profile"), ... )## S3 method for class 'cp_model_nls' confint( object, parm = NULL, level = 0.95, method = c("asymptotic", "profile"), ... )
object |
A |
parm |
Character vector of parameter names to compute CIs for. Default includes all parameters. |
level |
Confidence level (default 0.95). |
method |
Character. Method for computing intervals passed to
|
... |
Additional arguments passed to |
This method wraps nlstools::confint2() to provide confidence intervals
for the log10-parameterized coefficients (log10_qalone, I, log10_beta).
For back-transformed natural-scale confidence intervals, apply the
transformation: 10^conf.low and 10^conf.high for log10-scale parameters.
A tibble with columns: term, estimate, conf.low, conf.high,
level, method.
data(etm) fit <- fit_cp_nls(etm, equation = "exponentiated") confint(fit)data(etm) fit <- fit_cp_nls(etm, equation = "exponentiated") confint(fit)
A small illustrative dataset of price (x) and consumption (y) target (target), and group (group).
cpcp
A data frame with N rows and columns:
unique identifier
price of the alternative
consumption/demand
target (e.g., alone, own, alt)
e.g., drug or product
Performs pairwise comparisons of intercepts between groups in a cross-price demand model, but only when a significant interaction is present. The emmeans table showing estimated marginal means for intercepts is always returned.
cp_posthoc_intercepts(object, alpha = 0.05, adjust = "tukey", ...)cp_posthoc_intercepts(object, alpha = 0.05, adjust = "tukey", ...)
object |
A cp_model_lmer object from fit_cp_linear |
alpha |
Significance level for testing (default: 0.05) |
adjust |
Method for p-value adjustment; see emmeans::contrast (default: "tukey") |
... |
Additional arguments passed to emmeans |
List containing the emmeans table and optionally pairwise comparisons if interaction is significant
data(etm) fit <- fit_cp_linear(etm, type = "mixed", group_effects = TRUE) cp_posthoc_intercepts(fit)data(etm) fit <- fit_cp_linear(etm, type = "mixed", group_effects = TRUE) cp_posthoc_intercepts(fit)
Performs pairwise comparisons of slopes between groups in a cross-price demand model, but only when a significant interaction is present. The emmeans table showing estimated marginal means for slopes is always returned.
cp_posthoc_slopes(object, alpha = 0.05, adjust = "tukey", ...)cp_posthoc_slopes(object, alpha = 0.05, adjust = "tukey", ...)
object |
A cp_model_lmer object from fit_cp_linear |
alpha |
Significance level for testing (default: 0.05) |
adjust |
Method for p-value adjustment; see emmeans::contrast (default: "tukey") |
... |
Additional arguments passed to emmeans |
List containing the emmeans table and optionally pairwise comparisons if interaction is significant
data(etm) fit <- fit_cp_linear(etm, type = "mixed", group_effects = TRUE) cp_posthoc_slopes(fit)data(etm) fit <- fit_cp_linear(etm, type = "mixed", group_effects = TRUE) cp_posthoc_slopes(fit)
A dataset containing ETM data for a small number of participants
etmetm
Long-form data.frame with columns: id, x, y, target, group. Participants were asked how many cigarettes, e-cigarettes, combustible, and non-combustible products they would buy at various prices.
A convenience function to extract coefficients from any type of cross-price demand model in a unified format. For mixed effects models, returns a list with different coefficient types.
extract_coefficients(object, ...)extract_coefficients(object, ...)
object |
A cross-price demand model object (cp_model_nls, cp_model_lm, or cp_model_lmer) |
... |
Additional arguments passed to the appropriate coef method |
For cp_model_nls and cp_model_lm, returns the model coefficients. For cp_model_lmer, returns a list with fixed, random, and combined coefficients.
data(etm, package = "beezdemand") fit <- fit_cp_nls(etm, equation = "exponentiated") extract_coefficients(fit)data(etm, package = "beezdemand") fit <- fit_cp_nls(etm, equation = "exponentiated") extract_coefficients(fit)
Extra Sum of Squares F-test
ExtraF( dat, equation = "hs", groups = NULL, verbose = FALSE, k, compare = "alpha", idcol = "id", xcol = "x", ycol = "y", groupcol = NULL, start_alpha = 0.001 )ExtraF( dat, equation = "hs", groups = NULL, verbose = FALSE, k, compare = "alpha", idcol = "id", xcol = "x", ycol = "y", groupcol = NULL, start_alpha = 0.001 )
dat |
Long form data frame |
equation |
"hs" |
groups |
NULL for all. Character vector matching groups in groupcol |
verbose |
If TRUE, prints all output including models |
k |
User-defined k value; if missing will attempt to find shared k and then mean empirical range (in log units) |
compare |
Specify whether to compare alpha or Q0. Default is alpha |
idcol |
The column name that should be treated as dataset identifier |
xcol |
The column name that should be treated as "x" data |
ycol |
The column name that should be treated as "y" data |
groupcol |
The column name that should be treated as the groups |
start_alpha |
Optional numeric to inform starting value for alpha |
One alpha better than individual alphas?
List of results and models
Brent Kaplan [email protected]
## Compare two groups using equation by Koffarnus et al., 2015 and a fixed k of 2 apt$group <- NA apt[apt$id %in% sample(unique(apt$id), length(unique(apt$id))/2), "group"] <- "a" apt$group[is.na(apt$group)] <- "b" ExtraF(apt, "koff", k = 2, groupcol = "group")## Compare two groups using equation by Koffarnus et al., 2015 and a fixed k of 2 apt$group <- NA apt[apt$id %in% sample(unique(apt$id), length(unique(apt$id))/2), "group"] <- "a" apt$group[is.na(apt$group)] <- "b" ExtraF(apt, "koff", k = 2, groupcol = "group")
Bisection search over n_subjects for the smallest N whose Monte Carlo
power estimate from power_demand() 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_demand() 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_demand( target_power = 0.8, effect = list(delta_q0 = NULL, delta_alpha = 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 = "simplified", random_effects = NULL, multi_start = FALSE, verbose = TRUE, design_type = c("within", "between"), ... )find_n_demand( target_power = 0.8, effect = list(delta_q0 = NULL, delta_alpha = 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 = "simplified", random_effects = NULL, multi_start = FALSE, verbose = TRUE, design_type = c("within", "between"), ... )
target_power |
Target power in (0, 1). |
effect |
Named list supplying exactly one of |
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 |
Demand equation passed to |
random_effects |
Random-effects specification passed to
|
multi_start |
Passed to |
verbose |
Logical; report each evaluation. |
design_type |
Either |
... |
Additional arguments passed to |
An object of class beezdemand_power_n: a list with
Estimated smallest 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_demand() for the Monte Carlo engine.
Other power-analysis:
power_demand()
# Tiny search for demonstration only (use n_sim >= 200 and a wide # n_range for real planning; see vignette("power-analysis")) res <- find_n_demand( target_power = 0.8, effect = list(delta_q0 = log(2.5)), n_range = c(4, 8), n_sim = 5, n_sim_max = 10, seed = 1, verbose = FALSE ) print(res)# Tiny search for demonstration only (use n_sim >= 200 and a wide # n_range for real planning; see vignette("power-analysis")) res <- find_n_demand( target_power = 0.8, effect = list(delta_q0 = log(2.5)), n_range = c(4, 8), n_sim = 5, n_sim_max = 10, seed = 1, verbose = FALSE ) print(res)
Fit a Linear Cross-Price Demand Model
fit_cp_linear( data, type = c("fixed", "mixed"), x_var = "x", y_var = "y", id_var = "id", group_var = "group", target_var = "target", filter_target = TRUE, target_level = "alt", formula = NULL, log10x = FALSE, group_effects = FALSE, random_slope = FALSE, return_all = TRUE, ... ) fit_cp_linear.default( data, formula = NULL, log10x = FALSE, return_all = FALSE, ... ) fit_cp_linear.mixed( data, formula = NULL, log10x = FALSE, return_all = FALSE, ... )fit_cp_linear( data, type = c("fixed", "mixed"), x_var = "x", y_var = "y", id_var = "id", group_var = "group", target_var = "target", filter_target = TRUE, target_level = "alt", formula = NULL, log10x = FALSE, group_effects = FALSE, random_slope = FALSE, return_all = TRUE, ... ) fit_cp_linear.default( data, formula = NULL, log10x = FALSE, return_all = FALSE, ... ) fit_cp_linear.mixed( data, formula = NULL, log10x = FALSE, return_all = FALSE, ... )
data |
A data frame containing columns for price, consumption, and optionally target indicator, subject identifier, and group. |
type |
The type of model: |
x_var |
Character string; name of the price column. Default is |
y_var |
Character string; name of the consumption column. Default is
|
id_var |
Character string; name of the subject identifier column.
Default is |
group_var |
Character string; name of the group column. Default is
|
target_var |
Character string; name of the target indicator column.
Default is |
filter_target |
Logical; if TRUE (default), filters to rows where the
target column equals |
target_level |
Character string; value of the target column to retain
when |
formula |
Optional formula override. If NULL, a formula will be
constructed based on other parameters. If non-NULL and any |
log10x |
Logical; if TRUE and formula is NULL, uses |
group_effects |
Logical or character; if TRUE, includes group as a factor
with interactions. Can also be |
random_slope |
Logical; for mixed models, if TRUE, includes random slopes for x. Default is FALSE. |
return_all |
Logical; if TRUE, returns additional model metadata. |
... |
Additional arguments passed to underlying modeling functions. |
Fitted linear model.
data(etm) ## Fixed-effects linear cross-price model fit_fixed <- fit_cp_linear(etm, type = "fixed", group_effects = TRUE) summary(fit_fixed) ## Mixed-effects linear cross-price model fit_mixed <- fit_cp_linear(etm, type = "mixed", group_effects = TRUE) summary(fit_mixed)data(etm) ## Fixed-effects linear cross-price model fit_fixed <- fit_cp_linear(etm, type = "fixed", group_effects = TRUE) summary(fit_fixed) ## Mixed-effects linear cross-price model fit_mixed <- fit_cp_linear(etm, type = "mixed", group_effects = TRUE) summary(fit_mixed)
Fits a cross-price demand curve using log10-parameterization for numerical stability. The optimizer estimates parameters on the log10 scale where applicable, so positivity constraints hold automatically.
Equation forms:
Exponentiated (default):
Exponential (fits on log10 response scale):
Additive (level on ):
where is the alternative product price (or "cross" price) and
is consumption of the target good.
Optimizer parameters (log10 parameterization):
log10_qalone: - baseline consumption
when the alternative is effectively absent.
I: cross-price interaction intensity; sign and magnitude reflect
substitution/complementarity. Unconstrained (can be negative for substitutes).
log10_beta: - rate at which cross-price
influence decays as increases.
Natural-scale values are recovered as and
.
The function first attempts a multi-start nonlinear least squares fit
(nls.multstart). If that fails (or if explicit start_values are provided), it
falls back to minpack.lm::nlsLM. Optionally, it will make a final attempt
with nlsr::wrapnlsr. Returns either the fitted model or a structured object
with metadata for downstream methods.
fit_cp_nls( data, equation = c("exponentiated", "exponential", "additive"), x_var = "x", y_var = "y", start_values = NULL, start_vals = lifecycle::deprecated(), iter = 100, bounds = NULL, fallback_to_nlsr = TRUE, return_all = TRUE )fit_cp_nls( data, equation = c("exponentiated", "exponential", "additive"), x_var = "x", y_var = "y", start_values = NULL, start_vals = lifecycle::deprecated(), iter = 100, bounds = NULL, fallback_to_nlsr = TRUE, return_all = TRUE )
data |
A data frame with columns for price and consumption. Additional columns are ignored. Input is validated internally. |
equation |
Character string; model family, one of
|
x_var |
Character string; name of the price column in |
y_var |
Character string; name of the consumption column in |
start_values |
Optional named list of initial values for parameters
|
start_vals |
|
iter |
Integer; number of random starts for |
bounds |
Deprecated. Log10-parameterized parameters are naturally unbounded. This argument is ignored but retained for backwards compatibility. |
fallback_to_nlsr |
Logical; if |
return_all |
Logical; if |
Start values. When start_values is missing, the function:
(1) estimates a reasonable range for log10_qalone from the observed y,
(2) estimates log10_beta from the price range, and (3) launches a multi-start
grid in nls.multstart.
Zero handling for exponential equation. Since the exponential equation fits
on the scale, observations with are automatically
removed with a warning. Use the exponentiated or additive forms if you need to
retain zero consumption values.
Fitting pipeline (short-circuiting):
nls.multstart::nls_multstart() with random starts.
If that fails (or if start_values provided): minpack.lm::nlsLM() using
start_values (user or internally estimated).
If that fails and fallback_to_nlsr = TRUE: nlsr::wrapnlsr().
The returned object has class "cp_model_nls" (when return_all = TRUE) with
components: model, method (the algorithm used), equation, start_vals,
nlsLM_fit, nlsr_fit, and the data used. This is convenient for custom
print/summary/plot methods.
If return_all = TRUE (default): a list of class "cp_model_nls":
model: the fitted object from the successful backend.
method: one of "nls_multstart", "nlsLM", or "wrapnlsr".
equation: the model family used.
start_vals: named list of starting values (final used; kept for backward compatibility).
nlsLM_fit, nlsr_fit: fits from later stages (if attempted; NULL when that
stage was not reached, or was attempted but did not itself produce a fit).
data: the 2-column data frame actually fit.
convergence: list with isConv, finIter, stopCode, stopMessage for the
winning backend, read from model$convInfo; populated for nls/nlsLM-class
fits, and for nlsr::wrapnlsr() fits too when it returns a plain nls-class
object (its usual successful case); isConv = NA only when the winning fit
carries no convInfo at all.
If return_all = FALSE: the fitted model object from the successful backend.
summary() and confint() on the returned object emit a classed warning
(beezdemand_cp_nls_nonconverged_warning) when convergence$isConv is explicitly
FALSE for the winning fit; unreported convergence (NA, when the winning object
carries no convInfo) is silent.
Poor scaling or extreme y dispersion can make parameters weakly identified.
For "exponential", the model fits on the scale internally.
check_unsystematic_cp for pre-fit data screening,
validate_cp_data for input validation.
## --- Example: Real data (E-Cigarettes, id = 1) --- dat <- structure(list( id = c(1, 1, 1, 1, 1, 1), x = c(2, 4, 8, 16, 32, 64), y = c(3, 5, 5, 16, 17, 13), target = c("alt", "alt", "alt", "alt", "alt", "alt"), group = c("E-Cigarettes", "E-Cigarettes", "E-Cigarettes", "E-Cigarettes", "E-Cigarettes", "E-Cigarettes") ), row.names = c(NA, -6L), class = c("tbl_df", "tbl", "data.frame")) ## Fit the default (exponentiated) cross-price form fit_ecig <- fit_cp_nls(dat, equation = "exponentiated", return_all = TRUE) summary(fit_ecig) # model summary fit_ecig$method # backend actually used (e.g., "nls_multstart") coef(fit_ecig$model) # parameter estimates: log10_qalone, I, log10_beta## --- Example: Real data (E-Cigarettes, id = 1) --- dat <- structure(list( id = c(1, 1, 1, 1, 1, 1), x = c(2, 4, 8, 16, 32, 64), y = c(3, 5, 5, 16, 17, 13), target = c("alt", "alt", "alt", "alt", "alt", "alt"), group = c("E-Cigarettes", "E-Cigarettes", "E-Cigarettes", "E-Cigarettes", "E-Cigarettes", "E-Cigarettes") ), row.names = c(NA, -6L), class = c("tbl_df", "tbl", "data.frame")) ## Fit the default (exponentiated) cross-price form fit_ecig <- fit_cp_nls(dat, equation = "exponentiated", return_all = TRUE) summary(fit_ecig) # model summary fit_ecig$method # backend actually used (e.g., "nls_multstart") coef(fit_ecig$model) # parameter estimates: log10_qalone, I, log10_beta
Modern interface for fitting individual demand curves via nonlinear
least squares. Returns a structured S3 object with standard methods
including summary(), tidy(), and glance().
fit_demand_fixed( data, equation = c("hs", "koff", "simplified", "linear", "exponential", "exponentiated"), k = 2, agg = NULL, x_var = "x", y_var = "y", id_var = "id", param_space = c("natural", "log10"), by = NULL, multistart = TRUE, S = NULL, ... )fit_demand_fixed( data, equation = c("hs", "koff", "simplified", "linear", "exponential", "exponentiated"), k = 2, agg = NULL, x_var = "x", y_var = "y", id_var = "id", param_space = c("natural", "log10"), by = NULL, multistart = TRUE, S = NULL, ... )
data |
Data frame in long format with columns: |
equation |
Character. Equation type: |
k |
Scaling constant. Numeric value (fixed), |
agg |
Character. Aggregation method: |
x_var |
Character. Name of the price column. Default |
y_var |
Character. Name of the consumption column. Default |
id_var |
Character. Name of the subject identifier column. Default |
param_space |
Character. Parameterization used for fitting. One of:
|
by |
Optional character vector of column names to group by.
When supplied, fits are run separately within each unique
combination of the |
multistart |
Logical. If |
S |
Integer or Note: |
... |
Additional arguments passed to the underlying |
This function is a modern wrapper around the legacy FitCurves() function.
It provides the same fitting capabilities but returns a structured S3 object
with standardized methods for model interrogation.
fit_demand_fixed() always runs FitCurves()'s existing heuristic start
exactly as before (the "production start"). A subject whose production
fit is strict-converged (converged_strict: the optimizer's own
convergence flag AND finite coefficients/objective AND not sitting on a
user-supplied bound) is accepted immediately; no sampled starts are ever
run for it, so its row, fitted model, predictions, and data are
byte-identical to the multistart = FALSE / S = 1 protocol by
construction. Only subjects whose production fit is not strict-converged
are re-fit from S - 1 additional starts, sampled log-uniformly in
interpretable (Q0, Pmax) coordinates and mapped to each equation's native
(Q0, alpha) parameterization via the same closed forms used by
beezdemand_calc_pmax_omax(). Among the sampled attempts that themselves
strict-converge, the minimum-residual-SS start wins (ties broken by draw
order). If none of the sampled starts strict-converge, the original
(non-converged) production row is kept. equation = "linear" is a
closed-form fit and is never multistarted. FitCurves() itself is
unchanged; sampling draws from the ambient RNG stream (call set.seed()
before fit_demand_fixed() for reproducibility; the helpers never call
set.seed() themselves).
An object of class beezdemand_fixed with components:
Data frame of fitted parameters for each subject.
Gains n_starts_tried, n_starts_converged, and start_source
("production", "sampled", or "none") from the multi-start
protocol; see Details.
List of model fit objects (if detailed = TRUE internally)
List of prediction data frames
List of data frames used for each fit
List describing the multi-start protocol: multistart,
S (resolved budget), equation, eligible (whether this equation
supports rescue), and summary (per-subject start metadata, or
NULL when not applicable)
The original function call
The equation form used
Description of k specification
Aggregation method used
Total number of subjects/fits attempted
Number of successful fits
Number of failed fits
fit_demand_tmb() for TMB mixed-effects models,
fit_demand_mixed() for NLME mixed-effects models,
fit_demand_hurdle() for hurdle models.
Other demand-fitting:
fit_demand_hurdle(),
fit_demand_mixed(),
fit_demand_tmb()
data(apt) fit <- fit_demand_fixed(apt, equation = "hs", k = 2) print(fit) summary(fit) tidy(fit) glance(fit) # Grouped analysis: fit separately by gender (subset keeps it fast) data(apt_full) ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit_g <- fit_demand_fixed(dat, equation = "hs", k = 2, by = "gender") tidy(fit_g) # group column prepended glance(fit_g) # one row per groupdata(apt) fit <- fit_demand_fixed(apt, equation = "hs", k = 2) print(fit) summary(fit) tidy(fit) glance(fit) # Grouped analysis: fit separately by gender (subset keeps it fast) data(apt_full) ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit_g <- fit_demand_fixed(dat, equation = "hs", k = 2, by = "gender") tidy(fit_g) # group column prepended glance(fit_g) # one row per group
Fits a two-part hurdle model for demand data using TMB (Template Model Builder). Part I models the probability of zero consumption using logistic regression. Part II models log-consumption given positive response using a nonlinear mixed effects model.
fit_demand_hurdle( data, y_var, x_var, id_var, random_effects = c("zeros", "q0", "alpha"), epsilon = 0.001, start_values = NULL, tmb_control = list(max_iter = 200, eval_max = 1000, trace = 0), verbose = 1, part2 = c("zhao_exponential", "exponential", "simplified_exponential"), ... )fit_demand_hurdle( data, y_var, x_var, id_var, random_effects = c("zeros", "q0", "alpha"), epsilon = 0.001, start_values = NULL, tmb_control = list(max_iter = 200, eval_max = 1000, trace = 0), verbose = 1, part2 = c("zhao_exponential", "exponential", "simplified_exponential"), ... )
data |
A data frame containing the demand data. |
y_var |
Character string specifying the column name for consumption values. |
x_var |
Character string specifying the column name for price. |
id_var |
Character string specifying the column name for subject IDs. |
random_effects |
Character vector specifying which random effects to include.
Options are |
epsilon |
Small constant added to price before log transformation in Part I.
Used to handle zero prices: |
start_values |
Optional named list of starting values for optimization.
If |
tmb_control |
List of control parameters for TMB optimization:
|
verbose |
Integer controlling output verbosity: 0 = silent, 1 = progress messages, 2 = detailed optimization trace. Default is 1. |
part2 |
Character string selecting the Part II mean function. Options are
|
... |
Additional arguments (reserved for future use). |
The model structure is:
Part I (Binary - probability of zero consumption):
Part II (Continuous - log consumption given positive):
With 3 random effects (random_effects = c("zeros", "q0", "alpha")):
where and .
With 2 random effects (random_effects = c("zeros", "q0")):
where and .
Random effects follow a multivariate normal distribution with unstructured
covariance matrix. Use compare_hurdle_models for likelihood
ratio tests comparing nested models.
An object of class beezdemand_hurdle containing:
List with coefficients, se, variance_components, correlations
Matrix of empirical Bayes random effect estimates
Data frame of subject-specific parameters including Q0, alpha, breakpoint, Pmax, Omax
TMB objective function object
Optimization result from nlminb
TMB sdreport object
The matched call
Original data used for fitting
List with y_var, x_var, id_var, n_subjects, n_obs, etc.
Logical indicating convergence
Logical. TRUE when the estimated random-effects
covariance was not positive definite and the subject-level effects
were computed from an uncorrelated (diagonal) approximation; the
reported correlations do not apply to those outputs. summary() and
print() carry a note when it is TRUE.
Log-likelihood at convergence
Information criteria
Error message if fitting failed, NULL otherwise
The TMB backend estimates positive-constrained parameters on the natural-log
scale: , , and . Reporting methods
(summary(), tidy(), coef()) can back-transform to the natural scale or
present parameters on the scale.
To compare estimates with models fit in space,
use:
The default part2 = "zhao_exponential" places no inside the
exponent (), whereas the Hursh & Silberberg
form used by fit_demand_fixed(equation = "hs") and
fit_demand_tmb(equation = "exponential") uses .
The two values are therefore on different scales and relate as
so a Zhao is not directly comparable with an HS
unless divided by the subject's (or group's) . Use
part2 = "exponential" (alias "hs_stdq0") for an HS-scaled .
The 3-random-effect spec (random_effects = c("zeros", "q0", "alpha"))
can false-converge on real purchase task data: nlminb() reports
a nonzero convergence code (e.g. "false convergence (8)") while the
Hessian is not positive definite (fit$hessian_pd == FALSE). This
reflects weak identification of the alpha random effect rather than a
broken model specification (companion datasets from the same source can
converge cleanly with the identical spec). fit$opt$message and
fit$opt$convergence retain nlminb's own diagnostic fields alongside
fit$converged and fit$hessian_pd; print() and summary() display a
prominent warning block (quoting the optimizer message) whenever either
signals a problem, so a non-converged or non-PD-Hessian fit is hard to
miss even without inspecting these fields directly.
The recommended stability check when the 3RE spec reports
non-convergence or a non-PD Hessian is to refit with
random_effects = c("zeros", "q0") (dropping the alpha random effect,
which converges more readily) and compare the empirical-Bayes subject
parameters between the two fits. Broadly similar per-subject estimates
support treating the 2RE fit's conclusions as stable across the two
specifications. This is a diagnostic comparison rather than a claim that
the 2RE spec is generally preferred, and beezdemand does not refit
automatically.
summary.beezdemand_hurdle, predict.beezdemand_hurdle
(and its Scoring predictions section: score zero-inclusive
consumption with type = "demand", the default since 0.3.0),
plot.beezdemand_hurdle, compare_hurdle_models,
simulate_hurdle_data
fit_demand_tmb() for continuous mixed-effects models,
fit_demand_mixed() for NLME-based fitting,
fit_demand_fixed() for individual NLS curves.
Other demand-fitting:
fit_demand_fixed(),
fit_demand_mixed(),
fit_demand_tmb()
# Load example data data(apt) # Fit full model with 3 random effects fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) # Fit simplified model with 2 random effects (fixed alpha) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) # View results summary(fit3) # Compare models with likelihood ratio test compare_hurdle_models(fit3, fit2)# Load example data data(apt) # Fit full model with 3 random effects fit3 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) # Fit simplified model with 2 random effects (fixed alpha) fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0")) # View results summary(fit3) # Compare models with likelihood ratio test compare_hurdle_models(fit3, fit2)
Fits a nonlinear mixed-effects model for behavioral economic demand data. The function allows Q0 and alpha parameters to vary by specified factors and supports different demand equation forms.
fit_demand_mixed( data, y_var, x_var, id_var, factors = NULL, factor_interaction = FALSE, equation_form = c("zben", "simplified", "exponentiated"), param_space = c("log10", "natural"), k = NULL, custom_model_formula = NULL, fixed_rhs = NULL, continuous_covariates = NULL, start_value_method = c("heuristic", "pooled_nls"), random_effects = Q0 + alpha ~ 1, covariance_structure = c("pdDiag", "pdSymm"), start_values = NULL, collapse_levels = NULL, nlme_control = list(msMaxIter = 200, niterEM = 100, maxIter = 200, pnlsTol = 0.001, tolerance = 1e-06, apVar = TRUE, minScale = 1e-09, opt = "nlminb", msVerbose = FALSE), method = "ML", ... )fit_demand_mixed( data, y_var, x_var, id_var, factors = NULL, factor_interaction = FALSE, equation_form = c("zben", "simplified", "exponentiated"), param_space = c("log10", "natural"), k = NULL, custom_model_formula = NULL, fixed_rhs = NULL, continuous_covariates = NULL, start_value_method = c("heuristic", "pooled_nls"), random_effects = Q0 + alpha ~ 1, covariance_structure = c("pdDiag", "pdSymm"), start_values = NULL, collapse_levels = NULL, nlme_control = list(msMaxIter = 200, niterEM = 100, maxIter = 200, pnlsTol = 0.001, tolerance = 1e-06, apVar = TRUE, minScale = 1e-09, opt = "nlminb", msVerbose = FALSE), method = "ML", ... )
data |
A data frame. |
y_var |
Character string, the name of the dependent variable column.
For |
x_var |
Character string, the name of the independent variable column (e.g., "x", price). |
id_var |
Character string, the name of the subject/group identifier column for random effects. |
factors |
A character vector of factor names (up to two) by which Q0 and alpha
are expected to vary (e.g., |
factor_interaction |
Logical. If |
equation_form |
Character string specifying the demand equation form. Options are:
|
param_space |
Character. Parameterization used for fitting core demand parameters. One of:
Notes:
|
k |
Numeric. Range parameter (in log10 units) used with |
custom_model_formula |
An optional custom nonlinear model formula (nlme format).
If provided, this overrides |
fixed_rhs |
Optional one-sided formula or character string specifying the
right-hand side (RHS) for the fixed-effects linear models of |
continuous_covariates |
Optional character vector of continuous (numeric)
predictor names to be included additively in the fixed-effects RHS when
|
start_value_method |
Character, method to generate starting values if |
random_effects |
A formula or a list of formulas for the random effects structure.
Default |
covariance_structure |
Character, covariance structure for random effects.
Options: |
start_values |
Optional named list of starting values for fixed effects.
If |
collapse_levels |
Optional named list specifying factor level collapsing separately for Q0 and alpha parameters. Structure: list( Q0 = list(factor_name = list(new_level = c(old_levels), ...)), alpha = list(factor_name = list(new_level = c(old_levels), ...)) ) Either |
nlme_control |
Control parameters for |
method |
Fitting method for |
... |
Additional arguments passed to |
An object of class beezdemand_nlme containing:
The fitted nlme model object
Data frame used for fitting
The original function call
Equation form used
Parameterization space
Factor variables in the model
Variable name strings
Starting values used
fit_demand_tmb() for TMB-based mixed-effects (preferred for new work),
fit_demand_fixed() for individual NLS curves,
fit_demand_hurdle() for hurdle models,
get_demand_param_emms() for estimated marginal means,
get_demand_comparisons() for pairwise comparisons.
Other demand-fitting:
fit_demand_fixed(),
fit_demand_hurdle(),
fit_demand_tmb()
# Basic mixed-effects demand fit with apt data # Transform consumption using LL4 for the zben equation apt_ll4 <- apt |> dplyr::mutate(y_ll4 = ll4(y)) fit <- fit_demand_mixed( data = apt_ll4, y_var = "y_ll4", x_var = "x", id_var = "id", equation_form = "zben" ) print(fit) summary(fit)# Basic mixed-effects demand fit with apt data # Transform consumption using LL4 for the zben equation apt_ll4 <- apt |> dplyr::mutate(y_ll4 = ll4(y)) fit <- fit_demand_mixed( data = apt_ll4, y_var = "y_ll4", x_var = "x", id_var = "id", equation_form = "zben" ) print(fit) summary(fit)
Fits nonlinear mixed-effects demand models using Template Model Builder (TMB)
for exact automatic differentiation and Laplace approximation. In practice
this converges on demand equations where the PNLS-based nlme::nlme()
fails (see Details).
fit_demand_tmb( data, y_var = "y", x_var = "x", id_var = "id", equation = c("exponentiated", "exponential", "simplified", "zben"), estimate_k = FALSE, k = NULL, random_effects = Q0 + alpha ~ 1, covariance_structure = c("pdSymm", "pdDiag"), factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, collapse_levels = NULL, start_values = NULL, tmb_control = list(iter_max = 1000, eval_max = 2000), multi_start = TRUE, validate_subject_pars = TRUE, verbose = 1, ..., store_report_cov = FALSE )fit_demand_tmb( data, y_var = "y", x_var = "x", id_var = "id", equation = c("exponentiated", "exponential", "simplified", "zben"), estimate_k = FALSE, k = NULL, random_effects = Q0 + alpha ~ 1, covariance_structure = c("pdSymm", "pdDiag"), factors = NULL, factor_interaction = FALSE, continuous_covariates = NULL, collapse_levels = NULL, start_values = NULL, tmb_control = list(iter_max = 1000, eval_max = 2000), multi_start = TRUE, validate_subject_pars = TRUE, verbose = 1, ..., store_report_cov = FALSE )
data |
A data frame in long format with columns for subject ID, price, and consumption. |
y_var |
Character. Name of the consumption/response variable. |
x_var |
Character. Name of the price variable. |
id_var |
Character. Name of the subject identifier variable. |
equation |
Character. The demand equation to fit. One of:
|
estimate_k |
Logical. If |
k |
Numeric or |
random_effects |
Specification of subject-level random effects. Accepts any of the following, in order of generality:
|
covariance_structure |
|
factors |
Character vector of factor variable names for group comparisons. |
factor_interaction |
Logical. If |
continuous_covariates |
Character vector of continuous covariate names
entered as fixed (population) effects on Q0 and alpha. To also let the
per-subject dose-response vary, add the same (centered) covariate as a
random slope in |
collapse_levels |
Named list for asymmetric factor collapsing. Structure:
|
start_values |
Named list of starting values. If |
tmb_control |
List of control parameters for the optimizer:
|
multi_start |
Logical. If |
validate_subject_pars |
Logical. If |
verbose |
Integer. Verbosity level: 0 = silent, 1 = progress, 2 = debug. |
... |
Additional arguments (currently unused). |
store_report_cov |
Logical. Advanced storage control. When |
Traditional NLME approaches using nlme::nlme() universally fail for
demand equations because the PNLS algorithm with numerical finite-difference
gradients cannot navigate the likelihood surface. TMB succeeds using exact
automatic differentiation, Laplace approximation, and joint marginal
likelihood optimization.
Fixed versus estimated k. By default k is held at 2, the convention of
Hursh & Silberberg (2008) and the default of fit_demand_fixed(). It is
a convention rather than an estimate, so fits at a second value (say
k = 1.5 or k = 3) are worth reporting as a sensitivity check.
alpha and the derived Pmax / Omax / EV move most; Q0 enters the likelihood
jointly with alpha, so it can shift too.
Setting estimate_k = TRUE estimates k alongside Q0 and alpha, which
fits better on data that carry the information to support it. Many do not.
The response depends on k through , so while
stays small the curve is a straight line of slope
and only that product is identified. What pins k down is the
curvature that appears as consumption approaches its floor. On data whose
consumption never gets there, a free k can drift to arbitrarily large values
with a compensating alpha, giving a non-positive-definite Hessian and
meaningless Pmax / Omax. check_demand_model() screens a free-k fit for
that signature and summary() carries the note. The screen reports what
it detects; passing it is no evidence that k is identified.
Continuous within-subject random slopes (dose-response). A numeric term
in the random-effects formula (e.g. Q0 + alpha ~ dose_c) gives each
subject a random slope on a continuous within-id covariate, so
intensity and elasticity change with the covariate (dose) at a
subject-specific rate. The population (fixed) slope is sourced separately from
continuous_covariates; recovering it requires both. The covariate must
vary within id for enough subjects (a hard error below 2 informative
subjects; a warning below 80\
ladders typically a centered log10 dose) so the random intercept is the
subject deviation at the reference value and the intercept/slope covariance is
interpretable. No silent transform is applied: an uncentered covariate is still
fit, but the intercept/slope correlation is reference-dependent and a warning
is emitted. Per-subject parameters at a chosen covariate value are available
via get_subject_pars(fit, at = c(dose_c = value)) and
predict(fit, type = "parameters", at = ...); the per-subject slope
deviations appear as q0_<term> / alpha_<term> columns there and
in ranef(), and the variance components are labelled by the covariate
term in summary() / VarCorr(). See
vignette("tmb-advanced-random-effects") for a worked example.
Error model considerations: The exponentiated and
simplified equations use a Gaussian error model on raw consumption
(Q), which assigns non-zero density to negative values. For data with many
near-zero observations, prefer exponential (Gaussian on log Q, zeros
dropped) or zben (Gaussian on LL4-transformed Q, zeros handled by
the transformation).
Random-effect variance components are reported by summary() on the
log10 scale; see ?summary.beezdemand_tmb for the scale convention and
its nlme::VarCorr() equivalence.
An object of class beezdemand_tmb containing:
List with coefficients, se, variance_components
Data frame of subject-specific Q0, alpha, Pmax, Omax
TMB objective function object
Optimization result (normalized across optimizers)
TMB sdreport object. Its $cov (full covariance of all
ADREPORT'd quantities) is not materialized (a scalar NA) unless
store_report_cov = TRUE.
Logical convergence indicator
Log-likelihood at convergence
Akaike Information Criterion
Bayesian Information Criterion
Original data (after any filtering)
List of model metadata
Design matrix and formula information
Collapse levels information (if used)
fit_demand_mixed() for NLME-based fitting,
fit_demand_hurdle() for two-part hurdle models,
fit_demand_fixed() for individual NLS curves.
Other demand-fitting:
fit_demand_fixed(),
fit_demand_hurdle(),
fit_demand_mixed()
data(apt) # Exponential (HS) on log(Q) fit <- fit_demand_tmb(apt, y_var = "y", x_var = "x", id_var = "id", equation = "exponential") summary(fit) plot(fit) # Exponentiated (Koffarnus) on raw Q fit2 <- fit_demand_tmb(apt, y_var = "y", x_var = "x", id_var = "id", equation = "exponentiated") # With covariates (a 30-per-gender subset keeps the example fast) data(apt_full) ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 30)) dat <- apt_full[apt_full$id %in% keep, ] fit3 <- fit_demand_tmb(dat, y_var = "y", x_var = "x", id_var = "id", equation = "exponential", factors = "gender") get_demand_param_emms(fit3, param = "alpha") # Factor-expanded random slopes on a within-subject factor are supported # through the `random_effects` formula interface, e.g. # random_effects = nlme::pdDiag(Q0 + alpha ~ cond) # so each subject contributes a Q0 / alpha random effect per factor level. # See vignette("tmb-advanced-random-effects", package = "beezdemand").data(apt) # Exponential (HS) on log(Q) fit <- fit_demand_tmb(apt, y_var = "y", x_var = "x", id_var = "id", equation = "exponential") summary(fit) plot(fit) # Exponentiated (Koffarnus) on raw Q fit2 <- fit_demand_tmb(apt, y_var = "y", x_var = "x", id_var = "id", equation = "exponentiated") # With covariates (a 30-per-gender subset keeps the example fast) data(apt_full) ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 30)) dat <- apt_full[apt_full$id %in% keep, ] fit3 <- fit_demand_tmb(dat, y_var = "y", x_var = "x", id_var = "id", equation = "exponential", factors = "gender") get_demand_param_emms(fit3, param = "alpha") # Factor-expanded random slopes on a within-subject factor are supported # through the `random_effects` formula interface, e.g. # random_effects = nlme::pdDiag(Q0 + alpha ~ cond) # so each subject contributes a Q0 / alpha random effect per factor level. # See vignette("tmb-advanced-random-effects", package = "beezdemand").
Analyzes purchase task data
FitCurves( dat, equation, k, agg = NULL, detailed = FALSE, xcol = "x", ycol = "y", idcol = "id", groupcol = NULL, lobound, hibound, constrainq0 = NULL, startq0 = NULL, startalpha = NULL, param_space = c("natural", "log10") )FitCurves( dat, equation, k, agg = NULL, detailed = FALSE, xcol = "x", ycol = "y", idcol = "id", groupcol = NULL, lobound, hibound, constrainq0 = NULL, startq0 = NULL, startalpha = NULL, param_space = c("natural", "log10") )
dat |
data frame (long form) of purchase task data. |
equation |
Character vector of length one. Accepts either "hs" for Hursh and Silberberg (2008) or "koff" for Koffarnus, Franck, Stein, and Bickel (2015). |
k |
A numeric (or character) vector of length one. Reflects the range of consumption in log10 units. If none provided, k will be calculated based on the max/min of the entire sample + .5. If k = "ind", k will be calculated per individual using max/min + .5. If k = "fit", k will be a free parameter on an individual basis. If k = "range", k will be calculated based on the max/min of the entire sample + .5. |
agg |
Character vector of length one accepts either "Mean" or "Pooled". If not NULL (default), data will be aggregated appropriately and analyzed in the specified way. |
detailed |
If TRUE, output will be a 3 element list including (1) dataframe of results, (2) list of model objects, (3) list of individual dataframes used in fitting. Default value is FALSE, which returns only the dataframe of results. |
xcol |
The column name that should be treated as "x" data |
ycol |
The column name that should be treated as "y" data |
idcol |
The column name that should be treated as dataset identifier |
groupcol |
The column name that should be treated as the groups |
lobound |
Optional. A named vector of length 2 ("q0", "alpha") or 3 ("q0", "k", "alpha"), the latter length if k = "fit", specifying the lower bounds. |
hibound |
Optional. A named vector of length 2 ("q0", "alpha") or 3 ("q0", "k", "alpha"), the latter length if k = "fit", specifying the upper bounds. |
constrainq0 |
Optional. A number that will be used to constrain Q0 in the fitting process. Currently experimental and only works with a fixed k value. |
startq0 |
Optional. A number that will be used to start Q0 in the fitting process. Currently experimental. |
startalpha |
Optional. A number that will be used to start Alpha in the fitting process. Currently experimental. |
param_space |
Character. One of "natural" (default) or "log10". Specifies whether parameters (Q0, alpha) are estimated in natural space or log10-transformed space. |
FitCurves() has been superseded by fit_demand_fixed(), which provides a
modern S3 interface with standardized methods (summary(), tidy(),
glance(), predict()). FitCurves() will continue to work but is no
longer recommended for new code. See vignette("migration-guide") for
migration instructions.
If detailed == FALSE (default), a dataframe of results. If detailed == TRUE, a 3 element list consisting of (1) dataframe of results, (2) list of model objects, (3) list of individual dataframes used in fitting
Brent Kaplan [email protected] Shawn Gilroy [email protected]
fit_demand_fixed() for the modern interface
## Analyze using Hursh & Silberberg, 2008 equation with a k fixed to 2 FitCurves(apt[sample(apt$id, 5), ], "hs", k = 2)## Analyze using Hursh & Silberberg, 2008 equation with a k fixed to 2 FitCurves(apt[sample(apt$id, 5), ], "hs", k = 2)
Fits curve to pooled or mean data
FitMeanCurves( dat, equation, k, remq0e = FALSE, replfree = NULL, rem0 = FALSE, nrepl = NULL, replnum = NULL, plotcurves = FALSE, method = NULL, indpoints = TRUE, vartext = NULL )FitMeanCurves( dat, equation, k, remq0e = FALSE, replfree = NULL, rem0 = FALSE, nrepl = NULL, replnum = NULL, plotcurves = FALSE, method = NULL, indpoints = TRUE, vartext = NULL )
dat |
data frame (long form) of purchase task data. |
equation |
Character vector of length one. Accepts either "hs" for Hursh and Silberberg (2008) or "koff" for Koffarnus, Franck, Stein, and Bickel (2015). |
k |
A numeric vector of length one. Reflects the range of consumption in log10 units. If none provided, k will be calculated based on the max/min of the entire sample. If k = "fit", k will be a free parameter |
remq0e |
If TRUE, removes consumption and price where price == 0. Default value is FALSE |
replfree |
Optionally replaces price == 0 with specified value. Note, if fitting using equation == "hs", and 0 is first price, 0 gets replaced by replfree. Default value is .01 |
rem0 |
If TRUE, removes all 0s in consumption data prior to analysis. Default value is FALSE. |
nrepl |
Number of zeros to replace with replacement value (replnum). Can accept either a number or "all" if all zeros should be replaced. Default is to replace the first zero only. |
replnum |
Value to replace zeros. Default is .01 |
plotcurves |
Boolean whether to create plot. If TRUE, a "plots/" directory is created one level above working directory. Default is FALSE. |
method |
Character string of length 1. Accepts "Mean" to fit to mean data or "Pooled" to fit to pooled data |
indpoints |
Boolean whether to plot individual points in gray. Default is TRUE. |
vartext |
Character vector specifying indices to report on plots. Valid indices include "Q0d", "Alpha", "Q0e", "EV", "Pmaxe", "Omaxe", "Pmaxd", "Omaxd", "K", "Q0se", "Alphase", "R2", "AbsSS" |
FitMeanCurves() has been superseded by fit_demand_fixed() with the
agg parameter. FitMeanCurves() will continue to work but is no longer
recommended for new code. See vignette("migration-guide") for migration
instructions.
Data frame
Brent Kaplan [email protected]
fit_demand_fixed() for the modern interface with agg parameter
## Fit aggregated data (mean only) using Hursh & Silberberg, 2008 equation with a k fixed at 2 FitMeanCurves(apt[sample(apt$id, 5), ], "hs", k = 2, method = "Mean")## Fit aggregated data (mean only) using Hursh & Silberberg, 2008 equation with a k fixed at 2 FitMeanCurves(apt[sample(apt$id, 5), ], "hs", k = 2, method = "Mean")
Returns marginal expected consumption P(y > 0 | x) * E(y | y > 0, x)
by default (the .fitted column of predict(fit, type = "demand")).
With marginal = FALSE, returns the conditional-on-positive expectation
(the .fitted column of predict(fit, type = "response")).
## S3 method for class 'beezdemand_hurdle' fitted(object, marginal = TRUE, ...)## S3 method for class 'beezdemand_hurdle' fitted(object, marginal = TRUE, ...)
object |
A |
marginal |
If |
... |
Unused. |
Numeric vector of length nobs(object).
predict.beezdemand_hurdle(), augment.beezdemand_hurdle().
Default returns fitted values on the model's native likelihood scale
(log scale for "exponential", natural/LL4 scale for others), matching
augment(fit)$.fitted. Set scale = "natural" to back-transform.
## S3 method for class 'beezdemand_tmb' fitted( object, scale = c("model", "natural"), level = c("subject", "population"), ... )## S3 method for class 'beezdemand_tmb' fitted( object, scale = c("model", "natural"), level = c("subject", "population"), ... )
object |
A |
scale |
One of |
level |
One of |
... |
Unused. |
Numeric vector of length nobs(object).
predict.beezdemand_tmb(), augment.beezdemand_tmb(),
residuals.beezdemand_tmb().
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(fitted(fit))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(fitted(fit))
Modern wrapper for fitting individual demand curves via nonlinear least squares. Returns a structured S3 object with standard methods.
S3 method for fixef for objects of class beezdemand_nlme.
Extracts the fixed-effect coefficients from the fitted nlme model.
## S3 method for class 'beezdemand_nlme' fixef(object, ...)## S3 method for class 'beezdemand_nlme' fixef(object, ...)
object |
A |
... |
Additional arguments passed to |
A named numeric vector of fixed-effect coefficients.
coef.beezdemand_nlme, ranef.beezdemand_nlme
Extract Fixed Effects from TMB Model
## S3 method for class 'beezdemand_tmb' fixef(object, ...)## S3 method for class 'beezdemand_tmb' fixef(object, ...)
object |
A |
... |
Additional arguments. |
Named numeric vector of fixed effects.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) nlme::fixef(fit)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) nlme::fixef(fit)
Extract Fixed Effects from Mixed-Effects Cross-Price Model
## S3 method for class 'cp_model_lmer' fixef(object, ...)## S3 method for class 'cp_model_lmer' fixef(object, ...)
object |
A cp_model_lmer object |
... |
Additional arguments passed to fixef |
Named vector of fixed effects
Returns the (currently intercept-only) component formulas plus the
random-effect specification preserved at fit time. Hurdle does not yet
support factor or covariate effects on its Part I (logit participation)
or Part II (log-consumption intensity) components; both binary and
consumption are ~ 1. The API is intentionally aligned with
formula.beezdemand_tmb() so the shape extends without breaking when
hurdle components gain factor/covariate support.
## S3 method for class 'beezdemand_hurdle' formula(x, ...)## S3 method for class 'beezdemand_hurdle' formula(x, ...)
x |
A |
... |
Unused. |
Named list list(binary, consumption, random).
model.matrix.beezdemand_hurdle().
Returns the fixed-effect RHS formulas for Q0 and alpha plus the
original random-effect specification preserved at fit time. The
Q0 and alpha formulas may differ when collapse_levels was used
to collapse factor levels asymmetrically.
## S3 method for class 'beezdemand_tmb' formula(x, ...)## S3 method for class 'beezdemand_tmb' formula(x, ...)
x |
A |
... |
Unused. |
Named list list(Q0, alpha, random). Q0 and alpha are
one-sided formulas built from fit$formula_details. random is
the original random_effects argument value (round-trippable back
to fit_demand_tmb()).
model.matrix.beezdemand_tmb(), update.beezdemand_tmb().
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) formula(fit)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) formula(fit)
Conducts pairwise comparisons for Q0 and/or alpha parameters from a
beezdemand_nlme model across levels of specified factors.
Comparisons are performed on the log10 scale of the parameters.
Results include estimates of differences (on log10 scale) and
optionally, ratios (on the natural scale by applying 10^difference).
get_demand_comparisons(fit_obj, ...) ## Default S3 method: get_demand_comparisons(fit_obj, ...) ## S3 method for class 'beezdemand_nlme' get_demand_comparisons( fit_obj, param = c("Q0", "alpha"), compare_specs = NULL, contrast_type = "pairwise", contrast_by = NULL, adjust = "holm", at = NULL, ci_level = 0.95, report_ratios = TRUE, params_to_compare = lifecycle::deprecated(), ... )get_demand_comparisons(fit_obj, ...) ## Default S3 method: get_demand_comparisons(fit_obj, ...) ## S3 method for class 'beezdemand_nlme' get_demand_comparisons( fit_obj, param = c("Q0", "alpha"), compare_specs = NULL, contrast_type = "pairwise", contrast_by = NULL, adjust = "holm", at = NULL, ci_level = 0.95, report_ratios = TRUE, params_to_compare = lifecycle::deprecated(), ... )
fit_obj |
A |
... |
Additional arguments passed to |
param |
Character vector: "Q0", "alpha", or |
compare_specs |
A formula specifying the factors whose levels are to be included in the EMM calculation
prior to contrasting. This defines the "cells" of your design for EMMs.
E.g., |
contrast_type |
Character string specifying the type of contrast (passed to |
contrast_by |
Optional character vector of factor names to condition the contrasts by (passed to |
adjust |
P-value adjustment method. Default |
at |
Optional named list for |
ci_level |
Confidence level. Default 0.95. |
report_ratios |
Logical. If TRUE, reports contrasts as ratios. Default |
params_to_compare |
|
A list named by parameter. Each element contains:
emmeans |
Tibble of EMMs (internal scale, i.e., log10 for
|
contrasts_log10 |
Tibble of comparisons (differences on the fit's
internal scale, despite the name; see the Details on
|
contrasts_ratio |
(If |
S3 class beezdemand_comparison is assigned. When contrast_by is active,
the nested contrast tables carry leading by-column(s) named with the
user-requested original factor name (e.g. dose, not the
collapse-mapped dose_alpha), harmonized with the TMB backend and the flat
tidy() output.
data(ko, package = "beezdemand") ko$y_ll4 <- ll4(ko$y, lambda = 4) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") get_demand_comparisons(fit)data(ko, package = "beezdemand") ko$y_ll4 <- ll4(ko$y, lambda = 4) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") get_demand_comparisons(fit)
Computes factor-level contrasts for demand parameters from a
beezdemand_tmb model. Returns a classed beezdemand_comparison object
(the same container the NLME backend returns), so
tidy.beezdemand_comparison() gives a backend-agnostic flat frame.
## S3 method for class 'beezdemand_tmb' get_demand_comparisons( fit_obj, param = c("Q0", "alpha"), compare_specs = NULL, contrast_type = c("pairwise", "trt.vs.ctrl"), contrast_by = NULL, adjust = "holm", at = NULL, ci_level = 0.95, report_ratios = TRUE, ... )## S3 method for class 'beezdemand_tmb' get_demand_comparisons( fit_obj, param = c("Q0", "alpha"), 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_obj |
A |
param |
Character vector. Which parameter(s) to compare: any of
|
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 beezdemand_comparison object: a list named by parameter, each
element a list with emmeans (native cell means), contrasts_log10
(log10-scale contrasts with contrast, estimate, std.error,
statistic, df, conf.low, conf.high, p.value), and (if
report_ratios) contrasts_ratio. When contrast_by is active, the
contrast tables gain leading by-column(s) (user-requested original names)
before contrast. Attributes backend, adjustment_method,
compare_specs_used, contrast_type_used, contrast_by_used, and
contrast_by_map (per-parameter original -> effective by-name map)
describe the call.
tidy.beezdemand_comparison() for the backend-agnostic frame.
data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) res <- get_demand_comparisons(fit, param = "Q0") tidy(res)data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) res <- get_demand_comparisons(fit, param = "Q0") tidy(res)
Calculates Estimated Marginal Means (EMMs) for Q0 and alpha parameters
from a beezdemand_nlme model for all combinations of specified factor levels.
Reports parameters on both their estimation scale (log10) and their
natural, back-transformed scale. Optionally includes Essential Value (EV).
get_demand_param_emms(fit_obj, ...) ## Default S3 method: get_demand_param_emms(fit_obj, ...) ## S3 method for class 'beezdemand_nlme' get_demand_param_emms( fit_obj, factors_in_emm = NULL, at = NULL, ci_level = 0.95, include_ev = FALSE, param = c("both", "Q0", "alpha"), ... )get_demand_param_emms(fit_obj, ...) ## Default S3 method: get_demand_param_emms(fit_obj, ...) ## S3 method for class 'beezdemand_nlme' get_demand_param_emms( fit_obj, factors_in_emm = NULL, at = NULL, ci_level = 0.95, include_ev = FALSE, param = c("both", "Q0", "alpha"), ... )
fit_obj |
A |
... |
Additional arguments passed to |
factors_in_emm |
Character vector of factor names to compute EMMs over.
Defaults to all factors present in the |
at |
Optional named list specifying levels of conditioning variables for |
ci_level |
Confidence level for the EMMs (default 0.95). |
include_ev |
Logical. If TRUE, calculates and includes Essential Value (EV)
derived from alpha, along with its confidence interval (calculated by
back-transforming the CI of alpha_param_log10). Default |
param |
Character, one of |
A tibble containing:
Factor levels |
Columns for each factor in |
Q0_param_log10, alpha_param_log10
|
EMMs for the model parameters (log10 scale) with their respective confidence intervals (LCL_Q0_param, UCL_Q0_param, etc.). |
Q0_natural, alpha_natural
|
EMMs back-transformed to the natural scale (10^param) with their respective confidence intervals (LCL_Q0_natural, UCL_Q0_natural, etc.). |
EV, LCL_EV, UCL_EV
|
(If |
When param = "Q0" or param = "alpha", only the columns associated with
the requested parameter (plus factor columns and, for "alpha", the EV
block) are returned.
For a param_space = "natural" fit (see fit_demand_mixed()), the
emmeans reference-grid summary IS already natural-scale, so
*_natural columns are populated directly and *_param_log10 columns
are filled with log10() of them for column-set parity with
param_space = "log10" fits. Because that fit is an unconstrained
parameterization, a Wald CI bound (or, rarely, the point estimate
itself) can be non-positive; *_param_log10 is NA wherever the
corresponding *_natural value is <= 0 (log10 is undefined there),
without raising a warning. *_natural itself is never NA from this.
data(ko, package = "beezdemand") ko$y_ll4 <- ll4(ko$y, lambda = 4) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") get_demand_param_emms(fit) # Request only Q0 columns (convenient for pivoting and plotting) get_demand_param_emms(fit, param = "Q0")data(ko, package = "beezdemand") ko$y_ll4 <- ll4(ko$y, lambda = 4) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") get_demand_param_emms(fit) # Request only Q0 columns (convenient for pivoting and plotting) get_demand_param_emms(fit, param = "Q0")
Computes estimated marginal means (EMMs) for demand parameters from a
beezdemand_tmb model. Uses design matrices and beta vectors with vcov
from TMB::sdreport().
## S3 method for class 'beezdemand_tmb' get_demand_param_emms( fit_obj, param = c("Q0", "alpha"), factors_in_emm = NULL, at = NULL, ci_level = 0.95, ... )## S3 method for class 'beezdemand_tmb' get_demand_param_emms( fit_obj, param = c("Q0", "alpha"), factors_in_emm = NULL, at = NULL, ci_level = 0.95, ... )
fit_obj |
A |
param |
Character. Which parameter to compute EMMs for: |
factors_in_emm |
Character vector of factors to retain in the EMM
reference grid. If it names a strict subset of the fitted factors, the
omitted factors are marginalized over using equal weights across the
full crossing of their levels (emmeans' default |
at |
Named list specifying factor levels and continuous-covariate
values for conditional EMMs. For continuous covariates, a single
numeric value per covariate; multiple values produce a warning and
only the first is used. |
ci_level |
Numeric. Confidence level for intervals. |
... |
Additional arguments. |
A tibble with columns: level, estimate, std.error, conf.low, conf.high.
Marginalization is exact because Q0/alpha are linear in the
fixed-effect coefficients on the log scale, so averaging the reference-grid
design rows and then multiplying by the coefficient vector equals averaging
the per-cell parameter predictions.
data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) get_demand_param_emms(fit, param = "Q0") get_demand_param_emms(fit, param = "alpha")data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) get_demand_param_emms(fit, param = "Q0") get_demand_param_emms(fit, param = "alpha")
Computes the trend (slope) of Q0 and/or alpha with respect to one or more
continuous covariates using emmeans::emtrends() on a fitted beezdemand_nlme
model. Trends are computed on the parameter estimation scale (log10), consistent
with how parameters are modeled.
get_demand_param_trends( fit_obj, params = c("Q0", "alpha"), covariates, specs = ~1, at = NULL, ci_level = 0.95, ... )get_demand_param_trends( fit_obj, params = c("Q0", "alpha"), covariates, specs = ~1, at = NULL, ci_level = 0.95, ... )
fit_obj |
A |
params |
Character vector of parameters to analyze: any of "Q0", "alpha".
Default |
covariates |
Character vector of continuous covariate names for which to compute trends. |
specs |
A formula specifying the factors over which to produce trends
(e.g., |
at |
Optional named list to condition variables (factors or continuous)
when computing trends (passed through to |
ci_level |
Confidence level for intervals. Default 0.95. |
... |
Additional args passed to |
A tibble combining trends for each requested parameter and covariate,
including columns for grouping factors (from specs), parameter,
covariate, trend (slope on log10 scale), and its CI (lower.CL, upper.CL).
data(ko) ko$dose_num <- as.numeric(as.character(ko$dose)) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "drug", equation_form = "zben") trends <- get_demand_param_trends(fit, covariates = "dose_num", specs = ~ drug)data(ko) ko$dose_num <- as.numeric(as.character(ko$dose)) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "drug", equation_form = "zben") trends <- get_demand_param_trends(fit, covariates = "dose_num", specs = ~ drug)
Calculates summary statistics for consumption data at each price point, including measures of central tendency (mean, median), variability (SD), range (min, max), and data quality (proportion of zeros, missing values).
This is the modern replacement for GetDescriptives(), returning a structured
S3 object with dedicated methods for printing, summarizing, and visualizing.
get_descriptive_summary( data, x_var = "x", y_var = "y", id_var = "id", by = NULL )get_descriptive_summary( data, x_var = "x", y_var = "y", id_var = "id", by = NULL )
data |
A data frame in long format with columns for subject ID, price, and consumption |
x_var |
Character string specifying the column name for price (default: "x") |
y_var |
Character string specifying the column name for consumption (default: "y") |
id_var |
Character string specifying the column name for subject ID (default: "id") |
by |
Optional character vector of column names to group by.
When supplied, statistics are computed separately within each unique
combination of the |
For each unique price in the dataset, the function calculates:
Mean - Average consumption across subjects (rounded to 2 decimals)
Median - Median consumption (rounded to 2 decimals)
SD - Standard deviation (rounded to 2 decimals)
PropZeros - Proportion of subjects with zero consumption (0-1)
NAs - Count of missing values
Min - Minimum consumption value (rounded to 2 decimals)
Max - Maximum consumption value (rounded to 2 decimals)
An S3 object of class beezdemand_descriptive containing:
statistics - Data frame with 8 columns (Price, Mean, Median, SD, PropZeros, NAs, Min, Max) and one row per unique price
call - The matched call
data_summary - List with n_subjects, n_prices, and prices vector
GetDescriptives() - Legacy function (superseded)
plot.beezdemand_descriptive() - Visualization method
summary.beezdemand_descriptive() - Extended summary
data(apt, package = "beezdemand") # Calculate descriptive statistics desc <- get_descriptive_summary(apt) print(desc) # View statistics table desc$statistics # Create visualization plot(desc) # Extended summary with distribution info summary(desc) # Grouped summary: statistics and plots faceted by group data(apt_full) desc_g <- get_descriptive_summary(apt_full, by = "gender") desc_g$statistics plot(desc_g)data(apt, package = "beezdemand") # Calculate descriptive statistics desc <- get_descriptive_summary(apt) print(desc) # View statistics table desc$statistics # Create visualization plot(desc) # Extended summary with distribution info summary(desc) # Grouped summary: statistics and plots faceted by group data(apt_full) desc_g <- get_descriptive_summary(apt_full, by = "gender") desc_g$statistics plot(desc_g)
Calculates empirical (model-free) measures of demand from purchase task data. These metrics characterize consumption patterns without fitting a demand curve model.
This is the modern replacement for GetEmpirical(), returning a structured
S3 object with dedicated methods for printing, summarizing, and visualizing.
get_empirical_measures(data, x_var = "x", y_var = "y", id_var = "id")get_empirical_measures(data, x_var = "x", y_var = "y", id_var = "id")
data |
A data frame in long format with columns for subject ID, price, and consumption |
x_var |
Character string specifying the column name for price (default: "x") |
y_var |
Character string specifying the column name for consumption (default: "y") |
id_var |
Character string specifying the column name for subject ID (default: "id") |
Intensity - The consumption value at the lowest price point. Reflects unrestricted demand or preferred level of consumption.
BP0 (Breakpoint 0) - The first price at which consumption drops to zero. If consumption never reaches zero, BP0 = NA. Indicates the price threshold where the commodity becomes unaffordable or undesirable.
BP1 (Breakpoint 1) - The highest price at which consumption is still non-zero. If all consumption is zero, BP1 = NA. Represents the upper limit of the commodity's value to the consumer.
Omaxe (Empirical Omax) - The maximum observed expenditure across all prices (max of price × consumption). Represents peak spending on the commodity.
Pmaxe (Empirical Pmax) - The price at which maximum expenditure occurs. If maximum expenditure is zero, Pmaxe = 0. If multiple prices have the same maximum expenditure, the highest price is returned.
An S3 object of class beezdemand_empirical containing:
measures - Data frame with one row per subject and columns:
id - Subject identifier
Intensity - Consumption at lowest price (demand intensity)
BP0 - Breakpoint 0: first price where consumption = 0
BP1 - Breakpoint 1: last price with non-zero consumption
Omaxe - Empirical Omax: maximum total expenditure (price × consumption)
Pmaxe - Empirical Pmax: price at which maximum expenditure occurs
call - The matched call
data_summary - List with n_subjects, has_zeros, and complete_cases
Data must not contain duplicate prices within a subject (will error)
Missing values (NA) in consumption are preserved in calculations where applicable
Breakpoints require at least one zero consumption value to be meaningful
GetEmpirical() - Legacy function (superseded)
plot.beezdemand_empirical() - Visualization method
summary.beezdemand_empirical() - Extended summary
data(apt, package = "beezdemand") # Calculate empirical measures emp <- get_empirical_measures(apt) print(emp) # View measures table emp$measures # Extended summary with distribution info summary(emp) # Visualize distribution of measures plot(emp) # histogram by default plot(emp, type = "matrix") # scatterplot matrixdata(apt, package = "beezdemand") # Calculate empirical measures emp <- get_empirical_measures(apt) print(emp) # View measures table emp$measures # Extended summary with distribution info summary(emp) # Visualize distribution of measures plot(emp) # histogram by default plot(emp, type = "matrix") # scatterplot matrix
Provides summary statistics for subject-level demand parameters from a hurdle demand model. This is analogous to EMMs but based on empirical Bayes estimates of subject-specific parameters.
get_hurdle_param_summary(fit_obj, ci_level = 0.95)get_hurdle_param_summary(fit_obj, ci_level = 0.95)
fit_obj |
A |
ci_level |
Confidence level for intervals (default 0.95). |
A data frame with summary statistics for each parameter:
Parameter name
Mean across subjects
Standard deviation across subjects
Median across subjects
Lower confidence limit (based on percentiles)
Upper confidence limit (based on percentiles)
Minimum value
Maximum value
fit_demand_hurdle, get_subject_pars
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") get_hurdle_param_summary(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") get_hurdle_param_summary(fit)
Extracts and combines fixed and random effects to calculate individual-level predicted coefficients for all parameter-factor combinations from a beezdemand_nlme model object. It automatically detects the factor structure and calculates coefficients for each individual and factor level.
get_individual_coefficients( fit_obj, params = c("Q0", "alpha"), format = c("wide", "long") )get_individual_coefficients( fit_obj, params = c("Q0", "alpha"), format = c("wide", "long") )
fit_obj |
A |
params |
Character vector specifying which parameters to calculate. Options are "Q0", "alpha", or c("Q0", "alpha"). Default is c("Q0", "alpha"). |
format |
Character, output format. "wide" returns one row per individual with separate columns for each parameter-factor combination. "long" returns one row per individual-parameter-factor combination. Default is "wide". |
Individual-level coefficients represent the predicted parameter values for each subject in the study. For models with factors, these coefficients combine:
The baseline intercept effect (fixed + random)
The factor-specific effect (fixed + random) for each factor level
This is equivalent to manually calculating:
coefficient = intercept_fixed + intercept_random + factor_fixed + factor_random
The function automatically handles:
Models with or without factors
Any number of factor levels
Missing random effects (defaults to 0)
Complex factor structures with multiple factors
For models without factors, only intercept coefficients are calculated. For models with factors, both intercept and factor-level coefficients are provided.
A data frame with individual-level predicted coefficients.
In "wide" format: rows are individuals, columns are parameter-factor combinations
In "long" format: columns are id, parameter, condition, coefficient_value
Column naming convention for wide format:
estimated_\{param\}_intercept: Baseline/reference level coefficient
estimated_\{param\}_\{factor\}\{level\}: Factor level-specific coefficient
All coefficients are on the log10 scale (same as model estimation scale).
To convert to natural scale, use 10^coefficient.
fit_demand_mixed for fitting the original model
coef.beezdemand_nlme for extracting model coefficients
get_demand_param_emms for estimated marginal means
data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "drug", equation_form = "zben") individual_coefs <- get_individual_coefficients(fit) head(individual_coefs)data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "drug", equation_form = "zben") individual_coefs <- get_individual_coefficients(fit) head(individual_coefs)
Calculates the k scaling parameter used in demand curve equations to normalize consumption across different units or ranges. The k value is derived from the logarithmic range of consumption values.
This is the modern replacement for GetK(), with explicit parameters for
the adjustment value and optional verbose output.
get_k( data, use_means = TRUE, adjustment = 0.5, x_var = "x", y_var = "y", verbose = FALSE )get_k( data, use_means = TRUE, adjustment = 0.5, x_var = "x", y_var = "y", verbose = FALSE )
data |
A data frame in long format with columns for price and consumption |
use_means |
Logical indicating whether to calculate k from mean consumption by price (TRUE, default) or from individual consumption values (FALSE) |
adjustment |
Numeric adjustment added to the log range (default: 0.5). This value ensures k is slightly larger than the observed range. |
x_var |
Character string specifying the column name for price (default: "x") |
y_var |
Character string specifying the column name for consumption (default: "y") |
verbose |
Logical indicating whether to print calculation details (default: FALSE) |
The k parameter is calculated as:
where max and min are the maximum and minimum non-zero consumption values.
The k parameter appears in several demand curve equations:
Hursh & Silberberg (2008): Scales the exponential term
Koffarnus et al. (2015): Normalizes the exponentiated model
use_means = TRUE (default): Calculates k from mean consumption at each price point. Recommended when data has multiple subjects, as it reduces influence of individual outliers.
use_means = FALSE: Calculates k from the full range of individual consumption values. May be preferable for single-subject data or when individual variability is theoretically important.
A single numeric value representing the k scaling parameter
Only non-zero consumption values are used (zero values are excluded)
Missing values (NA) are automatically removed via na.rm = TRUE
The default adjustment of 0.5 is conventional but can be modified
GetK() - Legacy function (superseded)
FitCurves() - Uses k parameter in demand curve fitting
data(apt, package = "beezdemand") # Calculate k using default settings (mean range + 0.5) k_val <- get_k(apt) # Calculate k from individual values k_ind <- get_k(apt, use_means = FALSE) # Calculate with custom adjustment k_custom <- get_k(apt, adjustment = 1.0) # Show calculation details k_verbose <- get_k(apt, verbose = TRUE)data(apt, package = "beezdemand") # Calculate k using default settings (mean range + 0.5) k_val <- get_k(apt) # Calculate k from individual values k_ind <- get_k(apt, use_means = FALSE) # Calculate with custom adjustment k_custom <- get_k(apt, adjustment = 1.0) # Show calculation details k_verbose <- get_k(apt, verbose = TRUE)
Wraps get_demand_param_emms: it first computes Estimated Marginal Means
(EMMs) for Q0 and alpha over all combinations of the specified factor
levels, then filters these results to the combinations of factor levels
that were actually present in the original dataset used to fit the
beezdemand_nlme model.
get_observed_demand_param_emms( fit_obj, factors_in_emm = NULL, at = NULL, ci_level = 0.95, include_ev = FALSE, ... )get_observed_demand_param_emms( fit_obj, factors_in_emm = NULL, at = NULL, ci_level = 0.95, include_ev = FALSE, ... )
fit_obj |
A |
factors_in_emm |
Character vector of factor names to compute EMMs over.
Defaults to all factors present in the |
at |
Optional named list specifying levels of conditioning variables for |
ci_level |
Confidence level for the EMMs (default 0.95).
Passed to |
include_ev |
Logical. If TRUE, calculates and includes Essential Value (EV)
derived from alpha. Passed to |
... |
Additional arguments passed to |
A tibble similar to the output of get_demand_param_emms, but filtered
to include only rows corresponding to factor level combinations that were
observed in the original fit_obj$data. Contains:
Factor levels |
Columns for each factor in |
Q0_param_log10, alpha_param_log10
|
EMMs for model parameters (log10 scale) and CIs. |
Q0_natural, alpha_natural
|
EMMs back-transformed to natural scale and CIs. |
EV, LCL_EV, UCL_EV
|
(If |
data(ko, package = "beezdemand") ko$y_ll4 <- ll4(ko$y, lambda = 4) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") get_observed_demand_param_emms(fit)data(ko, package = "beezdemand") ko$y_ll4 <- ll4(ko$y, lambda = 4) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", factors = "dose", equation_form = "zben") get_observed_demand_param_emms(fit)
Convenience function to extract subject-specific demand parameters from
a fitted demand model. For beezdemand_hurdle fits this is
equivalent to accessing object$subject_pars. For
beezdemand_tmb fits the return shape depends on the
expanded argument: it may expand across within-id factor levels
or condition within-id numeric covariates at the subject mean rather
than return the cached object$subject_pars; see
get_subject_pars.beezdemand_tmb.
get_subject_pars(object, ...)get_subject_pars(object, ...)
object |
A fitted |
... |
Method-specific arguments. The |
Data frame with subject-specific parameters including:
Subject identifier
Random effect for Part I (zeros)
Random effect for Part II (Q0)
Random effect for alpha (3-RE model only)
Subject-specific intensity (consumption at price 0)
Subject-specific elasticity
Price where P(quit) = 0.5
Price at maximum expenditure
Maximum expenditure
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") pars <- get_subject_pars(fit) head(pars)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") pars <- get_subject_pars(fit) head(pars)
Subject-level demand parameters for a beezdemand_nlme fit,
matching the column / scale / expanded contract of
get_subject_pars.beezdemand_tmb. Combines the population
fixed effects with each subject's random-effect deviations and
back-transforms to the natural scale.
## S3 method for class 'beezdemand_nlme' get_subject_pars(object, expanded = NULL, ...)## S3 method for class 'beezdemand_nlme' get_subject_pars(object, expanded = NULL, ...)
object |
A |
expanded |
Controls the return shape for fits with within-id-varying
design columns (within-subject factors, within-id covariates, or
multi-block
|
... |
Currently unused. |
A data frame. Wide form: id, b_i, c_i (if
alpha has random effects), Q0, alpha, Pmax,
Omax. Expanded form additionally includes the within-subject
factor column(s) with one row per (subject, factor-level) cell.
Q0, alpha, Pmax, Omax are on the natural
scale.
b_i / c_i)b_i / c_i are the subject's first-block random-effect
deviation for Q0 / alpha. For parity with the TMB method these are
reported on the natural-log linear-predictor scale: for the default
param_space = "log10" the stored log10 deviation is multiplied by
log(10); for param_space = "natural" the deviation is
returned on the natural parameter scale. The full per-coefficient random
effects remain available via ranef().
get_subject_pars.beezdemand_tmb
Get Subject-Specific Parameters from TMB Model
## S3 method for class 'beezdemand_tmb' get_subject_pars(object, expanded = NULL, at = NULL, ...)## S3 method for class 'beezdemand_tmb' get_subject_pars(object, expanded = NULL, at = NULL, ...)
object |
A |
expanded |
Controls return shape for fits with within-id-varying
design columns (factor-expanded random effects, within-id
continuous covariates, or multi-block
|
at |
Optional named numeric vector/list (e.g. |
... |
Additional arguments (currently unused). |
When the resolved expanded is FALSE: data
frame with columns id, b_i, c_i (if 2 RE),
Q0, alpha, Pmax, Omax. When the
resolved expanded is TRUE, the shape depends on the
kind of within-id variation: for fits with within-id factors, the
within-subject factor columns are added and rows are expanded to one
per (subject, factor-level) cell with per-cell Q0,
alpha, Pmax, Omax; for fits whose only within-id
variation is numeric, the numerics are conditioned at the subject's
mean and the return is one row per subject (no added factor columns)
with finite Q0 / alpha.
For factor-expanded or multi-block fits, the wide table's
b_i / c_i columns hold the first RE column from each
block (intercept slot for the M1 baseline block, for example) for
backward compatibility with downstream consumers. Power users who
need the full per-block RE structure can access
attr(subject_pars, "re_q0_mat") and
attr(subject_pars, "re_alpha_mat") as
n_subjects x re_dim matrices ordered by block.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(get_subject_pars(fit))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(get_subject_pars(fit))
...
GetAnalyticPmax(Alpha, K, Q0)GetAnalyticPmax(Alpha, K, Q0)
Alpha |
alpha parameter |
K |
k parameter ( > lower limit ) |
Q0 |
Q0 |
...
Numeric
Shawn Gilroy [email protected]
GetAnalyticPmax(Alpha = 0.001, K = 3, Q0 = 10)GetAnalyticPmax(Alpha = 0.001, K = 3, Q0 = 10)
Fallback method for Analytic Pmax
GetAnalyticPmaxFallback(K_, A_, Q0_)GetAnalyticPmaxFallback(K_, A_, Q0_)
K_ |
k parameter |
A_ |
alpha parameter |
Q0_ |
q0 parameter |
Derivative-based optimization strategy
numeric
Shawn Gilroy [email protected]
GetAnalyticPmaxFallback(K_ = 1, A_ = 0.001, Q0_ = 10)GetAnalyticPmaxFallback(K_ = 1, A_ = 0.001, Q0_ = 10)
Calculates descriptive statistics from purchase task data.
GetDescriptives( dat, bwplot = FALSE, outdir = "../plots/", device = "png", filename = "bwplot" )GetDescriptives( dat, bwplot = FALSE, outdir = "../plots/", device = "png", filename = "bwplot" )
dat |
Dataframe (long form) |
bwplot |
Boolean. If TRUE, a ggplot2 box and whisker plot is saved. Default is FALSE. |
outdir |
Character. Directory where plot will be saved. Be sure to include trailing '/'. Default location is one level up in "../plots/". |
device |
Character. Type of file. Default is "png". Can be "pdf". |
filename |
Character. Specify filename. Default is "bwplot". |
GetDescriptives() has been superseded by get_descriptive_summary(), which
provides a modern S3 interface with standardized methods (print(), summary(), plot()).
GetDescriptives() will continue to work but is no longer recommended for new code.
Provides the following descriptive statistics from purchase task data at each price: mean consumption, median consumption, standard deviation of consumption, proportion of 0 values, number of NAs, minimum consumption, and maximum consumption.
Dataframe with descriptive statistics
Brent Kaplan [email protected]
get_descriptive_summary() for the modern interface
GetDescriptives(apt)GetDescriptives(apt)
Calculates empirical measures for purchase task data
GetEmpirical(dat, xcol = "x", ycol = "y", idcol = "id")GetEmpirical(dat, xcol = "x", ycol = "y", idcol = "id")
dat |
data frame (long form) of purchase task data. |
xcol |
The column name that should be treated as "x" data |
ycol |
The column name that should be treated as "y" data |
idcol |
The column name that should be treated as dataset identifier |
GetEmpirical() has been superseded by get_empirical_measures(), which
provides a modern S3 interface with standardized methods (print(), summary(), plot()).
GetEmpirical() will continue to work but is no longer recommended for new code.
Will calculate and return the following empirical measures: Intensity, BP0, BP1, Omax, and Pmax
Data frame of empirical measures
Brent Kaplan [email protected]
get_empirical_measures() for the modern interface
## Obtain empirical measures GetEmpirical(apt)## Obtain empirical measures GetEmpirical(apt)
Calculates a k value by looking for the max/min consumption across entire dataset and adds .5 to that range
GetK(dat, mnrange = TRUE)GetK(dat, mnrange = TRUE)
dat |
Dataframe (long form) |
mnrange |
Boolean for whether k should be calculated based on the mean range + .5 |
GetK() has been superseded by get_k(), which provides explicit parameters
for the adjustment value and optional verbose output for better transparency.
GetK() will continue to work but is no longer recommended for new code.
Will look for maximum/minimum greater zero
Numeric
Brent Kaplan [email protected]
get_k() for the modern interface
GetK(apt)GetK(apt)
Gets values used in SimulateDemand
GetValsForSim(dat)GetValsForSim(dat)
dat |
Dataframe (long form) |
Gets values used in SimulateDemand. Per-subject residuals are matched to
the returned x price columns by EXACT identity
(make.names(as.character(x)) – no numeric tolerance), so a
subject's price values must match the global price set bit-for-bit. A
subject missing a price contributes NA for that price (tolerated
by sdindex's na.rm = TRUE); a subject with a duplicated
price is rejected with an informative error naming the subject and the
duplicated price(s), since duplicate rows have no well-defined single
residual to place in that column.
List of 3: setaparams, sdindex, x
Brent Kaplan [email protected]
GetValsForSim(apt)GetValsForSim(apt)
Glance Method for beezdemand_fixed
## S3 method for class 'beezdemand_fixed' glance(x, ...)## S3 method for class 'beezdemand_fixed' glance(x, ...)
x |
A beezdemand_fixed object |
... |
Additional arguments (ignored) |
A one-row tibble of model statistics
Returns one row per group with model-level summaries.
## S3 method for class 'beezdemand_fixed_grouped' glance(x, ...)## S3 method for class 'beezdemand_fixed_grouped' glance(x, ...)
x |
A beezdemand_fixed_grouped object |
... |
Additional arguments passed to |
A tibble with group columns prepended
Returns a one-row tibble of model-level statistics.
## S3 method for class 'beezdemand_hurdle' glance(x, ...)## S3 method for class 'beezdemand_hurdle' glance(x, ...)
x |
An object of class |
... |
Additional arguments (currently unused). |
A one-row tibble with columns:
"beezdemand_hurdle"
"TMB"
Number of observations
Number of subjects
Number of random effects
Convergence status
Log-likelihood
Akaike Information Criterion
Bayesian Information Criterion
Glance method for beezdemand_nlme
## S3 method for class 'beezdemand_nlme' glance(x, ...)## S3 method for class 'beezdemand_nlme' glance(x, ...)
x |
A beezdemand_nlme object |
... |
Additional arguments (ignored) |
A one-row tibble of model statistics with columns:
model_class: "beezdemand_nlme"
backend: "nlme"
equation_form: The equation form used
nobs: Number of observations
n_subjects: Number of subjects
n_random_effects: Number of random-effect terms (e.g. 2 for
Q0 + alpha ~ 1)
converged: Operational convergence status. TRUE when the final fit is
usable for inference, i.e., apVar (nlme's approximate covariance of the
variance-covariance parameters) is positive-definite and there is no
terminal error. Alias for final_fit_ok. It is not flipped to FALSE
by iteration-level optimizer warnings (see fit_warned).
final_fit_ok: The canonical usable-for-inference gate (apVar PD and no
terminal error); identical to converged. NLME-only.
fit_warned: Diagnostic flag that is TRUE when nlme emitted iteration-level
convergence warnings (false convergence, singular, step-halving, iteration
limit, ...) during PNLS-LME alternation. Informational only; does not gate
converged. NLME-only.
logLik, AIC, BIC: Model fit statistics
sigma: Residual standard error (NLME-only)
The shared canonical columns (through converged, logLik, AIC, BIC)
match glance.beezdemand_tmb(), so backend-agnostic code needs no dispatch
glue; final_fit_ok and fit_warned are additive NLME-only diagnostics.
Glance Method for beezdemand_systematicity
## S3 method for class 'beezdemand_systematicity' glance(x, ...)## S3 method for class 'beezdemand_systematicity' glance(x, ...)
x |
A beezdemand_systematicity object |
... |
Additional arguments (ignored) |
A one-row tibble of overall statistics
Glance at a beezdemand_tmb Model
## S3 method for class 'beezdemand_tmb' glance(x, ...)## S3 method for class 'beezdemand_tmb' glance(x, ...)
x |
A |
... |
Additional arguments. |
A one-row tibble of model-level statistics with columns:
model_class: "beezdemand_tmb"
backend: "TMB_mixed"
equation_form: The demand equation that was fitted
nobs: Number of observations
n_subjects: Number of subjects
n_random_effects: Total number of random-effect columns per subject
converged: Convergence status
logLik, AIC, BIC: Model fit statistics
The canonical columns match glance.beezdemand_nlme(), so
backend-agnostic code needs no dispatch glue.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) glance(fit)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) glance(fit)
Get model summaries from a linear cross-price model
## S3 method for class 'cp_model_lm' glance(x, ...)## S3 method for class 'cp_model_lm' glance(x, ...)
x |
A cp_model_lm object. |
... |
Additional arguments (unused). |
A tibble with model summary statistics.
Get model summaries from a mixed-effects cross-price model
## S3 method for class 'cp_model_lmer' glance(x, ...)## S3 method for class 'cp_model_lmer' glance(x, ...)
x |
A cp_model_lmer object. |
... |
Additional arguments passed to broom.mixed::glance. |
A tibble with model summary statistics: the columns of
broom.mixed::glance() for the underlying merMod fit, followed by
converged (logical; TRUE when lme4 reported no convergence problem,
FALSE when it did, NA when the fit predates the stored metadata or
failed). converged mirrors broom::glance.nls()'s isConv so a
batch of fits can be screened programmatically; print() and
summary() report the same flag.
Extracts model summary statistics (goodness-of-fit measures and other model information) from a cross-price demand model into a single-row data frame, following the conventions of the broom package.
## S3 method for class 'cp_model_nls' glance(x, ...)## S3 method for class 'cp_model_nls' glance(x, ...)
x |
A model object from fit_cp_nls or fit_cp_linear |
... |
Additional arguments (unused) |
A one-row data frame with model summary statistics:
r.squared |
R-squared value indicating model fit |
aic |
Akaike Information Criterion |
bic |
Bayesian Information Criterion |
equation |
The equation type used in the model |
method |
The method used to fit the model |
transform |
The transformation applied to the data, if any |
data(etm) fit <- fit_cp_nls(etm, equation = "exponentiated") glance(fit)data(etm) fit <- fit_cp_nls(etm, equation = "exponentiated") glance(fit)
Data from Ko et al. (2002)
koko
A tibble with 135 rows and 6 columns:
unique identifier
fixed-ratio requirement
consumption
consumption transformed via ll4
drug
dose
Ben Bolker's port of Lambert W from GNU Scientific Library (GPLV3)
lambertW(z, b = 0, maxiter = 10, eps = .Machine$double.eps, min.imag = 1e-09)lambertW(z, b = 0, maxiter = 10, eps = .Machine$double.eps, min.imag = 1e-09)
z |
input value |
b |
branch, set to principal by default |
maxiter |
Halley iteration count |
eps |
error precision |
min.imag |
minimum for imaginary solution |
Ben Bolker's port of Lambert W from GNU Scientific Library
numeric
Benjamin Bolker (port)
## Principal branch: W(1) ~ 0.5671 lambertW(1) ## Verify: W(z) * exp(W(z)) == z w <- lambertW(2) w * exp(w)## Principal branch: W(1) ~ 0.5671 lambertW(1) ## Verify: W(z) * exp(W(z)) == z w <- lambertW(2) w * exp(w)
Applies a log-logistic like transformation, specifically log_base(x^lambda + 1) / lambda.
This transformation is useful for compressing data that spans several orders of
magnitude while remaining defined at zero (x = 0 yields 0).
It is related to the Box-Cox transformation and to the generalized logarithm.
ll4(x, lambda = 4, base = 10)ll4(x, lambda = 4, base = 10)
x |
A numeric vector or scalar of non-negative values to be transformed. |
lambda |
A positive numeric scalar, the lambda parameter of the transformation.
Controls the curvature. Default is |
base |
A positive numeric scalar, the base of the logarithm. Default is |
A numeric vector or scalar of the transformed values.
Returns NaN for x < 0 if lambda results in non-real numbers (e.g., even root of negative).
However, the intended domain is x >= 0.
# Basic usage ll4(0) ll4(1) ll4(10) ll4(c(0, 1, 10, 100, 1000)) # Using a different lambda or base ll4(10, lambda = 2) ll4(10, base = exp(1)) # Natural log base # Typical workflow: transform -> fit -> back-transform data(apt) apt$y_ll4 <- ll4(apt$y) # Verify round-trip: ll4_inv(ll4(x)) == x all.equal(apt$y, ll4_inv(apt$y_ll4))# Basic usage ll4(0) ll4(1) ll4(10) ll4(c(0, 1, 10, 100, 1000)) # Using a different lambda or base ll4(10, lambda = 2) ll4(10, base = exp(1)) # Natural log base # Typical workflow: transform -> fit -> back-transform data(apt) apt$y_ll4 <- ll4(apt$y) # Verify round-trip: ll4_inv(ll4(x)) == x all.equal(apt$y, ll4_inv(apt$y_ll4))
Applies the inverse of the ll4 transformation.
Given y = ll4(x), this function calculates x = (base^(y * lambda) - 1)^(1/lambda).
ll4_inv(y, lambda = 4, base = 10)ll4_inv(y, lambda = 4, base = 10)
y |
A numeric vector or scalar of transformed values (output from |
lambda |
A positive numeric scalar, the lambda parameter used in the original
|
base |
A positive numeric scalar, the base of the logarithm used in the
original |
Domain and boundary behavior. The inverse LL4 transformation is defined
for y >= 0 (when base = 10 and lambda = 4). For y < 0, the
intermediate quantity base^(y * lambda) - 1 becomes negative, and raising
a negative number to the fractional power 1/lambda is undefined in real
arithmetic. In this case, the function returns 0 (consumption cannot be
negative).
This boundary condition arises in practice when a model predicts fitted values
below zero on the LL4 scale, typically for extrapolation to very high
prices. The mapping to zero is the natural floor because ll4(0) = 0 and
the LL4 transformation is monotonically increasing on [0, Inf).
A numeric vector or scalar of the original, untransformed values.
Returns 0 when the intermediate quantity base^(y * lambda) - 1 is
negative (i.e., when y < 0 for base = 10 and even lambda), since
consumption cannot be negative.
original_values <- c(0, 1, 10, 100, 1000) transformed_values <- ll4(original_values) back_transformed_values <- ll4_inv(transformed_values) print(data.frame(original_values, transformed_values, back_transformed_values)) all.equal(original_values, back_transformed_values) # Should be TRUE or very close # Negative y values are mapped to 0 (consumption floor) ll4_inv(-0.5, lambda = 4, base = 10) # Returns 0original_values <- c(0, 1, 10, 100, 1000) transformed_values <- ll4(original_values) back_transformed_values <- ll4_inv(transformed_values) print(data.frame(original_values, transformed_values, back_transformed_values)) all.equal(original_values, back_transformed_values) # Should be TRUE or very close # Negative y values are mapped to 0 (consumption floor) ll4_inv(-0.5, lambda = 4, base = 10) # Returns 0
Extracts the log-likelihood from a fitted hurdle demand model. Useful for likelihood ratio tests comparing nested models.
## S3 method for class 'beezdemand_hurdle' logLik(object, ...)## S3 method for class 'beezdemand_hurdle' logLik(object, ...)
object |
An object of class |
... |
Additional arguments (currently unused). |
An object of class logLik with the log-likelihood value
and attributes for degrees of freedom and number of observations.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") logLik(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") logLik(fit)
Long-form purchase task data across nicotine conditions.
lowNicCleanlowNicClean
A data frame with 5 columns: id, condition, x, y, commodity.
# data(lowNicClean)# data(lowNicClean)
Hurdle currently has intercept-only Part I and Part II linear
predictors, so X_binary and X_consumption are each a single column
of ones with nobs(fit) rows. Returned for parity with
model.matrix.beezdemand_tmb(). Future support for factor / covariate
effects on hurdle components will enrich these matrices without
changing the API.
## S3 method for class 'beezdemand_hurdle' model.matrix(object, what = NULL, ...)## S3 method for class 'beezdemand_hurdle' model.matrix(object, what = NULL, ...)
object |
A |
what |
|
... |
Unused. |
Named list of numeric matrices, or a single matrix when what
is set.
By default returns a named list of all four design matrices the TMB
template consumed: X_q0, X_alpha, Z_q0, Z_alpha. Use what
to select a single matrix. X_q0 and X_alpha are zero-copy
references to fit$formula_details; Z_q0 and Z_alpha are
recomputed via the internal builder.
## S3 method for class 'beezdemand_tmb' model.matrix(object, what = NULL, ...)## S3 method for class 'beezdemand_tmb' model.matrix(object, what = NULL, ...)
object |
A |
what |
|
... |
Unused. |
Returning a named list (vs the single matrix lm/lme4 return) is
intentional: the TMB tier has two fixed-effect linear predictors
(one per nonlinear parameter), not one.
Named list of numeric matrices, or a single numeric matrix when
what is set. NULL (with a message) when a degenerate Z is requested.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) str(model.matrix(fit)) head(model.matrix(fit, what = "X_q0"))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) str(model.matrix(fit)) head(model.matrix(fit, what = "X_q0"))
Universal-accessor parity with nobs.beezdemand_tmb() and the cross-price
classes. broom::glance(fit)$nobs and BIC(fit) were already correct via
their own paths (param_info$n_obs and the nobs attribute on
logLik(), respectively); this method closes the gap for any caller
that consumes nobs() directly.
## S3 method for class 'beezdemand_hurdle' nobs(object, ...)## S3 method for class 'beezdemand_hurdle' nobs(object, ...)
object |
A |
... |
Unused. |
Integer scalar, the number of observations the model was fit on.
Number of Observations in a Cross-Price Demand Model (Linear)
## S3 method for class 'cp_model_lm' nobs(object, ...)## S3 method for class 'cp_model_lm' nobs(object, ...)
object |
A |
... |
Additional arguments (unused). |
Integer; observation count or NA_integer_ if unavailable.
Number of Observations in a Cross-Price Demand Model (Mixed-Effects)
## S3 method for class 'cp_model_lmer' nobs(object, ...)## S3 method for class 'cp_model_lmer' nobs(object, ...)
object |
A |
... |
Additional arguments (unused). |
Integer; observation count or NA_integer_ if unavailable.
Number of Observations in a Cross-Price Demand Model (Nonlinear)
## S3 method for class 'cp_model_nls' nobs(object, ...)## S3 method for class 'cp_model_nls' nobs(object, ...)
object |
A |
... |
Additional arguments (unused). |
Integer; observation count or NA_integer_ if unavailable.
ETM data across price points with product quantities.
ongoingETMongoingETM
A data frame with 6 columns: id, x, AdjCig, FixCig, ECig, flavor.
# data(ongoingETM)# data(ongoingETM)
beezdemand Color Palette
palette_beezdemand()palette_beezdemand()
Named vector of brand colors.
palette_beezdemand()palette_beezdemand()
Converts demand data between wide format (one row per subject,
prices as columns) and long format (one row per observation with id, x,
y columns). This is a convenience wrapper around tidyr::pivot_longer()
and tidyr::pivot_wider() tailored for behavioral economic purchase task
data.
pivot_demand_data( data, format = c("long", "wide"), id_var = "id", x_var = "x", y_var = "y", price_cols = NULL, x_values = NULL, drop_na = TRUE )pivot_demand_data( data, format = c("long", "wide"), id_var = "id", x_var = "x", y_var = "y", price_cols = NULL, x_values = NULL, drop_na = TRUE )
data |
A data frame or tibble to reshape. |
format |
Character. Direction of reshaping: |
id_var |
Character. Name of the subject/series identifier column.
Default |
x_var |
Character. Name of the price column in long-format data. Used
when |
y_var |
Character. Name of the consumption column in long-format data.
Used when |
price_cols |
Character vector of column names in wide-format data that
represent prices. Used when |
x_values |
Numeric vector of actual price values corresponding to each
price column in wide-format data. Used when |
drop_na |
Logical. When |
format = "long")The function determines which columns are price columns by:
If price_cols is provided, those columns are used directly.
If price_cols is NULL, column names are tested with as.numeric().
Columns whose names successfully parse as numbers (e.g., "0", "0.5",
"10") are treated as price columns. Remaining non-id_var columns are
preserved as extra identifiers.
If no column names parse as numeric, all non-id_var columns become price
columns and x_values must be supplied.
Actual numeric price values come from x_values if supplied, or from parsing
column names. If column names cannot be parsed and x_values is not supplied,
an error is raised.
format = "wide")Pivots long data so that each unique value in x_var becomes a column, with
values from y_var. All columns except x_var and y_var are used as
identifiers.
A tibble. When format = "long": columns id, any extra identifier
columns, x (numeric price), and y (numeric consumption). When
format = "wide": one row per subject with prices as column names.
# --- Wide to long --- # Columns named as prices (auto-parsed) wide_num <- data.frame( id = 1:3, "0" = c(10, 8, 12), "0.5" = c(9, 7, 11), "1" = c(8, 6, 9), check.names = FALSE ) pivot_demand_data(wide_num, format = "long") # Columns with non-numeric names require x_values wide_named <- data.frame(id = 1:2, price_1 = c(10, 8), price_2 = c(5, 4)) pivot_demand_data(wide_named, format = "long", x_values = c(0, 0.5)) # --- Long to wide --- data(apt, package = "beezdemand") wide_apt <- pivot_demand_data(apt, format = "wide") head(wide_apt)# --- Wide to long --- # Columns named as prices (auto-parsed) wide_num <- data.frame( id = 1:3, "0" = c(10, 8, 12), "0.5" = c(9, 7, 11), "1" = c(8, 6, 9), check.names = FALSE ) pivot_demand_data(wide_num, format = "long") # Columns with non-numeric names require x_values wide_named <- data.frame(id = 1:2, price_1 = c(10, 8), price_2 = c(5, 4)) pivot_demand_data(wide_named, format = "long", x_values = c(0, 0.5)) # --- Long to wide --- data(apt, package = "beezdemand") wide_apt <- pivot_demand_data(apt, format = "wide") head(wide_apt)
Visualizes the distribution of individual-level alpha (elasticity) estimates from a fitted hurdle model, with the population MLE as a reference.
plot_alpha_distribution(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_alpha_distribution( object, type = c("density", "histogram"), log_scale = TRUE, bins = 30, style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_alpha_distribution( object, type = c("density", "histogram"), log_scale = TRUE, bins = 30, style = c("modern", "apa"), ... )plot_alpha_distribution(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_alpha_distribution( object, type = c("density", "histogram"), log_scale = TRUE, bins = 30, style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_alpha_distribution( object, type = c("density", "histogram"), log_scale = TRUE, bins = 30, style = c("modern", "apa"), ... )
object |
A fitted model object with subject-level parameters. |
... |
Additional arguments passed to methods. |
type |
Character; |
log_scale |
Logical; plot on log10 scale (default |
bins |
Integer; histogram bins (default 30). |
style |
Character; |
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) plot_alpha_distribution(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", random_effects = c("zeros", "q0", "alpha")) plot_alpha_distribution(fit)
Plots population-level demand curves from multiple fitted models on the same axes for visual comparison.
plot_demand_overlay( ..., model_list = NULL, labels = NULL, prices = NULL, n_points = 200, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, y_min = 0.001, inv_fun = identity, x_lab = "Price", y_lab = "Consumption", style = c("modern", "apa") )plot_demand_overlay( ..., model_list = NULL, labels = NULL, prices = NULL, n_points = 200, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, y_min = 0.001, inv_fun = identity, x_lab = "Price", y_lab = "Consumption", style = c("modern", "apa") )
... |
Fitted model objects (named or unnamed). |
model_list |
Optional named list of models (combined with |
labels |
Character vector of model labels for the legend. |
prices |
Numeric vector of prices. Default uses the union of observed prices across all models. |
n_points |
Integer; number of points for smooth curves (default 200). |
x_trans |
Character; x-axis transformation (default |
free_trans |
Numeric; replacement for price = 0 on log scales. |
y_min |
Numeric; minimum consumption value to display. Values below
this floor are dropped to prevent extreme predictions (e.g., 1e-16 from
hurdle models) from compressing the y-axis. Set to |
inv_fun |
Function to back-transform consumption values (e.g., ll4_inv for LL4-transformed models). Default is identity. |
x_lab, y_lab
|
Axis labels. |
style |
Character; |
A ggplot2 object.
data(apt) fit1 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") # Compare with a 3-RE model: # fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", # random_effects = c("zeros", "q0", "alpha")) # plot_demand_overlay(fit1, fit2, labels = c("2-RE", "3-RE")) plot_demand_overlay(fit1, labels = c("Hurdle"))data(apt) fit1 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") # Compare with a 3-RE model: # fit2 <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id", # random_effects = c("zeros", "q0", "alpha")) # plot_demand_overlay(fit1, fit2, labels = c("2-RE", "3-RE")) plot_demand_overlay(fit1, labels = c("Hurdle"))
Computes and plots the own-price point elasticity of demand across prices. Elasticity is computed numerically via central differences on the unconditional demand curve.
plot_elasticity(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_elasticity( object, prices = NULL, n_points = 200, show_unit = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Elasticity", style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_elasticity( object, prices = NULL, n_points = 200, show_unit = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Elasticity", style = c("modern", "apa"), ... )plot_elasticity(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_elasticity( object, prices = NULL, n_points = 200, show_unit = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Elasticity", style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_elasticity( object, prices = NULL, n_points = 200, show_unit = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Elasticity", style = c("modern", "apa"), ... )
object |
A fitted model object. |
... |
Additional arguments passed to methods. |
prices |
Numeric vector of prices. If |
n_points |
Integer; number of grid points (default 200). |
show_unit |
Logical; show unit elasticity reference line at -1
(default |
x_trans |
Character; x-axis transformation (default |
free_trans |
Numeric; replacement for price = 0 on log scales. |
x_lab, y_lab
|
Axis labels. |
style |
Character; |
Point elasticity is computed as:
This uses the unconditional demand Q(P) for hurdle models (which includes the probability of zero consumption), so the result is the economically relevant total elasticity.
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_elasticity(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_elasticity(fit)
Plots the expenditure curve (Price x Consumption) derived from the fitted demand model. Optionally overlays Pmax and Omax reference lines.
plot_expenditure(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_expenditure( object, prices = NULL, n_points = 200, show_pmax = TRUE, show_omax = TRUE, demand_type = c("unconditional", "conditional"), x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Expenditure (P × Q)", style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_expenditure( object, prices = NULL, n_points = 200, show_pmax = TRUE, show_omax = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Expenditure (P × Q)", style = c("modern", "apa"), ... )plot_expenditure(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_expenditure( object, prices = NULL, n_points = 200, show_pmax = TRUE, show_omax = TRUE, demand_type = c("unconditional", "conditional"), x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Expenditure (P × Q)", style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_expenditure( object, prices = NULL, n_points = 200, show_pmax = TRUE, show_omax = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), free_trans = 0.01, x_lab = "Price", y_lab = "Expenditure (P × Q)", style = c("modern", "apa"), ... )
object |
A fitted model object. |
... |
Additional arguments passed to methods. |
prices |
Numeric vector of prices. If |
n_points |
Integer; number of grid points (default 200). |
show_pmax |
Logical; show Pmax vertical line (default |
show_omax |
Logical; show Omax horizontal line (default |
demand_type |
Character; for |
x_trans |
Character; x-axis transformation (default |
free_trans |
Numeric; replacement for price = 0 on log scales. |
x_lab, y_lab
|
Axis labels. |
style |
Character; |
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_expenditure(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_expenditure(fit)
Plots 1D slices of the SSR surface, fixing one parameter at the MLE and varying the other.
plot_loss_profile(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_loss_profile( object, parameter = c("both", "q0", "alpha"), resolution = 200, range = c(-3, 3), style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_loss_profile( object, parameter = c("both", "q0", "alpha"), resolution = 200, range = c(-3, 3), style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_nlme' plot_loss_profile( object, parameter = c("both", "q0", "alpha"), resolution = 200, range = c(-3, 3), style = c("modern", "apa"), type = c("ssr", "marginal"), ... )plot_loss_profile(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_loss_profile( object, parameter = c("both", "q0", "alpha"), resolution = 200, range = c(-3, 3), style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_loss_profile( object, parameter = c("both", "q0", "alpha"), resolution = 200, range = c(-3, 3), style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_nlme' plot_loss_profile( object, parameter = c("both", "q0", "alpha"), resolution = 200, range = c(-3, 3), style = c("modern", "apa"), type = c("ssr", "marginal"), ... )
object |
A fitted model object. |
... |
Additional arguments passed to methods. |
parameter |
Character; which parameter to profile: |
resolution |
Integer; number of grid points (default 200). |
range |
Numeric vector of length 2; range in log10 units relative to
MLE (default |
style |
Character; plot style, |
type |
Character; loss profile to plot for NLME models. |
A ggplot2 object. If parameter = "both" and patchwork is
available, returns a combined patchwork object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_loss_profile(fit, parameter = "q0") plot_loss_profile(fit, parameter = "both")data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_loss_profile(fit, parameter = "q0") plot_loss_profile(fit, parameter = "both")
Visualizes the sum-of-squared-residuals (SSR) surface over a grid of Q0 and alpha values, holding other parameters (k, variance components) fixed at their MLE. The SSR is computed on aggregated mean log-consumption by price for the Part II (continuous) component of hurdle models.
plot_loss_surface(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_loss_surface( object, resolution = 80, q0_range = NULL, alpha_range = NULL, fill_palette = "D", show_mle = TRUE, show_contours = FALSE, style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_loss_surface( object, resolution = 80, q0_range = NULL, alpha_range = NULL, fill_palette = "D", show_mle = TRUE, show_contours = FALSE, style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_nlme' plot_loss_surface( object, resolution = 80, q0_range = NULL, alpha_range = NULL, fill_palette = "D", show_mle = TRUE, show_contours = FALSE, style = c("modern", "apa"), type = c("ssr", "marginal"), ... )plot_loss_surface(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_loss_surface( object, resolution = 80, q0_range = NULL, alpha_range = NULL, fill_palette = "D", show_mle = TRUE, show_contours = FALSE, style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_loss_surface( object, resolution = 80, q0_range = NULL, alpha_range = NULL, fill_palette = "D", show_mle = TRUE, show_contours = FALSE, style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_nlme' plot_loss_surface( object, resolution = 80, q0_range = NULL, alpha_range = NULL, fill_palette = "D", show_mle = TRUE, show_contours = FALSE, style = c("modern", "apa"), type = c("ssr", "marginal"), ... )
object |
A fitted model object. |
... |
Additional arguments passed to methods. |
resolution |
Integer; grid resolution per axis (default 80). |
q0_range |
Numeric vector of length 2; Q0 range (natural scale). Default: MLE +/- 3 orders of magnitude. |
alpha_range |
Numeric vector of length 2; alpha range (natural scale). Default: MLE +/- 3 orders of magnitude. |
fill_palette |
Character; viridis palette option (default |
show_mle |
Logical; overlay MLE point (default |
show_contours |
Logical; add contour lines (default |
style |
Character; plot style, |
type |
Character; loss surface to plot for NLME models. |
Note that this function computes SSR on price-aggregated means rather than a true profile likelihood. The resulting surface shows how well different (Q0, alpha) pairs explain the average demand pattern, but does not account for individual variation. For models with large random effects, the surface may appear sharper than the full-data objective.
Supported model classes: beezdemand_hurdle, beezdemand_tmb, and
beezdemand_nlme. Models with factor covariates on Q0 or alpha are not
supported; use get_demand_param_emms() instead.
A ggplot2 object.
plot_loss_profile() for 1D profile slices
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_loss_surface(fit) plot_loss_surface(fit, resolution = 50, show_contours = TRUE)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_loss_surface(fit) plot_loss_surface(fit, resolution = 50, show_contours = TRUE)
Creates a forest plot (coefficient plot) comparing parameter estimates and confidence intervals across multiple fitted demand models.
plot_model_comparison( ..., model_list = NULL, labels = NULL, parameters = c("Q0", "alpha"), conf_level = 0.95, style = c("modern", "apa") )plot_model_comparison( ..., model_list = NULL, labels = NULL, parameters = c("Q0", "alpha"), conf_level = 0.95, style = c("modern", "apa") )
... |
Fitted model objects. |
model_list |
Optional named list of models. |
labels |
Character vector of model labels. |
parameters |
Character vector of parameter names to compare
(default |
conf_level |
Numeric; confidence level for intervals (default 0.95). |
style |
Character; |
Uses tidy() methods to extract parameter estimates and standard errors.
Confidence intervals are computed as estimate +/- z * SE. Parameters are
matched by the term column from tidy() output.
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_model_comparison(fit, labels = "Hurdle")data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_model_comparison(fit, labels = "Hurdle")
Creates Q-Q plots for random effects to assess normality assumptions.
plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdemand_hurdle' plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdemand_nlme' plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdemand_tmb' plot_qq(object, which = NULL, ...)plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdemand_hurdle' plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdemand_nlme' plot_qq(object, which = NULL, ...) ## S3 method for class 'beezdemand_tmb' plot_qq(object, which = NULL, ...)
object |
A fitted model object with random effects ( |
which |
Character vector; which random effects to plot. Default is all. |
... |
Additional arguments (ignored). |
For beezdemand_tmb models, Q-Q plots display empirical Bayes predictions
(BLUPs) of the random effects (b_i for Q0, c_i for alpha). These
exhibit shrinkage toward the population mean: subjects with fewer
observations or higher variability are pulled toward zero. Consequently,
Q-Q plots may appear more normal than the true random effect distribution
(Verbeke & Molenberghs, 2000). Useful for detecting gross departures
(bimodality, heavy tails) but not a definitive normality test.
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_qq(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_qq(fit)
Creates diagnostic panels for random effects: histogram and Q-Q plot for each selected random effect, plus (for the zeros RE) a comparison of observed vs predicted proportion of zeros across prices.
plot_re_diagnostics(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_re_diagnostics( object, which = c("all", "zeros", "q0", "alpha"), style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_re_diagnostics( object, which = c("all", "q0", "alpha"), style = c("modern", "apa"), ... )plot_re_diagnostics(object, ...) ## S3 method for class 'beezdemand_hurdle' plot_re_diagnostics( object, which = c("all", "zeros", "q0", "alpha"), style = c("modern", "apa"), ... ) ## S3 method for class 'beezdemand_tmb' plot_re_diagnostics( object, which = c("all", "q0", "alpha"), style = c("modern", "apa"), ... )
object |
A fitted model object with random effects. |
... |
Additional arguments (ignored). |
which |
Character; which random effects to diagnose. One of |
style |
Character; |
A ggplot2/patchwork object, or a list of ggplot2 objects if patchwork is not installed.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_re_diagnostics(fit) plot_re_diagnostics(fit, which = "zeros")data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_re_diagnostics(fit) plot_re_diagnostics(fit, which = "zeros")
Creates diagnostic plots for model residuals including residuals vs fitted, scale-location, and histogram of residuals.
plot_residuals( object, type = c("all", "fitted", "histogram", "qq"), component = c("combined", "continuous"), ... )plot_residuals( object, type = c("all", "fitted", "histogram", "qq"), component = c("combined", "continuous"), ... )
object |
A fitted model object. |
type |
Character; type of residual plot. One of:
|
component |
Character; for hurdle models, which residuals to plot:
|
... |
Additional arguments passed to plotting functions. |
For hurdle models, diagnostic plots default to randomized quantile residuals
that assess both the binary and continuous components simultaneously. Set
component = "continuous" to see only Part II (log-scale) residuals.
A ggplot2 object or list of ggplot2 objects.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_residuals(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_residuals(fit)
Creates a demand curve plot for a single subject with optional observed data and population reference curve.
plot_subject( object, subject_id, prices = NULL, show_data = TRUE, show_population = TRUE, style = c("modern", "apa") )plot_subject( object, subject_id, prices = NULL, show_data = TRUE, show_population = TRUE, style = c("modern", "apa") )
object |
An object of class |
subject_id |
The ID of the subject to plot. |
prices |
Numeric vector of prices for plotting. If |
show_data |
Logical; if |
show_population |
Logical; if |
style |
Plot styling, passed to |
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_subject(fit, subject_id = "19")data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") plot_subject(fit, subject_id = "19")
Central plotting style API for beezdemand, with a modern default based on the codedbx "Refined Contemporary" brand and an APA alternative.
Plot Method for beezdemand_fixed
## S3 method for class 'beezdemand_fixed' plot( x, type = c("demand", "population", "individual", "both"), ids = NULL, style = c("modern", "apa"), show_observed = TRUE, show_pred = NULL, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, free_trans = 0.01, x_limits = NULL, y_limits = NULL, n_points = 200, x_lab = NULL, y_lab = NULL, xlab = NULL, ylab = NULL, facet = NULL, observed_point_alpha = 0.5, observed_point_size = 1.8, pop_line_alpha = 0.9, pop_line_size = 1, ind_line_alpha = 0.35, ind_line_size = 0.7, subtitle = NULL, ... )## S3 method for class 'beezdemand_fixed' plot( x, type = c("demand", "population", "individual", "both"), ids = NULL, style = c("modern", "apa"), show_observed = TRUE, show_pred = NULL, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, free_trans = 0.01, x_limits = NULL, y_limits = NULL, n_points = 200, x_lab = NULL, y_lab = NULL, xlab = NULL, ylab = NULL, facet = NULL, observed_point_alpha = 0.5, observed_point_size = 1.8, pop_line_alpha = 0.9, pop_line_size = 1, ind_line_alpha = 0.35, ind_line_size = 0.7, subtitle = NULL, ... )
x |
A beezdemand_fixed object. |
type |
Plot type: "demand", "population", "individual", or "both". |
ids |
Optional vector of subject IDs to plot. Defaults to all subjects. |
style |
Plot styling, passed to |
show_observed |
Logical; if TRUE, overlay observed data points where possible. |
show_pred |
Which prediction layers to plot: "population", "individual", or "both". |
x_trans |
X-axis transform: "log", "log10", "linear", or "pseudo_log". |
y_trans |
Y-axis transform: "log", "log10", "linear", or "pseudo_log". |
free_trans |
Value used to display free (x = 0) on log scales. Use NULL to drop x <= 0 values instead. |
x_limits |
Optional numeric vector of length 2 for x-axis limits. |
y_limits |
Optional numeric vector of length 2 for y-axis limits. |
n_points |
Number of points to use for prediction curves when thinning. |
x_lab |
Optional x-axis label. |
y_lab |
Optional y-axis label. |
xlab |
Deprecated alias for |
ylab |
Deprecated alias for |
facet |
Faceting specification (TRUE for |
observed_point_alpha |
Alpha for observed points. |
observed_point_size |
Size for observed points. |
pop_line_alpha |
Alpha for population curve. |
pop_line_size |
Line size for population curve. |
ind_line_alpha |
Alpha for individual curves. |
ind_line_size |
Line size for individual curves. |
subtitle |
Optional subtitle for the plot. |
... |
Additional arguments (currently unused). |
A ggplot2 object.
Calls plot() on each per-group child and combines with
patchwork::wrap_plots() if available, otherwise returns a list.
## S3 method for class 'beezdemand_fixed_grouped' plot(x, ...)## S3 method for class 'beezdemand_fixed_grouped' plot(x, ...)
x |
A beezdemand_fixed_grouped object |
... |
Additional arguments passed to |
A combined patchwork plot if patchwork is available, otherwise a named list of ggplot objects
Creates visualizations of fitted demand curves from a hurdle demand model.
## S3 method for class 'beezdemand_hurdle' plot( x, type = c("demand", "population", "probability", "parameters", "individual", "both"), ids = NULL, subjects = NULL, parameters = c("Q0", "alpha", "breakpoint", "Pmax", "Omax"), prices = NULL, show_population = TRUE, show_pred = NULL, show_observed = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, free_trans = 0.01, facet = NULL, x_limits = NULL, y_limits = NULL, x_lab = NULL, y_lab = NULL, xlab = NULL, ylab = NULL, style = c("modern", "apa"), observed_point_alpha = 0.5, observed_point_size = 1.8, pop_line_alpha = 0.9, pop_line_size = 1, ind_line_alpha = 0.35, ind_line_size = 0.7, marginal = TRUE, marginal_method = c("normal", "kde", "empirical"), par_trans = NULL, ... )## S3 method for class 'beezdemand_hurdle' plot( x, type = c("demand", "population", "probability", "parameters", "individual", "both"), ids = NULL, subjects = NULL, parameters = c("Q0", "alpha", "breakpoint", "Pmax", "Omax"), prices = NULL, show_population = TRUE, show_pred = NULL, show_observed = TRUE, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, free_trans = 0.01, facet = NULL, x_limits = NULL, y_limits = NULL, x_lab = NULL, y_lab = NULL, xlab = NULL, ylab = NULL, style = c("modern", "apa"), observed_point_alpha = 0.5, observed_point_size = 1.8, pop_line_alpha = 0.9, pop_line_size = 1, ind_line_alpha = 0.35, ind_line_size = 0.7, marginal = TRUE, marginal_method = c("normal", "kde", "empirical"), par_trans = NULL, ... )
x |
An object of class |
type |
Character string specifying the plot type:
|
ids |
Optional vector of subject IDs to plot (alias of |
subjects |
Character or numeric vector of subject IDs to plot for
|
parameters |
Character vector specifying which parameters to plot when
|
prices |
Numeric vector of prices for plotting. If |
show_population |
Logical; if |
show_pred |
Which prediction layers to plot: "population", "individual", or "both". |
show_observed |
Logical; if |
x_trans |
Character. Transformation for x-axis. Default "log". |
y_trans |
Character. Transformation for y-axis. Default "log". |
free_trans |
Value used to display free (x = 0) on log scales. Use NULL to drop x <= 0 values instead. |
facet |
Faceting specification (TRUE for |
x_limits |
Optional numeric vector of length 2 for x-axis limits. |
y_limits |
Optional numeric vector of length 2 for y-axis limits. |
x_lab |
Optional x-axis label. |
y_lab |
Optional y-axis label. |
xlab |
Deprecated alias for |
ylab |
Deprecated alias for |
style |
Plot styling, passed to |
observed_point_alpha |
Alpha for observed points. |
observed_point_size |
Size for observed points. |
pop_line_alpha |
Alpha for population curve. |
pop_line_size |
Line size for population curve. |
ind_line_alpha |
Alpha for individual curves. |
ind_line_size |
Line size for individual curves. |
marginal |
Logical; if |
marginal_method |
Character. Method for marginal integration when
|
par_trans |
Named list of transformations for parameter distribution
plots (when |
... |
Additional arguments (currently unused). |
A ggplot2 object.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") # Plot mean demand curve plot(fit) # Plot parameter distributions plot(fit, type = "parameters")data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") # Plot mean demand curve plot(fit) # Plot parameter distributions plot(fit, type = "parameters")
Creates a ggplot2 visualization of a fitted beezdemand_nlme model,
showing observed data points and/or model prediction lines.
## S3 method for class 'beezdemand_nlme' plot( x, type = c("demand", "population", "individual", "both"), ids = NULL, show_observed = TRUE, observed_point_alpha = 0.6, show_pred = "population", n_points = 200, inv_fun = identity, facet = NULL, at = NULL, color_by = NULL, linetype_by = NULL, shape_by = NULL, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, free_trans = 0.01, x_limits = NULL, y_limits = NULL, style = c("modern", "apa"), title = NULL, subtitle = NULL, x_lab = NULL, y_lab = NULL, xlab = NULL, ylab = NULL, observed_point_size = 2, pop_line_size = 1, ind_line_size = 0.6, pop_line_alpha = 0.9, ind_line_alpha = 0.3, ... )## S3 method for class 'beezdemand_nlme' plot( x, type = c("demand", "population", "individual", "both"), ids = NULL, show_observed = TRUE, observed_point_alpha = 0.6, show_pred = "population", n_points = 200, inv_fun = identity, facet = NULL, at = NULL, color_by = NULL, linetype_by = NULL, shape_by = NULL, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, free_trans = 0.01, x_limits = NULL, y_limits = NULL, style = c("modern", "apa"), title = NULL, subtitle = NULL, x_lab = NULL, y_lab = NULL, xlab = NULL, ylab = NULL, observed_point_size = 2, pop_line_size = 1, ind_line_size = 0.6, pop_line_alpha = 0.9, ind_line_alpha = 0.3, ... )
x |
A |
type |
Plot type: "demand", "population", "individual", or "both". |
ids |
Optional vector of subject IDs to plot. |
show_observed |
Logical. If TRUE, plots the original data points. Default |
observed_point_alpha |
Alpha for observed points. Default |
show_pred |
Which prediction layers to plot: "population", "individual", or "both". |
n_points |
Integer. Number of points for prediction lines. Default |
inv_fun |
Optional function to inverse-transform y-axis and predictions. Default |
facet |
Optional faceting formula (e.g., |
at |
Optional named list giving values for continuous covariates used in the fixed-effects RHS. When building prediction grids for population- or individual- level lines, these values will be used. If not provided, the function will default to the median of each continuous covariate found in the original model data. Factor variables are always handled as grids (population) or observed combinations (individual) as before. |
color_by |
Optional character string: name of a factor to color lines and/or points by.
Must be a column in |
linetype_by |
Optional character string: name of a factor for linetypes of population prediction lines
if individual lines are also shown (otherwise applies to the shown lines).
Must be a model factor in |
shape_by |
Optional character string: name of a factor for shapes of observed points.
Must be a column in |
x_trans |
Character. Transformation for x-axis. Default "log". |
y_trans |
Character. Transformation for y-axis. Default "log". |
free_trans |
Value used to display free (x = 0) on log scales. Use NULL to drop x <= 0 values instead. |
x_limits |
Optional numeric vector of length 2 for x-axis limits. |
y_limits |
Optional numeric vector of length 2 for y-axis limits. |
style |
Plot styling, passed to |
title |
Optional plot title. |
subtitle |
Optional subtitle for the plot. |
x_lab |
Optional x-axis label. |
y_lab |
Optional y-axis label. |
xlab |
Deprecated alias for |
ylab |
Deprecated alias for |
observed_point_size |
Size for observed points. Default |
pop_line_size |
Size for population prediction lines. Default |
ind_line_size |
Size for individual prediction lines. Default |
pop_line_alpha |
Alpha for population prediction lines. Default |
ind_line_alpha |
Alpha for individual prediction lines. Default |
... |
Additional arguments (currently unused). |
A ggplot2 object.
Plot TMB Mixed-Effects Demand Model
## S3 method for class 'beezdemand_tmb' plot( x, type = c("demand", "individual", "parameters"), ids = NULL, prices = NULL, show_population = TRUE, show_observed = TRUE, show_pred = NULL, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, inv_fun = NULL, x_limits = NULL, y_limits = NULL, x_lab = NULL, y_lab = NULL, style = c("modern", "apa"), observed_point_alpha = 0.3, observed_point_size = 1.5, pop_line_alpha = 1, pop_line_size = 1.2, ind_line_alpha = 0.3, ind_line_size = 0.5, ... )## S3 method for class 'beezdemand_tmb' plot( x, type = c("demand", "individual", "parameters"), ids = NULL, prices = NULL, show_population = TRUE, show_observed = TRUE, show_pred = NULL, x_trans = c("log10", "log", "linear", "pseudo_log"), y_trans = NULL, inv_fun = NULL, x_limits = NULL, y_limits = NULL, x_lab = NULL, y_lab = NULL, style = c("modern", "apa"), observed_point_alpha = 0.3, observed_point_size = 1.5, pop_line_alpha = 1, pop_line_size = 1.2, ind_line_alpha = 0.3, ind_line_size = 0.5, ... )
x |
A |
type |
Character. One of |
ids |
Character vector of subject IDs to plot (for individual type). |
prices |
Optional numeric vector of prices for curve generation. |
show_population |
Logical. Show population curve overlay. |
show_observed |
Logical. Show observed data points. |
show_pred |
Character. Which predictions to show: |
x_trans |
Character. X-axis transformation. |
y_trans |
Character. Y-axis transformation. If |
inv_fun |
Optional function to back-transform y-axis. For |
x_limits, y_limits
|
Numeric length-2 vectors for axis limits. |
x_lab |
Character. X-axis label. |
y_lab |
Character. Y-axis label. |
style |
Character. Plot style: "modern" or "apa". |
observed_point_alpha, observed_point_size
|
Numeric. Aesthetics for observed data points. |
pop_line_alpha, pop_line_size
|
Numeric. Aesthetics for population curve. |
ind_line_alpha, ind_line_size
|
Numeric. Aesthetics for individual curves. |
... |
Additional arguments. |
A ggplot2 object.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) # Population demand curve plot(fit, type = "demand") # Individual curves for selected subjects plot(fit, type = "individual", ids = c("19", "51")) # Parameter distributions plot(fit, type = "parameters")data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) # Population demand curve plot(fit, type = "demand") # Individual curves for selected subjects plot(fit, type = "individual", ids = c("19", "51")) # Parameter distributions plot(fit, type = "parameters")
Creates a ggplot2 visualization of a fitted linear cross-price demand model
(of class cp_model_lm). The plot overlays a prediction line over the
observed data points. Axis transformations (e.g., "log10") can be applied.
If the model includes group effects, separate lines will be drawn for each group.
## S3 method for class 'cp_model_lm' plot( x, data = NULL, inv_fun = identity, n_points = 100, title = NULL, xlab = "Price", ylab = "Consumption", x_trans = "identity", y_trans = "identity", point_size = 3, ... )## S3 method for class 'cp_model_lm' plot( x, data = NULL, inv_fun = identity, n_points = 100, title = NULL, xlab = "Price", ylab = "Consumption", x_trans = "identity", y_trans = "identity", point_size = 3, ... )
x |
A |
data |
Optional data frame containing columns |
inv_fun |
Optional function to inverse-transform predictions. Default is |
n_points |
Number of points to create in the prediction grid. Default is |
title |
Optional title for the plot; default is |
xlab |
Label for the x-axis. Default is |
ylab |
Label for the y-axis. Default is |
x_trans |
Transformation for the x-axis; one of |
y_trans |
Transformation for the y-axis; one of |
point_size |
Size of the data points in the plot. Default is |
... |
Additional arguments passed to the generic |
A ggplot2 object displaying the fitted model predictions and observed data.
Creates a ggplot2 visualization of a fitted mixed-effects cross-price demand model
(of class cp_model_lmer). The pred_type argument selects what is drawn:
## S3 method for class 'cp_model_lmer' plot( x, data = NULL, inv_fun = identity, n_points = 100, title = NULL, xlab = "Price", ylab = "Consumption", x_trans = "identity", y_trans = "identity", point_size = 3, pred_type = c("fixed", "random", "all"), ... )## S3 method for class 'cp_model_lmer' plot( x, data = NULL, inv_fun = identity, n_points = 100, title = NULL, xlab = "Price", ylab = "Consumption", x_trans = "identity", y_trans = "identity", point_size = 3, pred_type = c("fixed", "random", "all"), ... )
x |
A |
data |
Optional data frame containing columns |
inv_fun |
Optional function to inverse-transform predictions. Default is |
n_points |
Number of points to use in creating the prediction grid. Default is |
title |
Optional title for the plot; default is |
xlab |
Label for the x-axis. Default is |
ylab |
Label for the y-axis. Default is |
x_trans |
Transformation for the x-axis; one of |
y_trans |
Transformation for the y-axis; one of |
point_size |
Size of the observed data points. Default is |
pred_type |
Character string specifying which prediction components to plot:
The default is |
... |
Additional arguments passed to |
"fixed"Only the population-level (fixed-effects) prediction.
"random"Only the subject-specific predictions.
"all"Both: the fixed-effects and the subject-specific predictions.
If the model includes group effects, separate lines will be drawn for each group.
A ggplot2 object displaying the observed data points along with the prediction curves.
Plot a Cross-Price Demand Model (Nonlinear)
## S3 method for class 'cp_model_nls' plot( x, data = NULL, inv_fun = identity, n_points = 100, title = NULL, xlab = "Price", ylab = "Consumption", x_trans = "identity", y_trans = "identity", point_size = 3, inverse_fun = deprecated(), ... )## S3 method for class 'cp_model_nls' plot( x, data = NULL, inv_fun = identity, n_points = 100, title = NULL, xlab = "Price", ylab = "Consumption", x_trans = "identity", y_trans = "identity", point_size = 3, inverse_fun = deprecated(), ... )
x |
A cross-price model object from fit_cp_nls with return_all=TRUE. |
data |
Optional data frame with x and y; if NULL, uses object$data. |
inv_fun |
Optional function to inverse-transform predictions. Default is |
n_points |
Number of points used for prediction curve. |
title |
Optional plot title. |
xlab |
X-axis label. |
ylab |
Y-axis label. |
x_trans |
Transformation for x-axis: "identity", "log10", or "pseudo_log". |
y_trans |
Transformation for y-axis: "identity", "log10", or "pseudo_log". |
point_size |
Size of data points. |
inverse_fun |
|
... |
Additional arguments (passed to predict). |
A ggplot2 object.
Creates a single plot object
PlotCurve(adf, dfrow, newdats, yscale = "log", style = c("modern", "apa"))PlotCurve(adf, dfrow, newdats, yscale = "log", style = c("modern", "apa"))
adf |
Data frame (long form) of purchase task data. |
dfrow |
A row of results from FitCurves |
newdats |
A newdat dataframe from FitCurves |
yscale |
Scaling of y axis. Default is "log". Can also take "linear" |
style |
Plot styling, passed to |
Creates individual demand curves
ggplot2 graphical object
Shawn Gilroy [email protected]
## Creates a single plot from elements of an object created by FitCurves if (interactive()) { fc <- FitCurves(apt, "hs", k = 2, detailed = TRUE) PlotCurve(fc$adfs[[1]], fc$dfres[1, ], fc$newdats[[1]]) }## Creates a single plot from elements of an object created by FitCurves if (interactive()) { fc <- FitCurves(apt, "hs", k = 2, detailed = TRUE) PlotCurve(fc$adfs[[1]], fc$dfres[1, ], fc$newdats[[1]]) }
Creates plots
PlotCurves(dat, outdir = NULL, device = "png", ending = NULL, ask = TRUE, ...)PlotCurves(dat, outdir = NULL, device = "png", ending = NULL, ask = TRUE, ...)
dat |
FitCurves object with 4 elements (dfres, newdats, adfs, fits) |
outdir |
Directory where plots are saved |
device |
Type of file. Default is "png". Can be "pdf" |
ending |
Optional. Can specify to only plot through a certain number of datasets |
ask |
Can view plots one by one. If TRUE, plots will not save |
... |
Pass arguments to PlotCurve (for example yscale = c("log", "linear")) |
Creates and saves plots of individual demand curves
Nothing
Brent Kaplan [email protected], Shawn Gilroy [email protected]
## Interactively view plots from output from FitCurves if (interactive()) { fc <- FitCurves(apt, "hs", k = 2, detailed = TRUE) PlotCurves(fc, ask = TRUE) }## Interactively view plots from output from FitCurves if (interactive()) { fc <- FitCurves(apt, "hs", k = 2, detailed = TRUE) PlotCurves(fc, ask = TRUE) }
Estimates statistical power to detect a single fixed-effect difference in a
demand parameter (Q0 or alpha) between two conditions, by simulation:
each replicate (1) simulates a two-condition dataset from the mixed-effects
demand model in .simulate_within_subject_demand() under assumed
population parameters plus the effect delta, under either a
within-subject design (every subject observed in both conditions) or, with
design_type = "between", a two-arm between-subject design (each subject
in one condition); (2) refits it with fit_demand_tmb(); and (3) tests the
condition contrast on the estimation (natural log) scale 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.
power_demand( n_subjects, effect = list(delta_q0 = NULL, delta_alpha = NULL), design = list(), n_sim = 500, alpha = 0.05, df = NULL, seed = NULL, equation = "simplified", random_effects = NULL, multi_start = FALSE, verbose = TRUE, design_type = c("within", "between"), ... )power_demand( n_subjects, effect = list(delta_q0 = NULL, delta_alpha = NULL), design = list(), n_sim = 500, alpha = 0.05, df = NULL, seed = NULL, equation = "simplified", random_effects = NULL, multi_start = FALSE, verbose = TRUE, design_type = c("within", "between"), ... )
n_subjects |
Number of simulated subjects per replicate. For
|
effect |
Named list supplying exactly one of |
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 |
Demand equation passed to |
random_effects |
Random-effects specification passed to
|
multi_start |
Passed to |
verbose |
Logical; show a progress bar. |
design_type |
Either |
... |
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, since power conditional on convergence can be
selected when convergence depends on the realized data.
The v1 scope is a single fixed-effect delta. Joint Q0 + alpha effects,
power for derived measures (Pmax, Omax), and arbitrary designs are out of
scope; see vignette("power-analysis").
The within-subject simulator draws a fresh pair of subject deviations
(log Q0, log alpha) for every subject in every condition. A subject's
deviation in condition 1 is independent of their deviation in condition 2,
and rho_bd (the within-condition Q0/alpha correlation) is pinned at 0.
No subject-level effect is shared across conditions, so the within-subject
power estimate describes two conditions with independent subject effects.
In the usual repeated-measures design each subject's deviation carries over
and the condition contrast is paired; that design is not what is simulated
here. When the carried-over deviations are positively correlated across
conditions (the common case) pairing reduces the contrast variance, so the
power reported here is a conservative benchmark for such a design; a
negative cross-condition correlation would make it optimistic instead. The
default random_effects (per-condition effects) matches the simulated
data-generating process exactly.
For design_type = "between", the two arms are composed by running the
within-subject simulator once per arm with a single condition, then binding
the arms and refitting with per-subject intercept random effects. Because
each subject appears in only one condition, that random-effects structure
matches the composed data-generating process exactly, as the within-subject
default matches its own. In both designs the additive-Gaussian residual
likelihood is a working model for the simulator's multiplicative-lognormal
errors (closest at small sigma_e). Type I error is calibrated by the test
suite for both designs.
An object of class beezdemand_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 - 1 for "within", n_subjects - 2 for
"between", unless overridden).
The validated effect specification (name and delta).
The tested coefficient (e.g. "Q0: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
scale of the simulated delta.
As supplied.
List of equation, design_type, multi_start, and
the deparsed random-effects specification.
The matched call.
find_n_demand() to search for the smallest adequate sample
size; fit_demand_tmb() for the model being refit.
Other power-analysis:
find_n_demand()
# Tiny illustrative run (use n_sim >= 500 for real planning; the # Monte Carlo interval at n_sim = 5 is uninformative by design) res <- power_demand( n_subjects = 12, effect = list(delta_q0 = log(1.5)), n_sim = 5, seed = 1, verbose = FALSE ) print(res) # Between-subject design: group difference in Q0 across two arms res_b <- power_demand( n_subjects = 16, effect = list(delta_q0 = log(1.5)), design_type = "between", n_sim = 5, seed = 1, verbose = FALSE ) print(res_b)# Tiny illustrative run (use n_sim >= 500 for real planning; the # Monte Carlo interval at n_sim = 5 is uninformative by design) res <- power_demand( n_subjects = 12, effect = list(delta_q0 = log(1.5)), n_sim = 5, seed = 1, verbose = FALSE ) print(res) # Between-subject design: group difference in Q0 across two arms res_b <- power_demand( n_subjects = 16, effect = list(delta_q0 = log(1.5)), design_type = "between", n_sim = 5, seed = 1, verbose = FALSE ) print(res_b)
Predict Method for beezdemand_fixed
## S3 method for class 'beezdemand_fixed' predict( object, newdata = NULL, type = c("response", "link"), se.fit = FALSE, interval = c("none", "confidence"), level = 0.95, ... )## S3 method for class 'beezdemand_fixed' predict( object, newdata = NULL, type = c("response", "link"), se.fit = FALSE, interval = c("none", "confidence"), level = 0.95, ... )
object |
A |
newdata |
A data frame containing a price column matching the fitted
object's |
type |
One of |
se.fit |
Logical; if |
interval |
One of |
level |
Confidence level when |
... |
Unused. |
A tibble containing the original newdata columns, plus .fitted
and, when requested, .se.fit and .lower/.upper. If newdata does not
include an id column, predictions are returned for all subjects (cross
product of newdata × subjects) unless k is subject-specific (k = "ind").
Returns predictions from a fitted hurdle demand model.
## S3 method for class 'beezdemand_hurdle' predict( object, newdata = NULL, type = c("demand", "response", "link", "parameters", "probability"), prices = NULL, marginal = FALSE, marginal_method = c("normal", "kde", "empirical"), correction = TRUE, seed = 42L, se.fit = FALSE, interval = c("none", "confidence"), level = 0.95, ... )## S3 method for class 'beezdemand_hurdle' predict( object, newdata = NULL, type = c("demand", "response", "link", "parameters", "probability"), prices = NULL, marginal = FALSE, marginal_method = c("normal", "kde", "empirical"), correction = TRUE, seed = 42L, se.fit = FALSE, interval = c("none", "confidence"), level = 0.95, ... )
object |
An object of class |
newdata |
Optional data frame containing a price column matching the fitted
object's |
type |
One of:
The default changed from |
prices |
Optional numeric vector of prices used only when |
marginal |
Logical; if |
marginal_method |
Character. Method for marginal integration; one of
|
correction |
Logical; if |
seed |
Integer or |
se.fit |
Logical; if |
interval |
One of |
level |
Confidence level when |
... |
Unused. |
The hurdle model specifies Gaussian errors on log-consumption (Part II):
log(Q) ~ N(mu, sigma_e^2). The conditional distribution of Q given
Q > 0 is therefore lognormal. The arithmetic mean of a lognormal is
exp(mu + sigma_e^2/2), not exp(mu). Using exp(mu) returns the
median (geometric mean), which systematically underestimates the
arithmetic mean by a factor of exp(sigma_e^2/2). This correction is
applied by default when type = "response" or type = "demand". Set
correction = FALSE to obtain the median instead.
This is a parametric correction assuming normality of log-scale residuals (Duan, 1983). Under the model's normality assumption, this is equivalent to Duan's nonparametric smearing estimator.
The conditional P(zero) curve (when marginal = FALSE) sets the random
intercept to zero, which produces a near step-function that misrepresents
the observed fraction of zero responses. The marginal curve integrates over
the random effect distribution, answering "what fraction of the population
has stopped buying at this price?"
The "kde" and "empirical" methods integrate over empirical Bayes
estimates (BLUPs) of the random intercepts. They are descriptive rather
than model-consistent: BLUPs are shrunk toward zero compared to the true
random effects (more so for subjects with few observations), so these
methods understate the RE spread, and the marginal curve they produce
summarises the fitted subjects rather than the population-level quantity
the model defines. The "normal" method integrates over the model-assumed
N(0, sigma_a) distribution, which is the model-consistent marginal (the
same one the fitted likelihood integrates over) and is the choice to use
when the marginal curve is reported as an estimate. It can be wrong only
in the way the model itself is wrong, that is, if the normality assumption
fails. The default remains "kde" for continuity with earlier versions.
Use plot_qq() to assess RE normality.
Population-level demand predictions (when no subject ID is provided) can be computed in two ways:
Conditional (default, marginal = FALSE): Sets all random effects to
zero and evaluates the demand function at the fixed-effect (population)
parameters. For nonlinear models, this corresponds to the conditional
mode rather than the population-average mean.
Marginal (marginal = TRUE): Integrates the prediction over the
estimated random-effects distribution via Monte Carlo sampling. This gives
the population-average demand curve. Due to Jensen's inequality, this
curve lies above the conditional curve when the demand function is convex
in the random effects (which it is for exponential demand with log-normal
Q0 and alpha).
The conditional prediction is appropriate for characterizing a "typical" subject. The marginal prediction is appropriate for predicting aggregate consumption in a population.
For type = "parameters", a tibble of subject-level parameters.
For type = "probability" with marginal = TRUE, a tibble with columns
for price, prob_zero, and .fitted (no subject column).
Otherwise, a tibble containing the newdata columns plus .fitted and
helper columns predicted_log_consumption, predicted_consumption,
prob_zero, and expected_consumption. When requested, .se.fit and
.lower/.upper are included. Marginal results carry a
marginal_method attribute; Monte Carlo marginal results
(type = "response" / "demand") also carry a logical
re_cov_fallback attribute that is TRUE when the random-effect draws
had to use an uncorrelated (diagonal) covariance because the estimated
covariance was not positive definite.
A hurdle model has two natural "predicted consumption" quantities and
they answer different questions. type = "response" is the
conditional positive mean : consumption given
that any is purchased. type = "demand" is the marginal expectation
, which weights in the probability of
buying nothing. Observed consumption includes zeros, so predictions
scored against raw data (cross-validation error, calibration, model
comparison on predictions) must use type = "demand"; scoring with
"response" systematically overstates error wherever is
large (typically at high prices). This is why "demand" is the
default as of beezdemand 0.3.0.
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") # Get subject-specific parameters pars <- predict(fit, type = "parameters") # Predict demand at specific prices demand <- predict(fit, type = "demand", prices = c(0, 0.5, 1, 2, 5))data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") # Get subject-specific parameters pars <- predict(fit, type = "parameters") # Predict demand at specific prices demand <- predict(fit, type = "demand", prices = c(0, 0.5, 1, 2, 5))
Generates point predictions from a fitted beezdemand_nlme model.
Predictions can be made at the population level (fixed effects only) or
group/subject level (fixed + random effects). The output scale depends
on the equation_form used during model fitting and whether inv_fun is applied.
## S3 method for class 'beezdemand_nlme' predict( object, newdata = NULL, type = c("response", "link", "population", "individual"), level = 0, inv_fun = identity, se.fit = FALSE, interval = c("none", "confidence"), interval_level = 0.95, ... )## S3 method for class 'beezdemand_nlme' predict( object, newdata = NULL, type = c("response", "link", "population", "individual"), level = 0, inv_fun = identity, se.fit = FALSE, interval = c("none", "confidence"), interval_level = 0.95, ... )
object |
A |
newdata |
Optional data frame for which to make predictions.
Must contain |
type |
One of |
level |
Integer, prediction level for
Default is |
inv_fun |
Optional function to inverse-transform the predictions.
Example: If |
se.fit |
Logical; if |
interval |
One of |
interval_level |
Confidence level when |
... |
Additional arguments passed to |
A tibble containing the original newdata columns plus .fitted.
When requested, .se.fit and .lower/.upper are included (currently NA).
data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") # Population-level predictions preds <- predict(fit, level = 0) # Subject-level predictions preds_subj <- predict(fit, level = 1)data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") # Population-level predictions preds <- predict(fit, level = 0) # Subject-level predictions preds_subj <- predict(fit, level = 1)
Predict from TMB Mixed-Effects Demand Model
## S3 method for class 'beezdemand_tmb' predict( object, newdata = NULL, type = c("response", "parameters", "demand"), level = "subject", prices = NULL, scale = c("model", "natural"), correction = TRUE, at = NULL, ... )## S3 method for class 'beezdemand_tmb' predict( object, newdata = NULL, type = c("response", "parameters", "demand"), level = "subject", prices = NULL, scale = c("model", "natural"), correction = TRUE, at = NULL, ... )
object |
A |
newdata |
Optional data frame. If NULL, predicts for original data. |
type |
Character. One of |
level |
Character, used when |
prices |
Optional numeric vector of prices for population prediction. |
scale |
Character. Output scale for predictions: When |
correction |
Logical. If |
at |
Optional named numeric vector/list (e.g. |
... |
Additional arguments. |
Depends on type:
"response", level = "subject": tibble of newdata plus a
.fitted column (the historical column name, retained for backward
compatibility).
"response", level = "population": tibble of newdata plus a
predict.fixed column.
"response", level = c("population", "subject"): tibble of
newdata plus predict.fixed and predict.id columns, matching the
nlme::predict.lme(level = 0:1) schema so nlme-based plotting code
runs unchanged.
"parameters": tibble of subject-specific parameters.
"demand": tibble with price and .fitted columns.
Population-averaged (marginal) predictions integrating over the
random effects distribution are not yet implemented for this model tier.
The type = "demand" prediction and level = "population" both use
RE = 0 (population fixed effects only). For marginal integration
accounting for Jensen's inequality, use predict.beezdemand_hurdle()
with marginal = TRUE.
predict.beezdemand_nlme() for the nlme-backed equivalent,
which uses the numeric level convention.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) # Fitted values (subject-conditional, the default) head(predict(fit, type = "response")) # Population-mean predictions: no `id` column needed in newdata nd <- data.frame(x = c(0.01, 1, 5, 10)) predict(fit, newdata = nd, level = "population") # Subject-conditional and population side by side in one call nd_id <- data.frame(x = c(0.01, 1, 5, 10), id = unique(apt$id)[1]) predict(fit, newdata = nd_id, level = c("population", "subject")) # Population demand curve at specific prices predict(fit, type = "demand", prices = c(0, 1, 5, 10, 20)) # Subject-level parameters head(predict(fit, type = "parameters"))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) # Fitted values (subject-conditional, the default) head(predict(fit, type = "response")) # Population-mean predictions: no `id` column needed in newdata nd <- data.frame(x = c(0.01, 1, 5, 10)) predict(fit, newdata = nd, level = "population") # Subject-conditional and population side by side in one call nd_id <- data.frame(x = c(0.01, 1, 5, 10), id = unique(apt$id)[1]) predict(fit, newdata = nd_id, level = c("population", "subject")) # Population demand curve at specific prices predict(fit, type = "demand", prices = c(0, 1, 5, 10, 20)) # Subject-level parameters head(predict(fit, type = "parameters"))
Predict method for cp_model_lm objects.
## S3 method for class 'cp_model_lm' predict(object, newdata = NULL, ...)## S3 method for class 'cp_model_lm' predict(object, newdata = NULL, ...)
object |
A cp_model_lm object. |
newdata |
Data frame containing new x values. |
... |
Additional arguments. |
Data frame with predictions.
Generates predictions from a mixed-effects cross-price demand model (of class
cp_model_lmer). The function supports two modes:
## S3 method for class 'cp_model_lmer' predict(object, newdata = NULL, pred_type = c("fixed", "random"), ...)## S3 method for class 'cp_model_lmer' predict(object, newdata = NULL, pred_type = c("fixed", "random"), ...)
object |
A |
newdata |
A data frame containing at least an |
pred_type |
Character string specifying the type of prediction: either |
... |
Additional arguments passed to the underlying |
"fixed"Returns predictions based solely on the fixed-effects component
(using re.form = NA).
"random"Returns subject-specific predictions (fixed plus random effects)
(using re.form = NULL).
A data frame containing all columns of newdata plus a column y_pred
with the corresponding predictions.
data(etm) fit <- fit_cp_linear(etm, type = "mixed") new_prices <- data.frame(x = c(2, 4, 8, 16, 32, 64)) predict(fit, newdata = new_prices, pred_type = "fixed")data(etm) fit <- fit_cp_linear(etm, type = "mixed") new_prices <- data.frame(x = c(2, 4, 8, 16, 32, 64)) predict(fit, newdata = new_prices, pred_type = "fixed")
Predict from a Cross-Price Demand Model (Nonlinear)
## S3 method for class 'cp_model_nls' predict( object, newdata = NULL, inv_fun = identity, inverse_fun = deprecated(), ... )## S3 method for class 'cp_model_nls' predict( object, newdata = NULL, inv_fun = identity, inverse_fun = deprecated(), ... )
object |
A cross-price model object from fit_cp_nls with return_all=TRUE. |
newdata |
A data frame containing an 'x' column. |
inv_fun |
Optional inverse transformation function. Default is |
inverse_fun |
|
... |
Additional arguments. |
A data frame with x values and predicted y values.
Prints a formatted summary of Monte Carlo simulation results.
print_mc_summary(mc_results, digits = 3)print_mc_summary(mc_results, digits = 3)
mc_results |
Output from |
digits |
Number of digits to display. Default is 3. |
Invisibly returns the input mc_results object.
# Tiny run for illustration (use n_sim >= 200 for a real calibration study) mc_results <- run_hurdle_monte_carlo(n_sim = 5, n_subjects = 30, seed = 123, verbose = FALSE) print_mc_summary(mc_results)# Tiny run for illustration (use n_sim >= 200 for a real calibration study) mc_results <- run_hurdle_monte_carlo(n_sim = 5, n_subjects = 30, seed = 123, verbose = FALSE) print_mc_summary(mc_results)
Print Method for ANOVA Comparisons
## S3 method for class 'anova.beezdemand_hurdle' print(x, digits = 4, ...)## S3 method for class 'anova.beezdemand_hurdle' print(x, digits = 4, ...)
x |
An |
digits |
Number of significant digits to print. |
... |
Additional arguments (ignored). |
Invisibly returns the input object x.
Print method for beezdemand_comparison objects
## S3 method for class 'beezdemand_comparison' print(x, digits = 3, ...)## S3 method for class 'beezdemand_comparison' print(x, digits = 3, ...)
x |
A |
digits |
Number of significant digits to display for estimates. |
... |
Additional arguments (unused). |
Invisibly returns the input object x.
Print Method for Diagnostic Plots
## S3 method for class 'beezdemand_diagnostic_plots' print(x, ...)## S3 method for class 'beezdemand_diagnostic_plots' print(x, ...)
x |
A |
... |
Additional arguments (ignored). |
Invisibly returns the input object x.
Print Method for Model Diagnostics
## S3 method for class 'beezdemand_diagnostics' print(x, ...)## S3 method for class 'beezdemand_diagnostics' print(x, ...)
x |
A |
... |
Additional arguments (ignored). |
Invisibly returns the input object x.
Print Method for beezdemand_fixed
## S3 method for class 'beezdemand_fixed' print(x, ...)## S3 method for class 'beezdemand_fixed' print(x, ...)
x |
A beezdemand_fixed object |
... |
Additional arguments (ignored) |
Invisibly returns the input object x.
Print Method for beezdemand_fixed_grouped
## S3 method for class 'beezdemand_fixed_grouped' print(x, ...)## S3 method for class 'beezdemand_fixed_grouped' print(x, ...)
x |
A beezdemand_fixed_grouped object |
... |
Additional arguments (ignored) |
Invisibly returns the input object x.
Print Method for Hurdle Demand Model
## S3 method for class 'beezdemand_hurdle' print(x, ...)## S3 method for class 'beezdemand_hurdle' print(x, ...)
x |
An object of class |
... |
Additional arguments (currently unused). |
Invisibly returns the input object x.
Print Method for Model Comparison
## S3 method for class 'beezdemand_model_comparison' print(x, digits = 4, ...)## S3 method for class 'beezdemand_model_comparison' print(x, digits = 4, ...)
x |
A |
digits |
Number of significant digits to print. |
... |
Additional arguments (ignored). |
Invisibly returns the input object x.
Provides a concise summary of a beezdemand_nlme object, typically
displaying the call, model specifications, and key results from the
nlme fit if successful.
## S3 method for class 'beezdemand_nlme' print(x, digits = max(3L, getOption("digits") - 3L), ...)## S3 method for class 'beezdemand_nlme' print(x, digits = max(3L, getOption("digits") - 3L), ...)
x |
An object of class |
digits |
Minimal number of significant digits, see |
... |
Additional arguments passed to |
Invisibly returns the original object x.
data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") print(fit)data(ko) fit <- fit_demand_mixed(ko, y_var = "y_ll4", x_var = "x", id_var = "monkey", equation_form = "zben") print(fit)
Fallback print method for summary objects inheriting from beezdemand_summary.
Specific summary classes should implement their own print.summary.* methods
for detailed output; this provides a minimal fallback.
## S3 method for class 'beezdemand_summary' print(x, ...)## S3 method for class 'beezdemand_summary' print(x, ...)
x |
A summary object with class including |
... |
Additional arguments (unused). |
Invisibly returns x.
Print Method for beezdemand_systematicity
## S3 method for class 'beezdemand_systematicity' print(x, ...)## S3 method for class 'beezdemand_systematicity' print(x, ...)
x |
A beezdemand_systematicity object |
... |
Additional arguments (ignored) |
Invisibly returns the input object x.
Print Method for TMB Mixed-Effects Demand Model
## S3 method for class 'beezdemand_tmb' print(x, ...)## S3 method for class 'beezdemand_tmb' print(x, ...)
x |
An object of class |
... |
Additional arguments (currently unused). |
Invisibly returns the input object x.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) print(fit)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) print(fit)
Print a Cross-Price Demand Model (Linear)
## S3 method for class 'cp_model_lm' print(x, ...)## S3 method for class 'cp_model_lm' print(x, ...)
x |
A |
... |
Additional arguments (unused). |
Invisibly returns x.
Print a Cross-Price Demand Model (Mixed-Effects)
## S3 method for class 'cp_model_lmer' print(x, ...)## S3 method for class 'cp_model_lmer' print(x, ...)
x |
A |
... |
Additional arguments (unused). |
Invisibly returns x.
Print a Cross-Price Demand Model (Nonlinear)
## S3 method for class 'cp_model_nls' print(x, ...)## S3 method for class 'cp_model_nls' print(x, ...)
x |
A |
... |
Additional arguments (unused). |
Invisibly returns x.
Print method for cp_posthoc objects
## S3 method for class 'cp_posthoc' print(x, ...)## S3 method for class 'cp_posthoc' print(x, ...)
x |
A cp_posthoc object |
... |
Additional arguments passed to print |
Invisibly returns the input object x.
Print Method for summary.beezdemand_fixed
## S3 method for class 'summary.beezdemand_fixed' print(x, digits = 4, n = 20, ...)## S3 method for class 'summary.beezdemand_fixed' print(x, digits = 4, n = 20, ...)
x |
A summary.beezdemand_fixed object |
digits |
Number of significant digits to print |
n |
Number of subjects (ids) to print (default 20) |
... |
Additional arguments (ignored) |
Invisibly returns the input object x.
Print Summary of Hurdle Demand Model
## S3 method for class 'summary.beezdemand_hurdle' print(x, digits = 4, n = Inf, ...)## S3 method for class 'summary.beezdemand_hurdle' print(x, digits = 4, n = Inf, ...)
x |
An object of class |
digits |
Number of significant digits to print. Default is 4. |
n |
Number of rows to print for any tables (unused for this class). |
... |
Additional arguments passed to |
Invisibly returns the input object x.
Print method for summary.beezdemand_nlme
## S3 method for class 'summary.beezdemand_nlme' print(x, digits = 4, n = Inf, ...)## S3 method for class 'summary.beezdemand_nlme' print(x, digits = 4, n = Inf, ...)
x |
A summary.beezdemand_nlme object |
digits |
Number of significant digits to print |
n |
Number of rows to print for any tables (unused for this class). |
... |
Additional arguments (ignored) |
Invisibly returns the input object x.
Print Method for summary.beezdemand_systematicity
## S3 method for class 'summary.beezdemand_systematicity' print(x, n = 20, ...)## S3 method for class 'summary.beezdemand_systematicity' print(x, n = 20, ...)
x |
A summary.beezdemand_systematicity object |
n |
Number of IDs to display (default 20) |
... |
Additional arguments (ignored) |
Invisibly returns the input object x.
Print Method for TMB Model Summary
## S3 method for class 'summary.beezdemand_tmb' print(x, digits = 4, ...)## S3 method for class 'summary.beezdemand_tmb' print(x, digits = 4, ...)
x |
A |
digits |
Number of significant digits. |
... |
Additional arguments. |
Invisibly returns the input object x; called for its
side effect of printing a formatted model summary to the console.
Print method for summary.cp_model_lm objects.
## S3 method for class 'summary.cp_model_lm' print(x, ...)## S3 method for class 'summary.cp_model_lm' print(x, ...)
x |
A |
... |
Unused |
Invisibly returns the input object x.
Print method for summary.cp_model_lmer objects.
## S3 method for class 'summary.cp_model_lmer' print(x, ...)## S3 method for class 'summary.cp_model_lmer' print(x, ...)
x |
A |
... |
Unused |
Invisibly returns the input object x.
Print method for summary.cp_model_nls objects
## S3 method for class 'summary.cp_model_nls' print(x, ...)## S3 method for class 'summary.cp_model_nls' print(x, ...)
x |
A |
... |
Unused |
Invisibly returns the input object x.
Print Method for summary.cp_unsystematic
## S3 method for class 'summary.cp_unsystematic' print(x, ...)## S3 method for class 'summary.cp_unsystematic' print(x, ...)
x |
A |
... |
Additional arguments (ignored). |
Invisibly returns the input object x.
Generates a scales::trans object using the ll4 transformation.
This transformation object can be passed to the trans argument of
ggplot2::scale_x_continuous or ggplot2::scale_y_continuous.
It is intended for non-negative data and is defined at zero.
The "pseudo" aspect is conceptual, similar to pseudo_log_trans in that it
handles a range including zero, but the transformation is ll4.
pseudo_ll4_trans(lambda = 4)pseudo_ll4_trans(lambda = 4)
lambda |
A positive numeric scalar, the lambda parameter for the |
A trans object (from the scales package).
if (require(ggplot2) && require(scales)) { set.seed(123) df <- data.frame( x_vals = c(0, 0.01, 0.1, 1, 10, 100, 1000, NA), # Include 0 and NA y_vals = c(0, 10, 50, 100, 500, 1000, 2000, 50) ) # Using pseudo_ll4_trans for the y-axis ggplot(df, aes(x = x_vals, y = y_vals)) + geom_point() + scale_y_continuous(trans = pseudo_ll4_trans(lambda = 4), name = "Y-Values (Pseudo-LL4 Scale)") + ggtitle("Y-Axis with Pseudo-LL4 Transformation") # Using pseudo_ll4_trans for the x-axis ggplot(df, aes(x = x_vals, y = y_vals)) + geom_point() + scale_x_continuous(trans = pseudo_ll4_trans(lambda = 2), # Different lambda name = "X-Values (Pseudo-LL4 Scale)") + ggtitle("X-Axis with Pseudo-LL4 Transformation") }if (require(ggplot2) && require(scales)) { set.seed(123) df <- data.frame( x_vals = c(0, 0.01, 0.1, 1, 10, 100, 1000, NA), # Include 0 and NA y_vals = c(0, 10, 50, 100, 500, 1000, 2000, 50) ) # Using pseudo_ll4_trans for the y-axis ggplot(df, aes(x = x_vals, y = y_vals)) + geom_point() + scale_y_continuous(trans = pseudo_ll4_trans(lambda = 4), name = "Y-Values (Pseudo-LL4 Scale)") + ggtitle("Y-Axis with Pseudo-LL4 Transformation") # Using pseudo_ll4_trans for the x-axis ggplot(df, aes(x = x_vals, y = y_vals)) + geom_point() + scale_x_continuous(trans = pseudo_ll4_trans(lambda = 2), # Different lambda name = "X-Values (Pseudo-LL4 Scale)") + ggtitle("X-Axis with Pseudo-LL4 Transformation") }
S3 method for ranef for objects of class beezdemand_nlme.
Extracts the random effects from the fitted nlme model.
## S3 method for class 'beezdemand_nlme' ranef(object, ...)## S3 method for class 'beezdemand_nlme' ranef(object, ...)
object |
A |
... |
Additional arguments passed to |
A data frame (or list of data frames if multiple levels of grouping)
of random effects, as returned by ranef.nlme().
coef.beezdemand_nlme, fixef.beezdemand_nlme
Returns subject-level random effect deviations on the natural (log) scale.
These are the Cholesky-transformed deviations, not standardized scores.
To obtain the standardized random effects (u matrix), access
object$tmb_obj directly.
## S3 method for class 'beezdemand_tmb' ranef(object, ...)## S3 method for class 'beezdemand_tmb' ranef(object, ...)
object |
A |
... |
Additional arguments. |
Data frame with subject-level random effects. Columns:
id: subject identifier
b_i, c_i (when present): first-column convenience aliases
for q0_(Intercept) and alpha_(Intercept). Preserved for
backward compatibility with older callers.
q0_<term>: per-block random-effect coefficients for log-Q0,
one column per random-effects design column from the parsed
block structure. For factor-expanded or multi-block fits, these
expose the per-condition slope REs that b_i / c_i alone do
not surface.
alpha_<term>: analogous columns for log-alpha.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(nlme::ranef(fit))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(nlme::ranef(fit))
Extract Random Effects from Mixed-Effects Cross-Price Model
## S3 method for class 'cp_model_lmer' ranef(object, ...)## S3 method for class 'cp_model_lmer' ranef(object, ...)
object |
A cp_model_lmer object |
... |
Additional arguments passed to ranef |
List of random effects
Recodes outliers
RecodeOutliers(df, outval = 3.29, unitshigher = 0)RecodeOutliers(df, outval = 3.29, unitshigher = 0)
df |
A dataframe of consumption values |
outval |
Values greater/less than or equal to this number (specified in standard deviations) will be recoded. Default is 3.29SD as specified by Tabachnick and Fidell (2013) |
unitshigher |
Outliers identified by outval will be coded to a certain number of units higher/lower than the greatest nonoutlier value. Default is 0 units. |
Recodes outliers using Tabachnick and Fidell's (2013) criteria. A variable is standardized and values that are greater/less than or equal to a specified outlier value (specified in standard deviations; default 3.29SD) are recoded to a certain number of units (default 0) higher/lower than the greatest nonoutlier value. Disregards 'NA' values.
Invisibly, a dataframe with original and recoded (if any) values
Brent Kaplan [email protected]
## If any outliers are detected, they would be coded as 1 unit higher emp <- GetEmpirical(apt) RecodeOutliers(emp[, c(2:6)], unitshigher = 1)## If any outliers are detected, they would be coded as 1 unit higher emp <- GetEmpirical(apt) RecodeOutliers(emp[, c(2:6)], unitshigher = 1)
Replaces 0 values
ReplaceZeros(dat, nrepl = 1, replnum = 0.01)ReplaceZeros(dat, nrepl = 1, replnum = 0.01)
dat |
Dataframe (long form) |
nrepl |
Number of zeros to replace with replacement value (replnum). Can accept either a number or "all" if all zeros should be replaced. Default is to replace the first zero only. |
replnum |
Value to replace zeros. Default is .01 |
Replaces specified number of 0s with replacement value.
Dataframe (long form)
Brent Kaplan [email protected]
## Replace all zeros with .01 ReplaceZeros(apt, nrepl = "all", replnum = .01)## Replace all zeros with .01 ReplaceZeros(apt, nrepl = "all", replnum = .01)
Response-scale residuals against the marginal (default) or conditional
fitted values. type = "pearson" returns the Part-II standardized
residual on the model (log-consumption) scale,
(log(y) - mu_i) / sigma_e, where mu_i is the subject-conditional
linear predictor (predict(type = "link")) and sigma_e is
exp(coef[["logsigma_e"]]); observations with y = 0 have no Part-II
residual and are NA. (Before 0.3.0 the raw-scale residual was divided by
the log-scale sigma_e, which changed with the consumption unit.)
## S3 method for class 'beezdemand_hurdle' residuals(object, type = c("response", "pearson"), marginal = TRUE, ...)## S3 method for class 'beezdemand_hurdle' residuals(object, type = c("response", "pearson"), marginal = TRUE, ...)
object |
A |
type |
One of |
marginal |
Passed to |
... |
Unused. |
Numeric vector of length nobs(object).
Default returns response residuals (y_on_scale - fitted) on the model's
native scale. type = "pearson" divides by the residual SD on the model
scale (exp(coef[["logsigma_e"]])). Requesting type = "pearson" with
scale = "natural" falls back to type = "response" with a message
because a response-scale residual SD is not identified for the
exponential/zben variants without a separate variance assumption.
## S3 method for class 'beezdemand_tmb' residuals( object, type = c("response", "pearson"), scale = c("model", "natural"), level = c("subject", "population"), ... )## S3 method for class 'beezdemand_tmb' residuals( object, type = c("response", "pearson"), scale = c("model", "natural"), level = c("subject", "population"), ... )
object |
A |
type |
One of |
scale |
One of |
level |
One of |
... |
Unused. |
Numeric vector of length nobs(object).
fitted.beezdemand_tmb(), augment.beezdemand_tmb().
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(residuals(fit)) head(residuals(fit, type = "pearson"))data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) head(residuals(fit)) head(residuals(fit, type = "pearson"))
Runs a Monte Carlo simulation study to assess model performance, including bias, standard error estimates, and confidence interval coverage.
run_hurdle_monte_carlo( n_sim = 100, n_subjects = 100, true_params = NULL, n_random_effects = 2, prices = seq(0, 11, by = 0.5), stop_at_zero = TRUE, verbose = TRUE, seed = NULL )run_hurdle_monte_carlo( n_sim = 100, n_subjects = 100, true_params = NULL, n_random_effects = 2, prices = seq(0, 11, by = 0.5), stop_at_zero = TRUE, verbose = TRUE, seed = NULL )
n_sim |
Number of simulated datasets. Default is 100. |
n_subjects |
Number of subjects per dataset. Default is 100. |
true_params |
Named list of true parameter values. If NULL, defaults
are used from |
n_random_effects |
Number of random effects (2 or 3). Default is 2. |
prices |
Numeric vector of prices. Default is seq(0, 11, by = 0.5). |
stop_at_zero |
Logical; if TRUE in simulation, subjects stop after first zero. Default is TRUE. |
verbose |
Logical; print progress. Default is TRUE. |
seed |
Random seed for reproducibility. |
A list with:
Data frame of parameter estimates from each converged
simulation (includes non-PD-Hessian replicates, flagged via a
hessian_pd column, for callers that want them)
True parameter values used
Summary statistics including bias, SE ratio, and coverage,
computed only from replicates that converged with a positive-definite
Hessian (diagnostics$status == "clean"); converged-but-non-PD and
converged-but-Hessian-unavailable replicates are excluded
since their SEs are unreliable or unknown
Number of simulations that converged (regardless of Hessian positive-definiteness/availability; unchanged definition)
Total number of simulations attempted
Data frame with one row per simulation: sim_id,
status ("error", "nonconverged", "converged_non_pd",
"converged_hessian_unavailable", or "clean"), converged,
hessian_pd (TRUE/FALSE/NA; NA means sdreport() itself
failed, a different condition from an explicit non-PD Hessian),
opt_convergence, and opt_message
Number of converged replicates excluded from
summary because hessian_pd was explicitly FALSE
Number of converged replicates excluded
from summary because hessian_pd was NA (Hessian PD status
unavailable)
simulate_hurdle_data, fit_demand_hurdle
# Tiny simulation study for demonstration (use n_sim >= 200 in practice) mc_results <- run_hurdle_monte_carlo(n_sim = 5, n_subjects = 30, seed = 123, verbose = FALSE) # View summary print(mc_results$summary) # Check convergence rate cat("Convergence rate:", mc_results$n_converged / mc_results$n_sim, "\n")# Tiny simulation study for demonstration (use n_sim >= 200 in practice) mc_results <- run_hurdle_monte_carlo(n_sim = 5, n_subjects = 30, seed = 123, verbose = FALSE) # View summary print(mc_results$summary) # Check convergence rate cat("Convergence rate:", mc_results$n_converged / mc_results$n_sim, "\n")
beezdemand Color Scale (Discrete)
scale_color_beezdemand(...)scale_color_beezdemand(...)
... |
Additional arguments passed to |
A ggplot2 discrete color scale.
library(ggplot2) ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) + geom_point() + scale_color_beezdemand()library(ggplot2) ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) + geom_point() + scale_color_beezdemand()
beezdemand Fill Scale (Discrete)
scale_fill_beezdemand(...)scale_fill_beezdemand(...)
... |
Additional arguments passed to |
A ggplot2 discrete fill scale.
library(ggplot2) ggplot(iris, aes(Species, Sepal.Length, fill = Species)) + geom_boxplot() + scale_fill_beezdemand()library(ggplot2) ggplot(iris, aes(Species, Sepal.Length, fill = Species)) + geom_boxplot() + scale_fill_beezdemand()
This function generates a ggplot2 continuous scale that applies the ll4
transformation (and its inverse ll4_inv) to an axis. This is useful for
visualizing data spanning multiple orders of magnitude while handling zeros.
scale_ll4(..., lambda = 4)scale_ll4(..., lambda = 4)
... |
Arguments passed on to |
lambda |
A positive numeric scalar, the lambda parameter for the |
A ggplot2 scale object.
if (require(ggplot2) && require(scales)) { set.seed(123) df <- data.frame( x = 1:100, y_raw = c(0, 0.1, 0.5, 1, 5, 10, 50, 100, 500, 1000, sample(1:2000, 90, replace = TRUE)) ) # Plot with y-axis on LL4 scale ggplot(df, aes(x = x, y = y_raw)) + geom_point() + scale_ll4(name = "Y-axis (LL4 Scale)", lambda = 4) + ggtitle("Data with LL4 Transformed Y-Axis") # Can also be used for x-axis by replacing scale_y_continuous in its definition # Or by creating a scale_x_ll4 variant. }if (require(ggplot2) && require(scales)) { set.seed(123) df <- data.frame( x = 1:100, y_raw = c(0, 0.1, 0.5, 1, 5, 10, 50, 100, 500, 1000, sample(1:2000, 90, replace = TRUE)) ) # Plot with y-axis on LL4 scale ggplot(df, aes(x = x, y = y_raw)) + geom_point() + scale_ll4(name = "Y-axis (LL4 Scale)", lambda = 4) + ggtitle("Data with LL4 Transformed Y-Axis") # Can also be used for x-axis by replacing scale_y_continuous in its definition # Or by creating a scale_x_ll4 variant. }
Generates simulated demand data from the two-part hurdle model. Useful for Monte Carlo simulation studies, power analyses, and model validation.
simulate_hurdle_data( n_subjects = 100, prices = seq(0, 11, by = 0.5), beta0 = -2, beta1 = 1, log_q0 = log(10), logQ0 = deprecated(), k = 2, alpha = 0.5, sigma_a = 1, sigma_b = 0.5, sigma_c = 0.1, rho_ab = 0.3, rho_ac = 0, rho_bc = 0, sigma_e = 0.3, epsilon = 0.001, n_random_effects = 2, stop_at_zero = TRUE, seed = NULL, part2 = c("koff", "snd"), rho_ab_raw = NULL, rho_ac_raw = NULL, rho_bc_raw = NULL )simulate_hurdle_data( n_subjects = 100, prices = seq(0, 11, by = 0.5), beta0 = -2, beta1 = 1, log_q0 = log(10), logQ0 = deprecated(), k = 2, alpha = 0.5, sigma_a = 1, sigma_b = 0.5, sigma_c = 0.1, rho_ab = 0.3, rho_ac = 0, rho_bc = 0, sigma_e = 0.3, epsilon = 0.001, n_random_effects = 2, stop_at_zero = TRUE, seed = NULL, part2 = c("koff", "snd"), rho_ab_raw = NULL, rho_ac_raw = NULL, rho_bc_raw = NULL )
n_subjects |
Number of subjects to simulate. Default is 100. |
prices |
Numeric vector of prices at which to simulate consumption.
Default is |
beta0 |
Intercept for Part I (logistic). Default is -2. |
beta1 |
Slope for Part I (on log(price + epsilon)). Default is 1. |
log_q0 |
Log of intensity parameter (Q0). Default is log(10), meaning
Q0 = 10. To specify Q0 directly, use |
logQ0 |
|
k |
Scaling parameter for demand decay. Default is 2. |
alpha |
Elasticity parameter controlling rate of demand decay. Default is 0.5. |
sigma_a |
Standard deviation of random intercept for Part I. Default is 1. |
sigma_b |
Standard deviation of random intercept for Part II. Default is 0.5. |
sigma_c |
Standard deviation of random slope for alpha (only used if
|
rho_ab |
Correlation between a_i and b_i. Default is 0.3. |
rho_ac |
Correlation between a_i and c_i. Default is 0. |
rho_bc |
Correlation between b_i and c_i. Default is 0. |
sigma_e |
Residual standard deviation. Default is 0.3. |
epsilon |
Small constant for log(price + epsilon). Default is 0.001. |
n_random_effects |
Number of random effects (2 or 3). Default is 2. |
stop_at_zero |
Logical; if TRUE, stop generating observations for a subject once zero consumption is observed. This means subjects will have varying numbers of observations. Set to FALSE to generate all prices for all subjects. Default is TRUE. |
seed |
Optional random seed for reproducibility. |
part2 |
Character. Positive-part (Part II) generator: |
rho_ab_raw |
Only used when |
rho_ac_raw |
Only used when |
rho_bc_raw |
Only used when Note: |
Part I (Zero vs Positive), shared by both part2 generators:
Part II, part2 = "koff" (Zhao et al., 2016; mirrors
src/HurdleDemand3RE.h):
Part II, part2 = "snd" (exactly mirrors
src/HurdleDemand3RE_SND.h / src/HurdleDemand2RE_SND.h, i.e. a
log-linear/SND mean with lognormal errors and no k):
with (residual on the log-consumption
scale) in both generators. When n_random_effects = 2, c_i = 0 for
every subject in both generators, so alpha_i reduces to the fixed
population alpha.
Random effects or are drawn from a
multivariate normal distribution with mean zero and covariance built from
sigma_a, sigma_b, sigma_c (SDs, natural scale) and the specified
correlations. For part2 = "koff" (unchanged from previous releases),
rho_ab, rho_ac, rho_bc are the actual (final) correlations, checked
for a jointly positive-definite covariance matrix. For part2 = "snd",
correlations instead mirror the TMB model's own raw-parameter -> actual
correlation mapping exactly (any raw values are guaranteed to give a
valid PD matrix, so no PD check is needed): rho_ab = tanh(rho_ab_raw),
rho_ac = tanh(rho_ac_raw), and rho_bc via the LKJ-Cholesky
partial-correlation transform
This lets a user plug a fitted part2 = "snd" model's own
rho_ab_raw/rho_ac_raw/rho_bc_raw coefficients directly into the
simulator for a parametric bootstrap or recovery study.
A data frame with columns:
Subject identifier
Price value
Simulated consumption (may include zeros)
Indicator for zero consumption (1 = zero, 0 = positive)
Subject-specific random effect for Part I
Subject-specific random effect for Part II
Subject-specific random effect for alpha (if n_random_effects = 3)
fit_demand_hurdle, run_hurdle_monte_carlo
# Simulate with default parameters (2 RE model) sim_data <- simulate_hurdle_data(n_subjects = 100, seed = 123) head(sim_data) # Simulate with custom prices apt_prices <- c(0, 0.25, 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 8, 9, 10) sim_apt <- simulate_hurdle_data(n_subjects = 100, prices = apt_prices, seed = 123) # Simulate with custom parameters (Q0 = 15, alpha = 0.1) sim_custom <- simulate_hurdle_data( n_subjects = 100, log_q0 = log(15), alpha = 0.1, seed = 123 ) # Simulate 3 RE model sim_3re <- simulate_hurdle_data( n_subjects = 100, n_random_effects = 3, sigma_c = 0.1, seed = 456 )# Simulate with default parameters (2 RE model) sim_data <- simulate_hurdle_data(n_subjects = 100, seed = 123) head(sim_data) # Simulate with custom prices apt_prices <- c(0, 0.25, 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 8, 9, 10) sim_apt <- simulate_hurdle_data(n_subjects = 100, prices = apt_prices, seed = 123) # Simulate with custom parameters (Q0 = 15, alpha = 0.1) sim_custom <- simulate_hurdle_data( n_subjects = 100, log_q0 = log(15), alpha = 0.1, seed = 123 ) # Simulate 3 RE model sim_3re <- simulate_hurdle_data( n_subjects = 100, n_random_effects = 3, sigma_c = 0.1, seed = 456 )
Simulate demand data
SimulateDemand(nruns = 10, setparams, sdindex, x, outdir = NULL, fn = NULL)SimulateDemand(nruns = 10, setparams, sdindex, x, outdir = NULL, fn = NULL)
nruns |
Number of runs. Default value is 10 |
setparams |
A 6x1 matrix (or 6 element vector) containing (in order) mean log10alpha, sd log10alpha, mean log10q0, sd log10q0, k, sd of consumption values across all prices |
sdindex |
A vector of n length of sd consumption values for n prices |
x |
A vector of n prices |
outdir |
Optional. Directory to save results. Must end with a "/" |
fn |
Optional. Filename of saved RData object |
Generates and saves simulated datasets in the manner specified in Koffarnus, Franck, Stein, & Bickel (2015).
Invisibly a list consisting of: rounded consumption values, unrounded consumption values, simulation parameters, and inState and outState of seeds.
Brent Kaplan [email protected]
## set values setparams <- vector(length = 4) setparams <- c(-2.5547, .702521, 1.239893, .320221, 3.096, 1.438231) names(setparams) <- c("alphalm", "alphalsd", "q0lm", "q0lsd", "k", "yvalssd") sdindex <- c(2.1978, 1.9243, 1.5804, 1.2465, 0.8104, 0.1751, 0.0380, 0.0270) x <- c(.1, 1, 3, 10, 30, 100, 300, 1000) set.seed(1234) sim <- SimulateDemand(nruns = 1, setparams = setparams, sdindex = sdindex, x = x) sim## set values setparams <- vector(length = 4) setparams <- c(-2.5547, .702521, 1.239893, .320221, 3.096, 1.438231) names(setparams) <- c("alphalm", "alphalsd", "q0lm", "q0lsd", "k", "yvalssd") sdindex <- c(2.1978, 1.9243, 1.5804, 1.2465, 0.8104, 0.1751, 0.0380, 0.0270) x <- c(.1, 1, 3, 10, 30, 100, 300, 1000) set.seed(1234) sim <- SimulateDemand(nruns = 1, setparams = setparams, sdindex = sdindex, x = x) sim
Summary Method for beezdemand_fixed
## S3 method for class 'beezdemand_fixed' summary(object, report_space = c("natural", "log10"), ...)## S3 method for class 'beezdemand_fixed' summary(object, report_space = c("natural", "log10"), ...)
object |
A beezdemand_fixed object |
report_space |
Character. Reporting space for core parameters. One of:
|
... |
Additional arguments (ignored) |
A summary.beezdemand_fixed object (inherits from beezdemand_summary)
Returns a named list of per-group summaries.
## S3 method for class 'beezdemand_fixed_grouped' summary(object, ...)## S3 method for class 'beezdemand_fixed_grouped' summary(object, ...)
object |
A beezdemand_fixed_grouped object |
... |
Additional arguments passed to |
A named list of per-group summary objects
Provides a summary of a fitted hurdle demand model, including fixed effects, variance components, correlations, and fit statistics.
## S3 method for class 'beezdemand_hurdle' summary(object, report_space = c("natural", "log10", "internal"), ...)## S3 method for class 'beezdemand_hurdle' summary(object, report_space = c("natural", "log10", "internal"), ...)
object |
An object of class |
report_space |
Character. Reporting space for core demand parameters. One of:
|
... |
Additional arguments (currently unused). |
An object of class summary.beezdemand_hurdle (also inherits
from beezdemand_summary) containing:
The original function call
"beezdemand_hurdle"
"TMB"
Tibble of fixed effects with estimates, SEs, z-values, p-values
Matrix form for printing, on the internal
(estimation) scale regardless of report_space (legacy compatibility)
Matrix of variance/covariance estimates
Matrix of correlation estimates
Number of subjects
Number of observations
Logical indicating convergence
Log-likelihood at convergence
Akaike Information Criterion
Bayesian Information Criterion
Group-level Pmax and Omax
Summary of individual-level parameters
Character vector of warnings/notes
data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") summary(fit)data(apt) fit <- fit_demand_hurdle(apt, y_var = "y", x_var = "x", id_var = "id") summary(fit)
Returns a structured summary object containing model coefficients, fit statistics, and random effects information.
## S3 method for class 'beezdemand_nlme' summary( object, report_space = c("natural", "log10"), df_method = c("containment", "between"), ... )## S3 method for class 'beezdemand_nlme' summary( object, report_space = c("natural", "log10"), df_method = c("containment", "between"), ... )
object |
A beezdemand_nlme object |
report_space |
Character. Reporting space for core parameters. One of
|
df_method |
Character. Degrees of freedom used for the fixed-effect
t tests. |
... |
Additional arguments (passed to summary.nlme) |
A summary.beezdemand_nlme object (inherits from
beezdemand_summary) with fields including:
call: The original function call
model_class: "beezdemand_nlme"
backend: "nlme"
equation_form: The equation form used ("zben" or "simplified")
coefficients: Tibble of fixed effects with std.error, statistic,
p.value and df (the degrees of freedom the p-value uses; see
df_method)
df_method: The df_method in effect
random_effects: VarCorr output for random effects
logLik, AIC, BIC: Model fit statistics
Pinheiro, J. C., & Bates, D. M. (2000). Mixed-Effects Models in S and S-PLUS. Springer. Section 2.4.2.
Summary Method for beezdemand_systematicity
## S3 method for class 'beezdemand_systematicity' summary(object, ...)## S3 method for class 'beezdemand_systematicity' summary(object, ...)
object |
A beezdemand_systematicity object |
... |
Additional arguments (ignored) |
A summary.beezdemand_systematicity object
Summarize a TMB Mixed-Effects Demand Model Fit
## S3 method for class 'beezdemand_tmb' summary(object, report_space = c("natural", "log10", "internal"), ...)## S3 method for class 'beezdemand_tmb' summary(object, report_space = c("natural", "log10", "internal"), ...)
object |
An object of class |
report_space |
Character. Reporting space for core demand parameters.
One of |
... |
Additional arguments (currently unused). |
An object of class summary.beezdemand_tmb (also inherits
from beezdemand_summary). The variance_components element
reports the Q0 and alpha random-effect SDs on the log10 scale:
the random effects are estimated on the natural-log scale internally and
divided by log(10) for reporting, so they are directly comparable
with nlme::VarCorr() on a fit_demand_mixed() fit using the
default param_space = "log10". The residual SD is reported on the
model's likelihood scale (equation-dependent) and the random-effect
correlations are scale-invariant; neither is rescaled.
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) summary(fit) summary(fit, report_space = "log10")data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) summary(fit) summary(fit, report_space = "log10")
Summary method for cp_model_lm objects.
## S3 method for class 'cp_model_lm' summary(object, ...)## S3 method for class 'cp_model_lm' summary(object, ...)
object |
A cp_model_lm object. |
... |
Additional arguments. |
A list summarizing the linear model.
Summary method for cp_model_lmer objects.
## S3 method for class 'cp_model_lmer' summary(object, ...)## S3 method for class 'cp_model_lmer' summary(object, ...)
object |
A cp_model_lmer object. |
... |
Additional arguments. |
A list summarizing the mixed-effects model.
Summarize a Cross-Price Demand Model (Nonlinear)
## S3 method for class 'cp_model_nls' summary(object, inv_fun = identity, inverse_fun = deprecated(), ...)## S3 method for class 'cp_model_nls' summary(object, inv_fun = identity, inverse_fun = deprecated(), ...)
object |
A cross-price model object from fit_cp_nls with return_all=TRUE. |
inv_fun |
Optional function to inverse-transform predictions (e.g., ll4_inv).
Default is |
inverse_fun |
|
... |
Additional arguments (unused). |
A list containing model summary information.
Summarizes systematic and unsystematic patterns from multiple calls to
check_unsystematic_cp(). This includes overall proportions, trend and bounce
direction counts, and optionally summaries by subject or group.
## S3 method for class 'cp_unsystematic' summary(object, ...)## S3 method for class 'cp_unsystematic' summary(object, ...)
object |
A data frame containing results from multiple |
... |
Additional arguments (currently unused) |
A list of class summary.cp_unsystematic with the following elements:
Number of total patterns examined.
Count of systematic patterns (no bounce).
Count of unsystematic patterns (bounce detected).
Proportion of systematic patterns.
Proportion of unsystematic patterns.
Breakdown of trend directions.
Breakdown of bounce directions.
(Optional) Summary of zero-reversal patterns, if present in input.
(Optional) Summary of zero-return patterns, if present in input.
(Optional) Summary stats by 'group'.
(Optional) Top IDs with unsystematic patterns.
Modern wrappers for systematicity checks with unified output vocabulary.
APA theme for ggplot
theme_apa(plot.box = FALSE)theme_apa(plot.box = FALSE)
plot.box |
Boolean for a box around the plot |
Theme for ggplot graphics that closely align with APA formatting
ggplot theme
Brent Kaplan [email protected]
p <- ggplot2::ggplot(apt, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() p + theme_apa()p <- ggplot2::ggplot(apt, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() p + theme_apa()
beezdemand Plot Theme
theme_beezdemand( style = c("modern", "apa"), base_size = 11, base_family = "sans" )theme_beezdemand( style = c("modern", "apa"), base_size = 11, base_family = "sans" )
style |
Character. One of |
base_size |
Base font size (default: 11). |
base_family |
Base font family (default: "sans"). |
A ggplot2 theme object.
library(ggplot2) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point() + theme_beezdemand()library(ggplot2) ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point() + theme_beezdemand()
Backend-agnostic broom::tidy() method for beezdemand_comparison objects
(returned by get_demand_comparisons() on both the NLME and TMB backends).
This flat long tibble is the cross-backend contract: identical column names
and order regardless of backend. The nested object itself keeps each
backend's native dialect (see get_demand_comparisons()).
## S3 method for class 'beezdemand_comparison' tidy(x, exponentiate = FALSE, ...)## S3 method for class 'beezdemand_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. Estimates and CIs
are on the log10 scale (or ratios when exponentiate = TRUE). statistic
is a t ratio with finite df on the NLME backend and an asymptotic z
(df = Inf) on the TMB backend (the value differs by backend, by design).
data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) res <- get_demand_comparisons(fit, param = c("Q0", "alpha")) tidy(res) tidy(res, exponentiate = TRUE)data(apt_full) # 40 subjects per gender keep the example fast; use the full data in practice ids <- unique(apt_full[c("id", "gender")]) ids <- ids[ids$gender %in% c("Male", "Female"), ] keep <- unlist(lapply(split(ids$id, ids$gender), head, 40)) dat <- apt_full[apt_full$id %in% keep, ] fit <- fit_demand_tmb(dat, equation = "exponential", factors = "gender", verbose = 0) res <- get_demand_comparisons(fit, param = c("Q0", "alpha")) tidy(res) tidy(res, exponentiate = TRUE)
Tidy Method for beezdemand_fixed
## S3 method for class 'beezdemand_fixed' tidy(x, report_space = c("natural", "log10"), ...)## S3 method for class 'beezdemand_fixed' tidy(x, report_space = c("natural", "log10"), ...)
x |
A beezdemand_fixed object |
report_space |
Character. Reporting space for core parameters. One of
|
... |
Additional arguments (ignored) |
A tibble of model coefficients with columns: id, term, estimate, std.error, statistic, p.value, component
Calls tidy() on each per-group child and prepends group columns.
## S3 method for class 'beezdemand_fixed_grouped' tidy(x, ...)## S3 method for class 'beezdemand_fixed_grouped' tidy(x, ...)
x |
A beezdemand_fixed_grouped object |
... |
Additional arguments passed to |
A tibble with group columns prepended
Returns a tibble of model coefficients in tidy format.
## S3 method for class 'beezdemand_hurdle' tidy(x, report_space = c("natural", "log10", "internal"), ...)## S3 method for class 'beezdemand_hurdle' tidy(x, report_space = c("natural", "log10", "internal"), ...)
x |
An object of class |
report_space |
Character. Reporting space for core demand parameters.
One of |
... |
Additional arguments (currently unused). |
A tibble with columns:
Parameter name
Point estimate
Standard error
z-value
P-value
One of "zero_probability", "consumption", "variance", or "fixed"
Tidy method for beezdemand_nlme
## S3 method for class 'beezdemand_nlme' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10"), df_method = c("containment", "between"), ... )## S3 method for class 'beezdemand_nlme' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10"), df_method = c("containment", "between"), ... )
x |
A beezdemand_nlme object |
effects |
Character. Which effects to include: |
report_space |
Character. Reporting space for core parameters. One of
|
df_method |
Character. |
... |
Additional arguments (ignored) |
A tibble of model terms with columns:
term: Parameter name
estimate: Point estimate. For component == "variance" rows this is
a standard deviation (pulled from nlme::VarCorr(model)[, "StdDev"]),
matching tidy.beezdemand_tmb() and the broom.mixed::tidy.lme
convention.
std.error: Standard error (NA for variance components)
statistic: t-value (NA for variance components)
p.value: P-value (NA for variance components)
df: Degrees of freedom the p-value uses (NA for variance
components); see df_method
component: "fixed" or "variance"
estimate_scale: Scale that estimate is reported on
term_display: Display label for term
estimate_internal: Pre-transform estimate; present whenever
effects includes "fixed"
Tidy Method for beezdemand_systematicity
## S3 method for class 'beezdemand_systematicity' tidy(x, ...)## S3 method for class 'beezdemand_systematicity' tidy(x, ...)
x |
A beezdemand_systematicity object |
... |
Additional arguments (ignored) |
The per-subject results tibble
Tidy a beezdemand_tmb Model
## S3 method for class 'beezdemand_tmb' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal"), ... )## S3 method for class 'beezdemand_tmb' tidy( x, effects = c("fixed", "ran_pars"), report_space = c("natural", "log10", "internal"), ... )
x |
A |
effects |
Character. Which effects to return: |
report_space |
Character. Reporting space for the fixed-effect
(core demand parameter) rows. One of |
... |
Additional arguments. |
Variance-component rows (effects = "ran_pars") are exactly the rows of
summary(x)$variance_components: the Q0 and alpha random-effect standard
deviations on the log10 scale and the residual standard deviation on
the model's likelihood scale. They are not the raw internal logsigma
optimizer coefficients and do not respond to report_space; std.error
is NA for them. Random-effect correlations are not tidied here (see
summary(x)$correlations or VarCorr(x) for those). The NLME sibling
tidy.beezdemand_nlme() likewise reports SDs, so backend-agnostic code can
consume the estimate column without dispatch logic on either side.
A tibble of model terms with columns term, estimate,
std.error, statistic, p.value, df (Inf on fixed-effect rows:
the Wald test is an asymptotic z, i.e. a t on infinite df; NA on
variance rows), component, estimate_scale,
and term_display. An estimate_internal column (the pre-transform
estimate) is additionally present whenever effects includes
"fixed". Fixed-effect rows carry component == "fixed" (matching
tidy.beezdemand_nlme() and the nlme/lme4 convention);
variance-component rows carry component == "variance". A
hessian_warning attribute (character scalar, or absent) is attached
depending on x$hessian_pd: absent (no attribute) when hessian_pd
is TRUE or NULL (the field is missing on a legacy fit object, so
there is nothing to report); a message noting the Hessian is not positive definite
when hessian_pd is FALSE; a message noting
positive-definiteness is unknown (because TMB::sdreport() failed
entirely, so SEs/CIs are unavailable, not merely unreliable) when
hessian_pd is NA. This attribute is not printed by an ordinary
tibble print (see summary.beezdemand_tmb() or
check_demand_model() for the surfaced versions of the same
diagnostic).
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) tidy(fit) tidy(fit, effects = "fixed", report_space = "log10") tidy(fit, effects = "ran_pars")data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) tidy(fit) tidy(fit, effects = "fixed", report_space = "log10") tidy(fit, effects = "ran_pars")
Extract coefficients from a linear cross-price model in tidy format
## S3 method for class 'cp_model_lm' tidy(x, ...)## S3 method for class 'cp_model_lm' tidy(x, ...)
x |
A cp_model_lm object. |
... |
Additional arguments (unused). |
A tibble with columns: term, estimate, std.error, statistic, p.value.
Extract coefficients from a mixed-effects cross-price model in tidy format
## S3 method for class 'cp_model_lmer' tidy(x, effects = c("fixed", "ran_vals", "ran_pars", "random"), ...)## S3 method for class 'cp_model_lmer' tidy(x, effects = c("fixed", "ran_vals", "ran_pars", "random"), ...)
x |
A cp_model_lmer object. |
effects |
Which effects to return: |
... |
Additional arguments passed to broom.mixed::tidy. |
A tibble with tidy coefficient information. When the model is
NULL (the fit failed) a zero-row tibble with the same columns as the
corresponding successful call is returned.
Extracts model coefficients from a cross-price demand model into a tidy data frame, following the conventions of the broom package. When model fitting failed, an empty data frame with the expected structure is returned.
## S3 method for class 'cp_model_nls' tidy(x, ...)## S3 method for class 'cp_model_nls' tidy(x, ...)
x |
A model object from fit_cp_nls or fit_cp_linear |
... |
Additional arguments (unused) |
A data frame with one row per coefficient, containing columns:
term |
The name of the model parameter |
estimate |
The estimated coefficient value |
std.error |
The standard error of the coefficient |
statistic |
The t-statistic for the coefficient |
p.value |
The p-value for the coefficient |
data(etm) fit <- fit_cp_nls(etm, equation = "exponentiated") tidy(fit)data(etm) fit <- fit_cp_nls(etm, equation = "exponentiated") tidy(fit)
Re-fits with named arguments substituted into the original call. Pass
any argument of fit_demand_tmb() (e.g., factors = NULL,
random_effects = ~ 1, equation = "simplified"). Does NOT support
formula-update syntax (. - term) because fit_demand_tmb() is
argument-driven, not formula-driven.
## S3 method for class 'beezdemand_tmb' update(object, ..., evaluate = TRUE)## S3 method for class 'beezdemand_tmb' update(object, ..., evaluate = TRUE)
object |
A |
... |
Named arguments to substitute into the original
|
evaluate |
If |
A new beezdemand_tmb object, or an unevaluated call.
fit_demand_tmb(), formula.beezdemand_tmb().
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) update(fit, equation = "simplified", evaluate = FALSE)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) update(fit, equation = "simplified", evaluate = FALSE)
Extracts the random-effect variance components from a beezdemand_tmb
fit in the matrix layout produced by nlme::VarCorr(), so users
familiar with nlme or lme4 can introspect a TMB fit with the
accessor they already know. The reported values are the same ones returned
by summary.beezdemand_tmb: the Q0 and alpha random-effect
standard deviations on the log10 scale and the residual standard
deviation on the model's likelihood scale. This is a presentation shim that
formats already-computed values and recomputes nothing.
## S3 method for class 'beezdemand_tmb' VarCorr(x, sigma = 1, rdig = 3, ...)## S3 method for class 'beezdemand_tmb' VarCorr(x, sigma = 1, rdig = 3, ...)
x |
A |
sigma |
Present for signature compatibility with
|
rdig |
Integer. Number of significant digits used when formatting the
displayed values. Default |
... |
Unused; present for generic compatibility. |
A character matrix of class "VarCorr.lme" with one row per
random-effect term plus a final "Residual" row, columns
"Variance" and "StdDev", and, for fits with correlated
random effects (pdSymm), a "Corr" column. print()
dispatches to nlme's print.VarCorr.lme().
The Corr column is placed using nlme's convention,
with each correlation on the row of its higher-indexed random effect. For
multi-block pdBlocked fits the correlations are placed on the
correct global rows (each correlated block's off-diagonals are offset by
the cumulative random-effect dimension of the earlier blocks);
summary(x)$correlations remains available for the labelled values.
VarCorr, summary.beezdemand_tmb
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) VarCorr(fit)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) VarCorr(fit)
Returns the joint fixed-effect VCOV across Part I (logit-link
participation) and Part II (log-link demand) components, with row/col
names prefixed by component (zero_probability.<term>,
consumption.<term>, variance.<term>). Same component classification
as confint.beezdemand_hurdle().
## S3 method for class 'beezdemand_hurdle' vcov(object, ...)## S3 method for class 'beezdemand_hurdle' vcov(object, ...)
object |
A |
... |
Unused. |
Numeric symmetric matrix with component-prefixed dim names.
coef.beezdemand_hurdle(), confint.beezdemand_hurdle().
Returns the fixed-effect VCOV from the TMB sdreport, i.e., the inverse of
the negative Hessian at the MLE after Laplace-marginalizing the random
effects. Row/column names follow the optimizer's internal parameterization
(matching names(coef(object, type = "internal"))).
## S3 method for class 'beezdemand_tmb' vcov(object, ...)## S3 method for class 'beezdemand_tmb' vcov(object, ...)
object |
A |
... |
Unused. |
Numeric symmetric matrix of dimension p x p.
coef.beezdemand_tmb(), confint.beezdemand_tmb().
data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) V <- vcov(fit) isSymmetric(V)data(apt) fit <- fit_demand_tmb(apt, equation = "exponential", verbose = 0) V <- vcov(fit) isSymmetric(V)