
Binomial Identity Risk Difference Inference for Incidence Responses
Source:R/inference_incidence_binomial_identity.R
InferenceIncidBinomialIdentityRiskDiff.RdFits a binomial regression with the identity link for binary
(incidence) responses: \(P(Y_i = 1) = \beta_0 + \beta_T W_i + X_i^\top
\gamma\), where \(W_i\) is the treatment indicator and \(X_i\) are
optional recorded covariates, by maximum likelihood
(fast_identity_binomial_regression_cpp/
fast_identity_binomial_regression_weighted_cpp). Because the
link is the identity rather than the logit, \(\hat\beta_T\) is directly a
risk difference on the probability scale, not a log-odds-ratio —
the class name and estimand differ from
InferenceIncidLogRegr for exactly
this reason. likelihood_tier = "full": likelihood-ratio, score,
gradient, and Wald tests are all available when the model converges, plus
parametric-likelihood-bootstrap calibration of the likelihood-ratio test.
Because the identity link does not constrain fitted probabilities to
\([0,1]\), fits are hardened by QR column-dropping and rejected as
nonestimable when the fitted linear predictor produces implausible
coefficients (see private$is_identity_binomial_fit_reasonable());
this is a real practical limitation of the identity link relative to
logit/probit, not a bug. Validity requires the additive risk-difference
model to be correctly specified over the covariate range actually observed
(an identity-link fit can be well-behaved in-sample yet imply
out-of-range probabilities for other covariate values).
Estimand. Composes
MarginalEstimand
(set_estimand()/get_estimand()/get_supported_estimands()).
estimand = "marginal_mean_diff" is supported for API consistency
with the other GLM families, but it is algebraically identical to
the default estimand = "conditional" for this class: the
g-computation marginal risk difference is \(\frac{1}{n}\sum_i
\{(\hat\beta_0 + \hat\beta_T + X_i^\top \hat\gamma) - (\hat\beta_0 +
X_i^\top \hat\gamma)\}\), which simplifies to exactly \(\hat\beta_T\) for
every subject (not merely on average) because the identity link is linear
in \(W_i\) with no treatment-by-covariate interaction term — the
per-subject treated-minus-control difference \(\hat\beta_T\) does not
depend on \(X_i\) at all, so standardizing over the covariate
distribution changes nothing. Contrast with
InferenceCountPoisson's
"marginal_ratio" (also a collapsing case, for the same
no-interaction reason) and "marginal_mean_diff" (which does not
collapse, since a difference does not distribute through the nonlinear
log-link mean). This collapsing is a genuine property of the identity-link
model, not a wiring bug — it is documented here so a user comparing
estimands for this class is not surprised the two never differ.
Standard errors are computed independently for each estimand (the
marginal path uses the delta method against the model's coefficient
covariance; the conditional path uses the model information matrix), so
while the two point estimates coincide exactly, their standard errors may
differ slightly by construction even though both are asymptotically valid.
References
McCullagh, P., and Nelder, J. A. (1989). Generalized Linear Models (2nd ed.). Chapman and Hall/CRC, for the binomial GLM family and identity-link risk-difference parameterization.
See also
InferenceIncidLogRegr
(logit link, log-odds-ratio estimand),
InferenceIncidLogBinomial
(log link, log-risk-ratio estimand) for alternative link/estimand
choices on the same response type. Comparable Python API:
statsmodels GLM
(family=Binomial(link=identity())). See also:
Generalized
linear model (Wikipedia).
Super class
Inference -> InferenceIncidBinomialIdentityRiskDiff
Methods
+ inherited public methods from Inference
Inference$capabilities()Inference$compute_exact_confidence_interval()Inference$compute_exact_two_sided_pval_for_treatment_effect()Inference$duplicate()Inference$get_analysis_data()Inference$get_covariates()Inference$get_design_object()Inference$get_model_formula()Inference$get_nonestimable_reason()Inference$get_nonestimable_stage()Inference$get_optimization_alg()Inference$get_response()Inference$get_response_type()Inference$get_treatment()Inference$initialize()Inference$is_nonestimable()Inference$set_optimization_alg()Inference$set_seed()Inference$supports()
InferenceIncidBinomialIdentityRiskDiff$compute_estimate()
Fits the identity-link binomial regression model by
maximum likelihood. Under the default estimand =
"conditional", returns \(\hat\beta_T\), the risk difference
coefficient. Under estimand = "marginal_mean_diff" (set via
set_estimand()), returns the g-computation marginal risk
difference — see the class-level @details for why this is
algebraically identical to the conditional estimate for this
family. The underlying model fit is identical either way (a pure
post-fit transform of the same cached fit, no refit).
InferenceIncidBinomialIdentityRiskDiff$compute_asymp_confidence_interval()
Wald confidence interval, dispatched by
testing_type for the conditional estimand; under a
marginal estimand testing_type is always "wald" (the
only value set_estimand() permits there). Calls
self$compute_estimate() first (not private$shared()
directly) so the estimand-aware cache is always current
regardless of call order.
InferenceIncidBinomialIdentityRiskDiff$compute_asymp_two_sided_pval()
Wald two-sided p-value, dispatched by
testing_type exactly as
compute_asymp_confidence_interval(); see that method's
description for the marginal-estimand always-Wald note.
Examples
# \donttest{
seq_des = DesignSeqOneByOneBernoulli$new(n = 10, response_type = 'incidence')
for (i in 1:10) {
seq_des$add_one_subject_to_experiment_and_assign(data.frame(x1 = rnorm(1)))
}
seq_des$add_all_subject_responses(rbinom(10, 1, 0.5))
inf = InferenceIncidBinomialIdentityRiskDiff$new(seq_des)
inf$compute_estimate()
#> [1] 0.26291
# }