├── .Rbuildignore ├── .Rprofile ├── .github ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ └── issue_template.md ├── PULL_REQUEST_TEMPLATE.md ├── SUPPORT.md └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ ├── pr-commands.yaml │ └── test-coverage.yaml ├── .gitignore ├── CRAN-SUBMISSION ├── DESCRIPTION ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── add_overall.R ├── broom-helpers.R ├── complete_ae_data.R ├── data.R ├── gtreg-package.R ├── inline_text_tbl_ae.R ├── reexport.R ├── selectors.R ├── style_xxx.R ├── tbl_ae.R ├── tbl_ae_count.R ├── tbl_ae_focus.R ├── tbl_listing.R ├── tbl_reg_summary.R └── utils-tbl_ae.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── codecov.yml ├── cran-comments.md ├── data-raw ├── DATASET.R ├── img │ ├── gtreg_hex_hires.png │ ├── modify-t0.png │ ├── modify-t1.png │ ├── modify-t2.png │ ├── modify-t3.png │ ├── modify-t4.png │ ├── modify-t5.png │ ├── modify-t6.png │ └── modify-t7.png ├── modify-tables-article-gif.R └── rd_documentation_images.R ├── data ├── df_adverse_events.rda └── df_patient_characteristics.rda ├── gtreg.Rproj ├── inst └── WORDLIST ├── man ├── add_overall_tbl_ae.Rd ├── df_adverse_events.Rd ├── df_patient_characteristics.Rd ├── dot-complete_ae_data.Rd ├── figures │ ├── Introducing-gtreg-screenshot.png │ ├── README-example-tbl_ae-1.png │ ├── README-example-tbl_ae_count-1.png │ ├── README-example-tbl_ae_focus-1.png │ ├── README-example-tbl_listing-1.png │ ├── README-example-tbl_reg_summary-1.png │ ├── add_overall_ex1.png │ ├── add_overall_ex2.png │ ├── add_overall_ex3.png │ ├── add_overall_ex4.png │ ├── logo.png │ ├── modify_ae_header_ex1.png │ ├── selectors_ex1.png │ ├── tbl_ae_count_ex1.png │ ├── tbl_ae_ex1.png │ ├── tbl_ae_ex2.png │ ├── tbl_ae_focus_ex1.png │ ├── tbl_listing_ex1.png │ ├── tbl_listing_ex2.png │ └── tbl_reg_summary_ex1.png ├── gtreg-package.Rd ├── inline_text_tbl_ae.Rd ├── reexports.Rd ├── selectors.Rd ├── style_xxx.Rd ├── tbl_ae.Rd ├── tbl_ae_count.Rd ├── tbl_ae_focus.Rd ├── tbl_listing.Rd └── tbl_reg_summary.Rd ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── tests ├── spelling.R ├── testthat.R └── testthat │ ├── _snaps │ ├── add_overall.md │ └── style_xxx.md │ ├── test-add_overall.R │ ├── test-complete_ae_data.R │ ├── test-inline_text.R │ ├── test-modify_header.R │ ├── test-style_xxx.R │ ├── test-tbl_ae.R │ ├── test-tbl_ae_count.R │ ├── test-tbl_ae_focus.R │ ├── test-tbl_listing.R │ ├── test-tbl_reg_summary.R │ └── test-utils-tbl_ae.R └── vignettes ├── .gitignore ├── articles ├── counting-methods.Rmd ├── misc │ ├── gtreg-modify.gif │ ├── out_excel.png │ ├── out_pdf.png │ └── out_word.png ├── output-gtreg.Rmd ├── table-modifications.Rmd └── table-shells.Rmd └── further_documentation.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^renv$ 2 | ^renv\.lock$ 3 | ^gtreg\.Rproj$ 4 | ^\.Rproj\.user$ 5 | ^LICENSE\.md$ 6 | ^README\.Rmd$ 7 | ^cran-comments\.md$ 8 | ^\.github$ 9 | ^codecov\.yml$ 10 | ^_pkgdown\.yml$ 11 | ^docs$ 12 | ^pkgdown$ 13 | ^data-raw$ 14 | ^CRAN-SUBMISSION$ 15 | ^vignettes/articles$ 16 | -------------------------------------------------------------------------------- /.Rprofile: -------------------------------------------------------------------------------- 1 | # this sets the dev folder in the libPath 2 | tryCatch( 3 | devtools::dev_mode(on = TRUE), 4 | error = function(e) invisible() 5 | ) 6 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at codeofconduct@rstudio.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at . 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | . Translations are available at . 127 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to gtreg 2 | 3 | This outlines how to propose a change to gtreg. 4 | For more detailed info about contributing to this, and other tidyverse packages, please see the 5 | [**development contributing guide**](https://rstd.io/tidy-contrib). 6 | 7 | ## Fixing typos 8 | 9 | You can fix typos, spelling mistakes, or grammatical errors in the documentation directly using the GitHub web interface, as long as the changes are made in the _source_ file. 10 | This generally means you'll need to edit [roxygen2 comments](https://roxygen2.r-lib.org/articles/roxygen2.html) in an `.R`, not a `.Rd` file. 11 | You can find the `.R` file that generates the `.Rd` by reading the comment in the first line. 12 | 13 | ## Bigger changes 14 | 15 | If you want to make a bigger change, it's a good idea to first file an issue and make sure someone from the team agrees that it’s needed. 16 | If you’ve found a bug, please file an issue that illustrates the bug with a minimal 17 | [reprex](https://www.tidyverse.org/help/#reprex) (this will also help you write a unit test, if needed). 18 | 19 | ### Pull request process 20 | 21 | * Fork the package and clone onto your computer. If you haven't done this before, we recommend using `usethis::create_from_github("shannonpileggi/gtreg", fork = TRUE)`. 22 | 23 | * Install all development dependencies with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 24 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 25 | * Create a Git branch for your pull request (PR). We recommend using `usethis::pr_init("brief-description-of-change")`. 26 | 27 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 28 | The title of your PR should briefly describe the change. 29 | The body of your PR should contain `Fixes #issue-number`. 30 | 31 | * For user-facing changes, add a bullet to the top of `NEWS.md` (i.e. just below the first header). Follow the style described in . 32 | 33 | ### Code style 34 | 35 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 36 | You can use the [styler](https://CRAN.R-project.org/package=styler) package to apply these styles, but please don't restyle code that has nothing to do with your PR. 37 | 38 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/rd-formatting.html), for documentation. 39 | 40 | * We use [testthat](https://cran.r-project.org/package=testthat) for unit tests. 41 | Contributions with test cases included are easier to accept. 42 | 43 | ## Code of Conduct 44 | 45 | Please note that the gtreg project is released with a 46 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 47 | project you agree to abide by its terms. 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report or feature request 3 | about: Describe a bug you've seen or make a case for a new feature 4 | --- 5 | 6 | Please briefly describe your problem and what output you expect. If you have a question, please don't use this form. Instead, ask on or . 7 | 8 | Please include a minimal reproducible example (AKA a reprex). If you've never heard of a [reprex](http://reprex.tidyverse.org/) before, start by reading . 9 | 10 | Brief description of the problem 11 | 12 | ```r 13 | # insert reprex here 14 | ``` 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **What changes are proposed in this pull request?** 2 | 3 | 4 | **If there is an GitHub issue associated with this pull request, please provide link.** 5 | 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | Checklist for PR reviewer 10 | 11 | - [ ] Ensure all package dependencies are installed by running `renv::install()` 12 | - [ ] PR branch has pulled the most recent updates from main branch. Ensure the pull request branch and your local version match and both have the latest updates from the main branch. 13 | - [ ] If a new function was added, function included in `_pkgdown.yml` 14 | - [ ] If a bug was fixed, a unit test was added for the bug check 15 | - [ ] Run `pkgdown::build_site()`. Check the R console for errors, and review the rendered website. 16 | - [ ] Code coverage is suitable for any new functions/features. Review coverage with `covr::report()`. Before you run, set `Sys.setenv(NOT_CRAN="true")` and begin in a fresh R session without any packages loaded. 17 | - [ ] R CMD Check runs without errors, warnings, and notes 18 | - [ ] `usethis::use_spell_check()` runs with no spelling errors in documentation 19 | - [ ] Has `NEWS.md` been updated with the changes from this pull request under the heading "`# gtreg (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). 20 | - [ ] Has the version number been incremented using `usethis::use_version(which = "dev")` 21 | - [ ] Approve Pull Request 22 | - [ ] Merge the PR. Please use "Squash and merge". 23 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting help with gtreg 2 | 3 | Thanks for using gtreg! 4 | Before filing an issue, there are a few places to explore and pieces to put together to make the process as smooth as possible. 5 | 6 | ## Make a reprex 7 | 8 | Start by making a minimal **repr**oducible **ex**ample using the [reprex](https://reprex.tidyverse.org/) package. 9 | If you haven't heard of or used reprex before, you're in for a treat! 10 | Seriously, reprex will make all of your R-question-asking endeavors easier (which is a pretty insane ROI for the five to ten minutes it'll take you to learn what it's all about). 11 | For additional reprex pointers, check out the [Get help!](https://www.tidyverse.org/help/) section of the tidyverse site. 12 | 13 | ## Where to ask? 14 | 15 | Armed with your reprex, the next step is to figure out [where to ask](https://www.tidyverse.org/help/#where-to-ask). 16 | 17 | * If it's a question: start with [community.rstudio.com](https://community.rstudio.com/), and/or StackOverflow. There are more people there to answer questions. 18 | 19 | * If it's a bug: you're in the right place, [file an issue](https://github.com/shannonpileggi/gtreg/issues/new). 20 | 21 | * If you're not sure: let the community help you figure it out! 22 | If your problem _is_ a bug or a feature request, you can easily return here and report it. 23 | 24 | Before opening a new issue, be sure to [search issues and pull requests](https://github.com/shannonpileggi/gtreg/issues) to make sure the bug hasn't been reported and/or already fixed in the development version. 25 | By default, the search will be pre-populated with `is:issue is:open`. 26 | You can [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/) (e.g. `is:pr`, `is:closed`) as needed. 27 | For example, you'd simply remove `is:open` to search _all_ issues in the repo, open or closed. 28 | 29 | ## What happens next? 30 | 31 | To be as efficient as possible, development of tidyverse packages tends to be very bursty, so you shouldn't worry if you don't get an immediate response. 32 | Typically we don't look at a repo until a sufficient quantity of issues accumulates, then there’s a burst of intense activity as we focus our efforts. 33 | That makes development more efficient because it avoids expensive context switching between problems, at the cost of taking longer to get back to you. 34 | This process makes a good reprex particularly important because it might be multiple months between your initial report and when we start working on it. 35 | If we can’t reproduce the bug, we can’t fix it! 36 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | 13 | name: R-CMD-check 14 | 15 | jobs: 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | - {os: macOS-latest, r: 'release'} 26 | - {os: windows-latest, r: 'release'} 27 | # Use older ubuntu to maximise backward compatibility 28 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 29 | - {os: ubuntu-latest, r: 'release'} 30 | - {os: ubuntu-latest, r: 'oldrel-1'} 31 | - {os: ubuntu-latest, r: 'oldrel-2'} 32 | # - {os: ubuntu-latest, r: 'oldrel-3'} 33 | # - {os: ubuntu-latest, r: 'oldrel-4'} 34 | 35 | env: 36 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 37 | R_KEEP_PKG_SOURCE: yes 38 | 39 | steps: 40 | - uses: actions/checkout@v2 41 | 42 | - uses: r-lib/actions/setup-pandoc@v2 43 | 44 | - uses: r-lib/actions/setup-r@v2 45 | with: 46 | r-version: ${{ matrix.config.r }} 47 | http-user-agent: ${{ matrix.config.http-user-agent }} 48 | use-public-rspm: true 49 | 50 | - uses: r-lib/actions/setup-r-dependencies@v2 51 | with: 52 | extra-packages: rcmdcheck 53 | 54 | - uses: r-lib/actions/check-r-package@v2 55 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - uses: r-lib/actions/setup-pandoc@v2 26 | 27 | - uses: r-lib/actions/setup-r@v2 28 | with: 29 | use-public-rspm: true 30 | 31 | - uses: r-lib/actions/setup-r-dependencies@v2 32 | with: 33 | extra-packages: any::pkgdown, local::. 34 | needs: website 35 | 36 | - name: Build site 37 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 38 | shell: Rscript {0} 39 | 40 | - name: Deploy to GitHub pages 🚀 41 | if: github.event_name != 'pull_request' 42 | uses: JamesIves/github-pages-deploy-action@4.1.4 43 | with: 44 | clean: false 45 | branch: gh-pages 46 | folder: docs 47 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | issue_comment: 5 | types: [created] 6 | 7 | name: Commands 8 | 9 | jobs: 10 | document: 11 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }} 12 | name: document 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - uses: r-lib/actions/pr-fetch@v2 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - uses: r-lib/actions/setup-r@v2 24 | with: 25 | use-public-rspm: true 26 | 27 | - uses: r-lib/actions/setup-r-dependencies@v2 28 | with: 29 | extra-packages: roxygen2 30 | 31 | - name: Document 32 | run: Rscript -e 'roxygen2::roxygenise()' 33 | 34 | - name: commit 35 | run: | 36 | git config --local user.name "$GITHUB_ACTOR" 37 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 38 | git add man/\* NAMESPACE 39 | git commit -m 'Document' 40 | 41 | - uses: r-lib/actions/pr-push@v2 42 | with: 43 | repo-token: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | style: 46 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }} 47 | name: style 48 | runs-on: ubuntu-latest 49 | env: 50 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 51 | steps: 52 | - uses: actions/checkout@v2 53 | 54 | - uses: r-lib/actions/pr-fetch@v2 55 | with: 56 | repo-token: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | - uses: r-lib/actions/setup-r@v2 59 | 60 | - name: Install dependencies 61 | run: Rscript -e 'install.packages("styler")' 62 | 63 | - name: Style 64 | run: Rscript -e 'styler::style_pkg()' 65 | 66 | - name: commit 67 | run: | 68 | git config --local user.name "$GITHUB_ACTOR" 69 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 70 | git add \*.R 71 | git commit -m 'Style' 72 | 73 | - uses: r-lib/actions/pr-push@v2 74 | with: 75 | repo-token: ${{ secrets.GITHUB_TOKEN }} 76 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: covr 27 | 28 | - name: Test coverage 29 | run: covr::codecov() 30 | shell: Rscript {0} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | .Rapp.history 4 | 5 | # Session Data files 6 | .RData 7 | 8 | # User-specific files 9 | .Ruserdata 10 | 11 | # Example code in package build process 12 | *-Ex.R 13 | 14 | # Output files from R CMD build 15 | /*.tar.gz 16 | 17 | # Output files from R CMD check 18 | /*.Rcheck/ 19 | 20 | # RStudio files 21 | .Rproj.user/ 22 | 23 | # produced vignettes 24 | vignettes/*.html 25 | vignettes/*.pdf 26 | 27 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 28 | .httr-oauth 29 | 30 | # knitr and R markdown default cache directories 31 | *_cache/ 32 | /cache/ 33 | 34 | # Temporary files created by R markdown 35 | *.utf8.md 36 | *.knit.md 37 | 38 | # R Environment Variables 39 | .Renviron 40 | .Rproj.user 41 | docs 42 | inst/doc 43 | .DS_Store 44 | -------------------------------------------------------------------------------- /CRAN-SUBMISSION: -------------------------------------------------------------------------------- 1 | Version: 0.3.0 2 | Date: 2023-11-17 15:26:23 UTC 3 | SHA: d59e8052f4f776e7d108ac00ea1543c1d0b060b8 4 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: gtreg 2 | Title: Regulatory Tables for Clinical Research 3 | Version: 0.4.0.9000 4 | Authors@R: c( 5 | person("Shannon", "Pileggi", , "shannon.pileggi@gmail.com", role = c("aut", "cre", "cph"), 6 | comment = c(ORCID = "0000-0002-7732-4164")), 7 | person("Daniel D.", "Sjoberg", , "danield.sjoberg@gmail.com", role = "aut", 8 | comment = c(ORCID = "0000-0003-0862-2018")) 9 | ) 10 | Description: Creates tables suitable for regulatory agency submission by 11 | leveraging the 'gtsummary' package as the back end. Tables can be exported 12 | to HTML, Word, PDF and more. Highly customized outputs are 13 | available by utilizing existing styling functions from 'gtsummary' as 14 | well as custom options designed for regulatory tables. 15 | License: GPL (>= 3) 16 | URL: https://github.com/shannonpileggi/gtreg, 17 | https://shannonpileggi.github.io/gtreg/ 18 | BugReports: https://github.com/shannonpileggi/gtreg/issues 19 | Depends: 20 | R (>= 3.4) 21 | Imports: 22 | cli (>= 3.6.1), 23 | dplyr (>= 1.1.1), 24 | forcats (>= 1.0.0), 25 | glue (>= 1.6.2), 26 | gtsummary (>= 2.0.4.9015), 27 | purrr (>= 1.0.1), 28 | rlang (>= 1.1.1), 29 | stringr (>= 1.5.0), 30 | tibble (>= 3.2.1), 31 | tidyr (>= 1.2.1) 32 | Suggests: 33 | covr (>= 3.6.1), 34 | gt (>= 0.10.0), 35 | knitr (>= 1.43), 36 | rmarkdown (>= 2.22), 37 | spelling (>= 2.2.1), 38 | testthat (>= 3.1.9) 39 | Remotes: ddsjoberg/gtsummary 40 | VignetteBuilder: 41 | knitr 42 | Config/testthat/edition: 3 43 | Encoding: UTF-8 44 | Language: en-US 45 | LazyData: true 46 | Roxygen: list(markdown = TRUE) 47 | RoxygenNote: 7.3.2 48 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(add_overall,tbl_ae) 4 | S3method(add_overall,tbl_ae_count) 5 | S3method(add_overall,tbl_ae_focus) 6 | S3method(inline_text,tbl_ae) 7 | S3method(inline_text,tbl_ae_count) 8 | S3method(inline_text,tbl_ae_focus) 9 | export("%>%") 10 | export(.complete_ae_data) 11 | export(add_overall) 12 | export(all_ae_cols) 13 | export(all_categorical) 14 | export(all_cols_in_strata) 15 | export(all_continuous) 16 | export(all_continuous2) 17 | export(all_of) 18 | export(all_overall_cols) 19 | export(all_stat_cols) 20 | export(all_unknown_cols) 21 | export(any_of) 22 | export(as_flex_table) 23 | export(as_gt) 24 | export(as_hux_table) 25 | export(as_hux_xlsx) 26 | export(as_kable) 27 | export(as_kable_extra) 28 | export(as_tibble) 29 | export(bold_labels) 30 | export(bold_levels) 31 | export(contains) 32 | export(ends_with) 33 | export(everything) 34 | export(inline_text) 35 | export(italicize_labels) 36 | export(last_col) 37 | export(matches) 38 | export(modify_caption) 39 | export(modify_column_indent) 40 | export(modify_footnote) 41 | export(modify_header) 42 | export(modify_spanning_header) 43 | export(num_range) 44 | export(one_of) 45 | export(select) 46 | export(show_header_names) 47 | export(starts_with) 48 | export(style_number) 49 | export(style_percent) 50 | export(style_sigfig) 51 | export(style_xxx) 52 | export(tbl_ae) 53 | export(tbl_ae_count) 54 | export(tbl_ae_focus) 55 | export(tbl_listing) 56 | export(tbl_reg_summary) 57 | export(vars) 58 | export(where) 59 | importFrom(cli,cli_alert_danger) 60 | importFrom(cli,cli_alert_info) 61 | importFrom(cli,cli_code) 62 | importFrom(dplyr,"%>%") 63 | importFrom(dplyr,across) 64 | importFrom(dplyr,all_of) 65 | importFrom(dplyr,any_of) 66 | importFrom(dplyr,arrange) 67 | importFrom(dplyr,contains) 68 | importFrom(dplyr,ends_with) 69 | importFrom(dplyr,everything) 70 | importFrom(dplyr,filter) 71 | importFrom(dplyr,group_by) 72 | importFrom(dplyr,last_col) 73 | importFrom(dplyr,matches) 74 | importFrom(dplyr,mutate) 75 | importFrom(dplyr,num_range) 76 | importFrom(dplyr,one_of) 77 | importFrom(dplyr,select) 78 | importFrom(dplyr,starts_with) 79 | importFrom(dplyr,ungroup) 80 | importFrom(dplyr,vars) 81 | importFrom(dplyr,where) 82 | importFrom(gtsummary,add_overall) 83 | importFrom(gtsummary,all_categorical) 84 | importFrom(gtsummary,all_continuous) 85 | importFrom(gtsummary,all_continuous2) 86 | importFrom(gtsummary,all_stat_cols) 87 | importFrom(gtsummary,as_flex_table) 88 | importFrom(gtsummary,as_gt) 89 | importFrom(gtsummary,as_hux_table) 90 | importFrom(gtsummary,as_hux_xlsx) 91 | importFrom(gtsummary,as_kable) 92 | importFrom(gtsummary,as_kable_extra) 93 | importFrom(gtsummary,bold_labels) 94 | importFrom(gtsummary,bold_levels) 95 | importFrom(gtsummary,inline_text) 96 | importFrom(gtsummary,italicize_labels) 97 | importFrom(gtsummary,modify_caption) 98 | importFrom(gtsummary,modify_column_indent) 99 | importFrom(gtsummary,modify_footnote) 100 | importFrom(gtsummary,modify_header) 101 | importFrom(gtsummary,modify_spanning_header) 102 | importFrom(gtsummary,show_header_names) 103 | importFrom(gtsummary,style_number) 104 | importFrom(gtsummary,style_percent) 105 | importFrom(gtsummary,style_sigfig) 106 | importFrom(purrr,"%||%") 107 | importFrom(rlang,":=") 108 | importFrom(rlang,.data) 109 | importFrom(rlang,.env) 110 | importFrom(rlang,is_empty) 111 | importFrom(rlang,is_missing) 112 | importFrom(rlang,sym) 113 | importFrom(tibble,as_tibble) 114 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # gtreg (development version) 2 | 3 | * Update internals to be compatible with {gtsummary} 2.1.0 release. 4 | 5 | * Removed {broom.helpers} dependency. 6 | 7 | # gtreg 0.4.0 8 | 9 | * Update internals to be compatible with {gtsummary} 2.0.0 release. 10 | 11 | # gtreg 0.3.0 12 | 13 | * Add errors for missing `soc` and `ae` in `complete_ae_data()` (#204). 14 | 15 | * Fix GitHub link on `pkgdown` site (#207). 16 | 17 | * Address deprecated `purrr::when()` and `forcats::fct_explicit_na()` (#210). 18 | 19 | * Update package versions for dependencies. 20 | 21 | # gtreg 0.2.0 22 | 23 | Documentation: 24 | 25 | * Added R in Medicine "Introducing {gtreg}" recording to readme. 26 | 27 | * Migrated "Adverse Event Counting Methods" vignette to an article. (#186) 28 | 29 | * Add a vignette to show how to modify table headers, footnotes, and captions. (#96) 30 | 31 | * Added new Table Shells article outlining how to create shells with the {gtreg} package. (#85) 32 | 33 | * Added vignette highlighting the options to export {gtreg} tables. (#108) 34 | 35 | 36 | Functionality: 37 | 38 | * Updated structure of package to no longer use tbl_stack() internally, improving speed. (#126) 39 | 40 | * Fix in `add_overall()` to correctly order adverse events within system organ classes. (#183) 41 | 42 | * Added function `style_xxx()` to assist in creating table shells. 43 | 44 | * Re-exporting additional functions from {gtsummary}. (#172) 45 | 46 | * Allow for special characters, like "\n " to be followed by a space. (#174) 47 | 48 | # gtreg 0.1.1 49 | 50 | * Fix in `tbl_ae_count()` when individual stratum had complete unobserved data for the SOC and AE. (#165) 51 | 52 | * Fix in the denominator of `tbl_ae_focus()` for the system organ class level. (#163) 53 | 54 | # gtreg 0.1.0 55 | 56 | * Initial release. 57 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Simulated Adverse Event Database 2 | #' 3 | #' A data set containing reported AEs from a trial. 4 | #' 5 | #' @format A data frame with 100 rows--one row per patient per AE 6 | #' \describe{ 7 | #' \item{patient_id}{Patient ID} 8 | #' \item{trt}{Treatment Group} 9 | #' \item{system_organ_class}{System Organ Class} 10 | #' \item{adverse_event}{Adverse Event} 11 | #' \item{grade}{Grade} 12 | #' \item{drug_attribution}{Drug Attribution} 13 | #' \item{any_complication}{Any Grade Complication} 14 | #' \item{grade3_complication}{Grade 3+ Complication} 15 | #' } 16 | "df_adverse_events" 17 | 18 | #' Simulated Patient Characteristics Database 19 | #' 20 | #' @format A data frame with 100 rows--one row per patient 21 | #' \describe{ 22 | #' \item{patient_id}{Patient ID} 23 | #' \item{trt}{Treatment Group} 24 | #' \item{age}{Patient Age} 25 | #' \item{marker}{Biological Marker} 26 | #' \item{status}{Study Status} 27 | #' \item{discontinued}{Discontinued from Study} 28 | #' \item{off_trt_ae}{Off Treatment Adverse Event} 29 | #' } 30 | "df_patient_characteristics" 31 | -------------------------------------------------------------------------------- /R/gtreg-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | #' @importFrom purrr %||% 3 | #' @importFrom rlang .env .data sym is_missing := is_empty 4 | #' @importFrom dplyr mutate select filter ungroup group_by arrange across 5 | #' @importFrom cli cli_alert_danger cli_code cli_alert_info 6 | "_PACKAGE" 7 | 8 | # allowing for the use of the dot when piping 9 | utils::globalVariables(c(".", "where")) 10 | 11 | ## usethis namespace: start 12 | ## usethis namespace: end 13 | NULL 14 | -------------------------------------------------------------------------------- /R/inline_text_tbl_ae.R: -------------------------------------------------------------------------------- 1 | #' Report Values from gtreg tables in-line 2 | #' 3 | #' Function allows users to report formatted and styled results from 4 | #' gtreg tables in-line. 5 | #' 6 | #' @param x an object of class `tbl_ae()`, `tbl_ae_count()`, `tbl_ae_focus()` 7 | #' @param row string indicating the AE or SOC to report 8 | #' @param column column name of cell to report. Use `show_header_names(x)` 9 | #' to print all column names beside the current header. 10 | #' @param ... not used 11 | #' 12 | #' @return string 13 | #' @name inline_text_tbl_ae 14 | #' @examples 15 | #' \donttest{ 16 | #' tbl <- 17 | #' df_adverse_events %>% 18 | #' tbl_ae( 19 | #' id = patient_id, 20 | #' ae = adverse_event, 21 | #' soc = system_organ_class, 22 | #' by = grade 23 | #' ) 24 | #' show_header_names(tbl) 25 | #' 26 | #' inline_text(tbl, "Anaemia", column = stat_5) 27 | #'} 28 | NULL 29 | 30 | #' @rdname inline_text_tbl_ae 31 | #' @export 32 | inline_text.tbl_ae <- function(x, row, column = NULL, ...) { 33 | # check inputs --------------------------------------------------------------- 34 | rlang::check_dots_empty() 35 | column <- rlang::enquo(column) 36 | if (rlang::quo_is_null(column)) { 37 | cli::cli_alert_danger("The {.code column=} argument is required.") 38 | cli::cli_alert_info( 39 | "Run {.code show_header_names(x)} to list the column names and headers.") 40 | return(invisible()) 41 | } 42 | if (!rlang::is_string(row)) { 43 | stop("Argument `row=` must be a string.", call. = FALSE) 44 | } 45 | 46 | # identify the variable name associated with `row=` -------------------------- 47 | vct_ae_or_soc <- 48 | x$table_body %>% 49 | dplyr::pull("label") 50 | 51 | if (!(row %in% vct_ae_or_soc)) { 52 | paste0("Invalid selection in `row=`.\n", 53 | "Select one of\n\n", 54 | paste(shQuote(vct_ae_or_soc), collapse = ", ")) %>% 55 | stop(call. = FALSE) 56 | } 57 | 58 | variable <- 59 | x$table_body %>% 60 | filter(.data$label %in% .env$row) %>% 61 | dplyr::pull("variable") 62 | 63 | variable_is_ae <- startsWith(variable, "ae") 64 | 65 | # return result -------------------------------------------------------------- 66 | gtsummary::inline_text( 67 | x = structure(x, class = "gtsummary"), # forcing evaluation with `gtsummary::inline_text.gtsummary()` 68 | variable = all_of(variable), 69 | level = switch(variable_is_ae, row), 70 | column = !!column 71 | ) 72 | } 73 | 74 | #' @rdname inline_text_tbl_ae 75 | #' @export 76 | inline_text.tbl_ae_count <- inline_text.tbl_ae 77 | 78 | #' @rdname inline_text_tbl_ae 79 | #' @export 80 | inline_text.tbl_ae_focus <- inline_text.tbl_ae 81 | -------------------------------------------------------------------------------- /R/reexport.R: -------------------------------------------------------------------------------- 1 | # dplyr ------------------------------------------------------------------------ 2 | #' @export 3 | #' @importFrom dplyr %>% 4 | dplyr::`%>%` 5 | 6 | #' @importFrom dplyr vars 7 | #' @export 8 | dplyr::vars 9 | 10 | #' @importFrom dplyr select 11 | #' @export 12 | dplyr::select 13 | 14 | #' @importFrom dplyr where 15 | #' @export 16 | dplyr::where 17 | 18 | #' @importFrom dplyr starts_with 19 | #' @export 20 | dplyr::starts_with 21 | 22 | #' @importFrom dplyr ends_with 23 | #' @export 24 | dplyr::ends_with 25 | 26 | #' @importFrom dplyr contains 27 | #' @export 28 | dplyr::contains 29 | 30 | #' @importFrom dplyr matches 31 | #' @export 32 | dplyr::matches 33 | 34 | #' @importFrom dplyr num_range 35 | #' @export 36 | dplyr::num_range 37 | 38 | #' @importFrom dplyr all_of 39 | #' @export 40 | dplyr::all_of 41 | 42 | #' @importFrom dplyr any_of 43 | #' @export 44 | dplyr::any_of 45 | 46 | #' @importFrom dplyr everything 47 | #' @export 48 | dplyr::everything 49 | 50 | #' @importFrom dplyr last_col 51 | #' @export 52 | dplyr::last_col 53 | 54 | #' @importFrom dplyr one_of 55 | #' @export 56 | dplyr::one_of 57 | 58 | # tibble ----------------------------------------------------------------------- 59 | #' @export 60 | #' @importFrom tibble as_tibble 61 | tibble::as_tibble 62 | 63 | # gtsummary -------------------------------------------------------------------- 64 | #' @importFrom gtsummary as_gt 65 | #' @export 66 | gtsummary::as_gt 67 | 68 | #' @importFrom gtsummary as_flex_table 69 | #' @export 70 | gtsummary::as_flex_table 71 | 72 | #' @importFrom gtsummary as_hux_table 73 | #' @export 74 | gtsummary::as_hux_table 75 | 76 | #' @importFrom gtsummary as_hux_xlsx 77 | #' @export 78 | gtsummary::as_hux_xlsx 79 | 80 | #' @importFrom gtsummary as_kable 81 | #' @export 82 | gtsummary::as_kable 83 | 84 | #' @importFrom gtsummary as_kable_extra 85 | #' @export 86 | gtsummary::as_kable_extra 87 | 88 | #' @importFrom gtsummary inline_text 89 | #' @export 90 | gtsummary::inline_text 91 | 92 | #' @importFrom gtsummary add_overall 93 | #' @export 94 | gtsummary::add_overall 95 | 96 | #' @importFrom gtsummary modify_spanning_header 97 | #' @export 98 | gtsummary::modify_spanning_header 99 | 100 | #' @importFrom gtsummary modify_header 101 | #' @export 102 | gtsummary::modify_header 103 | 104 | #' @importFrom gtsummary modify_footnote 105 | #' @export 106 | gtsummary::modify_footnote 107 | 108 | #' @importFrom gtsummary modify_caption 109 | #' @export 110 | gtsummary::modify_caption 111 | 112 | #' @importFrom gtsummary modify_column_indent 113 | #' @export 114 | gtsummary::modify_column_indent 115 | 116 | #' @importFrom gtsummary bold_labels 117 | #' @export 118 | gtsummary::bold_labels 119 | 120 | #' @importFrom gtsummary bold_levels 121 | #' @export 122 | gtsummary::bold_levels 123 | 124 | #' @importFrom gtsummary italicize_labels 125 | #' @export 126 | gtsummary::italicize_labels 127 | 128 | #' @importFrom gtsummary italicize_labels 129 | #' @export 130 | gtsummary::italicize_labels 131 | 132 | #' @importFrom gtsummary bold_labels 133 | #' @export 134 | gtsummary::bold_labels 135 | 136 | #' @importFrom gtsummary show_header_names 137 | #' @export 138 | gtsummary::show_header_names 139 | 140 | #' @importFrom gtsummary style_number 141 | #' @export 142 | gtsummary::style_number 143 | 144 | #' @importFrom gtsummary style_sigfig 145 | #' @export 146 | gtsummary::style_sigfig 147 | 148 | #' @importFrom gtsummary style_percent 149 | #' @export 150 | gtsummary::style_percent 151 | 152 | #' @importFrom gtsummary all_continuous 153 | #' @export 154 | gtsummary::all_continuous 155 | 156 | #' @importFrom gtsummary all_continuous2 157 | #' @export 158 | gtsummary::all_continuous2 159 | 160 | #' @importFrom gtsummary all_categorical 161 | #' @export 162 | gtsummary::all_categorical 163 | 164 | #' @importFrom gtsummary all_stat_cols 165 | #' @export 166 | gtsummary::all_stat_cols 167 | 168 | -------------------------------------------------------------------------------- /R/selectors.R: -------------------------------------------------------------------------------- 1 | #' Column Selectors 2 | #' 3 | #' See the \href{https://shannonpileggi.github.io/gtreg/articles/table-modifications.html}{Table modifications article} for examples. 4 | #' - `all_ae_cols(overall, unknown)` selects all columns summarizing AE statistics. By default, unknown and overall columns are not selected. 5 | #' - `all_cols_in_strata(strata)` selects all columns from specified stratum. 6 | #' - `all_overall_cols()` selects all overall columns 7 | #' - `all_unknown_cols()` selects all unknown columns 8 | #' 9 | #' @param overall logical indicating whether to include the overall columns. 10 | #' Default is FALSE 11 | #' @param unknown logical indicating whether to include the unknown or missing columns. 12 | #' Default is FALSE 13 | #' @param strata character vector of the selected stratum 14 | #' 15 | #' @name selectors 16 | #' @return selected columns 17 | #' 18 | #' @seealso `gtsummary::all_stat_cols()` 19 | #' 20 | #' @examples 21 | #' \donttest{ 22 | #' selectors_ex1 <- 23 | #' df_adverse_events %>% 24 | #' dplyr::mutate(grade = ifelse(dplyr::row_number() == 1L, NA, grade)) %>% 25 | #' tbl_ae( 26 | #' id = patient_id, 27 | #' ae = adverse_event, 28 | #' soc = system_organ_class, 29 | #' by = grade 30 | #' ) %>% 31 | #' add_overall(across = 'by') %>% 32 | #' modify_header( 33 | #' all_ae_cols() ~ "**Grade {by}**", 34 | #' all_overall_cols() ~ "**Total**", 35 | #' all_unknown_cols() ~ "**Unknown Grade**" 36 | #' ) 37 | #' } 38 | #' @section Example Output: 39 | #' \if{html}{Example 1} 40 | #' 41 | #' \if{html}{\figure{selectors_ex1.png}{options: width=65\%}} 42 | NULL 43 | 44 | #' @export 45 | #' @rdname selectors 46 | all_ae_cols <- function(overall = FALSE, unknown = FALSE) { 47 | # construct requested selector 48 | if (identical(overall, FALSE) && identical(unknown, FALSE)) { 49 | return(where(\(x) isFALSE(attr(x, "gtsummary.hide")) && 50 | !rlang::is_empty(attr(x, "gtsummary.overall")) && !rlang::is_empty(attr(x, "gtsummary.unknown")) && 51 | !isTRUE(attr(x, "gtsummary.overall")) && !isTRUE(attr(x, "gtsummary.unknown")))) 52 | } 53 | else if (identical(overall, FALSE)) { 54 | return(where(\(x) isFALSE(attr(x, "gtsummary.hide")) && 55 | !rlang::is_empty(attr(x, "gtsummary.overall")) && 56 | !isTRUE(attr(x, "gtsummary.overall")))) 57 | } 58 | else if (identical(unknown, FALSE)) { 59 | return(where(\(x) isFALSE(attr(x, "gtsummary.hide")) && 60 | !rlang::is_empty(attr(x, "gtsummary.unknown")) && 61 | !isTRUE(attr(x, "gtsummary.unknown")))) 62 | } 63 | 64 | where(\(x) isFALSE(attr(x, "gtsummary.hide"))) 65 | } 66 | 67 | #' @export 68 | #' @rdname selectors 69 | all_cols_in_strata <- function(strata) { 70 | where(function(x) isFALSE(attr(x, "gtsummary.hide")) && isTRUE(attr(x, "gtsummary.strata") %in% strata)) 71 | } 72 | 73 | #' @export 74 | #' @rdname selectors 75 | all_overall_cols <- function() { 76 | where(function(x) isFALSE(attr(x, "gtsummary.hide")) && isTRUE(attr(x, "gtsummary.overall"))) 77 | } 78 | 79 | #' @export 80 | #' @rdname selectors 81 | all_unknown_cols <- function() { 82 | where(function(x) isFALSE(attr(x, "gtsummary.hide")) && isTRUE(attr(x, "gtsummary.unknown"))) 83 | } 84 | -------------------------------------------------------------------------------- /R/style_xxx.R: -------------------------------------------------------------------------------- 1 | #' Style numbers as x's 2 | #' 3 | #' The purpose of `style_xxx()` is to convert numeric values in 4 | #' summary tables to x's of consistent length for mock tables. 5 | #' See the \href{https://shannonpileggi.github.io/gtreg/articles/table-shells.html}{Table shells vignette} 6 | #' for detailed examples. 7 | #' 8 | #' @param x a numeric or character vector 9 | #' @param width the width of output field of x's, including the decimal place 10 | #' @param digits the number of digits displayed after the decimal place 11 | #' 12 | #' @return a character vector 13 | #' @export 14 | #' 15 | #' @examples 16 | #' style_xxx(7:10, digits = 0) 17 | #' style_xxx(7:10, digits = 1) 18 | #' style_xxx(7:10, width = 2, digits = 0) 19 | #' style_xxx(7:10, width = 5, digits = 2) 20 | #' 21 | style_xxx <- function(x, width = digits + 2, digits = 0) { 22 | after_decimal <- 23 | paste(rep_len("x", digits), collapse = "") %>% rep_len(length(x)) 24 | 25 | before_decimal <- 26 | dplyr::case_when( 27 | digits == 0 ~ paste(rep_len("x", width), collapse = "") %>% rep_len(length(x)), 28 | TRUE ~ paste(rep_len("x", width - digits - 1), collapse = "") %>% rep_len(length(x)) 29 | ) 30 | 31 | if (digits == 0) return(before_decimal) 32 | 33 | paste(before_decimal, after_decimal, sep = ".") 34 | } 35 | -------------------------------------------------------------------------------- /R/tbl_ae.R: -------------------------------------------------------------------------------- 1 | #' Tabulate Adverse Events 2 | #' 3 | #' @description 4 | #' The function tabulates adverse events. One AE per ID will be counted in the 5 | #' resulting table. If a `by=` variable is passed and a 6 | #' patient experienced more than one of the same AE, the AE associated with the 7 | #' highest `by=` level will be included. For example, if a patient has two of 8 | #' the same AE and `by = grade`, the AE with the highest grade will be 9 | #' included. 10 | #' Similarly, if tabulations within system organ class are requested, the 11 | #' AE within SOC associated with the highest grade will be tabulated. 12 | #' 13 | #' @param data Data frame 14 | #' @param id Variable name of the patient ID 15 | #' @param soc Variable name of the system organ class column 16 | #' @param ae Variable name of the adverse event column 17 | #' @param by Variable to split results by, e.g. report AEs by grade 18 | #' @param strata Variable to stratify results by, e.g. report AEs summaries 19 | #' by treatment group 20 | #' @param id_df Optional data frame of complete id values and strata to achieve correct 21 | #' base n for the situation in which not all subjects experience adverse events. See 22 | #' \code{\link{df_patient_characteristics}} for an example `id_df` that pairs with 23 | #' \code{\link{df_adverse_events}}. 24 | #' @param by_values Optional vector of complete by values, listed in desired order, 25 | #' to achieve correct table structure for the situation in which an adverse 26 | #' event of a certain grade is not observed for a given soc 27 | #' @param missing_location location where the column summarizing values with 28 | #' missing levels `by=` will be located in the final table. Must be 29 | #' one of `c("first", "last", "hide)`. Default is `"first"` 30 | #' @param statistic String indicating the statistics that will be reported. 31 | #' The default is `"{n} ({p})"` 32 | #' @param zero_symbol String used to represent cells with zero counts. Default 33 | #' is the em-dash (`"\U2014"`). Using `zero_symbol = NULL` will print the 34 | #' zero count statistics, e.g. `"0 (0)"` 35 | #' @param sort Controls order of AEs and SOCs in output table. 36 | #' The default is `NULL`, where AEs and SOCs are sorted alphanumerically 37 | #' (and factors sorted according to their factor level). 38 | #' Use `sort = "ae"` to sort AEs in decreasing frequency order, `sort = "soc"` 39 | #' to sort SOCs in decreasing order, and `sort = c("ae", "soc")` to sort both. 40 | #' AEs are sorted within SOC. 41 | #' @param digits Specifies the number of decimal places to round the summary statistics. 42 | #' By default integers are shown to zero decimal places, and percentages are 43 | #' formatted with `style_percent()`. If you would like to modify either 44 | #' of these, pass a vector of integers indicating the number of decimal 45 | #' places to round the statistics. For example, if the statistic being 46 | #' calculated is `"{n} ({p}%)"` and you want the percent rounded to 47 | #' 2 decimal places use `digits = c(0, 2)`. 48 | #' User may also pass a styling function: `digits = style_sigfig` 49 | #' 50 | #' @return a 'tbl_ae' object 51 | #' @export 52 | #' 53 | #' @examples 54 | #' \donttest{ 55 | #' # Example 1 ----------------------------------------------------------------- 56 | #' tbl_ae_ex1 <- 57 | #' df_adverse_events %>% 58 | #' tbl_ae( 59 | #' id = patient_id, 60 | #' ae = adverse_event, 61 | #' soc = system_organ_class, 62 | #' by = grade, 63 | #' strata = trt 64 | #' ) %>% 65 | #' modify_header(all_ae_cols() ~ "**Grade {by}**") 66 | #' 67 | #' # Example 2 ----------------------------------------------------------------- 68 | #' tbl_ae_ex2 <- 69 | #' df_adverse_events %>% 70 | #' tbl_ae( 71 | #' id = patient_id, 72 | #' ae = adverse_event, 73 | #' by = grade 74 | #' ) %>% 75 | #' modify_header(all_ae_cols() ~ "**Grade {by}**") 76 | #' } 77 | #' @section Example Output: 78 | #' \if{html}{Example 1} 79 | #' 80 | #' \if{html}{\figure{tbl_ae_ex1.png}{options: width=95\%}} 81 | #' 82 | #' \if{html}{Example 2} 83 | #' 84 | #' \if{html}{\figure{tbl_ae_ex2.png}{options: width=65\%}} 85 | #' 86 | #' @usage 87 | #' tbl_ae( 88 | #' data, 89 | #' id, 90 | #' ae, 91 | #' soc = NULL, 92 | #' by = NULL, 93 | #' strata = NULL, 94 | #' id_df = NULL, 95 | #' statistic = "{n} ({p})", 96 | #' by_values = NULL, 97 | #' digits = NULL, 98 | #' sort = NULL, 99 | #' zero_symbol = "\U2014", 100 | #' missing_location = c("first", "last", "hide") 101 | #' ) 102 | 103 | tbl_ae <- function(data, 104 | id, 105 | ae, 106 | soc = NULL, 107 | by = NULL, 108 | strata = NULL, 109 | id_df = NULL, 110 | statistic = "{n} ({p})", 111 | by_values = NULL, 112 | digits = NULL, 113 | sort = NULL, 114 | zero_symbol = "\U2014", 115 | missing_location = c("first", "last", "hide")) { 116 | # evaluate bare selectors/check inputs --------------------------------------- 117 | if(!inherits(data, "data.frame")) { 118 | stop("`data=` argument must be a tibble or data frame.", call. = FALSE) 119 | } 120 | if (!is.null(sort)) { 121 | sort <- match.arg(sort, choices = c("ae", "soc"), several.ok = TRUE) 122 | } 123 | missing_location <- match.arg(missing_location) 124 | 125 | id <- 126 | .select_to_varnames({{ id }}, data = data, 127 | arg_name = "id", select_single = TRUE) 128 | ae <- 129 | .select_to_varnames({{ ae }}, data = data, 130 | arg_name = "ae", select_single = TRUE) 131 | soc <- 132 | .select_to_varnames({{ soc }}, data = data, 133 | arg_name = "soc", select_single = TRUE) 134 | by <- 135 | .select_to_varnames({{ by }}, data = data, 136 | arg_name = "by", select_single = TRUE) 137 | strata <- 138 | .select_to_varnames({{ strata }}, data = data, 139 | arg_name = "strata", select_single = TRUE) 140 | if (is.null(id) || is.null(ae)) { 141 | stop("Arguments `id=` and `ae=` must be specified.", call. = FALSE) 142 | } 143 | 144 | # will return inputs --------------------------------------------------------- 145 | tbl_ae_inputs <- as.list(environment()) 146 | 147 | # obtain the complete data --------------------------------------------------- 148 | data_complete <- 149 | .complete_ae_data(data, id = id, ae = ae, soc = soc, by = by, 150 | strata = strata, id_df = id_df, by_values = by_values, 151 | missing_location = missing_location) 152 | 153 | # tabulate SOC --------------------------------------------------------------- 154 | if (!is.null(soc)) { 155 | tbl_soc <- 156 | .construct_summary_table(data = data_complete %>% filter(.data$..soc..), 157 | variable = "soc", 158 | digits = digits, 159 | statistic = statistic, 160 | sort = sort, 161 | zero_symbol = zero_symbol, 162 | missing_location = missing_location) 163 | } 164 | 165 | # tabulate AE ---------------------------------------------------------------- 166 | tbl_ae <- 167 | .construct_summary_table(data = data_complete %>% filter(.data$..ae..), 168 | variable = "ae", 169 | digits = digits, 170 | statistic = statistic, 171 | sort = sort, 172 | zero_symbol = zero_symbol, 173 | missing_location = missing_location) 174 | 175 | # combine the SOC and AE tbls ------------------------------------------------ 176 | tbl_final <- 177 | .combine_soc_and_ae_tbls( 178 | data = data_complete, 179 | tbl_ae = tbl_ae, 180 | tbl_soc = switch("soc" %in% names(data_complete), tbl_soc) 181 | ) 182 | 183 | # update `modify_stat_*` columns in `tbl$table_styling$header` --------------- 184 | tbl_final <- .update_modify_stat_columns(tbl = tbl_final, data = data_complete) 185 | 186 | # return final tbl ----------------------------------------------------------- 187 | tbl_final %>% 188 | purrr::compact() %>% 189 | # add inputs 190 | purrr::list_modify(inputs = tbl_ae_inputs) %>% 191 | # add class 192 | structure(class = c("tbl_ae", "gtsummary")) %>% 193 | # add default spanning headers 194 | .when( 195 | !is.null(strata) ~ 196 | modify_spanning_header(., gtsummary::all_stat_cols() ~ "**{strata}**, N = {n}"), 197 | TRUE ~ modify_spanning_header(., gtsummary::all_stat_cols() ~ "**N = {N}**") 198 | ) 199 | } 200 | -------------------------------------------------------------------------------- /R/tbl_ae_count.R: -------------------------------------------------------------------------------- 1 | #' Tabulate Raw AE Counts 2 | #' 3 | #' Create a table counting all AEs. 4 | #' 5 | #' \code{tbl_ae_count} counts all AEs (whereas \code{\link{tbl_ae}} 6 | #' counts by maximum grade). Thus, \code{tbl_ae_count} does 7 | #' not provide percentages as multiple AEs can be counted per subject. 8 | #' 9 | #' @inheritParams tbl_ae 10 | #' 11 | #' @return a 'tbl_ae_count' object 12 | #' @export 13 | #' @seealso \code{\link{tbl_ae}} 14 | #' @examples 15 | #' \donttest{ 16 | #' # Example 1 ----------------------------------------------------------------- 17 | #' tbl_ae_count_ex1 <- 18 | #' tbl_ae_count( 19 | #' data = df_adverse_events, 20 | #' ae = adverse_event, 21 | #' soc = system_organ_class, 22 | #' strata = trt, 23 | #' by = grade 24 | #' ) %>% 25 | #' modify_header(all_ae_cols() ~ "**Grade {by}**") 26 | #' } 27 | #' @section Example Output: 28 | #' \if{html}{Example 1} 29 | #' 30 | #' \if{html}{\figure{tbl_ae_count_ex1.png}{options: width=90\%}} 31 | #' 32 | #' @usage 33 | #' tbl_ae_count( 34 | #' data, 35 | #' ae, 36 | #' soc = NULL, 37 | #' by = NULL, 38 | #' strata = NULL, 39 | #' by_values = NULL, 40 | #' digits = NULL, 41 | #' sort = NULL, 42 | #' zero_symbol = "\U2014", 43 | #' missing_location = c("first", "last", "hide") 44 | #' ) 45 | 46 | tbl_ae_count <- function(data, 47 | ae, 48 | soc = NULL, 49 | by = NULL, 50 | strata = NULL, 51 | by_values = NULL, 52 | digits = NULL, 53 | sort = NULL, 54 | zero_symbol = "\U2014", 55 | missing_location = c("first", "last", "hide")) { 56 | # evaluate bare selectors/check inputs --------------------------------------- 57 | if(!inherits(data, "data.frame")) { 58 | stop("`data=` argument must be a tibble or data frame.", call. = FALSE) 59 | } 60 | if (!is.null(sort)) { 61 | sort <- match.arg(sort, choices = c("ae", "soc"), several.ok = TRUE) 62 | } 63 | missing_location <- match.arg(missing_location) 64 | 65 | ae <- 66 | .select_to_varnames({{ ae }}, data = data, 67 | arg_name = "ae", select_single = TRUE) 68 | soc <- 69 | .select_to_varnames({{ soc }}, data = data, 70 | arg_name = "soc", select_single = TRUE) 71 | by <- 72 | .select_to_varnames({{ by }}, data = data, 73 | arg_name = "by", select_single = TRUE) 74 | strata <- 75 | .select_to_varnames({{ strata }}, data = data, 76 | arg_name = "strata", select_single = TRUE) 77 | 78 | if (is.null(ae)) { 79 | stop("Argument `ae=` must be specified.", call. = FALSE) 80 | } 81 | 82 | # will return inputs --------------------------------------------------------- 83 | tbl_ae_count_inputs <- as.list(environment()) 84 | 85 | # adding default statistic --------------------------------------------------- 86 | statistic <- "{n}" 87 | missing_text <- "Unknown" 88 | 89 | # obtain the complete data --------------------------------------------------- 90 | data_complete <- 91 | data %>% 92 | dplyr::mutate(.......gtreg_id_for_tbl_ae_count....... = 1) %>% 93 | .complete_ae_data(id = ".......gtreg_id_for_tbl_ae_count.......", 94 | ae = ae, soc = soc, by = by, 95 | strata = strata, id_df = NULL, by_values = by_values, 96 | missing_location = missing_location) %>% 97 | dplyr::mutate(across(any_of(c("..soc..", "..ae..")), ~TRUE)) 98 | 99 | # tabulate SOC --------------------------------------------------------------- 100 | if (!is.null(soc)) { 101 | tbl_soc <- 102 | .construct_summary_table(data = data_complete %>% filter(.data$..soc..), 103 | variable = "soc", 104 | digits = digits, 105 | statistic = statistic, 106 | sort = sort, 107 | zero_symbol = zero_symbol, 108 | missing_location = missing_location) 109 | } 110 | 111 | # tabulate AE ---------------------------------------------------------------- 112 | tbl_ae <- 113 | .construct_summary_table(data = data_complete %>% filter(.data$..ae..), 114 | variable = "ae", 115 | digits = digits, 116 | statistic = statistic, 117 | sort = sort, 118 | zero_symbol = zero_symbol, 119 | missing_location = missing_location) 120 | 121 | 122 | # combine the SOC and AE tbls ------------------------------------------------ 123 | tbl_final <- 124 | .combine_soc_and_ae_tbls( 125 | data = data_complete, 126 | tbl_ae = tbl_ae, 127 | tbl_soc = switch("soc" %in% names(data_complete), tbl_soc) 128 | ) 129 | 130 | # update `modify_stat_*` columns in `tbl$table_styling$header` --------------- 131 | tbl_final <- .update_modify_stat_columns(tbl = tbl_final, data = data_complete) 132 | 133 | # return final tbl ----------------------------------------------------------- 134 | tbl_final %>% 135 | purrr::compact() %>% 136 | # add inputs 137 | purrr::list_modify(inputs = tbl_ae_count_inputs) %>% 138 | # add class 139 | structure(class = c("tbl_ae_count", "gtsummary")) %>% 140 | # add default spanning headers 141 | .when( 142 | !is.null(strata) ~ 143 | modify_spanning_header( 144 | ., gtsummary::all_stat_cols() ~ "**{strata}**"), 145 | TRUE ~ . 146 | ) 147 | } 148 | -------------------------------------------------------------------------------- /R/tbl_ae_focus.R: -------------------------------------------------------------------------------- 1 | #' Tabulate AE Focused (Dichotomous) Summaries 2 | #' 3 | #' Summarize dichotomous AE data. For example, report the 4 | #' rate of patients that have an AE of Grade 3 or higher. 5 | #' 6 | #' @inheritParams tbl_ae 7 | #' @param include Vector of column names to summarize. Column names may be 8 | #' quoted or unquoted. All columns must be class 'logical'. 9 | #' @param label A named list of labels that will be applied in the 10 | #' resulting table. Names must be those passed in `include=`. Default is 11 | #' NULL, and either the label attribute or the column name will be used. 12 | #' 13 | #' @return a 'tbl_ae_focus' object 14 | #' @export 15 | #' @examples 16 | #' \donttest{ 17 | #' # Example 1 ----------------------------------------------------------------- 18 | #' tbl_ae_focus_ex1 <- 19 | #' df_adverse_events %>% 20 | #' tbl_ae_focus( 21 | #' include = c(any_complication, grade3_complication), 22 | #' id = patient_id, 23 | #' ae = adverse_event, 24 | #' soc = system_organ_class, 25 | #' label = 26 | #' list(any_complication = "Any Grade Complication", 27 | #' grade3_complication = "Grade 3+ Complication") 28 | #' ) %>% 29 | #' bold_labels() 30 | #' } 31 | #' @section Example Output: 32 | #' \if{html}{Example 1} 33 | #' 34 | #' \if{html}{\figure{tbl_ae_focus_ex1.png}{options: width=75\%}} 35 | #' 36 | #' @usage 37 | #' tbl_ae_focus( 38 | #' data, 39 | #' include, 40 | #' id, 41 | #' ae, 42 | #' soc = NULL, 43 | #' strata = NULL, 44 | #' label = NULL, 45 | #' id_df = NULL, 46 | #' statistic = "{n} ({p})", 47 | #' digits = NULL, 48 | #' sort = NULL, 49 | #' zero_symbol = "\U2014" 50 | #' ) 51 | 52 | 53 | tbl_ae_focus <- function(data, 54 | include, 55 | id, 56 | ae, 57 | soc = NULL, 58 | strata = NULL, 59 | label = NULL, 60 | id_df = NULL, 61 | statistic = "{n} ({p})", 62 | digits = NULL, 63 | sort = NULL, 64 | zero_symbol = "\U2014") { 65 | # evaluate bare selectors/check inputs --------------------------------------- 66 | if(!inherits(data, "data.frame")) { 67 | stop("`data=` argument must be a tibble or data frame.", call. = FALSE) 68 | } 69 | if (!is.null(sort)) { 70 | sort <- match.arg(sort, choices = c("ae", "soc"), several.ok = TRUE) 71 | } 72 | 73 | id <- 74 | .select_to_varnames({{ id }}, data = data, 75 | arg_name = "id", select_single = TRUE) 76 | ae <- 77 | .select_to_varnames({{ ae }}, data = data, 78 | arg_name = "ae", select_single = TRUE) 79 | soc <- 80 | .select_to_varnames({{ soc }}, data = data, 81 | arg_name = "soc", select_single = TRUE) 82 | strata <- 83 | .select_to_varnames({{ strata }}, data = data, 84 | arg_name = "strata", select_single = TRUE) 85 | include <- 86 | .select_to_varnames({{ include }}, data = data, 87 | arg_name = "include", select_single = FALSE) 88 | label <- 89 | .formula_list_to_named_list(x = {{ label }}, 90 | data = data, 91 | arg_name = "label", 92 | type_check = rlang::is_string) 93 | if (is.null(include) || is.null(id) || is.null(ae)) { 94 | stop("Arguments `include=`, `id=`, `ae=` must be specified.", call. = FALSE) 95 | } 96 | 97 | purrr::walk( 98 | include, 99 | ~switch(!is.logical(data[[.x]]), 100 | stop("Columns indicated in `include=` must be class 'logical'.", call. = FALSE))) 101 | purrr::walk( 102 | include, 103 | ~switch(any(is.na(data[[.x]])), 104 | stop("Columns indicated in `include=` cannot be NA.", call. = FALSE))) 105 | 106 | # will return inputs --------------------------------------------------------- 107 | tbl_ae_focus_inputs <- as.list(environment()) 108 | 109 | # obtain the complete data --------------------------------------------------- 110 | lst_data_complete <- 111 | include %>% 112 | purrr::map( 113 | ~.complete_ae_data(data, id = id, ae = ae, soc = soc, by = .x, 114 | strata = strata, id_df = id_df, by_values = c("FALSE", "TRUE")) 115 | ) %>% 116 | stats::setNames(include) 117 | 118 | 119 | # tabulate SOC --------------------------------------------------------------- 120 | if (!is.null(soc)) { 121 | tbl_soc <- 122 | lst_data_complete %>% 123 | purrr::imap( 124 | ~.construct_summary_table( 125 | data = filter(.x, .data$..soc..), 126 | variable = "soc", 127 | by = "by", 128 | digits = digits, 129 | statistic = statistic, 130 | sort = sort, 131 | zero_symbol = zero_symbol, 132 | missing_location = "first", 133 | columns_to_hide = c("NOT OBSERVED", "FALSE"), 134 | header = glue::glue("**{label[[.y]] %||% attr(data[[.y]], 'label') %||% .y}**") 135 | ) 136 | ) %>% 137 | gtsummary::tbl_merge(tab_spanner = FALSE) 138 | } 139 | 140 | 141 | # tabulate AE ---------------------------------------------------------------- 142 | tbl_ae <- 143 | lst_data_complete %>% 144 | purrr::imap( 145 | ~.construct_summary_table( 146 | data = filter(.x, .data$..ae..), 147 | variable = "ae", 148 | by = "by", 149 | digits = digits, 150 | statistic = statistic, 151 | sort = sort, 152 | zero_symbol = zero_symbol, 153 | missing_location = "first", 154 | columns_to_hide = c("NOT OBSERVED", "FALSE"), 155 | header = glue::glue("**{label[[.y]] %||% attr(data[[.y]], 'label') %||% .y}**") 156 | ) 157 | ) %>% 158 | gtsummary::tbl_merge(tab_spanner = FALSE) 159 | 160 | # combine the SOC and AE tbls ------------------------------------------------ 161 | tbl_final <- 162 | .combine_soc_and_ae_tbls( 163 | data = lst_data_complete[[1]], 164 | tbl_ae = tbl_ae, 165 | tbl_soc = switch(!is.null(soc), tbl_soc) 166 | ) 167 | 168 | # update `modify_stat_*` columns in `tbl$table_styling$header` --------------- 169 | tbl_final <- .update_modify_stat_columns(tbl = tbl_final, data = lst_data_complete[[1]]) 170 | 171 | # re-order the columns to group strata together ------------------------------ 172 | tbl_final <- .group_strata_columns(tbl_final) 173 | 174 | # return final tbl ----------------------------------------------------------- 175 | tbl_final %>% 176 | purrr::compact() %>% 177 | # add inputs 178 | purrr::list_modify(inputs = tbl_ae_focus_inputs) %>% 179 | # add class 180 | structure(class = c("tbl_ae_focus", "gtsummary")) %>% 181 | # add default spanning headers 182 | .when( 183 | !is.null(strata) ~ 184 | modify_spanning_header(., gtsummary::all_stat_cols() ~ "**{strata}**, N = {n}"), 185 | TRUE ~ modify_spanning_header(., gtsummary::all_stat_cols() ~ "**N = {N}**") 186 | ) 187 | } 188 | 189 | .group_strata_columns <- function(tbl) { 190 | # if not strata, then skip this step 191 | if (!"modify_selector_strata" %in% names(tbl$table_styling$header)) 192 | return(tbl) 193 | 194 | # identify column names and group them by strata level 195 | stat_columns_group_by_strata <- 196 | tbl$table_styling$header %>% 197 | select(all_of(c("column", "modify_selector_strata"))) %>% 198 | filter(startsWith(.data$column, "stat_")) %>% 199 | mutate(strata_order = factor(.data$modify_selector_strata, levels = unique(.data$modify_selector_strata))) %>% 200 | dplyr::arrange(.data$strata_order) %>% 201 | dplyr::pull("column") 202 | 203 | # return tbl with columns re-ordered 204 | tbl %>% 205 | gtsummary::modify_table_body( 206 | ~ .x %>% 207 | dplyr::relocate(all_of(stat_columns_group_by_strata), .after = "label") 208 | ) 209 | } 210 | 211 | -------------------------------------------------------------------------------- /R/tbl_listing.R: -------------------------------------------------------------------------------- 1 | #' Data Listing Table 2 | #' 3 | #' Function creates a gtsummary-class listing of data. Column labels are 4 | #' used as column headers, when present. 5 | #' The listing prints observations in the order of the input data. 6 | #' 7 | #' @param data a data frame 8 | #' @param bold_headers logical indicating whether to bold column headers. 9 | #' Default is `TRUE` 10 | #' @param group_by Single variable name indicating a grouping variable. 11 | #' Default is `NULL` for no grouping variable. When specified, a grouping 12 | #' row will be added to the first column. See details below. 13 | #' 14 | #' @section group_by: 15 | #' 16 | #' The grouping column and the first column in the table will be combined 17 | #' and the type/class may be converted to common type/class for both columns. 18 | #' However, if either the `group_by=` column or the first column are factors, 19 | #' the factor column(s) will first be converted to character. 20 | #' 21 | #' The groups are ordered according to the grouping 22 | #' variable's type (i.e., character, numeric, or factor). 23 | #' 24 | #' @section Details: 25 | #' 26 | #' The purpose of `tbl_listing()` is to add support for printing data frames, 27 | #' while taking advantage of the \{gtsummary\} defaults, e.g. ability to print 28 | #' to most output formats, using print themes to have a common style to all 29 | #' tables in a document, etc. 30 | #' 31 | #' While the output of `tbl_listing()` is class `'gtsummary'`, these tables 32 | #' are not meant to be merged with other `'gtsummary'` tables with `tbl_merge()`, 33 | #' or reporting table contents with `inline_text()`. The reason is that a 34 | #' proper `'gtsummary'` contains 35 | #' [additional, hidden structure](https://www.danieldsjoberg.com/gtsummary/articles/gtsummary_definition.html) 36 | #' not present in the result of `tbl_listing()`. If you do need to report 37 | #' the results of `tbl_listing()` in-line, it's recommended to convert 38 | #' the table to a data frame, then extract the needed cell, e.g. 39 | #' 40 | #' ```r 41 | #' tbl_listing() |> 42 | #' as_tibble(col_names = FALSE) |> 43 | #' dplyr::slice(1) |> 44 | #' dplyr::pull(colname)` 45 | #' ```` 46 | #' 47 | #' @return gtsummary data listing 48 | #' @export 49 | #' 50 | #' @examples 51 | #' library(dplyr, warn.conflicts = FALSE) 52 | #' 53 | #' tbl_listing_ex1 <- 54 | #' head(df_adverse_events, n = 10) %>% 55 | #' select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 56 | #' arrange(adverse_event, desc(grade)) %>% 57 | #' tbl_listing(group_by = system_organ_class) %>% 58 | #' bold_labels() 59 | #' 60 | #' set.seed(11234) 61 | #' tbl_listing_ex2 <- 62 | #' df_patient_characteristics %>% 63 | #' dplyr::slice_sample(n = 10) %>% 64 | #' select(patient_id, status, discontinued, off_trt_ae) %>% 65 | #' tbl_listing() %>% 66 | #' as_gt() %>% 67 | #' gt::opt_row_striping() 68 | #' 69 | #' @section Example Output: 70 | #' \if{html}{Example 1} 71 | #' 72 | #' \if{html}{\figure{tbl_listing_ex1.png}{options: width=65\%}} 73 | #' 74 | #' \if{html}{Example 2} 75 | #' 76 | #' \if{html}{\figure{tbl_listing_ex2.png}{options: width=75\%}} 77 | tbl_listing <- function(data, group_by = NULL, bold_headers = TRUE) { 78 | # process inputs ------------------------------------------------------------- 79 | stopifnot(is.data.frame(data)) 80 | data <- 81 | dplyr::ungroup(data) %>% 82 | dplyr::mutate(row_type = "level", .before = 1L) 83 | 84 | group_by <- 85 | .select_to_varnames( 86 | select = {{ group_by }}, 87 | data = data, 88 | arg_name = "group_by", 89 | select_single = TRUE 90 | ) 91 | 92 | # add a grouping row if specified, add the grouping rows to the data frame --- 93 | if (!is.null(group_by)) { 94 | first_column <- names(data) %>% setdiff(c(group_by, "row_type")) %>% purrr::pluck(1) 95 | 96 | # adding grouped row ------------------------------------------------------- 97 | data <- 98 | data %>% 99 | dplyr::group_by(.data[[group_by]]) %>% 100 | dplyr::group_map( 101 | function(.x, .y) { 102 | rbind( 103 | # creating a 1 row data frame to stack with the primary data set 104 | rlang::set_names(.y, first_column) %>% 105 | mutate( 106 | dplyr::across(where(is.factor) & all_of(first_column), as.character), # rbind() cannot stack a factor column and numeric column, must convert to chr 107 | row_type = "label", 108 | .before = 1L 109 | ) %>% 110 | {cbind(., rlang::inject(tibble::tibble(!!!(rep_len(list(NA), length.out = length(setdiff(names(.x), c("row_type", first_column)))) %>% 111 | stats::setNames(setdiff(names(.x), c("row_type", first_column)))))))}, 112 | as.data.frame(.x) %>% 113 | mutate(dplyr::across(where(is.factor) & all_of(first_column), as.character)) 114 | ) 115 | } 116 | ) %>% 117 | dplyr::bind_rows() %>% 118 | # re-instating column labels 119 | purrr::imap_dfr( 120 | function(.x, .y) { 121 | attr(.x, "label") <- attr(data[[.y]], "label") 122 | .x 123 | } 124 | ) 125 | } 126 | 127 | 128 | # convert data frame to gtsummary table -------------------------------------- 129 | tbl <- 130 | gtsummary::.create_gtsummary_object(table_body = data) %>% 131 | gtsummary::modify_column_unhide(columns = -any_of("row_type")) %>% 132 | gtsummary::modify_column_alignment(columns = everything(), align = "left") %>% 133 | gtsummary::modify_column_indent(columns = dplyr::everything(), indent = 0) 134 | 135 | # indenting levels if there is a grouping variable --------------------------- 136 | if (!is.null(group_by)) { 137 | tbl <- 138 | gtsummary::modify_table_styling( 139 | x = tbl, 140 | columns = all_of(first_column), 141 | rows = .data$row_type == "level", 142 | indent = 4L 143 | ) 144 | } 145 | 146 | # add column labels --------------------------------------------------------- 147 | tbl$table_styling$header <- 148 | tbl$table_styling$header %>% 149 | dplyr::rowwise() %>% 150 | dplyr::mutate( 151 | label = attr(data[[.data$column]], "label") %||% .data$column 152 | ) %>% 153 | dplyr::ungroup() 154 | 155 | 156 | # add markdown bold syntax --------------------------------------------------- 157 | if (isTRUE(bold_headers)) { 158 | tbl$table_styling$header$label <- 159 | paste0("**", tbl$table_styling$header$label, "**") 160 | } 161 | 162 | # return gtsummary tbl ------------------------------------------------------- 163 | tbl %>% 164 | structure(class = c("tbl_listing", "gtsummary")) 165 | } 166 | -------------------------------------------------------------------------------- /R/tbl_reg_summary.R: -------------------------------------------------------------------------------- 1 | #' Data Summary Table 2 | #' 3 | #' Function wraps `gtsummary::tbl_summary()` to create a data summary 4 | #' table often seen in regulatory submissions. Continuous variable summaries 5 | #' are shown on multiple lines with additional summary statistics and percentages 6 | #' are shown for categorical variables; precision levels estimated based on values observed. 7 | #' 8 | #' @param by A column name (quoted or unquoted) in `data.` Summary statistics 9 | #' will be calculated separately for each level of the by variable 10 | #' (e.g. `by = trt`). If `NULL`, summary statistics are calculated using all observations. 11 | #' @param statistic List of formulas specifying types of summary statistics 12 | #' to display for each variable. 13 | #' @param type List of formulas specifying variable types. 14 | #' Accepted values are `c("continuous", "continuous2", "categorical", "dichotomous")`, 15 | #' e.g. `type = list(age ~ "continuous", female ~ "dichotomous")`. 16 | #' If type not specified for a variable, the function will default to an appropriate summary type. 17 | #' @param value List of formulas specifying the value to display for dichotomous 18 | #' variables. gtsummary selectors, e.g. `all_dichotomous()`, cannot be used with this argument. 19 | #' @inheritParams gtsummary::tbl_summary 20 | #' 21 | #' @return a 'tbl_reg_summary' object 22 | #' @export 23 | #' 24 | #' @seealso See \href{https://www.danieldsjoberg.com/gtsummary/reference/tbl_summary.html}{`gtsummary::tbl_summary()`} help file 25 | #' @seealso See \href{https://www.danieldsjoberg.com/gtsummary/articles/tbl_summary.html}{vignette} for detailed tutorial 26 | #' @examples 27 | #' tbl_reg_summary_ex1 <- 28 | #' df_patient_characteristics %>% 29 | #' tbl_reg_summary(by = trt, include = c(marker, status)) 30 | #' 31 | #' @section Example Output: 32 | #' \if{html}{Example 1} 33 | #' 34 | #' \if{html}{\figure{tbl_reg_summary_ex1.png}{options: width=65\%}} 35 | tbl_reg_summary <- function(data, 36 | by = NULL, 37 | label = NULL, 38 | statistic = 39 | list(all_continuous() ~ c("{N_nonmiss}", "{mean} ({sd})", "{median} ({p25}, {p75})", "{min}, {max}", "{N_miss}"), 40 | all_categorical() ~ "{n} ({p}%)"), 41 | digits = NULL, 42 | type = NULL, 43 | value = NULL, 44 | missing = c("no", "yes", "ifany"), 45 | missing_text = "Unknown", 46 | missing_stat = "{N_miss}", 47 | sort = all_categorical(FALSE) ~ "alphanumeric", 48 | percent = c("column", "row", "cell"), 49 | include = everything()) { 50 | missing <- match.arg(missing) 51 | percent <- match.arg(percent) 52 | 53 | # execute `tbl_summary()` code with gtreg theme/defaults --------------------- 54 | gtsummary::with_gtsummary_theme( 55 | x = gtreg_theme, 56 | expr = 57 | gtsummary::tbl_summary( 58 | data = data, by = {{ by }}, label = label, statistic = statistic, 59 | digits = digits, type = type, value = value, missing = missing, 60 | missing_text = missing_text, sort = sort, percent = percent, 61 | include = {{ include }} 62 | ), 63 | msg_ignored_elements = 64 | paste("Theme element(s) {.val {elements}} utilized internally", 65 | "by {.code tbl_reg_summary()} and cannot be modified.\n", 66 | "Use {.code gtsummary::tbl_summary()} if you", 67 | "wish to modify these theme elements.") 68 | ) %>% 69 | structure(class = c("tbl_reg_summary", "tbl_summary", "gtsummary")) 70 | } 71 | 72 | # creating theme for gtreg summaries ------------------------------------------- 73 | gtreg_theme <- 74 | list( 75 | "tbl_summary-str:default_con_type" = "continuous2" 76 | ) 77 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | # gtreg 17 | 18 | 19 | 20 | [![R-CMD-check](https://github.com/shannonpileggi/gtreg/workflows/R-CMD-check/badge.svg)](https://github.com/shannonpileggi/gtreg/actions) 21 | [![Codecov test coverage](https://codecov.io/gh/shannonpileggi/gtreg/branch/main/graph/badge.svg)](https://app.codecov.io/gh/shannonpileggi/gtreg?branch=main) 22 | [![CRAN status](https://www.r-pkg.org/badges/version/gtreg)](https://CRAN.R-project.org/package=gtreg) [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) 23 | 24 | 25 | 26 | ## Installation 27 | 28 | You can install {gtreg} with the following code. 29 | 30 | ``` r 31 | install.packages("gtreg") 32 | ``` 33 | 34 | You can install the development version of {gtreg} from [GitHub](https://github.com/shannonpileggi/gtreg) with: 35 | 36 | ``` r 37 | # install.packages("devtools") 38 | devtools::install_github("shannonpileggi/gtreg") 39 | ``` 40 | 41 | ## Overview 42 | 43 | The {gtreg} package creates tabular data summaries appropriate for regulatory submissions. 44 | The package builds the tables using {gtsummary}. 45 | 46 | 47 | Here are [slides](https://shannonpileggi.github.io/introducing-gtreg-rmed-2022/#/title-slide) and a [recorded talk (17 min)](https://youtu.be/DmslEfczYqM) from "Introducing {gtreg}: an R package to produce regulatory tables for clinical research" presented at the 2022 R in Medicine conference. 48 | 49 | [![Screenshot of YouTube recording on title slide](man/figures/Introducing-gtreg-screenshot.png){width=70%}](https://youtu.be/DmslEfczYqM) 50 | 51 | ## Functions for adverse event (AE) reporting 52 | 53 | **Summarize Raw Adverse Counts** 54 | 55 | `tbl_ae_count()` provides counts of all AEs, and omits percentage statistics as multiple AEs 56 | can occur per subject. 57 | 58 | 59 | ```{r example2} 60 | library(gtreg) 61 | tbl_ae_count <- 62 | df_adverse_events |> 63 | tbl_ae_count( 64 | ae = adverse_event, 65 | soc = system_organ_class, 66 | by = drug_attribution 67 | ) |> 68 | add_overall(across = "by") |> 69 | modify_spanning_header(all_ae_cols() ~ "**Drug Attribution**") |> 70 | bold_labels() 71 | ``` 72 | 73 | ```{r example-tbl_ae_count, include = FALSE} 74 | # Had to manually save images in temp file, not sure if better way. 75 | gt::gtsave(as_gt(tbl_ae_count), file = file.path(tempdir(), "temp2.png")) 76 | ``` 77 | 78 | ```{r out.width = "76%", echo = FALSE} 79 | # Have to do this workaround since the README needs markdown format for GitHub page but a different format for the website. 80 | if (identical(Sys.getenv("IN_PKGDOWN"), "true")) { 81 | as_gt(tbl) 82 | } else { 83 | knitr::include_graphics("man/figures/README-example-tbl_ae_count-1.png") 84 | } 85 | ``` 86 | 87 | **Summarize Adverse Events by Grade** 88 | 89 | `tbl_ae()` counts one AE per subject by maximum grade; percentage statistics are provided by default with 90 | the denominators reflecting the number of patients in the study. 91 | 92 | ```{r example} 93 | library(gtreg) 94 | gtsummary::theme_gtsummary_compact() 95 | 96 | tbl_ae <- 97 | df_adverse_events |> 98 | tbl_ae( 99 | id_df = df_patient_characteristics, 100 | id = patient_id, 101 | ae = adverse_event, 102 | soc = system_organ_class, 103 | by = grade, 104 | strata = trt 105 | ) |> 106 | modify_header(all_ae_cols() ~ "**Grade {by}**") |> 107 | bold_labels() 108 | ``` 109 | 110 | ```{r example-tbl_ae, include = FALSE} 111 | # Had to manually save images in temp file, not sure if better way. 112 | gt::gtsave(as_gt(tbl_ae), file = file.path(tempdir(), "temp.png")) 113 | ``` 114 | 115 | ```{r out.width = "100%", echo = FALSE} 116 | # Have to do this workaround since the README needs markdown format for GitHub page but a different format for the website. 117 | if (identical(Sys.getenv("IN_PKGDOWN"), "true")) { 118 | as_gt(tbl) 119 | } else { 120 | knitr::include_graphics("man/figures/README-example-tbl_ae-1.png") 121 | } 122 | ``` 123 | 124 | 125 | **Focus on rates of high grade complications** 126 | 127 | `tbl_ae_focus()` also counts one AE per subject by maximum grade, and is a convenience 128 | to summarize dichotomous AE attributes. 129 | 130 | 131 | ```{r example3} 132 | tbl_ae_focus <- 133 | df_adverse_events |> 134 | tbl_ae_focus( 135 | id_df = df_patient_characteristics, 136 | id = patient_id, 137 | ae = adverse_event, 138 | include = c(any_complication, grade3_complication) 139 | ) 140 | ``` 141 | 142 | ```{r example-tbl_ae_focus, include = FALSE} 143 | # Had to manually save images in temp file, not sure if better way. 144 | gt::gtsave(as_gt(tbl_ae_focus), file = file.path(tempdir(), "temp3.png")) 145 | ``` 146 | 147 | ```{r out.width = "62%", echo = FALSE} 148 | # Have to do this workaround since the README needs markdown format for GitHub page but a different format for the website. 149 | if (identical(Sys.getenv("IN_PKGDOWN"), "true")) { 150 | as_gt(tbl) 151 | } else { 152 | knitr::include_graphics("man/figures/README-example-tbl_ae_focus-1.png") 153 | } 154 | ``` 155 | 156 | ## Other Functions for Clinical Reporting 157 | 158 | **Regulatory summary** 159 | 160 | `tbl_reg_summary()` creates a data summary table often seen in regulatory submissions. 161 | 162 | ```{r example4} 163 | tbl_reg_summary <- 164 | df_patient_characteristics |> 165 | tbl_reg_summary(by = trt, include = c(marker, status)) 166 | ``` 167 | 168 | ```{r example-tbl_reg_summary, include = FALSE} 169 | # Had to manually save images in temp file, not sure if better way. 170 | gt::gtsave(as_gt(tbl_reg_summary), file = file.path(tempdir(), "temp4.png")) 171 | ``` 172 | 173 | 174 | ```{r out.width = "42%", echo = FALSE} 175 | # Have to do this workaround since the README needs markdown format for GitHub page but a different format for the website. 176 | if (identical(Sys.getenv("IN_PKGDOWN"), "true")) { 177 | as_gt(tbl) 178 | } else { 179 | knitr::include_graphics("man/figures/README-example-tbl_reg_summary-1.png") 180 | } 181 | ``` 182 | 183 | **Print an AE listing** 184 | 185 | `tbl_listing()` creates a gtsummary-class listing of data to enable grouped printing. 186 | 187 | ```{r example5} 188 | tbl_listing <- 189 | head(df_adverse_events, n = 10) |> 190 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) |> 191 | dplyr::arrange(adverse_event, desc(grade)) |> 192 | tbl_listing(group_by = system_organ_class) |> 193 | bold_labels() 194 | ``` 195 | 196 | ```{r example-tbl_listing, include = FALSE} 197 | # Had to manually save images in temp file, not sure if better way. 198 | gt::gtsave(as_gt(tbl_listing), file = file.path(tempdir(), "temp5.png")) 199 | ``` 200 | 201 | 202 | ```{r out.width = "62%", echo = FALSE} 203 | # Have to do this workaround since the README needs markdown format for GitHub page but a different format for the website. 204 | if (identical(Sys.getenv("IN_PKGDOWN"), "true")) { 205 | as_gt(tbl) 206 | } else { 207 | knitr::include_graphics("man/figures/README-example-tbl_listing-1.png") 208 | } 209 | ``` 210 | 211 | 212 | 213 | ## Code of Conduct 214 | 215 | Please note that the gtreg project is released with a [Contributor Code of Conduct](https://contributor-covenant.org/version/2/0/CODE_OF_CONDUCT.html). 216 | By contributing to this project, you agree to abide by its terms. 217 | 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # gtreg 5 | 6 | 7 | 8 | [![R-CMD-check](https://github.com/shannonpileggi/gtreg/workflows/R-CMD-check/badge.svg)](https://github.com/shannonpileggi/gtreg/actions) 9 | [![Codecov test 10 | coverage](https://codecov.io/gh/shannonpileggi/gtreg/branch/main/graph/badge.svg)](https://app.codecov.io/gh/shannonpileggi/gtreg?branch=main) 11 | [![CRAN 12 | status](https://www.r-pkg.org/badges/version/gtreg)](https://CRAN.R-project.org/package=gtreg) 13 | [![Lifecycle: 14 | experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) 15 | 16 | 17 | ## Installation 18 | 19 | You can install {gtreg} with the following code. 20 | 21 | ``` r 22 | install.packages("gtreg") 23 | ``` 24 | 25 | You can install the development version of {gtreg} from 26 | [GitHub](https://github.com/shannonpileggi/gtreg) with: 27 | 28 | ``` r 29 | # install.packages("devtools") 30 | devtools::install_github("shannonpileggi/gtreg") 31 | ``` 32 | 33 | ## Overview 34 | 35 | The {gtreg} package creates tabular data summaries appropriate for 36 | regulatory submissions. The package builds the tables using {gtsummary}. 37 | 38 | Here are 39 | [slides](https://shannonpileggi.github.io/introducing-gtreg-rmed-2022/#/title-slide) 40 | and a [recorded talk (17 min)](https://youtu.be/DmslEfczYqM) from 41 | “Introducing {gtreg}: an R package to produce regulatory tables for 42 | clinical research” presented at the 2022 R in Medicine conference. 43 | 44 | [Screenshot of YouTube recording on title slide](https://youtu.be/DmslEfczYqM) 47 | 48 | ## Functions for adverse event (AE) reporting 49 | 50 | **Summarize Raw Adverse Counts** 51 | 52 | `tbl_ae_count()` provides counts of all AEs, and omits percentage 53 | statistics as multiple AEs can occur per subject. 54 | 55 | ``` r 56 | library(gtreg) 57 | tbl_ae_count <- 58 | df_adverse_events |> 59 | tbl_ae_count( 60 | ae = adverse_event, 61 | soc = system_organ_class, 62 | by = drug_attribution 63 | ) |> 64 | add_overall(across = "by") |> 65 | modify_spanning_header(all_ae_cols() ~ "**Drug Attribution**") |> 66 | bold_labels() 67 | ``` 68 | 69 | 70 | 71 | **Summarize Adverse Events by Grade** 72 | 73 | `tbl_ae()` counts one AE per subject by maximum grade; percentage 74 | statistics are provided by default with the denominators reflecting the 75 | number of patients in the study. 76 | 77 | ``` r 78 | library(gtreg) 79 | gtsummary::theme_gtsummary_compact() 80 | #> Setting theme "Compact" 81 | 82 | tbl_ae <- 83 | df_adverse_events |> 84 | tbl_ae( 85 | id_df = df_patient_characteristics, 86 | id = patient_id, 87 | ae = adverse_event, 88 | soc = system_organ_class, 89 | by = grade, 90 | strata = trt 91 | ) |> 92 | modify_header(all_ae_cols() ~ "**Grade {by}**") |> 93 | bold_labels() 94 | ``` 95 | 96 | 97 | 98 | **Focus on rates of high grade complications** 99 | 100 | `tbl_ae_focus()` also counts one AE per subject by maximum grade, and is 101 | a convenience to summarize dichotomous AE attributes. 102 | 103 | ``` r 104 | tbl_ae_focus <- 105 | df_adverse_events |> 106 | tbl_ae_focus( 107 | id_df = df_patient_characteristics, 108 | id = patient_id, 109 | ae = adverse_event, 110 | include = c(any_complication, grade3_complication) 111 | ) 112 | ``` 113 | 114 | 115 | 116 | ## Other Functions for Clinical Reporting 117 | 118 | **Regulatory summary** 119 | 120 | `tbl_reg_summary()` creates a data summary table often seen in 121 | regulatory submissions. 122 | 123 | ``` r 124 | tbl_reg_summary <- 125 | df_patient_characteristics |> 126 | tbl_reg_summary(by = trt, include = c(marker, status)) 127 | #> Setting theme "Compact" 128 | ``` 129 | 130 | 131 | 132 | **Print an AE listing** 133 | 134 | `tbl_listing()` creates a gtsummary-class listing of data to enable 135 | grouped printing. 136 | 137 | ``` r 138 | tbl_listing <- 139 | head(df_adverse_events, n = 10) |> 140 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) |> 141 | dplyr::arrange(adverse_event, desc(grade)) |> 142 | tbl_listing(group_by = system_organ_class) |> 143 | bold_labels() 144 | ``` 145 | 146 | 147 | 148 | ## Code of Conduct 149 | 150 | Please note that the gtreg project is released with a [Contributor Code 151 | of 152 | Conduct](https://contributor-covenant.org/version/2/0/CODE_OF_CONDUCT.html). 153 | By contributing to this project, you agree to abide by its terms. 154 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://shannonpileggi.github.io/gtreg/ 2 | 3 | template: 4 | bootstrap: 5 5 | bootswatch: simplex 6 | bslib: 7 | primary: "#002D58" 8 | base_font: {google: "Atkinson Hyperlegible"} 9 | heading_font: {google: "Atkinson Hyperlegible"} 10 | text-muted: "#FFFFFF" 11 | 12 | authors: 13 | Shannon Pileggi: 14 | href: "https://www.pipinghotdata.com/" 15 | Daniel D. Sjoberg: 16 | href: "https://www.danieldsjoberg.com/" 17 | 18 | development: 19 | mode: auto 20 | version_label: default 21 | 22 | navbar: 23 | bg: primary 24 | left: 25 | - icon: fa-home 26 | href: index.html 27 | - text: Reference 28 | href: reference/index.html 29 | - text: Articles 30 | menu: 31 | - text: "Adverse Event Counting Methods" 32 | href: articles/counting-methods.html 33 | - text: "Output types" 34 | href: articles/output-gtreg.html 35 | - text: "Table modifications: headers, footnotes, and captions" 36 | href: articles/table-modifications.html 37 | - text: "Table shells" 38 | href: articles/table-shells.html 39 | - text: News 40 | href: news/index.html 41 | right: 42 | - icon: fa-github fa-lg 43 | href: https://github.com/shannonpileggi/gtreg 44 | 45 | reference: 46 | - title: Data Summaries 47 | - contents: 48 | - starts_with("tbl_") 49 | - starts_with("add_") 50 | - starts_with("inline_text") 51 | 52 | - title: Column Selectors 53 | - contents: 54 | - selectors 55 | 56 | - title: Utility Functions 57 | - contents: 58 | - starts_with("\\.") 59 | - style_xxx 60 | 61 | - title: Data 62 | - contents: 63 | - df_adverse_events 64 | - df_patient_characteristics 65 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 1% 9 | informational: true 10 | patch: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | informational: true 15 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Test environments 2 | 3 | * Ubuntu latest (on github actions), devel, release, oldrel-1, oldrel-2 4 | * Windows latest (on github actions), release 5 | * macOS latest (on github actions), release 6 | * win-builder devel 7 | 8 | ## R CMD check results 9 | 10 | 0 errors | 0 warnings | 0 notes 11 | 12 | Maintainer: 'Shannon Pileggi ' 13 | -------------------------------------------------------------------------------- /data-raw/DATASET.R: -------------------------------------------------------------------------------- 1 | set.seed(1122336654) 2 | n <- 50 3 | df_adverse_events <- 4 | tibble::tibble( 5 | patient_id = paste("ID", sample.int(10, n, replace = TRUE)), 6 | system_organ_class = "Blood and lymphatic system disorders", 7 | adverse_event = 8 | sample(c("Anaemia", "Increased tendency to bruise", 9 | "Iron deficiency anaemia", "Thrombocytopenia"), 10 | size = n, replace = TRUE), 11 | grade = sample.int(5, n, replace = TRUE) 12 | ) |> 13 | dplyr::bind_rows( 14 | tibble::tibble( 15 | patient_id = paste("ID", sample.int(10, n, replace = TRUE)), 16 | system_organ_class = "Gastrointestinal disorders", 17 | adverse_event = 18 | sample(c("Intestinal dilatation", "Myochosis", "Difficult digestion", 19 | "Pancreatic enzyme abnormality", "Non-erosive reflux disease"), 20 | size = n, replace = TRUE), 21 | grade = sample.int(5, n, replace = TRUE) 22 | ) 23 | ) |> 24 | dplyr::left_join( 25 | tibble::tibble( 26 | patient_id = paste("ID", 1:10), 27 | trt = sample(c("Drug A", "Drug B"), size = 10, replace = TRUE) 28 | ), 29 | by = "patient_id" 30 | ) |> 31 | dplyr::arrange(patient_id, system_organ_class, adverse_event) |> 32 | dplyr::mutate( 33 | drug_attribution = 34 | sample(c("Unrelated", "Unlikely", "Possible", "Probably", "Definite"), 35 | dplyr::n(), 36 | replace = TRUE) |> 37 | factor(levels = c("Unrelated", "Unlikely", "Possible", "Probably", "Definite")), 38 | any_complication = TRUE, 39 | grade3_complication = grade >= 3 40 | ) |> 41 | dplyr::select(patient_id, trt, dplyr::everything()) |> 42 | labelled::set_variable_labels( 43 | patient_id = "Patient ID", 44 | trt = "Treatment Group", 45 | system_organ_class = "System Organ Class", 46 | adverse_event = "Adverse Event", 47 | grade = "Grade", 48 | drug_attribution = "Drug Attribution", 49 | any_complication = "Any Grade Complication", 50 | grade3_complication = "Grade 3+ Complication" 51 | ) 52 | 53 | n <- 100 54 | df_patient_characteristics <- 55 | tibble::tibble( 56 | patient_id = paste("ID", 1:n), 57 | trt = sample(c("Drug A", "Drug B"), size = n, replace = TRUE), 58 | age = floor(rnorm(n, 50, 10)), 59 | marker = abs(-age / 33 + rnorm(n, 7, 1)), 60 | status = 61 | sample( 62 | x = c("Completed Study", "Adverse Event", "Progressive Disease", 63 | "Physician Decision", "Subject Withdrew", "Active"), 64 | size = n, 65 | replace = TRUE, 66 | prob = c(0.50, 0.1, 0.1, 0.1, 0.1, 0.1) 67 | ) |> 68 | factor(levels = c("Completed Study", "Adverse Event", "Progressive Disease", 69 | "Physician Decision", "Subject Withdrew", "Active")) 70 | ) |> 71 | dplyr::mutate( 72 | discontinued = 73 | ifelse(status %in% c("Completed Study", "Active"), "No", "Yes"), 74 | off_trt_ae = 75 | dplyr::case_when( 76 | status %in% "Progressive Disease" ~ 77 | sample(c("Brain Metastasis", "Bone Metastasis"), 78 | size = n, replace = TRUE), 79 | status %in% "Adverse Event" ~ 80 | sample(c("Intestinal dilatation", "Anaemia", "Increased tendency to bruise"), 81 | size = n, replace = TRUE), 82 | TRUE ~ NA_character_ 83 | ) 84 | ) |> 85 | # forcing the patients to have the same trt as in the AE dataset 86 | dplyr::rows_update( 87 | df_adverse_events |> 88 | dplyr::select(patient_id, trt) |> 89 | dplyr::distinct(), 90 | by = "patient_id" 91 | ) |> 92 | dplyr::select(patient_id, trt, dplyr::everything()) |> 93 | labelled::set_variable_labels( 94 | patient_id = "Patient ID", 95 | trt = "Treatment Group", 96 | age = "Patient Age", 97 | marker = "Biological Marker", 98 | status = "Study Status", 99 | discontinued = "Discontinued from Study", 100 | off_trt_ae = "Off Treatment Adverse Event" 101 | ) 102 | 103 | usethis::use_data(df_adverse_events, df_patient_characteristics, overwrite = TRUE) 104 | 105 | 106 | -------------------------------------------------------------------------------- /data-raw/img/gtreg_hex_hires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/gtreg_hex_hires.png -------------------------------------------------------------------------------- /data-raw/img/modify-t0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t0.png -------------------------------------------------------------------------------- /data-raw/img/modify-t1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t1.png -------------------------------------------------------------------------------- /data-raw/img/modify-t2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t2.png -------------------------------------------------------------------------------- /data-raw/img/modify-t3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t3.png -------------------------------------------------------------------------------- /data-raw/img/modify-t4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t4.png -------------------------------------------------------------------------------- /data-raw/img/modify-t5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t5.png -------------------------------------------------------------------------------- /data-raw/img/modify-t6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t6.png -------------------------------------------------------------------------------- /data-raw/img/modify-t7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data-raw/img/modify-t7.png -------------------------------------------------------------------------------- /data-raw/modify-tables-article-gif.R: -------------------------------------------------------------------------------- 1 | # demonstration tables for gif 2 | # slides here: https://docs.google.com/presentation/d/1ue-Vqls0AO6LsoN87V7oA2sRUALe_zHlp4YxMGKvX2w/edit?usp=sharing 3 | 4 | library(gtsummary) 5 | library(gtreg) 6 | library(tidyverse) 7 | library(gt) 8 | 9 | # initial table 10 | t0 <- df_adverse_events %>% 11 | # create a missing value to demonstrate unknown columns 12 | mutate(grade = ifelse(dplyr::row_number() == 1L, NA, grade)) %>% 13 | tbl_ae( 14 | id = patient_id, 15 | ae = adverse_event, 16 | soc = system_organ_class, 17 | by = grade 18 | ) %>% 19 | add_overall(across = 'by') %>% 20 | bold_labels() 21 | 22 | 23 | t1 <- t0 %>% 24 | modify_header( 25 | label ~ "**Event**" 26 | ) 27 | 28 | t2 <- t0 %>% 29 | modify_header( 30 | label ~ "**Event**", 31 | all_ae_cols() ~ "**Grade {by}**" 32 | ) 33 | 34 | t3 <- t0 %>% 35 | modify_header( 36 | label ~ "**Event**", 37 | all_ae_cols() ~ "**Grade {by}**", 38 | all_overall_cols() ~ "**Total**" 39 | ) 40 | 41 | t4 <- t0 %>% 42 | modify_header( 43 | label ~ "**Event**", 44 | all_ae_cols() ~ "**Grade {by}**", 45 | all_overall_cols() ~ "**Total**", 46 | all_unknown_cols() ~ "**Unknown Grade**" 47 | ) 48 | 49 | t5 <- t0 %>% 50 | modify_header( 51 | label ~ "**Event**", 52 | all_ae_cols() ~ "**Grade {by}**", 53 | all_overall_cols() ~ "**Total**", 54 | all_unknown_cols() ~ "**Unknown Grade**" 55 | ) |> 56 | modify_spanning_header( 57 | all_ae_cols(TRUE, TRUE) ~ "**All cohorts**, N = {N}" 58 | ) 59 | 60 | t6 <- t0 %>% 61 | modify_header( 62 | label ~ "**Event**", 63 | all_ae_cols() ~ "**Grade {by}**", 64 | all_overall_cols() ~ "**Total**", 65 | all_unknown_cols() ~ "**Unknown Grade**" 66 | ) |> 67 | modify_spanning_header( 68 | all_ae_cols(TRUE, TRUE) ~ "**All cohorts**, N = {N}" 69 | ) |> 70 | modify_caption("My caption: N = {N}") 71 | 72 | t7 <- t0 %>% 73 | modify_header( 74 | label ~ "**Event**", 75 | all_ae_cols() ~ "**Grade {by}**", 76 | all_overall_cols() ~ "**Total**", 77 | all_unknown_cols() ~ "**Unknown Grade**" 78 | ) %>% 79 | modify_spanning_header( 80 | all_ae_cols(TRUE, TRUE) ~ "**All cohorts**, N = {N}" 81 | ) %>% 82 | modify_caption("My caption: N = {N}") %>% 83 | modify_footnote(label = "My footnote: N = {N}") 84 | 85 | 86 | 87 | gtsave(as_gt(t0), filename = "modify-t0.png", path = here::here("data-raw", "img")) 88 | gtsave(as_gt(t1), filename = "modify-t1.png", path = here::here("data-raw", "img")) 89 | gtsave(as_gt(t2), filename = "modify-t2.png", path = here::here("data-raw", "img")) 90 | gtsave(as_gt(t3), filename = "modify-t3.png", path = here::here("data-raw", "img")) 91 | gtsave(as_gt(t4), filename = "modify-t4.png", path = here::here("data-raw", "img")) 92 | gtsave(as_gt(t5), filename = "modify-t5.png", path = here::here("data-raw", "img")) 93 | gtsave(as_gt(t6), filename = "modify-t6.png", path = here::here("data-raw", "img")) 94 | gtsave(as_gt(t7), filename = "modify-t7.png", path = here::here("data-raw", "img")) 95 | -------------------------------------------------------------------------------- /data-raw/rd_documentation_images.R: -------------------------------------------------------------------------------- 1 | # 1. install pkg that will run and save the documentation images 2 | install.packages( 3 | 'gt.doc.images', 4 | repos = c(ddsjoberg = 'https://ddsjoberg.r-universe.dev', 5 | CRAN = 'https://cloud.r-project.org') 6 | ) 7 | 8 | # 2. Install the most recent version of gtreg 9 | 10 | # 3. Restart R to have a fresh R session. 11 | # No packages should be loaded, not even gtreg. 12 | # Any object that is in the global environment may be written over! 13 | 14 | # 4. Run the function below to save the images created in the help files. 15 | # Only objects whose named in `_ex` or `_ex[:digit:]+` are saved. 16 | # Files will be saved to "~/man/figures/.png", where the filename 17 | # is the object name, i.e.'tbl_ae_count_ex1.png'. No example object 18 | # may overlap throughout the entire package. 19 | gt.doc.images::save_help_file_images(pkg = "gtreg") 20 | 21 | gt.doc.images::save_help_file_images( 22 | pkg = "gtreg", 23 | rd_files = "tbl_listing.Rd" 24 | ) 25 | 26 | # 5. Shrink png files 27 | gt.doc.images::shrink_help_file_images(pkg = "gtreg") 28 | -------------------------------------------------------------------------------- /data/df_adverse_events.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data/df_adverse_events.rda -------------------------------------------------------------------------------- /data/df_patient_characteristics.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/data/df_patient_characteristics.rda -------------------------------------------------------------------------------- /gtreg.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: 38738e9e-3f43-421a-a36d-166e47ceaaa2 3 | 4 | RestoreWorkspace: No 5 | SaveWorkspace: No 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: XeLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | LineEndingConversion: Posix 19 | 20 | BuildType: Package 21 | PackageUseDevtools: Yes 22 | PackageInstallArgs: --no-multiarch --with-keep.source 23 | PackageRoxygenize: rd,collate,namespace 24 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | AE 2 | AEs 3 | CMD 4 | Codecov 5 | Lifecycle 6 | MedDRA 7 | ORCID 8 | PHUSE 9 | SOCs 10 | YAML 11 | ae 12 | flextable 13 | gtsummary 14 | huxtable 15 | kableExtra 16 | knitr 17 | readme 18 | reflux 19 | rmarkdown 20 | tbl 21 | tibble 22 | -------------------------------------------------------------------------------- /man/add_overall_tbl_ae.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/add_overall.R 3 | \name{add_overall_tbl_ae} 4 | \alias{add_overall_tbl_ae} 5 | \alias{add_overall.tbl_ae} 6 | \alias{add_overall.tbl_ae_count} 7 | \alias{add_overall.tbl_ae_focus} 8 | \title{Tabulate Overall Summary} 9 | \usage{ 10 | \method{add_overall}{tbl_ae}(x, across = NULL, ...) 11 | 12 | \method{add_overall}{tbl_ae_count}(x, across = NULL, ...) 13 | 14 | \method{add_overall}{tbl_ae_focus}(x, across = NULL, ...) 15 | } 16 | \arguments{ 17 | \item{x}{Object of class \code{"tbl_ae"}, \code{"tbl_ae_focus"}, or \code{"tbl_ae_count"}} 18 | 19 | \item{across}{Specify the type of overall statistics to include. 20 | \itemize{ 21 | \item \code{"both"} adds summaries across both the \verb{by=} and \verb{strata=} levels 22 | \item \code{"by"} adds summaries across the \verb{by=} levels 23 | \item \code{"strata"} adds summaries across the \verb{strata=} levels 24 | \item \code{"overall-only"} adds a single overall column 25 | Default is all possible overall types. 26 | }} 27 | 28 | \item{...}{Not used} 29 | } 30 | \value{ 31 | Summary object of same input class 32 | } 33 | \description{ 34 | Tabulate Overall Summary 35 | } 36 | \section{Notes}{ 37 | 38 | If the spanning headers are modified prior to the call of \code{add_overall()}, 39 | the ordering of the columns may not be correct. 40 | } 41 | 42 | \section{Example Output}{ 43 | 44 | \if{html}{Example 1} 45 | 46 | \if{html}{\figure{add_overall_ex1.png}{options: width=100\%}} 47 | 48 | \if{html}{Example 2} 49 | 50 | \if{html}{\figure{add_overall_ex2.png}{options: width=70\%}} 51 | 52 | \if{html}{Example 3} 53 | 54 | \if{html}{\figure{add_overall_ex3.png}{options: width=90\%}} 55 | 56 | \if{html}{Example 4} 57 | 58 | \if{html}{\figure{add_overall_ex4.png}{options: width=90\%}} 59 | } 60 | 61 | \examples{ 62 | \donttest{ 63 | # Example 1 ----------------------------------------------------------------- 64 | add_overall_ex1 <- 65 | df_adverse_events \%>\% 66 | tbl_ae_count( 67 | ae = adverse_event, 68 | soc = system_organ_class, 69 | by = grade, 70 | strata = trt 71 | ) \%>\% 72 | add_overall() \%>\% 73 | modify_header(all_ae_cols() ~ "**Grade {by}**") \%>\% 74 | bold_labels() 75 | 76 | # Example 2 ----------------------------------------------------------------- 77 | add_overall_ex2 <- 78 | df_adverse_events \%>\% 79 | tbl_ae( 80 | id = patient_id, 81 | ae = adverse_event, 82 | soc = system_organ_class, 83 | by = grade 84 | ) \%>\% 85 | add_overall(across = 'by') \%>\% 86 | modify_header(all_ae_cols() ~ "**Grade {by}**") \%>\% 87 | bold_labels() 88 | 89 | # Example 3 ----------------------------------------------------------------- 90 | add_overall_ex3 <- 91 | df_adverse_events \%>\% 92 | tbl_ae_focus( 93 | id = patient_id, 94 | include = c(any_complication, grade3_complication), 95 | ae = adverse_event, 96 | strata = trt 97 | ) \%>\% 98 | add_overall(across = 'strata') 99 | 100 | # Example 4 ----------------------------------------------------------------- 101 | add_overall_ex4 <- 102 | df_adverse_events \%>\% 103 | tbl_ae( 104 | id = patient_id, 105 | ae = adverse_event, 106 | soc = system_organ_class, 107 | by = grade, 108 | strata = trt 109 | ) \%>\% 110 | add_overall(across = 'overall-only') \%>\% 111 | modify_header(all_ae_cols() ~ "**Grade {by}**") \%>\% 112 | bold_labels() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /man/df_adverse_events.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{df_adverse_events} 5 | \alias{df_adverse_events} 6 | \title{Simulated Adverse Event Database} 7 | \format{ 8 | A data frame with 100 rows--one row per patient per AE 9 | \describe{ 10 | \item{patient_id}{Patient ID} 11 | \item{trt}{Treatment Group} 12 | \item{system_organ_class}{System Organ Class} 13 | \item{adverse_event}{Adverse Event} 14 | \item{grade}{Grade} 15 | \item{drug_attribution}{Drug Attribution} 16 | \item{any_complication}{Any Grade Complication} 17 | \item{grade3_complication}{Grade 3+ Complication} 18 | } 19 | } 20 | \usage{ 21 | df_adverse_events 22 | } 23 | \description{ 24 | A data set containing reported AEs from a trial. 25 | } 26 | \keyword{datasets} 27 | -------------------------------------------------------------------------------- /man/df_patient_characteristics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{df_patient_characteristics} 5 | \alias{df_patient_characteristics} 6 | \title{Simulated Patient Characteristics Database} 7 | \format{ 8 | A data frame with 100 rows--one row per patient 9 | \describe{ 10 | \item{patient_id}{Patient ID} 11 | \item{trt}{Treatment Group} 12 | \item{age}{Patient Age} 13 | \item{marker}{Biological Marker} 14 | \item{status}{Study Status} 15 | \item{discontinued}{Discontinued from Study} 16 | \item{off_trt_ae}{Off Treatment Adverse Event} 17 | } 18 | } 19 | \usage{ 20 | df_patient_characteristics 21 | } 22 | \description{ 23 | Simulated Patient Characteristics Database 24 | } 25 | \keyword{datasets} 26 | -------------------------------------------------------------------------------- /man/dot-complete_ae_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/complete_ae_data.R 3 | \name{.complete_ae_data} 4 | \alias{.complete_ae_data} 5 | \title{Create a complete and expanded data frame for tabulating adverse events} 6 | \usage{ 7 | .complete_ae_data( 8 | data, 9 | id, 10 | ae, 11 | soc = NULL, 12 | by = NULL, 13 | strata = NULL, 14 | id_df = NULL, 15 | by_values = NULL, 16 | missing_text = "Unknown", 17 | missing_location = "first" 18 | ) 19 | } 20 | \arguments{ 21 | \item{data}{Data frame} 22 | 23 | \item{id}{String variable name of the patient ID} 24 | 25 | \item{ae}{String variable name of the adverse event column} 26 | 27 | \item{soc}{Optional string variable name of the system organ class column} 28 | 29 | \item{by}{Optional string variable to split results by, e.g. report AEs by grade or attribution} 30 | 31 | \item{strata}{Optional string variable to stratify results by, 32 | e.g. report AEs summaries by treatment group} 33 | 34 | \item{id_df}{Optional data frame of complete id values and strata to achieve correct 35 | base n for the situation in which not all subjects experience adverse events} 36 | 37 | \item{by_values}{Optional vector of complete by values, listed in desired order, 38 | to achieve correct table structure for the situation in which an adverse 39 | event of a certain grade is not observed for a given soc} 40 | 41 | \item{missing_text}{String that will be shown for missing levels of \verb{by=}, 42 | Default is \code{"Unknown"}} 43 | 44 | \item{missing_location}{location where the column summarizing values with 45 | missing levels \verb{by=} will be located in the final table. Must be 46 | one of \verb{c("first", "last", "hide)}. Default is \code{"first"}} 47 | } 48 | \value{ 49 | a tibble 50 | } 51 | \description{ 52 | Returns a data frame that has an observation for each patient in the study, 53 | with combinations for each ID, SOC, and AE. The returned data frame includes 54 | new logical columns \code{"..ae.."} and \code{"..soc.."} indicating whether that 55 | row should be included when tabulating the AE table. When multiple 56 | AEs of the same type are observed, the AE with the largest \verb{by=} value 57 | is the observation to be used in the tabulation. 58 | } 59 | \examples{ 60 | df_adverse_events \%>\% 61 | .complete_ae_data( 62 | id = "patient_id", 63 | ae = "adverse_event", 64 | soc = "system_organ_class", 65 | by = "grade", 66 | strata = "trt" 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /man/figures/Introducing-gtreg-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/Introducing-gtreg-screenshot.png -------------------------------------------------------------------------------- /man/figures/README-example-tbl_ae-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/README-example-tbl_ae-1.png -------------------------------------------------------------------------------- /man/figures/README-example-tbl_ae_count-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/README-example-tbl_ae_count-1.png -------------------------------------------------------------------------------- /man/figures/README-example-tbl_ae_focus-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/README-example-tbl_ae_focus-1.png -------------------------------------------------------------------------------- /man/figures/README-example-tbl_listing-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/README-example-tbl_listing-1.png -------------------------------------------------------------------------------- /man/figures/README-example-tbl_reg_summary-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/README-example-tbl_reg_summary-1.png -------------------------------------------------------------------------------- /man/figures/add_overall_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/add_overall_ex1.png -------------------------------------------------------------------------------- /man/figures/add_overall_ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/add_overall_ex2.png -------------------------------------------------------------------------------- /man/figures/add_overall_ex3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/add_overall_ex3.png -------------------------------------------------------------------------------- /man/figures/add_overall_ex4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/add_overall_ex4.png -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/logo.png -------------------------------------------------------------------------------- /man/figures/modify_ae_header_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/modify_ae_header_ex1.png -------------------------------------------------------------------------------- /man/figures/selectors_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/selectors_ex1.png -------------------------------------------------------------------------------- /man/figures/tbl_ae_count_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_ae_count_ex1.png -------------------------------------------------------------------------------- /man/figures/tbl_ae_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_ae_ex1.png -------------------------------------------------------------------------------- /man/figures/tbl_ae_ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_ae_ex2.png -------------------------------------------------------------------------------- /man/figures/tbl_ae_focus_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_ae_focus_ex1.png -------------------------------------------------------------------------------- /man/figures/tbl_listing_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_listing_ex1.png -------------------------------------------------------------------------------- /man/figures/tbl_listing_ex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_listing_ex2.png -------------------------------------------------------------------------------- /man/figures/tbl_reg_summary_ex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/man/figures/tbl_reg_summary_ex1.png -------------------------------------------------------------------------------- /man/gtreg-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gtreg-package.R 3 | \docType{package} 4 | \name{gtreg-package} 5 | \alias{gtreg} 6 | \alias{gtreg-package} 7 | \title{gtreg: Regulatory Tables for Clinical Research} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | Creates tables suitable for regulatory agency submission by leveraging the 'gtsummary' package as the back end. Tables can be exported to HTML, Word, PDF and more. Highly customized outputs are available by utilizing existing styling functions from 'gtsummary' as well as custom options designed for regulatory tables. 12 | } 13 | \seealso{ 14 | Useful links: 15 | \itemize{ 16 | \item \url{https://github.com/shannonpileggi/gtreg} 17 | \item \url{https://shannonpileggi.github.io/gtreg/} 18 | \item Report bugs at \url{https://github.com/shannonpileggi/gtreg/issues} 19 | } 20 | 21 | } 22 | \author{ 23 | \strong{Maintainer}: Shannon Pileggi \email{shannon.pileggi@gmail.com} (\href{https://orcid.org/0000-0002-7732-4164}{ORCID}) [copyright holder] 24 | 25 | Authors: 26 | \itemize{ 27 | \item Daniel D. Sjoberg \email{danield.sjoberg@gmail.com} (\href{https://orcid.org/0000-0003-0862-2018}{ORCID}) 28 | } 29 | 30 | } 31 | \keyword{internal} 32 | -------------------------------------------------------------------------------- /man/inline_text_tbl_ae.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/inline_text_tbl_ae.R 3 | \name{inline_text_tbl_ae} 4 | \alias{inline_text_tbl_ae} 5 | \alias{inline_text.tbl_ae} 6 | \alias{inline_text.tbl_ae_count} 7 | \alias{inline_text.tbl_ae_focus} 8 | \title{Report Values from gtreg tables in-line} 9 | \usage{ 10 | \method{inline_text}{tbl_ae}(x, row, column = NULL, ...) 11 | 12 | \method{inline_text}{tbl_ae_count}(x, row, column = NULL, ...) 13 | 14 | \method{inline_text}{tbl_ae_focus}(x, row, column = NULL, ...) 15 | } 16 | \arguments{ 17 | \item{x}{an object of class \code{tbl_ae()}, \code{tbl_ae_count()}, \code{tbl_ae_focus()}} 18 | 19 | \item{row}{string indicating the AE or SOC to report} 20 | 21 | \item{column}{column name of cell to report. Use \code{show_header_names(x)} 22 | to print all column names beside the current header.} 23 | 24 | \item{...}{not used} 25 | } 26 | \value{ 27 | string 28 | } 29 | \description{ 30 | Function allows users to report formatted and styled results from 31 | gtreg tables in-line. 32 | } 33 | \examples{ 34 | \donttest{ 35 | tbl <- 36 | df_adverse_events \%>\% 37 | tbl_ae( 38 | id = patient_id, 39 | ae = adverse_event, 40 | soc = system_organ_class, 41 | by = grade 42 | ) 43 | show_header_names(tbl) 44 | 45 | inline_text(tbl, "Anaemia", column = stat_5) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reexport.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{\%>\%} 7 | \alias{vars} 8 | \alias{select} 9 | \alias{where} 10 | \alias{starts_with} 11 | \alias{ends_with} 12 | \alias{contains} 13 | \alias{matches} 14 | \alias{num_range} 15 | \alias{all_of} 16 | \alias{any_of} 17 | \alias{everything} 18 | \alias{last_col} 19 | \alias{one_of} 20 | \alias{as_tibble} 21 | \alias{as_gt} 22 | \alias{as_flex_table} 23 | \alias{as_hux_table} 24 | \alias{as_hux_xlsx} 25 | \alias{as_kable} 26 | \alias{as_kable_extra} 27 | \alias{inline_text} 28 | \alias{add_overall} 29 | \alias{modify_spanning_header} 30 | \alias{modify_header} 31 | \alias{modify_footnote} 32 | \alias{modify_caption} 33 | \alias{modify_column_indent} 34 | \alias{bold_labels} 35 | \alias{bold_levels} 36 | \alias{italicize_labels} 37 | \alias{show_header_names} 38 | \alias{style_number} 39 | \alias{style_sigfig} 40 | \alias{style_percent} 41 | \alias{all_continuous} 42 | \alias{all_continuous2} 43 | \alias{all_categorical} 44 | \alias{all_stat_cols} 45 | \title{Objects exported from other packages} 46 | \keyword{internal} 47 | \description{ 48 | These objects are imported from other packages. Follow the links 49 | below to see their documentation. 50 | 51 | \describe{ 52 | \item{dplyr}{\code{\link[dplyr:reexports]{\%>\%}}, \code{\link[dplyr:reexports]{all_of}}, \code{\link[dplyr:reexports]{any_of}}, \code{\link[dplyr:reexports]{contains}}, \code{\link[dplyr:reexports]{ends_with}}, \code{\link[dplyr:reexports]{everything}}, \code{\link[dplyr:reexports]{last_col}}, \code{\link[dplyr:reexports]{matches}}, \code{\link[dplyr:reexports]{num_range}}, \code{\link[dplyr:reexports]{one_of}}, \code{\link[dplyr]{select}}, \code{\link[dplyr:reexports]{starts_with}}, \code{\link[dplyr]{vars}}, \code{\link[dplyr:reexports]{where}}} 53 | 54 | \item{gtsummary}{\code{\link[gtsummary]{add_overall}}, \code{\link[gtsummary:select_helpers]{all_categorical}}, \code{\link[gtsummary:select_helpers]{all_continuous}}, \code{\link[gtsummary:select_helpers]{all_continuous2}}, \code{\link[gtsummary:select_helpers]{all_stat_cols}}, \code{\link[gtsummary]{as_flex_table}}, \code{\link[gtsummary]{as_gt}}, \code{\link[gtsummary]{as_hux_table}}, \code{\link[gtsummary:as_hux_table]{as_hux_xlsx}}, \code{\link[gtsummary]{as_kable}}, \code{\link[gtsummary]{as_kable_extra}}, \code{\link[gtsummary:bold_italicize_labels_levels]{bold_labels}}, \code{\link[gtsummary:bold_italicize_labels_levels]{bold_labels}}, \code{\link[gtsummary:bold_italicize_labels_levels]{bold_levels}}, \code{\link[gtsummary]{inline_text}}, \code{\link[gtsummary:bold_italicize_labels_levels]{italicize_labels}}, \code{\link[gtsummary:bold_italicize_labels_levels]{italicize_labels}}, \code{\link[gtsummary]{modify_caption}}, \code{\link[gtsummary]{modify_column_indent}}, \code{\link[gtsummary:deprecated_modify_footnote]{modify_footnote}}, \code{\link[gtsummary:modify]{modify_header}}, \code{\link[gtsummary:modify]{modify_spanning_header}}, \code{\link[gtsummary:modify]{show_header_names}}, \code{\link[gtsummary]{style_number}}, \code{\link[gtsummary]{style_percent}}, \code{\link[gtsummary]{style_sigfig}}} 55 | 56 | \item{tibble}{\code{\link[tibble]{as_tibble}}} 57 | }} 58 | 59 | -------------------------------------------------------------------------------- /man/selectors.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/selectors.R 3 | \name{selectors} 4 | \alias{selectors} 5 | \alias{all_ae_cols} 6 | \alias{all_cols_in_strata} 7 | \alias{all_overall_cols} 8 | \alias{all_unknown_cols} 9 | \title{Column Selectors} 10 | \usage{ 11 | all_ae_cols(overall = FALSE, unknown = FALSE) 12 | 13 | all_cols_in_strata(strata) 14 | 15 | all_overall_cols() 16 | 17 | all_unknown_cols() 18 | } 19 | \arguments{ 20 | \item{overall}{logical indicating whether to include the overall columns. 21 | Default is FALSE} 22 | 23 | \item{unknown}{logical indicating whether to include the unknown or missing columns. 24 | Default is FALSE} 25 | 26 | \item{strata}{character vector of the selected stratum} 27 | } 28 | \value{ 29 | selected columns 30 | } 31 | \description{ 32 | See the \href{https://shannonpileggi.github.io/gtreg/articles/table-modifications.html}{Table modifications article} for examples. 33 | \itemize{ 34 | \item \code{all_ae_cols(overall, unknown)} selects all columns summarizing AE statistics. By default, unknown and overall columns are not selected. 35 | \item \code{all_cols_in_strata(strata)} selects all columns from specified stratum. 36 | \item \code{all_overall_cols()} selects all overall columns 37 | \item \code{all_unknown_cols()} selects all unknown columns 38 | } 39 | } 40 | \section{Example Output}{ 41 | 42 | \if{html}{Example 1} 43 | 44 | \if{html}{\figure{selectors_ex1.png}{options: width=65\%}} 45 | } 46 | 47 | \examples{ 48 | \donttest{ 49 | selectors_ex1 <- 50 | df_adverse_events \%>\% 51 | dplyr::mutate(grade = ifelse(dplyr::row_number() == 1L, NA, grade)) \%>\% 52 | tbl_ae( 53 | id = patient_id, 54 | ae = adverse_event, 55 | soc = system_organ_class, 56 | by = grade 57 | ) \%>\% 58 | add_overall(across = 'by') \%>\% 59 | modify_header( 60 | all_ae_cols() ~ "**Grade {by}**", 61 | all_overall_cols() ~ "**Total**", 62 | all_unknown_cols() ~ "**Unknown Grade**" 63 | ) 64 | } 65 | } 66 | \seealso{ 67 | \code{gtsummary::all_stat_cols()} 68 | } 69 | -------------------------------------------------------------------------------- /man/style_xxx.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/style_xxx.R 3 | \name{style_xxx} 4 | \alias{style_xxx} 5 | \title{Style numbers as x's} 6 | \usage{ 7 | style_xxx(x, width = digits + 2, digits = 0) 8 | } 9 | \arguments{ 10 | \item{x}{a numeric or character vector} 11 | 12 | \item{width}{the width of output field of x's, including the decimal place} 13 | 14 | \item{digits}{the number of digits displayed after the decimal place} 15 | } 16 | \value{ 17 | a character vector 18 | } 19 | \description{ 20 | The purpose of \code{style_xxx()} is to convert numeric values in 21 | summary tables to x's of consistent length for mock tables. 22 | See the \href{https://shannonpileggi.github.io/gtreg/articles/table-shells.html}{Table shells vignette} 23 | for detailed examples. 24 | } 25 | \examples{ 26 | style_xxx(7:10, digits = 0) 27 | style_xxx(7:10, digits = 1) 28 | style_xxx(7:10, width = 2, digits = 0) 29 | style_xxx(7:10, width = 5, digits = 2) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /man/tbl_ae.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbl_ae.R 3 | \name{tbl_ae} 4 | \alias{tbl_ae} 5 | \title{Tabulate Adverse Events} 6 | \usage{ 7 | tbl_ae( 8 | data, 9 | id, 10 | ae, 11 | soc = NULL, 12 | by = NULL, 13 | strata = NULL, 14 | id_df = NULL, 15 | statistic = "{n} ({p})", 16 | by_values = NULL, 17 | digits = NULL, 18 | sort = NULL, 19 | zero_symbol = "\U2014", 20 | missing_location = c("first", "last", "hide") 21 | ) 22 | } 23 | \arguments{ 24 | \item{data}{Data frame} 25 | 26 | \item{id}{Variable name of the patient ID} 27 | 28 | \item{ae}{Variable name of the adverse event column} 29 | 30 | \item{soc}{Variable name of the system organ class column} 31 | 32 | \item{by}{Variable to split results by, e.g. report AEs by grade} 33 | 34 | \item{strata}{Variable to stratify results by, e.g. report AEs summaries 35 | by treatment group} 36 | 37 | \item{id_df}{Optional data frame of complete id values and strata to achieve correct 38 | base n for the situation in which not all subjects experience adverse events. See 39 | \code{\link{df_patient_characteristics}} for an example \code{id_df} that pairs with 40 | \code{\link{df_adverse_events}}.} 41 | 42 | \item{statistic}{String indicating the statistics that will be reported. 43 | The default is \code{"{n} ({p})"}} 44 | 45 | \item{by_values}{Optional vector of complete by values, listed in desired order, 46 | to achieve correct table structure for the situation in which an adverse 47 | event of a certain grade is not observed for a given soc} 48 | 49 | \item{digits}{Specifies the number of decimal places to round the summary statistics. 50 | By default integers are shown to zero decimal places, and percentages are 51 | formatted with \code{style_percent()}. If you would like to modify either 52 | of these, pass a vector of integers indicating the number of decimal 53 | places to round the statistics. For example, if the statistic being 54 | calculated is \code{"{n} ({p}\%)"} and you want the percent rounded to 55 | 2 decimal places use \code{digits = c(0, 2)}. 56 | User may also pass a styling function: \code{digits = style_sigfig}} 57 | 58 | \item{sort}{Controls order of AEs and SOCs in output table. 59 | The default is \code{NULL}, where AEs and SOCs are sorted alphanumerically 60 | (and factors sorted according to their factor level). 61 | Use \code{sort = "ae"} to sort AEs in decreasing frequency order, \code{sort = "soc"} 62 | to sort SOCs in decreasing order, and \code{sort = c("ae", "soc")} to sort both. 63 | AEs are sorted within SOC.} 64 | 65 | \item{zero_symbol}{String used to represent cells with zero counts. Default 66 | is the em-dash (\code{"\\U2014"}). Using \code{zero_symbol = NULL} will print the 67 | zero count statistics, e.g. \code{"0 (0)"}} 68 | 69 | \item{missing_location}{location where the column summarizing values with 70 | missing levels \verb{by=} will be located in the final table. Must be 71 | one of \verb{c("first", "last", "hide)}. Default is \code{"first"}} 72 | } 73 | \value{ 74 | a 'tbl_ae' object 75 | } 76 | \description{ 77 | The function tabulates adverse events. One AE per ID will be counted in the 78 | resulting table. If a \verb{by=} variable is passed and a 79 | patient experienced more than one of the same AE, the AE associated with the 80 | highest \verb{by=} level will be included. For example, if a patient has two of 81 | the same AE and \code{by = grade}, the AE with the highest grade will be 82 | included. 83 | Similarly, if tabulations within system organ class are requested, the 84 | AE within SOC associated with the highest grade will be tabulated. 85 | } 86 | \section{Example Output}{ 87 | 88 | \if{html}{Example 1} 89 | 90 | \if{html}{\figure{tbl_ae_ex1.png}{options: width=95\%}} 91 | 92 | \if{html}{Example 2} 93 | 94 | \if{html}{\figure{tbl_ae_ex2.png}{options: width=65\%}} 95 | } 96 | 97 | \examples{ 98 | \donttest{ 99 | # Example 1 ----------------------------------------------------------------- 100 | tbl_ae_ex1 <- 101 | df_adverse_events \%>\% 102 | tbl_ae( 103 | id = patient_id, 104 | ae = adverse_event, 105 | soc = system_organ_class, 106 | by = grade, 107 | strata = trt 108 | ) \%>\% 109 | modify_header(all_ae_cols() ~ "**Grade {by}**") 110 | 111 | # Example 2 ----------------------------------------------------------------- 112 | tbl_ae_ex2 <- 113 | df_adverse_events \%>\% 114 | tbl_ae( 115 | id = patient_id, 116 | ae = adverse_event, 117 | by = grade 118 | ) \%>\% 119 | modify_header(all_ae_cols() ~ "**Grade {by}**") 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /man/tbl_ae_count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbl_ae_count.R 3 | \name{tbl_ae_count} 4 | \alias{tbl_ae_count} 5 | \title{Tabulate Raw AE Counts} 6 | \usage{ 7 | tbl_ae_count( 8 | data, 9 | ae, 10 | soc = NULL, 11 | by = NULL, 12 | strata = NULL, 13 | by_values = NULL, 14 | digits = NULL, 15 | sort = NULL, 16 | zero_symbol = "\U2014", 17 | missing_location = c("first", "last", "hide") 18 | ) 19 | } 20 | \arguments{ 21 | \item{data}{Data frame} 22 | 23 | \item{ae}{Variable name of the adverse event column} 24 | 25 | \item{soc}{Variable name of the system organ class column} 26 | 27 | \item{by}{Variable to split results by, e.g. report AEs by grade} 28 | 29 | \item{strata}{Variable to stratify results by, e.g. report AEs summaries 30 | by treatment group} 31 | 32 | \item{by_values}{Optional vector of complete by values, listed in desired order, 33 | to achieve correct table structure for the situation in which an adverse 34 | event of a certain grade is not observed for a given soc} 35 | 36 | \item{digits}{Specifies the number of decimal places to round the summary statistics. 37 | By default integers are shown to zero decimal places, and percentages are 38 | formatted with \code{style_percent()}. If you would like to modify either 39 | of these, pass a vector of integers indicating the number of decimal 40 | places to round the statistics. For example, if the statistic being 41 | calculated is \code{"{n} ({p}\%)"} and you want the percent rounded to 42 | 2 decimal places use \code{digits = c(0, 2)}. 43 | User may also pass a styling function: \code{digits = style_sigfig}} 44 | 45 | \item{sort}{Controls order of AEs and SOCs in output table. 46 | The default is \code{NULL}, where AEs and SOCs are sorted alphanumerically 47 | (and factors sorted according to their factor level). 48 | Use \code{sort = "ae"} to sort AEs in decreasing frequency order, \code{sort = "soc"} 49 | to sort SOCs in decreasing order, and \code{sort = c("ae", "soc")} to sort both. 50 | AEs are sorted within SOC.} 51 | 52 | \item{zero_symbol}{String used to represent cells with zero counts. Default 53 | is the em-dash (\code{"\\U2014"}). Using \code{zero_symbol = NULL} will print the 54 | zero count statistics, e.g. \code{"0 (0)"}} 55 | 56 | \item{missing_location}{location where the column summarizing values with 57 | missing levels \verb{by=} will be located in the final table. Must be 58 | one of \verb{c("first", "last", "hide)}. Default is \code{"first"}} 59 | } 60 | \value{ 61 | a 'tbl_ae_count' object 62 | } 63 | \description{ 64 | Create a table counting all AEs. 65 | } 66 | \details{ 67 | \code{tbl_ae_count} counts all AEs (whereas \code{\link{tbl_ae}} 68 | counts by maximum grade). Thus, \code{tbl_ae_count} does 69 | not provide percentages as multiple AEs can be counted per subject. 70 | } 71 | \section{Example Output}{ 72 | 73 | \if{html}{Example 1} 74 | 75 | \if{html}{\figure{tbl_ae_count_ex1.png}{options: width=90\%}} 76 | } 77 | 78 | \examples{ 79 | \donttest{ 80 | # Example 1 ----------------------------------------------------------------- 81 | tbl_ae_count_ex1 <- 82 | tbl_ae_count( 83 | data = df_adverse_events, 84 | ae = adverse_event, 85 | soc = system_organ_class, 86 | strata = trt, 87 | by = grade 88 | ) \%>\% 89 | modify_header(all_ae_cols() ~ "**Grade {by}**") 90 | } 91 | } 92 | \seealso{ 93 | \code{\link{tbl_ae}} 94 | } 95 | -------------------------------------------------------------------------------- /man/tbl_ae_focus.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbl_ae_focus.R 3 | \name{tbl_ae_focus} 4 | \alias{tbl_ae_focus} 5 | \title{Tabulate AE Focused (Dichotomous) Summaries} 6 | \usage{ 7 | tbl_ae_focus( 8 | data, 9 | include, 10 | id, 11 | ae, 12 | soc = NULL, 13 | strata = NULL, 14 | label = NULL, 15 | id_df = NULL, 16 | statistic = "{n} ({p})", 17 | digits = NULL, 18 | sort = NULL, 19 | zero_symbol = "\U2014" 20 | ) 21 | } 22 | \arguments{ 23 | \item{data}{Data frame} 24 | 25 | \item{include}{Vector of column names to summarize. Column names may be 26 | quoted or unquoted. All columns must be class 'logical'.} 27 | 28 | \item{id}{Variable name of the patient ID} 29 | 30 | \item{ae}{Variable name of the adverse event column} 31 | 32 | \item{soc}{Variable name of the system organ class column} 33 | 34 | \item{strata}{Variable to stratify results by, e.g. report AEs summaries 35 | by treatment group} 36 | 37 | \item{label}{A named list of labels that will be applied in the 38 | resulting table. Names must be those passed in \verb{include=}. Default is 39 | NULL, and either the label attribute or the column name will be used.} 40 | 41 | \item{id_df}{Optional data frame of complete id values and strata to achieve correct 42 | base n for the situation in which not all subjects experience adverse events. See 43 | \code{\link{df_patient_characteristics}} for an example \code{id_df} that pairs with 44 | \code{\link{df_adverse_events}}.} 45 | 46 | \item{statistic}{String indicating the statistics that will be reported. 47 | The default is \code{"{n} ({p})"}} 48 | 49 | \item{digits}{Specifies the number of decimal places to round the summary statistics. 50 | By default integers are shown to zero decimal places, and percentages are 51 | formatted with \code{style_percent()}. If you would like to modify either 52 | of these, pass a vector of integers indicating the number of decimal 53 | places to round the statistics. For example, if the statistic being 54 | calculated is \code{"{n} ({p}\%)"} and you want the percent rounded to 55 | 2 decimal places use \code{digits = c(0, 2)}. 56 | User may also pass a styling function: \code{digits = style_sigfig}} 57 | 58 | \item{sort}{Controls order of AEs and SOCs in output table. 59 | The default is \code{NULL}, where AEs and SOCs are sorted alphanumerically 60 | (and factors sorted according to their factor level). 61 | Use \code{sort = "ae"} to sort AEs in decreasing frequency order, \code{sort = "soc"} 62 | to sort SOCs in decreasing order, and \code{sort = c("ae", "soc")} to sort both. 63 | AEs are sorted within SOC.} 64 | 65 | \item{zero_symbol}{String used to represent cells with zero counts. Default 66 | is the em-dash (\code{"\\U2014"}). Using \code{zero_symbol = NULL} will print the 67 | zero count statistics, e.g. \code{"0 (0)"}} 68 | } 69 | \value{ 70 | a 'tbl_ae_focus' object 71 | } 72 | \description{ 73 | Summarize dichotomous AE data. For example, report the 74 | rate of patients that have an AE of Grade 3 or higher. 75 | } 76 | \section{Example Output}{ 77 | 78 | \if{html}{Example 1} 79 | 80 | \if{html}{\figure{tbl_ae_focus_ex1.png}{options: width=75\%}} 81 | } 82 | 83 | \examples{ 84 | \donttest{ 85 | # Example 1 ----------------------------------------------------------------- 86 | tbl_ae_focus_ex1 <- 87 | df_adverse_events \%>\% 88 | tbl_ae_focus( 89 | include = c(any_complication, grade3_complication), 90 | id = patient_id, 91 | ae = adverse_event, 92 | soc = system_organ_class, 93 | label = 94 | list(any_complication = "Any Grade Complication", 95 | grade3_complication = "Grade 3+ Complication") 96 | ) \%>\% 97 | bold_labels() 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /man/tbl_listing.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbl_listing.R 3 | \name{tbl_listing} 4 | \alias{tbl_listing} 5 | \title{Data Listing Table} 6 | \usage{ 7 | tbl_listing(data, group_by = NULL, bold_headers = TRUE) 8 | } 9 | \arguments{ 10 | \item{data}{a data frame} 11 | 12 | \item{group_by}{Single variable name indicating a grouping variable. 13 | Default is \code{NULL} for no grouping variable. When specified, a grouping 14 | row will be added to the first column. See details below.} 15 | 16 | \item{bold_headers}{logical indicating whether to bold column headers. 17 | Default is \code{TRUE}} 18 | } 19 | \value{ 20 | gtsummary data listing 21 | } 22 | \description{ 23 | Function creates a gtsummary-class listing of data. Column labels are 24 | used as column headers, when present. 25 | The listing prints observations in the order of the input data. 26 | } 27 | \section{group_by}{ 28 | 29 | 30 | The grouping column and the first column in the table will be combined 31 | and the type/class may be converted to common type/class for both columns. 32 | However, if either the \verb{group_by=} column or the first column are factors, 33 | the factor column(s) will first be converted to character. 34 | 35 | The groups are ordered according to the grouping 36 | variable's type (i.e., character, numeric, or factor). 37 | } 38 | 39 | \section{Details}{ 40 | 41 | 42 | The purpose of \code{tbl_listing()} is to add support for printing data frames, 43 | while taking advantage of the \{gtsummary\} defaults, e.g. ability to print 44 | to most output formats, using print themes to have a common style to all 45 | tables in a document, etc. 46 | 47 | While the output of \code{tbl_listing()} is class \code{'gtsummary'}, these tables 48 | are not meant to be merged with other \code{'gtsummary'} tables with \code{tbl_merge()}, 49 | or reporting table contents with \code{inline_text()}. The reason is that a 50 | proper \code{'gtsummary'} contains 51 | \href{https://www.danieldsjoberg.com/gtsummary/articles/gtsummary_definition.html}{additional, hidden structure} 52 | not present in the result of \code{tbl_listing()}. If you do need to report 53 | the results of \code{tbl_listing()} in-line, it's recommended to convert 54 | the table to a data frame, then extract the needed cell, e.g. 55 | 56 | \if{html}{\out{
}}\preformatted{tbl_listing() |> 57 | as_tibble(col_names = FALSE) |> 58 | dplyr::slice(1) |> 59 | dplyr::pull(colname)` 60 | }\if{html}{\out{
}} 61 | } 62 | 63 | \section{Example Output}{ 64 | 65 | \if{html}{Example 1} 66 | 67 | \if{html}{\figure{tbl_listing_ex1.png}{options: width=65\%}} 68 | 69 | \if{html}{Example 2} 70 | 71 | \if{html}{\figure{tbl_listing_ex2.png}{options: width=75\%}} 72 | } 73 | 74 | \examples{ 75 | library(dplyr, warn.conflicts = FALSE) 76 | 77 | tbl_listing_ex1 <- 78 | head(df_adverse_events, n = 10) \%>\% 79 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) \%>\% 80 | arrange(adverse_event, desc(grade)) \%>\% 81 | tbl_listing(group_by = system_organ_class) \%>\% 82 | bold_labels() 83 | 84 | set.seed(11234) 85 | tbl_listing_ex2 <- 86 | df_patient_characteristics \%>\% 87 | dplyr::slice_sample(n = 10) \%>\% 88 | select(patient_id, status, discontinued, off_trt_ae) \%>\% 89 | tbl_listing() \%>\% 90 | as_gt() \%>\% 91 | gt::opt_row_striping() 92 | 93 | } 94 | -------------------------------------------------------------------------------- /man/tbl_reg_summary.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tbl_reg_summary.R 3 | \name{tbl_reg_summary} 4 | \alias{tbl_reg_summary} 5 | \title{Data Summary Table} 6 | \usage{ 7 | tbl_reg_summary( 8 | data, 9 | by = NULL, 10 | label = NULL, 11 | statistic = list(all_continuous() ~ c("{N_nonmiss}", "{mean} ({sd})", 12 | "{median} ({p25}, {p75})", "{min}, {max}", "{N_miss}"), all_categorical() ~ 13 | "{n} ({p}\%)"), 14 | digits = NULL, 15 | type = NULL, 16 | value = NULL, 17 | missing = c("no", "yes", "ifany"), 18 | missing_text = "Unknown", 19 | missing_stat = "{N_miss}", 20 | sort = all_categorical(FALSE) ~ "alphanumeric", 21 | percent = c("column", "row", "cell"), 22 | include = everything() 23 | ) 24 | } 25 | \arguments{ 26 | \item{data}{(\code{data.frame})\cr A data frame.} 27 | 28 | \item{by}{A column name (quoted or unquoted) in \code{data.} Summary statistics 29 | will be calculated separately for each level of the by variable 30 | (e.g. \code{by = trt}). If \code{NULL}, summary statistics are calculated using all observations.} 31 | 32 | \item{label}{(\code{\link[gtsummary:syntax]{formula-list-selector}})\cr 33 | Used to override default labels in summary table, e.g. \code{list(age = "Age, years")}. 34 | The default for each variable is the column label attribute, \code{attr(., 'label')}. 35 | If no label has been set, the column name is used.} 36 | 37 | \item{statistic}{List of formulas specifying types of summary statistics 38 | to display for each variable.} 39 | 40 | \item{digits}{(\code{\link[gtsummary:syntax]{formula-list-selector}})\cr 41 | Specifies how summary statistics are rounded. Values may be either integer(s) 42 | or function(s). If not specified, default formatting is assigned 43 | via \code{assign_summary_digits()}. See below for details.} 44 | 45 | \item{type}{List of formulas specifying variable types. 46 | Accepted values are \code{c("continuous", "continuous2", "categorical", "dichotomous")}, 47 | e.g. \code{type = list(age ~ "continuous", female ~ "dichotomous")}. 48 | If type not specified for a variable, the function will default to an appropriate summary type.} 49 | 50 | \item{value}{List of formulas specifying the value to display for dichotomous 51 | variables. gtsummary selectors, e.g. \code{all_dichotomous()}, cannot be used with this argument.} 52 | 53 | \item{missing, missing_text, missing_stat}{Arguments dictating how and if missing values are presented: 54 | \itemize{ 55 | \item \code{missing}: must be one of \code{c("ifany", "no", "always")} 56 | \item \code{missing_text}: string indicating text shown on missing row. Default is \code{"Unknown"} 57 | \item \code{missing_stat}: statistic to show on missing row. Default is \code{"{N_miss}"}. 58 | Possible values are \code{N_miss}, \code{N_obs}, \code{N_nonmiss}, \code{p_miss}, \code{p_nonmiss}. 59 | }} 60 | 61 | \item{sort}{(\code{\link[gtsummary:syntax]{formula-list-selector}})\cr 62 | Specifies sorting to perform for categorical variables. 63 | Values must be one of \code{c("alphanumeric", "frequency")}. 64 | Default is \code{all_categorical(FALSE) ~ "alphanumeric"}.} 65 | 66 | \item{percent}{(\code{string})\cr 67 | Indicates the type of percentage to return. 68 | Must be one of \code{c("column", "row", "cell")}. Default is \code{"column"}.} 69 | 70 | \item{include}{(\code{\link[dplyr:dplyr_tidy_select]{tidy-select}})\cr 71 | Variables to include in the summary table. Default is \code{everything()}.} 72 | } 73 | \value{ 74 | a 'tbl_reg_summary' object 75 | } 76 | \description{ 77 | Function wraps \code{gtsummary::tbl_summary()} to create a data summary 78 | table often seen in regulatory submissions. Continuous variable summaries 79 | are shown on multiple lines with additional summary statistics and percentages 80 | are shown for categorical variables; precision levels estimated based on values observed. 81 | } 82 | \section{Example Output}{ 83 | 84 | \if{html}{Example 1} 85 | 86 | \if{html}{\figure{tbl_reg_summary_ex1.png}{options: width=65\%}} 87 | } 88 | 89 | \examples{ 90 | tbl_reg_summary_ex1 <- 91 | df_patient_characteristics \%>\% 92 | tbl_reg_summary(by = trt, include = c(marker, status)) 93 | 94 | } 95 | \seealso{ 96 | See \href{https://www.danieldsjoberg.com/gtsummary/reference/tbl_summary.html}{\code{gtsummary::tbl_summary()}} help file 97 | 98 | See \href{https://www.danieldsjoberg.com/gtsummary/articles/tbl_summary.html}{vignette} for detailed tutorial 99 | } 100 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if(requireNamespace('spelling', quietly = TRUE)) 2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE, 3 | skip_on_cran = TRUE) 4 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(gtreg) 3 | 4 | test_check("gtreg") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/add_overall.md: -------------------------------------------------------------------------------- 1 | # add_overall() works 2 | 3 | Code 4 | tbl1 %>% as.data.frame() 5 | Output 6 | **Adverse Event** **Grade 1** **Grade 2** **Grade 3** **Grade 4** 7 | 1 Anaemia 2 2 8 | 2 Difficult digestion 1 3 9 | 3 Increased tendency to bruise 4 10 | 4 Intestinal dilatation 2 1 11 | 5 Iron deficiency anaemia 1 2 2 12 | 6 Myochosis 3 1 1 13 | 7 Non-erosive reflux disease 4 3 14 | 8 Pancreatic enzyme abnormality 2 1 2 2 15 | 9 Thrombocytopenia 1 3 1 16 | **Grade 5** **Overall** 17 | 1 3 7 18 | 2 1 5 19 | 3 2 6 20 | 4 1 4 21 | 5 2 7 22 | 6 3 8 23 | 7 3 10 24 | 8 1 8 25 | 9 4 9 26 | 27 | --- 28 | 29 | Code 30 | tbl1 %>% as.data.frame() 31 | Output 32 | **Adverse Event** **Grade 1** **Grade 2** **Grade 3** 33 | 1 Blood and lymphatic system disorders 1 (33) 34 | 2 Anaemia 1 (33) 35 | 3 Increased tendency to bruise 36 | 4 Iron deficiency anaemia 37 | 5 Thrombocytopenia 1 (33) 38 | 6 Gastrointestinal disorders 39 | 7 Difficult digestion 40 | 8 Intestinal dilatation 1 (33) 41 | 9 Myochosis 2 (67) 1 (33) 42 | 10 Non-erosive reflux disease 3 (100) 43 | 11 Pancreatic enzyme abnormality 1 (33) 44 | **Grade 4** **Grade 5** **Overall** **Grade 1** **Grade 2** **Grade 3** 45 | 1 1 (33) 1 (33) 3 (100) 46 | 2 1 (33) 2 (67) 1 (14) 47 | 3 1 (33) 1 (33) 48 | 4 1 (33) 1 (33) 2 (67) 1 (14) 2 (29) 49 | 5 1 (33) 2 (67) 3 (43) 50 | 6 2 (67) 1 (33) 3 (100) 51 | 7 3 (100) 3 (100) 1 (14) 52 | 8 1 (33) 1 (14) 1 (14) 53 | 9 3 (100) 1 (14) 54 | 10 3 (100) 1 (14) 55 | 11 1 (33) 1 (33) 3 (100) 2 (29) 1 (14) 1 (14) 56 | **Grade 4** **Grade 5** **Overall** **Grade 1** **Grade 2** **Grade 3** 57 | 1 1 (14) 6 (86) 7 (100) 1 (10) 58 | 2 1 (14) 3 (43) 5 (71) 2 (20) 59 | 3 3 (43) 2 (29) 5 (71) 60 | 4 1 (14) 1 (14) 5 (71) 1 (10) 2 (20) 61 | 5 4 (57) 7 (100) 1 (10) 3 (30) 62 | 6 2 (29) 5 (71) 7 (100) 63 | 7 1 (14) 2 (29) 1 (10) 64 | 8 1 (14) 3 (43) 2 (20) 1 (10) 65 | 9 1 (14) 3 (43) 5 (71) 3 (30) 1 (10) 66 | 10 3 (43) 3 (43) 7 (100) 4 (40) 67 | 11 1 (14) 5 (71) 2 (20) 1 (10) 2 (20) 68 | **Grade 4** **Grade 5** **Overall** 69 | 1 2 (20) 7 (70) 10 (100) 70 | 2 2 (20) 3 (30) 7 (70) 71 | 3 4 (40) 2 (20) 6 (60) 72 | 4 2 (20) 2 (20) 7 (70) 73 | 5 1 (10) 4 (40) 9 (90) 74 | 6 4 (40) 6 (60) 10 (100) 75 | 7 3 (30) 1 (10) 5 (50) 76 | 8 1 (10) 4 (40) 77 | 9 1 (10) 3 (30) 8 (80) 78 | 10 3 (30) 3 (30) 10 (100) 79 | 11 2 (20) 1 (10) 8 (80) 80 | 81 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/style_xxx.md: -------------------------------------------------------------------------------- 1 | # style_xxx works with tbl_ae family 2 | 3 | Code 4 | as.data.frame(t1) 5 | Output 6 | **Adverse Event** **Grade 1** **Grade 2** **Grade 3** 7 | 1 Blood and lymphatic system disorders xx (xx.x) xx (xx.x) xx (xx.x) 8 | 2 Anaemia xx (xx.x) xx (xx.x) xx (xx.x) 9 | 3 Increased tendency to bruise xx (xx.x) xx (xx.x) xx (xx.x) 10 | 4 Iron deficiency anaemia xx (xx.x) xx (xx.x) xx (xx.x) 11 | 5 Thrombocytopenia xx (xx.x) xx (xx.x) xx (xx.x) 12 | 6 Gastrointestinal disorders xx (xx.x) xx (xx.x) xx (xx.x) 13 | 7 Difficult digestion xx (xx.x) xx (xx.x) xx (xx.x) 14 | 8 Intestinal dilatation xx (xx.x) xx (xx.x) xx (xx.x) 15 | 9 Myochosis xx (xx.x) xx (xx.x) xx (xx.x) 16 | 10 Non-erosive reflux disease xx (xx.x) xx (xx.x) xx (xx.x) 17 | 11 Pancreatic enzyme abnormality xx (xx.x) xx (xx.x) xx (xx.x) 18 | **Grade 4** **Grade 5** 19 | 1 xx (xx.x) xx (xx.x) 20 | 2 xx (xx.x) xx (xx.x) 21 | 3 xx (xx.x) xx (xx.x) 22 | 4 xx (xx.x) xx (xx.x) 23 | 5 xx (xx.x) xx (xx.x) 24 | 6 xx (xx.x) xx (xx.x) 25 | 7 xx (xx.x) xx (xx.x) 26 | 8 xx (xx.x) xx (xx.x) 27 | 9 xx (xx.x) xx (xx.x) 28 | 10 xx (xx.x) xx (xx.x) 29 | 11 xx (xx.x) xx (xx.x) 30 | 31 | --- 32 | 33 | Code 34 | as.data.frame(t2) 35 | Output 36 | **Adverse Event** **Grade 1** **Grade 2** **Grade 3** 37 | 1 Blood and lymphatic system disorders xx xx xx 38 | 2 Anaemia xx xx xx 39 | 3 Increased tendency to bruise xx xx xx 40 | 4 Iron deficiency anaemia xx xx xx 41 | 5 Thrombocytopenia xx xx xx 42 | 6 Gastrointestinal disorders xx xx xx 43 | 7 Difficult digestion xx xx xx 44 | 8 Intestinal dilatation xx xx xx 45 | 9 Myochosis xx xx xx 46 | 10 Non-erosive reflux disease xx xx xx 47 | 11 Pancreatic enzyme abnormality xx xx xx 48 | **Grade 4** **Grade 5** 49 | 1 xx xx 50 | 2 xx xx 51 | 3 xx xx 52 | 4 xx xx 53 | 5 xx xx 54 | 6 xx xx 55 | 7 xx xx 56 | 8 xx xx 57 | 9 xx xx 58 | 10 xx xx 59 | 11 xx xx 60 | 61 | --- 62 | 63 | Code 64 | as.data.frame(t3) 65 | Output 66 | **Adverse Event** **Any Grade Complication** 67 | 1 Blood and lymphatic system disorders xx (xx.x) 68 | 2 Anaemia xx (xx.x) 69 | 3 Increased tendency to bruise xx (xx.x) 70 | 4 Iron deficiency anaemia xx (xx.x) 71 | 5 Thrombocytopenia xx (xx.x) 72 | 6 Gastrointestinal disorders xx (xx.x) 73 | 7 Difficult digestion xx (xx.x) 74 | 8 Intestinal dilatation xx (xx.x) 75 | 9 Myochosis xx (xx.x) 76 | 10 Non-erosive reflux disease xx (xx.x) 77 | 11 Pancreatic enzyme abnormality xx (xx.x) 78 | **Grade 3+ Complication** 79 | 1 xx (xx.x) 80 | 2 xx (xx.x) 81 | 3 xx (xx.x) 82 | 4 xx (xx.x) 83 | 5 xx (xx.x) 84 | 6 xx (xx.x) 85 | 7 xx (xx.x) 86 | 8 xx (xx.x) 87 | 9 xx (xx.x) 88 | 10 xx (xx.x) 89 | 11 xx (xx.x) 90 | 91 | # style_xxx works with tbl_reg_summary 92 | 93 | Code 94 | as.data.frame(t4) 95 | Output 96 | **Characteristic** **N = xx** 97 | 1 Biological Marker 98 | 2 N Non-missing xx 99 | 3 Mean (SD) xx (xx) 100 | 4 Median (Q1, Q3) xx (xx, xx) 101 | 5 Min, Max xx, xx 102 | 6 N Missing xx 103 | 7 Treatment Group 104 | 8 Drug A xx (xx.x%) 105 | 9 Drug B xx (xx.x%) 106 | 107 | --- 108 | 109 | Code 110 | as.data.frame(t5) 111 | Output 112 | **Characteristic** **N = xx** 113 | 1 Biological Marker 114 | 2 N Non-missing xx 115 | 3 Mean (SD) xx.x (xx.x) 116 | 4 Median (Q1, Q3) xx (xx, xx) 117 | 5 Min, Max xx, xx 118 | 6 N Missing xx.x 119 | 7 Treatment Group 120 | 8 Drug A xx (xx.x%) 121 | 9 Drug B xx (xx.x%) 122 | 123 | -------------------------------------------------------------------------------- /tests/testthat/test-add_overall.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | 3 | test_that("add_overall() works", { 4 | expect_error( 5 | tbl1 <- 6 | df_adverse_events %>% 7 | tbl_ae( 8 | id = patient_id, 9 | ae = adverse_event, 10 | by = grade, 11 | statistic = "{n}" 12 | ) %>% 13 | add_overall(across = 'by') %>% 14 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 15 | NA 16 | ) 17 | expect_snapshot( 18 | tbl1 %>% 19 | as.data.frame() 20 | ) 21 | 22 | expect_true( 23 | df_adverse_events %>% 24 | dplyr::select(patient_id, adverse_event) %>% 25 | dplyr::distinct() %>% 26 | dplyr::group_by(adverse_event) %>% 27 | dplyr::mutate( 28 | n = dplyr::n() %>% as.character() 29 | ) %>% 30 | dplyr::select(label = adverse_event, n) %>% 31 | dplyr::distinct() %>% 32 | dplyr::ungroup() %>% 33 | dplyr::inner_join( 34 | tbl1 %>% 35 | as_tibble(col_label = FALSE) %>% 36 | dplyr::select(label, stat_1_2), 37 | by = "label" 38 | ) %>% 39 | dplyr::mutate(check = n == stat_1_2) %>% 40 | dplyr::pull(check) %>% 41 | all() 42 | ) 43 | 44 | expect_error( 45 | tbl1 <- 46 | df_adverse_events %>% 47 | tbl_ae( 48 | id = patient_id, 49 | ae = adverse_event, 50 | soc = system_organ_class, 51 | by = grade, 52 | strata = trt 53 | ) %>% 54 | add_overall() %>% 55 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 56 | NA 57 | ) 58 | 59 | expect_snapshot( 60 | tbl1 %>% 61 | as.data.frame() 62 | ) 63 | 64 | expect_error( 65 | df_adverse_events %>% 66 | tbl_ae( 67 | id = patient_id, 68 | ae = adverse_event, 69 | soc = system_organ_class, 70 | by = grade, 71 | strata = trt 72 | ) %>% 73 | add_overall(across = 'strata') %>% 74 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 75 | NA 76 | ) 77 | 78 | expect_error( 79 | df_adverse_events %>% 80 | tbl_ae( 81 | id = patient_id, 82 | ae = adverse_event, 83 | soc = system_organ_class, 84 | by = grade, 85 | strata = trt 86 | ) %>% 87 | add_overall(across = 'overall-only'), 88 | NA 89 | ) 90 | }) 91 | 92 | 93 | test_that("add_overall() warns", { 94 | 95 | expect_message( 96 | df_adverse_events %>% 97 | tbl_ae( 98 | id = patient_id, 99 | ae = adverse_event, 100 | soc = system_organ_class, 101 | by = grade, 102 | statistic = "{n}" 103 | ) %>% 104 | add_overall(across = "both") %>% 105 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 106 | "Using `across = 'by'` instead." 107 | ) 108 | 109 | expect_message( 110 | df_adverse_events %>% 111 | tbl_ae( 112 | id = patient_id, 113 | ae = adverse_event, 114 | soc = system_organ_class, 115 | by = grade, 116 | statistic = "{n}" 117 | ) %>% 118 | add_overall(across = 'strata') %>% 119 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 120 | "Using `across = 'by'` instead." 121 | ) 122 | 123 | 124 | expect_message( 125 | df_adverse_events %>% 126 | tbl_ae( 127 | id = patient_id, 128 | ae = adverse_event, 129 | soc = system_organ_class, 130 | strata = trt, 131 | statistic = "{n}" 132 | ) %>% 133 | add_overall(across = 'by'), 134 | "Using `across = 'strata'` instead." 135 | ) 136 | 137 | expect_message( 138 | df_adverse_events %>% 139 | tbl_ae( 140 | id = patient_id, 141 | ae = adverse_event, 142 | soc = system_organ_class, 143 | strata = trt, 144 | statistic = "{n}" 145 | ) %>% 146 | add_overall(across = "both"), 147 | "Using `across = 'strata'` instead." 148 | ) 149 | 150 | }) 151 | 152 | test_that("no errors with `by_values=`", { 153 | tbl <- 154 | df_adverse_events %>% 155 | tbl_ae( 156 | id = patient_id, 157 | ae = adverse_event, 158 | soc = system_organ_class, 159 | by = grade, 160 | by_values = as.character(0:5), 161 | strata = trt 162 | ) 163 | 164 | expect_error(add_overall(tbl, across = "both"), NA) 165 | expect_error(add_overall(tbl, across = "by"), NA) 166 | expect_error(add_overall(tbl, across = "overall-only"), NA) 167 | }) 168 | 169 | test_that("add_overall(missing_location=) works", { 170 | expect_error( 171 | tbl <- 172 | df_adverse_events %>% 173 | dplyr::mutate( 174 | grade = ifelse(dplyr::row_number() == 1, NA, grade) 175 | ) %>% 176 | tbl_ae( 177 | ae = adverse_event, 178 | id = patient_id, 179 | soc = system_organ_class, 180 | by = grade, 181 | strata = trt, 182 | missing_location = "first" 183 | ) %>% 184 | add_overall() %>% 185 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 186 | NA 187 | ) 188 | expect_equal( 189 | tbl$table_styling$header %>% dplyr::filter(!hide) %>% dplyr::pull(label), 190 | c("**Adverse Event**", "**Unknown**", "**Grade 1**", "**Grade 2**", "**Grade 3**", 191 | "**Grade 4**", "**Grade 5**", "**Overall**", "**Unknown**", "**Grade 1**", "**Grade 2**", 192 | "**Grade 3**", "**Grade 4**", "**Grade 5**","**Overall**", "**Unknown**", "**Grade 1**", "**Grade 2**", 193 | "**Grade 3**", "**Grade 4**", "**Grade 5**", "**Overall**") 194 | ) 195 | 196 | expect_error( 197 | tbl <- 198 | df_adverse_events %>% 199 | dplyr::mutate( 200 | grade = ifelse(dplyr::row_number() == 1, NA, grade) 201 | ) %>% 202 | tbl_ae( 203 | ae = adverse_event, 204 | id = patient_id, 205 | soc = system_organ_class, 206 | by = grade, 207 | strata = trt, 208 | missing_location = "last" 209 | ) %>% 210 | add_overall() %>% 211 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 212 | NA 213 | ) 214 | expect_equal( 215 | tbl$table_styling$header %>% dplyr::filter(!hide) %>% dplyr::pull(label), 216 | c("**Adverse Event**", "**Grade 1**", "**Grade 2**", "**Grade 3**", 217 | "**Grade 4**", "**Grade 5**", "**Unknown**", "**Overall**", "**Grade 1**", "**Grade 2**", 218 | "**Grade 3**", "**Grade 4**", "**Grade 5**", "**Unknown**", "**Overall**", "**Grade 1**", "**Grade 2**", 219 | "**Grade 3**", "**Grade 4**", "**Grade 5**", "**Unknown**", "**Overall**") 220 | ) 221 | }) 222 | 223 | -------------------------------------------------------------------------------- /tests/testthat/test-inline_text.R: -------------------------------------------------------------------------------- 1 | test_that("inline text works", { 2 | expect_equal( 3 | df_adverse_events %>% 4 | tbl_ae( 5 | id = patient_id, 6 | ae = adverse_event, 7 | by = grade 8 | ) %>% 9 | inline_text(row = "Anaemia", column = stat_5), 10 | "3 (30)" 11 | ) 12 | 13 | expect_equal( 14 | df_adverse_events %>% 15 | tbl_ae_count( 16 | ae = adverse_event, 17 | by = grade 18 | ) %>% 19 | inline_text(row = "Anaemia", column = stat_4), 20 | "3" 21 | ) 22 | 23 | expect_equal( 24 | df_adverse_events %>% 25 | tbl_ae_focus( 26 | id = patient_id, 27 | ae = adverse_event, 28 | include = grade3_complication 29 | ) %>% 30 | inline_text(row = "Anaemia", column = stat_2_1), 31 | "7 (70)" 32 | ) 33 | 34 | expect_equal( 35 | df_adverse_events %>% 36 | tbl_ae_focus( 37 | id = patient_id, 38 | ae = adverse_event, 39 | include = grade3_complication 40 | ) %>% 41 | inline_text(row = "Anaemia"), 42 | NULL 43 | ) 44 | 45 | expect_equal( 46 | df_adverse_events %>% 47 | tbl_ae( 48 | id = patient_id, 49 | soc = system_organ_class, 50 | ae = adverse_event, 51 | by = grade 52 | ) %>% 53 | inline_text(row = "Blood and lymphatic system disorders", column = stat_5), 54 | "7 (70)" 55 | ) 56 | 57 | tbl <- 58 | df_adverse_events %>% 59 | tbl_ae( 60 | id = patient_id, 61 | soc = system_organ_class, 62 | ae = adverse_event, 63 | by = grade 64 | ) 65 | 66 | expect_error(inline_text(tbl, row = 1L, column = stat_1)) 67 | expect_error(inline_text(tbl, row = "not an AE", column = stat_1)) 68 | 69 | 70 | }) 71 | -------------------------------------------------------------------------------- /tests/testthat/test-modify_header.R: -------------------------------------------------------------------------------- 1 | test_that("modify_header() works", { 2 | tbl1 <- 3 | df_adverse_events %>% 4 | dplyr::mutate(grade = ifelse(dplyr::row_number() == 1L, NA, grade)) %>% 5 | tbl_ae( 6 | id = patient_id, 7 | id_df = df_patient_characteristics, 8 | ae = adverse_event, 9 | soc = system_organ_class, 10 | by = grade, 11 | strata = trt 12 | ) 13 | tbl2 <- add_overall(tbl1) 14 | 15 | expect_equal( 16 | modify_header(tbl1, all_ae_cols() ~ "**Grade {by}**") %>% 17 | as_tibble() %>% 18 | names(), 19 | c("**Adverse Event**", 20 | "**Unknown**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**", 21 | "**Unknown**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**") 22 | ) 23 | 24 | expect_equal( 25 | modify_header(tbl1, all_ae_cols(unknown = TRUE) ~ "**Grade {by}**") %>% 26 | as_tibble() %>% 27 | names(), 28 | c("**Adverse Event**", 29 | "**Grade Unknown**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**", 30 | "**Grade Unknown**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**") 31 | ) 32 | 33 | expect_equal( 34 | modify_header( 35 | tbl1, 36 | all_unknown_cols() ~ "**Unknown Grade**", 37 | all_ae_cols() ~ "**Grade {by}**" 38 | ) %>% 39 | as_tibble() %>% 40 | names(), 41 | c("**Adverse Event**", 42 | "**Unknown Grade**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**", 43 | "**Unknown Grade**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**") 44 | ) 45 | 46 | expect_equal( 47 | modify_spanning_header( 48 | tbl1, 49 | all_cols_in_strata("Drug A") ~ "**Control Group**, N = {n}/{N} ({style_percent(p)}%)", 50 | all_cols_in_strata("Drug B") ~ "**Experimental Group**, N = {n}/{N} ({style_percent(p)}%)" 51 | ) %>% 52 | purrr::pluck("table_styling", "spanning_header") %>% 53 | dplyr::filter(.by = c(level, column), dplyr::row_number() == dplyr::n(), startsWith(column, "stat_")) %>% 54 | dplyr::inner_join( 55 | tbl1$table_styling$header %>% dplyr::filter(!hide) %>% select(column), 56 | by = "column" 57 | ) %>% 58 | dplyr::pull(spanning_header) |> 59 | unique(), 60 | c("**Control Group**, N = 44/100 (44%)", "**Experimental Group**, N = 56/100 (56%)") 61 | ) 62 | 63 | expect_equal( 64 | modify_header( 65 | tbl2, 66 | all_overall_cols() ~ "**Total**", 67 | all_unknown_cols() ~ "**Unknown Grade**", 68 | all_ae_cols() ~ "**Grade {by}**" 69 | ) %>% 70 | as_tibble() %>% 71 | names(), 72 | c("**Adverse Event**", 73 | "**Unknown Grade**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**", "**Total**", 74 | "**Unknown Grade**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**", "**Total**", 75 | "**Unknown Grade**", "**Grade 1**", "**Grade 2**", "**Grade 3**", "**Grade 4**", "**Grade 5**", "**Total**") 76 | ) 77 | 78 | # modify_header works ------------------------------------------------------------------- 79 | t1 <- df_adverse_events %>% 80 | tbl_ae( 81 | id = patient_id, 82 | ae = adverse_event, 83 | strata = trt, 84 | statistic = "{n}", 85 | by = grade 86 | ) 87 | 88 | expect_error( 89 | t1_modified <- 90 | t1 %>% 91 | modify_header(all_ae_cols() ~ "**Grade {by}**, N = {n} / {N}") %>% 92 | modify_spanning_header(all_ae_cols() ~ "**{strata}**, N = {n} / {N}") %>% 93 | modify_footnote(label = "N = {N}") %>% 94 | modify_caption("N = {N}"), 95 | NA 96 | ) 97 | 98 | expect_equal( 99 | t1_modified$table_styling$header %>% filter(!hide) %>% dplyr::pull(label), 100 | c("**Adverse Event**", "**Grade 1**, N = 3 / 10", "**Grade 2**, N = 3 / 10", 101 | "**Grade 3**, N = 3 / 10", "**Grade 4**, N = 3 / 10", "**Grade 5**, N = 3 / 10", 102 | "**Grade 1**, N = 7 / 10", "**Grade 2**, N = 7 / 10", "**Grade 3**, N = 7 / 10", 103 | "**Grade 4**, N = 7 / 10", "**Grade 5**, N = 7 / 10") 104 | ) 105 | expect_equal( 106 | t1_modified$table_styling$header %>% 107 | filter(!hide) %>% 108 | dplyr::inner_join( 109 | t1_modified$table_styling$spanning_header %>% 110 | dplyr::slice_tail(by = c(level, column), n = 1) %>% 111 | dplyr::select(column, spanning_header), 112 | by = "column" 113 | ) %>% 114 | dplyr::pull(spanning_header) %>% 115 | unique(), 116 | c("**Drug A**, N = 3 / 10", "**Drug B**, N = 7 / 10") 117 | ) 118 | expect_equal( 119 | t1_modified$table_styling$caption, 120 | "N = 10", 121 | ignore_attr = TRUE 122 | ) 123 | }) 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /tests/testthat/test-style_xxx.R: -------------------------------------------------------------------------------- 1 | test_that("style_xxx works", { 2 | expect_error( 3 | style_xxx(7:10, width = 2, digits = 0), 4 | NA) 5 | 6 | expect_equal( 7 | style_xxx(7:10, width = 2, digits = 0), 8 | c("xx", "xx", "xx", "xx") 9 | ) 10 | 11 | expect_equal( 12 | style_xxx(7:10, width = 5, digits = 2), 13 | c("xx.xx", "xx.xx", "xx.xx", "xx.xx") 14 | ) 15 | 16 | }) 17 | 18 | 19 | 20 | test_that("style_xxx works with tbl_ae family", { 21 | 22 | expect_error( 23 | t1 <- df_adverse_events %>% 24 | tbl_ae( 25 | id = patient_id, 26 | ae = adverse_event, 27 | soc = system_organ_class, 28 | by = grade, 29 | zero_symbol = NULL, 30 | digits = 31 | list(purrr::partial(style_xxx, width = 2), 32 | purrr::partial(style_xxx, width = 4, digits = 1)) 33 | ) %>% 34 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 35 | modify_spanning_header(all_ae_cols() ~ "**N = xx**"), 36 | NA) 37 | 38 | expect_error( 39 | t2 <- df_adverse_events %>% 40 | tbl_ae_count( 41 | ae = adverse_event, 42 | soc = system_organ_class, 43 | by = grade, 44 | zero_symbol = NULL, 45 | digits = 46 | list(purrr::partial(style_xxx, width = 2)) 47 | ) %>% 48 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 49 | modify_spanning_header(all_ae_cols() ~ "**N = xx**"), 50 | NA) 51 | 52 | expect_error( 53 | t3 <- df_adverse_events %>% 54 | tbl_ae_focus( 55 | id = patient_id, 56 | ae = adverse_event, 57 | soc = system_organ_class, 58 | include = c(any_complication, grade3_complication), 59 | digits = 60 | list(purrr::partial(style_xxx, width = 2), 61 | purrr::partial(style_xxx, width = 4, digits = 1)) 62 | ) %>% 63 | modify_spanning_header(all_ae_cols() ~ "**N = xx**"), 64 | NA) 65 | 66 | 67 | expect_snapshot(as.data.frame(t1)) 68 | expect_snapshot(as.data.frame(t2)) 69 | expect_snapshot(as.data.frame(t3)) 70 | 71 | }) 72 | 73 | 74 | test_that("style_xxx works with tbl_reg_summary", { 75 | 76 | expect_error( 77 | t4 <- df_patient_characteristics %>% 78 | select(marker, trt) %>% 79 | tbl_reg_summary( 80 | digits = 81 | list( 82 | marker ~ purrr::partial(style_xxx, width = 2), 83 | trt ~ list(purrr::partial(style_xxx, width = 2), 84 | purrr::partial(style_xxx, width = 4, digits = 1)) 85 | ) 86 | ) %>% 87 | modify_header(stat_0 ~ "**N = xx**"), 88 | NA) 89 | 90 | expect_error( 91 | t5 <- df_patient_characteristics %>% 92 | select(marker, trt) %>% 93 | tbl_reg_summary( 94 | digits = 95 | list( 96 | marker ~ list(purrr::partial(style_xxx, width = 2), # N 97 | purrr::partial(style_xxx, width = 4, digits = 1), # Mean 98 | purrr::partial(style_xxx, width = 4, digits = 1), # SD 99 | purrr::partial(style_xxx, width = 2), # Median 100 | purrr::partial(style_xxx, width = 2), # IQR 101 | purrr::partial(style_xxx, width = 2), # Range 102 | purrr::partial(style_xxx, width = 2)), # N missing 103 | trt ~ list(purrr::partial(style_xxx, width = 2), 104 | purrr::partial(style_xxx, width = 4, digits = 1)) 105 | ) 106 | ) %>% 107 | modify_header(stat_0 ~ "**N = xx**"), 108 | NA) 109 | 110 | expect_snapshot(as.data.frame(t4)) 111 | expect_snapshot(as.data.frame(t5)) 112 | 113 | }) 114 | -------------------------------------------------------------------------------- /tests/testthat/test-tbl_ae_count.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | 3 | dat <- tibble::tribble( 4 | ~subject, ~visit, ~soc, ~ae, ~grade, 5 | # Subject 1 ---------------------------------------------------------------- 6 | "001", 1, "Eye disorders", "Eye irritation", 1, 7 | "001", 1, "Gastrointestinal disorders", "Difficult digestion", 1, 8 | "001", 2, "Eye disorders", "Eye irritation", 1, 9 | "001", 3, "Eye disorders", "Eye irritation", 2, 10 | "001", 4, "Eye disorders", "Vision blurred", 2, 11 | # Subject 2 ---------------------------------------------------------------- 12 | "002", 1, "Gastrointestinal disorders", "Difficult digestion", 2, 13 | "002", 1, "Gastrointestinal disorders", "Reflux", 2, 14 | "002", 2, "Eye disorders", "Vision blurred", 2, 15 | "002", 2, "Gastrointestinal disorders", "Reflux", 2, 16 | "002", 3, "Gastrointestinal disorders", "Reflux", NA 17 | ) 18 | 19 | dat_unobserved_stratum <- 20 | tibble::tribble( 21 | ~id, ~cohort, ~soc, ~pt, ~valxx, 22 | 1, "C1", "General disorders and administration site conditions", "Fatigue", "1", 23 | 1, "C1", "Vascular disorders", "Hot flush", "1", 24 | 1, "C1", "Investigations", "Weight increased", "2", 25 | 3, "C2", "Investigations", "Alanine aminotransferase increased", "1", 26 | 3, "C2", "Investigations", "Aspartate aminotransferase increased", "1", 27 | 3, "C2", "Nervous system disorders", "Cognitive disorder", "1", 28 | 16, "C3", "Musculoskeletal and connective tissue disorders", "Arthralgia", "1", 29 | 16, "C3", "Nervous system disorders", "Cognitive disorder", "1", 30 | 16, "C3", "General disorders and administration site conditions", "Fatigue", "1", 31 | 6, "C4", "Gastrointestinal disorders", "Diarrhoea", "2", 32 | 6, "C4", "Nervous system disorders", "Dysgeusia", "1", 33 | 6, "C4", "Vascular disorders", "Hot flush", "1" 34 | ) 35 | 36 | test_that("tbl_ae_count() works", { 37 | expect_error( 38 | tbl1 <- 39 | tbl_ae_count( 40 | data = df_adverse_events, 41 | ae = adverse_event, 42 | soc = system_organ_class, 43 | strata = trt, 44 | by = grade, 45 | zero_symbol = NULL 46 | ) %>% 47 | modify_header(all_ae_cols() ~ "**Grade {by}**"), 48 | NA 49 | ) 50 | 51 | expect_equal( 52 | tbl1$table_body %>% 53 | filter(label %in% "Blood and lymphatic system disorders") %>% 54 | dplyr::select(gtsummary::all_stat_cols()) %>% 55 | c() %>% 56 | unname() %>% 57 | unlist(), 58 | df_adverse_events %>% 59 | dplyr::filter(system_organ_class %in% "Blood and lymphatic system disorders") %>% 60 | with(table(trt, grade)) %>% 61 | t() %>% 62 | matrix(nrow = 1) %>% 63 | c() %>% 64 | as.character() 65 | ) 66 | 67 | expect_equal( 68 | tbl1$table_body %>% 69 | dplyr::filter(label %in% "Anaemia") %>% 70 | dplyr::select(gtsummary::all_stat_cols()) %>% 71 | c() %>% 72 | unname() %>% 73 | unlist(), 74 | c("0", "0", "1", "1", "0", "1", "0", "2", "2", "3") 75 | ) 76 | 77 | expect_error( 78 | tbl_ae_count( 79 | data = df_adverse_events, 80 | ae = adverse_event, 81 | soc = system_organ_class, 82 | strata = trt 83 | ), 84 | NA 85 | ) 86 | expect_error( 87 | tbl_ae_count( 88 | data = df_adverse_events, 89 | ae = adverse_event, 90 | soc = system_organ_class 91 | ), 92 | NA 93 | ) 94 | expect_error( 95 | tbl <- 96 | tbl_ae_count( 97 | data = df_adverse_events, 98 | ae = adverse_event, 99 | digits = function(x) style_number(x, digits = 2) 100 | ), 101 | NA 102 | ) 103 | expect_equal(as_tibble(tbl, col_labels = FALSE)$stat_1[1:3], 104 | c("10.00", "5.00", "9.00")) 105 | 106 | # bad call to missing_location= ---------------------------------------------- 107 | expect_error( 108 | df_adverse_events %>% 109 | tbl_ae_count( 110 | ae = adverse_event, 111 | by = grade, 112 | missing_location = "NOPE" 113 | ) 114 | ) 115 | 116 | expect_equal( 117 | dat %>% 118 | tbl_ae_count( 119 | ae = ae, 120 | soc = soc, 121 | by = grade, 122 | missing_location = "hide" 123 | ) %>% 124 | as_tibble() %>% 125 | names(), 126 | c("**Adverse Event**", "**1**", "**2**") 127 | ) 128 | 129 | expect_equal( 130 | dat %>% 131 | tbl_ae_count( 132 | ae = ae, 133 | soc = soc, 134 | by = grade, 135 | missing_location = "first" 136 | ) %>% 137 | as_tibble() %>% 138 | names(), 139 | c("**Adverse Event**", "**Unknown**", "**1**", "**2**") 140 | ) 141 | 142 | expect_equal( 143 | dat %>% 144 | tbl_ae_count( 145 | ae = ae, 146 | soc = soc, 147 | by = grade, 148 | missing_location = "last" 149 | ) %>% 150 | as_tibble() %>% 151 | names(), 152 | c("**Adverse Event**", "**1**", "**2**", "**Unknown**") 153 | ) 154 | 155 | # error messaging with by levels --------------------------------------------- 156 | expect_error( 157 | dat %>% 158 | dplyr::mutate( 159 | grade = ifelse(dplyr::row_number() == 1L, "Unknown", grade) 160 | ) %>% 161 | tbl_ae_count( 162 | ae = ae, 163 | soc = soc, 164 | by = grade 165 | ) 166 | ) 167 | expect_error( # no error when no NA present 168 | dat %>% 169 | tidyr::drop_na() %>% 170 | dplyr::mutate( 171 | grade = ifelse(dplyr::row_number() == 1L, "Unknown", grade) 172 | ) %>% 173 | tbl_ae_count( 174 | ae = ae, 175 | soc = soc, 176 | by = grade 177 | ), 178 | NA 179 | ) 180 | 181 | 182 | expect_error( 183 | dat %>% 184 | tbl_ae_count( 185 | ae = ae, 186 | soc = soc, 187 | by = grade, 188 | by_values = c("Unknown", 1:5) 189 | ) 190 | ) 191 | expect_error( # no error when no NA present 192 | dat %>% 193 | tidyr::drop_na() %>% 194 | tbl_ae_count( 195 | ae = ae, 196 | soc = soc, 197 | by = grade, 198 | by_values = c("Unknown", 1:5) 199 | ), 200 | NA 201 | ) 202 | }) 203 | 204 | test_that("tbl_ae_count() works with unobserved data in stratum", { 205 | expect_error( 206 | tbl_unobserved_stratum <- 207 | dat_unobserved_stratum %>% 208 | tbl_ae_count( 209 | ae = pt, 210 | soc = soc, 211 | by = valxx, 212 | strata = cohort 213 | ), 214 | NA 215 | ) 216 | 217 | # tabulating counts to compare against results from tbl_ae_count() 218 | df_count_checks_ae <- 219 | dat_unobserved_stratum %>% 220 | mutate(strata = paste(cohort, valxx)) %>% 221 | dplyr::mutate(across(c(soc, pt), factor)) %>% 222 | dplyr::count(soc, pt, strata) %>% 223 | select(label = pt, strata, n) 224 | 225 | df_count_checks_soc <- 226 | dat_unobserved_stratum %>% 227 | mutate(strata = paste(cohort, valxx)) %>% 228 | dplyr::count(soc, strata) %>% 229 | select(label = soc, strata, n) 230 | 231 | stat_col_rename <- 232 | tbl_unobserved_stratum$table_styling$header %>% 233 | dplyr::filter(!hide, startsWith(column, "stat_")) %>% 234 | dplyr::mutate_all( 235 | ~stringr::str_replace_all(., pattern = "\\*\\*(.*?)\\*\\*", replacement = "\\1") 236 | ) %>% 237 | dplyr::left_join(tbl_unobserved_stratum$table_styling$spanning_header[c("column", "spanning_header")], by = "column") %>% 238 | mutate(col_name = paste(spanning_header, label)) %>% 239 | select(col_name, column) %>% 240 | tibble::deframe() 241 | 242 | tbl_counts <- 243 | tbl_unobserved_stratum %>% 244 | gtsummary::modify_column_unhide(variable) %>% 245 | as_tibble(col_labels = FALSE) %>% 246 | dplyr::rename(!!!stat_col_rename) %>% 247 | tidyr::pivot_longer(cols = -c(variable, label)) %>% 248 | mutate(value = suppressWarnings(as.integer(value))) %>% 249 | tidyr::drop_na() %>% 250 | select(label, strata = name, n = value) %>% 251 | arrange(label, strata, n) 252 | 253 | expect_equal( 254 | dplyr::bind_rows(df_count_checks_soc, df_count_checks_ae) %>% 255 | arrange(label, strata, n), 256 | tbl_counts %>% 257 | mutate(strata = stringr::str_remove_all(strata, pattern = stringr::fixed("*"))) 258 | ) 259 | }) 260 | -------------------------------------------------------------------------------- /tests/testthat/test-tbl_ae_focus.R: -------------------------------------------------------------------------------- 1 | skip_on_cran() 2 | 3 | test_that("tbl_ae_focus() works", { 4 | expect_error( 5 | tbl1 <- 6 | df_adverse_events %>% 7 | tbl_ae_focus( 8 | include = c(any_complication, grade3_complication), 9 | id = patient_id, 10 | ae = adverse_event, 11 | soc = system_organ_class, 12 | statistic = "{n}", 13 | label = 14 | list( 15 | any_complication = "Any Grade Complication", 16 | grade3_complication = "Grade 3+ Complication" 17 | ) 18 | ), 19 | NA 20 | ) 21 | 22 | expect_equal( 23 | as_tibble(tbl1) %>% colnames(), 24 | c("**Adverse Event**", "**Any Grade Complication**", "**Grade 3+ Complication**") 25 | ) 26 | expect_equal( 27 | as_tibble(tbl1, col_labels = FALSE) %>% dplyr::slice(1) %>% dplyr::pull(2), 28 | df_adverse_events %>% 29 | dplyr::filter(system_organ_class %in% "Blood and lymphatic system disorders") %>% 30 | dplyr::select(patient_id) %>% 31 | dplyr::distinct() %>% 32 | nrow() %>% 33 | as.character() 34 | ) 35 | expect_equal( 36 | as_tibble(tbl1, col_labels = FALSE) %>% dplyr::slice(2) %>% dplyr::pull(2), 37 | df_adverse_events %>% 38 | dplyr::filter(adverse_event %in% "Anaemia") %>% 39 | dplyr::select(patient_id) %>% 40 | dplyr::distinct() %>% 41 | nrow() %>% 42 | as.character() 43 | ) 44 | 45 | expect_error( 46 | tbl2 <- 47 | df_adverse_events %>% 48 | tbl_ae_focus( 49 | include = c(any_complication, grade3_complication), 50 | id = patient_id, 51 | ae = adverse_event, 52 | digits = 1 53 | ), 54 | NA 55 | ) 56 | expect_equal( 57 | as_tibble(tbl2, col_labels = FALSE)$stat_2_1[1:2], 58 | c("7.0 (70.0)", "5.0 (50.0)") 59 | ) 60 | 61 | expect_error( 62 | df_adverse_events %>% 63 | tbl_ae_focus( 64 | include = c(any_complication, system_organ_class), 65 | id = patient_id, 66 | ae = adverse_event 67 | ) 68 | ) 69 | 70 | 71 | expect_error( 72 | tbl_ae_focus( 73 | data = letters, 74 | include = c(any_complication, grade3_complication), 75 | id = patient_id, 76 | ae = adverse_event, 77 | soc = system_organ_class 78 | ) 79 | ) 80 | 81 | expect_error( 82 | tbl_ae_focus( 83 | data = df_adverse_events, 84 | include = c(any_complication, grade3_complication), 85 | ae = adverse_event, 86 | soc = system_organ_class 87 | ) 88 | ) 89 | 90 | # checking returned percentages and labels 91 | tbl <- 92 | df_adverse_events %>% 93 | dplyr::mutate( 94 | dlt = ifelse(dplyr::row_number() == 1L, TRUE, FALSE) 95 | ) %>% 96 | tbl_ae_focus( 97 | id = patient_id, 98 | id_df = df_patient_characteristics, 99 | ae = adverse_event, 100 | include = dlt, 101 | label = list(dlt = "DLT"), 102 | zero_symbol = NULL 103 | ) %>% 104 | as_tibble() 105 | 106 | expect_equal( 107 | tbl %>% dplyr::pull(2), 108 | c("1 (1.0)", "0 (0)", "0 (0)", "0 (0)", "0 (0)", "0 (0)", "0 (0)", "0 (0)", "0 (0)") 109 | ) 110 | expect_equal( 111 | names(tbl), 112 | c("**Adverse Event**", "**DLT**") 113 | ) 114 | 115 | # include vars cannot be NA 116 | expect_error( 117 | df_adverse_events %>% 118 | dplyr::mutate( 119 | any_complication = ifelse(dplyr::row_number() == 1L, NA, any_complication) 120 | ) %>% 121 | tbl_ae_focus( 122 | id = patient_id, 123 | include = c(any_complication, grade3_complication), 124 | ae = adverse_event, 125 | soc = system_organ_class 126 | ) 127 | ) 128 | 129 | # spanning header without strata --------------------------------------------- 130 | tbl_no_strata <- 131 | df_adverse_events %>% 132 | tbl_ae_focus( 133 | id = patient_id, 134 | include = c(any_complication, grade3_complication), 135 | ae = adverse_event 136 | ) 137 | 138 | expect_equal( 139 | tbl_no_strata$table_styling$header %>% dplyr::filter(!hide) %>% select(-starts_with("modify_stat_"), -starts_with("modify_selector_")), 140 | tibble::tribble( 141 | ~column, ~hide, ~align, ~interpret_label, ~label, 142 | "label", FALSE, "left", "gt::md", "**Adverse Event**", 143 | "stat_2_1", FALSE, "center", "gt::md", "**Any Grade Complication**", 144 | "stat_2_2", FALSE, "center", "gt::md", "**Grade 3+ Complication**" 145 | ) 146 | ) 147 | 148 | 149 | # spanning header with strata --------------------------------------------- 150 | tbl_w_strata <- 151 | df_adverse_events %>% 152 | tbl_ae_focus( 153 | id = patient_id, 154 | strata = trt, 155 | include = c(any_complication, grade3_complication), 156 | ae = adverse_event 157 | ) 158 | 159 | expect_equal( 160 | tbl_w_strata$table_styling$header %>% dplyr::filter(!hide) %>% select(-starts_with("modify_stat_"), -starts_with("modify_selector_")), 161 | tibble::tribble( 162 | ~column, ~hide, ~align, ~interpret_label, ~label, 163 | "label", FALSE, "left", "gt::md", "**Adverse Event**", 164 | "stat_2_1_1", FALSE, "center", "gt::md", "**Any Grade Complication**", 165 | "stat_2_1_2", FALSE, "center", "gt::md", "**Grade 3+ Complication**", 166 | "stat_2_2_1", FALSE, "center", "gt::md", "**Any Grade Complication**", 167 | "stat_2_2_2", FALSE, "center", "gt::md", "**Grade 3+ Complication**" 168 | ) 169 | ) 170 | 171 | expect_equal( 172 | tbl_focs_soc <- 173 | df_adverse_events %>% 174 | tbl_ae_focus( 175 | id = patient_id, 176 | id_df = df_patient_characteristics, 177 | ae = adverse_event, 178 | soc = system_organ_class, 179 | include = c(any_complication, grade3_complication) 180 | ) %>% 181 | gtsummary::modify_table_body( 182 | ~dplyr::filter(.x, variable == "soc") 183 | ) %>% 184 | as_tibble(col_labels = FALSE), 185 | tibble::tribble( 186 | ~label, ~stat_2_1, ~stat_2_2, 187 | "Blood and lymphatic system disorders", "10 (10)", "9 (9.0)", 188 | "Gastrointestinal disorders", "10 (10)", "10 (10)" 189 | ) 190 | ) 191 | 192 | expect_equal( 193 | tbl_focs_soc %>% 194 | dplyr::select(label, stat_2_2), 195 | df_adverse_events %>% 196 | dplyr::select(patient_id, system_organ_class, grade3_complication) %>% 197 | dplyr::group_by(system_organ_class, patient_id) %>% 198 | dplyr::mutate(grade3_complication = max(grade3_complication)) %>% 199 | dplyr::distinct() %>% 200 | dplyr::full_join( 201 | df_patient_characteristics %>% 202 | dplyr::select(patient_id) %>% 203 | dplyr::mutate(system_organ_class = list(unique(df_adverse_events$system_organ_class))) %>% 204 | tidyr::unnest(system_organ_class), 205 | by = c("patient_id", "system_organ_class") 206 | ) %>% 207 | dplyr::mutate(grade3_complication = ifelse(is.na(grade3_complication), 0, grade3_complication)) %>% 208 | dplyr::group_by(system_organ_class) %>% 209 | dplyr::summarise( 210 | N = dplyr::n(), 211 | n = sum(grade3_complication), 212 | p = mean(grade3_complication) 213 | ) %>% 214 | dplyr::mutate( 215 | stat_2_2 = stringr::str_glue("{n} ({gtsummary::style_sigfig(p, scale = 100)})") %>% as.character() 216 | ) %>% 217 | dplyr::select(label = system_organ_class, stat_2_2), 218 | ignore_attr = TRUE 219 | ) 220 | 221 | # test that the columns still appear when no TRUE values are observed. 222 | expect_equal( 223 | df_adverse_events %>% 224 | dplyr::mutate( 225 | all_false = FALSE 226 | ) %>% 227 | tbl_ae_focus( 228 | include = all_false, 229 | id = patient_id, 230 | ae = adverse_event 231 | ) %>% 232 | as_tibble(fmt_missing = FALSE, col_labels = FALSE) %>% 233 | dplyr::pull(2) %>% 234 | unique(), 235 | NA_character_ 236 | ) 237 | 238 | # test to ensure formatting missing value cells are added to AEs, when no missing are present in SOC 239 | expect_equal( 240 | df_adverse_events %>% 241 | dplyr::mutate( 242 | grade = ifelse(grade == 5 & adverse_event == "Anaemia", 4L, grade), 243 | grade5_complication = grade == 5) %>% 244 | tbl_ae_focus( 245 | include = grade5_complication, 246 | id = patient_id, 247 | id_df = df_patient_characteristics, 248 | ae = adverse_event, 249 | soc = system_organ_class 250 | ) %>% 251 | purrr::pluck("table_styling", "fmt_missing") %>% 252 | dplyr::select(column, symbol), 253 | structure(list(column = c("stat_1_1", "stat_2_1", "stat_3_1"), 254 | symbol = c("—", "—", "—")), row.names = c(NA, -3L), class = c("tbl_df", 255 | "tbl", "data.frame")) 256 | ) 257 | 258 | }) 259 | -------------------------------------------------------------------------------- /tests/testthat/test-tbl_listing.R: -------------------------------------------------------------------------------- 1 | test_that("tbl_listing() works", { 2 | expect_error( 3 | tbl <- 4 | head(df_adverse_events, n = 10) %>% 5 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 6 | arrange(adverse_event, desc(grade)) %>% 7 | tbl_listing() %>% 8 | as_tibble(col_labels = FALSE), 9 | NA 10 | ) 11 | expect_equal( 12 | tbl, 13 | head(df_adverse_events, n = 10) %>% 14 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 15 | arrange(adverse_event, desc(grade)) 16 | ) 17 | # labels are correctly applied to tbl 18 | expect_equal( 19 | head(df_adverse_events, n = 10) %>% 20 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 21 | arrange(adverse_event, desc(grade)) %>% 22 | tbl_listing() %>% 23 | as_tibble(col_labels = TRUE) %>% 24 | names(), 25 | c("**System Organ Class**", "**Adverse Event**", "**Grade**", "**Drug Attribution**", "**Patient ID**") 26 | ) 27 | 28 | # check the two additional grouping rows are added to tbl 29 | expect_equal( 30 | head(df_adverse_events, n = 10) %>% 31 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 32 | arrange(adverse_event, desc(grade)) %>% 33 | tbl_listing(group_by = system_organ_class) %>% 34 | as_tibble(col_labels = FALSE) %>% 35 | nrow(), 36 | 12 37 | ) 38 | }) 39 | 40 | test_that("tbl_listing(group_by=) works with various column types", { 41 | expect_error( 42 | tbl <- head(df_adverse_events, n = 10) %>% 43 | mutate(num = rep(1:2, 5)) %>% 44 | select(system_organ_class, num, system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 45 | tbl_listing(group_by = system_organ_class, bold_headers = TRUE) %>% 46 | as_tibble(col_labels = FALSE), 47 | NA 48 | ) 49 | expect_equal(tbl$num[1:3], c("Blood and lymphatic system disorders", "1", "2")) 50 | 51 | expect_error( 52 | tbl <- head(df_adverse_events, n = 10) %>% 53 | mutate(num = rep(1:2, 5)) %>% 54 | select(num, grade, system_organ_class, adverse_event, drug_attribution, patient_id) %>% 55 | tbl_listing(group_by = num, bold_headers = TRUE) %>% 56 | as_tibble(col_labels = FALSE), 57 | NA 58 | ) 59 | expect_equal(tbl$grade[1:3], c(1L, 4L, 4L)) 60 | 61 | expect_error( 62 | tbl <- head(df_adverse_events, n = 10) %>% 63 | select(system_organ_class, adverse_event, grade, drug_attribution, patient_id) %>% 64 | mutate(system_organ_class = forcats::fct_relevel(system_organ_class, "Gastrointestinal disorders")) %>% 65 | tbl_listing(group_by = system_organ_class, bold_headers = TRUE) %>% 66 | as_tibble(col_labels = FALSE), 67 | NA 68 | ) 69 | expect_equal(tbl$adverse_event[1:3] %>% as.character(), 70 | c("Gastrointestinal disorders", "Intestinal dilatation", "Intestinal dilatation")) 71 | 72 | expect_error( 73 | tbl <- 74 | head(mtcars, n = 3) %>% 75 | tibble::as_tibble() %>% 76 | dplyr::mutate(cyl = factor(cyl)) %>% 77 | tbl_listing(group_by = "cyl"), 78 | NA 79 | ) 80 | expect_equal( 81 | as_tibble(tbl, col_labels = FALSE)[["mpg"]], 82 | c("4", "22.8", "6", "21", "21") 83 | ) 84 | 85 | expect_error( 86 | tbl <- 87 | head(mtcars, n = 3) %>% 88 | tibble::as_tibble() %>% 89 | dplyr::mutate(mpg = factor(mpg)) %>% 90 | tbl_listing(group_by = "mpg"), 91 | NA 92 | ) 93 | expect_equal( 94 | as_tibble(tbl, col_labels = FALSE)[["cyl"]], 95 | c("21", "6" , "6" , "22.8", "4" ) 96 | ) 97 | 98 | expect_error( 99 | tbl <- 100 | head(mtcars, n = 3) %>% 101 | tibble::as_tibble() %>% 102 | dplyr::mutate(mpg = factor(mpg), 103 | cyl = factor(cyl)) %>% 104 | tbl_listing(group_by = "cyl"), 105 | NA 106 | ) 107 | expect_equal( 108 | as_tibble(tbl, col_labels = FALSE)[["mpg"]], 109 | c("4", "22.8", "6", "21", "21") 110 | ) 111 | }) 112 | -------------------------------------------------------------------------------- /tests/testthat/test-tbl_reg_summary.R: -------------------------------------------------------------------------------- 1 | test_that("tbl_reg_summary works", { 2 | expect_error( 3 | df_patient_characteristics %>% 4 | tbl_reg_summary(by = trt, include = c(marker, status)), 5 | NA 6 | ) 7 | 8 | gtsummary::set_gtsummary_theme(list("tbl_summary-str:default_con_type" = "continuous")) 9 | expect_message( 10 | df_patient_characteristics %>% 11 | tbl_reg_summary(by = trt, include = c(marker, status)) 12 | ) 13 | gtsummary::reset_gtsummary_theme() 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testthat/test-utils-tbl_ae.R: -------------------------------------------------------------------------------- 1 | test_that("check_factor() fails when needed", { 2 | expect_equal(.check_factor("x"), factor("x")) 3 | expect_equal(.check_factor(factor("x")), factor("x")) 4 | 5 | expect_error(.check_factor(NA), "Input must be a factor or character vector.") 6 | }) 7 | 8 | test_that("factor unchanged if no missing levels", { 9 | 10 | f1 <- factor(letters[1:3]) 11 | f2 <- .fct_explicit_na(f1) 12 | 13 | expect_identical(f1, f2) 14 | }) 15 | 16 | test_that("converts implicit NA", { 17 | 18 | f1 <- factor(c("a", NA)) 19 | f2 <- .fct_explicit_na(f1) 20 | 21 | expect_equal(f2, factor(c("a", "(Missing)"), levels = c("a", "(Missing)"))) 22 | }) 23 | 24 | test_that("converts explicit NA", { 25 | 26 | f1 <- factor(c("a", NA), exclude = NULL) 27 | f2 <- .fct_explicit_na(f1) 28 | 29 | expect_equal(f2, factor(c("a", "(Missing)"), levels = c("a", "(Missing)"))) 30 | }) 31 | 32 | 33 | 34 | test_that("when chooses the correct action", { 35 | 36 | x <- 37 | 1:5 %>% 38 | .when( 39 | sum(.) <= 50 ~ sum(.), 40 | sum(.) <= 100 ~ sum(.) / 2, 41 | ~ 0 42 | ) 43 | 44 | expect_equal(x, 15) 45 | 46 | y <- 47 | 1:10 %>% 48 | .when( 49 | sum(.) <= 50 ~ sum(.), 50 | sum(.) <= 100 ~ sum(.) / 2, 51 | ~ 0 52 | ) 53 | 54 | expect_equal(y, sum(1:10) / 2) 55 | 56 | z <- 57 | 1:100 %>% 58 | .when( 59 | sum(.) <= 50 ~ sum(.), 60 | sum(.) <= 100 ~ sum(.) / 2, 61 | ~ 0 62 | ) 63 | 64 | expect_equal(z, 0) 65 | }) 66 | 67 | test_that("named arguments work with when", { 68 | 69 | x <- 70 | 1:10 %>% 71 | .when( 72 | sum(.) <= x ~ sum(.) * x, 73 | sum(.) <= 2 * x ~ sum(.) * x / 2, 74 | ~ 0, 75 | x = 60 76 | ) 77 | 78 | expect_equal(x, sum(1:10) * 60) 79 | }) 80 | 81 | test_that("default values work without a formula", { 82 | 83 | x <- iris %>% 84 | subset(Sepal.Length > 10) %>% 85 | .when( 86 | nrow(.) > 0 ~ ., 87 | head(iris, 10) 88 | ) 89 | 90 | expect_equal(x, head(iris, 10)) 91 | }) 92 | 93 | test_that("error when named arguments have no matching conditions", { 94 | 95 | expect_error(1:5 %>% .when(a = sum(.) < 5 ~ 3)) 96 | }) 97 | 98 | 99 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/articles/counting-methods.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Adverse Event Counting Methods" 3 | --- 4 | 5 | ```{r, include = FALSE} 6 | knitr::opts_chunk$set( 7 | collapse = TRUE, 8 | comment = "#>" 9 | ) 10 | ``` 11 | 12 | # Setup 13 | 14 | ```{r setup} 15 | # install.packages("gtreg") 16 | library(gtreg) 17 | ``` 18 | 19 | # Overview 20 | 21 | Reporting of adverse events to regulatory agencies typically employs both 22 | standardized terminology and specific counting methods for adverse events. 23 | 24 | # Standardized Terminology 25 | 26 | Providing standardized terminology is not in the scope of this package. Standardized 27 | terminology adheres to a hierarchy of terms from specific to general, as discussed 28 | in the [MedDRA Hierarchy](https://www.meddra.org/how-to-use/basics/hierarchy) 29 | documentation. In {gtreg}, we facilitate reporting of a single term 30 | (i.e., `ae`, or adverse event), or a lower level term within a higher level term 31 | (i.e., `ae` within `soc`, system organ class). 32 | 33 | # Example Data 34 | 35 | Data is typically recorded in a long format with multiple rows per subject. 36 | Events can be reported over time, where both multiple time points per subject and 37 | multiple events per time point are possible. It is possible that grade of the 38 | event is missing. 39 | 40 | ```{r create-data} 41 | dat <- tibble::tribble( 42 | ~subject, ~visit, ~soc, ~ae, ~grade, 43 | # Subject 1 ---------------------------------------------------------------- 44 | "001", 1, "Eye disorders", "Eye irritation", 1, 45 | "001", 1, "Gastrointestinal disorders", "Difficult digestion", NA, 46 | "001", 2, "Eye disorders", "Eye irritation", 1, 47 | "001", 3, "Eye disorders", "Eye irritation", 2, 48 | "001", 4, "Eye disorders", "Vision blurred", 2, 49 | # Subject 2 ---------------------------------------------------------------- 50 | "002", 1, "Gastrointestinal disorders", "Difficult digestion", 2, 51 | "002", 1, "Gastrointestinal disorders", "Reflux", 2, 52 | "002", 2, "Eye disorders", "Vision blurred", 2, 53 | "002", 2, "Gastrointestinal disorders", "Reflux", 2, 54 | "002", 3, "Gastrointestinal disorders", "Reflux", NA 55 | ) 56 | ``` 57 | 58 | ```{r print-data} 59 | dat 60 | ``` 61 | 62 | # Challenges Addressed 63 | 64 | Study participants without adverse events are absent from the adverse event 65 | raw data table. Furthermore, study participants may have multiple 66 | adverse events, often with different severity grades. 67 | 68 | These two features pose difficulties to computing the percentage of participants 69 | who experience a specific adverse event. 70 | 71 | 1. In the numerator, we need to make sure that we do not double count the same AE within 72 | each participant, and in the denominator we need to make sure we include all patients 73 | in the treatment arm (not just those who experience an AE). 74 | These challenges are amplified when AE terms need to roll up into system organ class 75 | summaries. 76 | Functions in {gtreg} address these problems (among others) by implementing industry 77 | recommended approaches. 78 | For further reading, the PHUSE white paper regarding 79 | [Analysis and Displays Associated with Adverse events](https://phuse.s3.eu-central-1.amazonaws.com/Deliverables/Standard+Analyses+and+Code+Sharing/Analyses+and+Displays+Associated+with+Adverse+Events+Focus+on+Adverse+Events+in+Phase+2-4+Clinical+Trials+and+Integrated+Summary.pdf) 80 | provides an excellent summary of such standards. 81 | 82 | # Counting All Events 83 | 84 | To count _all_ instances of any event, utilize `tbl_ae_count`. 85 | 86 | ```{r count-all} 87 | dat %>% 88 | tbl_ae_count( 89 | ae = ae, 90 | soc = soc, 91 | by = grade 92 | ) %>% 93 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 94 | add_overall() %>% 95 | bold_labels() 96 | ``` 97 | 98 | In this case, we have two subjects which experienced a total of 5 eye disorders and 4 99 | gastrointestinal disorders over the course of multiple time points. The only statistic 100 | allowed in `tbl_ae_count()` is `n`. 101 | 102 | # Counting By Highest Grade 103 | 104 | Rather than counting all events, it is typical in regulatory reporting to count 105 | one event per subject by the highest grade experienced. To count the highest grade 106 | per subject, utilize `tbl_ae()`. 107 | 108 | The input `by` variable can be numeric (e.g., grade 1, 2, 3) or factor 109 | (e.g. attribution unlikely, possible, definite). In the respective cases, 110 | the highest value observed would be according to numeric order or factor 111 | level order, with missing values considered as the lowest level. 112 | 113 | When counting by highest grade, the count reported in any cell cannot exceed the 114 | number of subjects. These methods apply to both the individual adverse events and 115 | the system organ class. 116 | 117 | ## Counts Only 118 | 119 | ```{r count-max-1} 120 | dat %>% 121 | tbl_ae( 122 | id = subject, 123 | ae = ae, 124 | soc = soc, 125 | by = grade, 126 | statistic = "{n}" 127 | ) %>% 128 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 129 | add_overall() %>% 130 | bold_labels() 131 | ``` 132 | 133 | ### Adverse Events 134 | 135 | * Subject `001` experienced two instances of grade 1 eye irritation and one instance 136 | of grade 2 eye irritation. The maximum grade experienced of eye irritation experienced is 2, 137 | resulting in a count of `1` for eye irritation. 138 | 139 | * Subject `001` and Subject `002` both experienced grade 2 vision blurred, resulting 140 | in a count of `2` of vision blurred. 141 | 142 | * Subject `002` experienced two instances of grade 2 reflux and one instance of 143 | reflux with unknown grade; in a count of `1` for grade 2 reflux. 144 | 145 | 146 | ### System Organ Class 147 | 148 | _Eye disorders_ 149 | 150 | + Subject `001` contributes 1 count to grade 2 eye irritation; together, subject `001` and `002` 151 | contribute 2 counts to grade 2 vision blurred. 152 | 153 | + 2 subjects experienced a grade 2 eye disorder. 154 | 155 | + 2 subjects overall experienced an eye disorder. 156 | 157 | _Gastrointestinal disorders_ 158 | 159 | + Subject `001` experienced difficult digestion of unknown grade, and contributes 160 | a count of 1 to unknown grade gastrointestinal disorder. 161 | 162 | + Subject `002` experienced both grade 2 difficult digestion and grade 2 reflux, and 163 | contributes a count of 1 to grade 2 gastrointestinal disorders. 164 | 165 | + 2 subjects overall experienced a gastrointestinal disorder. 166 | 167 | 168 | ## Counts With Percents 169 | 170 | The default statistic reported for `tbl_ae()` is `n (%)`. The percent represents 171 | the percent of all subjects present in the data. 172 | 173 | ```{r count-max-2} 174 | dat %>% 175 | tbl_ae( 176 | id = subject, 177 | ae = ae, 178 | soc = soc, 179 | by = grade 180 | ) %>% 181 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 182 | modify_spanning_header(all_ae_cols() ~ "**N = {N}**") %>% 183 | add_overall() %>% 184 | bold_labels() 185 | ``` 186 | 187 | In adverse event reporting, it is common that not all subjects experience 188 | an adverse event, and hence are not included in the event reporting data set. 189 | For example, suppose there were 3 subjects of interest in this study, but 190 | subject `003` did not experience an adverse event. In order to get the correct 191 | subject denominator in this case, supply `tbl_ae()` with `id_df`, a data frame 192 | containing all subject ids. 193 | 194 | 195 | ```{r count-max-3} 196 | dat %>% 197 | tbl_ae( 198 | id = subject, 199 | id_df = tibble::tibble(subject = c("001", "002", "003")), 200 | ae = ae, 201 | soc = soc, 202 | by = grade 203 | ) %>% 204 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 205 | modify_spanning_header(all_ae_cols() ~ "**N = {N}**") %>% 206 | add_overall() %>% 207 | bold_labels() 208 | ``` 209 | 210 | 211 | -------------------------------------------------------------------------------- /vignettes/articles/misc/gtreg-modify.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/vignettes/articles/misc/gtreg-modify.gif -------------------------------------------------------------------------------- /vignettes/articles/misc/out_excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/vignettes/articles/misc/out_excel.png -------------------------------------------------------------------------------- /vignettes/articles/misc/out_pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/vignettes/articles/misc/out_pdf.png -------------------------------------------------------------------------------- /vignettes/articles/misc/out_word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannonpileggi/gtreg/9141167200c3b30f0a17fcd87eb5c6e4f1fb4fd8/vignettes/articles/misc/out_word.png -------------------------------------------------------------------------------- /vignettes/articles/output-gtreg.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Output types" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Output types} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | # Setup 18 | 19 | ```{r setup} 20 | # install.packages("gtreg") 21 | library(gtreg) 22 | library(gtsummary) 23 | ``` 24 | 25 | # Overview 26 | 27 | As {gtreg} is built upon {gtsummary}, you can use the same methods you 28 | would use for {gtsummary} to output tables to various formats. 29 | A {gtreg} table can be exported with {gt}, {flextable}, {huxtable}, {kableExtra}, `knitr::kable()`, and as a tibble. 30 | See the [print engines available in {gtsummary}](https://www.danieldsjoberg.com/gtsummary/#gtsummary--r-markdown) for details. 31 | 32 | Here, we share approaches to common requests and recommendations for each output type. 33 | 34 | # Example table 35 | 36 | ```{r} 37 | table1 <- 38 | df_adverse_events %>% 39 | tbl_ae( 40 | id = patient_id, 41 | id_df = df_patient_characteristics, 42 | ae = adverse_event, 43 | soc = system_organ_class, 44 | by = grade 45 | ) %>% 46 | modify_header( 47 | all_ae_cols() ~ "**Grade {by}**" 48 | ) %>% 49 | bold_labels() 50 | ``` 51 | 52 | # Example outputs 53 | 54 | ## HTML 55 | 56 | The default output type is HTML. 57 | 58 | ```{r} 59 | table1 60 | ``` 61 | 62 | ## Word 63 | 64 | Leverage the [{flextable}](https://ardata-fr.github.io/flextable-book/) package for word output. 65 | 66 | ```{r, eval=FALSE} 67 | table1 %>% 68 | as_flex_table() %>% 69 | flextable::save_as_docx(path = "misc/ae_table.docx") 70 | ``` 71 | 72 | 73 | ![](misc/out_word.png) 74 | 75 | 76 | 77 | ## Excel 78 | 79 | Leverage the [{huxtable}](https://hughjonesd.github.io/huxtable/) package for excel output. 80 | 81 | ```{r, eval=FALSE} 82 | table1 %>% 83 | as_hux_xlsx( 84 | file = "misc/ae_table.xlsx" 85 | ) 86 | ``` 87 | 88 | ![](misc/out_excel.png) 89 | 90 | ## PDF 91 | 92 | Leverage the [{kableExtra}](https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_pdf.pdf) package for styled PDF output. 93 | 94 | To export to PDF, create a new `.Rmd` or `.qmd` document with a PDF output type. 95 | The YAML shown here is for `.Rmd`. 96 | You can then use `as_kable_extra()` to convert the table to the LaTeX required for pdf rendering, and `kable_styling()` for extra styling of the table. 97 | For more pdf styling options, see the blog post [Report Ready PDF tables with rmarkdown, knitr, kableExtra, and LaTeX](https://www.pipinghotdata.com/posts/2022-01-24-report-ready-pdf-tables-with-rmarkdown-knitr-kableextra-and-latex/). 98 | 99 | 100 | ````{verbatim} 101 | --- 102 | output: pdf_document 103 | --- 104 | 105 | 106 | ```{r} 107 | table1 %>% 108 | as_kable_extra( 109 | booktabs = TRUE 110 | ) %>% 111 | kableExtra::kable_styling( 112 | latex_options = "striped", 113 | stripe_color = "gray!15" 114 | ) 115 | ``` 116 | 117 | ```` 118 | 119 | ![](misc/out_pdf.png) 120 | -------------------------------------------------------------------------------- /vignettes/articles/table-modifications.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Table modifications: headers, footnotes, and captions" 3 | --- 4 | 5 | 6 | ```{r, include = FALSE} 7 | knitr::opts_chunk$set( 8 | collapse = TRUE, 9 | comment = "#>" 10 | ) 11 | ``` 12 | 13 | ```{r setup, warning=FALSE, message=FALSE} 14 | library(gtreg) 15 | library(dplyr) 16 | library(gt) 17 | gtsummary::theme_gtsummary_compact() 18 | ``` 19 | 20 | 21 | 22 | 23 | # Overview 24 | 25 | 26 | In general, use `modify_header()` and `modify_spanning_header()` to 27 | specify *how* headers should be modified; use in conjunction with column 28 | selectors to specify *which* column headers to modify. Use 29 | `show_header_names()` on a saved table object to display a usage guide 30 | for modifying headers. 31 | 32 | To specify footnotes and captions, use `modify_footnote()` and 33 | `modify_caption()`. 34 | 35 | For all `modify_` functions, formatting is applied via markdown and glue 36 | syntax can be used to insert summary statistics. In addition, a return 37 | carriage inserts a line break when preceded by two spaces: `\n`. 38 | 39 | # AE tables 40 | 41 | For adverse event tables functions `tbl_ae()`, `tbl_ae_focus()`, and 42 | `tbl_ae_count()`, use the {gtreg} column selectors `all_ae_cols()`, 43 | `all_overall_cols()`, `all_unknown_cols()`, and `all_cols_in_strata()` 44 | to specify *which* columns to apply formatting. 45 | 46 | ```{r, echo = FALSE} 47 | tibble::tribble( 48 | ~`Selector`, ~`AE`, ~`Overall`, ~`Unknown`, 49 | "`all_ae_cols()`", "✔️", "", "", 50 | "`all_overall_cols()`", "", "✔️", "", 51 | "`all_unknown_cols()`", "", "", "✔️" , 52 | "`all_ae_cols(overall = TRUE, unknown = TRUE)`", "✔️", "✔️", "✔️" 53 | ) %>% 54 | gt::gt() %>% 55 | gt::cols_align( 56 | align = "center", 57 | columns = c(AE, Overall, Unknown) 58 | ) %>% 59 | gt::tab_header( 60 | title = md("**{gtreg} column selectors and resulting columns selected.**") 61 | ) %>% 62 | cols_width( 63 | 2:4 ~ px(80) 64 | ) %>% 65 | tab_stubhead(label = "Selector") %>% 66 | fmt_markdown(1) 67 | 68 | ``` 69 | 70 |
71 | 72 | ```{r, eval=TRUE, echo=FALSE} 73 | #| fig.cap: > 74 | #| Demonstration of `modify_` functions used with {gtreg} column selectors. 75 | #| fig.alt: > 76 | #| Left hand side code, right hand side output table. Gif contains 8 frames 77 | #| that sequentially builds a table. 78 | knitr::include_graphics("misc/gtreg-modify.gif") 79 | ``` 80 | 81 | 82 | 83 | Additionally, the `all_cols_in_strata()` selector can be used 84 | with `modify_spanning_header()` to apply differing modifications 85 | within strata. 86 | 87 | ## tbl_ae() without strata 88 | 89 | ```{r} 90 | tbl1 <- df_adverse_events %>% 91 | # create a missing value to demonstrate unknown columns 92 | mutate(grade = ifelse(dplyr::row_number() == 1L, NA, grade)) %>% 93 | tbl_ae( 94 | id = patient_id, 95 | ae = adverse_event, 96 | soc = system_organ_class, 97 | by = grade 98 | ) %>% 99 | add_overall(across = 'by') %>% 100 | bold_labels() 101 | ``` 102 | 103 | ### Unmodified 104 | 105 | ```{r} 106 | # show_header_names(tbl1) 107 | tbl1 108 | ``` 109 | 110 | ### Modified 111 | 112 | ```{r} 113 | tbl1 %>% 114 | modify_header( 115 | label ~ "**Event**", 116 | all_ae_cols() ~ "**Grade {by}**", 117 | all_overall_cols() ~ "**Total**", 118 | all_unknown_cols() ~ "**Unknown Grade**" 119 | ) %>% 120 | modify_spanning_header( 121 | all_ae_cols(TRUE, TRUE) ~ "**All cohorts**, N = {N}" 122 | ) 123 | ``` 124 | 125 | ## tbl_ae() with strata 126 | 127 | ```{r} 128 | tbl2 <- 129 | df_adverse_events %>% 130 | tbl_ae( 131 | id = patient_id, 132 | soc = system_organ_class, 133 | ae = adverse_event, 134 | strata = trt, 135 | by = grade 136 | ) %>% 137 | bold_labels() 138 | ``` 139 | 140 | ### Unmodified 141 | 142 | ```{r} 143 | # show_header_names(tbl2) 144 | tbl2 145 | ``` 146 | 147 | ### Modified 148 | 149 | ```{r} 150 | tbl2 %>% 151 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 152 | modify_spanning_header(all_ae_cols() ~ "**{strata}** \n{style_percent(p)}% ({n}/{N})") %>% 153 | modify_caption("My caption: N = {N}") %>% 154 | modify_footnote(label = "My footnote: N = {N}") 155 | ``` 156 | 157 | ```{r} 158 | tbl2 %>% 159 | modify_spanning_header( 160 | all_cols_in_strata("Drug A") ~ "**Control Group** \n{style_percent(p)}% ({n}/{N})", 161 | all_cols_in_strata("Drug B") ~ "**Experimental Group** \n{style_percent(p)}% ({n}/{N})" 162 | ) 163 | ``` 164 | 165 | # Summary tables 166 | 167 | For `tbl_reg_summary()`, the `modify_header()` and 168 | `modify_spanning_header()` functions work with the {gtsummary} column 169 | selectors like `all_stat_cols()` to specify formatting of table headers. 170 | 171 | ## tbl_reg_summary(), without overall 172 | 173 | ```{r} 174 | tbl3 <- 175 | df_patient_characteristics %>% 176 | select(trt, marker, discontinued) %>% 177 | tbl_reg_summary( 178 | by = trt 179 | ) %>% 180 | bold_labels() 181 | ``` 182 | 183 | ### Unmodified 184 | 185 | ```{r} 186 | # show_header_names(tbl3) 187 | tbl3 188 | ``` 189 | 190 | ### Modified 191 | 192 | ```{r} 193 | tbl3 %>% 194 | modify_header( 195 | all_stat_cols() ~ "**{level}**, N = {n}/{N} ({style_percent(p)}%)" 196 | ) 197 | ``` 198 | 199 | ## tbl_reg_summary(), with overall 200 | 201 | ```{r} 202 | tbl4 <- 203 | df_patient_characteristics %>% 204 | select(trt, marker, discontinued) %>% 205 | tbl_reg_summary( 206 | by = trt 207 | ) %>% 208 | add_overall(last = TRUE) %>% 209 | bold_labels() 210 | ``` 211 | 212 | ### Unmodified 213 | 214 | ```{r} 215 | # show_header_names(tbl4) 216 | tbl4 217 | ``` 218 | 219 | ### Modified 220 | 221 | ```{r} 222 | tbl4 %>% 223 | modify_header( 224 | list( 225 | stat_1 ~ "**Control Group** \n N = {n}", 226 | stat_2 ~ "**Experimental Group** \n N = {n}", 227 | stat_0 ~ "**Overall** \n N = {N}" 228 | )) 229 | ``` 230 | -------------------------------------------------------------------------------- /vignettes/articles/table-shells.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Table shells" 3 | --- 4 | 5 | ```{r, include = FALSE} 6 | knitr::opts_chunk$set( 7 | collapse = TRUE, 8 | comment = "#>" 9 | ) 10 | ``` 11 | 12 | ```{r setup, warning=FALSE, message=FALSE} 13 | library(gtreg) 14 | library(dplyr) 15 | gtsummary::theme_gtsummary_compact() 16 | ``` 17 | 18 | # Overview 19 | 20 | Table shells are tables with generic `xx` place holders for numeric 21 | values. Table shells can be generated by: 22 | 23 | 1. Create dummy data or use your own data. 24 | 25 | 2. Pass the data to your function of choice. 26 | 27 | 3. Overwrite the statistic(s) shown to a fixed character string by implementing 28 | the `style_xxx()` function in the `digits` argument. 29 | 30 | See more details about the `digits` argument in `?tbl_ae`. Here are example 31 | specifications. 32 | 33 | ```{r, echo = FALSE} 34 | tibble::tribble( 35 | ~`Integer Specification`, ~`Function Specification`, 36 | "`digits = 1`", "`digits = function(x) style_number(x, digits = 1)`", 37 | "`digits = c(0, 1)`", "`digits = list(function(x) style_number(x, digits = 0), function(x) style_number(x, digits = 1))`" 38 | ) |> 39 | knitr::kable() 40 | ``` 41 | 42 | 43 | ```{r} 44 | df_patient_characteristics %>% 45 | select(marker) %>% 46 | tbl_reg_summary( 47 | # integer specification 48 | digits = everything() ~ 1 49 | # function specification 50 | # digits = everything() ~ function(x) style_number(x, digits = 1) 51 | ) 52 | ``` 53 | 54 | 55 | 56 | # `tbl_reg_summary()` uniform `xx` 57 | 58 | Here, we specify the `digits` argument to uniformly apply to all summary 59 | statistics in the table. Note that we apply formatting 60 | to all columns using the column selector `everything()`. 61 | 62 | ```{r} 63 | df_patient_characteristics %>% 64 | select(marker, trt) %>% 65 | tbl_reg_summary( 66 | digits = everything() ~ style_xxx 67 | ) %>% 68 | modify_header(stat_0 ~ "**N = xx**") 69 | ``` 70 | 71 | 72 | # `tbl_reg_summary()` custom `xx` 73 | 74 | If you want custom formatting that differs between statistics, implement 75 | the `digits` argument with a `list`. Categorical variables are selected using 76 | `all_categorical()` and have two statistics to specify (`n` and `%`). 77 | The multi-line continuous variables are selected with `all_continuous()` and have 78 | nine statistics to specify. 79 | 80 | 81 | ```{r} 82 | df_patient_characteristics %>% 83 | select(marker, trt) %>% 84 | tbl_reg_summary( 85 | digits = list( 86 | all_categorical() ~ list( 87 | style_xxx, # n 88 | function(x) style_xxx(x, width = 4, digits = 1) # % 89 | ), 90 | all_continuous() ~ list( 91 | style_xxx, # N 92 | function(x) style_xxx(x, width = 4, digits = 1), # Mean 93 | function(x) style_xxx(x, width = 4, digits = 1), # SD 94 | style_xxx, # Median 95 | style_xxx, # IQR lower 96 | style_xxx, # IQR upper 97 | style_xxx, # Range lower 98 | style_xxx, # Range upper 99 | style_xxx # N missing 100 | ))) %>% 101 | modify_header(stat_0 ~ "**N = xx**") 102 | ``` 103 | 104 | 105 | 106 | 107 | 108 | # `tbl_ae()` uniform `xx` 109 | 110 | First, we create some dummy data to fill in our table shell. Note 111 | that not all values of grade need to be observed as factor levels 112 | populate the tables. 113 | 114 | ```{r} 115 | dat_shell <- tribble( 116 | ~ id, ~ soc, ~ ae, ~ grade, 117 | 1, "SOC 1", "ae1", 1, 118 | 2, "SOC 1", "ae2", 1, 119 | 3, "SOC 2", "ae3", 1, 120 | 4, "SOC 2", "ae4", 1 121 | ) |> 122 | mutate( 123 | grade = factor(grade, levels = 1:5) 124 | ) 125 | 126 | dat_shell 127 | ``` 128 | 129 | 130 | Here, we specify the `digits` argument to uniformly apply to all summary 131 | statistics in the table; in this case this is both `n` and `%`. As `tbl_ae()` 132 | provides the same summary statistic(s) across all variables, column selectors 133 | are not required. 134 | 135 | Be sure to use the `zero_symbol = NULL` option in `tbl_ae*` functions to 136 | print `xx`'s rather than dashes for zero cells. 137 | 138 | 139 | ```{r} 140 | dat_shell %>% 141 | tbl_ae( 142 | id = id, 143 | ae = ae, 144 | soc = soc, 145 | by = grade, 146 | zero_symbol = NULL, 147 | digits = style_xxx 148 | ) %>% 149 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 150 | modify_spanning_header(all_ae_cols() ~ "**N = xx**") 151 | ``` 152 | 153 | # `tbl_ae()` custom `xx` 154 | 155 | If you want custom formatting that differs between `n` and `%`, specify 156 | the `digits` argument with a `list`. 157 | 158 | ```{r} 159 | dat_shell %>% 160 | tbl_ae( 161 | id = id, 162 | ae = ae, 163 | soc = soc, 164 | by = grade, 165 | zero_symbol = NULL, 166 | digits = list( 167 | style_xxx, # n 168 | function(x) style_xxx(x, width = 4, digits = 1) # % 169 | )) %>% 170 | modify_header(all_ae_cols() ~ "**Grade {by}**") %>% 171 | modify_spanning_header(all_ae_cols() ~ "**N = xx**") 172 | ``` 173 | -------------------------------------------------------------------------------- /vignettes/further_documentation.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Further documentation" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Further documentation} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | Further long form documentation is available in the "Articles" section of the 18 | {gtreg} website. 19 | 20 | 1. [Adverse event counting methods.](https://shannonpileggi.github.io/gtreg/articles/counting-methods.html) 21 | 22 | 2. [Output types.](https://shannonpileggi.github.io/gtreg/articles/output-gtreg.html) 23 | 24 | 3. [Table modifications: headers, footnotes, and captions.](https://shannonpileggi.github.io/gtreg/articles/table-modifications.html) 25 | 26 | 4. [Table shells.](https://shannonpileggi.github.io/gtreg/articles/table-shells.html) 27 | 28 | --------------------------------------------------------------------------------