Skip to content

Experimental Design and Inference

0packages depend
335downloads / year
55.8%test coverage
13/13flavors OK

Overview

About

Implements a comprehensive suite of experimental designs, both fixed (e.g., block, stratified, matched-pair, cluster, factorial, and mixed-integer-programming-based optimal designs) and sequential (including matching-on-the-fly designs, biased coin designs, and covariate-adaptive urn designs that assign treatment one subject at a time while maintaining covariate balance), for continuous, incidence, count, proportion, survival, and ordinal response types. For each design and response type combination, provides the corresponding inference procedures, including exact, asymptotic, distribution-free, and resampling-based (bootstrap, jackknife, and randomization) methods, so that estimation and testing are always matched to how the data were generated. An 'InferenceSuite' facility runs all applicable inference procedures for a given design and response type at once and reports a single Cauchy-combined p-value summarizing their evidence. A built-in simulation framework supports power analysis and operating-characteristic studies across designs, response types, and inference procedures, with optional parallelization via 'mirai'. Missing covariate data is handled automatically via built-in imputation. Core numerical routines are implemented in C++ via 'Rcpp' for speed on large designs and simulation studies. Machine-specific tuning for optimization is included.

Changes in 1.0.2

This is the first version submitted to CRAN. Versions 1.0.0 and 1.0.1 were

GitHub-only releases; their entries below record what changed in each. Most of

1.0.2 is correctness work found by the extended comprehensive test suite and

by writing unit tests: resampling and randomization results that were silently

wrong in specific classes, a few kernel defects (including one memory-safety

bug), and standard-error and confidence-interval fixes.

Breaking changes

  • toggle_asserts(), set_num_cores(), and the internal thread-count bookkeeping it drives no longer set global options() as a side effect (CRAN policy: a package must not modify and leave changed the user's session options). toggle_asserts() now stores its flag in the package's own internal state; calling it no longer changes what getOption("edi.run_asserts") returns (a user's own options(edi.run_asserts = ...) is still read and honored). Likewise, set_num_cores()/set_package_threads() no longer set options(mc.cores = ...) (nothing in EDI read it back; downstream code relying on this package to set it for parallel/pbmcapply needs to set it itself now) and no longer call fixest::setFixest_nthreads() (which sets options(fixest_nthreads), the user's own option, not EDI's to change). No change to which thread counts are actually used internally — only to these previously-observable options() side effects.
Show all changes in 1.0.2Hide the rest of the changes in 1.0.2

New features

  • InferenceCountQuasiPoisson now composes the marginal-estimand component (MarginalEstimand), like InferenceCountPoisson, so marginal (standardized) effect estimands are available for the quasi-Poisson class as well.

Bug fixes

  • Reused-worker resampling reused a stale cached fit across draws for classes whose fit is cached under a custom guard key (first found in InferenceOrdinalGCompMeanDiff, whose cached_values$md guard survived the loader's narrow reset list). Every draw then returned the same number, the resampling distribution collapsed to a point mass, and compute_rand_two_sided_pval() sat at its floor regardless of the true effect (reject rate 1.0 at a true null). The worker loaders for randomization, non-parametric bootstrap, m-out-of-n bootstrap and randomization-bootstrap draws now reset the cached fit state between draws, and a structural regression test asserts that every class exposing a reusable-worker method returns a non-degenerate distribution.
  • InferencePropGCompMeanDiff and InferenceSurvivalGLMMWeibullFrailtyLoggammaIVWC returned NA (or a stale value) in randomization inference: the first from a sample-usability gate applied to the wrong resampling context, the second from its estimate-only w_star pooling. Both are fixed and no longer listed as known-broken.
  • The KK survival compound classes InferenceSurvivalKKLWACoxPHOneLik, InferenceSurvivalGLMMWeibullFrailtyLoggammaOneLik and InferenceSurvivalGLMMWeibullFrailtyLoggammaIVWC fed real NAs into the fitter for every censored subject during randomization inference: compute_treatment_estimate_during_randomization_inference() re-read y from the design and derived dead = as.numeric(!is.na(y)), but since the y/y_L/y_R migration the design's y uses NA to mark a censored observation. On simulated data with a strong effect the randomization p-value was 0.02 with no censoring and 0.97 with about 20% censoring. y/dead are now derived as Design$get_effective_time() / $get_effective_dead() do. A sweep of the other 16 survival classes found no further instance.
  • InferenceOrdinalCloglogRegr's parametric-bootstrap p-value was badly over-rejecting (60% at a true null). One cause is fixed: the delta-constrained null refit started from a single cold start and could stall; it is now multi-start (the same fix InferenceOrdinalStereotypeLogitRegr received earlier). *A second cause, a sign mismatch in the shared bootstrap-data simulator, is root-caused but not fixed here.*
  • The Efron biased-coin randomization null draws (generate_permutations) compared weighted arm counts (n_T * prob_T vs n_C * (1 - prob_T)) instead of the raw arm counts DesignSeqOneByOneEfron actually compares, so for any prob_T != 0.5 the randomization null did not follow the design's own assignment rule. It now uses the raw counts.
  • fast_probit_regression's L-BFGS objective held a dangling Eigen::Ref to a temporary, giving non-deterministic fits and NaN negative log-likelihoods. The objective now owns its design matrix.
  • fast_log_binomial_regression's weighted log-likelihood let zero-weight rows constrain the [0, 1] mean support although they are absent from the likelihood; zero-weight rows are now skipped, so the identity- and log-link binomial fits no longer reject valid data because of them.
  • The robust-regression bootstrap kernel accepted any method string and would throw inside an OpenMP region, terminating R; method is now validated ("M" or "MM") before the parallel region.
  • stable_signature() (used to key cached randomization state) hashed a strided sample of the serialized object, which collided on 0/1 integer permutation matrices; it now hashes the full serialization.
  • InferenceCountPoisson's covariance falls back to the Fisher information when the kernel returns no X'WX; InferenceIncidLogRegr gained an estimand-aware standard error and degrees of freedom.
  • Weibull and Cox fits with fixed (held) coefficients now compute the covariance from the free-parameter information block only and expand it back, so the free coefficients' variances are finite and the fixed parameters' rows and columns are NaN by convention; the Cox cluster-robust sandwich does the same.
  • OpenMP's primary thread no longer polls R's interrupt machinery while worker threads are active in the Wilcoxon-Hodges-Lehmann kernels.
  • fast_gaussian_lmm_gls_cpp() (the fixed-variance-component GLS solve used by the randomization and non-studentised bootstrap fast paths of InferenceContinKKGLMM) mixed X'X with a cross-term scaled by an extra 1/sigma_e^2, so it returned the wrong coefficient whenever the residual standard deviation was not exactly 1 (on a paired-plus-singleton fixture with sigma_e = 0.4 the slope was -0.53 instead of the correct 0.63; at sigma_e = 1 the two agreed). It now matches an explicit GLS to machine precision at every sigma_e.
  • The KK21 stepwise weight selection for ordinal responses read a nonexistent field (ssq_b_2) from fast_ordinal_regression_with_var_cpp(); it now reads the returned ssq_b_j.
  • fast_zero_one_inflated_beta_cpp() did not check the length of a supplied warm_start_params (or warm_start_fisher_info, or the row counts of X, X_zero_one and y), so a mis-sized start let the optimizer read and write past its parameter buffer (heap corruption, found under valgrind; it aborted R with free(): invalid next size). It now raises an ordinary error. InferencePropZeroOneInflatedBetaRegr always passed a correctly sized start and was not affected.
  • fast_ordinal_clmm now errors on mismatched X/y/group_id lengths instead of reading out of bounds.
  • InferenceIncidRiskDiff, InferenceCountRobustPoisson, and the zero-inflated/hurdle Poisson classes reported a confident (often zero-width) confidence interval and p-value on a perfectly or near-perfectly fit response (e.g. y fully separated by w, or constant), because the Huber-White/model-based standard error collapses to a numerically-zero-but-positive value there. Found via a raw comprehensive_tests results audit's new adversarial-data/fault-injection sweep. robust_sandwich_variance() and the affected classes' standard-error paths now treat a variance below .Machine$double.eps as non-estimable (NA), not as precision.
  • InferenceCountHurdlePoisson/InferenceCountZeroInflatedPoisson reported a confident estimate and p-value on a hurdle fit that has no MLE by construction (every positive count equal to 1, so the zero-truncated Poisson likelihood only improves as lambda -> 0). Non-estimability is now decided from the data alone (not from what the optimizer/glmmTMB happens to return), so the result is NA on every platform.
  • InferenceRandCustom$compute_rand_confidence_interval()'s fast path ignored the trial delta shift during the randomization search, so the returned interval was in the custom statistic's own scale (e.g. centred on a Welch-t value of ~2-4) rather than the response's scale. The comprehensive_tests harness's own custom-statistic randomization-CI check also fed it a non-translation-equivariant statistic (Welch t), which cannot give a meaningful CI under this or any correct implementation; it now uses a difference-in-means statistic for that check.
  • InferenceAllSimpleWilcox$compute_asymp_confidence_interval(alpha) cached and returned the 95% Wilcoxon interval for every alpha, so a 90% or 99% interval silently came back at the wrong nominal level.
  • InferenceOrdinalAdjCatLogitRegr's parametric-bootstrap confidence interval could be wildly wrong (e.g. [-30, -11] around an estimate of -0.09) because each simulated replicate's treatment coefficient was read from the wrong field of the refit object (fit$b, slopes only) when the anchor fit's index into its own coefficient vector (fit$params, thresholds + slopes) pointed somewhere else entirely on fit$b. Every parametric-bootstrap replicate now reads the same field the anchor fit used.
  • InferenceOrdinalContRatioRegr (and, via the same hardened QR-column-dropping helper, 8 other ordinal threshold-model classes) could silently fit from a garbage cold start and "converge" after one iteration when the design matrix contained columns collinear with the model's *implicit* per-stage intercept (e.g. a stratified design's two complementary factor dummies) — qr() alone, with no intercept column to check against, reported the matrix as full rank. The column-dropping helper now optionally includes that implicit intercept in its rank check.
  • A likelihood-ratio confidence interval whose Newton/bisection inversion converges to within a small tolerance of the point estimate — without landing on it exactly — was reported as a zero-width interval instead of falling back to the Wald interval (or NA), because the "failed inversion" sentinel check required bit-identical bounds. InferenceOrdinalStereotypeLogitRegr's compute_lik_ratio_confidence_interval() hit this on ~6% of one audited sample. Its compute_lik_ratio_bootstrap_confidence_interval() had a related bug: a bootstrap p-value below alpha *at the point estimate itself* (which should be ~1, since the LR statistic there is 0) was silently treated as a legitimate zero-width interval rather than a failed constrained-fit refit; it now reports NA.
  • compute_rand_bootstrap_confidence_interval(type = "smoothed") and compute_rand_bootstrap_two_sided_pval(type = "smoothed") added raw-scale Gaussian kernel noise to count responses, so a resampled zero could become a slightly negative non-integer. Under the CI inversion's multiplicative count shift that became a large negative integer, the Rcpp Poisson-GLMM fit gave up, and the glmmTMB fallback rejected every such draw (GLMM FIT ERROR: negative values not allowed for the 'Poisson' family), corrupting the null distribution and yielding a degenerate conservative bound (e.g. InferenceCountKKGLMM). With use_rcpp = FALSE every draw failed and the p-value was NA. Kernel noise on count responses is now rounded and floored at zero so the resampled draw stays on the non-negative integer support (the same convention the count shift already uses); other response types are unchanged.
  • InferenceCountHurdlePoisson/InferenceCountHurdleNegBin/ InferenceCountZeroInflatedPoisson/InferenceCountZeroInflatedNegBin's standard error could fall through to the generic information-matrix-inverse fallback on a hurdle fit with no MLE by construction (every positive count equal to 1), and whether that generic inversion happened to return a spurious finite value or correctly fail depended on the LAPACK/BLAS backend (confirmed via CI to differ between this environment, where it correctly returned non-estimable, and Windows). The data-driven non-estimability check that already gated the estimate and p-value now also runs before this generic fallback, so the standard error is NA on every platform.
  • SimulationFramework's internal design/inference-combination builder unconditionally disabled package assertions and never restored them; a caller running with turn_off_asserts_for_speed = FALSE (assertions intentionally kept on) had assertions silently disabled for the rest of the R session after this method ran once, which could mask unrelated argument-validation bugs in later, unrelated code in the same session. It now restores whatever assert state was in effect on entry.

From NEWS.md in the release

Install

Health

CRAN checks
13OK
Slowest check: 49.0 min · r-oldrel-windows-x86_64
Status changes
  • OK13 OKNow
  • OK3 OK2026-10-07
Advisories

none known

Documentation

In release 1.0.2
READMEYes · 217 wordsVignettesYes · 11 · dynamicNEWSYes · 100% structured · mentions 1.0.2Citation fileYes · computed when R runs itWebsiteYes
Help pages
371 (190 internal)includes a package overview page
Examples that run
54%86 run in a plain check (1 of them under a condition) · 73 only when donttest examples are enabled (R CMD check --as-cran)
Exported functions with examples
57%
Documented parameters
99%
Return-value docs
65%
Help pages citing references
30%

Downloads

335
CRAN downloads in the past year
Rank #25,285 · ~1/day · ~28/mo
Daily download trend is not available in this view yet.
33530 days
33590 days
3351 year
Compare downloads with other packages →
Also on1 autocran

Repository

1Stars
1Forks
3Open issues
0Open PRs
10Releases
809Commits
3Commit identities
causal-inferenceclinical-trialscppexperimental-designpythonr-packagerandomization-inferencesequential-design
License file reads as GPL-3.0 · 809 commits · Last activity 2026-10-18 · Discussions: 1 post
Responsiveness
How these are sampled
PRs merged (all time)
0%
Median days to close, 50 most recently updated closed issues
21
Median days to close, 50 most recently updated closed PRs
0

Issues over time

2026-10-07 · 42026-10-08 · 3
PracticesBeta

14 development-tooling and community-health practices detected across 6 families in the upstream repository

Checks run against github.com/kapelner/edi on 2026-10-18.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Governance and community (5)
Code of conductContributing guideIssue templatesPR templateSecurity policy
Docs source (2)

GitHub Pages site last deployed 2026-10-18 · The repository has a site deploy workflow

Coverage (1)
Coverage reporting (Codecov)
Show all practicesHide 2 practices
Reproducibility and dev environment (1)
Dev Container
Research and citation (3)
CITATION.cffcodemeta.jsonZenodo
How this is detected·Detection ruleset v3 (2026-09-26)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Claude Codefirst seen 2026-02-03
    Seen in the repository: CLAUDE.md
    Named in .gitignore: .claude, .mcp.json
    Pull request it wrote: opened by a project member
    Commits: at least 71 crediting it in the message
  • Codexfirst seen 2026-04-05
    Named in .gitignore: .codex
  • Aiderfirst seen 2026-05-26
    Named in .gitignore: .aider*
  • Gemini CLIfirst seen 2026-08-05
    Seen in the repository: GEMINI.md
    Named in .gitignore: .gemini
  • GitHub Copilotfirst seen 2026-09-15
    Seen in the repository: .github/copilot-instructions.md, .github/workflows/copilot-setup-steps.yml
  • Cursorfirst seen 2026-09-16
    Seen in the repository: .cursor
Shared conventions any agent may read, so they name no particular tool.
Seen in the repository: AGENTS.md
Models named in its commits
  • Claude Sonnet 541 commits · Jul to Sep 2026
  • Claude Fable 511 commits · Aug to Sep 2026
  • Claude Fable 5.19 commits · Sep 2026
  • Claude Sonnet 4.66 commits · May to Jul 2026
  • Claude Opus 53 commits · Sep 2026

1 more model not listed.

Counts are commits, not lines of code. Commits by a tool's accounts count once across all of its accounts. Commits crediting it are the largest count among its credit lines, a minimum.How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Declared dependencies
51 external dependencies (excludes base and recommended)

Installing EDI adds 23 packages (13 with compiled code, 13 MB of source tarballs). Heaviest: data.table 5.7 MB, Rcpp 1.8 MB, RcppEigen 1.7 MB.

Depends (1)
R >= 3.5.0
Enhances (0)
none
Reverse dependencies
0direct
0indirect

Nothing depends on this yet.

People & History

People (1)
Author, Maintainer since 1.0.2 (2026), Copyright holder
Releases (1)
  • 1.0.2Latest
    2026-10-07 · current release ·
    3 days in CRAN's queue
    Listed in CRAN's incoming queue from 2026-10-03 21:57 to 2026-10-07 06:45 UTC, in 16 readings.
  • R
    R 4.6.0 released · 2026-04-24
Across CRAN, the median accepted release of the past year spent 23 hours in the queue, 90% under 11 days.How queue time is measured

Package metadata

First published
2026-10-07
Total releases
1 / 1 yrs
License
GPL-3 OSI
Minimum R
≥ 3.5.0
Download size
3.4 MB

Cite

Cite this package

Run in R for the authors' preferred citation:

citation("EDI")

The package provides a citation file that builds its entry when R runs it. Run citation() to see it.

The repository also keeps a CITATION.cff.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for EDI version 1.0.2 [Data set]. HJJB, LLC. Data release v2026-10-21. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-21, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy