diff --git a/.lintr b/.lintr index 34473d27..3377b646 100644 --- a/.lintr +++ b/.lintr @@ -1,5 +1,4 @@ linters: linters_with_defaults( line_length_linter = line_length_linter(120), - cyclocomp_linter = NULL, object_usage_linter = NULL ) diff --git a/DESCRIPTION b/DESCRIPTION index b8202e33..3cdee656 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,8 +24,8 @@ Depends: formatters (>= 0.5.10.9001), R (>= 4.2), rlistings (>= 0.2.10), - rtables (>= 0.6.11), - tern (>= 0.9.7.9003) + rtables (>= 0.6.11.9017), + tern (>= 0.9.7.9018) Imports: pharmaverseadam (>= 1.1.0), random.cdisc.data (>= 0.3.16) @@ -46,6 +46,7 @@ VignetteBuilder: knitr Remotes: insightsengineering/formatters@main, + insightsengineering/rtables@main, insightsengineering/tern@main, pharmaverse/pharmaverseadam@main biocViews: diff --git a/tests/testthat/_snaps/char-support.md b/tests/testthat/_snaps/char-support.md new file mode 100644 index 00000000..7f01a55d --- /dev/null +++ b/tests/testthat/_snaps/char-support.md @@ -0,0 +1,252 @@ +# CRITDIR is factor, and one PARAMCD is not available on all AVISITS + + Code + res + Output + Time Point + Test A: Drug X B: Placebo C: Combination + Direction N=134 N=134 N=132 + ——————————————————————————————————————————————————————————————————————————— + SCREENING + HR + Direction 1 + HIGH HR 5 (3.7%) 3 (2.2%) 8 (6.1%) + LOW HR 10 (7.5%) 7 (5.2%) 9 (6.8%) + NORMAL HR 119 (88.8%) 124 (92.5%) 115 (87.1%) + Direction 2 + HIGH Heart Rate 5 (3.7%) 3 (2.2%) 8 (6.1%) + LOW Heart Rate 10 (7.5%) 7 (5.2%) 9 (6.8%) + NORMAL Heart Rate 119 (88.8%) 124 (92.5%) 115 (87.1%) + RR + Direction 1 + HIGH RR 6 (4.5%) 9 (6.7%) 11 (8.3%) + LOW RR 11 (8.2%) 8 (6%) 9 (6.8%) + NORMAL RR 117 (87.3%) 117 (87.3%) 112 (84.8%) + Direction 2 + HIGH RR Duration 6 (4.5%) 9 (6.7%) 11 (8.3%) + LOW RR Duration 11 (8.2%) 8 (6%) 9 (6.8%) + NORMAL RR Duration 117 (87.3%) 117 (87.3%) 112 (84.8%) + QT + Direction 2 + HIGH QT Duration 0 0 0 + LOW QT Duration 0 0 0 + NORMAL QT Duration 0 0 0 + + BASELINE + HR + Direction 1 + HIGH HR 7 (5.2%) 5 (3.7%) 10 (7.6%) + LOW HR 6 (4.5%) 10 (7.5%) 15 (11.4%) + NORMAL HR 121 (90.3%) 119 (88.8%) 107 (81.1%) + Direction 2 + HIGH Heart Rate 7 (5.2%) 5 (3.7%) 10 (7.6%) + LOW Heart Rate 6 (4.5%) 10 (7.5%) 15 (11.4%) + NORMAL Heart Rate 121 (90.3%) 119 (88.8%) 107 (81.1%) + RR + Direction 1 + HIGH RR 7 (5.2%) 12 (9%) 7 (5.3%) + LOW RR 5 (3.7%) 11 (8.2%) 6 (4.5%) + NORMAL RR 122 (91%) 111 (82.8%) 119 (90.2%) + Direction 2 + HIGH RR Duration 7 (5.2%) 12 (9%) 7 (5.3%) + LOW RR Duration 5 (3.7%) 11 (8.2%) 6 (4.5%) + NORMAL RR Duration 122 (91%) 111 (82.8%) 119 (90.2%) + QT + Direction 2 + HIGH QT Duration 9 (6.7%) 4 (3%) 13 (9.8%) + LOW QT Duration 21 (15.7%) 8 (6%) 8 (6.1%) + NORMAL QT Duration 104 (77.6%) 122 (91%) 111 (84.1%) + + WEEK 1 DAY 8 + HR + Direction 1 + HIGH HR 10 (7.5%) 9 (6.7%) 6 (4.5%) + LOW HR 8 (6%) 11 (8.2%) 8 (6.1%) + NORMAL HR 116 (86.6%) 114 (85.1%) 118 (89.4%) + Direction 2 + HIGH Heart Rate 10 (7.5%) 9 (6.7%) 6 (4.5%) + LOW Heart Rate 8 (6%) 11 (8.2%) 8 (6.1%) + NORMAL Heart Rate 116 (86.6%) 114 (85.1%) 118 (89.4%) + RR + Direction 1 + HIGH RR 7 (5.2%) 5 (3.7%) 8 (6.1%) + LOW RR 12 (9%) 9 (6.7%) 4 (3%) + NORMAL RR 115 (85.8%) 120 (89.6%) 120 (90.9%) + Direction 2 + HIGH RR Duration 7 (5.2%) 5 (3.7%) 8 (6.1%) + LOW RR Duration 12 (9%) 9 (6.7%) 4 (3%) + NORMAL RR Duration 115 (85.8%) 120 (89.6%) 120 (90.9%) + QT + Direction 2 + HIGH QT Duration 7 (5.2%) 14 (10.4%) 12 (9.1%) + LOW QT Duration 14 (10.4%) 5 (3.7%) 8 (6.1%) + NORMAL QT Duration 113 (84.3%) 115 (85.8%) 112 (84.8%) + +# CRITDIR is not factor, and all PARAMCD available on all AVISITS + + Code + res + Output + Time Point + Test A: Drug X B: Placebo C: Combination + Direction N=134 N=134 N=132 + ——————————————————————————————————————————————————————————————————————————— + SCREENING + HR + Direction 1 + HIGH HR 5 (3.7%) 3 (2.2%) 8 (6.1%) + LOW HR 10 (7.5%) 7 (5.2%) 9 (6.8%) + NORMAL HR 119 (88.8%) 124 (92.5%) 115 (87.1%) + Direction 2 + HIGH Heart Rate 5 (3.7%) 3 (2.2%) 8 (6.1%) + LOW Heart Rate 10 (7.5%) 7 (5.2%) 9 (6.8%) + NORMAL Heart Rate 119 (88.8%) 124 (92.5%) 115 (87.1%) + RR + Direction 1 + HIGH RR 6 (4.5%) 9 (6.7%) 11 (8.3%) + LOW RR 11 (8.2%) 8 (6%) 9 (6.8%) + NORMAL RR 117 (87.3%) 117 (87.3%) 112 (84.8%) + Direction 2 + HIGH RR Duration 6 (4.5%) 9 (6.7%) 11 (8.3%) + LOW RR Duration 11 (8.2%) 8 (6%) 9 (6.8%) + NORMAL RR Duration 117 (87.3%) 117 (87.3%) 112 (84.8%) + QT + Direction 2 + HIGH QT Duration 7 (5.2%) 14 (10.4%) 7 (5.3%) + LOW QT Duration 8 (6%) 9 (6.7%) 6 (4.5%) + NORMAL QT Duration 119 (88.8%) 111 (82.8%) 119 (90.2%) + + BASELINE + HR + Direction 1 + HIGH HR 7 (5.2%) 5 (3.7%) 10 (7.6%) + LOW HR 6 (4.5%) 10 (7.5%) 15 (11.4%) + NORMAL HR 121 (90.3%) 119 (88.8%) 107 (81.1%) + Direction 2 + HIGH Heart Rate 7 (5.2%) 5 (3.7%) 10 (7.6%) + LOW Heart Rate 6 (4.5%) 10 (7.5%) 15 (11.4%) + NORMAL Heart Rate 121 (90.3%) 119 (88.8%) 107 (81.1%) + RR + Direction 1 + HIGH RR 7 (5.2%) 12 (9%) 7 (5.3%) + LOW RR 5 (3.7%) 11 (8.2%) 6 (4.5%) + NORMAL RR 122 (91%) 111 (82.8%) 119 (90.2%) + Direction 2 + HIGH RR Duration 7 (5.2%) 12 (9%) 7 (5.3%) + LOW RR Duration 5 (3.7%) 11 (8.2%) 6 (4.5%) + NORMAL RR Duration 122 (91%) 111 (82.8%) 119 (90.2%) + QT + Direction 2 + HIGH QT Duration 9 (6.7%) 4 (3%) 13 (9.8%) + LOW QT Duration 21 (15.7%) 8 (6%) 8 (6.1%) + NORMAL QT Duration 104 (77.6%) 122 (91%) 111 (84.1%) + + WEEK 1 DAY 8 + HR + Direction 1 + HIGH HR 10 (7.5%) 9 (6.7%) 6 (4.5%) + LOW HR 8 (6%) 11 (8.2%) 8 (6.1%) + NORMAL HR 116 (86.6%) 114 (85.1%) 118 (89.4%) + Direction 2 + HIGH Heart Rate 10 (7.5%) 9 (6.7%) 6 (4.5%) + LOW Heart Rate 8 (6%) 11 (8.2%) 8 (6.1%) + NORMAL Heart Rate 116 (86.6%) 114 (85.1%) 118 (89.4%) + RR + Direction 1 + HIGH RR 7 (5.2%) 5 (3.7%) 8 (6.1%) + LOW RR 12 (9%) 9 (6.7%) 4 (3%) + NORMAL RR 115 (85.8%) 120 (89.6%) 120 (90.9%) + Direction 2 + HIGH RR Duration 7 (5.2%) 5 (3.7%) 8 (6.1%) + LOW RR Duration 12 (9%) 9 (6.7%) 4 (3%) + NORMAL RR Duration 115 (85.8%) 120 (89.6%) 120 (90.9%) + QT + Direction 2 + HIGH QT Duration 7 (5.2%) 14 (10.4%) 12 (9.1%) + LOW QT Duration 14 (10.4%) 5 (3.7%) 8 (6.1%) + NORMAL QT Duration 113 (84.3%) 115 (85.8%) 112 (84.8%) + +# CRITDIR is not factor, and one PARAMCD is not available on all AVISITS + + Code + res + Output + Time Point + Test A: Drug X B: Placebo C: Combination + Direction N=134 N=134 N=132 + ——————————————————————————————————————————————————————————————————————————— + SCREENING + HR + Direction 1 + HIGH HR 5 (3.7%) 3 (2.2%) 8 (6.1%) + LOW HR 10 (7.5%) 7 (5.2%) 9 (6.8%) + NORMAL HR 119 (88.8%) 124 (92.5%) 115 (87.1%) + Direction 2 + HIGH Heart Rate 5 (3.7%) 3 (2.2%) 8 (6.1%) + LOW Heart Rate 10 (7.5%) 7 (5.2%) 9 (6.8%) + NORMAL Heart Rate 119 (88.8%) 124 (92.5%) 115 (87.1%) + RR + Direction 1 + HIGH RR 6 (4.5%) 9 (6.7%) 11 (8.3%) + LOW RR 11 (8.2%) 8 (6%) 9 (6.8%) + NORMAL RR 117 (87.3%) 117 (87.3%) 112 (84.8%) + Direction 2 + HIGH RR Duration 6 (4.5%) 9 (6.7%) 11 (8.3%) + LOW RR Duration 11 (8.2%) 8 (6%) 9 (6.8%) + NORMAL RR Duration 117 (87.3%) 117 (87.3%) 112 (84.8%) + QT + Direction 2 + HIGH QT Duration 0 0 0 + LOW QT Duration 0 0 0 + NORMAL QT Duration 0 0 0 + + BASELINE + HR + Direction 1 + HIGH HR 7 (5.2%) 5 (3.7%) 10 (7.6%) + LOW HR 6 (4.5%) 10 (7.5%) 15 (11.4%) + NORMAL HR 121 (90.3%) 119 (88.8%) 107 (81.1%) + Direction 2 + HIGH Heart Rate 7 (5.2%) 5 (3.7%) 10 (7.6%) + LOW Heart Rate 6 (4.5%) 10 (7.5%) 15 (11.4%) + NORMAL Heart Rate 121 (90.3%) 119 (88.8%) 107 (81.1%) + RR + Direction 1 + HIGH RR 7 (5.2%) 12 (9%) 7 (5.3%) + LOW RR 5 (3.7%) 11 (8.2%) 6 (4.5%) + NORMAL RR 122 (91%) 111 (82.8%) 119 (90.2%) + Direction 2 + HIGH RR Duration 7 (5.2%) 12 (9%) 7 (5.3%) + LOW RR Duration 5 (3.7%) 11 (8.2%) 6 (4.5%) + NORMAL RR Duration 122 (91%) 111 (82.8%) 119 (90.2%) + QT + Direction 2 + HIGH QT Duration 9 (6.7%) 4 (3%) 13 (9.8%) + LOW QT Duration 21 (15.7%) 8 (6%) 8 (6.1%) + NORMAL QT Duration 104 (77.6%) 122 (91%) 111 (84.1%) + + WEEK 1 DAY 8 + HR + Direction 1 + HIGH HR 10 (7.5%) 9 (6.7%) 6 (4.5%) + LOW HR 8 (6%) 11 (8.2%) 8 (6.1%) + NORMAL HR 116 (86.6%) 114 (85.1%) 118 (89.4%) + Direction 2 + HIGH Heart Rate 10 (7.5%) 9 (6.7%) 6 (4.5%) + LOW Heart Rate 8 (6%) 11 (8.2%) 8 (6.1%) + NORMAL Heart Rate 116 (86.6%) 114 (85.1%) 118 (89.4%) + RR + Direction 1 + HIGH RR 7 (5.2%) 5 (3.7%) 8 (6.1%) + LOW RR 12 (9%) 9 (6.7%) 4 (3%) + NORMAL RR 115 (85.8%) 120 (89.6%) 120 (90.9%) + Direction 2 + HIGH RR Duration 7 (5.2%) 5 (3.7%) 8 (6.1%) + LOW RR Duration 12 (9%) 9 (6.7%) 4 (3%) + NORMAL RR Duration 115 (85.8%) 120 (89.6%) 120 (90.9%) + QT + Direction 2 + HIGH QT Duration 7 (5.2%) 14 (10.4%) 12 (9.1%) + LOW QT Duration 14 (10.4%) 5 (3.7%) 8 (6.1%) + NORMAL QT Duration 113 (84.3%) 115 (85.8%) 112 (84.8%) + diff --git a/tests/testthat/_snaps/table_aovt01.md b/tests/testthat/_snaps/table_aovt01.md index 90268ad9..2110134b 100644 --- a/tests/testthat/_snaps/table_aovt01.md +++ b/tests/testthat/_snaps/table_aovt01.md @@ -63,3 +63,47 @@ 95% CI (-1.17, 2.76) (-0.56, 3.39) p-value 0.4288 0.1591 +# AOVT01 variant with proportional weights is produced correctly + + Code + res + Output + ARM A ARM B ARM C + (N=134) (N=134) (N=132) + —————————————————————————————————————————————————————————————————————————— + BFIALL + Adjusted mean + n 134 134 132 + Adjusted Mean 4.50 6.36 4.05 + Difference in Adjusted Means 1.85 -0.46 + 95% CI (-0.14, 3.85) (-2.45, 1.54) + p-value 0.0679 0.6539 + FATIGI + Adjusted mean + n 134 134 132 + Adjusted Mean 5.42 4.83 4.56 + Difference in Adjusted Means -0.59 -0.86 + 95% CI (-2.58, 1.41) (-2.87, 1.15) + p-value 0.5644 0.4026 + FKSI-FWB + Adjusted mean + n 134 134 132 + Adjusted Mean 4.31 3.53 3.08 + Difference in Adjusted Means -0.79 -1.24 + 95% CI (-2.71, 1.14) (-3.17, 0.69) + p-value 0.4221 0.2088 + FKSI-TSE + Adjusted mean + n 134 134 132 + Adjusted Mean 4.70 3.84 4.45 + Difference in Adjusted Means -0.86 -0.25 + 95% CI (-2.80, 1.09) (-2.20, 1.70) + p-value 0.3858 0.8007 + FKSIALL + Adjusted mean + n 134 134 132 + Adjusted Mean 5.00 5.79 6.42 + Difference in Adjusted Means 0.79 1.42 + 95% CI (-1.17, 2.76) (-0.56, 3.39) + p-value 0.4288 0.1591 + diff --git a/tests/testthat/test-char-support.R b/tests/testthat/test-char-support.R new file mode 100644 index 00000000..2d2637bf --- /dev/null +++ b/tests/testthat/test-char-support.R @@ -0,0 +1,120 @@ +adsl <- random.cdisc.data::cadsl +adeg <- random.cdisc.data::cadeg + +################################################################################ +# Define script level parameters: +################################################################################ +adsl_f <- adsl %>% + select(USUBJID, ACTARM) + +# Note: We keep only post-baseline for analysis. +adeg_f <- adeg %>% + filter(AVISIT %in% levels(adeg$AVISIT)[1:3]) %>% + filter(PARAM %in% c("Heart Rate", "QT Duration", "RR Duration")) %>% + var_relabel( + PARAM = "Assessment", + ANRIND = "Abnormality" + ) %>% + select(USUBJID, AVISIT, PARAM, PARAMCD, ANRIND, ACTARM) %>% + mutate(PARCAT = case_when( + PARAMCD == "QT" ~ "QT", + TRUE ~ "Other" + )) %>% + mutate(ANRIND2 = paste(ANRIND, PARAMCD)) %>% + mutate(ANRIND3 = paste(ANRIND, PARAM)) + +# Ensure character variables are converted to factors and empty strings and NAs are explicit missing levels. +adsl_f <- df_explicit_na(adsl_f) +adeg_f <- df_explicit_na(adeg_f) + +adeg_f_1 <- adeg_f %>% + filter(PARAMCD != "QT") %>% + mutate(CRITDIR = "Direction 1") %>% + mutate(ANRIND23 = ANRIND2) + +adeg_f_2 <- adeg_f %>% + mutate(CRITDIR = "Direction 2") %>% + mutate(ANRIND23 = ANRIND3) + +adeg_f <- rbind(adeg_f_1, adeg_f_2) + + + +### mapping dataframe +uniq_adeg <- unique(adeg_f %>% select(PARCAT, PARAMCD, CRITDIR, ANRIND23)) +mapdf <- uniq_adeg %>% + arrange(PARCAT, PARAMCD, CRITDIR, ANRIND23) %>% + mutate(across(where(is.factor), as.character)) + +### 3 versions of dataset +### CRITDIR not factor, all visits have available data for all PARAMCD +adeg_f1 <- adeg_f + +### CRITDIR not factor, not all visits available data for all PARAMCD +### exclude QT from screening - this will generate the problem in 6.11 +adeg_f <- adeg_f %>% + filter(!(AVISIT == "SCREENING" & PARAMCD == "QT")) + +### CRITDIR factor, not all visits available data +adeg_f2 <- adeg_f +### also part of the issue +adeg_f2$CRITDIR <- factor(adeg_f2$CRITDIR) + + + + + + +################################################################################ +# Define layout and build table: +################################################################################ + + +lyt <- basic_table( + show_colcounts = TRUE, + colcount_format = "N=xx" +) %>% + split_cols_by("ACTARM") %>% + split_rows_by("AVISIT", + label_pos = "topleft", + section_div = " ", + split_fun = drop_split_levels, + split_label = "Time Point", + child_labels = "visible" + ) %>% + split_rows_by("PARAMCD", + split_label = "Test", + label_pos = "topleft", + indent_mod = 1L, + child_labels = "visible", + split_fun = trim_levels_to_map(mapdf) + ) %>% + split_rows_by("CRITDIR", + split_label = "Direction", + label_pos = "topleft", + indent_mod = 1L, + child_labels = "visible", + split_fun = trim_levels_to_map(mapdf) + ) %>% + analyze(c("ANRIND23"), + a_summary, + extra_args = list(.stats = "count_fraction"), + show_labels = "hidden", + indent_mod = 1L + ) + +testthat::test_that("CRITDIR is factor, and one PARAMCD is not available on all AVISITS", { + res <- testthat::expect_silent(build_table(lyt, adeg_f2, alt_counts_df = adsl_f)) + testthat::expect_snapshot(res) +}) + + +testthat::test_that("CRITDIR is not factor, and all PARAMCD available on all AVISITS", { + res <- testthat::expect_silent(build_table(lyt, adeg_f1, alt_counts_df = adsl_f)) + testthat::expect_snapshot(res) +}) + +testthat::test_that("CRITDIR is not factor, and one PARAMCD is not available on all AVISITS", { + res <- testthat::expect_silent(build_table(lyt, adeg_f, alt_counts_df = adsl_f)) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-listing_adal02.R b/tests/testthat/test-listing_adal02.R index 8ec2fa00..1ea2ee98 100644 --- a/tests/testthat/test-listing_adal02.R +++ b/tests/testthat/test-listing_adal02.R @@ -20,7 +20,6 @@ testthat::test_that("ADAL02 listing is produced correctly", { values_from = AVAL ) %>% filter(if_any(matches("Treatment Emergent - Positive"), ~ .x == 1)) %>% - # filter(`Treatment Emergent - Positive` == 1) %>% mutate( VISN = factor(paste0( VISIT, "\n(Day ", diff --git a/tests/testthat/test-table_aet04.R b/tests/testthat/test-table_aet04.R index ffee904b..1fa7d0f6 100644 --- a/tests/testthat/test-table_aet04.R +++ b/tests/testthat/test-table_aet04.R @@ -25,11 +25,13 @@ adae <- level_reducer(adae, "AEDECOD", p_to_keep = 0.7) # Helper function to avoid filtering also the first part of the table, where general information is given. my_row_condition <- function(row_fnc_condition) { function(table_row) { + ret <- NULL if (indent_mod(table_row) == 0) { - return(TRUE) + ret <- TRUE } else { - row_fnc_condition(table_row) + ret <- row_fnc_condition(table_row) } + ret } } diff --git a/tests/testthat/test-table_aovt01.R b/tests/testthat/test-table_aovt01.R index cf968211..de877d28 100644 --- a/tests/testthat/test-table_aovt01.R +++ b/tests/testthat/test-table_aovt01.R @@ -51,3 +51,23 @@ testthat::test_that("AOVT01 variant with multiple endpoints is produced correctl res <- testthat::expect_silent(result) testthat::expect_snapshot(res) }) + +testthat::test_that("AOVT01 variant with proportional weights is produced correctly", { + adqs_multi <- dplyr::filter(adqs, AVISIT == "WEEK 1 DAY 8") + n_per_arm <- table(adsl$ARM) + + result <- basic_table() %>% + split_cols_by("ARMCD", ref_group = "ARM A", split_fun = ref_group_position("first")) %>% + add_colcounts() %>% + split_rows_by("PARAMCD") %>% + summarize_ancova( + vars = "CHG", + variables = list(arm = "ARMCD", covariates = c("BASE", "STRATA1")), + conf_level = 0.95, var_labels = "Adjusted mean", + weights_emmeans = "proportional" + ) %>% + build_table(adqs_multi, alt_counts_df = adsl) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) diff --git a/tests/testthat/test-table_pkpt04.R b/tests/testthat/test-table_pkpt04.R index cbd48ddf..a33cdfc4 100644 --- a/tests/testthat/test-table_pkpt04.R +++ b/tests/testthat/test-table_pkpt04.R @@ -33,7 +33,6 @@ l <- basic_table() %>% testthat::test_that("PKPT04 is produced correctly for Drug X", { adpp0 <- adpp_urine %>% filter(PPCAT == "XANOMELINE") %>% - # h_pkparam_sort() %>% mutate(PKPARAM = factor(paste0(PPTEST, " (", AVALU, ")"))) result <- build_table(l, df = adpp0) diff --git a/tests/testthat/test-table_pkpt05.R b/tests/testthat/test-table_pkpt05.R index 12ee73e3..b12b00b0 100644 --- a/tests/testthat/test-table_pkpt05.R +++ b/tests/testthat/test-table_pkpt05.R @@ -56,7 +56,6 @@ l <- basic_table() %>% testthat::test_that("PKPT05 Drug X is produced correctly", { adpp0 <- adpp_urine %>% filter(PPCAT == "XANOMELINE") %>% - # h_pkparam_sort() %>% dplyr::mutate(PKPARAM = factor(paste0(PPTEST, " (", AVALU, ")"))) result <- build_table(l, df = adpp0) main_title(result) <- paste("Summary of", unique(adpp0$PPSPEC), "PK Parameter by Treatment Arm, PK Population")