Skip to content

Commit 2557c66

Browse files
edelaruaddsjoberg
andauthored
Add ard_stats_mantelhaen_test() (#246)
**What changes are proposed in this pull request?** * Added function `ard_stats_mantelhaen_test()` for calculating ARDs for Cochran-Mantel-Haenszel test results using `stats::mantelhaen.test()`. (#238) Closes #238 -------------------------------------------------------------------------------- Pre-review Checklist (if item does not apply, mark is as complete) - [ ] **All** GitHub Action workflows pass with a ✅ - [ ] PR branch has pulled the most recent updates from master branch: `usethis::pr_merge_main()` - [ ] If a bug was fixed, a unit test was added. - [ ] If a new `ard_*()` function was added, it passes the ARD structural checks from `cards::check_ard_structure()`. - [ ] If a new `ard_*()` function was added, `set_cli_abort_call()` has been set. - [ ] If a new `ard_*()` function was added and it depends on another package (such as, `broom`), `is_pkg_installed("broom")` has been set in the function call and the following added to the roxygen comments: `@examplesIf do.call(asNamespace("cardx")$is_pkg_installed, list(pkg = "broom""))` - [ ] Code coverage is suitable for any new functions/features (generally, 100% coverage for new code): `devtools::test_coverage()` Reviewer Checklist (if item does not apply, mark is as complete) - [x] If a bug was fixed, a unit test was added. - [x] Code coverage is suitable for any new functions/features: `devtools::test_coverage()` When the branch is ready to be merged: - [ ] Update `NEWS.md` with the changes from this pull request under the heading "`# cardx (development version)`". If there is an issue associated with the pull request, reference it in parentheses at the end update (see `NEWS.md` for examples). - [ ] **All** GitHub Action workflows pass with a ✅ - [ ] Approve Pull Request - [ ] Merge the PR. Please use "Squash and merge" or "Rebase and merge". --------- Signed-off-by: Emily de la Rua <emily.de_la_rua@contractors.roche.com> Co-authored-by: Daniel Sjoberg <danield.sjoberg@gmail.com>
1 parent 292c9b3 commit 2557c66

File tree

8 files changed

+230
-0
lines changed

8 files changed

+230
-0
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export(ard_stats_aov)
4747
export(ard_stats_chisq_test)
4848
export(ard_stats_fisher_test)
4949
export(ard_stats_kruskal_test)
50+
export(ard_stats_mantelhaen_test)
5051
export(ard_stats_mcnemar_test)
5152
export(ard_stats_mcnemar_test_long)
5253
export(ard_stats_mood_test)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
* Added `cards::as_cards_fun()` to `ard_emmeans_mean_difference()` so when an error occurs the user gets an ARD with the expected ARD structure. (#132)
1212

13+
* Added function `ard_stats_mantelhaen_test()` to calculate ARDs for Cochran-Mantel-Haenszel test results using `stats::mantelhaen.test()`. (#238)
14+
1315
# cardx 0.2.3
1416

1517
## New Features and Updates
@@ -42,6 +44,8 @@
4244

4345
* Methods in the {survey} and {survival} packages do not retain inputs variables types in their outputs. We now are able retain these variable types in ARDs returned by `ard_continuous.survey.design()`, `ard_categorical.survey.design()`, `ard_continuous_ci.survey.design()`, `ard_categorical_ci.survey.design()`, and `ard_survival_survfit.data.frame()` (and notably, _not_ in `ard_survival_survfit.survfit()`).
4446

47+
* Added function `ard_stats_mantelhaen_test()` for calculating ARDs for Cochran-Mantel-Haenszel test results using `stats::mantelhaen.test()`. (#238)
48+
4549
# cardx 0.2.1
4650

4751
## New Features and Updates

R/ard_stats_mantelhaen_test.R

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#' ARD Cochran-Mantel-Haenszel Chi-Squared Test
2+
#'
3+
#' @description
4+
#' Analysis results data for Cochran-Mantel-Haenszel Chi-Squared Test for count data.
5+
#' Calculated with `mantelhaen.test(x = data[[variables]], y = data[[by]], z = data[[strata]], ...)`.
6+
#'
7+
#' @param data (`data.frame`)\cr
8+
#' a data frame.
9+
#' @param by ([`tidy-select`][dplyr::dplyr_tidy_select])\cr
10+
#' column name to compare by.
11+
#' @param variables ([`tidy-select`][dplyr::dplyr_tidy_select])\cr
12+
#' column names to be compared. Independent tests will be computed for each variable.
13+
#' @param strata ([`tidy-select`][dplyr::dplyr_tidy_select])\cr
14+
#' column name to stratify by.
15+
#' @param ... additional arguments passed to `stats::mantelhaen.test(...)`
16+
#'
17+
#' @return ARD data frame
18+
#' @name ard_stats_mantelhaen_test
19+
#' @export
20+
#'
21+
#' @examplesIf do.call(asNamespace("cardx")$is_pkg_installed, list(pkg = "broom"))
22+
#' cards::ADSL |>
23+
#' ard_stats_mantelhaen_test(by = "ARM", variables = "AGEGR1", strata = "SEX")
24+
ard_stats_mantelhaen_test <- function(data, by, variables, strata, ...) {
25+
set_cli_abort_call()
26+
27+
# check installed packages ---------------------------------------------------
28+
check_pkg_installed("broom")
29+
30+
# check/process inputs -------------------------------------------------------
31+
check_not_missing(data)
32+
check_not_missing(variables)
33+
check_not_missing(by)
34+
check_not_missing(strata)
35+
cards::process_selectors(data, by = {{ by }}, variables = {{ variables }}, strata = {{ strata }})
36+
check_class(variables, "character")
37+
check_scalar(by)
38+
check_scalar(strata)
39+
check_class(data[[variables]], c("character", "factor"))
40+
check_class(data[[by]], c("character", "factor"))
41+
check_class(data[[strata]], c("character", "factor"))
42+
43+
# return empty ARD if no variables selected ----------------------------------
44+
if (is_empty(variables)) {
45+
return(dplyr::tibble() |> cards::as_card())
46+
}
47+
48+
dots <- dots_list(...)
49+
formals_cmh <- formals(asNamespace("stats")[["mantelhaen.test"]])[-c(1:3)]
50+
if (!"alternative" %in% names(dots)) formals_cmh$alternative <- "two.sided"
51+
mantelhaen.args <- c(dots, formals_cmh[setdiff(names(formals_cmh), names(dots))])
52+
53+
# build ARD ------------------------------------------------------------------
54+
cards::ard_complex(
55+
data = data,
56+
variables = all_of(variables),
57+
statistic = all_of(variables) ~ list(
58+
stats_mantelhaen_test = .calc_mantelhaen_test(data, by, variables, strata, mantelhaen.args)
59+
)
60+
) |>
61+
dplyr::select(-"stat_label") |>
62+
dplyr::left_join(
63+
.df_mantelhaentest_stat_labels(exact = mantelhaen.args$exact),
64+
by = "stat_name"
65+
) |>
66+
dplyr::mutate(
67+
group1 = by,
68+
group2 = strata,
69+
stat_label = dplyr::coalesce(.data$stat_label, .data$stat_name),
70+
context = "stats_mantelhaen_test",
71+
) |>
72+
cards::as_card() |>
73+
cards::tidy_ard_column_order() |>
74+
cards::tidy_ard_row_order()
75+
}
76+
77+
.calc_mantelhaen_test <- function(data, by, variables, strata, mantelhaen.args) {
78+
cards::as_cards_fn(
79+
\(x, data, variables, ...) {
80+
stats::mantelhaen.test(
81+
x = x,
82+
y = data[[by]],
83+
z = data[[strata]],
84+
mantelhaen.args
85+
) |>
86+
broom::tidy() |>
87+
dplyr::bind_cols(mantelhaen.args)
88+
},
89+
stat_names = c(
90+
"estimate", "statistic", "p.value", "parameter", "correct", "exact", "conf.level", "conf.low", "conf.high"
91+
)
92+
)
93+
}
94+
95+
.df_mantelhaentest_stat_labels <- function(exact = FALSE) {
96+
dplyr::tribble(
97+
~stat_name, ~stat_label,
98+
"estimate", ifelse(exact, "Mantel-Haenszel Odds Ratio Estimate", "Conditional Maximum Likelihood Odds Ratio Estimate"),
99+
"statistic", ifelse(exact, "Mantel-Haenszel X-squared Statistic", "Generalized Cochran-Mantel-Haenszel Statistic"),
100+
"p.value", "p-value",
101+
"parameter", "Degrees of Freedom",
102+
"correct", "Continuity Correction",
103+
"exact", "Exact Conditional Test",
104+
"conf.level", "CI Confidence Level",
105+
"conf.low", "CI Lower Bound",
106+
"conf.high", "CI Upper Bound"
107+
)
108+
}

_pkgdown.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ reference:
3030
- ard_stats_chisq_test
3131
- ard_stats_fisher_test
3232
- ard_stats_kruskal_test
33+
- ard_stats_mantelhaen_test
3334
- ard_stats_mood_test
3435
- ard_stats_mcnemar_test
3536
- ard_stats_oneway_test

inst/WORDLIST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CMD
55
Chisq
66
Clopper
77
Codecov
8+
Haenszel
89
Hoffmann
910
Jeffreys
1011
Kaplan

man/ard_stats_mantelhaen_test.Rd

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# ard_stats_mantelhaen_test() works
2+
3+
Code
4+
print(ard_mantelhaentest, columns = "all")
5+
Message
6+
{cards} data frame: 8 x 10
7+
Output
8+
group1 group2 variable context stat_name stat_label stat fmt_fn warning error
9+
1 ARM SEX AGEGR1 stats_ma… statistic Generali… 6.455 1
10+
2 ARM SEX AGEGR1 stats_ma… p.value p-value 0.168 1
11+
3 ARM SEX AGEGR1 stats_ma… parameter Degrees … 4 1
12+
4 ARM SEX AGEGR1 stats_ma… method method Cochran-… <fn>
13+
5 ARM SEX AGEGR1 stats_ma… alternative alternat… two.sided <fn>
14+
6 ARM SEX AGEGR1 stats_ma… correct Continui… TRUE <fn>
15+
7 ARM SEX AGEGR1 stats_ma… exact Exact Co… FALSE <fn>
16+
8 ARM SEX AGEGR1 stats_ma… conf.level CI Confi… 0.95 1
17+
18+
---
19+
20+
Code
21+
print(ard_mantelhaentest, columns = "all")
22+
Message
23+
{cards} data frame: 8 x 10
24+
Output
25+
group1 group2 variable context stat_name stat_label stat fmt_fn warning error
26+
1 ARM SEX AGEGR1 stats_ma… statistic Mantel-H… 6.455 1
27+
2 ARM SEX AGEGR1 stats_ma… p.value p-value 0.168 1
28+
3 ARM SEX AGEGR1 stats_ma… parameter Degrees … 4 1
29+
4 ARM SEX AGEGR1 stats_ma… method method Cochran-… <fn>
30+
5 ARM SEX AGEGR1 stats_ma… alternative alternat… less <fn>
31+
6 ARM SEX AGEGR1 stats_ma… correct Continui… FALSE <fn>
32+
7 ARM SEX AGEGR1 stats_ma… exact Exact Co… TRUE <fn>
33+
8 ARM SEX AGEGR1 stats_ma… conf.level CI Confi… 0.9 1
34+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
skip_if_not(is_pkg_installed("broom"))
2+
3+
test_that("ard_stats_mantelhaen_test() works", {
4+
withr::local_options(width = 200)
5+
6+
expect_silent(
7+
ard_mantelhaentest <- cards::ADSL |>
8+
ard_stats_mantelhaen_test(by = ARM, variables = AGEGR1, strata = SEX)
9+
)
10+
11+
expect_snapshot(ard_mantelhaentest |> print(columns = "all"))
12+
13+
expect_equal(
14+
ard_mantelhaentest |>
15+
cards::get_ard_statistics(stat_name %in% c("statistic", "p.value")),
16+
with(cards::ADSL, mantelhaen.test(AGEGR1, ARM, SEX)) |>
17+
broom::tidy() |>
18+
dplyr::select(statistic, p.value) |>
19+
unclass(),
20+
ignore_attr = TRUE
21+
)
22+
23+
# custom arguments to stats::mantelhaen.test()
24+
expect_silent(
25+
ard_mantelhaentest <- cards::ADSL |>
26+
ard_stats_mantelhaen_test(
27+
by = ARM, variable = AGEGR1, strata = SEX,
28+
alternative = "less", correct = FALSE, exact = TRUE, conf.level = 0.90
29+
)
30+
)
31+
32+
expect_snapshot(ard_mantelhaentest |> print(columns = "all"))
33+
})
34+
35+
test_that("ard_stats_mantelhaen_test() displays errors correctly", {
36+
data <- cards::ADSL |>
37+
dplyr::mutate(ARM = "ARM A")
38+
39+
expect_silent(
40+
ard_mantelhaentest <- data |>
41+
ard_stats_mantelhaen_test(by = ARM, variables = AGEGR1, strata = SEX)
42+
)
43+
expect_equal(nrow(ard_mantelhaentest), 9)
44+
expect_equal(ard_mantelhaentest$error, as.list(rep("'x' and 'y' must have at least 2 levels", 9)))
45+
})

0 commit comments

Comments
 (0)