Cookbook: Proportion Outcome, End to End

One complete, runnable script for a proportion outcome — a response that is itself a fraction in (0, 1) per subject (adherence rate, fraction of tissue affected, score normalized to a 0–1 scale). This is distinct from the incidence cookbook, where each subject contributes a single 0/1. Same four steps as every cookbook (see vignette("cookbook-continuous")); the inference classes are the InferenceProp* family — fractional logit (quasi-binomial), Beta regression, and zero/one-inflated Beta for data with exact 0s and 1s.

Setup

EDI is not on CRAN yet, so install.packages("EDI") fails — install from R-universe (fallback: GitHub, subdir = "R/EDI"). Not evaluated here.

install.packages("EDI", repos = c("https://kapelner.r-universe.dev", "https://cloud.r-project.org"))
# or: remotes::install_github("kapelner/EDI", subdir = "R/EDI")
library(EDI)
set.seed(20260916)

n = 80
X = data.frame(
  severity = round(runif(n, 1, 10), 1),
  prior    = rbinom(n, 1, 0.4)
)
true_logit_shift = 0.7

Fixed design, fractional logit

The response is generated from a Beta distribution whose mean follows a logistic model in treatment and covariates, then kept strictly inside (0, 1) — fractional logit and Beta regression require open-interval data; the zero/one-inflated class handles exact boundary values.

des = DesignFixedBernoulli$new(n = n, response_type = "proportion", verbose = FALSE)
des$add_all_subjects_to_experiment(X)
des$assign_w_to_all_subjects()
w = des$get_w()

mu  = plogis(-0.5 + true_logit_shift * w - 0.1 * X$severity + 0.4 * X$prior)
phi = 15                                  # Beta precision
y   = rbeta(n, mu * phi, (1 - mu) * phi)
y   = pmin(pmax(y, 1e-4), 1 - 1e-4)       # keep strictly inside (0, 1)
des$add_all_subject_responses(y)

inf = InferencePropFractionalLogit$new(des, verbose = FALSE)
inf$num_cores = 1L
inf$compute_estimate()                    # treatment effect on the logit scale
#> [1] 0.692258
inf$compute_asymp_confidence_interval(alpha = 0.05)
#>      2.5%     97.5% 
#> 0.4710137 0.9135023
inf$compute_asymp_two_sided_pval()
#> [1] 8.645963e-10
inf$set_seed(1)
inf$compute_rand_two_sided_pval(r = 200, show_progress = FALSE)
#> [1] 0.01
inf$set_seed(1)
inf$compute_bootstrap_confidence_interval(alpha = 0.05, B = 200, show_progress = FALSE)
#>      2.5%     97.5% 
#> 0.4432881 0.9270299

Beta regression on the same design

inf_beta = InferencePropBetaRegr$new(des, verbose = FALSE)
inf_beta$num_cores = 1L
inf_beta$compute_estimate()
#> [1] 0.7128658
inf_beta$compute_asymp_confidence_interval(alpha = 0.05)
#>      2.5%     97.5% 
#> 0.4939567 0.9317750

Everything at once

suite = InferenceSuite$new(des)
res = suite$run_all_inference(screen = TRUE, plots = FALSE, num_cores = 1L,
                              methods = c("wald", "score", "lik_ratio"), max_secs_per_class = 15)
#> inference       cov      estimand    est       se        pval       pval method     status 
#> class           mod                                                                        
#> ===========================================================================================
#> Classes 0/14  [                0%                 ] Status: Estimating...Avg Δ                    mean Δ      0.145     0.0288    3.61e-06   wald            ok     
#> Classes 1/14  [==             7%                ] Estimated Time Left: 0sAvg Δ Pooled …           mean Δ      0.145     0.0287    2.91e-06   wald            ok     
#> Classes 2/14  [====           14%               ] Estimated Time Left: 0sWilcox                   HL shift    0.143     0.0307    1.45e-05   wald            ok     
#> Classes 3/14  [=======        21%               ] Estimated Time Left: 0sBeta Regr       ~.       logodds m…  0.713     0.112     1.74e-10   wald            ok     
#> Classes 4/14  [=========      28%               ] Estimated Time Left: 0sBeta Regr       ~.       logodds m…  0.713     0.112     2.80e-17   score           ok     
#> Classes 5/14  [===========    35%               ] Estimated Time Left: 0sBeta Regr       ~.       logodds m…  0.713     0.112     8.38e-09   lik_ratio       ok     
#> Classes 6/14  [============== 42%               ] Estimated Time Left: 0sFractional Lo…  ~.       logodds m…  0.692     0.113     8.65e-10   wald            ok     
#> Classes 7/14  [============== 50%               ] Estimated Time Left: 0sFractional Lo…  ~.       logodds m…  0.692     0.113     NA         score           ok     
#> Classes 8/14  [============== 57%               ] Estimated Time Left: 0sFractional Lo…  ~.       logodds m…  0.692     0.113     NA         lik_ratio       ok     
#> Classes 9/14  [============== 64% ==            ] Estimated Time Left: 0sG Comp Avg Δ    ~.       mean Δ      0.158     0.0249    NA         NA              ok     
#> Classes 10/14 [============== 71% ====          ] Estimated Time Left: 0sMedian Regr     ~.       median ef…  0.768     0.158     5.96e-06   wald            ok     
#> Classes 11/14 [============== 78% ======        ] Estimated Time Left: 0sZero One Infl…  ~.       logodds m…  0.715     0.112     1.45e-10   wald            ok     
#> Classes 12/14 [============== 85% =========     ] Estimated Time Left: 0sZero One Infl…  ~.       logodds m…  0.715     0.112     2.84e-17   score           ok     
#> Classes 13/14 [============== 92% ===========   ] Estimated Time Left: 0sZero One Infl…  ~.       logodds m…  0.715     0.112     8.37e-09   lik_ratio       ok     
#> Classes 14/14 [============= 100% ==============] Estimated Time Left: 0s-------------------------------------------------------------------------------------------
#> Status: Completed in 0s.
#> 
#>   Estimand: HL shift (1 inferences)        : p =       NA
#>   Estimand: logodds marginal (7 inferences): p = 0.000100
#>   Estimand: mean Δ (2 inferences)          : p = 0.000100
#>   Estimand: median effect (1 inferences)   : p =       NA
#> 
#> Combined evidence against the sharp null across 4 estimands
#> (11 inferences, weighting = uniform within estimand):
#> p = 0.0001

Sequential design

des_seq = DesignSeqOneByOneKK14$new(n = n, response_type = "proportion", verbose = FALSE)
for (i in seq_len(n)) {
  w_i  = des_seq$add_one_subject_to_experiment_and_assign(X[i, , drop = FALSE])
  mu_i = plogis(-0.5 + true_logit_shift * w_i - 0.1 * X$severity[i] + 0.4 * X$prior[i])
  y_i  = rbeta(1, mu_i * phi, (1 - mu_i) * phi)
  des_seq$add_one_subject_response(i, min(max(y_i, 1e-4), 1 - 1e-4))
}

inf_seq = InferencePropFractionalLogit$new(des_seq, verbose = FALSE)
inf_seq$num_cores = 1L
inf_seq$compute_estimate()
#> [1] 0.7444038
inf_seq$set_seed(1)
inf_seq$compute_rand_two_sided_pval(r = 200, show_progress = FALSE)
#> [1] 0.01

Where to go next