knitr::opts_chunk$set( collapse = TRUE, comment = "#>", dev = "png", dpi = 144, fig.width = 7, fig.height = 5, warning = FALSE, message = FALSE, cache = TRUE, cache.path = "fixed-demand_cache/", autodep = TRUE ) library(beezdemand) library(dplyr) library(ggplot2) data("apt", package = "beezdemand", envir = environment()) cache_key_object <- function(x) { tmp <- tempfile(fileext = ".rds") saveRDS(x, tmp) on.exit(unlink(tmp), add = TRUE) unname(tools::md5sum(tmp)) } knitr::opts_chunk$set( cache.extra = list( beezdemand_version = as.character(utils::packageVersion("beezdemand")), apt = cache_key_object(apt) ) )
This vignette demonstrates how to fit individual (fixed-effect) demand curves
using fit_demand_fixed(). This function fits separate nonlinear least squares
(NLS) models for each subject, producing per-subject estimates of demand
parameters like $Q_{0}$ (intensity) and $\alpha$ (elasticity).
When to use fixed-effect models: Fixed-effect models are appropriate when
you want independent curve fits for each participant. They make no assumptions
about the distribution of parameters across subjects and are the simplest
approach to demand curve analysis. For hierarchical models that share
information across subjects, see vignette("mixed-demand"). For guidance on
choosing between approaches, see vignette("model-selection").
Data format: All modeling functions expect long-format data with columns
id (subject identifier), x (price), and y (consumption). See
vignette("beezdemand") for details on data preparation and conversion from
wide format.
fit_demand_fixed() supports several equation forms. We demonstrate the three
most common below using the apt (Alcohol Purchase Task) dataset.
The exponential model of demand (Hursh & Silberberg, 2008):
$$\log_{10}(Q) = \log_{10}(Q_0) + k \cdot (e^{-\alpha \cdot Q_0 \cdot x} - 1)$$
fit_hs <- fit_demand_fixed(apt, equation = "hs", k = 2) fit_hs
The exponentiated model (Koffarnus et al., 2015):
$$Q = Q_0 \cdot 10^{k \cdot (e^{-\alpha \cdot Q_0 \cdot x} - 1)}$$
fit_koff <- fit_demand_fixed(apt, equation = "koff", k = 2) fit_koff
The simplified exponential model (Rzeszutek et al., 2025) does not require a scaling constant $k$:
$$Q = Q_0 \cdot e^{-\alpha \cdot Q_0 \cdot x}$$
fit_simplified <- fit_demand_fixed(apt, equation = "simplified") fit_simplified
The scaling constant $k$ controls the range of the demand function. For the
"hs" and "koff" equations, k can be specified in several ways:
| k value | Behavior |
|-----------|----------|
| Numeric (e.g., 2) | Fixed constant for all subjects (default) |
| "ind" | Individual $k$ per subject, computed from each subject's data range |
| "share" | Single shared $k$ estimated across all subjects via global regression |
| "fit" | $k$ is a free parameter estimated jointly with $Q_0$ and $\alpha$ |
## Fixed k (default) fit_demand_fixed(apt, equation = "hs", k = 2) ## Individual k per subject fit_demand_fixed(apt, equation = "hs", k = "ind") ## Shared k across subjects fit_demand_fixed(apt, equation = "hs", k = "share") ## Fitted k as free parameter fit_demand_fixed(apt, equation = "hs", k = "fit")
The param_space argument controls whether optimization is performed on the
natural scale ("natural", the default) or log10 scale ("log10"). The log10
scale can improve convergence for some datasets:
fit_demand_fixed(apt, equation = "hs", k = 2, param_space = "log10")
All beezdemand_fixed objects support the standard tidy(), glance(),
augment(), and confint() methods for programmatic access to results.
tidy(fit_hs)
glance(fit_hs)
augment(fit_hs)
confint(fit_hs)
The summary() method provides a formatted overview including parameter
distributions across subjects:
summary(fit_hs)
When $k$ varies across participants or studies (e.g., k = "ind" or
k = "fit"), raw $\alpha$ values are not directly comparable. The alpha_star
column in tidy() output provides a normalized version (Strategy B; Rzeszutek
et al., 2025) that adjusts for the scaling constant:
$$\alpha^* = \frac{-\alpha}{\ln!\left(1 - \frac{1}{k \cdot \ln(b)}\right)}$$
where $b$ is the logarithmic base (10 for HS/Koff equations). Standard errors
are computed via the delta method. alpha_star requires $k \cdot \ln(b) > 1$;
otherwise NA is returned.
tidy(fit_hs) |> filter(term == "alpha_star") |> select(id, term, estimate, std.error)
The plot() method displays fitted demand curves with observed data points.
The x-axis defaults to a log10 scale:
plot(fit_hs)
Use facet = TRUE to show each subject in a separate panel:
plot(fit_hs, facet = TRUE)
Control the x- and y-axis transformations with x_trans and y_trans:
plot(fit_hs, x_trans = "pseudo_log", y_trans = "pseudo_log")
check_demand_model() summarizes convergence, residual properties, and
potential issues:
check_demand_model(fit_hs)
plot_residuals() produces diagnostic plots. Use $fitted for a
residuals-vs-fitted plot:
plot_residuals(fit_hs)$fitted
predict() with no arguments returns fitted values at the observed prices:
predict(fit_hs)
Supply newdata to predict at specific prices:
new_prices <- data.frame(x = c(0, 0.5, 1, 2, 5, 10, 20)) predict(fit_hs, newdata = new_prices)
Instead of fitting each subject individually, you can fit a single curve to aggregated data.
agg = "Mean" computes the mean consumption at each price across subjects,
then fits a single curve to those means:
fit_mean <- fit_demand_fixed(apt, equation = "hs", k = 2, agg = "Mean") fit_mean
plot(fit_mean)
agg = "Pooled" fits a single curve to all data points pooled together,
retaining error around each observation but ignoring within-subject clustering:
fit_pooled <- fit_demand_fixed(apt, equation = "hs", k = 2, agg = "Pooled") fit_pooled
plot(fit_pooled)
The fit_demand_fixed() interface provides a modern, consistent API for
individual demand curve fitting with full support for the tidy() / glance()
/ augment() workflow. For datasets where borrowing strength across subjects
is desirable, consider the mixed-effects or hurdle model approaches.
vignette("beezdemand") -- Getting started with beezdemandvignette("model-selection") -- Choosing the right model classvignette("group-comparisons") -- Extra sum-of-squares F-test for group comparisonsvignette("mixed-demand") -- Mixed-effects nonlinear demand models (NLME)vignette("mixed-demand-advanced") -- Advanced mixed-effects topicsvignette("hurdle-demand-models") -- Two-part hurdle demand models via TMBvignette("cross-price-models") -- Cross-price demand analysisvignette("migration-guide") -- Migrating from FitCurves() to fit_demand_fixed()Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.