-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
**What changes are proposed in this pull request?** * Added function `ard_stats_poisson_test()` for calculating Poisson test results and returning the results as an ARD. (#187) I'm not quite sure the best way to take in the two required variables (`x` and `T`), or if there are other types of variables that should be accepted and then converted. Currently I have them as `variables`. Let me know what you think! Closes #187 -------------------------------------------------------------------------------- Pre-review Checklist (if item does not apply, mark is as complete) - [x] **All** GitHub Action workflows pass with a ✅ - [x] PR branch has pulled the most recent updates from master branch: `usethis::pr_merge_main()` - [x] If a bug was fixed, a unit test was added. - [x] If a new `ard_*()` function was added, it passes the ARD structural checks from `cards::check_ard_structure()`. - [x] If a new `ard_*()` function was added, `set_cli_abort_call()` has been set. - [x] If a new `ard_*()` function was added and it depends on another package (such as, `broom`), `is_pkg_installed("broom", reference_pkg = "cardx")` 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"", reference_pkg = "cardx"))` - [x] 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) - [ ] If a bug was fixed, a unit test was added. - [ ] 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". --------- Co-authored-by: Daniel Sjoberg <[email protected]> Co-authored-by: ayogasekaram <[email protected]>
- Loading branch information
1 parent
f9377fd
commit 65d05c7
Showing
7 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
#' ARD Poisson Test | ||
#' | ||
#' @description | ||
#' Analysis results data for exact tests of a simple null hypothesis about the rate parameter | ||
#' in Poisson distribution, or the comparison of two rate parameters. | ||
#' | ||
#' @param data (`data.frame`)\cr | ||
#' a data frame. See below for details. | ||
#' @param variables ([`tidy-select`][dplyr::dplyr_tidy_select])\cr | ||
#' names of the event and time variables (in that order) to be used in computations. Must be of length 2. | ||
#' @param by ([`tidy-select`][dplyr::dplyr_tidy_select])\cr | ||
#' optional column name to compare by. | ||
#' @param conf.level (scalar `numeric`)\cr | ||
#' confidence level for confidence interval. Default is `0.95`. | ||
#' @param na.rm (scalar `logical`)\cr | ||
#' whether missing values should be removed before computations. Default is `TRUE`. | ||
#' @param ... arguments passed to [poisson.test()]. | ||
#' @return an ARD data frame of class 'card' | ||
#' @name ard_stats_poisson_test | ||
#' | ||
#' @details | ||
#' * For the `ard_stats_poisson_test()` function, the data is expected to be one row per subject. | ||
#' * If `by` is not specified, an exact Poisson test of the rate parameter will be performed. Otherwise, a | ||
#' Poisson comparison of two rate parameters will be performed on the levels of `by`. If `by` has more than 2 | ||
#' levels, an error will occur. | ||
#' | ||
#' @examplesIf do.call(asNamespace("cardx")$is_pkg_installed, list(pkg = "broom", reference_pkg = "cardx")) | ||
#' # Exact test of rate parameter against null hypothesis | ||
#' cards::ADTTE |> | ||
#' ard_stats_poisson_test(variables = c(CNSR, AVAL)) | ||
#' | ||
#' # Comparison test of ratio of 2 rate parameters against null hypothesis | ||
#' cards::ADTTE |> | ||
#' dplyr::filter(TRTA %in% c("Placebo", "Xanomeline High Dose")) |> | ||
#' ard_stats_poisson_test(by = TRTA, variables = c(CNSR, AVAL)) | ||
NULL | ||
|
||
#' @rdname ard_stats_poisson_test | ||
#' @export | ||
ard_stats_poisson_test <- function(data, variables, na.rm = TRUE, by = NULL, conf.level = 0.95, ...) { | ||
set_cli_abort_call() | ||
|
||
# check installed packages --------------------------------------------------- | ||
check_pkg_installed("broom", reference_pkg = "cardx") | ||
|
||
# check/process inputs ------------------------------------------------------- | ||
check_not_missing(data) | ||
check_not_missing(variables) | ||
check_data_frame(data) | ||
data <- dplyr::ungroup(data) | ||
cards::process_selectors(data, by = {{ by }}, variables = {{ variables }}) | ||
check_length(variables, 2) | ||
check_logical(na.rm) | ||
check_scalar(by, allow_empty = TRUE) | ||
check_range(conf.level, range = c(0, 1)) | ||
|
||
# return empty ARD if no variables selected ---------------------- | ||
if (is_empty(variables)) { | ||
return(dplyr::tibble() |> cards::as_card()) | ||
} | ||
|
||
# check number of levels in `by` | ||
if (!is_empty(by) && dplyr::n_distinct(data[[by]], na.rm = TRUE) != 2L) { | ||
cli::cli_abort( | ||
"The {.arg by} argument must have a maximum of two levels.", | ||
call = get_cli_abort_call() | ||
) | ||
} | ||
|
||
# calculate numerator and denominator values | ||
if (!is_empty(by)) { | ||
num <- data |> | ||
dplyr::group_by(.data[[by]]) |> | ||
dplyr::summarise(sum = sum(.data[[variables[1]]], na.rm = na.rm)) |> | ||
dplyr::pull(sum) | ||
denom <- data |> | ||
dplyr::group_by(.data[[by]]) |> | ||
dplyr::summarise(sum = sum(.data[[variables[2]]], na.rm = na.rm)) |> | ||
dplyr::pull(sum) | ||
} else { | ||
num <- sum(data[[variables[1]]], na.rm = na.rm) | ||
denom <- sum(data[[variables[2]]], na.rm = na.rm) | ||
} | ||
|
||
# build ARD ------------------------------------------------------------------ | ||
.format_poissontest_results( | ||
by = by, | ||
variables = variables, | ||
lst_tidy = | ||
cards::eval_capture_conditions( | ||
stats::poisson.test(x = num, T = denom, conf.level = conf.level, ...) |> broom::tidy() | ||
), | ||
... | ||
) | ||
} | ||
|
||
#' Convert Poisson test to ARD | ||
#' | ||
#' @inheritParams cards::tidy_as_ard | ||
#' @inheritParams stats::poisson.test | ||
#' @param by (`string`)\cr by column name | ||
#' @param variables (`character`)\cr names of the event and time variables | ||
#' @param ... passed to [poisson.test()] | ||
#' | ||
#' @return ARD data frame | ||
#' @keywords internal | ||
#' @examplesIf do.call(asNamespace("cardx")$is_pkg_installed, list(pkg = "broom", reference_pkg = "cardx")) | ||
#' cardx:::.format_poissontest_results( | ||
#' by = "ARM", | ||
#' variables = c("CNSR", "AVAL"), | ||
#' lst_tidy = | ||
#' cards::eval_capture_conditions( | ||
#' stats::poisson.test(sum(cards::ADTTE[["CNSR"]]), sum(cards::ADTTE[["AVAL"]])) |> | ||
#' broom::tidy() | ||
#' ) | ||
#' ) | ||
.format_poissontest_results <- function(by = NULL, variables, lst_tidy, ...) { | ||
# build ARD ------------------------------------------------------------------ | ||
ret <- | ||
cards::tidy_as_ard( | ||
lst_tidy = lst_tidy, | ||
tidy_result_names = | ||
c( | ||
"estimate", "statistic", | ||
"p.value", "parameter", "conf.low", "conf.high", | ||
"method", "alternative" | ||
), | ||
fun_args_to_record = c("conf.level", "r"), | ||
formals = formals(asNamespace("stats")[["poisson.test"]]), | ||
passed_args = dots_list(...), | ||
lst_ard_columns = list(context = "stats_poisson_test", variable = variables[2]) | ||
) |> | ||
dplyr::distinct() | ||
|
||
# rename "r" statistic to "mu" | ||
ret$stat_name[ret$stat_name == "r"] <- "mu" | ||
|
||
if (!is_empty(by)) { | ||
ret <- ret |> | ||
dplyr::mutate(group1 = by) | ||
} | ||
|
||
# add the stat label --------------------------------------------------------- | ||
ret |> | ||
dplyr::left_join( | ||
.df_poissontest_stat_labels(by = by), | ||
by = "stat_name" | ||
) |> | ||
dplyr::mutate(stat_label = dplyr::coalesce(.data$stat_label, .data$stat_name)) |> | ||
cards::as_card() |> | ||
cards::tidy_ard_column_order() | ||
} | ||
|
||
.df_poissontest_stat_labels <- function(by = NULL) { | ||
dplyr::tribble( | ||
~stat_name, ~stat_label, | ||
"estimate", ifelse(is_empty(by), "Estimated Rate", "Estimated Rate Ratio"), | ||
"statistic", ifelse(is_empty(by), "Number of Events", "Number of Events in First Sample"), | ||
"p.value", "p-value", | ||
"parameter", "Expected Count", | ||
"conf.low", "CI Lower Bound", | ||
"conf.high", "CI Upper Bound", | ||
"mu", "H0 Mean", | ||
"conf.level", "CI Confidence Level" | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# ard_stats_poisson_test() errors are handled correctly | ||
|
||
Code | ||
ard_stats_poisson_test(cards::ADTTE, by = TRTA, variables = c(CNSR, AVAL)) | ||
Condition | ||
Error in `ard_stats_poisson_test()`: | ||
! The `by` argument must have a maximum of two levels. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
skip_if_not(is_pkg_installed("broom", reference_pkg = "cardx")) | ||
|
||
test_that("ard_stats_poisson_test() works for one sample tests", { | ||
# Default values work | ||
expect_silent( | ||
ard_stats_poisson_test(cards::ADTTE, variables = c(CNSR, AVAL)) | ||
) | ||
|
||
# Custom values work | ||
expect_silent( | ||
ard_single <- ard_stats_poisson_test( | ||
cards::ADTTE, | ||
variables = c("CNSR", "AVAL"), | ||
conf.level = 0.90, | ||
r = 0.8, | ||
alternative = "greater" | ||
) | ||
) | ||
|
||
# Statistics calculated correctly | ||
expect_equal( | ||
ard_single |> | ||
cards::get_ard_statistics( | ||
stat_name %in% | ||
c("estimate", "statistic", "p.value", "parameter", "conf.low", "conf.high", "method", "alternative") | ||
), | ||
poisson.test( | ||
x = sum(cards::ADTTE$CNSR), | ||
T = sum(cards::ADTTE$AVAL), | ||
r = 0.8, | ||
conf.level = 0.9, | ||
alternative = "greater" | ||
) |> | ||
broom::tidy() |> | ||
unclass(), | ||
ignore_attr = TRUE | ||
) | ||
}) | ||
|
||
test_that("ard_stats_poisson_test() works for two sample tests", { | ||
expect_silent( | ||
ard_compare <- | ||
cards::ADTTE |> | ||
dplyr::filter(TRTA %in% c("Placebo", "Xanomeline High Dose")) |> | ||
ard_stats_poisson_test(by = TRTA, variables = c(CNSR, AVAL)) | ||
) | ||
|
||
# Statistics calculated correctly | ||
expect_equal( | ||
ard_compare |> | ||
cards::get_ard_statistics( | ||
stat_name %in% | ||
c("estimate", "statistic", "p.value", "parameter", "conf.low", "conf.high", "method", "alternative") | ||
), | ||
poisson.test( | ||
x = cards::ADTTE |> | ||
dplyr::filter(TRTA %in% c("Placebo", "Xanomeline High Dose")) |> | ||
dplyr::group_by(TRTA) |> | ||
dplyr::summarise(sum = sum(CNSR)) |> | ||
dplyr::pull(sum), | ||
T = cards::ADTTE |> | ||
dplyr::filter(TRTA %in% c("Placebo", "Xanomeline High Dose")) |> | ||
dplyr::group_by(TRTA) |> | ||
dplyr::summarise(sum = sum(AVAL)) |> | ||
dplyr::pull(sum) | ||
) |> | ||
broom::tidy() |> | ||
unclass(), | ||
ignore_attr = TRUE | ||
) | ||
}) | ||
|
||
test_that("ard_stats_poisson_test() errors are handled correctly", { | ||
expect_snapshot( | ||
cards::ADTTE |> | ||
ard_stats_poisson_test(by = TRTA, variables = c(CNSR, AVAL)), | ||
error = TRUE | ||
) | ||
}) | ||
|
||
test_that("ard_stats_poisson_test() follows ard structure", { | ||
expect_silent( | ||
ard_stats_poisson_test(cards::ADTTE, variables = c(CNSR, AVAL)) |> | ||
cards::check_ard_structure(method = T) | ||
) | ||
}) |