Adds emax_logistic() for fitting binary-outcome Emax
models using iterative reweighted least squares (IRLS), along with
emax_logistic_init() and
emax_logistic_options() for initialisation and
configuration. All standard S3 methods (coef(),
vcov(), confint(), residuals(),
fitted(), predict(), anova(),
logLik(), AIC(), BIC(),
deviance(), simulate()) are supported for the
new emaxlogistic class (#22).
Adds input validation for the binary response variable in
emax_logistic(), with informative errors for non-binary or
out-of-range values (#42).
Adds an erplots model interface so that
emaxnls and emaxlogistic objects work
seamlessly with erplots::er_plot_add_model(),
er_plot_add_summary(),
er_plot_add_quantiles(), and the VPC pipeline. Three S3
methods are registered lazily at load time (no hard dependency on
erplots):
er_predict() returns point predictions and confidence
intervals on a user-supplied exposure grid, with predictions on the
probability scale for emaxlogistic models.er_simulate() returns nsim mean-curve
draws reflecting parameter uncertainty only (no residual noise),
suitable for spaghetti/ribbon plots.er_summary() returns a coefficient table and a
model-level glance row; p_value is always NULL
because Emax models have no single privileged parameter.Covariates present in the model but absent from the exposure grid passed by erplots are filled in automatically with reference values (numeric: column mean; factor/character: first factor level) (#65).
emax_nls(), emax_logistic(),
emax_nls_init(), and emax_logistic_init() now
have covariate_model = NULL as a default. When
covariate_model is omitted, an intercept-only hyperbolic
Emax model is fitted — equivalent to supplying
list(E0 ~ 1, Emax ~ 1, logEC50 ~ 1) explicitly. Sigmoidal
models still require an explicit logHill ~ 1 term in the
covariate model. All existing calls that supply
covariate_model explicitly continue to work without
modification (#69).
emax_converged() now returns a
named logical scalar. The names attribute
holds a short description of the outcome: "converged" on
success, "maximum time exceeded" when the
max_time limit was hit,
"maximum iterations exceeded" when the optimiser ran out of
iterations, or the raw optimiser error message for any other failure.
The value itself is still TRUE/FALSE, so all
existing code that checks if (emax_converged(mod))
continues to work without modification. The SCM history returned by
emax_scm_history() gains a new
convergence_reason column (character) that records this
information for every model tested during the procedure (#62).
emax_scm_forward() and
emax_scm_backward() now accept a criterion
argument ("p-value", "aic", or
"bic"). When criterion = "aic" or
"bic", terms are added or removed based on whether they
strictly improve the information criterion rather than a p-value
threshold, and the threshold argument is ignored. The
default remains "p-value", preserving existing behaviour.
The history returned by emax_scm_history() gains a
criterion column recording which selection rule was applied
in each step (#68).
Adds a max_time argument to
emax_nls_options() and emax_logistic_options()
that sets a maximum elapsed time (in seconds) for model fitting. If the
optimiser has not converged within the limit it is terminated and the
model is treated as non-converged, consistent with any other convergence
failure. Defaults to Inf (no limit). This is particularly
useful when running many models in an SCM procedure, where a single
pathological fit can otherwise stall the entire covariate search
(#16).
Redesigns print() and summary() methods
for emaxnls and emaxlogistic objects:
print() is now a concise model overview showing
structural and covariate formulas, fit statistics (observations,
residual df, sigma or deviance, AIC), and a coefficient table with
estimates, standard errors, and confidence intervals. Hypothesis tests
are not shown by print(); a footer line directs users to
summary(). A conf_level argument controls the
interval level (default 0.95).
summary() gains three new arguments:
suppress_nonsensical = TRUE: suppresses the test
statistic and p-value for logEC50_Intercept by default. The
logEC50 intercept is estimated on the log-concentration scale, so
testing H0: logEC50 = 0 corresponds to testing EC50 = 1 on
the concentration scale — a threshold with no pharmacometric meaning.
The confidence interval for logEC50 is always reported. Pass
suppress_nonsensical = FALSE to restore the raw test.p_adjust = "none": adjusts non-NA p-values via
p.adjust() for multiple-comparison correction; suppressed
p-values are excluded from the adjustment set.simultaneous = FALSE: when TRUE, computes
simultaneous Wald confidence intervals using
mvtnorm::qmvnorm() on the correlation matrix from
vcov(), giving intervals with joint coverage at
conf_level across all parameters simultaneously.summary(back_transform = TRUE) now also sets the
test statistic to NA for back-transformed parameters,
consistent with the standard error already being NA on the
back-transformed scale (#45).
confint() now falls back to Wald intervals with a
warning if profile likelihood computation fails (which can occur for
sigmoidal models) (#45).
simulate() and
confint(simultaneous = TRUE) /
summary(simultaneous = TRUE) now degrade gracefully on
platforms where the mvtnorm shared object is installed but
fails to link at runtime (observed on some clang-based Rhub builders). A
warning is issued and the computation continues using base-R fallbacks:
Cholesky-based multivariate normal sampling for simulate(),
and a Bonferroni-corrected normal quantile for simultaneous confidence
intervals. The fallback intervals are conservative but valid
(#52).
The tibble package is now listed under Suggests
rather than Imports, making it a genuine optional
dependency. All package functionality works with or without tibble
installed (#24).
Fixes AIC() and BIC() when called with
multiple model arguments (#37).
Fixes na.action parameter not being passed through
correctly in emax_nls() and emax_logistic()
(#38).
Fixes predict() omitting residual.scale
from the return value when se.fit = TRUE (#39).
Fixes crashes in emax_logistic_init() and prevents
Inf parameter bounds arising during initialisation
(#40).
Improves error messages when the underlying optimiser fails
during emax_nls() or emax_logistic() fitting,
and tightens argument validation in emax_fun()
(#41).
Fixes validator error messages to reference public API parameter names rather than internal names (#43).
Fixes NaN deviance values for
emaxlogistic models when predicted probabilities are
exactly 0 or 1 (boundary cases) (#44).
confint() now accepts a simultaneous
argument, mirroring summary(). Previously
confint(object, simultaneous = TRUE) silently ignored the
argument (swallowed by ...) and returned pointwise
intervals. Setting simultaneous = TRUE now returns
simultaneous (joint) Wald intervals that match those reported by
summary(object, simultaneous = TRUE) (#46).
Switches package language from en-US to en-GB for consistency with the broader er* package family (ertte, erglm, erplots). All documentation, roxygen comments, vignettes, and code comments now use UK English spelling throughout (#61).
Expands the summary() documentation to explain the
relationship between the p_adjust and
simultaneous arguments. The two are independent tools for
multiplicity — p_adjust corrects the hypothesis-test
p-values, while simultaneous widens the confidence
intervals — and they use different machinery, so their reject/retain
decisions need not agree. The new “Multiplicity: p-value adjustment
versus simultaneous intervals” section spells out what each argument
changes, why the adjusted p-values and the simultaneous intervals may
disagree, and which tool to reach for (#47).
emax_nls_init() is called manually.anova() now warns rather than errors when fewer than
two converged models are supplied.Initial CRAN submission. The package provides tools for fitting and analysing Emax dose-response models via nonlinear least squares.
emax_nls() fits continuous-response Emax models
using NLS, supporting both the hyperbolic
(E0 + Emax * x / (EC50 + x)) and sigmoidal
(E0 + Emax * x^Hill / (EC50^Hill + x^Hill)) model
forms.
emax_nls_options() configures the optimisation
algorithm and control parameters. Three algorithms are supported via the
optim_method argument: "gauss" (Gauss-Newton,
default), "port" (bounded nl2sol), and
"levenberg" (Levenberg-Marquardt via
minpack.lm).
emax_nls_init() generates starting values and
parameter bounds automatically from the data, including support for
categorical covariates. Users can also call it directly to inspect or
override the initialisation before fitting.
Covariates can be added to any structural parameter (E0, Emax,
logEC50, logHill) via a formula interface,
e.g. emax_nls(rsp ~ dose, data = df, E0 = ~ group + age).
emax_add_term() and emax_remove_term()
update a fitted model by adding or removing a single covariate term
without refitting from scratch.
emax_scm_forward() performs forward covariate
selection, adding one term at a time while the likelihood-ratio test
p-value stays below a threshold.
emax_scm_backward() performs backward elimination,
removing terms while the p-value exceeds a threshold.
emax_scm_history() returns the sequence of models
tested during an SCM procedure, including AIC, BIC, and convergence
status for each candidate.
print() displays a brief model summary.
summary() produces a coefficient table with standard
errors, confidence intervals, and hypothesis tests. A
back_transform = TRUE argument re-expresses log-scaled
parameters (logEC50, logHill) on their natural scales (EC50, Hill) in
the output.
coef() extracts the parameter vector; supports
back_transform = TRUE.
vcov() returns the estimated variance-covariance
matrix.
confint() computes profile-likelihood confidence
intervals.
residuals() and fitted() return model
residuals and fitted values.
predict() generates predictions, optionally with
standard errors.
anova() compares nested models by likelihood-ratio
test.
logLik(), AIC(), BIC(),
sigma(), nobs(), and
df.residual() return standard model-fit
statistics.
simulate() draws Monte Carlo replicates by
resampling parameters from the asymptotic normal distribution of the
estimates (requires mvtnorm).
emax_fun() extracts a standalone prediction function
from a fitted model that can be evaluated at arbitrary dose values and
parameter vectors.
emax_converged() returns TRUE or
FALSE indicating whether the optimiser converged. All S3
methods handle non-convergent models gracefully by returning an
emaxnls_null object rather than erroring.
emax_df: a synthetic dataset of 400 observations across
four dose groups (0, 100, 200, 300), with a continuous response, a
binary response, two exposure metrics, continuous and binary covariates,
and a categorical covariate.