From 63c2f0271a051ca33442146bd43c565efa959a42 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 24 Oct 2025 10:06:28 -0400 Subject: [PATCH 1/2] Add contributing guide with overview of package architecture --- .github/CONTRIBUTING.md | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..f3efc431 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing + +Thanks for considering contributing to {gsDesign2}! We welcome your Pull +Requests to improve our software. For potentially larger changes, we advise +first opening an Issue to discuss the new feature. + +## Overview of package architecture + +The goal of {gsDesign2} is to enable fixed or group sequential design under +non-proportional hazards. When designing a trial, there are a few key important +decisions that affect how the software should behave: + +* Is it a fixed or group sequential design? +* Which method to use for testing? For example, average hazard ratio (ahr) or + weighted log rank (wlr) +* For a group sequential design, is the futility bound binding or non-binding? + +Internally, {gsDesign2} stores the above decisions using multiple different +implementations. + +The main functions have the pattern `{fixed,gs}_{design,power}_{method}`, where: + +* The prefix indicates if the trial is a fixed (`fixed`) or group sequential + (`gs`) design +* The middle portion indicates which input information is used to calculate the + trial details: + * `design`: given power and type I error, calculate sample size and bounds + * `power`: given sample size, calculate power +* The suffix indicates which test method is used, e.g. average hazard ratio + (`ahr`) or weighted log rank (`wlr`) + +Importantly, these characteristics are stored in the output object. + +* The type of design is specified by the class of the object: `"fixed_design"` + or `"gs_design"` +* The method for testing is specified by the list element `design`, which + contains a string abbreviation of the test that matches the original function: + `"ahr"`, `"wlr"`, etc +* The binding status of the futility bound is specified by the attribute + `binding`, which is either `TRUE` or `FALSE` + +Since the only class designation used by the package is `"fixed_design"` or +`"gs_design"`, it only contains S3 methods related to this distinction. +Specifically, the package provides S3 methods for `print()`, `summary()`, and +`to_integer()`. Furthermore, the `summary()` S3 method returns corresponding +objects of class `"fixed_design_summary"` or `"gs_design_summary"`, and the +package provides the S3 methods `as_gt()` and `as_rtf()` to convert the summary +tables to [gt][] or [RTF][] format, respectively. + +[gt]: https://github.com/rstudio/gt +[rtf]: https://en.wikipedia.org/wiki/Rich_Text_Format + +## How to query the trial characteristics from the design object + +If you contribute code to {gsDesign2}, you may need to write different logic +based on the characteristics of the trial design. Given the general overview +above, below are the practical steps for querying a design object. + +To determine if it is a fixed or group sequential design, use `inherits()`: + +```R +if (inherits(x, "gs_design")) { +``` + +To determine the testing method, query the list element `design`. You can use +nested if-else statements or `switch()`. + +```R +if (x$design == "ahr") { + # code for ahr designs +} else if (x$design == "wlr") { + # code for wlr designs +} else if (x$design == "rd") { + # code for rd designs +} else { + # something else +} + +result <- switch(x$design, ahr = 1, wlr = 2, rd = 3) +``` + +To determine if the futility is binding, query the attribute `binding`. + +```R +if (attr(x, "binding")) { + # code for design with binding futility bound +} else { + # code for design with non-binding futility bound +} +``` From e2bce6b580592e798c99ecb336856e9ee827212a Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 14 Nov 2025 12:01:10 -0500 Subject: [PATCH 2/2] Move package architecture overview from contributing to article --- .github/CONTRIBUTING.md | 86 +-------------- _pkgdown.yml | 6 ++ .../articles/story-package-architecture.Rmd | 101 ++++++++++++++++++ 3 files changed, 110 insertions(+), 83 deletions(-) create mode 100644 vignettes/articles/story-package-architecture.Rmd diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f3efc431..f8455b3f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,87 +4,7 @@ Thanks for considering contributing to {gsDesign2}! We welcome your Pull Requests to improve our software. For potentially larger changes, we advise first opening an Issue to discuss the new feature. -## Overview of package architecture +For more information on the organization of the package, please see the +article [Overview of package architecture][story-package-architecture]. -The goal of {gsDesign2} is to enable fixed or group sequential design under -non-proportional hazards. When designing a trial, there are a few key important -decisions that affect how the software should behave: - -* Is it a fixed or group sequential design? -* Which method to use for testing? For example, average hazard ratio (ahr) or - weighted log rank (wlr) -* For a group sequential design, is the futility bound binding or non-binding? - -Internally, {gsDesign2} stores the above decisions using multiple different -implementations. - -The main functions have the pattern `{fixed,gs}_{design,power}_{method}`, where: - -* The prefix indicates if the trial is a fixed (`fixed`) or group sequential - (`gs`) design -* The middle portion indicates which input information is used to calculate the - trial details: - * `design`: given power and type I error, calculate sample size and bounds - * `power`: given sample size, calculate power -* The suffix indicates which test method is used, e.g. average hazard ratio - (`ahr`) or weighted log rank (`wlr`) - -Importantly, these characteristics are stored in the output object. - -* The type of design is specified by the class of the object: `"fixed_design"` - or `"gs_design"` -* The method for testing is specified by the list element `design`, which - contains a string abbreviation of the test that matches the original function: - `"ahr"`, `"wlr"`, etc -* The binding status of the futility bound is specified by the attribute - `binding`, which is either `TRUE` or `FALSE` - -Since the only class designation used by the package is `"fixed_design"` or -`"gs_design"`, it only contains S3 methods related to this distinction. -Specifically, the package provides S3 methods for `print()`, `summary()`, and -`to_integer()`. Furthermore, the `summary()` S3 method returns corresponding -objects of class `"fixed_design_summary"` or `"gs_design_summary"`, and the -package provides the S3 methods `as_gt()` and `as_rtf()` to convert the summary -tables to [gt][] or [RTF][] format, respectively. - -[gt]: https://github.com/rstudio/gt -[rtf]: https://en.wikipedia.org/wiki/Rich_Text_Format - -## How to query the trial characteristics from the design object - -If you contribute code to {gsDesign2}, you may need to write different logic -based on the characteristics of the trial design. Given the general overview -above, below are the practical steps for querying a design object. - -To determine if it is a fixed or group sequential design, use `inherits()`: - -```R -if (inherits(x, "gs_design")) { -``` - -To determine the testing method, query the list element `design`. You can use -nested if-else statements or `switch()`. - -```R -if (x$design == "ahr") { - # code for ahr designs -} else if (x$design == "wlr") { - # code for wlr designs -} else if (x$design == "rd") { - # code for rd designs -} else { - # something else -} - -result <- switch(x$design, ahr = 1, wlr = 2, rd = 3) -``` - -To determine if the futility is binding, query the attribute `binding`. - -```R -if (attr(x, "binding")) { - # code for design with binding futility bound -} else { - # code for design with non-binding futility bound -} -``` +[story-package-architecture]: https://merck.github.io/gsDesign2/articles/story-package-architecture.html diff --git a/_pkgdown.yml b/_pkgdown.yml index 59f7f563..9445f335 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -183,3 +183,9 @@ articles: Develop designs for binary endpoints. contents: - articles/story-risk-difference + +- title: "Overview of package architecture" + desc: > + Explanation of how the code is organized and how to contribute + contents: + - articles/story-package-architecture diff --git a/vignettes/articles/story-package-architecture.Rmd b/vignettes/articles/story-package-architecture.Rmd new file mode 100644 index 00000000..1370de38 --- /dev/null +++ b/vignettes/articles/story-package-architecture.Rmd @@ -0,0 +1,101 @@ +--- +title: "Overview of package architecture" +author: "John Blischak" +output: + rmarkdown::html_document: + toc: true + toc_float: true + toc_depth: 2 + number_sections: true + highlight: "textmate" + css: "custom.css" +--- + +## Overview of package architecture + +The goal of {gsDesign2} is to enable fixed or group sequential design under +non-proportional hazards. When designing a trial, there are a few key important +decisions that affect how the software should behave: + +* Is it a fixed or group sequential design? +* Which method to use for testing? For example, average hazard ratio (ahr) or + weighted log rank (wlr) +* For a group sequential design, is the futility bound binding or non-binding? + +Internally, {gsDesign2} stores the above decisions using multiple different +implementations. + +The main functions have the pattern `{fixed,gs}_{design,power}_{method}`, where: + +* The prefix indicates if the trial is a fixed (`fixed`) or group sequential + (`gs`) design +* The middle portion indicates which input information is used to calculate the + trial details: + * `design`: given power and type I error, calculate sample size and bounds + * `power`: given sample size, calculate power +* The suffix indicates which test method is used, e.g. average hazard ratio + (`ahr`) or weighted log rank (`wlr`) + +Importantly, these characteristics are stored in the output object. + +* The type of design is specified by the class of the object: `"fixed_design"` + or `"gs_design"` +* The method for testing is specified by the list element `design`, which + contains a string abbreviation of the test that matches the original function: + `"ahr"`, `"wlr"`, etc +* The binding status of the futility bound is specified by the attribute + `binding`, which is either `TRUE` or `FALSE` + +Since the only class designation used by the package is `"fixed_design"` or +`"gs_design"`, it only contains S3 methods related to this distinction. +Specifically, the package provides S3 methods for `print()`, `summary()`, and +`to_integer()`. Furthermore, the `summary()` S3 method returns corresponding +objects of class `"fixed_design_summary"` or `"gs_design_summary"`, and the +package provides the S3 methods `as_gt()` and `as_rtf()` to convert the summary +tables to [gt][] or [RTF][] format, respectively. + +[gt]: https://github.com/rstudio/gt +[rtf]: https://en.wikipedia.org/wiki/Rich_Text_Format + +## How to query the trial characteristics from the design object + +**Note:** This section is mainly of interest to potential developers that wish +to contribute code. You do not need to be familiar with the internals in order +to successfully use the package for your analyses. + +If you contribute code to {gsDesign2}, you may need to write different logic +based on the characteristics of the trial design. Given the general overview +above, below are the practical steps for querying a design object. + +To determine if it is a fixed or group sequential design, use `inherits()`: + +```R +if (inherits(x, "gs_design")) { +``` + +To determine the testing method, query the list element `design`. You can use +nested if-else statements or `switch()`. + +```R +if (x$design == "ahr") { + # code for ahr designs +} else if (x$design == "wlr") { + # code for wlr designs +} else if (x$design == "rd") { + # code for rd designs +} else { + # something else +} + +result <- switch(x$design, ahr = 1, wlr = 2, rd = 3) +``` + +To determine if the futility is binding, query the attribute `binding`. + +```R +if (attr(x, "binding")) { + # code for design with binding futility bound +} else { + # code for design with non-binding futility bound +} +```