Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 197 additions & 90 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# CLAUDE.md

Project-level instructions for Claude Code working on the `RobinCar2` package.

The full agent brief — package overview, conventions, dependencies, schemas,
pitfalls — lives in [.github/copilot-instructions.md](.github/copilot-instructions.md).
Read it first; the points below are Claude-specific and complement it rather
than replace it.


## Working agreements

- **Default to `devtools` for local checks**: `devtools::test()`,
`devtools::document()`, `devtools::check()`. CI runs `R CMD check --as-cran`
via the `insightsengineering/r.pkg.template` workflows.
- **Don't modify `NAMESPACE` by hand** — it's roxygen-generated. Edit roxygen
tags in `R/*.R`, then `devtools::document()`.
- **Don't touch `README.md` directly** — edit `README.Rmd` and re-render with
`devtools::build_readme()`. Pre-commit's `readme-rmd-rendered` hook enforces this.
- **Snapshot tests**: when an `_snaps/*.md` file changes, eyeball the diff
before `testthat::snapshot_accept()`. Many snapshots capture printed
coefficient matrices that are sensitive to ordering and rounding.
- **Pre-commit is the single source of truth for style** — `air`, `styler`,
`lintr`, spelling, `deps-in-desc`, and `roxygenize` all run there.
- **Don't reintroduce `sp`** — it was renamed to `sr` (simple randomization)
in 0.2.2.
5 changes: 5 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# Generated by roxygen2: do not edit by hand

S3method(confint,mh_effect)
S3method(confint,prediction_cf)
S3method(confint,surv_effect)
S3method(confint,treatment_effect)
S3method(find_data,glm)
S3method(find_data,lm)
S3method(predict_counterfactual,glm)
S3method(predict_counterfactual,lm)
S3method(print,mh_effect)
S3method(print,prediction_cf)
S3method(print,robin_output)
S3method(print,surv_effect)
S3method(print,treatment_effect)
S3method(table,default)
S3method(table,mh_effect)
S3method(table,surv_effect)
S3method(treatment_effect,glm)
S3method(treatment_effect,lm)
Expand All @@ -34,6 +37,7 @@ export(pairwise)
export(predict_counterfactual)
export(robin_glm)
export(robin_lm)
export(robin_mh)
export(robin_surv)
export(surv_control)
export(table)
Expand All @@ -58,6 +62,7 @@ importFrom(stats,pnorm)
importFrom(stats,predict)
importFrom(stats,qnorm)
importFrom(stats,residuals)
importFrom(stats,setNames)
importFrom(stats,terms)
importFrom(stats,var)
importFrom(utils,combn)
Expand Down
21 changes: 21 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@

### New Features

* Added `robin_mh()` for the Mantel-Haenszel risk difference and the
corresponding average treatment effect on the risk-difference scale, with
Greenland-Robins, modified Greenland-Robins, and Sato variance estimators
and the Bannick-Ye additive correction for the ATE. The interface follows
the package's `treatment ~ schema(strata)` grammar; the implementation is
vectorised across joint analysis strata and pairwise treatment comparisons,
so multi-arm trials are supported in a single call. Since stratification is
the only adjustment mechanism, continuous analysis strata are rejected with a
pointer to `robin_glm(family = binomial())`.
* The new `surv_control` argument in `robin_surv()` allows to fine-control the root finding algorithm used for the hazard ratio estimation.

### Bug Fixes

* Previously `robin_surv()` gave small numerical differences to `survival::coxph()` for the hazard ratio estimate. This is now fixed.

### Misc

* Added `robincar-mh` vignette article comparing `robin_mh()` to
`RobinCar::robincar_mh()`, including a section on how to choose the analysis
strata.
* The `intro` vignette now explains how the randomization scheme enters each
analysis function, and why omitting a randomization variable from the analysis
formula is benign for `robin_lm()`/`robin_glm()` but not for
`robin_mh()`/`robin_surv()`.
* The internal Wald coefficient matrix and events table helpers are now shared
between the survival and Mantel-Haenszel result classes.

# RobinCar2 0.2.3

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion R/RobinCar2-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#' @import checkmate
#' @importFrom numDeriv grad
#' @importFrom stats predict residuals fitted model.response model.matrix coefficients family
#' @importFrom stats gaussian terms glm var family pnorm var as.formula qnorm lm confint
#' @importFrom stats gaussian terms glm var family pnorm var as.formula qnorm lm confint setNames
#' @importFrom sandwich vcovHC
#' @importFrom MASS negative.binomial
#' @importFrom utils combn tail
Expand Down
59 changes: 59 additions & 0 deletions R/mh_effect.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#' S3 Methods for `mh_effect`
#'
#' @param x (`mh_effect`) The result of [robin_mh()].
#' @param ... Ignored additional arguments (for compatibility).
#' @name mh_effect_methods
#'
#' @examples
#' x <- robin_mh(
#' y_b ~ s1 + s2,
#' data = subset(glm_data, treatment != "trt2"),
#' treatment = treatment ~ pb(s1, s2)
#' )
NULL

#' @export
#' @describeIn mh_effect_methods prints the `mh_effect` object.
#' @examples
#' print(x)
print.mh_effect <- function(x, ...) {
cat("Model : ", deparse(x$formula), "\n", sep = "")
cat(
"Randomization: ",
deparse(x$randomization),
" (",
randomization_schema$schema[randomization_schema$id == x$schema],
")\n",
sep = ""
)
if (length(x$vars$strata) > 0L) {
cat("Stratification variables: ", toString(x$vars$strata), "\n", sep = "")
}
estimand_label <- switch(x$estimand,
ATE = "Average Treatment Effect (risk difference)",
MH = "Mantel-Haenszel risk difference"
)
ci_label <- switch(x$ci_type,
GR = "Greenland-Robins",
mGR = "Modified Greenland-Robins",
Sato = "Sato"
)
cat(sprintf("\nEstimand : %s\n", estimand_label))
cat(sprintf("Variance : %s\n\n", ci_label))
stats::printCoefmat(x$coef_mat, has.Pvalue = TRUE, ...)
}

#' @export
#' @describeIn mh_effect_methods prints and returns invisibly the events
#' summary table of the `mh_effect` object.
#' @examples
#' table(x)
table.mh_effect <- function(x, ...) {
h_print_events_table(x)
}

#' @export
#' @rdname confint
confint.mh_effect <- function(object, parm, level = 0.95, transform, ...) {
h_confint(object$coef_mat, parm = parm, level = level, transform = transform, ...)
}
Loading
Loading