diff --git a/.Rbuildignore b/.Rbuildignore index e4b61a8f95..b2ca62554d 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -25,3 +25,6 @@ ^admiral\.Rcheck$ ^admiral.*\.tar\.gz$ ^admiral.*\.tgz$ +^\.lycheeignore$ +^staged_dependencies.yaml$ +^inst/dev_dependencies.R$ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a9518b802a..629d6b1c7a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,13 +1,13 @@ # Contribution to {admiral} -This outlines how to propose a change to the admiral package. For more detailed info about contributing to {admiral}, and other [pharmaverse packages](https://pharmaverse.org/), please see the [development process guide](https://pharmaverse.github.io/admiral/articles/development_process.html) as well as other Developer Guides in the Articles section of the [{admiral} website](https://pharmaverse.github.io/admiral/index.html) +This outlines how to propose a change to the admiral package. For more detailed info about contributing to {admiral}, and other [pharmaverse packages](https://pharmaverse.org/), please see the [development process guide](https://pharmaverse.github.io/admiraldev/main/articles/development_process.html) as well as other Developer Guides in the Articles section of the [{admiral} website](https://pharmaverse.github.io/admiral/index.html) # Basics * For each new contribution, the user creates an issue on the issue tab on [GitHub](https://github.com/pharmaverse/admiral/issues) to put it in our backlog. The issues can range from bug identification and/or fixes, enhancements to functions, documentation, tests or new features. * We advise you to contact us when an [issue](https://github.com/pharmaverse/admiral/issues) is created via [Slack](https://app.slack.com/client/T028PB489D3/C02M8KN8269) (If you don't have access, use this [link](https://join.slack.com/t/pharmaverse/shared_invite/zt-yv5atkr4-Np2ytJ6W_QKz_4Olo7Jo9A) to join). We can discuss details or align expectations if you are not familiar with the `{admiral}` philosophy and programming strategy. The team will try to review the issues within the next backlog meeting and give some initial feedback. Since we are not a 100% fully resourced software development team it might be that some issues will take longer to respond to depending on the amount of overall issues. - * Familiarize yourself with our [programming strategy](https://pharmaverse.github.io/admiral/articles/programming_strategy.html), guidance for [GitHub usage](https://pharmaverse.github.io/admiral/articles/git_usage.html) and [unit testing](https://pharmaverse.github.io/admiral/articles/unit_test_guidance.html). + * Familiarize yourself with our [programming strategy](https://pharmaverse.github.io/admiraldev/main/articles/programming_strategy.html), guidance for [GitHub usage](https://pharmaverse.github.io/admiraldev/main/articles/git_usage.html) and [unit testing](https://pharmaverse.github.io/admiraldev/main/articles/unit_test_guidance.html). * All newly [created issues](https://github.com/pharmaverse/admiral/issues) will be reviewed within the next backlog meeting and the creator will receive an initial feedback via a comment. Someone from the core development team will then triage new issues by assigning the appropriate labels (such as β€œuser request” so we can easily identify new requests). diff --git a/.github/ISSUE_TEMPLATE/05_onboard.yml b/.github/ISSUE_TEMPLATE/05_onboard.yml index d40c3f8721..d0ec7a6d6f 100644 --- a/.github/ISSUE_TEMPLATE/05_onboard.yml +++ b/.github/ISSUE_TEMPLATE/05_onboard.yml @@ -18,7 +18,7 @@ body: - label: Given a tour of Github from a Core member - label: Understand how to Create Issues and do a Pull Request - label: Understand the Programming Strategy - - label: Read and understand [Developer Guides Articles](https://pharmaverse.github.io/admiral/index.html) + - label: Read and understand [Developer Guides Articles](https://pharmaverse.github.io/admiraldev/main/articles/development_process.html) - label: Invited to all relevant meetings - Stand Ups, Question/Comments, Backlog, Community Meeting - label: Given access to Box and relevant documents - label: Given write access to Github Repository @@ -33,7 +33,7 @@ body: - label: Given a tour of Github from a Core member - label: Understand how to Create Issues and do a Pull Request - label: Understand the Programming Strategy - - label: Read and understand [Developer Guides Articles](https://pharmaverse.github.io/admiral/index.html) + - label: Read and understand [Developer Guides Articles](https://pharmaverse.github.io/admiraldev/main/articles/development_process.html) - label: Invited to all relevant meetings - Question/Comments, Community Meeting - label: Given write access to Github Repository - label: Slack channel invites to admiral diff --git a/.github/PULL_REQUEST_TEMPLATE/release.md b/.github/PULL_REQUEST_TEMPLATE/release.md index 5e7b61b15e..5e01a43b89 100644 --- a/.github/PULL_REQUEST_TEMPLATE/release.md +++ b/.github/PULL_REQUEST_TEMPLATE/release.md @@ -13,11 +13,16 @@ - [ ] Website is deployed and visual checks for integrity completed - [ ] Prepare and Post announcements for Linked-In and Slack Channels for Release + # Clean up Checklist +- [ ] Delete patch and pre-release branches - [ ] Delete branches that are stale or abandoned or leftover from a Pull Request - [ ] Triage and close issues that were not closed properly via PRs that made it into release - [ ] Close Pull Requests that are stale -- [ ] Set up and increment News/Changelog Skeleton +- [ ] Set up and increment News/Changelog Skeleton and Description File - [ ] Retire labels specific to a time-period (testing feedback) if no longer in use -- [ ] Remove Users with no acitivty for 12 months from Repository +- [ ] Remove Users with no activity for 12 months from Repository +- [ ] Update Release Schedule on Readme and Github Vignette with new Quarters and Dates +- [ ] Verify any pre-release or patch updates are appropriately merged into devel + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d2c16f6684..1aa352d865 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,12 +1,12 @@ -Thank you for your Pull Request! We have developed this task checklist from the [Development Process Guide](https://pharmaverse.github.io/admiral/articles/development_process.html) to help with the final steps of the process. Completing the below tasks helps to ensure our reviewers can maximize their time on your code as well as making sure the admiral codebase remains robust and consistent. +Thank you for your Pull Request! We have developed this task checklist from the [Development Process Guide](https://pharmaverse.github.io/admiraldev/main/articles/development_process.html) to help with the final steps of the process. Completing the below tasks helps to ensure our reviewers can maximize their time on your code as well as making sure the admiral codebase remains robust and consistent. Please check off each taskbox as an acknowledgment that you completed the task or check off that it is not relevant to your Pull Request. This checklist is part of the Github Action workflows and the Pull Request will not be merged into the `devel` branch until you have checked off each task. - [ ] Place Closes # into the beginning of your Pull Request Title (Use Edit button in top-right if you need to update) -- [ ] Code is formatted according to the [tidyverse style guide](https://style.tidyverse.org/) -- [ ] Updated relevant unit tests or have written new unit tests - See [Unit Test Guide](https://pharmaverse.github.io/admiral/articles/unit_test_guidance.html#writing-unit-tests-in-admiral-) -- [ ] If you removed/replaced any function and/or function parameters, did you fully follow the [deprecation guidance](https://pharmaverse.github.io/admiral/articles/programming_strategy.html#deprecation-1)? +- [ ] Code is formatted according to the [tidyverse style guide](https://style.tidyverse.org/). Run `styler::style_file()` to style R and Rmd files +- [ ] Updated relevant unit tests or have written new unit tests - See [Unit Test Guide](https://pharmaverse.github.io/admiraldev/main/articles/unit_test_guidance.html#writing-unit-tests-in-admiral-) +- [ ] If you removed/replaced any function and/or function parameters, did you fully follow the [deprecation guidance](https://pharmaverse.github.io/admiraldev/main/articles/programming_strategy.html#deprecation-1)? - [ ] Update to all relevant roxygen headers and examples - [ ] Run `devtools::document()` so all `.Rd` files in the `man` folder and the `NAMESPACE` file in the project root are updated appropriately - [ ] Address any updates needed for vignettes and/or templates diff --git a/.github/workflows/R-CMD-check.yml b/.github/workflows/R-CMD-check.yml deleted file mode 100644 index 1a5ed73e6a..0000000000 --- a/.github/workflows/R-CMD-check.yml +++ /dev/null @@ -1,94 +0,0 @@ -on: - workflow_dispatch: - push: - branches: - - main - - patch - - devel - pull_request: - branches: - - main - - patch - - devel - -name: R-CMD-check - -jobs: - R-CMD-check: - runs-on: ${{ matrix.config.os }} - - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - - strategy: - fail-fast: false - matrix: - config: - - {os: ubuntu-latest, r: '3.6', repos: 'https://cran.microsoft.com/snapshot/2020-02-29'} - - {os: ubuntu-latest, r: '4.0', repos: 'https://cran.microsoft.com/snapshot/2021-03-31/'} - - {os: ubuntu-latest, r: 'release', repos: 'https://packagemanager.rstudio.com/cran/__linux__/focal/latest'} - - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - R_KEEP_PKG_SOURCE: true - R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - R_REPOS: ${{ matrix.config.repos }} - - steps: - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@v1 - with: - r-version: ${{ matrix.config.r }} - http-user-agent: ${{ matrix.config.http-user-agent }} - use-public-rspm: true - - - uses: actions/cache@v2 - with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-r-${{ matrix.config.r }}-${{ hashFiles('DESCRIPTION') }} - restore-keys: ${{ runner.os }}-r-${{ matrix.config.r }} - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - options(repos = Sys.getenv("R_REPOS")) - remotes::install_deps(dependencies = TRUE) - ref <- if (Sys.getenv("GITHUB_REF_NAME") %in% c("main", "patch")) "main" else "devel" - remotes::install_github( - "pharmaverse/admiral.test", - ref = ref, - dependencies = FALSE, - upgrade = "never" - ) - shell: Rscript {0} - - - name: Check - env: - _R_CHECK_CRAN_INCOMING_REMOTE_: false - _R_CHECK_FORCE_SUGGESTS_: false - run: | - if (!requireNamespace("rcmdcheck", quietly = TRUE)) install.packages("rcmdcheck") - options(crayon.enabled = TRUE) - rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "note", check_dir = "check") - shell: Rscript {0} - - - name: Upload check results - if: failure() - uses: actions/upload-artifact@main - with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results - path: check diff --git a/.github/workflows/check_templates.yml b/.github/workflows/check_templates.yml deleted file mode 100644 index e2fdfaab73..0000000000 --- a/.github/workflows/check_templates.yml +++ /dev/null @@ -1,71 +0,0 @@ -on: - workflow_dispatch: - pull_request_review: - types: [submitted] - -name: Check Templates - -jobs: - check_templates: - if: github.event.review.state == 'approved' - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - - steps: - - uses: actions/cache@v2 - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - remotes::install_github("pharmaverse/admiral.test", ref = "devel") - shell: Rscript {0} - - - name: Install package - run: R CMD INSTALL . - - - name: Run Template Scripts - run: | - templates <- list.files( - system.file("templates", package = "admiral"), - full.names = TRUE - ) - exit_codes <- purrr::map_chr(templates, function(file) { - cmd <- sprintf("Rscript --vanilla %s", file) - system(cmd) - }) - if (any(exit_codes == 1L)) { - failed_scripts <- basename(templates)[which(exit_codes == 1L)] - err_msg <- sprintf( - "Executing the following template scripts failed:\n%s", - paste("-", failed_scripts, collapse = "\n") - ) - stop(err_msg, call. = FALSE) - } - shell: Rscript {0} diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml deleted file mode 100644 index dc5ecea770..0000000000 --- a/.github/workflows/code-coverage.yml +++ /dev/null @@ -1,185 +0,0 @@ -on: - workflow_dispatch: - push: - branches: - - main - - patch - - devel - pull_request: - branches: - - main - - patch - - devel - -name: Code Coverage β˜‚ - -jobs: - coverage: - name: Test Coverage πŸ§ͺ - runs-on: ubuntu-20.04 - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - outputs: - coverage-percent: ${{ steps.set-coverage-percentage.outputs.coverage-percentage }} - steps: - - uses: actions/cache@v2 - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Setup R - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - name: Setup Pandoc - uses: r-lib/actions/setup-pandoc@v1 - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - shell: Rscript {0} - - - name: Install package - run: R CMD INSTALL . - - - name: Run coverage πŸ‘Ÿ - run: | - tryCatch( - expr = { - x <- covr::package_coverage( - path=".", - clean = FALSE, - quiet = FALSE - ) - print(x) - covr::to_cobertura(x, filename = "coverage.xml") - p <- round(covr::percent_coverage(x)) - cat(p, file = "coverage.txt", sep = "") - covr::report( - x, - file = "coverage-report.html", - browse = FALSE - ) - }, - error = function(e) { - message("Errors generated during coverage analysis:") - print(e) - error_file <- stringr::str_match(e, "\`(.*?)\`")[, 2] - if (file.exists(error_file)) { - cat("__________FULL OUTPUT__________") - writeLines(readLines(error_file)) - } - }, - warning = function(w) { - message("Warnings generated during coverage analysis:") - print(w) - } - ) - shell: Rscript {0} - - - name: Check whether coverage reports exists - id: check_coverage_reports - uses: andstor/file-existence-action@v1 - with: - files: "coverage.xml, coverage.txt, coverage-report.html" - - - name: Set coverage percentage as output - id: set-coverage-percentage - run: echo "::set-output name=coverage-percentage::$(cat coverage.txt)" - if: steps.check_coverage_reports.outputs.files_exists == 'true' - - - name: Generate Coverage Summary Report - if: steps.check_coverage_reports.outputs.files_exists == 'true' && github.event_name == 'pull_request' - uses: irongut/CodeCoverageSummary@v1.2.0 - with: - filename: coverage.xml - badge: true - fail_below_min: false - format: markdown - hide_branch_rate: true - hide_complexity: true - indicators: true - output: both - thresholds: '60 80' - - - name: Upload report for review - if: steps.check_coverage_reports.outputs.files_exists == 'true' && github.event_name == 'pull_request' - uses: actions/upload-artifact@v2 - with: - name: coverage-report - path: "coverage-report.html" - - - name: Add Coverage PR Comment - if: steps.check_coverage_reports.outputs.files_exists == 'true' && github.event_name == 'pull_request' - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: code-coverage - path: code-coverage-results.md - - badge: - name: Generate badge for coverage - needs: [coverage] - runs-on: ubuntu-latest - steps: - - name: Checkout the badges branch in repo - uses: actions/checkout@v3 - with: - ref: badges - path: badges - # Use the output from the `coverage` step - - name: Generate the badge SVG image - uses: emibcn/badge-action@v1 - id: badge - with: - label: 'Test Coverage' - status: "${{ needs.coverage.outputs.coverage-percent }}%" - color: ${{ - needs.coverage.outputs.coverage-percent > 90 && 'green' || - needs.coverage.outputs.coverage-percent > 80 && 'yellow,green' || - needs.coverage.outputs.coverage-percent > 70 && 'yellow' || - needs.coverage.outputs.coverage-percent > 60 && 'orange,yellow' || - needs.coverage.outputs.coverage-percent > 50 && 'orange' || - needs.coverage.outputs.coverage-percent > 40 && 'red,orange' || - needs.coverage.outputs.coverage-percent > 30 && 'red,red,orange' || - needs.coverage.outputs.coverage-percent > 20 && 'red,red,red,orange' || - 'red' }} - path: badges/test-coverage.svg - - - name: Commit badge - working-directory: ./badges - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Actions" - BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} - mkdir -p "${BRANCH}" - mv test-coverage.svg "${BRANCH}" - git add "${BRANCH}/test-coverage.svg" - git commit -m "Add/Update badge" || true - - - name: Push badges - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: badges - directory: badges diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml new file mode 100644 index 0000000000..188020422e --- /dev/null +++ b/.github/workflows/common.yml @@ -0,0 +1,108 @@ +--- +# Source: https://github.com/pharmaverse/admiralci +# Common workflows designed for Admiral +# but can be easily used by any other R package +name: Admiral Workflows + +on: + # 'workflow_dispatch' gives you the ability + # to run this workflow on demand, anytime + workflow_dispatch: + # 'push' events are triggered when commits + # are pushed to one of these branches + push: + branches: + - main + - devel + - pre-release + # 'pull_request' events are triggered when PRs are + # created against one of these target branches. + pull_request: + branches: + - main + - devel + - pre-release + # 'release' events are triggered when... + # you guessed it - when releases are made. + release: + types: [published] + +env: + # R version to use for the workflows + R_VERSION: "3.6" + +# Docs on concurrency: +# https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: admiral-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + style: + name: Code Style + uses: pharmaverse/admiralci/.github/workflows/style.yml@main + if: github.event_name == 'pull_request' + with: + r-version: $R_VERSION + spellcheck: + name: Spelling + uses: pharmaverse/admiralci/.github/workflows/spellcheck.yml@main + if: github.event_name == 'pull_request' + with: + r-version: $R_VERSION + readme: + name: Render README + uses: pharmaverse/admiralci/.github/workflows/readme-render.yml@main + if: github.event_name == 'push' + with: + r-version: $R_VERSION + validation: + name: Validation + uses: pharmaverse/admiralci/.github/workflows/r-pkg-validation.yml@main + if: github.event_name == 'release' + with: + r-version: $R_VERSION + check: + name: Check + uses: pharmaverse/admiralci/.github/workflows/r-cmd-check.yml@main + if: github.event_name == 'pull_request' + docs: + name: Documentation + uses: pharmaverse/admiralci/.github/workflows/pkgdown.yml@main + # Only run this for main + # Change this after the next release to remove the ref condition + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + with: + r-version: $R_VERSION + # Whether to skip multiversion docs + # Note that if you have multiple versions of docs, + # your URL links are likely to break due to path changes + skip-multiversion-docs: true + linter: + name: Lint + uses: pharmaverse/admiralci/.github/workflows/lintr.yml@main + if: github.event_name == 'pull_request' + with: + r-version: $R_VERSION + links: + name: Links + uses: pharmaverse/admiralci/.github/workflows/links.yml@main + if: > + github.event_name == 'push' || github.event_name == 'pull_request' + coverage: + name: Code Coverage + uses: pharmaverse/admiralci/.github/workflows/code-coverage.yml@main + if: > + github.event_name == 'push' || github.event_name == 'pull_request' + with: + r-version: $R_VERSION + # Whether to skip code coverage badge creation + # Setting to 'false' will require you to create + # an orphan branch called 'badges' in your repository + skip-coverage-badges: false + man-pages: + name: Man Pages + uses: pharmaverse/admiralci/.github/workflows/man-pages.yml@main + if: github.event_name == 'pull_request' + with: + r-version: $R_VERSION diff --git a/.github/workflows/lintr.yaml b/.github/workflows/lintr.yaml deleted file mode 100644 index d31a9effe5..0000000000 --- a/.github/workflows/lintr.yaml +++ /dev/null @@ -1,72 +0,0 @@ -on: - workflow_dispatch: - push: - branches: - - main - - patch - - devel - pull_request: - branches: - - main - - patch - - devel - -name: lint - -jobs: - lint: - runs-on: ubuntu-20.04 - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - uses: actions/cache@v2 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - shell: Rscript {0} - - - name: Install package - run: R CMD INSTALL . - - - name: Lint - run: | - (lints <- lintr::lint_package()) - saveRDS(lints, file = "lints.rds") - shell: Rscript {0} - - - name: Error if lints are detected - run: | - lints <- readRDS("lints.rds") - if (length(lints) > 0L) { - stop("Lints detected. Please review and adjust code according to the comments provided.", call. = FALSE) - } - shell: Rscript {0} diff --git a/.github/workflows/man-pages.yml b/.github/workflows/man-pages.yml deleted file mode 100644 index 2863a77834..0000000000 --- a/.github/workflows/man-pages.yml +++ /dev/null @@ -1,136 +0,0 @@ ---- -name: Man Pages πŸ“ƒ - -on: - workflow_dispatch: - push: - branches: - - main - - patch - - devel - pull_request: - branches: - - main - - patch - - devel - issue_comment: - types: [created] - -concurrency: - group: roxygen-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - roxygen: - name: Roxygen πŸ…Ύ - runs-on: ubuntu-20.04 - if: > - !contains(github.event.commits[0].message, '[skip roxygen]') - && github.event.pull_request.draft == false - - steps: - - name: Checkout repo πŸ›Ž - uses: actions/checkout@v2 - with: - persist-credentials: false - fetch-depth: 0 - - - name: Fetch PR πŸ• - if: github.event_name == 'pull_request' - uses: r-lib/actions/pr-fetch@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup R πŸ“Š - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - uses: actions/cache@v2 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install roxygen2 πŸ…Ύ - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - renv::install("roxygen2") - shell: Rscript {0} - - - name: Generate man pages πŸ“„ - run: | - logfile <- "roxygen_${{ github.event.repository.name }}.log" - con <- file(logfile) - sink(con, append = TRUE, split = TRUE) - sink(con, append = TRUE, type = "message") - roxygen2::roxygenize('.', roclets = c('rd', 'collate', 'namespace')) - sink() - sink(type = "message") - logs <- readLines(logfile) - error_marker <- grep("Error:", logs) - if (length(error_marker) > 0) { - cat("☠ One or more errors were generated during the roxygen build:\n") - cat(logs[error_marker[[1]]:length(logs)], sep = "\n") - stop("Please πŸ™ fix the errors shown below this message πŸ‘‡") - } - shell: Rscript {0} - - - name: Commit updated man pages πŸ“Œ - if: startsWith(github.event.comment.body, '/roxygenize') - run: | - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git add man/. NAMESPACE - git commit -m "Update man pages" - - - name: Push updates ‴ - if: startsWith(github.event.comment.body, '/roxygenize') - uses: r-lib/actions/pr-push@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Roxygen check πŸ…Ύ - if: "!startsWith(github.event.comment.body, '/roxygenize')" - run: | - git status -s - if [[ -n `git status -s | grep man` ]] - then { - ROXYGEN_VERSION="$(Rscript -e 'packageVersion("roxygen2")' | awk '{print $NF}')" - echo "πŸ™ˆ Manuals are not up-to-date with roxygen comments!" - echo "πŸ”€ The following differences were noted:" - git diff man/* - echo -e "\nπŸ’» Please rerun the following command on your workstation and push your changes" - echo "--------------------------------------------------------------------" - echo "roxygen2::roxygenize('.', roclets = c('rd', 'collate', 'namespace'))" - echo "--------------------------------------------------------------------" - echo -e "\nβ–Ά Alternatively, if you have an open pull request, you can auto-generate the man pages by commenting the following in the PR" - echo "--------------------------------------------------------------------" - echo "/roxygenize" - echo "--------------------------------------------------------------------" - echo "β„Ή roxygen2 version that was used in this workflow: $ROXYGEN_VERSION" - echo "πŸ™ Please ensure that the 'RoxygenNote' field in the DESCRIPTION file matches this version" - exit 1 - } else { - echo "πŸ’š Manuals are up-to-date with roxygen comments" - } - fi - shell: bash diff --git a/.github/workflows/pkgdown.yml b/.github/workflows/pkgdown.yml deleted file mode 100644 index b97f6f0a9f..0000000000 --- a/.github/workflows/pkgdown.yml +++ /dev/null @@ -1,58 +0,0 @@ -on: - workflow_dispatch: - push: - branches: - - main - -name: pkgdown - -jobs: - pkgdown: - runs-on: ubuntu-20.04 - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/cache@v2 - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - uses: r-lib/actions/setup-pandoc@v1 - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install dependencies - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - shell: Rscript {0} - - - name: Install package - run: R CMD INSTALL . - - - name: Deploy package - run: | - git config --local user.email "actions@github.com" - git config --local user.name "GitHub Actions" - Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' diff --git a/.github/workflows/r-pkg-validation.yml b/.github/workflows/r-pkg-validation.yml deleted file mode 100644 index 3c2a23b480..0000000000 --- a/.github/workflows/r-pkg-validation.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: R Package Validation report - -on: # Run this action when a release is published - release: - types: [published] - -jobs: - r-pkg-validation: - name: Create report πŸ“ƒ - runs-on: ubuntu-latest - container: - image: rocker/verse:4.1.2 - # Set Github token permissions - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - permissions: - contents: write - packages: write - deployments: write - steps: - - name: Checkout repo πŸ›Ž - uses: actions/checkout@v2 - - - name: Build report πŸ— - id: validation - uses: insightsengineering/thevalidatoR@v1 - - # Upload the validation report to the release - - name: Upload report to release πŸ”Ό - if: success() - uses: svenstaro/upload-release-action@v2 - with: - file: ${{ steps.validation.outputs.report_output_filename }} - asset_name: ${{ steps.validation.outputs.report_output_filename }} - repo_token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ github.ref }} - overwrite: false diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml deleted file mode 100644 index 55586ae919..0000000000 --- a/.github/workflows/spellcheck.yml +++ /dev/null @@ -1,77 +0,0 @@ ---- -name: Spelling πŸ†Ž - -on: - workflow_dispatch: - push: - branches: - - main - - patch - - devel - pull_request: - branches: - - main - - patch - - devel - -concurrency: - group: spelling-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - roxygen: - name: Spellcheck πŸ”  - runs-on: ubuntu-20.04 - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - if: > - !contains(github.event.commits[0].message, '[skip spellcheck]') - && github.event.pull_request.draft == false - - steps: - - name: Checkout repo πŸ›Ž - uses: actions/checkout@v2 - with: - persist-credentials: false - fetch-depth: 0 - - - name: Setup R πŸ“Š - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - uses: actions/cache@v2 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install spelling πŸŽ“ - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - renv::install("spelling") - shell: Rscript {0} - - - name: Run Spellcheck πŸ‘Ÿ - uses: insightsengineering/r-spellcheck-action@v3 - with: - exclude: data/*,**/*.Rd,**/*.Rmd,**/*.md,*.md - additional_options: "" diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml deleted file mode 100644 index bdef65f579..0000000000 --- a/.github/workflows/style.yml +++ /dev/null @@ -1,108 +0,0 @@ ---- -name: Style 🎽 - -on: - workflow_dispatch: - push: - branches: - - main - - patch - - devel - pull_request: - branches: - - main - - patch - - devel - -concurrency: - group: style-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - style: - name: Code Style πŸ‘š - runs-on: ubuntu-20.04 - if: > - !contains(github.event.commits[0].message, '[skip style]') - && github.event.pull_request.draft == false - - steps: - - name: Checkout Code πŸ›Ž - uses: actions/checkout@v2 - with: - persist-credentials: false - fetch-depth: 0 - - - name: Setup R πŸ“Š - uses: r-lib/actions/setup-r@v1 - with: - r-version: 3.6 - - - uses: actions/cache@v2 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.local/share/renv - key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} - restore-keys: | - ${{ runner.os }}-renv- - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Install system dependencies - if: runner.os == 'Linux' - run: | - while read -r cmd - do - eval sudo $cmd - done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') - - - name: Install styler πŸ‘— - run: | - if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") - renv::restore() - shell: Rscript {0} - - - name: Get changed files πŸ—ž - id: changed-files - uses: tj-actions/changed-files@v17 - with: - separator: "," - files: | - **.R - **.Rmd - **.Rnw - **.Rmarkdown - - - name: Run styler πŸ‘Ÿ - run: | - changed_files <- unlist(strsplit( - "${{ steps.changed-files.outputs.all_changed_files }}", - split="," - )) - is_r_file <- function(x) { - ext <- tools::file_ext(x) - ext %in% c("R", "Rmd", "Rnw", "Rmarkdown") - } - changed_r_files <- Filter(is_r_file, changed_files) - detect <- styler::style_file(changed_r_files, dry = "on") - if (TRUE %in% detect$changed) { - problems <- subset(detect$file, detect$changed == T) - cat(paste( - "☠ One or more files had styling errors.", - "Please see the log above for remediations,", - "or simply run the following commands", - "for an immediate fix:\n" - )) - cat("────────────────────────────────────────\n") - cat(paste0( - "styler::style_file(", capture.output(dput(problems)), ")\n" - )) - cat("────────────────────────────────────────\n") - quit(status = length(problems)) - } - shell: Rscript {0} diff --git a/.github/workflows/templates.yml b/.github/workflows/templates.yml new file mode 100644 index 0000000000..dcfc6d3999 --- /dev/null +++ b/.github/workflows/templates.yml @@ -0,0 +1,19 @@ +--- +name: Check Templates + +on: + workflow_dispatch: + pull_request_review: + types: [submitted] + +env: + # R version to use for the workflows + R_VERSION: "3.6" + +jobs: + templates: + name: Check Templates + uses: pharmaverse/admiralci/.github/workflows/check-templates.yml@main + if: github.event.review.state == 'approved' + with: + r-version: $R_VERSION diff --git a/.gitignore b/.gitignore index e8b0cae4c4..6887dd7f14 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,8 @@ vignettes/*.pdf .Renviron # website documents -docs +/docs/* +!/docs/pkgdown.yml doc Meta admiral.Rcheck/ diff --git a/.lycheeignore b/.lycheeignore new file mode 100644 index 0000000000..7dbdbcd3c2 --- /dev/null +++ b/.lycheeignore @@ -0,0 +1,6 @@ +https://packagemanager.rstudio.com/cran/linux/focal/latest +https://github.com/pharmaverse/admiral/blob/main/ +https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adxx.R +irongut/CodeCoverageSummary@v1.2.0 +https://packagemanager.rstudio.com/cran/__linux__/focal/latest +https://pharmaverse.github.io/admiral/articles/higher_order.html diff --git a/DESCRIPTION b/DESCRIPTION index 5087bf3e47..ab4d1c3ea3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: admiral Type: Package Title: ADaM in R Asset Library -Version: 0.7.1 +Version: 0.8.0 Authors@R: c( person("Thomas", "Neitmann", email = "thomas.neitmann@roche.com", role = c("aut", "cre")), person("Stefan", "Bundfuss", role = "aut"), @@ -34,6 +34,18 @@ Authors@R: c( person("Ondrej", "Slama", role = "ctb"), person("Shimeng", "Huang", role = "ctb"), person("James", "Kim", role = "ctb"), + person("Shan", "Lee", role = "ctb"), + person("Bill", "Denney", role = "ctb"), + person("Syed", "Mubasheer", role = "ctb"), + person("Wenyi", "Liu", role = "ctb"), + person("Dinakar", "Kulkarni", role = "ctb"), + person("Tamara", "Senior", role = "ctb"), + person("Jordanna", "Morrish", role = "ctb"), + person("Anthony", "Howard", role = "ctb"), + person("Barbara", "O'Reilly", role = "ctb"), + person("John", "Kirkpatrick", role = "ctb"), + person("James", "Black", role = "ctb"), + person("Leena", "Khatri", role = "ctb"), person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")), person("GlaxoSmithKline LLC", role = c("cph", "fnd")) ) @@ -45,40 +57,41 @@ Description: A toolbox for programming Clinical Data Standards Interchange Conso (CDISC Analysis Data Model Team, 2021, ). Language: en-US License: Apache License (>= 2) -URL: https://pharmaverse.github.io/admiral/index.html, https://github.com/pharmaverse/admiral/ +URL: https://pharmaverse.github.io/admiral/, https://github.com/pharmaverse/admiral Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) RoxygenNote: 7.2.0 Depends: R (>= 3.5) Imports: - assertthat, - dplyr, - hms, - lifecycle, - lubridate, - magrittr, - purrr, - rlang, - stringr, - tidyr, - tidyselect + admiraldev, + assertthat (>= 0.2.1), + dplyr (>= 0.8.4), + hms (>= 0.5.3), + lifecycle (>= 0.1.0), + lubridate (>= 1.7.4), + magrittr (>= 1.5), + purrr (>= 0.3.3), + rlang (>= 0.4.4), + stringr (>= 1.4.0), + tidyr (>= 1.0.2), + tidyselect (>= 1.0.0) Suggests: admiral.test, + covr, devtools, + DT, diffdf, knitr, lintr, - pkgdown, - testthat, methods, - miniUI, + pkgdown, + readxl, rmarkdown, roxygen2, spelling, styler, + testthat, tibble, - usethis, - covr, - DT + usethis VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index ead7acadf8..63969d026c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ S3method(convert_blanks_to_na,default) S3method(convert_blanks_to_na,list) S3method(format,sdg_select) S3method(format,smq_select) +S3method(print,adam_templates) S3method(print,derivation_slice) S3method(print,tte_source) export(ae_event) @@ -18,27 +19,8 @@ export(ae_gr5_event) export(ae_ser_event) export(ae_sev_event) export(ae_wd_event) -export(assert_character_scalar) -export(assert_character_vector) -export(assert_data_frame) -export(assert_filter_cond) -export(assert_function) -export(assert_has_variables) -export(assert_integer_scalar) -export(assert_list_element) -export(assert_list_of) -export(assert_logical_scalar) -export(assert_numeric_vector) -export(assert_one_to_one) -export(assert_order_vars) -export(assert_param_does_not_exist) -export(assert_s3_class) -export(assert_symbol) export(assert_terms) -export(assert_unit) export(assert_valid_queries) -export(assert_vars) -export(assert_varval_list) export(call_derivation) export(call_user_fun) export(censor_source) @@ -46,30 +28,34 @@ export(compute_bmi) export(compute_bsa) export(compute_dtf) export(compute_duration) +export(compute_framingham) export(compute_map) export(compute_qtc) +export(compute_qual_imputation) +export(compute_qual_imputation_dec) export(compute_rr) export(compute_tmf) export(convert_blanks_to_na) export(convert_date_to_dtm) export(convert_dtc_to_dt) export(convert_dtc_to_dtm) +export(count_vals) export(create_query_data) export(create_single_dose_dataset) -export(dataset_vignette) export(date_source) export(death_event) export(default_qtc_paramcd) export(derivation_slice) export(derive_derived_param) export(derive_extreme_records) -export(derive_last_dose) export(derive_param_bmi) export(derive_param_bsa) +export(derive_param_computed) export(derive_param_doseint) export(derive_param_exist_flag) export(derive_param_exposure) export(derive_param_first_event) +export(derive_param_framingham) export(derive_param_map) export(derive_param_qtc) export(derive_param_rr) @@ -85,10 +71,12 @@ export(derive_var_analysis_ratio) export(derive_var_anrind) export(derive_var_astdy) export(derive_var_atirel) +export(derive_var_atoxgr) +export(derive_var_atoxgr_dir) export(derive_var_base) export(derive_var_basetype) export(derive_var_chg) -export(derive_var_disposition_dt) +export(derive_var_confirmation_flag) export(derive_var_disposition_status) export(derive_var_dthcaus) export(derive_var_extreme_dt) @@ -97,7 +85,6 @@ export(derive_var_extreme_flag) export(derive_var_last_dose_amt) export(derive_var_last_dose_date) export(derive_var_last_dose_grp) -export(derive_var_lstalvdt) export(derive_var_merged_cat) export(derive_var_merged_character) export(derive_var_merged_exist_flag) @@ -106,8 +93,6 @@ export(derive_var_ontrtfl) export(derive_var_pchg) export(derive_var_shift) export(derive_var_trtdurd) -export(derive_var_trtedtm) -export(derive_var_trtsdtm) export(derive_var_worst_flag) export(derive_vars_aage) export(derive_vars_atc) @@ -122,6 +107,7 @@ export(derive_vars_last_dose) export(derive_vars_merged) export(derive_vars_merged_dt) export(derive_vars_merged_dtm) +export(derive_vars_merged_lookup) export(derive_vars_query) export(derive_vars_suppqual) export(derive_vars_transposed) @@ -129,10 +115,10 @@ export(desc) export(dose_freq_lookup) export(dthcaus_source) export(event_source) -export(expect_dfs_equal) export(extend_source_datasets) export(extract_duplicate_records) export(extract_unit) +export(filter_confirmation) export(filter_date_sources) export(filter_extreme) export(filter_if) @@ -141,11 +127,16 @@ export(format_eoxxstt_default) export(format_reason_default) export(get_duplicates_dataset) export(get_many_to_one_dataset) +export(get_not_mapped) export(get_one_to_many_dataset) -export(impute_dtc) +export(get_summary_records) +export(impute_dtc_dt) +export(impute_dtc_dtm) export(lastalive_censor) export(list_all_templates) -export(lstalvdt_source) +export(list_tte_source_objects) +export(max_cond) +export(min_cond) export(negate_vars) export(params) export(query) @@ -154,20 +145,15 @@ export(sdg_select) export(signal_duplicate_records) export(slice_derivation) export(smq_select) -export(suppress_warning) export(use_ad_template) export(validate_query) export(validate_sdg_select) export(validate_smq_select) export(vars) export(vars2chr) -export(warn_if_inconsistent_list) -export(warn_if_invalid_dtc) -export(warn_if_vars_exist) export(yn_to_numeric) -importFrom(assertthat,"on_failure<-") +import(admiraldev) importFrom(assertthat,assert_that) -importFrom(assertthat,is.number) importFrom(dplyr,arrange) importFrom(dplyr,bind_cols) importFrom(dplyr,bind_rows) @@ -191,6 +177,7 @@ importFrom(dplyr,rename) importFrom(dplyr,rename_at) importFrom(dplyr,row_number) importFrom(dplyr,select) +importFrom(dplyr,semi_join) importFrom(dplyr,slice) importFrom(dplyr,starts_with) importFrom(dplyr,summarise) @@ -215,6 +202,7 @@ importFrom(lubridate,hours) importFrom(lubridate,is.Date) importFrom(lubridate,is.instant) importFrom(lubridate,minutes) +importFrom(lubridate,rollback) importFrom(lubridate,time_length) importFrom(lubridate,weeks) importFrom(lubridate,years) @@ -240,6 +228,7 @@ importFrom(rlang,.data) importFrom(rlang,abort) importFrom(rlang,arg_match) importFrom(rlang,as_function) +importFrom(rlang,as_label) importFrom(rlang,as_string) importFrom(rlang,call2) importFrom(rlang,call_name) @@ -287,9 +276,14 @@ importFrom(stringr,str_c) importFrom(stringr,str_detect) importFrom(stringr,str_extract) importFrom(stringr,str_glue) +importFrom(stringr,str_length) +importFrom(stringr,str_locate) +importFrom(stringr,str_match) importFrom(stringr,str_remove) importFrom(stringr,str_remove_all) importFrom(stringr,str_replace) +importFrom(stringr,str_sub) +importFrom(stringr,str_subset) importFrom(stringr,str_to_lower) importFrom(stringr,str_to_title) importFrom(stringr,str_to_upper) diff --git a/NEWS.md b/NEWS.md index 5c9c6a415b..00e6f2f90d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,150 @@ +# admiral 0.8.0 + +## New Features + +- `get_summary_records()` creates summary records e.g. derive analysis value (`AVAL`) from multiple records, only keeping the derived observations (#525) + +- `derive_param_framingham()` adds a Parameter for Framingham Heart Study Cardiovascular Disease 10-Year Risk Score (#977) + +- `compute_qual_imputation()` imputes values when qualifier exists in character result (#976) + +- `derive_vars_merged_lookup()` maps lookup tables (#940) + +- `filter_confirmation()` filters out confirmed observations +(#1292) including supporting functions `count_vals()`, `min_cond()`, and +`max_cond()`. + +- `derive_var_confirmation_flag()` derives a flag which +depends on other observations of the input dataset (#1293) + +- `derive_var_atoxgr()` derives lab toxicity/severity grade `ATOXGR` +from `ATOXGRL` and `ATOXGRH`. `ATOXGRL` holds toxicity/severity grade for low lab values, +and `ATOXGRH` holds toxicity/severity grade for high lab values. + +- `derive_var_atoxgr_dir()` derives lab toxicity/severity grade for low +lab values (`ATOXGRL`) or for high lab values (`ATOXGRH`). The grading is created from +metadata. + +- New metadata data set called `atoxgr_criteria_ctcv4` which holds criteria for lab grading +based on [Common Terminology Criteria for Adverse Events (CTCAE) v4.0](https://ctep.cancer.gov/protocoldevelopment/electronic_applications/ctc.htm) + + +## Updates of Existing Functions + +- `list_tte_source_objects()` gains a `package` parameter and is now exported (#1212) + +- `list_all_templates()` and `use_ad_template()` gain a `package` parameter which +can be used to indicate in which package to look for templates (#1205) + +- Randomization Date `RANDDT` variable added to ADSL template and vignette (#1126) + +- Renamed `derive_derived_param()` to `derive_param_computed()` and added a +deprecation notice (#1229) + +- `derive_vars_duration()` updated to not display units when there is missing duration (#1207) + +- `value_var` parameter added to `derive_vars_atc()` (#1120) + +- `format_eoxxstt_default()` - Updated the default value of EOSSTT for screen failure patients (#885) + +- The imputation functions (`derive_vars_dtm()`, `derive_vars_dt()`, +`convert_dtc_to_dtm()`, `convert_dtc_to_dt()`) have been enhanced to address +users feedback (#1300): + + - Partial dates with missing components in the middle like + `"2003-12-15T-:15:18"`, `"2003-12-15T13:-:19"`, `"2020-07--T00:00"` are + handled now. + + - The control of the level of imputation has been refined by adding the + `highest_imputation` argument. For example, `highest_imputation = "D"` + requests imputation for day and time but not for year and month. + + (For the `date_imputation` and the `time_imputation` argument `NULL` is no + longer a permitted value.) + + - It is now possible to impute completely missing dates by specifying + `highest_imputation = "Y"` and the `min_dates` or `max_dates` argument. + +- `order` parameter added to `dthcaus_source()` which allows an additional +character vector to be used for sorting the `dataset`, `derive_vars_dthcaus()` +updated to process additional parameter (#1125). + +- `create_single_dose_dataset()` Fixed bug where `ASTDTM` and `AENDTM` were not updated when `start_date = ASTDT` and `end_date = AENDT`. The function has been amended to now require `start_datetime` and `end_datetime` parameters in addition to `start_date` and `end_date`.The `keep_source_vars` has been added to specify the variables to be retained from the source dataset (#1224) + +## Breaking Changes + +- Moved all developer-facing functions and vignettes to `{admiraldev}`. `{admiraldev}` is now a dependency of `{admiral}` (#1231) + +- All ADaM datasets but `admiral_adsl` have been removed from the package (#1234) + +- `derive_var_agegr_ema()` and `derive_var_agegr_fda()` have been deprecated (#1333) + +- Imputation related arguments have been deprecated for all functions except the +imputation functions themselves (#1299). I.e., if a derivation like last known alive +date is based on dates, DTC variables have to be converted to numeric date or +datetime variables in a preprocessing step. For examples see the [ADSL +vignette](https://pharmaverse.github.io/admiral/articles/adsl.html). + + The following arguments were deprecated: + + - `date_imputation`, `time_imputation`, and `preserve` in `date_source()` + + The following arguments no longer accept DTC variables: + + - `date` in `date_source()`, `dthcaus_source()`, `censor_source()`, and + `event_source()` + - `dose_date` and `analysis_date` in `derive_vars_last_dose()`, + `derive_var_last_dose_amt()`, `derive_var_last_dose_date()`, + `derive_var_last_dose_grp()` + + The following functions were deprecated: + + - `derive_vars_merged_dt()` + - `derive_vars_merged_dtm()` + +- For the `date_imputation` and the `time_imputation` argument of the imputation +functions (`derive_vars_dtm()`, `derive_vars_dt()`, `convert_dtc_to_dtm()`, +`convert_dtc_to_dt()`) `NULL` is no longer a permitted value. The level of +imputation can be controlled by the `highest_imputation` argument now. + +- The following functions, which were deprecated in previous {admiral} versions, +have been removed: + + - `derive_var_disposition_dt()` + - `derive_var_lstalvdt()` + - `lstalvdt_source()` + - `derive_var_trtedtm()` + - `derive_var_trtsdtm()` + +- The following functions and parameters, which were deprecated in previous +{admiral} versions, are now defunct and will output an ERROR if used: + + - `derive_var_ady()` + - `derive_var_aendy()` + - `derive_var_astdy()` + - `derive_var_atirel()` + - `filter` parameter in `derive_var_extreme_flag()` and `derive_var_worst_flag()` + +## Documentation + +- New ADMH template script can be accessed using `admiral::use_ad_template("admh")` (#502) + +- New vignette "Higher Order Functions" (#1047) + +- New vignette "Lab Grading" (#1369) + +- Fixed `derive_var_disposition_status()` argument to render correctly (#1268) + +- Added link to [pharmaverse YouTube channel](https://www.youtube.com/channel/UCxQFEv8HNqM01DXzdQLCy6Q) to README + +## Various + +- Restructured Reference page and updated **all** functions to use `family` tag +in roxygen headers for finding similar functions. (#1105) + +- Rename "Articles" page on website to "User Guides" and moved developer vignettes to `{admiraldev}` website (#1356) + + # admiral 0.7.1 - `derive_vars_last_dose()` no longer fails when a variable renamed in `new_vars` is supplied @@ -9,6 +156,8 @@ duration (#1207) - `derive_param_first_event()` was updated (#1214) such that - `AVAL` is derived instead of `AVALN` and - all variables from the source dataset are kept. + +- `create_single_dose_dataset()` Fixed bug where ASTDTM and AENDTM were not updated when `start_date=ASTDT` and `end_date=AENDT`. The function has been amended to now require start_datetime and end_datetime parameters in addition to start_date and end_date.The keep_source_vars has been added to specify the variables to be retained from the source dataset. - `slice_derivation()` was updated such that it no longer fails if a slice is empty (#1309) @@ -178,6 +327,7 @@ Address [CRAN comments](https://github.com/pharmaverse/admiral/issues/918) raise - `derive_vars_dy()` derives the analysis day from one or more `--DT(M)` variables (#700) + ## Updates of Existing Functions - The `derive_last_dose()` function has been split into a general function @@ -234,9 +384,9 @@ this case the day is imputed as `15` (#592) - README and site homepage has been updated with important new section around expectations of {admiral}, as well as other useful references such as links to conference talks (#868 & #802) -- New vignette [Development Process](https://pharmaverse.github.io/admiral/articles/development_process.html) and improvements made to contribution vignettes (#765 & #758) +- New vignette [Development Process](https://pharmaverse.github.io/admiraldev/main/articles/development_process.html) and improvements made to contribution vignettes (#765 & #758) -- Updated [Pull Request Review Guidance](https://pharmaverse.github.io/admiral/articles/pr_review_guidance.html) on using `task-list-completed` workflow (#817) +- Updated [Pull Request Review Guidance](https://pharmaverse.github.io/admiraldev/main/articles/pr_review_guidance.html) on using `task-list-completed` workflow (#817) ## Various @@ -250,7 +400,7 @@ this case the day is imputed as `15` (#592) - New vignette [Contributing to admiral](https://pharmaverse.github.io/admiral/articles/contribution_model.html) (#679) -- New vignette [Unit Test Guidance](https://pharmaverse.github.io/admiral/articles/unit_test_guidance.html) (#679) +- New vignette [Unit Test Guidance](https://pharmaverse.github.io/admiraldev/main/articles/unit_test_guidance.html) (#679) - Broken links in README have been fixed (#564) @@ -309,9 +459,9 @@ to specify the unit of the input age (#569) - New vignette [Queries Dataset Documentation](https://pharmaverse.github.io/admiral/articles/queries_dataset.html) (#561) -- New vignette [Writing Vignettes](https://pharmaverse.github.io/admiral/articles/writing_vignettes.html) (#334) +- New vignette [Writing Vignettes](https://pharmaverse.github.io/admiraldev/main/articles/writing_vignettes.html) (#334) -- New vignette [Pull Request Review Guidance](https://pharmaverse.github.io/admiral/articles/pr_review_guidance.html) (#554) +- New vignette [Pull Request Review Guidance](https://pharmaverse.github.io/admiraldev/main/articles/pr_review_guidance.html) (#554) - A section on handling missing values when working with {admiral} has been added to the "Get Started" vignette (#577) diff --git a/R/admiral-package.R b/R/admiral-package.R index 24332db5ff..b2635e2913 100644 --- a/R/admiral-package.R +++ b/R/admiral-package.R @@ -1,25 +1,27 @@ #' @keywords internal +#' @family internal +#' @import admiraldev #' @importFrom dplyr arrange bind_rows case_when desc ends_with filter full_join group_by #' if_else mutate mutate_at mutate_if n pull rename rename_at row_number select slice -#' starts_with transmute ungroup vars n_distinct union distinct +#' semi_join starts_with transmute ungroup vars n_distinct union distinct #' summarise_at summarise coalesce bind_cols na_if tibble #' @importFrom magrittr %>% -#' @importFrom rlang := abort arg_match as_function as_string call2 caller_env -#' call_name current_env .data enexpr enquo eval_bare eval_tidy expr -#' expr_interp expr_label f_lhs f_rhs inform -#' is_bare_formula is_call is_character is_formula is_integerish -#' is_logical is_quosure is_quosures is_symbol new_formula -#' parse_expr parse_exprs quo quo_get_expr quo_is_call -#' quo_is_missing quo_is_null quo_is_symbol quos quo_squash quo_text -#' set_names sym syms type_of warn quo_set_env quo_get_env +#' @importFrom rlang := abort arg_match as_function as_label as_string call2 +#' caller_env call_name current_env .data enexpr enquo eval_bare eval_tidy +#' expr expr_interp expr_label f_lhs f_rhs inform is_bare_formula is_call +#' is_character is_formula is_integerish is_logical is_quosure is_quosures +#' is_symbol new_formula parse_expr parse_exprs quo quo_get_expr quo_is_call +#' quo_is_missing quo_is_null quo_is_symbol quos quo_squash quo_text set_names +#' sym syms type_of warn quo_set_env quo_get_env #' @importFrom utils capture.output str #' @importFrom purrr map map2 map_chr map_lgl reduce walk keep map_if transpose #' flatten every modify_at modify_if reduce compose -#' @importFrom stringr str_c str_detect str_extract str_remove str_remove_all -#' str_replace str_trim str_to_lower str_to_title str_to_upper str_glue -#' @importFrom assertthat assert_that is.number on_failure<- +#' @importFrom stringr str_c str_detect str_extract str_glue str_match +#' str_remove str_remove_all str_replace str_sub str_subset str_trim +#' str_to_lower str_to_title str_to_upper str_length str_locate +#' @importFrom assertthat assert_that #' @importFrom lubridate as_datetime ceiling_date date days duration floor_date is.Date is.instant -#' time_length %--% ymd ymd_hms weeks years hours minutes +#' rollback time_length %--% ymd ymd_hms weeks years hours minutes #' @importFrom tidyr drop_na nest pivot_longer pivot_wider unnest #' @importFrom tidyselect all_of contains vars_select #' @importFrom hms as_hms diff --git a/R/assertions.R b/R/assertions.R index 1668d72d0c..ee16d6462b 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -1,1345 +1,9 @@ -#' Is an Argument a Data Frame? -#' -#' Checks if an argument is a data frame and (optionally) whether is contains -#' a set of required variables -#' -#' @param arg A function argument to be checked -#' @param required_vars A list of variables created using `vars()` -#' @param check_is_grouped Throw an error is `dataset` is grouped? Defaults to `TRUE`. -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not a data frame or if `arg` -#' is a data frame but misses any variable specified in `required_vars`. Otherwise, -#' the input is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' library(admiral.test) -#' data(admiral_dm) -#' -#' example_fun <- function(dataset) { -#' assert_data_frame(dataset, required_vars = vars(STUDYID, USUBJID)) -#' } -#' -#' example_fun(admiral_dm) -#' -#' try(example_fun(dplyr::select(admiral_dm, -STUDYID))) -#' -#' try(example_fun("Not a dataset")) -assert_data_frame <- function(arg, - required_vars = NULL, - check_is_grouped = TRUE, - optional = FALSE) { - assert_vars(required_vars, optional = TRUE) - assert_logical_scalar(check_is_grouped) - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.data.frame(arg)) { - err_msg <- sprintf( - "`%s` must be a data frame but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - if (check_is_grouped && dplyr::is_grouped_df(arg)) { - err_msg <- sprintf( - "`%s` is a grouped data frame, please `ungroup()` it first", - arg_name(substitute(arg)) - ) - abort(err_msg) - } - - if (!is.null(required_vars)) { - required_vars <- vars2chr(required_vars) - is_missing <- !required_vars %in% colnames(arg) - if (any(is_missing)) { - missing_vars <- required_vars[is_missing] - if (length(missing_vars) == 1L) { - err_msg <- sprintf("Required variable `%s` is missing", missing_vars) - } else { - err_msg <- sprintf("Required variables %s are missing", enumerate(missing_vars)) - } - abort(err_msg) - } - } - - invisible(arg) -} - -#' Is an Argument a Character Scalar (String)? -#' -#' Checks if an argument is a character scalar and (optionally) whether it matches -#' one of the provided `values`. -#' -#' @param arg A function argument to be checked -#' @param values A `character` vector of valid values for `arg` -#' @param case_sensitive Should the argument be handled case-sensitive? -#' If set to `FALSE`, the argument is converted to lower case for checking the -#' permitted values and returning the argument. -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not a character vector or if `arg` -#' is a character vector but of length > 1 or if its value is not one of the `values` -#' specified. Otherwise, the input is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(msg_type) { -#' assert_character_scalar(msg_type, values = c("warning", "error")) -#' } -#' -#' example_fun("warning") -#' -#' try(example_fun("message")) -#' -#' try(example_fun(TRUE)) -#' -#' # handling parameters case-insensitive -#' example_fun2 <- function(msg_type) { -#' msg_type <- assert_character_scalar( -#' msg_type, -#' values = c("warning", "error"), -#' case_sensitive = FALSE -#' ) -#' if (msg_type == "warning") { -#' print("A warning was requested.") -#' } -#' } -#' -#' example_fun2("Warning") -assert_character_scalar <- function(arg, - values = NULL, - case_sensitive = TRUE, - optional = FALSE) { - assert_character_vector(values, optional = TRUE) - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.character(arg)) { - err_msg <- sprintf( - "`%s` must be a character scalar but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - if (length(arg) != 1L) { - err_msg <- sprintf( - "`%s` must be a character scalar but is a character vector of length %d", - arg_name(substitute(arg)), - length(arg) - ) - abort(err_msg) - } - - if (!case_sensitive) { - arg <- tolower(arg) - } - - if (!is.null(values) && arg %notin% values) { - err_msg <- sprintf( - "`%s` must be one of %s but is '%s'", - arg_name(substitute(arg)), - enumerate(values, quote_fun = squote, conjunction = "or"), - arg - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Is an Argument a Character Vector? -#' -#' Checks if an argument is a character vector -#' -#' @param arg A function argument to be checked -#' @param values A `character` vector of valid values for `arg` -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not a character vector or if -#' any element is not included in the list of valid values. Otherwise, the input -#' is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(chr) { -#' assert_character_vector(chr) -#' } -#' -#' example_fun(letters) -#' -#' try(example_fun(1:10)) -assert_character_vector <- function(arg, values = NULL, optional = FALSE) { - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.character(arg)) { - err_msg <- sprintf( - "`%s` must be a character vector but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - assert_character_vector(values, optional = TRUE) - if (!is.null(values)) { - mismatches <- unique(arg[!map_lgl(arg, `%in%`, values)]) - if (length(mismatches) > 0) { - abort(paste0( - "`", arg_name(substitute(arg)), - "` contains invalid values:\n", - enumerate(mismatches), "\n", - "Valid values:\n", - enumerate(values) - )) - } - } -} - -#' Is an Argument a Logical Scalar (Boolean)? -#' -#' Checks if an argument is a logical scalar -#' -#' @param arg A function argument to be checked -#' -#' @param optional Is the checked parameter optional? -#' -#' If set to `FALSE` and `arg` is `NULL` then an error is thrown. Otherwise, -#' `NULL` is considered as valid value. -#' -#' @author Thomas Neitmann, Stefan Bundfuss -#' -#' @return -#' The function throws an error if `arg` is neither `TRUE` or `FALSE`. Otherwise, -#' the input is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(flag) { -#' assert_logical_scalar(flag) -#' } -#' -#' example_fun(FALSE) -#' -#' try(example_fun(NA)) -#' -#' try(example_fun(c(TRUE, FALSE, FALSE))) -#' -#' try(example_fun(1:10)) -assert_logical_scalar <- function(arg, optional = FALSE) { - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.logical(arg) || length(arg) != 1L || is.na(arg)) { - err_msg <- sprintf( - "`%s` must be either `TRUE` or `FALSE` but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Is an Argument a Symbol? -#' -#' Checks if an argument is a symbol -#' -#' @param arg A function argument to be checked. Must be a `quosure`. See examples. -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not a symbol and returns the input -#' invisibly otherwise. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' library(admiral.test) -#' data(admiral_dm) -#' -#' example_fun <- function(dat, var) { -#' var <- assert_symbol(rlang::enquo(var)) -#' dplyr::select(dat, !!var) -#' } -#' -#' example_fun(admiral_dm, USUBJID) -#' -#' try(example_fun(admiral_dm)) -#' -#' try(example_fun(admiral_dm, "USUBJID")) -#' -#' try(example_fun(admiral_dm, toupper(PARAMCD))) -assert_symbol <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - if (optional && quo_is_null(arg)) { - return(invisible(arg)) - } - - if (quo_is_missing(arg)) { - err_msg <- sprintf("Argument `%s` missing, with no default", arg_name(substitute(arg))) - abort(err_msg) - } - - if (!quo_is_symbol(arg)) { - err_msg <- sprintf( - "`%s` must be a symbol but is %s", - arg_name(substitute(arg)), - what_is_it(quo_get_expr(arg)) - ) - abort(err_msg) - } - - invisible(arg) -} - -assert_expr <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - if (optional && quo_is_null(arg)) { - return(invisible(arg)) - } - - if (quo_is_missing(arg)) { - err_msg <- sprintf("Argument `%s` missing, with no default", arg_name(substitute(arg))) - abort(err_msg) - } - - if (!(quo_is_symbol(arg) || quo_is_call(arg))) { - err_msg <- sprintf( - "`%s` must be an expression but is %s", - arg_name(substitute(arg)), - what_is_it(quo_get_expr(arg)) - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Is an Argument a Filter Condition? -#' -#' @param arg Quosure - filtering condition. -#' @param optional Logical - is the argument optional? Defaults to `FALSE`. -#' -#' @details Check if `arg` is a suitable filtering condition to be used in -#' functions like `subset` or `dplyr::filter`. -#' -#' @return Performs necessary checks and returns `arg` if all pass. -#' Otherwise throws an informative error. -#' -#' @export -#' @keywords assertion -#' @author Ondrej Slama -#' -#' @examples -#' library(admiral.test) -#' data(admiral_dm) -#' -#' # typical usage in a function as a parameter check -#' example_fun <- function(dat, x) { -#' x <- assert_filter_cond(rlang::enquo(x)) -#' dplyr::filter(dat, !!x) -#' } -#' -#' example_fun(admiral_dm, AGE == 64) -#' -#' try(example_fun(admiral_dm, USUBJID)) -assert_filter_cond <- function(arg, optional = FALSE) { - stopifnot(is_quosure(arg)) - assert_logical_scalar(optional) - - if (optional && quo_is_null(arg)) { - return(invisible(arg)) - } - - provided <- !rlang::quo_is_missing(arg) - if (!provided & !optional) { - err_msg <- sprintf("Argument `%s` is missing, with no default", arg_name(substitute(arg))) - abort(err_msg) - } - - if (provided & !(quo_is_call(arg) | is_logical(quo_get_expr(arg)))) { - err_msg <- sprintf( - "`%s` must be a filter condition but is %s", - arg_name(substitute(arg)), - what_is_it(quo_get_expr(arg)) - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Is an Argument a List of Variables? -#' -#' Checks if an argument is a valid list of variables created using `vars()` -#' -#' @param arg A function argument to be checked -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Samia Kabi -#' -#' @return -#' The function throws an error if `arg` is not a list of variables created using `vars()` -#' and returns the input invisibly otherwise. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(by_vars) { -#' assert_vars(by_vars) -#' } -#' -#' example_fun(vars(USUBJID, PARAMCD)) -#' -#' try(example_fun(rlang::exprs(USUBJID, PARAMCD))) -#' -#' try(example_fun(c("USUBJID", "PARAMCD", "VISIT"))) -#' -#' try(example_fun(vars(USUBJID, toupper(PARAMCD), desc(AVAL)))) -assert_vars <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - default_err_msg <- sprintf( - "`%s` must be a list of unquoted variable names, e.g. `vars(USUBJID, VISIT)`", - arg_name(substitute(arg)) - ) - - if (isTRUE(tryCatch(force(arg), error = function(e) TRUE))) { - abort(default_err_msg) - } - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!inherits(arg, "quosures")) { - abort(default_err_msg) - } - - is_symbol <- map_lgl(arg, quo_is_symbol) - if (!all(is_symbol)) { - expr_list <- map_chr(arg, quo_text) - err_msg <- paste0( - default_err_msg, - ", but the following elements are not: ", - enumerate(expr_list[!is_symbol]) - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Is an Argument a List of Order Variables? -#' -#' Checks if an argument is a valid list of order variables created using `vars()` -#' -#' @param arg A function argument to be checked -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Stefan Bundfuss -#' -#' @return -#' The function throws an error if `arg` is not a list of variables or `desc()` -#' calls created using `vars()` and returns the input invisibly otherwise. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(by_vars) { -#' assert_order_vars(by_vars) -#' } -#' -#' example_fun(vars(USUBJID, PARAMCD, desc(AVISITN))) -#' -#' try(example_fun(rlang::exprs(USUBJID, PARAMCD))) -#' -#' try(example_fun(c("USUBJID", "PARAMCD", "VISIT"))) -#' -#' try(example_fun(vars(USUBJID, toupper(PARAMCD), -AVAL))) -assert_order_vars <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - default_err_msg <- paste( - backquote(arg_name(substitute(arg))), - "must be a a list of unquoted variable names or `desc()` calls,", - "e.g. `vars(USUBJID, desc(VISITNUM))`" - ) - - if (isTRUE(tryCatch(force(arg), error = function(e) TRUE))) { - abort(default_err_msg) - } - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!inherits(arg, "quosures")) { - abort(default_err_msg) - } - - assert_that(is_order_vars(arg)) - - invisible(arg) -} - -#' Is an Argument an Integer Scalar? -#' -#' Checks if an argument is an integer scalar -#' -#' @param arg A function argument to be checked -#' @param subset A subset of integers that `arg` should be part of. Should be one -#' of `"none"` (the default), `"positive"`, `"non-negative"` or `"negative"`. -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not an integer belonging to the -#' specified `subset`. Otherwise, the input is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(num1, num2) { -#' assert_integer_scalar(num1, subset = "positive") -#' assert_integer_scalar(num2, subset = "negative") -#' } -#' -#' example_fun(1, -9) -#' -#' try(example_fun(1.5, -9)) -#' -#' try(example_fun(2, 0)) -#' -#' try(example_fun("2", 0)) -assert_integer_scalar <- function(arg, subset = "none", optional = FALSE) { - subsets <- list( - "positive" = quote(arg > 0L), - "non-negative" = quote(arg >= 0L), - "negative" = quote(arg < 0L), - "none" = quote(TRUE) - ) - assert_character_scalar(subset, values = names(subsets)) - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is_integerish(arg) || length(arg) != 1L || !is.finite(arg) || !eval(subsets[[subset]])) { - err_msg <- sprintf( - "`%s` must be %s integer scalar but is %s", - arg_name(substitute(arg)), - ifelse(subset == "none", "an", paste("a", subset)), - what_is_it(arg) - ) - abort(err_msg) - } - - invisible(as.integer(arg)) -} - -#' Is an Argument a Numeric Vector? -#' -#' Checks if an argument is a numeric vector -#' -#' @param arg A function argument to be checked -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Stefan Bundfuss -#' -#' @return -#' The function throws an error if `arg` is not a numeric vector. -#' Otherwise, the input is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(num) { -#' assert_numeric_vector(num) -#' } -#' -#' example_fun(1:10) -#' -#' try(example_fun(letters)) -assert_numeric_vector <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.numeric(arg)) { - err_msg <- sprintf( - "`%s` must be a numeric vector but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } -} - - -#' Is an Argument an Object of a Specific S3 Class? -#' -#' Checks if an argument is an object inheriting from the S3 class specified. -#' @param arg A function argument to be checked -#' @param class The S3 class to check for -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is an object which does *not* inherit from `class`. -#' Otherwise, the input is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(obj) { -#' assert_s3_class(obj, "factor") -#' } -#' -#' example_fun(as.factor(letters)) -#' -#' try(example_fun(letters)) -#' -#' try(example_fun(1:10)) -assert_s3_class <- function(arg, class, optional = TRUE) { - assert_character_scalar(class) - assert_logical_scalar(optional) - - if (is.null(arg) && optional) { - return(invisible(arg)) - } - - if (!inherits(arg, class)) { - err_msg <- sprintf( - "`%s` must be an object of class '%s' but is %s", - arg_name(substitute(arg)), - class, - what_is_it(arg) - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Is an Argument a List of Objects of a Specific S3 Class? -#' -#' Checks if an argument is a `list` of objects inheriting from the S3 class specified. -#' -#' @param arg A function argument to be checked -#' @param class The S3 class to check for -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown -#' -#' @author Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not a list or if `arg` is a list but its -#' elements are not objects inheriting from `class`. Otherwise, the input is returned -#' invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(list) { -#' assert_list_of(list, "data.frame") -#' } -#' -#' example_fun(list(mtcars, iris)) -#' -#' try(example_fun(list(letters, 1:10))) -#' -#' try(example_fun(c(TRUE, FALSE))) -assert_list_of <- function(arg, class, optional = TRUE) { - assert_character_scalar(class) - assert_logical_scalar(optional) - - if (is.null(arg) && optional) { - return(invisible(arg)) - } - - assert_s3_class(arg, "list") - - is_class <- map_lgl(arg, inherits, class) - if (!all(is_class)) { - info_msg <- paste( - sprintf("\u2716 Element %s is %s", which(!is_class), map_chr(arg[!is_class], what_is_it)), - collapse = "\n" - ) - err_msg <- sprintf( - "Each element of `%s` must be an object of class '%s' but the following are not:\n%s", - arg_name(substitute(arg)), - class, - info_msg - ) - abort(err_msg) - } - - invisible(arg) -} - -assert_named_exprs <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.list(arg) || - !all(map_lgl(arg, ~ is.language(.x) | is.logical(.x))) || - any(names(arg) == "")) { - err_msg <- sprintf( - "`%s` must be a named list of expressions created using `rlang::exprs()` but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - invisible(arg) -} - -assert_list_of_formulas <- function(arg, optional = FALSE) { - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (!is.list(arg) || - !all(map_lgl(arg, ~ is_formula(.x, lhs = TRUE))) || - !all(map_lgl(arg, ~ is.symbol(.x[[2L]])))) { - err_msg <- paste( - backquote(arg_name(substitute(arg))), - "must be a list of formulas where each formula's left-hand side is a single", - "variable name and each right-hand side is a function, e.g. `list(AVAL ~ mean)`" - ) - abort(err_msg) - } - - invisible(arg) -} - -#' Does a Dataset Contain All Required Variables? -#' -#' Checks if a dataset contains all required variables -#' -#' @param dataset A `data.frame` -#' @param required_vars A `character` vector of variable names -#' -#' @author Thomas Neitmann -#' -#' @return The function throws an error if any of the required variables are -#' missing in the input dataset. Otherwise, the dataset is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' library(admiral.test) -#' data(admiral_dm) -#' -#' assert_has_variables(admiral_dm, "STUDYID") -#' -#' try(assert_has_variables(admiral_dm, "AVAL")) -assert_has_variables <- function(dataset, required_vars) { - is_missing <- !required_vars %in% colnames(dataset) - if (any(is_missing)) { - missing_vars <- required_vars[is_missing] - if (length(missing_vars) == 1L) { - err_msg <- paste0("Required variable `", missing_vars, "` is missing.") - } else { - err_msg <- paste0( - "Required variables ", - enumerate(missing_vars), - " are missing." - ) - } - abort(err_msg) - } - invisible(dataset) -} - -#' Is Argument a Function? -#' -#' Checks if the argument is a function and if all expected parameters are -#' provided by the function. -#' -#' @param arg A function argument to be checked -#' -#' @param params A character vector of expected parameter names -#' -#' @param optional Is the checked parameter optional? -#' -#' If set to `FALSE` and `arg` is `NULL` then an error is thrown. -#' -#' @author Stefan Bundfuss -#' -#' @return The function throws an error -#' -#' - if the argument is not a function or -#' -#' - if the function does not provide all parameters as specified for the -#' `params` parameter. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' example_fun <- function(fun) { -#' assert_function(fun, params = c("x")) -#' } -#' -#' example_fun(mean) -#' -#' try(example_fun(1)) -#' -#' try(example_fun(sum)) -assert_function <- function(arg, params = NULL, optional = FALSE) { - assert_character_vector(params, optional = TRUE) - assert_logical_scalar(optional) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (missing(arg)) { - err_msg <- sprintf( - "Argument `%s` missing, with no default", - arg_name(substitute(arg)) - ) - abort(err_msg) - } - - if (!is.function(arg)) { - err_msg <- sprintf( - "`%s` must be a function but is %s", - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - if (!is.null(params)) { - is_param <- params %in% names(formals(arg)) - if (!all(is_param)) { - txt <- if (sum(!is_param) == 1L) { - "%s is not a parameter of the function specified for `%s`" - } else { - "%s are not parameters of the function specified for `%s`" - } - err_msg <- sprintf(txt, enumerate(params[!is_param]), arg_name(substitute(arg))) - abort(err_msg) - } - } - invisible(arg) -} - -assert_function_param <- function(arg, params) { - assert_character_scalar(arg) - assert_character_vector(params) - fun <- match.fun(arg) - - is_param <- params %in% names(formals(fun)) - if (!all(is_param)) { - txt <- if (sum(!is_param) == 1L) { - "%s is not a parameter of `%s()`" - } else { - "%s are not parameters of `%s()`" - } - err_msg <- sprintf(txt, enumerate(params[!is_param]), arg) - abort(err_msg) - } - - invisible(arg) -} - -#' Asserts That a Parameter is Provided in the Expected Unit -#' -#' Checks if a parameter (`PARAMCD`) in a dataset is provided in the expected -#' unit. -#' -#' @param dataset A `data.frame` -#' @param param Parameter code of the parameter to check -#' @param required_unit Expected unit -#' @param get_unit_expr Expression used to provide the unit of `param` -#' -#' @author Stefan Bundfuss -#' -#' @return -#' The function throws an error if the unit variable differs from the -#' unit for any observation of the parameter in the input dataset. Otherwise, the -#' dataset is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' data(admiral_advs) -#' assert_unit(admiral_advs, param = "WEIGHT", required_unit = "kg", get_unit_expr = VSSTRESU) -#' \dontrun{ -#' assert_unit(admiral_advs, param = "WEIGHT", required_unit = "g", get_unit_expr = VSSTRESU) -#' } -assert_unit <- function(dataset, param, required_unit, get_unit_expr) { - assert_data_frame(dataset, required_vars = vars(PARAMCD)) - assert_character_scalar(param) - assert_character_scalar(required_unit) - get_unit_expr <- enquo(get_unit_expr) - - units <- dataset %>% - mutate(`_unit` = !!get_unit_expr) %>% - filter(PARAMCD == param & !is.na(`_unit`)) %>% - pull(`_unit`) %>% - unique() - - if (length(units) != 1L) { - abort( - paste0( - "Multiple units ", - enumerate(units, quote_fun = squote), - " found for ", - squote(param), - ".\n", - "Please review and update the units." - ) - ) - } - if (tolower(units) != tolower(required_unit)) { - abort( - paste0( - "It is expected that ", - squote(param), - " is measured in ", - squote(required_unit), - ".\n", - "In the input dataset it is measured in ", - enumerate(units, quote_fun = squote), - "." - ) - ) - } - - invisible(dataset) -} - -#' Asserts That a Parameter Does Not Exist in the Dataset -#' -#' Checks if a parameter (`PARAMCD`) does not exist in a dataset. -#' -#' @param dataset A `data.frame` -#' @param param Parameter code to check -#' -#' @author Stefan Bundfuss -#' -#' @return -#' The function throws an error if the parameter exists in the input -#' dataset. Otherwise, the dataset is returned invisibly. -#' -#' @export -#' -#' @keywords assertion -#' -#' @examples -#' data(admiral_advs) -#' assert_param_does_not_exist(admiral_advs, param = "HR") -#' try(assert_param_does_not_exist(admiral_advs, param = "WEIGHT")) -assert_param_does_not_exist <- function(dataset, param) { - assert_data_frame(dataset, required_vars = vars(PARAMCD)) - if (param %in% unique(dataset$PARAMCD)) { - abort( - paste0( - "The parameter code ", - squote(param), - " does already exist in `", - arg_name(substitute(dataset)), - "`." - ) - ) - } - invisible(dataset) -} - -#' Is an Argument a Variable-Value List? -#' -#' Checks if the argument is a list of `quosures` where the expressions are -#' variable-value pairs. The value can be a symbol, a string, a numeric, or -#' `NA`. More general expression are not allowed. -#' -#' @param arg A function argument to be checked -#' @param required_elements A `character` vector of names that must be present in `arg` -#' @param accept_expr Should expressions on the right hand side be accepted? -#' @param accept_var Should unnamed variable names (e.g. `vars(USUBJID)`) on the -#' right hand side be accepted? -#' @param optional Is the checked parameter optional? If set to `FALSE` and `arg` -#' is `NULL` then an error is thrown. -#' -#' @author Stefan Bundfuss, Thomas Neitmann -#' -#' @return -#' The function throws an error if `arg` is not a list of variable-value expressions. -#' Otherwise, the input it returned invisibly. -#' -#' @keywords assertion -#' -#' @export -#' -#' @examples -#' example_fun <- function(vars) { -#' assert_varval_list(vars) -#' } -#' example_fun(vars(DTHDOM = "AE", DTHSEQ = AESEQ)) -#' -#' try(example_fun(vars("AE", DTSEQ = AESEQ))) -assert_varval_list <- function(arg, # nolint - required_elements = NULL, - accept_expr = FALSE, - accept_var = FALSE, - optional = FALSE) { - assert_logical_scalar(accept_expr) - assert_logical_scalar(accept_var) - assert_logical_scalar(optional) - assert_character_vector(required_elements, optional = TRUE) - - if (optional && is.null(arg)) { - return(invisible(arg)) - } - - if (accept_expr) { - valid_vals <- "a symbol, character scalar, numeric scalar, an expression, or `NA`" - } else if (accept_var) { - valid_vals <- "a symbol, character scalar, numeric scalar, variable names or `NA`" - } else { - valid_vals <- "a symbol, character scalar, numeric scalar, or `NA`" - } - - if (!accept_var & (!is_quosures(arg) || !is_named(arg))) { - err_msg <- sprintf( - paste0( - "`%s` must be a named list of quosures where each element is ", - valid_vals, - " but it is %s\n", - "\u2139 To create a list of quosures use `vars()`" - ), - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - if (accept_var & (!contains_vars(arg))) { - err_msg <- sprintf( - paste0( - "`%s` must be a list of quosures where each element is ", - valid_vals, - " but it is %s\n", - "\u2139 To create a list of quosures use `vars()`" - ), - arg_name(substitute(arg)), - what_is_it(arg) - ) - abort(err_msg) - } - - if (!is.null(required_elements)) { - missing_elements <- setdiff(required_elements, names(arg)) - if (length(missing_elements) >= 1L) { - err_msg <- sprintf( - "The following required elements are missing in `%s`: %s", - arg_name(substitute(arg)), - enumerate(missing_elements, quote_fun = squote) - ) - abort(err_msg) - } - } - - expr_list <- map(arg, quo_get_expr) - if (accept_expr) { - invalids <- expr_list[!map_lgl( - expr_list, - ~ is.symbol(.x) || - is.character(.x) || - is.numeric(.x) || - is.language(.x) || - is.atomic(.x) && is.na(.x) - )] +filter_if <- function(dataset, filter) { + assert_data_frame(dataset, check_is_grouped = FALSE) + assert_filter_cond(filter, optional = TRUE) + if (quo_is_null(filter)) { + dataset } else { - invalids <- expr_list[!map_lgl( - expr_list, - ~ is.symbol(.x) || - is.character(.x) || - is.numeric(.x) || - is.atomic(.x) && is.na(.x) - )] - } - if (length(invalids) > 0) { - abort( - paste0( - "The elements of the list ", - arg_name(substitute(arg)), - " must be ", - valid_vals, - ".\n", - paste( - names(invalids), - "=", - map_chr(invalids, expr_label), - "is of type", - map_chr(invalids, typeof), - collapse = "\n" - ) - ) - ) - } - - invisible(arg) -} - -#' Is an Element of a List of Lists/Classes Fulfilling a Condition? -#' -#' Checks if the elements of a list of named lists/classes fulfill a certain -#' condition. If not, an error is issued and all elements of the list not -#' fulfilling the condition are listed. -#' -#' @param list A list to be checked -#' -#' A list of named lists or classes is expected. -#' -#' @param element The name of an element of the lists/classes -#' -#' A character scalar is expected. -#' -#' @param condition Condition to be fulfilled -#' -#' The condition is evaluated for each element of the list. The element of the -#' lists/classes can be referred to by its name, e.g., `censor == 0` to check -#' the `censor` field of a class. -#' -#' @param message_text Text to be displayed in the message -#' -#' The text should describe the condition to be fulfilled, e.g., "For events -#' the censor values must be zero.". -#' -#' @param ... Objects required to evaluate the condition -#' -#' If the condition contains objects apart from the element, they have to be -#' passed to the function. See the second example below. -#' -#' @author Stefan Bundfuss -#' -#' @return -#' An error if the condition is not meet. The input otherwise. -#' -#' @keywords assertion -#' -#' @export -#' -#' @examples -#' death <- event_source( -#' dataset_name = "adsl", -#' filter = DTHFL == "Y", -#' date = DTHDT, -#' set_values_to = vars( -#' EVNTDESC = "DEATH", -#' SRCDOM = "ADSL", -#' SRCVAR = "DTHDT" -#' ) -#' ) -#' -#' lstalv <- censor_source( -#' dataset_name = "adsl", -#' date = LSTALVDT, -#' set_values_to = vars( -#' EVNTDESC = "LAST KNOWN ALIVE DATE", -#' SRCDOM = "ADSL", -#' SRCVAR = "LSTALVDT" -#' ) -#' ) -#' events <- list(death, lstalv) -#' try(assert_list_element( -#' list = events, -#' element = "censor", -#' condition = censor == 0, -#' message_text = "For events the censor values must be zero." -#' )) -#' -#' valid_datasets <- c("adrs", "adae") -#' try(assert_list_element( -#' list = events, -#' element = "dataset_name", -#' condition = dataset_name %in% valid_datasets, -#' valid_datasets = valid_datasets, -#' message_text = paste0( -#' "The dataset name must be one of the following:\n", -#' paste(valid_datasets, collapse = ", ") -#' ) -#' )) -assert_list_element <- function(list, element, condition, message_text, ...) { - assert_s3_class(list, "list") - assert_character_scalar(element) - condition <- assert_filter_cond(enquo(condition)) - assert_character_scalar(message_text) - # store elements of the lists/classes in a vector named as the element # - rlang::env_poke(current_env(), eval(element), lapply(list, `[[`, element)) - invalids <- !eval( - quo_get_expr(condition), - envir = list(...), - enclos = current_env() - ) - if (any(invalids)) { - invalids_idx <- which(invalids) - abort( - paste0( - message_text, - "\n", - paste0( - arg_name(substitute(list)), - "[[", invalids_idx, "]]$", element, - " = ", - lapply(list[invalids_idx], `[[`, element), - collapse = "\n" - ) - ) - ) - } - invisible(list) -} - - -#' Is There a One to One Mapping between Variables? -#' -#' Checks if there is a one to one mapping between two lists of variables. -#' -#' @param dataset Dataset to be checked -#' -#' The variables specified for `vars1` and `vars2` are expected. -#' -#' @param vars1 First list of variables -#' -#' @param vars2 Second list of variables -#' -#' @author Stefan Bundfuss -#' -#' @return -#' An error if the condition is not meet. The input otherwise. -#' -#' @keywords assertion -#' -#' @export -#' -#' @examples -#' data(admiral_adsl) -#' try( -#' assert_one_to_one(admiral_adsl, vars(SEX), vars(RACE)) -#' ) -assert_one_to_one <- function(dataset, vars1, vars2) { - assert_vars(vars1) - assert_vars(vars2) - assert_data_frame(dataset, required_vars = quo_c(vars1, vars2)) - - uniques <- unique(select(dataset, !!!vars1, !!!vars2)) - one_to_many <- uniques %>% - group_by(!!!vars1) %>% - filter(n() > 1) %>% - arrange(!!!vars1) - if (nrow(one_to_many) > 0) { - .datasets$one_to_many <- one_to_many - abort( - paste0( - "For some values of ", - vars2chr(vars1), - " there is more than one value of ", - vars2chr(vars2), - ".\nCall `get_one_to_many_dataset()` to get all one to many values." - ) - ) - } - many_to_one <- uniques %>% - group_by(!!!vars2) %>% - filter(n() > 1) %>% - arrange(!!!vars2) - if (nrow(many_to_one) > 0) { - .datasets$many_to_one <- many_to_one - abort( - paste0( - "There is more than one value of ", - vars2chr(vars1), - " for some values of ", - vars2chr(vars2), - ".\nCall `get_many_to_one_dataset()` to get all many to one values." - ) - ) + filter(dataset, !!filter) } } diff --git a/R/assertthat_checks.R b/R/assertthat_checks.R deleted file mode 100644 index 256a394822..0000000000 --- a/R/assertthat_checks.R +++ /dev/null @@ -1,310 +0,0 @@ -#' Is Date/Date-time? -#' -#' Checks if a date or date-time vector was specified -#' -#' @param arg The argument to check -#' -#' @author Stefan Bundfuss -#' -#' @return `TRUE` if the argument is a date or date-time, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' refdate <- lubridate::ymd("2020-01-02") -#' date <- lubridate::ymd("2020-02-03") -#' assertthat::assert_that(admiral:::is_date(refdate), admiral:::is_date(date)) -is_date <- function(arg) { - is.instant(arg) -} -on_failure(is_date) <- function(call, env) { - evld <- eval(call$arg, envir = env) - len <- length(evld) - msg <- if (len == 0) { - deparse(evld) - } else if (len == 1) { - evld - } else { - paste0("c(", paste(head(evld, 5), collapse = ", "), `if`(len > 5, ", ..."), ")") - } - paste0( - "Argument ", - deparse(call$arg), - " = ", - msg, - " is not a lubridate date." - ) -} - -#' Is Time Unit? -#' -#' Checks if a string is a time unit, i.e., 'years', 'months', 'days', 'hours', -#' 'minutes', or 'seconds'. -#' -#' @param arg The argument to check -#' -#' @author Stefan Bundfuss -#' -#' @return `TRUE` if the argument is a time unit, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' unit <- "days" -#' assertthat::assert_that(admiral:::is_timeunit(unit)) -is_timeunit <- function(arg) { - arg %in% c("years", "months", "days", "hours", "minutes", "seconds") -} -on_failure(is_timeunit) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - " = ", - eval(call$arg, envir = env), - " is not a valid time unit.", - " Valid time units are 'years', 'months', 'days', 'hours', 'minutes', and 'seconds'." - ) -} - -#' Check Validity of the Date Imputation Input -#' -#' Date_imputation format should be specified as "dd-mm" (e.g. "01-01") -#' or as a keyword: "FIRST", "MID", "LAST" -#' -#' @param arg The argument to check -#' -#' @author Samia Kabi -#' -#' @return `TRUE` if the argument is a valid date_imputation input, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' assertthat::assert_that(admiral:::is_valid_date_entry("01-02")) -#' assertthat::assert_that(admiral:::is_valid_date_entry("FIRST")) -is_valid_date_entry <- function(arg) { - pattern <- "^(01|02|03|04|05|06|07|08|09|10|11|12)-([0-9]{2})$" - grepl(pattern, arg) | str_to_upper(arg) %in% c("FIRST", "MID", "LAST") -} -on_failure(is_valid_date_entry) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - " = ", - eval(call$arg, envir = env), - " is not a valid date entry.\n", - "date_imputation should be specified as 'mm-dd' (e.g. '01-21') or ", - "'FIRST', 'MID', 'LAST' to get the first/mid/last day/month" - ) -} - -#' Check Validity of the Time Imputation Input -#' -#' Time_imputation format should be specified as "hh:mm:ss" (e.g. "00:00:00") -#' or as a keyword: "FIRST", "LAST" -#' -#' @param arg The argument to check -#' -#' @author Samia Kabi -#' -#' @return `TRUE` if the argument is a valid time_imputation input, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' assertthat::assert_that(admiral:::is_valid_time_entry("23:59:59")) -#' assertthat::assert_that(admiral:::is_valid_time_entry("FIRST")) -is_valid_time_entry <- function(arg) { - pattern <- "^([0-9]{2}):([0-9]{2}):([0-9]{2})$" - grepl(pattern, arg) | str_to_upper(arg) %in% c("FIRST", "LAST") -} -on_failure(is_valid_time_entry) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - " = ", - eval(call$arg, envir = env), - " is not a valid time entry.\n", - "time_imputation should be specified as 'hh:mm:ss' (e.g. '00:00:00') or ", - "'FIRST', 'LAST' to get the first/last time of the day" - ) -} - -#' Check Validity of the Minute/Second Portion of the Time Input -#' -#' Minutes and seconds are expected to range from 0 to 59 -#' -#' @param arg The argument to check -#' -#' @author Samia Kabi -#' -#' @return `TRUE` if the argument is a valid min/sec input, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' assertthat::assert_that(admiral:::is_valid_sec_min(59)) -is_valid_sec_min <- function(arg) { - arg %in% 0:59 -} -on_failure(is_valid_sec_min) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - " = ", - eval(call$arg, envir = env), - " is not a valid min/sec.\n", - "Values must be between between 0-59" - ) -} - -#' Check Validity of the Hour Portion in the Time Input -#' -#' Hours are expected to range from 0 to 23 -#' -#' @param arg The argument to check -#' -#' @author Samia Kabi -#' -#' @return `TRUE` if the argument is a valid hour input, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' assertthat::assert_that(admiral:::is_valid_hour(20)) -is_valid_hour <- function(arg) { - arg %in% 0:23 -} -on_failure(is_valid_hour) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - "=", - eval(call$arg, envir = env), - " is not a valid hour.\n", - "Values must be between 0-23" - ) -} - -#' Check Validity of the Day Portion in the Date Input -#' -#' Days are expected to range from 1 to 31 -#' -#' @param arg The argument to check -#' -#' @author Samia Kabi -#' -#' @return `TRUE` if the argument is a day input, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' assertthat::assert_that(admiral:::is_valid_day(20)) -is_valid_day <- function(arg) { - arg %in% 1:31 -} -on_failure(is_valid_day) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - " = ", - eval(call$arg, envir = env), - " is not a valid day.\n", - "Values must be between 1-31" - ) -} - -#' Check Validity of the Month Portion in the Date Input -#' -#' Days are expected to range from 1 to 12 -#' -#' @param arg The argument to check -#' -#' @author Samia Kabi -#' -#' @return `TRUE` if the argument is a month input, `FALSE` otherwise -#' -#' @keywords check -#' -#' @noRd -#' -#' @examples -#' assertthat::assert_that(admiral:::is_valid_month(12)) -is_valid_month <- function(arg) { - arg %in% 1:12 -} -on_failure(is_valid_month) <- function(call, env) { - paste0( - "Argument ", - deparse(call$arg), - " = ", - eval(call$arg, envir = env), - " is not a valid month.\n", - "Values for month must be between 1-12. ", - "Please check the date_imputation input: it should be sepcified as 'dd-mm'" - ) -} - -is_order_vars <- function(arg) { - quo_is_desc_call <- function(quo) { - expr <- quo_get_expr(quo) - is_call(expr) && - length(expr) == 2L && - deparse(expr[[1L]]) == "desc" && - is_symbol(expr[[2L]]) - } - - inherits(arg, "quosures") && - all(map_lgl(arg, ~ quo_is_symbol(.x) || quo_is_desc_call(.x))) -} -on_failure(is_order_vars) <- function(call, env) { - paste0( - backquote(deparse(call$arg)), - " is not a valid input for `order_vars`.", - " Valid inputs are created using `vars()` and may only contain symbols or calls involving `desc()`.\n\n", # nolint - " # Bad:\n", - " vars(ADT = impute_dtc(LBDTC), is.na(AVAL))\n\n", - " # Good:\n", - " vars(AVAL, desc(ADT))" - ) -} - -#' Check Whether an Argument Is Not a Quosure of a Missing Argument -#' -#' @param x Test object -#' -#' @return TRUE or error. -#' -#' @author Thomas Neitmann, Ondrej Slama -#' -#' @noRd -#' -#' @examples -#' test_fun <- function(x) { -#' x <- rlang::enquo(x) -#' assertthat::assert_that(quo_not_missing(x)) -#' } -#' test_fun(my_variable) # no missing argument -> returns TRUE -#' \dontrun{ -#' test_fun() # missing argument -> throws error -#' } -quo_not_missing <- function(x) { - !rlang::quo_is_missing(x) -} -on_failure(quo_not_missing) <- function(call, env) { - paste0("Argument `", deparse(call$x), "` is missing, with no default") -} diff --git a/R/call_derivation.R b/R/call_derivation.R index a89481c4b2..8beb920d8b 100644 --- a/R/call_derivation.R +++ b/R/call_derivation.R @@ -17,7 +17,8 @@ #' The input dataset with additional records/variables added depending on #' which `derivation` has been used. #' -#' @keywords user_utility high_order_function +#' @family high_order_function +#' @keywords high_order_function #' #' @export #' @@ -102,6 +103,7 @@ call_derivation <- function(dataset = NULL, derivation, variable_params, ...) { #' #' @return An object of class `params` #' +#' @family source_specifications #' @keywords source_specifications #' #' @export diff --git a/R/call_user_fun.R b/R/call_user_fun.R index bbbb51cc26..5d47aec9bf 100644 --- a/R/call_user_fun.R +++ b/R/call_user_fun.R @@ -9,7 +9,8 @@ #' #' @return The return value of the function call #' -#' @keywords dev_utility +#' @family utils_help +#' @keywords utils_help #' #' @export #' diff --git a/R/compat_friendly_type.R b/R/compat_friendly_type.R deleted file mode 100644 index 5897a04bd0..0000000000 --- a/R/compat_friendly_type.R +++ /dev/null @@ -1,166 +0,0 @@ -#' Return English-friendly Type -#' @param x Any R object. -#' @param value Whether to describe the value of `x`. -#' @param length Whether to mention the length of vectors and lists. -#' @return A string describing the type. Starts with an indefinite -#' article, e.g. "an integer vector". -#' @noRd -friendly_type_of <- function(x, value = TRUE, length = FALSE) { # nolint - if (rlang::is_missing(x)) { - return("absent") - } - - if (is.object(x)) { - if (inherits(x, "quosure")) { - type <- "quosure" - } else { - type <- paste(class(x), collapse = "/") - } - return(sprintf("a <%s> object", type)) - } - - if (!rlang::is_vector(x)) { - return(.rlang_as_friendly_type(typeof(x))) - } - - n_dim <- length(dim(x)) - - if (value && !n_dim) { - if (rlang::is_na(x)) { - return(switch(typeof(x), - logical = "`NA`", - integer = "an integer `NA`", - double = "a numeric `NA`", - complex = "a complex `NA`", - character = "a character `NA`", - .rlang_stop_unexpected_typeof(x) - )) - } - if (length(x) == 1 && !rlang::is_list(x)) { - return(switch(typeof(x), - logical = if (x) "`TRUE`" else "`FALSE`", - integer = "an integer", - double = "a number", - complex = "a complex number", - character = if (nzchar(x)) "a string" else "`\"\"`", - raw = "a raw value", - .rlang_stop_unexpected_typeof(x) - )) - } - if (length(x) == 0) { - return(switch(typeof(x), - logical = "an empty logical vector", - integer = "an empty integer vector", - double = "an empty numeric vector", - complex = "an empty complex vector", - character = "an empty character vector", - raw = "an empty raw vector", - list = "an empty list", - .rlang_stop_unexpected_typeof(x) - )) - } - } - - type <- .rlang_as_friendly_vector_type(typeof(x), n_dim) - - if (length && !n_dim) { - type <- paste0(type, sprintf(" of length %s", length(x))) - } - - type -} - -.rlang_as_friendly_vector_type <- function(type, n_dim) { - if (type == "list") { - if (n_dim < 2) { - return("a list") - } else if (n_dim == 2) { - return("a list matrix") - } else { - return("a list array") - } - } - - type <- switch(type, - logical = "a logical %s", - integer = "an integer %s", - numeric = , # nolint - double = "a double %s", - complex = "a complex %s", - character = "a character %s", - raw = "a raw %s", - type = paste0("a ", type, " %s") - ) - - if (n_dim < 2) { - kind <- "vector" - } else if (n_dim == 2) { - kind <- "matrix" - } else { - kind <- "array" - } - sprintf(type, kind) -} - -.rlang_as_friendly_type <- function(type) { - switch(type, - list = "a list", - NULL = "NULL", - environment = "an environment", - externalptr = "a pointer", - weakref = "a weak reference", - S4 = "an S4 object", - name = , # nolint - symbol = "a symbol", - language = "a call", - pairlist = "a pairlist node", - expression = "an expression vector", - char = "an internal string", - promise = "an internal promise", - ... = "an internal dots object", - any = "an internal `any` object", - bytecode = "an internal bytecode object", - primitive = , # nolint - builtin = , # nolint - special = "a primitive function", - closure = "a function", - type - ) -} - -.rlang_stop_unexpected_typeof <- function(x, call = rlang::caller_env()) { - rlang::abort( - sprintf("Unexpected type <%s>.", typeof(x)), - call = call - ) -} - -#' @param x The object type which does not conform to `what`. Its -#' `friendly_type_of()` is taken and mentioned in the error message. -#' @param what The friendly expected type. -#' @param ... Arguments passed to [abort()]. -#' @inheritParams args_error_context -#' @noRd -stop_input_type <- function(x, - what, - ..., - arg = rlang::caller_arg(x), - call = rlang::caller_env()) { - # From compat-cli.R - format_arg <- rlang::env_get( - nm = "format_arg", - last = topenv(), - default = NULL - ) - if (!is.function(format_arg)) { - format_arg <- function(x) sprintf("`%s`", x) - } - - message <- sprintf( - "%s must be %s, not %s.", - format_arg(arg), - what, - friendly_type_of(x) - ) - rlang::abort(message, ..., call = call) -} diff --git a/R/compute_duration.R b/R/compute_duration.R index f746a647d5..bbf808c83f 100644 --- a/R/compute_duration.R +++ b/R/compute_duration.R @@ -10,6 +10,8 @@ #' Refer to `derive_vars_dt()` to impute and derive a date from a date #' character vector to a date object. #' +#' Refer to `convert_dtc_to_dt()` to obtain a vector of imputed dates. +#' #' @param end_date The end date #' #' A date or date-time object is expected. @@ -17,6 +19,8 @@ #' Refer to `derive_vars_dt()` to impute and derive a date from a date #' character vector to a date object. #' +#' Refer to `convert_dtc_to_dt()` to obtain a vector of imputed dates. +#' #' @param in_unit Input unit #' #' See floor_in and add_one parameter for details. @@ -69,18 +73,20 @@ #' #' @return The duration between the two date in the specified unit #' -#' @keywords computation adam timing +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' #' @examples -#' # derive duration in days (integer), i.e., relative day +#' # Derive duration in days (integer), i.e., relative day #' compute_duration( #' start_date = lubridate::ymd_hms("2020-12-06T15:00:00"), #' end_date = lubridate::ymd_hms("2020-12-24T08:15:00") #' ) #' -#' # derive duration in days (float) +#' # Derive duration in days (float) #' compute_duration( #' start_date = lubridate::ymd_hms("2020-12-06T15:00:00"), #' end_date = lubridate::ymd_hms("2020-12-24T08:15:00"), @@ -88,7 +94,7 @@ #' add_one = FALSE #' ) #' -#' # derive age +#' # Derive age in years #' compute_duration( #' start_date = lubridate::ymd("1984-09-06"), #' end_date = lubridate::ymd("2020-02-24"), @@ -96,6 +102,15 @@ #' out_unit = "years", #' add_one = FALSE #' ) +#' +#' # Derive duration in hours +#' compute_duration( +#' start_date = lubridate::ymd_hms("2020-12-06T9:00:00"), +#' end_date = lubridate::ymd_hms("2020-12-06T13:30:00"), +#' out_unit = "hours", +#' floor_in = FALSE, +#' add_one = FALSE, +#' ) compute_duration <- function(start_date, end_date, in_unit = "days", @@ -106,7 +121,9 @@ compute_duration <- function(start_date, # Checks assert_that(is_date(start_date), is_date(end_date)) assert_that(is_timeunit(in_unit), is_timeunit(out_unit) | out_unit == "weeks") - assert_that(is.logical(floor_in), is.logical(add_one), is.logical(trunc_out)) + assert_logical_scalar(floor_in) + assert_logical_scalar(add_one) + assert_logical_scalar(trunc_out) # Derivation if (floor_in) { diff --git a/R/compute_framingham.R b/R/compute_framingham.R new file mode 100644 index 0000000000..80875cadca --- /dev/null +++ b/R/compute_framingham.R @@ -0,0 +1,146 @@ +#' Compute Framingham Heart Study Cardiovascular Disease 10-Year Risk Score +#' +#' Computes Framingham Heart Study Cardiovascular Disease 10-Year Risk Score +#' (FCVD101) based on systolic blood pressure, total serum cholesterol (mg/dL), +#' HDL serum cholesterol (mg/dL), sex, smoking status, diabetic status, +#' and treated for hypertension flag. +#' +#' @param sysbp Systolic blood pressure +#' +#' A numeric vector is expected. +#' +#' @param chol Total serum cholesterol (mg/dL) +#' +#' A numeric vector is expected. +#' +#' @param cholhdl HDL serum cholesterol (mg/dL) +#' +#' A numeric vector is expected. +#' +#' @param age Age (years) +#' +#' A numeric vector is expected. +#' +#' @param sex Gender +#' +#' A character vector is expected. +#' Expected Values: 'M' 'F' +#' +#' @param smokefl Smoking Status +#' +#' A character vector is expected. +#' Expected Values: 'Y' 'N' +#' +#' @param diabetfl Diabetic Status +#' +#' A character vector is expected. +#' Expected Values: 'Y' 'N' +#' +#' @param trthypfl Treated for hypertension status +#' +#' A character vector is expected. +#' Expected Values: 'Y' 'N' +#' +#' @author Alice Ehmann +#' +#' @details +#' The predicted probability of having cardiovascular disease (CVD) +#' within 10-years according to Framingham formula +#' \href{https://www.ahajournals.org/doi/pdf/10.1161/CIRCULATIONAHA.107.699579}{D'Agostino, 2008} is: # nolint +#' +#' \strong{For Women:} +#' +#' \tabular{rr}{ +#' \strong{Factor} \tab \strong{Amount} \cr +#' Age \tab 2.32888 \cr +#' Total Chol \tab 1.20904 \cr +#' HDL Chol \tab -0.70833 \cr +#' Sys BP \tab 2.76157 \cr +#' Sys BP + Hypertension Meds \tab 2.82263 \cr +#' Smoker \tab 0.52873 \cr +#' Non-Smoker \tab 0 \cr +#' Diabetic \tab 0.69154 \cr +#' Not Diabetic \tab 0 \cr +#' Average Risk \tab 26.1931 \cr +#' Risk Period \tab 0.95012 \cr +#' } +#' +#' \strong{For Men:} +#' +#' \tabular{rr}{ +#' \strong{Factor} \tab \strong{Amount} \cr +#' Age \tab 3.06117 \cr +#' Total Chol \tab 1.12370 \cr +#' HDL Chol \tab -0.93263 \cr +#' Sys BP \tab 1.93303 \cr +#' Sys BP + Hypertension Meds \tab 2.99881 \cr +#' Smoker \tab .65451 \cr +#' Non-Smoker \tab 0 \cr +#' Diabetic \tab 0.57367 \cr +#' Not Diabetic \tab 0 \cr +#' Average Risk \tab 23.9802 \cr +#' Risk Period \tab 0.88936 \cr +#' } +#' +#' \strong{The equation for calculating risk:} +#' +#' \deqn{RiskFactors = (log(Age) * AgeFactor) +#' + (log(TotalChol) * TotalCholFactor) +#' + (log(CholHDL) * CholHDLFactor) \\ +#' + (log(SysBP) * SysBPFactor) + Smoker +#' + Diabetes Present - AvgRisk} +#' +#' \deqn{Risk = 100 * (1 - RiskPeriodFactor ^ exp(RiskFactors))} +#' +#' @return A numeric vector of Framingham values +#' +#' @keywords com_bds_findings +#' @family com_bds_findings +#' +#' @export +#' +#' @seealso [derive_param_framingham()] +#' +#' @examples +#' compute_framingham( +#' sysbp = 133, chol = 216.16, cholhdl = 54.91, age = 53, +#' sex = "M", smokefl = "N", diabetfl = "N", trthypfl = "N" +#' ) +#' +#' compute_framingham( +#' sysbp = 161, chol = 186.39, cholhdl = 64.19, age = 52, +#' sex = "F", smokefl = "Y", diabetfl = "N", trthypfl = "Y" +#' ) +compute_framingham <- function(sysbp, chol, cholhdl, age, sex, smokefl, + diabetfl, trthypfl) { + assert_numeric_vector(sysbp) + assert_numeric_vector(chol) + assert_numeric_vector(cholhdl) + assert_numeric_vector(age) + assert_character_vector(sex, values = c("M", "F")) + assert_character_vector(smokefl, values = c("Y", "N")) + assert_character_vector(diabetfl, values = c("Y", "N")) + assert_character_vector(trthypfl, values = c("Y", "N")) + + aval <- if_else(sex == "F", + 1 - (0.95012^exp((2.32888 * log(age)) + + (1.20904 * log(chol)) + - (0.70833 * log(cholhdl)) + + (2.76157 * log(if_else(trthypfl == "N", sysbp, 1))) + + (2.82263 * log(if_else(trthypfl == "Y", sysbp, 1))) + + (0.52873 * (if_else(smokefl == "Y", 1, 0))) + + (0.69154 * (if_else(diabetfl == "Y", 1, 0))) + - 26.1931)), + 1 - (0.88936^exp((3.06117 * log(age)) + + (1.12370 * log(chol)) + - (0.93263 * log(cholhdl)) + + (1.93303 * log(if_else(trthypfl == "N", sysbp, 1))) + + (1.99881 * log(if_else(trthypfl == "Y", sysbp, 1))) + + (0.65451 * (if_else(smokefl == "Y", 1, 0))) + + (0.57367 * (if_else(diabetfl == "Y", 1, 0))) + - 23.9802)) + ) + + + aval * 100 +} diff --git a/R/compute_qual_imputation.R b/R/compute_qual_imputation.R new file mode 100644 index 0000000000..a37faf6d67 --- /dev/null +++ b/R/compute_qual_imputation.R @@ -0,0 +1,94 @@ +#' Compute Factor for Value Imputations When Character Value Contains < or > +#' +#' Function to compute factor for value imputation when character +#' value contains < or >. The factor is calculated using the number of decimals. +#' If there are no decimals, the factor is 1, otherwise the factor = 1/10^decimal +#' place. For example, the factor for 100 = 1, the factor for 5.4 = 1/10^1, +#' the factor for 5.44 = 1/10^2. This results in no additional false precision +#' added to the value. This is an intermediate function. +#' +#' Derive an imputed value +#' +#' @param character_value_decimal Character value to determine decimal precision +#' +#' @return Decimal precision value to add or subtract +#' +#' @export +#' +#' @author Alice Ehmann Ojesh Upadhyay +#' +#' @keywords com_bds_findings +#' @family com_bds_findings +#' +#' @examples +#' compute_qual_imputation_dec("<40.1") +compute_qual_imputation_dec <- function(character_value_decimal) { + decimal <- ifelse(str_detect(character_value_decimal, "\\."), + 1 / (10^(str_length(str_trim(character_value_decimal)) - # nolint + str_locate(str_trim(character_value_decimal), "\\."))), + 1 / (10^0) + ) + + decimal +} + +#' Function to Impute Values When Qualifier Exists in Character Result +#' +#' Derive an imputed value +#' +#' @param character_value Character version of value to be imputed +#' @param imputation_type (default value=1) +#' Valid Values: +#' 1: Strip <, >, = and convert to numeric. +#' 2: imputation_type=1 and if the character value contains a < or >, the number of +#' of decimals associated with the character value is found and then a factor of +#' 1/10^(number of decimals + 1) will be added/subtracted from the numeric value. +#' If no decimals exists, a factor of 1/10 will be added/subtracted from the value. +#' @param factor Numeric value (default=0), when using `imputation_type` = 1, this +#' value can be added or subtracted when the qualifier is removed. +#' +#' @return The imputed value +#' +#' @importFrom dplyr case_when +#' @importFrom dplyr if_else +#' @importFrom stringr str_detect +#' +#' @export +#' +#' @author Alice Ehmann Ojesh Upadhyay +#' +#' @keywords com_bds_findings +#' @family com_bds_findings +#' +#' @examples +#' compute_qual_imputation("<40") +compute_qual_imputation <- function(character_value, imputation_type = 1, factor = 0) { + numeric_value <- ifelse(grepl("[A-z]", character_value), + NA_real_, + as.numeric(gsub("=|>|<", "", character_value)) + ) + + if (imputation_type == 1) { + numeric_value <- + case_when( + str_detect(character_value, ">") & !str_detect(character_value, "=") ~ + numeric_value + factor, + str_detect(character_value, "<") & !str_detect(character_value, "=") ~ + numeric_value - factor, + TRUE ~ numeric_value + ) + } + + if (imputation_type == 2) { + numeric_value <- + case_when( + str_detect(character_value, ">") & !str_detect(character_value, "=") ~ + numeric_value + compute_qual_imputation_dec(character_value), + str_detect(character_value, "<") & !str_detect(character_value, "=") ~ + numeric_value - compute_qual_imputation_dec(character_value), + TRUE ~ numeric_value + ) + } + + numeric_value +} diff --git a/R/create_query_data.R b/R/create_query_data.R index da7309cdb1..631ad27102 100644 --- a/R/create_query_data.R +++ b/R/create_query_data.R @@ -140,7 +140,8 @@ #' @return A dataset to be used as input dataset to the `dataset_queries` #' argument in `derive_vars_query()` #' -#' @keywords adae adcm user_utility +#' @family der_occds +#' @keywords der_occds #' #' @seealso [derive_vars_query()], [query()], [smq_select()], [sdg_select()], [Queries Dataset #' Documentation](../articles/queries_dataset.html) @@ -381,6 +382,9 @@ create_query_data <- function(queries, #' #' *Permitted Values*: `"smq"`, `"sdg"` #' +#' @family der_occds +#' @keywords der_occds +#' #' @return Output dataset of the access function #' #' @author Stefan Bundfuss @@ -452,6 +456,9 @@ get_terms_from_db <- function(version, #' #' Should be `"SMQ`" or `"SDG"`. #' +#' @keywords source_specifications +#' @family source_specifications +#' #' @return An error is issued if `version` or `fun` is null. #' #' @author Stefan Bundfuss @@ -495,7 +502,7 @@ assert_db_requirements <- function(version, version_arg_name, fun, fun_arg_name, #' Create an `query` object #' #' A `query` object defines a query, e.g., a Standard MedDRA Query (SMQ), a -#' Standardised Drug Grouping (SDG), or a customized query (CQ). It is used +#' Standardized Drug Grouping (SDG), or a customized query (CQ). It is used #' as input to `create_query_data()`. #' #' @param prefix The value is used to populate `VAR_PREFIX` in the output @@ -562,6 +569,7 @@ assert_db_requirements <- function(version, version_arg_name, fun, fun_arg_name, #' @seealso [create_query_data()], [smq_select()], [sdg_select()], [Queries Dataset #' Documentation](../articles/queries_dataset.html) #' +#' @family source_specifications #' @keywords source_specifications #' #' @export @@ -654,13 +662,16 @@ query <- function(prefix, #' #' @author Stefan Bundfuss #' +#' @keywords source_specifications +#' @family source_specifications +#' #' @seealso [query()] #' #' @export #' #' @return The original object. validate_query <- function(obj) { - assert_that(inherits(obj, "query")) + assert_s3_class(obj, "query") values <- unclass(obj) prefix <- values$prefix assert_character_scalar(prefix) @@ -795,7 +806,8 @@ validate_query <- function(obj) { #' #' @seealso [create_query_data()], [query()] #' -#' @keywords assertion +#' @keywords source_specifications +#' @family source_specifications #' #' @author Stefan Bundfuss assert_terms <- function(terms, @@ -885,6 +897,7 @@ assert_terms <- function(terms, #' #' @seealso [create_query_data()], [query()] #' +#' @family source_specifications #' @keywords source_specifications #' #' @export @@ -906,13 +919,16 @@ smq_select <- function(name = NULL, #' #' @seealso [smq_select()] #' +#' @keywords source_specifications +#' @family source_specifications +#' #' @author Stefan Bundfuss #' #' @export #' #' @return The original object. validate_smq_select <- function(obj) { - assert_that(inherits(obj, "smq_select")) + assert_s3_class(obj, "smq_select") values <- unclass(obj) name <- values$name assert_character_scalar(name, @@ -951,7 +967,8 @@ validate_smq_select <- function(obj) { #' #' @seealso [smq_select()] #' -#' @keywords dev_utility +#' @keywords source_specifications +#' @family source_specifications #' #' @export #' @@ -986,6 +1003,7 @@ format.smq_select <- function(x, ...) { #' #' @seealso [create_query_data()], [query()] #' +#' @family source_specifications #' @keywords source_specifications #' #' @export @@ -1007,11 +1025,14 @@ sdg_select <- function(name = NULL, #' #' @seealso [sdg_select()] #' +#' @keywords source_specifications +#' @family source_specifications +#' #' @export #' #' @return The original object. validate_sdg_select <- function(obj) { - assert_that(inherits(obj, "sdg_select")) + assert_s3_class(obj, "sdg_select") values <- unclass(obj) name <- values$name assert_character_scalar(name, @@ -1045,7 +1066,8 @@ validate_sdg_select <- function(obj) { #' #' @seealso [sdg_select()] #' -#' @keywords dev_utility +#' @keywords source_specifications +#' @family source_specifications #' #' @export #' diff --git a/R/create_single_dose_dataset.R b/R/create_single_dose_dataset.R index af3e0c4aa0..f42bb60c35 100644 --- a/R/create_single_dose_dataset.R +++ b/R/create_single_dose_dataset.R @@ -36,6 +36,7 @@ #' @export #' #' @keywords metadata +#' @family metadata #' #' @rdname dose_freq_lookup @@ -195,13 +196,22 @@ dose_freq_lookup <- tibble::tribble( #' #' @param start_date The start date #' -#' A date or date-time object is expected. This object cannot contain `NA` values. +#' A date object is expected. This object cannot contain `NA` values. #' #' Refer to `derive_vars_dt()` to impute and derive a date from a date #' character vector to a date object. #' #' Default: `ASTDT` #' +#' @param start_datetime The start date-time +#' +#' A date-time object is expected. This object cannot contain `NA` values. +#' +#' Refer to `derive_vars_dtm()` to impute and derive a date-time from a date +#' character vector to a date object. +#' +#' Default: `ASTDTM` +#' #' @param end_date The end date #' #' A date or date-time object is expected. This object cannot contain `NA` values. @@ -211,6 +221,15 @@ dose_freq_lookup <- tibble::tribble( #' #' Default: `AENDT` #' +#' @param end_datetime The end date-time +#' +#' A date-time object is expected. This object cannot contain `NA` values. +#' +#' Refer to `derive_vars_dtm()` to impute and derive a date-time from a date +#' character vector to a date object. +#' +#' Default: `AENDTM` +#' #' @param lookup_table The dose frequency value lookup table #' #' The table used to look up `dose_freq` values and determine the appropriate @@ -230,17 +249,22 @@ dose_freq_lookup <- tibble::tribble( #' #' Default: `CDISC_VALUE` (column of `dose_freq_lookup`) #' +#' @param keep_source_vars List of variables to be retained from source dataset +#' +#' Default: vars(USUBJID, EXDOSFRQ, ASTDT, ASTDTM, AENDT, AENDTM) #' #' @details Each aggregate dose row is split into multiple rows which each #' represent a single dose.The number of completed dose periods between -#' `start_date` and `end_date` is calculated with `compute_duration` and -#' multiplied by `DOSE_COUNT`. For `DOSE_WINDOW` values of `"WEEK"`, -#' `"MONTH"`, and `"YEAR"`, `CONVERSION_FACTOR` is used to convert into days -#' the time object to be added to `start_date`. +#' `start_date` or `start_datetime` and `end_date` or `end_datetime` is +#' calculated with `compute_duration` and multiplied by `DOSE_COUNT`. +#' For `DOSE_WINDOW` values of `"WEEK"`, `"MONTH"`, and `"YEAR"`, +#' `CONVERSION_FACTOR` is used to convert into days the time object +#' to be added to `start_date`. #' #' @author Michael Thorpe, Andrew Smith #' -#' @keywords adae adex user_utility +#' @family der_occds +#' @keywords der_occds #' #' @return The input dataset with a single dose per row. #' @@ -250,16 +274,21 @@ dose_freq_lookup <- tibble::tribble( #' # Example with default lookup #' #' library(lubridate) +#' library(stringr) #' #' data <- tibble::tribble( -#' ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, -#' "P01", "Q2D", ymd("2021-01-01"), ymd("2021-01-07"), -#' "P01", "Q3D", ymd("2021-01-08"), ymd("2021-01-15"), -#' "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), ymd("2021-01-29") +#' ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, +#' "P01", "Q2D", ymd("2021-01-01"), ymd_hms("2021-01-01 10:30:00"), +#' ymd("2021-01-07"), ymd_hms("2021-01-07 11:30:00"), +#' "P01", "Q3D", ymd("2021-01-08"), ymd_hms("2021-01-08 12:00:00"), +#' ymd("2021-01-14"), ymd_hms("2021-01-14 14:00:00"), +#' "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), ymd_hms("2021-01-15 09:57:00"), +#' ymd("2021-01-29"), ymd_hms("2021-01-29 10:57:00") #' ) #' #' create_single_dose_dataset(data) #' +#' #' # Example with custom lookup #' #' custom_lookup <- tibble::tribble( @@ -269,45 +298,81 @@ dose_freq_lookup <- tibble::tribble( #' ) #' #' data <- tibble::tribble( -#' ~USUBJID, ~EXDOSFRQ, ~ASTDTM, ~AENDTM, -#' "P01", "Q30MIN", ymd_hms("2021-01-01T06:00:00"), ymd_hms("2021-01-01T07:00:00"), -#' "P02", "Q90MIN", ymd_hms("2021-01-01T06:00:00"), ymd_hms("2021-01-01T09:00:00") +#' ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, +#' "P01", "Q30MIN", ymd("2021-01-01"), ymd_hms("2021-01-01T06:00:00"), +#' ymd("2021-01-01"), ymd_hms("2021-01-01T07:00:00"), +#' "P02", "Q90MIN", ymd("2021-01-01"), ymd_hms("2021-01-01T06:00:00"), +#' ymd("2021-01-01"), ymd_hms("2021-01-01T09:00:00") #' ) #' #' create_single_dose_dataset(data, #' lookup_table = custom_lookup, -#' lookup_column = Value, -#' start_date = ASTDTM, -#' end_date = AENDTM +#' lookup_column = Value #' ) create_single_dose_dataset <- function(dataset, dose_freq = EXDOSFRQ, start_date = ASTDT, + start_datetime = ASTDTM, end_date = AENDT, + end_datetime = AENDTM, lookup_table = dose_freq_lookup, - lookup_column = CDISC_VALUE) { + lookup_column = CDISC_VALUE, + keep_source_vars = vars( + USUBJID, EXDOSFRQ, ASTDT, ASTDTM, + AENDT, AENDTM + )) { col_names <- colnames(dataset) dose_freq <- assert_symbol(enquo(dose_freq)) lookup_column <- assert_symbol(enquo(lookup_column)) start_date <- assert_symbol(enquo(start_date)) + start_datetime <- assert_symbol(enquo(start_datetime)) end_date <- assert_symbol(enquo(end_date)) + end_datetime <- assert_symbol(enquo(end_datetime)) assert_data_frame(dataset, required_vars = quo_c(dose_freq, start_date, end_date)) assert_data_frame(lookup_table, required_vars = vars(DOSE_WINDOW, DOSE_COUNT, CONVERSION_FACTOR)) + assert_data_frame(dataset, required_vars = keep_source_vars) + + # Checking that the dates specified follow the ADaM naming convention of ending in DT + start_datec <- as_string(as_name(start_date)) + start_date_chk <- stringr::str_locate_all(start_datec, "DT") + start_date_chk_pos <- as.vector(start_date_chk[[1]]) + + if (stringr::str_length(start_datec) != start_date_chk_pos[-1]) { + err_msg <- paste0( + "The argument start_date is expected to have a name like xxxDT.\n", + "Please check as it does not follow the expected naming convention" + ) + abort(err_msg) + } + + end_datec <- as_string(as_name(end_date)) + end_date_chk <- stringr::str_locate_all(end_datec, "DT") + end_date_chk_pos <- as.vector(end_date_chk[[1]]) + + if (stringr::str_length(end_datec) != end_date_chk_pos[-1]) { + err_msg <- paste0( + "The argument end_date is expected to have a name like xxxDT.\n", + "Please check as it does not follow the expected naming convention" + ) + abort(err_msg) + } # Set up lookup table to be joined to dataset lookup <- lookup_table %>% rename(!!dose_freq := !!lookup_column) - # Check that NAs do not appear in start_date or end_date columns + # Check that NAs do not appear in start_date or start_datetime or end_date or end_datetime columns na_check <- dataset %>% - filter(is.na(!!start_date) | is.na(!!end_date)) %>% - select(!!start_date, !!end_date) + filter(is.na(!!start_date) | is.na(!!end_date) | + is.na(!!start_datetime) | is.na(!!end_datetime)) %>% + select(!!start_date, !!end_date, !!start_datetime, !!end_datetime) if (nrow(na_check) > 0) { na_columns <- paste0(colnames(na_check)[colSums(is.na(na_check)) > 0], collapse = ", ") err_msg <- paste0( - "The arguments start_date and end_date cannot contain `NA` values.\n", + "The arguments start_date or start_datetime", + " and end_date or end_datetime cannot contain `NA` values.\n", sprintf( "Please check %s for `NA` values.", na_columns @@ -348,8 +413,12 @@ create_single_dose_dataset <- function(dataset, dataset_part_2 <- dataset_part_2 %>% left_join(lookup, by = as.character(quo_get_expr(dose_freq))) %>% mutate(dose_periods = case_when( - DOSE_WINDOW == "MINUTE" ~ compute_duration(!!start_date, !!end_date, out_unit = "minutes"), - DOSE_WINDOW == "HOUR" ~ compute_duration(!!start_date, !!end_date, out_unit = "hours"), + DOSE_WINDOW == "MINUTE" ~ compute_duration(!!start_datetime, !!end_datetime, + in_unit = "minutes", out_unit = "minutes" + ), + DOSE_WINDOW == "HOUR" ~ compute_duration(!!start_datetime, !!end_datetime, + in_unit = "hours", out_unit = "hours" + ), DOSE_WINDOW == "DAY" ~ compute_duration(!!start_date, !!end_date, out_unit = "days"), DOSE_WINDOW == "WEEK" ~ compute_duration(!!start_date, !!end_date, out_unit = "weeks"), DOSE_WINDOW == "MONTH" ~ compute_duration(!!start_date, !!end_date, out_unit = "months"), @@ -358,42 +427,30 @@ create_single_dose_dataset <- function(dataset, mutate(dose_count = ceiling(dose_periods * DOSE_COUNT)) %>% derive_var_obs_number(new_var = grpseq) - # Flag to determine if date or datetime must be returned - - time_flag <- nrow(dataset_part_2 %>% - filter(DOSE_WINDOW %in% c("MINUTE", "HOUR"))) > 0 - - - if (time_flag & - (is.Date(eval_tidy(start_date, dataset)) | is.Date(eval_tidy(end_date, dataset)))) { - err_msg <- paste0( - sprintf( - "%s involves hours or minutes but one of %s or %s is a date variable. ", - as.character(quo_get_expr(dose_freq)), - as.character(quo_get_expr(start_date)), - as.character(quo_get_expr(end_date)) - ), - "\nPlease provide datetime variables for start_date and end_date arguments." - ) - abort(err_msg) - } - # Generate a row for each completed dose dataset_part_2 <- dataset_part_2[rep(row.names(dataset_part_2), dataset_part_2$dose_count), ] - # Determine amount of days to adjust start_date and end_date + # Determine amount of days to adjust start_date or start_datetime and end_date or end_datetime dataset_part_2 <- dataset_part_2 %>% group_by(grpseq, !!dose_freq, !!start_date, !!end_date) %>% mutate(time_increment = (row_number() - 1) / (DOSE_COUNT)) %>% ungroup() %>% - mutate(time_differential = case_when( - DOSE_WINDOW == "MINUTE" ~ minutes(floor(time_increment)), - DOSE_WINDOW == "HOUR" ~ hours(floor(time_increment)), - DOSE_WINDOW %in% c("DAY", "WEEK", "MONTH", "YEAR") ~ - days(floor(time_increment / CONVERSION_FACTOR)) - )) + mutate( + time_differential = case_when( + DOSE_WINDOW == "MINUTE" ~ minutes(floor(.data$time_increment)), + DOSE_WINDOW == "HOUR" ~ hours(floor(.data$time_increment)), + DOSE_WINDOW %in% c("DAY", "WEEK", "MONTH", "YEAR") ~ + days(floor(.data$time_increment / CONVERSION_FACTOR)) + ), + time_differential_dt = case_when( + DOSE_WINDOW == "MINUTE" ~ days(floor(.data$time_increment / 1440)), + DOSE_WINDOW == "HOUR" ~ days(floor(.data$time_increment / 24)), + DOSE_WINDOW %in% c("DAY", "WEEK", "MONTH", "YEAR") ~ + days(floor(.data$time_increment / CONVERSION_FACTOR)) + ) + ) # Adjust start_date and end_date, drop calculation columns, make sure nothing # later than end_date shows up in output @@ -401,17 +458,26 @@ create_single_dose_dataset <- function(dataset, dataset_part_2 <- dataset_part_2 %>% mutate( !!dose_freq := "ONCE", - !!start_date := !!start_date + time_differential + !!start_date := !!start_date + .data$time_differential_dt, + !!start_datetime := !!start_datetime + .data$time_differential, ) dataset_part_2 <- dataset_part_2 %>% filter(!(!!start_date > !!end_date)) %>% - mutate(!!end_date := !!start_date) %>% + mutate( + !!end_date := !!start_date, + !!end_datetime := case_when( + DOSE_WINDOW %in% c("MINUTE", "HOUR") ~ !!start_datetime, + DOSE_WINDOW %in% c("DAY", "WEEK", "MONTH", "YEAR") ~ + ymd_hms(paste0(!!start_date, " ", format(!!end_datetime, format = "%H:%M:%S"))) + ) + ) %>% select(!!!vars(all_of(col_names))) # Stitch back together dataset <- bind_rows(dataset_part_1, dataset_part_2) + dataset <- dataset %>% select(!!!keep_source_vars) return(dataset) } diff --git a/R/data.R b/R/data.R index db48a341c2..7f17a1d257 100644 --- a/R/data.R +++ b/R/data.R @@ -1,76 +1,69 @@ #' Single Dose Exposure Dataset #' #' A derived dataset with single dose per date. -#' +#' @keywords datasets +#' @family datasets #' @source #' Derived from the `ex` dataset using `{admiral}` and `{dplyr}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/example_scripts/derive_single_dose.R}) "ex_single" #' Queries Dataset -#' +#' @keywords datasets +#' @family datasets +#' @source #' An example of standard query dataset to be used in deriving variables in ADAE and ADCM #' "queries" +#' Queries MH Dataset +#' @keywords datasets +#' @family datasets +#' @source +#' An example of standard query MH dataset to be used in deriving variables in ADMH +#' +"queries_mh" + #' Subject Level Analysis Dataset #' #' An example subject level analysis dataset -#' +#' @keywords datasets +#' @family datasets #' @source #' Derived from the `dm` and `ds` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adsl.R}) #' "admiral_adsl" -#' Adverse Event Analysis Dataset -#' -#' An example adverse event analysis dataset -#' -#' @source -#' Derived from the `adsl` and `ae` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adae.R}) -#' -"admiral_adae" - -#' Concomitant Medication Analysis Dataset -#' -#' An example concomitant medication analysis dataset -#' -#' @source -#' Derived from the `adsl` and `cm` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adcm.R}) -#' -"admiral_adcm" - -#' Exposure Analysis Dataset -#' -#' An example exposure analysis dataset -#' -#' @source -#' Derived from the `adsl` and `ex` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adex.R}) -#' -"admiral_adex" - -#' ECG Analysis Dataset -#' -#' An example ECG analysis dataset -#' -#' @source -#' Derived from the `adsl` and `eg` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adeg.R}) -#' -"admiral_adeg" - -#' Vital Signs Analysis Dataset -#' -#' An example vital signs analysis dataset -#' -#' @source -#' Derived from the `adsl` and `vs` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_advs.R}) -#' -"admiral_advs" - -#' Pharmacokinetics Parameters Analysis Dataset -#' -#' An example pharmacokinetics parameters analysis dataset -#' -#' @source -#' Derived from the `adsl` and `pp` datasets using `{admiral}` (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adpp.R}) -#' -"admiral_adpp" +#' Metadata Holding Grading Criteria for NCI-CTCAEv4 +#' +#' @details +#' This metadata has its origin in the ADLB Grading Spec Excel file which ships with `{admiral}` +#' and can be accessed using `system.file("adlb_grading/adlb_grading_spec.xlsx", package = "admiral")`. +#' The dataset contained in there has the following columns: +#' - `SOC`: variable to hold the SOC of the lab test criteria. +#' - `TERM`: variable to hold the term describing the criteria applied to a particular lab test, +#' eg. 'Anemia' or 'INR Increased'. Note: the variable is case insensitive. +#' - `Grade 1`: Criteria defining lab value as Grade 1. +#' - `Grade 2`: Criteria defining lab value as Grade 2. +#' - `Grade 3`: Criteria defining lab value as Grade 3. +#' - `Grade 4`: Criteria defining lab value as Grade 4. +#' - `Grade 5`: Criteria defining lab value as Grade 5. +#' - `Definition`: Holds the definition of the lab test abnormality. +#' - `GRADE_CRITERIA_CODE`: variable to hold code that creates grade based on defined criteria. +#' - `SI_UNIT_CHECK`: variable to hold unit of particular lab test. Used to check against input data +#' if criteria is based on absolute values. +#' - `VAR_CHECK`: List of variables required to implement lab grade criteria. Use to check against +#' input data. +#' - `DIRECTION`: variable to hold the direction of the abnormality of a particular lab test +#' value. 'L' is for LOW values, 'H' is for HIGH values. Note: the variable is case insensitive. +#' - `COMMENT`: Holds any information regarding rationale behind implementation of grading criteria. +#' +#' Note: Variables `SOC`, `TERM`, `Grade 1`, `Grade 2`,`Grade 3`,`Grade 4`,`Grade 5`, `Definition` +#' are from the source document on NCI-CTC website defining the grading criteria. +#' From these variables only 'TERM' is used in the {admiral} code, the rest are for information and +#' tracability only. +#' +#' @author Gordon Miller +#' +#' @keywords datasets +#' @family datasets +"atoxgr_criteria_ctcv4" diff --git a/R/dataset_vignette.R b/R/dataset_vignette.R deleted file mode 100644 index 65af241aa5..0000000000 --- a/R/dataset_vignette.R +++ /dev/null @@ -1,87 +0,0 @@ -#' Output a Dataset in a Vignette in the admiral Format -#' -#' Output a dataset in a vignette with the pre-specified admiral format. -#' -#' @param dataset Dataset to output in the vignette -#' -#' @param display_vars Variables selected to demonstrate the outcome of the derivation -#' -#' Permitted Values: list of variables -#' -#' Default is NULL -#' -#' If `display_vars` is not NULL, only the selected variables are visible in the vignette while the -#' other variables are hidden. They can be made visible by clicking the`Choose the columns to -#' display` button. -#' -#' @param filter Filter condition -#' -#' The specified condition is applied to the dataset before it is displayed. -#' -#' Permitted Values: a condition -#' -#' @return A HTML table -#' -#' @keywords dev_utility -#' -#' @export -#' -#' @examples -#' library(admiral) -#' library(DT) -#' library(dplyr) -#' library(admiral.test) -#' data("admiral_dm") -#' -#' dataset_vignette(admiral_dm) -#' dataset_vignette(admiral_dm, -#' display_vars = vars(USUBJID, RFSTDTC, DTHDTC), -#' filter = ARMCD == "Pbo" -#' ) -dataset_vignette <- function(dataset, display_vars = NULL, filter = NULL) { - display_vars <- assert_vars(display_vars, optional = TRUE) - assert_data_frame(dataset, required_vars = display_vars) - filter <- assert_filter_cond(enquo(filter), optional = TRUE) - - out <- dataset %>% - filter_if(filter) %>% - mutate_if(is.character, as.factor) - - # Create a short markdown table when this function is called outside {pkgdown} - if (!identical(Sys.getenv("IN_PKGDOWN"), "true")) { - if (is.null(display_vars)) { - return(knitr::kable(utils::head(out, 10))) - } else { - return(knitr::kable(utils::head(select(out, !!!display_vars), 10))) - } - } - - if (!is.null(display_vars)) { - hide_columns <- which(!(colnames(out) %in% vars2chr(display_vars))) - cols_to_hide <- list(list(targets = hide_columns - 1, visible = FALSE)) - } else { - cols_to_hide <- list() - } - - DT::datatable( - out, - rownames = FALSE, - filter = "top", - extensions = c("Buttons", "ColReorder", "Scroller"), - options = list( - columnDefs = cols_to_hide, - searchHighlight = TRUE, - searching = TRUE, - pageLength = 5, - lengthMenu = c(5, 10, 15, 20, 50, 100), - dom = "Bfrtipl", - buttons = list(list( - extend = "colvis", - text = "Choose the columns to display", - scroller = T, - collectionLayout = "fixed two-column" - )), - colReorder = TRUE - ) - ) -} diff --git a/R/derive_adeg_params.R b/R/derive_adeg_params.R index 9a696abd00..e770b8fa8a 100644 --- a/R/derive_adeg_params.R +++ b/R/derive_adeg_params.R @@ -45,7 +45,7 @@ #' #' Permitted Values: A variable of the input dataset or a function call #' -#' @inheritParams derive_derived_param +#' @inheritParams derive_param_computed #' #' @seealso [compute_qtc()] #' @@ -54,7 +54,9 @@ #' @return The input dataset with the new parameter added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation adeg +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -141,7 +143,7 @@ derive_param_qtc <- function(dataset, get_unit_expr = !!get_unit_expr ) - derive_derived_param( + derive_param_computed( dataset, filter = !!filter, parameters = c(qt_code, rr_code), @@ -169,7 +171,8 @@ derive_param_qtc <- function(dataset, #' #' @export #' -#' @keywords user_utility +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings #' #' @examples #' default_qtc_paramcd("Sagie") @@ -209,7 +212,9 @@ default_qtc_paramcd <- function(method) { #' #' Usually this computation function can not be used with `%>%`. #' -#' @keywords computation adeg +#' @family com_bds_findings +#' +#' @keywords com_bds_findings #' #' @export #' @@ -256,7 +261,7 @@ compute_qtc <- function(qt, rr, method) { #' #' Permitted Values: character value #' -#' @inheritParams derive_derived_param +#' @inheritParams derive_param_computed #' #' @inheritParams derive_param_qtc #' @@ -265,7 +270,8 @@ compute_qtc <- function(qt, rr, method) { #' @return The input dataset with the new parameter added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation adeg +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings #' #' @export #' @@ -317,7 +323,7 @@ derive_param_rr <- function(dataset, get_unit_expr = !!get_unit_expr ) - derive_derived_param( + derive_param_computed( dataset, filter = !!filter, parameters = c(hr_code), @@ -343,7 +349,9 @@ derive_param_rr <- function(dataset, #' @return RR interval in msec: #' \deqn{\frac{60000}{HR}}{60000 / HR} #' -#' @keywords computation adeg +#' @family com_bds_findings +#' +#' @keywords com_bds_findings #' #' @export #' diff --git a/R/derive_advs_params.R b/R/derive_advs_params.R index 8478e34bab..bd3684bbdc 100644 --- a/R/derive_advs_params.R +++ b/R/derive_advs_params.R @@ -34,7 +34,7 @@ #' #' Permitted Values: character value #' -#' @inheritParams derive_derived_param +#' @inheritParams derive_param_computed #' #' @inheritParams derive_param_qtc #' @@ -51,7 +51,9 @@ #' @return The input dataset with the new parameter added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation advs +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -137,7 +139,7 @@ derive_param_map <- function(dataset, ) } - derive_derived_param( + derive_param_computed( dataset, filter = !!filter, parameters = c(sysbp_code, diabp_code, hr_code), @@ -177,7 +179,9 @@ derive_param_map <- function(dataset, #' #' @return A numeric vector of MAP values #' -#' @keywords computation advs +#' @family com_bds_findings +#' +#' @keywords com_bds_findings #' #' @export #' @@ -247,7 +251,7 @@ compute_map <- function(diabp, sysbp, hr = NULL) { #' #' Permitted Values: character value #' -#' @inheritParams derive_derived_param +#' @inheritParams derive_param_computed #' #' @inheritParams derive_param_qtc #' @@ -256,7 +260,9 @@ compute_map <- function(diabp, sysbp, hr = NULL) { #' @return The input dataset with the new parameter added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation advs +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -339,7 +345,7 @@ derive_param_bsa <- function(dataset, ) ) - derive_derived_param( + derive_param_computed( dataset, filter = !!filter, parameters = c(height_code, weight_code), @@ -389,7 +395,9 @@ derive_param_bsa <- function(dataset, #' #' @return The BSA (Body Surface Area) in m^2. #' -#' @keywords computation adam BSA +#' @family com_bds_findings +#' +#' @keywords com_bds_findings #' #' @export #' @@ -472,7 +480,7 @@ compute_bsa <- function(height = height, #' #' Permitted Values: character value #' -#' @inheritParams derive_derived_param +#' @inheritParams derive_param_computed #' #' @inheritParams derive_param_qtc #' @@ -485,7 +493,9 @@ compute_bsa <- function(height = height, #' @return The input dataset with the new parameter added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation advs +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -542,7 +552,7 @@ derive_param_bmi <- function(dataset, get_unit_expr = !!get_unit_expr ) - derive_derived_param( + derive_param_computed( dataset, filter = !!filter, parameters = c(weight_code, height_code), @@ -577,7 +587,9 @@ derive_param_bmi <- function(dataset, #' #' @return The BMI (Body Mass Index Area) in kg/m^2. #' -#' @keywords computation adam BMI +#' @family com_bds_findings +#' +#' @keywords com_bds_findings #' #' @export #' diff --git a/R/derive_date_vars.R b/R/derive_date_vars.R index b2f7df5614..55ba42ffc7 100644 --- a/R/derive_date_vars.R +++ b/R/derive_date_vars.R @@ -6,22 +6,46 @@ #' @param dtc The `'--DTC'` date to impute #' #' A character date is expected in a format like `yyyy-mm-dd` or -#' `yyyy-mm-ddThh:mm:ss`. If the year part is not recorded (missing date), no -#' imputation is performed. +#' `yyyy-mm-ddThh:mm:ss`. Trailing components can be omitted and `-` is a +#' valid "missing" value for any component. #' -#' @param date_imputation The value to impute the day/month when a datepart is -#' missing. +#' @param highest_imputation Highest imputation level +#' +#' The `highest_imputation` argument controls which components of the DTC +#' value are imputed if they are missing. All components up to the specified +#' level are imputed. +#' +#' If a component at a higher level than the highest imputation level is +#' missing, `NA_character_` is returned. For example, for `highest_imputation +#' = "D"` `"2020"` results in `NA_character_` because the month is missing. +#' +#' If `"n"` is specified, no imputation is performed, i.e., if any component is +#' missing, `NA_character_` is returned. +#' +#' If `"Y"` is specified, `date_imputation` should be `"first"` or `"last"` +#' and `min_dates` or `max_dates` should be specified respectively. Otherwise, +#' `NA_character_` is returned if the year component is missing. #' -#' If `NULL`: no date imputation is performed and partial dates are returned as +#' *Default*: `"h"` +#' +#' *Permitted Values*: `"Y"` (year, highest level), `"M"` (month), `"D"` +#' (day), `"h"` (hour), `"m"` (minute), `"s"` (second), `"n"` (none, lowest +#' level) +#' +#' @param date_imputation The value to impute the day/month when a datepart is #' missing. #' -#' Otherwise, a character value is expected, either as a -#' - format with month and day specified as `"mm-dd"`: e.g. `"06-15"` for the 15th -#' of June, -#' - or as a keyword: `"FIRST"`, `"MID"`, `"LAST"` to impute to the first/mid/last +#' A character value is expected, either as a +#' - format with month and day specified as `"mm-dd"`: e.g. `"06-15"` for the +#' 15th of June (The year can not be specified; for imputing the year +#' `"first"` or `"last"` together with `min_dates` or `max_dates` argument can +#' be used (see examples).), +#' - or as a keyword: `"first"`, `"mid"`, `"last"` to impute to the first/mid/last #' day/month. #' -#' Default is `NULL`. +#' The argument is ignored if `highest_imputation` is less then `"D"`. +#' +#' *Default*: `"first"`. #' #' @param time_imputation The value to impute the time when a timepart is #' missing. @@ -29,9 +53,11 @@ #' A character value is expected, either as a #' - format with hour, min and sec specified as `"hh:mm:ss"`: e.g. `"00:00:00"` #' for the start of the day, -#' - or as a keyword: `"FIRST"`,`"LAST"` to impute to the start/end of a day. +#' - or as a keyword: `"first"`,`"last"` to impute to the start/end of a day. +#' +#' The argument is ignored if `highest_imputation = "n"`. #' -#' Default is `"00:00:00"`. +#' *Default*: `"first"`. #' #' @param min_dates Minimum dates #' @@ -45,13 +71,13 @@ #' For example #' #' ```{r echo=TRUE, eval=FALSE} -#' impute_dtc( -#' "2020-11", -#' min_dates = list( -#' ymd_hms("2020-12-06T12:12:12"), -#' ymd_hms("2020-11-11T11:11:11") -#' ), -#' date_imputation = "first" +#' impute_dtc_dtm( +#' "2020-11", +#' min_dates = list( +#' ymd_hms("2020-12-06T12:12:12"), +#' ymd_hms("2020-11-11T11:11:11") +#' ), +#' highest_imputation = "M" #' ) #' ``` #' @@ -60,6 +86,10 @@ #' `"2020-12-06T12:12:12"` is ignored. Returning `"2020-12-06T12:12:12"` would #' have changed the month although it is not missing (in the `dtc` date). #' +#' For date variables (not datetime) in the list the time is imputed to +#' `"00:00:00"`. Specifying date variables makes sense only if the date is +#' imputed. If only time is imputed, date variables do not affect the result. +#' #' @param max_dates Maximum dates #' #' A list of dates is expected. It is ensured that the imputed date is not after @@ -67,22 +97,28 @@ #' cut off date. Only dates which are in the range of possible dates are #' considered. A date or date-time object is expected. #' +#' For date variables (not datetime) in the list the time is imputed to +#' `"23:59:59"`. Specifying date variables makes sense only if the date is +#' imputed. If only time is imputed, date variables do not affect the result. + #' @param preserve Preserve day if month is missing and day is present #' #' For example `"2019---07"` would return `"2019-06-07` if `preserve = TRUE` -#' (and `date_imputation = "MID"`). +#' (and `date_imputation = "mid"`). #' #' Permitted Values: `TRUE`, `FALSE` #' -#' Default: `FALSE` -#' -#' @author Samia Kabi +#' *Default*: `FALSE` #' #' @details Usually this computation function can not be used with `%>%`. #' #' @return A character vector #' -#' @keywords computation timing +#' @author Samia Kabi, Stefan Bundfuss +#' +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' @@ -101,214 +137,772 @@ #' "" #' ) #' -#' # No date imputation (date_imputation defaulted to NULL) +#' # No date imputation (highest_imputation defaulted to "h") #' # Missing time part imputed with 00:00:00 portion by default -#' impute_dtc(dtc = dates) +#' impute_dtc_dtm(dtc = dates) #' -#' # No date imputation (date_imputation defaulted to NULL) +#' # No date imputation (highest_imputation defaulted to "h") #' # Missing time part imputed with 23:59:59 portion -#' impute_dtc( +#' impute_dtc_dtm( #' dtc = dates, #' time_imputation = "23:59:59" #' ) #' #' # Same as above -#' impute_dtc( +#' impute_dtc_dtm( #' dtc = dates, -#' time_imputation = "LAST" +#' time_imputation = "last" #' ) #' #' # Impute to first day/month if date is partial #' # Missing time part imputed with 00:00:00 portion by default -#' impute_dtc( +#' impute_dtc_dtm( #' dtc = dates, -#' date_imputation = "01-01" +#' highest_imputation = "M" #' ) #' # same as above -#' impute_dtc( +#' impute_dtc_dtm( #' dtc = dates, -#' date_imputation = "FIRST" +#' highest_imputation = "M", +#' date_imputation = "01-01" #' ) #' #' # Impute to last day/month if date is partial #' # Missing time part imputed with 23:59:59 portion -#' impute_dtc( +#' impute_dtc_dtm( #' dtc = dates, -#' date_imputation = "LAST", -#' time_imputation = "LAST" +#' date_imputation = "last", +#' time_imputation = "last" #' ) #' #' # Impute to mid day/month if date is partial #' # Missing time part imputed with 00:00:00 portion by default -#' impute_dtc( +#' impute_dtc_dtm( #' dtc = dates, -#' date_imputation = "MID" +#' highest_imputation = "M", +#' date_imputation = "mid" #' ) #' #' # Impute a date and ensure that the imputed date is not before a list of #' # minimum dates -#' impute_dtc( +#' impute_dtc_dtm( #' "2020-12", #' min_dates = list( #' ymd_hms("2020-12-06T12:12:12"), #' ymd_hms("2020-11-11T11:11:11") #' ), -#' date_imputation = "first" -#' ) -impute_dtc <- function(dtc, - date_imputation = NULL, - time_imputation = "00:00:00", - min_dates = NULL, - max_dates = NULL, - preserve = FALSE) { - # Issue a warning if incorrect DTC is present - n_chr <- nchar(dtc) +#' highest_imputation = "M" +#' ) +#' +#' # Impute completely missing dates (only possible if min_dates or max_dates is specified) +#' impute_dtc_dtm( +#' c("2020-12", NA_character_), +#' min_dates = list( +#' ymd_hms("2020-12-06T12:12:12"), +#' ymd_hms("2020-11-11T11:11:11") +#' ), +#' highest_imputation = "Y" +#' ) +impute_dtc_dtm <- function(dtc, + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", + min_dates = NULL, + max_dates = NULL, + preserve = FALSE) { + # Check arguments ---- + assert_character_vector(dtc) valid_dtc <- is_valid_dtc(dtc) warn_if_invalid_dtc(dtc, valid_dtc) + imputation_levels <- c( + none = "n", + second = "s", + minute = "m", + hour = "h", + day = "D", + month = "M", + year = "Y" + ) + assert_character_scalar(highest_imputation, values = imputation_levels) + highest_imputation <- dtm_level(highest_imputation) + date_imputation <- + assert_character_scalar( + date_imputation, + case_sensitive = FALSE + ) + time_imputation <- + assert_character_scalar( + time_imputation, + case_sensitive = FALSE + ) assert_logical_scalar(preserve) - # date imputation - if (!is.null(date_imputation)) { - # check input for date_imputation - assert_that(is_valid_date_entry(date_imputation)) - - # Specific setup for FIRST/MID/LAST - # make keywords case-insensitive - date_imputation <- str_to_upper(date_imputation) - if (date_imputation %in% c("FIRST", "MID", "LAST")) { - d <- list(FIRST = "01", MID = "15", LAST = "01")[[date_imputation]] - mo <- list(FIRST = "01", MID = "06", LAST = "12")[[date_imputation]] - } else { - # otherwise, use time_imputation input - day__ <- as.integer(sub(".*-", "", date_imputation)) - mo__ <- as.integer(sub("-.*", "", date_imputation)) - # check input for day and moth are valid - assert_that(is_valid_day(day__)) - assert_that(is_valid_month(mo__)) - - d <- sprintf("%02d", day__) - mo <- sprintf("%02d", mo__) + if (length(dtc) == 0) { + return(vector("character")) + } + + # Parse character date ---- + partial <- get_partialdatetime(dtc) + components <- names(partial) + + # Handle preserve argument ---- + if (!preserve) { + for (i in 2:6) { + partial[[i]] <- if_else(is.na(partial[[i - 1]]), NA_character_, partial[[i]]) } + } + # Determine target components ---- + target_date <- get_imputation_target_date( + date_imputation = date_imputation, + month = partial[["month"]] + ) + target_time <- get_imputation_target_time( + time_imputation = time_imputation + ) + target <- c(target_date, target_time) + + for (c in components) { + if (highest_imputation < dtm_level(imputation_levels[[c]])) { + target[[c]] <- "xx" + } + } + + # Impute ---- + imputed <- vector("list", 6) + names(imputed) <- components + for (c in components) { + imputed[[c]] <- if_else(is.na(partial[[c]]), target[[c]], partial[[c]]) + } - imputed_date <- case_when( - !valid_dtc ~ NA_character_, - n_chr >= 10 ~ substr(dtc, 1, 10), - n_chr == 9 ~ paste0(substr(dtc, 1, 4), "-", mo, "-", d), - n_chr == 7 ~ paste0(dtc, "-", d), - n_chr == 4 ~ paste0(dtc, "-", mo, "-", d) + imputed_dtc <- + paste0( + paste(imputed[["year"]], imputed[["month"]], imputed[["day"]], sep = "-"), + "T", + paste(imputed[["hour"]], imputed[["minute"]], imputed[["second"]], sep = ":") ) - # 3 blocks of if/else statements that deal with date imputation and - # preserving partial dates. - # Ex: 2019---07 with MID and preserve = TRUE gives 2019-06-07 - if (date_imputation == "MID" & preserve) { - imputed_date <- case_when( - n_chr == 9 ~ paste0(substr(dtc, 1, 4), "-", "06", "-", substr(dtc, 8, 9)), - n_chr == 4 ~ paste0(dtc, "-", "06", "-", "30"), - TRUE ~ imputed_date + imputed_dtc <- + if_else( + str_detect(imputed_dtc, "x"), + NA_character_, + imputed_dtc + ) + + if (date_imputation == "last") { + imputed_dtc <- + if_else( + is.na(partial[["day"]]), + strftime( + rollback(ymd_hms(imputed_dtc) + months(1)), + format = "%Y-%m-%dT%H:%M:%S", + tz = "UTC" + ), + imputed_dtc ) - } else if (date_imputation == "MID" & !preserve) { - imputed_date <- case_when( - n_chr == 9 ~ paste0(substr(dtc, 1, 4), "-", "06", "-", "30"), - n_chr == 4 ~ paste0(dtc, "-", "06", "-", "30"), - TRUE ~ imputed_date + } + + # Handle min_dates and max_dates argument ---- + restrict_imputed_dtc_dtm( + dtc, + imputed_dtc = imputed_dtc, + min_dates = min_dates, + max_dates = max_dates + ) +} + +#' Create a `dtm_level` object +#' +#' @param level Datetime level +#' +#' *Permitted Values*: `"Y"` (year, highest level), `"M"` (month), `"D"` +#' (day), `"h"` (hour), `"m"` (minute), `"s"` (second, lowest level), `"n"` +#' (none) +#' +#' @returns A `dtm_level` object +#' +#' @details A `dtm_level` object is an ordered factor, i.e., two objects can be +#' compared. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' +#' @keywords utils_impute +dtm_level <- function(level) { + out <- + factor( + level, + levels = c("n", "s", "m", "h", "D", "M", "Y"), + ordered = TRUE + ) + class(out) <- c("dtm_level", class(out)) + out +} + +#' Parse DTC variable and Determine Components +#' +#' @param dtc The `'--DTC'` date to parse +#' +#' A character date is expected in a format like `yyyy-mm-dd` or +#' `yyyy-mm-ddThh:mm:ss`. Trailing components can be omitted and `-` is a +#' valid value for any component. +#' +#' @returns A list of character vectors. The elements of the list are named +#' "year", "month", "day", "hour", "minute", and "second". Missing components +#' are set to `NA_character_`. +#' +#' @details The function can be replaced by the parttime parser once it is +#' available. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' +#' @keywords utils_impute +#' +#' @seealso [impute_dtc_dtm()], [impute_dtc_dt()] +get_partialdatetime <- function(dtc) { + two <- "(\\d{2}|-?)" + partialdate <- stringr::str_match(dtc, paste0( + "(\\d{4}|-?)-?", + two, + "-?", + two, + "T?", + two, + ":?", + two, + ":?", + "(\\d{2}(\\.\\d{1,5})?)?" + )) + partial <- vector("list", 6) + components <- c("year", "month", "day", "hour", "minute", "second") + names(partial) <- components + for (i in seq_along(components)) { + partial[[i]] <- partialdate[, i + 1] + partial[[i]] <- if_else(partial[[i]] %in% c("-", ""), NA_character_, partial[[i]]) + } + partial +} + +#' Get Date Imputation Targets +#' +#' @param date_imputation The value to impute the day/month when a datepart is +#' missing. +#' +#' A character value is expected, either as a +#' - format with month and day specified as `"mm-dd"`: e.g. `"06-15"` for the 15th +#' of June, +#' - or as a keyword: `"first"`, `"mid"`, `"last"` to impute to the first/mid/last +#' day/month. + +#' @param month Month component of the partial date +#' +#' @returns A list of character vectors. The elements of the list are named +#' "year", "month", "day". +#' +#' @details +#' +#' - For `date_imputation = "first"` `"0000"`, `"01"`, `"01"` are returned. +#' - For `date_imputation = "mid"` `"xxxx"`, `"06"`, `"30"` if `month` is `NA` +#' and `"15"` otherwise are returned. +#' - For `date_imputation = "last"` `"9999"`, `"12"`, `"31"` are returned. +#' - For `date_imputation = "-
"` `"xxxx"`, `""`, `"
"` are returned. +#' +#' `"xxxx"` indicates that the component is undefined. If an undefined +#' component occurs in the imputed DTC value, the imputed DTC value is set to +#' `NA_character_` in the imputation functions. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' +#' @keywords utils_impute +#' +#' @seealso [impute_dtc_dtm()], [impute_dtc_dt()] +get_imputation_target_date <- function(date_imputation, + month) { + target <- vector("list", 3) + names(target) <- c("year", "month", "day") + if (date_imputation == "first") { + target[["year"]] <- "0000" + target[["month"]] <- "01" + target[["day"]] <- "01" + } else if (date_imputation == "mid") { + target[["year"]] <- "xxxx" + target[["month"]] <- "06" + target[["day"]] <- if_else(is.na(month), "30", "15") + } else if (date_imputation == "last") { + target[["year"]] <- "9999" + target[["month"]] <- "12" + target[["day"]] <- "28" + } else { + target[["year"]] <- "xxxx" + target[["month"]] <- str_sub(date_imputation, 1, 2) + target[["day"]] <- str_sub(date_imputation, 4, 5) + } + target +} + +#' Get Time Imputation Targets +#' +#' @param time_imputation The value to impute the time when a timepart is +#' missing. +#' +#' A character value is expected, either as a +#' - format with hour, min and sec specified as `"hh:mm:ss"`: e.g. `"00:00:00"` +#' for the start of the day, +#' - or as a keyword: `"first"`,`"last"` to impute to the start/end of a day. +#' +#' @returns A list of character vectors. The elements of the list are named +#' "hour", "minute", "second". +#' +#' @details +#' +#' - For `time_imputation = "first"` `"00"`, `"00"`, `"00"` are returned. +#' - For `time_imputation = "last"` `"23"`, `"59"`, `"59"` are returned. +#' - For `time_imputation = "::"` `""`, `""`, `""` are returned. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' +#' @keywords utils_impute +#' +#' @seealso [impute_dtc_dtm()] +get_imputation_target_time <- function(time_imputation) { + target <- vector("list", 3) + names(target) <- c("hour", "minute", "second") + if (time_imputation == "first") { + target[["hour"]] <- "00" + target[["minute"]] <- "00" + target[["second"]] <- "00" + } else if (time_imputation == "last") { + target[["hour"]] <- "23" + target[["minute"]] <- "59" + target[["second"]] <- "59" + } else { + target[["hour"]] <- str_sub(time_imputation, 1, 2) + target[["minute"]] <- str_sub(time_imputation, 4, 5) + target[["second"]] <- str_sub(time_imputation, 7, -1) + } + target +} + +#' Restrict Imputed DTC date to Minimum/Maximum Dates +#' +#' @param imputed_dtc The imputed DTC date +#' +#' @inheritParams impute_dtc_dtm +#' +#' @returns +#' - The last of the minimum dates (`min_dates`) which are in the range of the +#' partial DTC date (`dtc`) +#' - The first of the maximum dates (`max_dates`) which are in the range of the +#' partial DTC date (`dtc`) +#' - `imputed_dtc` if the partial DTC date (`dtc`) is not in range of any of +#' the minimum or maximum dates. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' +#' @keywords utils_impute +#' +#' @seealso [impute_dtc_dtm()], [impute_dtc_dt()] +restrict_imputed_dtc_dtm <- function(dtc, + imputed_dtc, + min_dates, + max_dates) { + if (!is.null(min_dates) | !is.null(max_dates)) { + # determine range of possible dates + min_dtc <- + impute_dtc_dtm( + dtc, + highest_imputation = "Y", + date_imputation = "first", + time_imputation = "first" ) - } else if (date_imputation != "MID" & preserve) { - imputed_date <- case_when( - n_chr == 9 ~ paste0(substr(dtc, 1, 4), "-", mo, "-", substr(dtc, 8, 9)), - TRUE ~ imputed_date + max_dtc <- + impute_dtc_dtm( + dtc, + highest_imputation = "Y", + date_imputation = "last", + time_imputation = "last" + ) + } + if (!is.null(min_dates)) { + # for each minimum date within the range ensure that the imputed date is not + # before it + for (min_date in min_dates) { + assert_that(is_date(min_date)) + min_date_iso <- strftime(min_date, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") + imputed_dtc <- if_else( + min_dtc <= min_date_iso & min_date_iso <= max_dtc, + pmax(imputed_dtc, min_date_iso), + imputed_dtc, + missing = imputed_dtc ) } - - # Ex: 2019---07 with LAST and preserve = TRUE gives 2019-12-07 - if (date_imputation == "LAST" & !preserve) { - imputed_date <- case_when( - n_chr < 10 & date_imputation == "LAST" & !preserve ~ - as.character( - ceiling_date( - as.Date(imputed_date, format = "%Y-%m-%d"), "month" - ) - days(1) - ), - TRUE ~ imputed_date + } + if (!is.null(max_dates)) { + # for each maximum date within the range ensure that the imputed date is not + # after it + for (max_date in max_dates) { + assert_that(is_date(max_date)) + max_date <- convert_date_to_dtm( + max_date, + time_imputation = "last" ) - } else if (date_imputation == "LAST" & preserve) { - imputed_date <- case_when( - n_chr == 9 ~ paste0(substr(dtc, 1, 4), "-", "12", "-", substr(dtc, 8, 9)), - n_chr %in% c(4, 7) ~ - as.character( - ceiling_date( - as.Date(imputed_date, format = "%Y-%m-%d"), "month" - ) - days(1) - ), - TRUE ~ imputed_date + max_date_iso <- strftime(max_date, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") + imputed_dtc <- if_else( + min_dtc <= max_date_iso & max_date_iso <= max_dtc, + pmin(imputed_dtc, max_date_iso), + imputed_dtc, + missing = imputed_dtc ) } + } + imputed_dtc +} - # Ex: 2019---07 with FIRST and preserve = TRUE gives 2019-01-07 - if (date_imputation == "FIRST" & preserve) { - imputed_date <- case_when( - n_chr == 9 & date_imputation == "FIRST" & preserve ~ - paste0(substr(dtc, 1, 4), "-", "01", "-", substr(dtc, 8, 9)), - TRUE ~ imputed_date - ) +#' Impute Partial Date Portion of a `'--DTC'` Variable +#' +#' Imputation partial date portion of a `'--DTC'` variable based on user input. +#' +#' @param dtc The `'--DTC'` date to impute +#' +#' A character date is expected in a format like `yyyy-mm-dd` or +#' `yyyy-mm-ddThh:mm:ss`. Trailing components can be omitted and `-` is a +#' valid "missing" value for any component. +#' +#' @param highest_imputation Highest imputation level +#' +#' The `highest_imputation` argument controls which components of the DTC +#' value are imputed if they are missing. All components up to the specified +#' level are imputed. +#' +#' If a component at a higher level than the highest imputation level is +#' missing, `NA_character_` is returned. For example, for `highest_imputation +#' = "D"` `"2020"` results in `NA_character_` because the month is missing. +#' +#' If `"n"` is specified no imputation is performed, i.e., if any component is +#' missing, `NA_character_` is returned. +#' +#' If `"Y"` is specified, `date_imputation` should be `"first"` or `"last"` +#' and `min_dates` or `max_dates` should be specified respectively. Otherwise, +#' `NA_character_` is returned if the year component is missing. +#' +#' *Default*: `"n"` +#' +#' *Permitted Values*: `"Y"` (year, highest level), `"M"` (month), `"D"` +#' (day), `"n"` (none, lowest level) +#' +#' @param date_imputation The value to impute the day/month when a datepart is +#' missing. +#' +#' A character value is expected, either as a +#' - format with month and day specified as `"mm-dd"`: e.g. `"06-15"` for the +#' 15th of June (The year can not be specified; for imputing the year +#' `"first"` or `"last"` together with `min_dates` or `max_dates` argument can +#' be used (see examples).), +#' - or as a keyword: `"first"`, `"mid"`, `"last"` to impute to the first/mid/last +#' day/month. +#' +#' The argument is ignored if `highest_imputation` is less then `"D"`. +#' +#' *Default*: `"first"` +#' +#' @param min_dates Minimum dates +#' +#' A list of dates is expected. It is ensured that the imputed date is not +#' before any of the specified dates, e.g., that the imputed adverse event start +#' date is not before the first treatment date. Only dates which are in the +#' range of possible dates of the `dtc` value are considered. The possible dates +#' are defined by the missing parts of the `dtc` date (see example below). This +#' ensures that the non-missing parts of the `dtc` date are not changed. +#' A date or date-time object is expected. +#' For example +#' +#' ```{r echo=TRUE, eval=FALSE} +#' impute_dtc_dtm( +#' "2020-11", +#' min_dates = list( +#' ymd_hms("2020-12-06T12:12:12"), +#' ymd_hms("2020-11-11T11:11:11") +#' ), +#' highest_imputation = "M" +#' ) +#' ``` +#' +#' returns `"2020-11-11T11:11:11"` because the possible dates for `"2020-11"` +#' range from `"2020-11-01T00:00:00"` to `"2020-11-30T23:59:59"`. Therefore +#' `"2020-12-06T12:12:12"` is ignored. Returning `"2020-12-06T12:12:12"` would +#' have changed the month although it is not missing (in the `dtc` date). +#' +#' @param max_dates Maximum dates +#' +#' A list of dates is expected. It is ensured that the imputed date is not after +#' any of the specified dates, e.g., that the imputed date is not after the data +#' cut off date. Only dates which are in the range of possible dates are +#' considered. A date or date-time object is expected. +#' +#' @param preserve Preserve day if month is missing and day is present +#' +#' For example `"2019---07"` would return `"2019-06-07` if `preserve = TRUE` +#' (and `date_imputation = "MID"`). +#' +#' Permitted Values: `TRUE`, `FALSE` +#' +#' Default: `FALSE` +#' +#' @details Usually this computation function can not be used with `%>%`. +#' +#' @return A character vector +#' +#' @author Samia Kabi, Stefan Bundfuss +#' +#' @family com_date_time +#' +#' @keywords com_date_time +#' +#' @export +#' +#' @examples +#' library(lubridate) +#' +#' dates <- c( +#' "2019-07-18T15:25:40", +#' "2019-07-18T15:25", +#' "2019-07-18T15", +#' "2019-07-18", +#' "2019-02", +#' "2019", +#' "2019", +#' "2019---07", +#' "" +#' ) +#' +#' # No date imputation (highest_imputation defaulted to "n") +#' impute_dtc_dt(dtc = dates) +#' +#' # Impute to first day/month if date is partial +#' impute_dtc_dt( +#' dtc = dates, +#' highest_imputation = "M" +#' ) +#' # Same as above +#' impute_dtc_dtm( +#' dtc = dates, +#' highest_imputation = "M", +#' date_imputation = "01-01" +#' ) +#' +#' # Impute to last day/month if date is partial +#' impute_dtc_dt( +#' dtc = dates, +#' highest_imputation = "M", +#' date_imputation = "last", +#' ) +#' +#' # Impute to mid day/month if date is partial +#' impute_dtc_dt( +#' dtc = dates, +#' highest_imputation = "M", +#' date_imputation = "mid" +#' ) +#' +#' # Impute a date and ensure that the imputed date is not before a list of +#' # minimum dates +#' impute_dtc_dt( +#' "2020-12", +#' min_dates = list( +#' ymd("2020-12-06"), +#' ymd("2020-11-11") +#' ), +#' highest_imputation = "M" +#' ) +#' +#' # Impute completely missing dates (only possible if min_dates or max_dates is specified) +#' impute_dtc_dt( +#' c("2020-12", NA_character_), +#' min_dates = list( +#' ymd("2020-12-06"), +#' ymd("2020-11-11") +#' ), +#' highest_imputation = "Y" +#' ) +impute_dtc_dt <- function(dtc, + highest_imputation = "n", + date_imputation = "first", + min_dates = NULL, + max_dates = NULL, + preserve = FALSE) { + # Check arguments ---- + assert_character_vector(dtc) + valid_dtc <- is_valid_dtc(dtc) + warn_if_invalid_dtc(dtc, valid_dtc) + imputation_levels <- c( + none = "n", + day = "D", + month = "M", + year = "Y" + ) + assert_character_scalar(highest_imputation, values = imputation_levels) + highest_imputation <- dt_level(highest_imputation) + date_imputation <- + assert_character_scalar( + date_imputation, + case_sensitive = FALSE + ) + assert_logical_scalar(preserve) + + # Parse character date ---- + two <- "(\\d{2}|-?)" + partialdate <- stringr::str_match(dtc, paste0( + "(\\d{4}|-?)-?", + two, + "-?", + two + )) + partial <- vector("list", 3) + components <- c("year", "month", "day") + names(partial) <- components + for (i in seq_along(components)) { + partial[[i]] <- partialdate[, i + 1] + partial[[i]] <- if_else(partial[[i]] %in% c("-", ""), NA_character_, partial[[i]]) + } + + # Handle preserve argument ---- + if (!preserve) { + for (i in 2:3) { + partial[[i]] <- if_else(is.na(partial[[i - 1]]), NA_character_, partial[[i]]) } - } else { - # no imputation - imputed_date <- if_else(n_chr >= 10 & valid_dtc, substr(dtc, 1, 10), NA_character_) } + # Determine target components ---- + target <- get_imputation_target_date( + date_imputation = date_imputation, + month = partial[["month"]] + ) - if (!is.null(time_imputation)) { - # impute time - assert_that(is_valid_time_entry(time_imputation)) - # make keywords case-insensitive - time_imputation <- str_to_upper(time_imputation) - if (time_imputation == "FIRST") { - imputed_time <- "00:00:00" - sec <- ":00" - min <- ":00" - h <- "00" - } else if (time_imputation == "LAST") { - imputed_time <- "23:59:59" - sec <- ":59" - min <- ":59" - h <- "23" - } else { - imputed_time <- time_imputation - sec <- paste0(":", paste0(substr(dtc, 18, 19), substr(time_imputation, 7, 8))) - min <- paste0(":", paste0(substr(dtc, 15, 16), substr(time_imputation, 4, 5))) - h <- paste0(substr(dtc, 12, 13), substr(time_imputation, 1, 2)) + for (c in components) { + if (highest_imputation < dt_level(imputation_levels[[c]])) { + target[[c]] <- "xx" } + } + + # Impute ---- + imputed <- vector("list", 3) + names(imputed) <- components + for (c in components) { + imputed[[c]] <- if_else(is.na(partial[[c]]), target[[c]], partial[[c]]) + } - imputed_time <- case_when( - n_chr >= 19 ~ substr(dtc, 12, 19), - n_chr == 16 ~ paste0(substr(dtc, 12, 16), sec), - n_chr == 13 ~ paste0(substr(dtc, 12, 13), min, sec), - n_chr == 10 ~ paste0(h, min, sec), - TRUE ~ imputed_time + imputed_dtc <- + paste(imputed[["year"]], imputed[["month"]], imputed[["day"]], sep = "-") + + imputed_dtc <- + if_else( + str_detect(imputed_dtc, "x"), + NA_character_, + imputed_dtc ) - } else { - # no imputation - imputed_time <- if_else(n_chr >= 19 & valid_dtc, substr(dtc, 12, 19), NA_character_) + + if (date_imputation == "last") { + imputed_dtc <- + if_else( + is.na(partial[["day"]]), + strftime( + rollback(ymd(imputed_dtc) + months(1)), + format = "%Y-%m-%d" + ), + imputed_dtc + ) } - imputed_dtc <- if_else( - !is.na(imputed_date) & !is.na(imputed_time), - paste0(imputed_date, "T", imputed_time), - NA_character_ + # Handle min_dates and max_dates argument ---- + restrict_imputed_dtc_dt( + dtc, + imputed_dtc = imputed_dtc, + min_dates = min_dates, + max_dates = max_dates ) +} + +#' Create a `dt_level` object +#' +#' @param level Date level +#' +#' *Permitted Values*: `"Y"` (year, highest level), `"M"` (month), `"D"` +#' (day), `"n"` (none, lowest level) +#' +#' @returns A `dt_level` object +#' +#' @details A `dt_level` object is an ordered factor, i.e., two objects can be +#' compared. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' @keywords utils_impute +#' +dt_level <- function(level) { + out <- + factor( + level, + levels = c("n", "D", "M", "Y"), + ordered = TRUE + ) + class(out) <- c("dt_level", class(out)) + out +} - # adjust imputed date to minimum and maximum dates +#' Restrict Imputed DTC date to Minimum/Maximum Dates +#' +#' @param imputed_dtc The imputed DTC date +#' +#' @inheritParams impute_dtc_dt +#' +#' @returns +#' - The last of the minimum dates (`min_dates`) which are in the range of the +#' partial DTC date (`dtc`) +#' - The first of the maximum dates (`max_dates`) which are in the range of the +#' partial DTC date (`dtc`) +#' - `imputed_dtc` if the partial DTC date (`dtc`) is not in range of any of +#' the minimum or maximum dates. +#' +#' @author Stefan Bundfuss +#' +#' @family utils_impute +#' +#' @keywords utils_impute +#' +#' @seealso [impute_dtc_dtm()], [impute_dtc_dt()] +restrict_imputed_dtc_dt <- function(dtc, + imputed_dtc, + min_dates, + max_dates) { if (!is.null(min_dates) | !is.null(max_dates)) { # determine range of possible dates - min_dtc <- impute_dtc(dtc, date_imputation = "first", time_imputation = "first") - max_dtc <- impute_dtc(dtc, date_imputation = "last", time_imputation = "last") + min_dtc <- + impute_dtc_dt( + dtc, + highest_imputation = "Y", + date_imputation = "first" + ) + max_dtc <- + impute_dtc_dt( + dtc, + highest_imputation = "Y", + date_imputation = "last" + ) } if (!is.null(min_dates)) { # for each minimum date within the range ensure that the imputed date is not # before it for (min_date in min_dates) { assert_that(is_date(min_date)) - min_date_iso <- strftime(min_date, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") + min_date_iso <- strftime(min_date, format = "%Y-%m-%d", tz = "UTC") imputed_dtc <- if_else( min_dtc <= min_date_iso & min_date_iso <= max_dtc, pmax(imputed_dtc, min_date_iso), @@ -322,7 +916,7 @@ impute_dtc <- function(dtc, # after it for (max_date in max_dates) { assert_that(is_date(max_date)) - max_date_iso <- strftime(max_date, format = "%Y-%m-%dT%H:%M:%S", tz = "UTC") + max_date_iso <- strftime(max_date, format = "%Y-%m-%d", tz = "UTC") imputed_dtc <- if_else( min_dtc <= max_date_iso & max_date_iso <= max_dtc, pmin(imputed_dtc, max_date_iso), @@ -340,12 +934,7 @@ impute_dtc <- function(dtc, #' #' @param dtc The --DTC date to convert. #' -#' A character date is expected in a format like yyyy-mm-dd or yyyy-mm-ddThh:mm:ss. -#' A partial date will return a NA date and a warning will be issued: -#' 'All formats failed to parse. No formats found.'. -#' Note: you can use impute_dtc function to build a complete date. -#' -#' @inheritParams impute_dtc +#' @inheritParams impute_dtc_dt #' #' @author Samia Kabi #' @@ -353,7 +942,9 @@ impute_dtc <- function(dtc, #' #' @return a date object #' -#' @keywords computation timing +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' @@ -361,27 +952,23 @@ impute_dtc <- function(dtc, #' convert_dtc_to_dt("2019-07-18") #' convert_dtc_to_dt("2019-07") convert_dtc_to_dt <- function(dtc, - date_imputation = NULL, + highest_imputation = "n", + date_imputation = "first", min_dates = NULL, max_dates = NULL, preserve = FALSE) { assert_that(is.character(dtc)) warn_if_invalid_dtc(dtc, is_valid_dtc(dtc)) - imputed_dtc <- impute_dtc( + imputed_dtc <- impute_dtc_dt( dtc = dtc, + highest_imputation = highest_imputation, date_imputation = date_imputation, - time_imputation = "first", min_dates = min_dates, max_dates = max_dates, preserve = preserve ) - - if_else( - is.na(imputed_dtc), - ymd(NA), - ymd(substr(imputed_dtc, 1, 10)) - ) + ymd(imputed_dtc) } #' Convert a Date Character Vector into a Datetime Object @@ -390,19 +977,17 @@ convert_dtc_to_dt <- function(dtc, #' #' @param dtc The `'--DTC'` date to convert. #' -#' A character date is expected in a format like `yyyy-mm-ddThh:mm:ss`. -#' A partial datetime will issue a warning. -#' Note: you can use [impute_dtc()] function to build a complete datetime. -#' -#' @inheritParams impute_dtc -#' -#' @author Samia Kabi +#' @inheritParams impute_dtc_dtm #' #' @details Usually this computation function can not be used with `%>%`. #' #' @return A datetime object #' -#' @keywords computation timing +#' @author Samia Kabi, Stefan Bundfuss +#' +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' @@ -411,8 +996,9 @@ convert_dtc_to_dt <- function(dtc, #' convert_dtc_to_dtm("2019-07-18T00:00:00") # note Time = 00:00:00 is not printed #' convert_dtc_to_dtm("2019-07-18") convert_dtc_to_dtm <- function(dtc, - date_imputation = NULL, - time_imputation = "00:00:00", + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", min_dates = NULL, max_dates = NULL, preserve = FALSE) { @@ -420,7 +1006,8 @@ convert_dtc_to_dtm <- function(dtc, warn_if_invalid_dtc(dtc, is_valid_dtc(dtc)) dtc %>% - impute_dtc( + impute_dtc_dtm( + highest_imputation = highest_imputation, date_imputation = date_imputation, time_imputation = time_imputation, min_dates = min_dates, @@ -440,13 +1027,15 @@ convert_dtc_to_dtm <- function(dtc, #' #' @inheritParams convert_dtc_to_dtm #' -#' @author Samia Kabi -#' #' @details Usually this computation function can not be used with `%>%`. #' #' @return A datetime object #' -#' @keywords computation timing +#' @author Samia Kabi +#' +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' @@ -457,8 +1046,9 @@ convert_dtc_to_dtm <- function(dtc, #' convert_date_to_dtm("2019-07-18", time_imputation = "23:59:59") #' convert_date_to_dtm("2019-07-18") convert_date_to_dtm <- function(dt, - date_imputation = NULL, - time_imputation = "00:00:00", + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", min_dates = NULL, max_dates = NULL, preserve = FALSE) { @@ -472,6 +1062,7 @@ convert_date_to_dtm <- function(dt, # convert dtc to dtm dt %>% convert_dtc_to_dtm( + highest_imputation = highest_imputation, date_imputation = date_imputation, time_imputation = time_imputation, min_dates = min_dates, @@ -494,13 +1085,15 @@ convert_date_to_dtm <- function(dt, #' #' A date object is expected. #' -#' @author Samia Kabi -#' #' @details Usually this computation function can not be used with `%>%`. #' #' @return The date imputation flag (`'--DTF'`) (character value of `'D'`, `'M'` , `'Y'` or `NA`) #' -#' @keywords computation timing +#' @author Samia Kabi +#' +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' @@ -508,7 +1101,8 @@ convert_date_to_dtm <- function(dt, #' compute_dtf(dtc = "2019-07", dt = as.Date("2019-07-18")) #' compute_dtf(dtc = "2019", dt = as.Date("2019-07-18")) compute_dtf <- function(dtc, dt) { - assert_that(is.character(dtc), is_date(dt)) + assert_character_vector(dtc) + assert_that(is_date(dt)) is_na <- is.na(dt) n_chr <- nchar(dtc) @@ -546,13 +1140,15 @@ compute_dtf <- function(dtc, dt) { #' #' Default: `FALSE` #' -#' @author Samia Kabi -#' #' @details Usually this computation function can not be used with `%>%`. #' #' @return The time imputation flag (`'--TMF'`) (character value of `'H'`, `'M'` , `'S'` or `NA`) #' -#' @keywords computation timing +#' @author Samia Kabi, Stefan Bundfuss +#' +#' @family com_date_time +#' +#' @keywords com_date_time #' #' @export #' @@ -567,38 +1163,43 @@ compute_tmf <- function(dtc, assert_character_vector(dtc) assert_logical_scalar(ignore_seconds_flag) + partial <- get_partialdatetime(dtc) + highest_miss <- convert_blanks_to_na(vector("character", length(dtc))) + for (c in c("hour", "minute", "second")) { + highest_miss <- + if_else(is.na(partial[[c]]) & is.na(highest_miss), c, highest_miss) + } is_na <- is.na(dtm) - n_chr <- nchar(dtc) valid_dtc <- is_valid_dtc(dtc) warn_if_invalid_dtc(dtc, valid_dtc) + map <- c( + hour = "H", + minute = "M", + second = "S" + ) + flag <- if_else(is.na(dtm) | is.na(highest_miss), NA_character_, map[highest_miss]) if (ignore_seconds_flag) { - if ((any(n_chr >= 17))) { + if (any(!is.na(partial[["second"]]))) { abort("Seconds detected in data while ignore_seconds_flag is invoked") } else { - case_when( - (!is_na & n_chr >= 19 & valid_dtc) | is_na | !valid_dtc ~ NA_character_, - n_chr == 13 ~ "M", - n_chr == 10 | (n_chr > 0 & n_chr < 10) ~ "H" - ) + flag <- if_else(flag == "S", NA_character_, flag) } - } else { - case_when( - (!is_na & n_chr >= 19 & valid_dtc) | is_na | !valid_dtc ~ NA_character_, - n_chr == 16 ~ "S", - n_chr == 13 ~ "M", - n_chr == 10 | (n_chr > 0 & n_chr < 10) ~ "H" - ) } + flag } #' Derive/Impute a Date from a Date Character Vector #' #' Derive a date (`'--DT'`) from a date character vector (`'--DTC`'). -#' The date can be imputed (see `date_imputation` parameter) +#' The date can be imputed (see `date_imputation` argument) #' and the date imputation flag ('`--DTF'`) can be added. #' +#' In {admiral} we don't allow users to pick any single part of the date/time to +#' impute, we only enable to impute up to a highest level, i.e. you couldn't +#' choose to say impute months, but not days. +#' #' @param dataset Input dataset. #' #' The date character vector (`dtc`) must be present. @@ -612,14 +1213,14 @@ compute_tmf <- function(dtc, #' @param flag_imputation Whether the date imputation flag must also be derived. #' #' If `"auto"` is specified, the date imputation flag is derived if the -#' `date_imputation` parameter is not null. +#' `date_imputation` argument is not null. #' #' *Default*: `"auto"` #' #' *Permitted Values*: `"auto"`, `"date"` or `"none"` #' #' -#' @inheritParams impute_dtc +#' @inheritParams impute_dtc_dt #' #' @return #' The input dataset with the date `'--DT'` (and the date imputation flag `'--DTF'` @@ -631,14 +1232,17 @@ compute_tmf <- function(dtc, #' #' @author Samia Kabi #' -#' @keywords adam derivation timing +#' @family der_date_time +#' +#' @keywords der_gen der_date_time #' #' @export #' #' @examples +#' library(tibble) #' library(lubridate) #' -#' mhdt <- tibble::tribble( +#' mhdt <- tribble( #' ~MHSTDTC, #' "2019-07-18T15:25:40", #' "2019-07-18T15:25", @@ -650,7 +1254,7 @@ compute_tmf <- function(dtc, #' ) #' #' # Create ASTDT and ASTDTF -#' # no imputation for partial date +#' # No imputation for partial date #' derive_vars_dt( #' mhdt, #' new_vars_prefix = "AST", @@ -663,7 +1267,7 @@ compute_tmf <- function(dtc, #' mhdt, #' new_vars_prefix = "AST", #' dtc = MHSTDTC, -#' date_imputation = "FIRST" +#' highest_imputation = "M" #' ) #' #' # Impute partial dates to 6th of April @@ -671,6 +1275,7 @@ compute_tmf <- function(dtc, #' mhdt, #' new_vars_prefix = "AST", #' dtc = MHSTDTC, +#' highest_imputation = "M", #' date_imputation = "04-06" #' ) #' @@ -680,7 +1285,8 @@ compute_tmf <- function(dtc, #' mhdt, #' new_vars_prefix = "AEN", #' dtc = MHSTDTC, -#' date_imputation = "LAST" +#' highest_imputation = "M", +#' date_imputation = "last" #' ) #' #' # Create BIRTHDT @@ -689,13 +1295,14 @@ compute_tmf <- function(dtc, #' mhdt, #' new_vars_prefix = "BIRTH", #' dtc = MHSTDTC, -#' date_imputation = "MID", +#' highest_imputation = "M", +#' date_imputation = "mid", #' flag_imputation = "none" #' ) #' #' # Impute AE start date to the first date and ensure that the imputed date #' # is not before the treatment start date -#' adae <- tibble::tribble( +#' adae <- tribble( #' ~AESTDTC, ~TRTSDTM, #' "2020-12", ymd_hms("2020-12-06T12:12:12"), #' "2020-11", ymd_hms("2020-12-06T12:12:12") @@ -705,11 +1312,11 @@ compute_tmf <- function(dtc, #' adae, #' dtc = AESTDTC, #' new_vars_prefix = "AST", -#' date_imputation = "first", +#' highest_imputation = "M", #' min_dates = vars(TRTSDTM) #' ) #' -#' # A user imputing dates as middle month/day, i.e. date_imputation = "MID" can +#' # A user imputing dates as middle month/day, i.e. date_imputation = "mid" can #' # use preserve argument to "preserve" partial dates. For example, "2019---07", #' # will be displayed as "2019-06-07" rather than 2019-06-15 with preserve = TRUE #' @@ -717,19 +1324,21 @@ compute_tmf <- function(dtc, #' mhdt, #' new_vars_prefix = "AST", #' dtc = MHSTDTC, -#' date_imputation = "MID", +#' highest_imputation = "M", +#' date_imputation = "mid", #' preserve = TRUE #' ) derive_vars_dt <- function(dataset, new_vars_prefix, dtc, - date_imputation = NULL, + highest_imputation = "n", + date_imputation = "first", flag_imputation = "auto", min_dates = NULL, max_dates = NULL, preserve = FALSE) { - # check and quote parameters + # check and quote arguments assert_character_scalar(new_vars_prefix) assert_vars(max_dates, optional = TRUE) assert_vars(min_dates, optional = TRUE) @@ -750,6 +1359,7 @@ derive_vars_dt <- function(dataset, mutate( !!sym(dt) := convert_dtc_to_dt( dtc = !!dtc, + highest_imputation = highest_imputation, date_imputation = date_imputation, min_dates = lapply(min_dates, eval_tidy, data = rlang::as_data_mask(.)), max_dates = lapply(max_dates, eval_tidy, data = rlang::as_data_mask(.)), @@ -758,8 +1368,8 @@ derive_vars_dt <- function(dataset, ) # derive DTF - if (flag_imputation %in% c("both", "date") || - flag_imputation == "auto" && !is.null(date_imputation)) { + if (flag_imputation == "date" || + flag_imputation == "auto" && highest_imputation != "n") { # add --DTF if not there already dtf <- paste0(new_vars_prefix, "DTF") dtf_exist <- dtf %in% colnames(dataset) @@ -781,9 +1391,13 @@ derive_vars_dt <- function(dataset, #' Derive/Impute a Datetime from a Date Character Vector #' #' Derive a datetime object (`'--DTM'`) from a date character vector (`'--DTC'`). -#' The date and time can be imputed (see `date_imputation`/`time_imputation` parameters) +#' The date and time can be imputed (see `date_imputation`/`time_imputation` arguments) #' and the date/time imputation flag (`'--DTF'`, `'--TMF'`) can be added. #' +#' In {admiral} we don't allow users to pick any single part of the date/time to +#' impute, we only enable to impute up to a highest level, i.e. you couldn't +#' choose to say impute months, but not days. +#' #' @param dataset Input dataset #' #' The date character vector (`dtc`) must be present. @@ -799,15 +1413,15 @@ derive_vars_dt <- function(dataset, #' @param flag_imputation Whether the date/time imputation flag(s) must also be derived. #' #' If `"auto"` is specified, the date imputation flag is derived if the -#' `date_imputation` parameter is not null and the time imputation flag is -#' derived if the `time_imputation` parameter is not null +#' `date_imputation` argument is not null and the time imputation flag is +#' derived if the `time_imputation` argument is not null #' #' *Default*: `"auto"` #' #' *Permitted Values*: `"auto"`, `"date"`, `"time"`, `"both"`, or `"none"` #' #' -#' @inheritParams impute_dtc +#' @inheritParams impute_dtc_dtm #' @inheritParams compute_tmf #' #' @details @@ -820,14 +1434,17 @@ derive_vars_dt <- function(dataset, #' #' @author Samia Kabi #' -#' @keywords derivation adam timing +#' @family der_date_time +#' +#' @keywords der_gen der_date_time #' #' @export #' #' @examples +#' library(tibble) #' library(lubridate) #' -#' mhdt <- tibble::tribble( +#' mhdt <- tribble( #' ~MHSTDTC, #' "2019-07-18T15:25:40", #' "2019-07-18T15:25", @@ -842,13 +1459,12 @@ derive_vars_dt <- function(dataset, #' mhdt, #' new_vars_prefix = "AST", #' dtc = MHSTDTC, -#' date_imputation = "FIRST", -#' time_imputation = "FIRST" +#' highest_imputation = "M" #' ) #' #' # Impute AE end date to the last date and ensure that the imputed date is not #' # after the death or data cut off date -#' adae <- tibble::tribble( +#' adae <- tribble( #' ~AEENDTC, ~DTHDT, ~DCUTDT, #' "2020-12", ymd("2020-12-06"), ymd("2020-12-24"), #' "2020-11", ymd("2020-12-06"), ymd("2020-12-24") @@ -858,6 +1474,7 @@ derive_vars_dt <- function(dataset, #' adae, #' dtc = AEENDTC, #' new_vars_prefix = "AEN", +#' highest_imputation = "M", #' date_imputation = "last", #' time_imputation = "last", #' max_dates = vars(DTHDT, DCUTDT) @@ -865,7 +1482,7 @@ derive_vars_dt <- function(dataset, #' #' # Seconds has been removed from the input dataset. Function now uses #' # ignore_seconds_flag to remove the 'S' from the --TMF variable. -#' mhdt <- tibble::tribble( +#' mhdt <- tribble( #' ~MHSTDTC, #' "2019-07-18T15:25", #' "2019-07-18T15:25", @@ -880,8 +1497,7 @@ derive_vars_dt <- function(dataset, #' mhdt, #' new_vars_prefix = "AST", #' dtc = MHSTDTC, -#' date_imputation = "FIRST", -#' time_imputation = "FIRST", +#' highest_imputation = "M", #' ignore_seconds_flag = TRUE #' ) #' @@ -893,21 +1509,23 @@ derive_vars_dt <- function(dataset, #' mhdt, #' new_vars_prefix = "AST", #' dtc = MHSTDTC, -#' date_imputation = "MID", +#' highest_imputation = "M", +#' date_imputation = "mid", #' preserve = TRUE #' ) derive_vars_dtm <- function(dataset, new_vars_prefix, dtc, - date_imputation = NULL, - time_imputation = "00:00:00", + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", flag_imputation = "auto", min_dates = NULL, max_dates = NULL, preserve = FALSE, ignore_seconds_flag = FALSE) { - # check and quote parameters + # check and quote arguments assert_character_scalar(new_vars_prefix) assert_vars(max_dates, optional = TRUE) assert_vars(min_dates, optional = TRUE) @@ -926,6 +1544,7 @@ derive_vars_dtm <- function(dataset, mask <- rlang::as_data_mask(dataset) dataset[[dtm]] <- convert_dtc_to_dtm( dtc = eval_tidy(dtc, mask), + highest_imputation = highest_imputation, date_imputation = date_imputation, time_imputation = time_imputation, min_dates = lapply(min_dates, eval_tidy, data = mask), @@ -934,7 +1553,7 @@ derive_vars_dtm <- function(dataset, ) if (flag_imputation %in% c("both", "date") || - flag_imputation == "auto" && !is.null(date_imputation)) { + flag_imputation == "auto" && dtm_level(highest_imputation) > dtm_level("h")) { # add --DTF if not there already dtf <- paste0(new_vars_prefix, "DTF") dtf_exist <- dtf %in% colnames(dataset) @@ -951,7 +1570,7 @@ derive_vars_dtm <- function(dataset, } if (flag_imputation %in% c("both", "time") || - flag_imputation == "auto" && !is.null(time_imputation)) { + flag_imputation == "auto" && highest_imputation != "n") { # add --TMF variable tmf <- paste0(new_vars_prefix, "TMF") warn_if_vars_exist(dataset, tmf) @@ -964,6 +1583,5 @@ derive_vars_dtm <- function(dataset, )) } - dataset } diff --git a/R/derive_extreme_records.R b/R/derive_extreme_records.R index 07d3c1164a..138a865395 100644 --- a/R/derive_extreme_records.R +++ b/R/derive_extreme_records.R @@ -42,7 +42,8 @@ #' @return The input dataset with the first or last observation of each by group #' added as new observations. #' -#' @keywords derivation bds +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings #' #' @export #' diff --git a/R/derive_last_dose.R b/R/derive_last_dose.R deleted file mode 100644 index 744c68089c..0000000000 --- a/R/derive_last_dose.R +++ /dev/null @@ -1,133 +0,0 @@ -#' Derive Last Dose Date(-time) -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' *Deprecated*, please use `derive_var_last_dose_date()` instead. -#' -#' @param dataset Input dataset. -#' @param dataset_ex Input EX dataset. -#' @param filter_ex Filtering condition applied to EX dataset. -#' For example, it can be used to filter for valid dose. -#' Defaults to NULL. -#' @param by_vars Variables to join by (created by `dplyr::vars`). -#' @param dose_start The dose start date variable. -#' @param dose_end The dose end date variable. -#' @param analysis_date The analysis date variable. -#' @param dataset_seq_var The sequence variable -#' (this together with `by_vars` creates the keys of `dataset`). -#' @param new_var The output variable. -#' @param output_datetime Logical. Should only date or date-time variable be returned? -#' Defaults to `TRUE` (i.e. date-time variable). -#' @param check_dates_only Logical. -#' An assumption that start and end dates of treatment match is checked. -#' By default (`FALSE`), the date as well as the time component is checked. -#' If set to `TRUE`, then only the date component of those variables is checked. -#' @param traceability_vars A named list returned by [`vars()`] listing the traceability variables, -#' e.g. `vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ)`. -#' The left-hand side (names of the list elements) gives the names of the traceability variables -#' in the returned dataset. -#' The right-hand side (values of the list elements) gives the values of the traceability variables -#' in the returned dataset. -#' These can be either strings or symbols referring to existing variables. -#' -#' @details All date (date-time) variables can be characters in standard ISO format or -#' of date / date-time class. -#' For ISO format, see [`impute_dtc`] - parameter `dtc` for further details. -#' Date-time imputations are done as follows: -#' * `dose_end`: no date imputation, time imputation to `00:00:00` if time is missing. -#' * `analysis_date`: no date imputation, time imputation to `23:59:59` if time is missing. -#' -#' The last dose date is derived as follows: -#' 1. The `dataset_ex` is filtered using `filter_ex`, if provided. -#' This is useful for, for example, filtering for valid dose only. -#' 2. The datasets `dataset` and `dataset_ex` are joined using `by_vars`. -#' 3. The last dose date is derived: -#' the last dose date is the maximum date where `dose_end` is lower to or equal to -#' `analysis_date`, subject to both date values are non-NA. -#' The last dose date is derived per `by_vars` and `dataset_seq_var`. -#' 4. The last dose date is appended to the `dataset` and returned to the user. -#' -#' Furthermore, the following assumption is checked: start and end dates (datetimes) need to match. -#' Use `check_dates_only` to control whether only dates or whole date-times need to be equal. -#' -#' @return Input dataset with additional column `new_var`. -#' -#' @author Ondrej Slama -#' -#' @keywords adae derivation -#' -#' @export -#' -#' @examples -#' library(dplyr, warn.conflicts = FALSE) -#' library(admiral.test) -#' data(admiral_ae) -#' data(ex_single) -#' -#' admiral_ae %>% -#' head(100) %>% -#' derive_last_dose( -#' head(ex_single, 100), -#' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, -#' dose_start = EXSTDTC, -#' dose_end = EXENDTC, -#' analysis_date = AESTDTC, -#' dataset_seq_var = AESEQ, -#' new_var = LDOSEDTM, -#' output_datetime = TRUE, -#' check_dates_only = FALSE -#' ) %>% -#' select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDTM) -#' -#' # or with traceability variables -#' admiral_ae %>% -#' head(100) %>% -#' derive_last_dose( -#' head(ex_single, 100), -#' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, -#' dose_start = EXSTDTC, -#' dose_end = EXENDTC, -#' analysis_date = AESTDTC, -#' dataset_seq_var = AESEQ, -#' new_var = LDOSEDTM, -#' output_datetime = TRUE, -#' check_dates_only = FALSE, -#' traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXSTDTC") -#' ) %>% -#' select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDTM, LDOSEDOM, LDOSESEQ, LDOSEVAR) -derive_last_dose <- function(dataset, - dataset_ex, - filter_ex = NULL, - by_vars = vars(STUDYID, USUBJID), - dose_start, - dose_end, - analysis_date, - dataset_seq_var, - new_var, - output_datetime = TRUE, - check_dates_only = FALSE, - traceability_vars = NULL) { - deprecate_warn("0.6.0", "derive_last_dose()", "derive_var_last_dose_date()") - assert_logical_scalar(check_dates_only) - if (check_dates_only) { - cond <- expr(substr(!!quo_get_expr(enquo(dose_start)), 1, 10) - == substr(!!quo_get_expr(enquo(dose_end)), 1, 10)) - } else { - cond <- expr(!!quo_get_expr(enquo(dose_start)) == !!quo_get_expr(enquo(dose_end))) - } - derive_var_last_dose_date( - dataset = dataset, - dataset_ex = dataset_ex, - filter_ex = !!enquo(filter_ex), - by_vars = by_vars, - dose_date = !!enquo(dose_start), - analysis_date = !!enquo(analysis_date), - new_var = !!enquo(new_var), - output_datetime = output_datetime, - single_dose_condition = !!cond, - traceability_vars = traceability_vars - ) -} diff --git a/R/derive_merged.R b/R/derive_merged.R index 87fff91246..655485f595 100644 --- a/R/derive_merged.R +++ b/R/derive_merged.R @@ -1,3 +1,4 @@ +.temp <- new.env(parent = emptyenv()) #' Add New Variable(s) to the Input Dataset Based on Variables from Another #' Dataset #' @@ -128,7 +129,8 @@ #' #' @author Stefan Bundfuss #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -138,7 +140,7 @@ #' data("admiral_vs") #' data("admiral_dm") #' -#' # merging all dm variables to vs +#' # Merging all dm variables to vs #' derive_vars_merged( #' admiral_vs, #' dataset_add = select(admiral_dm, -DOMAIN), @@ -146,7 +148,7 @@ #' ) %>% #' select(STUDYID, USUBJID, VSTESTCD, VISIT, VSTPT, VSSTRESN, AGE, AGEU) #' -#' # merge last weight to adsl +#' # Merge last weight to adsl #' data("admiral_adsl") #' derive_vars_merged( #' admiral_adsl, @@ -159,6 +161,69 @@ #' match_flag = vsdatafl #' ) %>% #' select(STUDYID, USUBJID, AGE, AGEU, LASTWGT, LASTWGTU, vsdatafl) +#' +#' # Derive treatment start datetime (TRTSDTM) +#' data(admiral_ex) +#' +#' ## Impute exposure start date to first date/time +#' ex_ext <- derive_vars_dtm( +#' admiral_ex, +#' dtc = EXSTDTC, +#' new_vars_prefix = "EXST", +#' highest_imputation = "M", +#' ) +#' +#' ## Add first exposure datetime and imputation flags to adsl +#' derive_vars_merged( +#' select(admiral_dm, STUDYID, USUBJID), +#' dataset_add = ex_ext, +#' by_vars = vars(STUDYID, USUBJID), +#' new_vars = vars(TRTSDTM = EXSTDTM, TRTSDTF = EXSTDTF, TRTSTMF = EXSTTMF), +#' order = vars(EXSTDTM), +#' mode = "first" +#' ) +#' +#' # Derive treatment start datetime (TRTSDTM) +#' data(admiral_ex) +#' +#' ## Impute exposure start date to first date/time +#' ex_ext <- derive_vars_dtm( +#' admiral_ex, +#' dtc = EXSTDTC, +#' new_vars_prefix = "EXST", +#' highest_imputation = "M", +#' ) +#' +#' ## Add first exposure datetime and imputation flags to adsl +#' derive_vars_merged( +#' select(admiral_dm, STUDYID, USUBJID), +#' dataset_add = ex_ext, +#' filter_add = !is.na(EXSTDTM), +#' by_vars = vars(STUDYID, USUBJID), +#' new_vars = vars(TRTSDTM = EXSTDTM, TRTSDTF = EXSTDTF, TRTSTMF = EXSTTMF), +#' order = vars(EXSTDTM), +#' mode = "first" +#' ) +#' +#' # Derive treatment end datetime (TRTEDTM) +#' ## Impute exposure end datetime to last time, no date imputation +#' ex_ext <- derive_vars_dtm( +#' admiral_ex, +#' dtc = EXENDTC, +#' new_vars_prefix = "EXEN", +#' time_imputation = "last", +#' ) +#' +#' ## Add last exposure datetime and imputation flag to adsl +#' derive_vars_merged( +#' select(admiral_dm, STUDYID, USUBJID), +#' dataset_add = ex_ext, +#' filter_add = !is.na(EXENDTM), +#' by_vars = vars(STUDYID, USUBJID), +#' new_vars = vars(TRTEDTM = EXENDTM, TRTETMF = EXENTMF), +#' order = vars(EXENDTM), +#' mode = "last" +#' ) derive_vars_merged <- function(dataset, dataset_add, by_vars, @@ -234,6 +299,12 @@ derive_vars_merged <- function(dataset, #' Merge a (Imputed) Date Variable #' +#' @description +#' `r lifecycle::badge("deprecated")` +#' +#' This function is *deprecated*, please use `derive_vars_dt()` and +#' `derive_vars_merged()` instead. +#' #' Merge a imputed date variable and date imputation flag from a dataset to the #' input dataset. The observations to merge can be selected by a condition #' and/or selecting the first or last observation for each by group. @@ -283,38 +354,10 @@ derive_vars_merged <- function(dataset, #' #' @author Stefan Bundfuss #' -#' @keywords derivation adam timing +#' @keywords deprecated #' #' @export #' -#' @examples -#' library(admiral.test) -#' library(dplyr, warn.conflicts = FALSE) -#' data("admiral_dm") -#' data("admiral_ex") -#' -#' # derive treatment start date (TRTSDT) -#' derive_vars_merged_dt( -#' select(admiral_dm, STUDYID, USUBJID), -#' dataset_add = admiral_ex, -#' by_vars = vars(STUDYID, USUBJID), -#' new_vars_prefix = "TRTS", -#' dtc = EXSTDTC, -#' date_imputation = "first", -#' order = vars(TRTSDT), -#' mode = "first" -#' ) -#' -#' # derive treatment end date (TRTEDT) (without imputation) -#' derive_vars_merged_dt( -#' select(admiral_dm, STUDYID, USUBJID), -#' dataset_add = admiral_ex, -#' by_vars = vars(STUDYID, USUBJID), -#' new_vars_prefix = "TRTE", -#' dtc = EXENDTC, -#' order = vars(TRTEDT), -#' mode = "last" -#' ) derive_vars_merged_dt <- function(dataset, dataset_add, by_vars, @@ -335,11 +378,24 @@ derive_vars_merged_dt <- function(dataset, filter_add <- assert_filter_cond(enquo(filter_add), optional = TRUE) assert_data_frame(dataset_add, required_vars = quo_c(by_vars, dtc)) + deprecate_warn( + "0.8.0", + "derive_vars_merged_dt()", + details = "Please use `derive_vars_dt()` and `derive_vars_merged()` instead." + ) + old_vars <- names(dataset_add) + if (is.null(date_imputation)) { + highest_imputation <- "n" + date_imputation <- "first" + } else { + highest_imputation <- "M" + } add_data <- filter_if(dataset_add, filter_add) %>% derive_vars_dt( new_vars_prefix = new_vars_prefix, dtc = !!dtc, + highest_imputation = highest_imputation, date_imputation = date_imputation, flag_imputation = flag_imputation, min_dates = min_dates, @@ -361,6 +417,12 @@ derive_vars_merged_dt <- function(dataset, #' Merge a (Imputed) Datetime Variable #' +#' @description +#' `r lifecycle::badge("deprecated")` +#' +#' This function is *deprecated*, please use `derive_vars_dtm()` and +#' `derive_vars_merged()` instead. +#' #' Merge a imputed datetime variable, date imputation flag, and time imputation #' flag from a dataset to the input dataset. The observations to merge can be #' selected by a condition and/or selecting the first or last observation for @@ -406,40 +468,10 @@ derive_vars_merged_dt <- function(dataset, #' #' @author Stefan Bundfuss #' -#' @keywords derivation adam timing +#' @keywords deprecated #' #' @export #' -#' @examples -#' library(admiral.test) -#' library(dplyr, warn.conflicts = FALSE) -#' data("admiral_dm") -#' data("admiral_ex") -#' -#' # derive treatment start datetime (TRTSDTM) -#' derive_vars_merged_dtm( -#' select(admiral_dm, STUDYID, USUBJID), -#' dataset_add = admiral_ex, -#' by_vars = vars(STUDYID, USUBJID), -#' new_vars_prefix = "TRTS", -#' dtc = EXSTDTC, -#' date_imputation = "first", -#' time_imputation = "first", -#' order = vars(TRTSDTM), -#' mode = "first" -#' ) -#' -#' # derive treatment end datetime (TRTEDTM) (without date imputation) -#' derive_vars_merged_dtm( -#' select(admiral_dm, STUDYID, USUBJID), -#' dataset_add = admiral_ex, -#' by_vars = vars(STUDYID, USUBJID), -#' new_vars_prefix = "TRTE", -#' dtc = EXENDTC, -#' time_imputation = "last", -#' order = vars(TRTEDTM), -#' mode = "last" -#' ) derive_vars_merged_dtm <- function(dataset, dataset_add, by_vars, @@ -461,11 +493,28 @@ derive_vars_merged_dtm <- function(dataset, filter_add <- assert_filter_cond(enquo(filter_add), optional = TRUE) assert_data_frame(dataset_add, required_vars = quo_c(by_vars, dtc)) + deprecate_warn( + "0.8.0", + "derive_vars_merged_dtm()", + details = "Please use `derive_vars_dtm()` and `derive_vars_merged()` instead." + ) + old_vars <- names(dataset_add) + if (is.null(date_imputation)) { + highest_imputation <- "h" + date_imputation <- "first" + } else { + highest_imputation <- "M" + } + if (is.null(time_imputation)) { + highest_imputation <- "n" + time_imputation <- "first" + } add_data <- filter_if(dataset_add, filter = filter_add) %>% derive_vars_dtm( new_vars_prefix = new_vars_prefix, dtc = !!dtc, + highest_imputation = highest_imputation, date_imputation = date_imputation, time_imputation = time_imputation, flag_imputation = flag_imputation, @@ -536,7 +585,8 @@ derive_vars_merged_dtm <- function(dataset, #' #' @author Stefan Bundfuss #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -684,7 +734,8 @@ derive_var_merged_cat <- function(dataset, #' #' @author Stefan Bundfuss #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -799,7 +850,8 @@ derive_var_merged_exist_flag <- function(dataset, #' #' @author Stefan Bundfuss #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -865,3 +917,123 @@ derive_var_merged_character <- function(dataset, mutate(!!new_var := if_else(temp_match_flag, !!new_var, missing_value, missing_value)) %>% select(-temp_match_flag) } + + +#' Merge Lookup Table with Source Dataset +#' +#' Merge user-defined lookup table with the input dataset. Optionally print a +#' list of records from the input dataset that do not have corresponding +#' mapping from the lookup table. +#' +#' @param dataset_add Lookup table +#' +#' The variables specified by the `by_vars` parameter are expected. +#' +#' @param print_not_mapped Print a list of unique `by_vars` values that do not +#' have corresponding records from the lookup table? +#' +#' *Default*: `TRUE` +#' +#' *Permitted Values*: `TRUE`, `FALSE` +#' +#' @inheritParams derive_vars_merged +#' +#' +#' @return The output dataset contains all observations and variables of the +#' input dataset, and add the variables specified in `new_vars` from the lookup +#' table specified in `dataset_add`. Optionally prints a list of unique +#' `by_vars` values that do not have corresponding records +#' from the lookup table (by specifying `print_not_mapped = TRUE`). +#' +#' @author Annie Yang +#' +#' @keywords der_gen +#' @family der_gen +#' +#' @export +#' +#' @examples +#' library(admiral.test) +#' library(dplyr, warn.conflicts = FALSE) +#' data("admiral_vs") +#' param_lookup <- tibble::tribble( +#' ~VSTESTCD, ~VSTEST, ~PARAMCD, ~PARAM, +#' "SYSBP", "Systolic Blood Pressure", "SYSBP", "Systolic Blood Pressure (mmHg)", +#' "WEIGHT", "Weight", "WEIGHT", "Weight (kg)", +#' "HEIGHT", "Height", "HEIGHT", "Height (cm)", +#' "TEMP", "Temperature", "TEMP", "Temperature (C)", +#' "MAP", "Mean Arterial Pressure", "MAP", "Mean Arterial Pressure (mmHg)", +#' "BMI", "Body Mass Index", "BMI", "Body Mass Index(kg/m^2)", +#' "BSA", "Body Surface Area", "BSA", "Body Surface Area(m^2)" +#' ) +#' derive_vars_merged_lookup( +#' dataset = admiral_vs, +#' dataset_add = param_lookup, +#' by_vars = vars(VSTESTCD), +#' new_vars = vars(PARAMCD), +#' print_not_mapped = TRUE +#' ) +derive_vars_merged_lookup <- function(dataset, + dataset_add, + by_vars, + order = NULL, + new_vars = NULL, + mode = NULL, + filter_add = NULL, + check_type = "warning", + duplicate_msg = NULL, + print_not_mapped = TRUE) { + assert_logical_scalar(print_not_mapped) + filter_add <- assert_filter_cond(enquo(filter_add), optional = TRUE) + + res <- derive_vars_merged( + dataset, + dataset_add, + by_vars = by_vars, + order = order, + new_vars = new_vars, + mode = mode, + filter_add = !!filter_add, + match_flag = temp_match_flag, + check_type = check_type, + duplicate_msg = duplicate_msg + ) + + if (print_not_mapped) { + temp_not_mapped <- res %>% + filter(is.na(temp_match_flag)) %>% + distinct(!!!by_vars) + + if (nrow(temp_not_mapped) > 0) { + .temp$nmap <- structure( + temp_not_mapped, + class = union("nmap", class(temp_not_mapped)), + by_vars = vars2chr(by_vars) + ) + + message( + "List of ", enumerate(vars2chr(by_vars)), " not mapped: ", "\n", + paste0(capture.output(temp_not_mapped), collapse = "\n"), + "\nRun `get_not_mapped()` to access the full list" + ) + } else if (nrow(temp_not_mapped) == 0) { + message( + "All ", enumerate(vars2chr(by_vars)), " are mapped." + ) + } + } + + res %>% select(-temp_match_flag) +} + +#' Get list of records not mapped from the lookup table. +#' +#' @export +#' +#' @return A `data.frame` or `NULL` +#' +#' @keywords utils_help +#' @family utils_help +get_not_mapped <- function() { + .temp$nmap +} diff --git a/R/derive_derived_param.R b/R/derive_param_computed.R similarity index 87% rename from R/derive_derived_param.R rename to R/derive_param_computed.R index a620bcda20..37c6ccfc87 100644 --- a/R/derive_derived_param.R +++ b/R/derive_param_computed.R @@ -88,7 +88,9 @@ #' @return The input dataset with the new parameter added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation bds +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -106,7 +108,7 @@ #' "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 132, "mmHg", "WEEK 2" #' ) #' -#' derive_derived_param( +#' derive_param_computed( #' advs, #' by_vars = vars(USUBJID, VISIT), #' parameters = c("SYSBP", "DIABP"), @@ -131,7 +133,7 @@ #' "01-701-1028", "WEIGHT", "Weight (kg)", 80.7, "kg", "WEEK 2" #' ) #' -#' derive_derived_param( +#' derive_param_computed( #' advs, #' by_vars = vars(USUBJID, VISIT), #' parameters = "WEIGHT", @@ -144,14 +146,14 @@ #' constant_parameters = c("HEIGHT"), #' constant_by_vars = vars(USUBJID) #' ) -derive_derived_param <- function(dataset, - by_vars, - parameters, - analysis_value, - set_values_to, - filter = NULL, - constant_by_vars = NULL, - constant_parameters = NULL) { +derive_param_computed <- function(dataset, + by_vars, + parameters, + analysis_value, + set_values_to, + filter = NULL, + constant_by_vars = NULL, + constant_parameters = NULL) { assert_vars(by_vars) assert_vars(constant_by_vars, optional = TRUE) assert_data_frame(dataset, required_vars = vars(!!!by_vars, PARAMCD, AVAL)) @@ -241,3 +243,43 @@ derive_derived_param <- function(dataset, bind_rows(dataset, hori_data) } + +#' Adds a Parameter Computed from the Analysis Value of Other Parameters +#' +#' @description +#' `r lifecycle::badge("deprecated")` +#' +#' This function is deprecated. Please use `derive_param-computed()` instead. +#' +#' @inheritParams derive_param_computed +#' +#' @author Stefan Bundfuss +#' +#' @return The input dataset with the new parameter added. Note, a variable will only +#' be populated in the new parameter rows if it is specified in `by_vars`. +#' +#' @keywords deprecated +#' @family deprecated +#' +#' @export +#' +derive_derived_param <- function(dataset, + by_vars, + parameters, + analysis_value, + set_values_to, + filter = NULL, + constant_by_vars = NULL, + constant_parameters = NULL) { + deprecate_warn("0.8.0", "derive_derived_param()", "derive_param_computed()") + derive_param_computed( + dataset, + by_vars = by_vars, + parameters = parameters, + analysis_value = !!enquo(analysis_value), + set_values_to = set_values_to, + filter = !!enquo(filter), + constant_by_vars = constant_by_vars, + constant_parameters = constant_parameters + ) +} diff --git a/R/derive_param_doseint.R b/R/derive_param_doseint.R index 49f721ba9c..b087f24959 100644 --- a/R/derive_param_doseint.R +++ b/R/derive_param_doseint.R @@ -61,14 +61,16 @@ #' 2. If the planned dose (`tpadm_code`) is 0 and the administered dose #' (`tadm_code`) is > 0, 100 is returned. #' -#' @inheritParams derive_derived_param +#' @inheritParams derive_param_computed #' #' @author Alice Ehmann #' #' @return The input dataset with the new parameter rows added. Note, a variable will only #' be populated in the new parameter rows if it is specified in `by_vars`. #' -#' @keywords derivation adex +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -123,7 +125,7 @@ derive_param_doseint <- function(dataset, assert_param_does_not_exist(dataset, quo_get_expr(set_values_to$PARAMCD)) # Create Dose intensity records - dataset <- derive_derived_param( + dataset <- derive_param_computed( dataset, filter = !!filter, parameters = c(tadm_code, tpadm_code), diff --git a/R/derive_param_exist_flag.R b/R/derive_param_exist_flag.R index df527b445c..6f52ebe531 100644 --- a/R/derive_param_exist_flag.R +++ b/R/derive_param_exist_flag.R @@ -124,7 +124,9 @@ #' occurred (`AVALC`, `AVAL`, and the variables specified by `subject_keys` #' and `set_value_to` are populated for the new parameter) #' -#' @keywords derivation bds +#' @family der_prm_bds_findings +#' +#' @keywords der_prm_bds_findings #' #' @export #' @@ -224,29 +226,3 @@ derive_param_exist_flag <- function(dataset = NULL, # Create output dataset bind_rows(dataset, new_obs) } - -#' Map `"Y"` and `"N"` to Numeric Values -#' -#' Map `"Y"` and `"N"` to numeric values. -#' -#' @param arg Character vector -#' -#' @author Stefan Bundfuss -#' -#' @keywords user_utility -#' -#' @export -#' -#' @return `1` if `arg` equals `"Y"`, `0` if `arg` equals `"N"`, `NA_real_` otherwise -#' -#' @examples -#' -#' yn_to_numeric(c("Y", "N", NA_character_)) -yn_to_numeric <- function(arg) { - assert_character_vector(arg) - case_when( - arg == "Y" ~ 1, - arg == "N" ~ 0, - TRUE ~ NA_real_ - ) -} diff --git a/R/derive_param_exposure.R b/R/derive_param_exposure.R index baf683026a..2a9402b766 100644 --- a/R/derive_param_exposure.R +++ b/R/derive_param_exposure.R @@ -70,7 +70,8 @@ #' + only `AxxDTM` then `ASTDTM`,`AENDTM` are computed #' + only `AxxDT` then `ASTDT`,`AENDT` are computed. #' -#' @keywords derivation bds adex +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings #' #' @export #' @@ -161,31 +162,32 @@ derive_param_exposure <- function(dataset, filter_if(filter) add_data <- subset_ds %>% - filter(PARAMCD == input_code) %>% - derive_summary_records( + get_summary_records( by_vars = by_vars, + filter = PARAMCD == input_code, analysis_var = !!analysis_var, summary_fun = summary_fun, set_values_to = set_values_to - ) %>% - filter(PARAMCD == quo_get_expr(set_values_to$PARAMCD)) + ) # add the dates for the derived parameters + tmp_start <- get_new_tmp_var(dataset) + tmp_end <- get_new_tmp_var(dataset) if (all(dtm)) { dates <- subset_ds %>% group_by(!!!by_vars) %>% summarise( - temp_start = min(ASTDTM, na.rm = TRUE), - temp_end = max(coalesce(AENDTM, ASTDTM), na.rm = TRUE) + !!tmp_start := min(ASTDTM, na.rm = TRUE), + !!tmp_end := max(coalesce(AENDTM, ASTDTM), na.rm = TRUE) ) %>% ungroup() expo_data <- add_data %>% derive_vars_merged(dataset_add = dates, by_vars = by_vars) %>% mutate( - ASTDTM = coalesce(ASTDTM, temp_start), - AENDTM = coalesce(AENDTM, temp_end) + ASTDTM = !!tmp_start, + AENDTM = !!tmp_end ) %>% - select(-starts_with("temp_")) + remove_tmp_vars() if (all(dt)) { expo_data <- expo_data %>% @@ -195,25 +197,18 @@ derive_param_exposure <- function(dataset, dates <- subset_ds %>% group_by(!!!by_vars) %>% summarise( - temp_start = min(ASTDT, na.rm = TRUE), - temp_end = max(coalesce(AENDT, ASTDT), na.rm = TRUE) + !!tmp_start := min(ASTDT, na.rm = TRUE), + !!tmp_end := max(coalesce(AENDT, ASTDT), na.rm = TRUE) ) %>% ungroup() expo_data <- add_data %>% derive_vars_merged(dataset_add = dates, by_vars = by_vars) %>% mutate( - ASTDT = coalesce(ASTDT, temp_start), - AENDT = coalesce(AENDT, temp_end) + ASTDT = !!tmp_start, + AENDT = !!tmp_end ) %>% - select(-starts_with("temp_")) + remove_tmp_vars() } - all_data <- bind_rows(dataset, expo_data) - - if (all(dtm)) { - attr(all_data$ASTDTM, "tzone") <- "UTC" - attr(all_data$AENDTM, "tzone") <- "UTC" - } - - all_data + bind_rows(dataset, expo_data) } diff --git a/R/derive_param_first_event.R b/R/derive_param_first_event.R index dfa282db80..3633bcd043 100644 --- a/R/derive_param_first_event.R +++ b/R/derive_param_first_event.R @@ -85,7 +85,8 @@ #' @return The input dataset with a new parameter indicating if and when an #' event occurred #' -#' @keywords derivation bds +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings #' #' @export #' diff --git a/R/derive_param_framingham.R b/R/derive_param_framingham.R new file mode 100644 index 0000000000..326f5faa4d --- /dev/null +++ b/R/derive_param_framingham.R @@ -0,0 +1,277 @@ +#' Adds a Parameter for Framingham Heart Study Cardiovascular Disease +#' 10-Year Risk Score +#' +#' Adds a record for framingham score (FCVD101) for each by group +#' (e.g., subject and visit) where the source parameters are available. +#' +#' @param dataset Input dataset +#' +#' The variables specified by the `by_vars` parameter, `PARAMCD`, and +#' `AVAL` are expected. +#' +#' The variable specified by `by_vars` and `PARAMCD` must be a unique key of +#' the input dataset after restricting it by the filter condition (`filter` +#' parameter) and to the parameters specified by `sysbp_code`, `chol_code` +#' and `hdl_code`. +#' +#' @param sysbp_code Systolic blood pressure parameter code +#' +#' The observations where `PARAMCD` equals the specified value are considered +#' as the systolic blood pressure assessments. +#' +#' \emph{Permitted Values:} character value +#' +#' @param chol_code Total serum cholesterol code +#' +#' The observations where `PARAMCD` equals the specified value are considered +#' as the total cholesterol assessments. This must be measured in mg/dL. +#' +#' \emph{Permitted Values:} character value +#' +#' @param cholhdl_code HDL serum cholesterol code +#' +#' The observations where `PARAMCD` equals the specified value are considered +#' as the HDL cholesterol assessments. This must be measured in mg/dL. +#' +#' \emph{Permitted Values:} character value +#' +#' @param age Subject age +#' +#' A variable containing the subject's age. +#' +#' \emph{Permitted Values:} A numeric variable name that refers to a subject age +#' column of the input dataset +#' +#' @param sex Subject sex +#' +#' A variable containing the subject's sex. +#' +#' \emph{Permitted Values:} A character variable name that refers to a subject sex +#' column of the input dataset +#' +#' @param smokefl Smoking status flag +#' +#' A flag indicating smoking status. +#' +#' \emph{Permitted Values:} A character variable name that refers to a smoking status +#' column of the input dataset. +#' +#' @param diabetfl Diabetic flag +#' +#' A flag indicating diabetic status. +#' +#' \emph{Permitted Values:} A character variable name that refers to a diabetic +#' status column of the input dataset +#' +#' @param trthypfl Treated with hypertension medication flag +#' +#' A flag indicating if a subject was treated with hypertension medication. +#' +#' \emph{Permitted Values:} A character variable name that refers to a column that +#' indicates whether a subject is treated for high blood +#' pressure +#' +#' @inheritParams derive_derived_param +#' +#' @inheritParams derive_param_qtc +#' +#' @details +#' The values of `age`, `sex`, `smokefl`, `diabetfl` and `trthypfl` will be +#' added to the `by_vars` list. +#' The predicted probability of having cardiovascular disease (CVD) +#' within 10-years according to Framingham formula +#' \href{https://www.ahajournals.org/doi/pdf/10.1161/CIRCULATIONAHA.107.699579}{D'Agostino, 2008} is: # nolint +#' \strong{For Women:} +#' +#' \tabular{rr}{ +#' \strong{Factor} \tab \strong{Amount} \cr +#' Age \tab 2.32888 \cr +#' Total Chol \tab 1.20904 \cr +#' HDL Chol \tab -0.70833 \cr +#' Sys BP \tab 2.76157 \cr +#' Sys BP + Hypertension Meds \tab 2.82263 \cr +#' Smoker \tab 0.52873 \cr +#' Non-Smoker \tab 0 \cr +#' Diabetic \tab 0.69154 \cr +#' Not Diabetic \tab 0 \cr +#' Average Risk \tab 26.1931 \cr +#' Risk Period \tab 0.95012 \cr +#' } +#' +#' \strong{For Men:} +#' +#' \tabular{rr}{ +#' \strong{Factor} \tab \strong{Amount} \cr +#' Age \tab 3.06117 \cr +#' Total Chol \tab 1.12370 \cr +#' HDL Chol \tab -0.93263 \cr +#' Sys BP \tab 1.93303 \cr +#' Sys BP + Hypertension Meds \tab 2.99881 \cr +#' Smoker \tab .65451 \cr +#' Non-Smoker \tab 0 \cr +#' Diabetic \tab 0.57367 \cr +#' Not Diabetic \tab 0 \cr +#' Average Risk \tab 23.9802 \cr +#' Risk Period \tab 0.88936 \cr +#' } +#' +#' \strong{The equation for calculating risk:} +#' +#' \deqn{RiskFactors = (log(Age) * AgeFactor) +#' + (log(TotalChol) * TotalCholFactor) +#' + (log(CholHDL) * CholHDLFactor) \\ +#' + (log(SysBP) * SysBPFactor) + Smoker +#' + Diabetes Present - AvgRisk} +#' +#' \deqn{Risk = 100 * (1 - RiskPeriodFactor^{RiskFactors})} +#' +#' @author +#' Alice Ehmann +#' Jack McGavigan +#' Ben Straub +#' +#' @return The input dataset with the new parameter added +#' +#' @keywords der_prm_bds_findings +#' @family der_prm_bds_findings +#' +#' @export +#' +#' @seealso [compute_framingham()] +#' +#' @examples +#' library(dplyr, warn.conflicts = FALSE) +#' +#' adcvrisk <- tibble::tribble( +#' ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, +#' ~VISIT, ~AGE, ~SEX, ~SMOKEFL, ~DIABETFL, ~TRTHYPFL, +#' "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, +#' "mmHg", "BASELINE", 44, "F", "N", "N", "N", +#' "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 115, +#' "mmHg", "WEEK 2", 44, "F", "N", "N", "Y", +#' "01-701-1015", "CHOL", "Total Cholesterol (mg/dL)", 216.16, +#' "mg/dL", "BASELINE", 44, "F", "N", "N", "N", +#' "01-701-1015", "CHOL", "Total Cholesterol (mg/dL)", 210.78, +#' "mg/dL", "WEEK 2", 44, "F", "N", "N", "Y", +#' "01-701-1015", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 54.91, +#' "mg/dL", "BASELINE", 44, "F", "N", "N", "N", +#' "01-701-1015", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 26.72, +#' "mg/dL", "WEEK 2", 44, "F", "N", "N", "Y", +#' "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 119, +#' "mmHg", "BASELINE", 55, "M", "Y", "Y", "Y", +#' "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 101, +#' "mmHg", "WEEK 2", 55, "M", "Y", "Y", "Y", +#' "01-701-1028", "CHOL", "Total Cholesterol (mg/dL)", 292.01, +#' "mg/dL", "BASELINE", 55, "M", "Y", "Y", "Y", +#' "01-701-1028", "CHOL", "Total Cholesterol (mg/dL)", 246.73, +#' "mg/dL", "WEEK 2", 55, "M", "Y", "Y", "Y", +#' "01-701-1028", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 65.55, +#' "mg/dL", "BASELINE", 55, "M", "Y", "Y", "Y", +#' "01-701-1028", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 44.62, +#' "mg/dL", "WEEK 2", 55, "M", "Y", "Y", "Y" +#' ) +#' +#' +#' adcvrisk %>% +#' derive_param_framingham( +#' by_vars = vars(USUBJID, VISIT), +#' set_values_to = vars( +#' PARAMCD = "FCVD101", +#' PARAM = "FCVD1-Framingham CVD 10-Year Risk Score (%)" +#' ), +#' get_unit_expr = AVALU +#' ) +#' +#' derive_param_framingham( +#' adcvrisk, +#' by_vars = vars(USUBJID, VISIT), +#' set_values_to = vars( +#' PARAMCD = "FCVD101", +#' PARAM = "FCVD1-Framingham CVD 10-Year Risk Score (%)" +#' ), +#' get_unit_expr = extract_unit(PARAM) +#' ) +derive_param_framingham <- function(dataset, + by_vars, + set_values_to = vars(PARAMCD = "FCVD101"), + sysbp_code = "SYSBP", + chol_code = "CHOL", + cholhdl_code = "CHOLHDL", + age = AGE, + sex = SEX, + smokefl = SMOKEFL, + diabetfl = DIABETFL, + trthypfl = TRTHYPFL, + get_unit_expr, + filter = NULL) { + assert_vars(by_vars) + + assert_data_frame( + dataset, + required_vars = quo_c( + vars(!!!by_vars, PARAMCD, AVAL), + enquo(age), + enquo(sex), + enquo(smokefl), + enquo(diabetfl), + enquo(trthypfl) + ) + ) + + assert_varval_list(set_values_to, required_elements = "PARAMCD") + assert_param_does_not_exist(dataset, quo_get_expr(set_values_to$PARAMCD)) + assert_character_scalar(sysbp_code) + assert_character_scalar(chol_code) + assert_character_scalar(cholhdl_code) + filter <- assert_filter_cond(enquo(filter), optional = TRUE) + + get_unit_expr <- assert_expr(enquo(get_unit_expr)) + assert_unit( + dataset, + sysbp_code, + required_unit = "mmHg", + get_unit_expr = !!get_unit_expr + ) + assert_unit( + dataset, + chol_code, + required_unit = "mg/dL", + get_unit_expr = !!get_unit_expr + ) + assert_unit( + dataset, + cholhdl_code, + required_unit = "mg/dL", + get_unit_expr = !!get_unit_expr + ) + + analysis_value <- expr( + compute_framingham( + sysbp = !!sym(paste0("AVAL.", sysbp_code)), + chol = !!sym(paste0("AVAL.", chol_code)), + cholhdl = !!sym(paste0("AVAL.", cholhdl_code)), + age = !!enquo(age), + sex = !!enquo(sex), + smokefl = !!enquo(smokefl), + diabetfl = !!enquo(diabetfl), + trthypfl = !!enquo(trthypfl) + ) + ) + + + derive_param_computed( + dataset, + filter = !!filter, + parameters = c(sysbp_code, chol_code, cholhdl_code), + by_vars = quo_c( + vars(!!!by_vars), + enquo(age), + enquo(sex), + enquo(smokefl), + enquo(diabetfl), + enquo(trthypfl) + ), + analysis_value = !!analysis_value, + set_values_to = set_values_to + ) +} diff --git a/R/derive_param_tte.R b/R/derive_param_tte.R index 980e444cec..3701aa3e73 100644 --- a/R/derive_param_tte.R +++ b/R/derive_param_tte.R @@ -73,9 +73,7 @@ #' #' \item The `ADT` variable is set to the variable specified by the #' \code{date} element. If the date variable is a datetime variable, only -#' the datepart is copied. If the source variable is a character variable, it -#' is converted to a date. If the date is incomplete, it is imputed as the -#' first possible date. +#' the datepart is copied. #' #' \item The `CNSR` variable is added and set to the \code{censor} element. #' @@ -96,9 +94,7 @@ #' #' \item The `ADT` variable is set to the variable specified by the #' \code{date} element. If the date variable is a datetime variable, only -#' the datepart is copied. If the source variable is a character variable, it -#' is converted to a date. If the date is incomplete, it is imputed as the -#' first possible date. +#' the datepart is copied. #' #' \item The `CNSR` variable is added and set to the \code{censor} element. #' @@ -127,11 +123,13 @@ #' #' @return The input dataset with the new parameter added #' -#' @keywords derivation bds +#' @family der_prm_tte +#' @keywords der_prm_tte #' #' @export #' #' @examples +#' library(tibble) #' library(dplyr, warn.conflicts = FALSE) #' library(lubridate) #' data("admiral_adsl") @@ -173,14 +171,14 @@ #' filter(row_number() %in% 20:30) #' #' # derive time to adverse event for each preferred term # -#' adsl <- tibble::tribble( +#' adsl <- tribble( #' ~USUBJID, ~TRTSDT, ~EOSDT, #' "01", ymd("2020-12-06"), ymd("2021-03-06"), #' "02", ymd("2021-01-16"), ymd("2021-02-03") #' ) %>% #' mutate(STUDYID = "AB42") #' -#' ae <- tibble::tribble( +#' ae <- tribble( #' ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, #' "01", "2021-01-03T10:56", 1, "Flu", #' "01", "2021-03-04", 2, "Cough", @@ -188,9 +186,17 @@ #' ) %>% #' mutate(STUDYID = "AB42") #' +#' ae_ext <- derive_vars_dt( +#' ae, +#' dtc = AESTDTC, +#' new_vars_prefix = "AEST", +#' highest_imputation = "M", +#' flag_imputation = "none" +#' ) +#' #' ttae <- event_source( #' dataset_name = "ae", -#' date = AESTDTC, +#' date = AESTDT, #' set_values_to = vars( #' EVNTDESC = "AE", #' SRCDOM = "AE", @@ -215,7 +221,7 @@ #' start_date = TRTSDT, #' event_conditions = list(ttae), #' censor_conditions = list(eos), -#' source_datasets = list(adsl = adsl, ae = ae), +#' source_datasets = list(adsl = adsl, ae = ae_ext), #' set_values_to = vars( #' PARAMCD = paste0("TTAE", as.numeric(as.factor(AEDECOD))), #' PARAM = paste("Time to First", AEDECOD, "Adverse Event"), @@ -280,6 +286,7 @@ derive_param_tte <- function(dataset = NULL, ) } + tmp_event <- get_new_tmp_var(dataset) # determine events # event_data <- filter_date_sources( sources = event_conditions, @@ -289,7 +296,7 @@ derive_param_tte <- function(dataset = NULL, subject_keys = subject_keys, mode = "first" ) %>% - mutate(temp_event = 1) + mutate(!!tmp_event := 1L) # determine censoring observations # censor_data <- filter_date_sources( @@ -300,7 +307,7 @@ derive_param_tte <- function(dataset = NULL, subject_keys = subject_keys, mode = "last" ) %>% - mutate(temp_event = 0) + mutate(!!tmp_event := 0L) # determine variable to add from ADSL # if (create_datetime) { @@ -340,7 +347,7 @@ derive_param_tte <- function(dataset = NULL, new_param <- filter_extreme( bind_rows(event_data, censor_data), by_vars = quo_c(subject_keys, by_vars), - order = vars(temp_event), + order = vars(!!tmp_event), mode = "last" ) %>% derive_vars_merged( @@ -370,7 +377,7 @@ derive_param_tte <- function(dataset = NULL, new_param <- new_param %>% mutate(!!date_var := pmax(!!date_var, !!start_var)) %>% - select(-starts_with("temp_")) + remove_tmp_vars() if (!is.null(by_vars)) { if (!is.null(set_values_to$PARAMCD)) { @@ -447,30 +454,37 @@ derive_param_tte <- function(dataset = NULL, #' #' @author Stefan Bundfuss #' -#' @keywords dev_utility +#' @keywords source_specifications +#' @family source_specifications +#' +#' @export #' #' @examples +#' library(tibble) #' library(dplyr, warn.conflicts = FALSE) #' library(lubridate) #' -#' adsl <- tibble::tribble( +#' adsl <- tribble( #' ~USUBJID, ~TRTSDT, ~EOSDT, #' "01", ymd("2020-12-06"), ymd("2021-03-06"), #' "02", ymd("2021-01-16"), ymd("2021-02-03") #' ) %>% #' mutate(STUDYID = "AB42") #' -#' ae <- tibble::tribble( -#' ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, -#' "01", "2021-01-03T10:56", 1, "Flu", -#' "01", "2021-03-04", 2, "Cough", -#' "01", "2021", 3, "Flu" +#' ae <- tribble( +#' ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, +#' "01", "2021-01-03", 1, "Flu", +#' "01", "2021-03-04", 2, "Cough", +#' "01", "2021-01-01", 3, "Flu" #' ) %>% -#' mutate(STUDYID = "AB42") +#' mutate( +#' STUDYID = "AB42", +#' AESTDT = ymd(AESTDTC) +#' ) #' #' ttae <- event_source( #' dataset_name = "ae", -#' date = AESTDTC, +#' date = AESTDT, #' set_values_to = vars( #' EVNTDESC = "AE", #' SRCDOM = "AE", @@ -487,7 +501,6 @@ derive_param_tte <- function(dataset = NULL, #' subject_keys = vars(STUDYID, USUBJID), #' mode = "first" #' ) -#' @export filter_date_sources <- function(sources, source_datasets, by_vars, @@ -513,7 +526,13 @@ filter_date_sources <- function(sources, data <- vector("list", length(sources)) for (i in seq_along(sources)) { date <- sources[[i]]$date - data[[i]] <- source_datasets[[sources[[i]]$dataset_name]] %>% + source_dataset <- source_datasets[[sources[[i]]$dataset_name]] + assert_date_var( + dataset = source_dataset, + var = !!date, + dataset_name = sources[[i]]$dataset_name + ) + data[[i]] <- source_dataset %>% filter_if(sources[[i]]$filter) %>% filter_extreme( order = vars(!!date), @@ -521,30 +540,12 @@ filter_date_sources <- function(sources, mode = mode, check_type = "none" ) + # add date variable and accompanying variables - if (is.instant(pull(data[[i]], !!date))) { - if (create_datetime) { - date_derv <- vars(!!date_var := as_datetime(!!date)) - } else { - date_derv <- vars(!!date_var := date(!!date)) - } + if (create_datetime) { + date_derv <- vars(!!date_var := as_datetime(!!date)) } else { - if (create_datetime) { - date_derv <- vars( - !!date_var := convert_dtc_to_dtm( - !!date, - date_imputation = "first", - time_imputation = "first" - ) - ) - } else { - date_derv <- vars( - !!date_var := convert_dtc_to_dt( - !!date, - date_imputation = "first" - ) - ) - } + date_derv <- vars(!!date_var := date(!!date)) } data[[i]] <- transmute( @@ -593,7 +594,8 @@ filter_date_sources <- function(sources, #' #' @author Stefan Bundfuss #' -#' @keywords dev_utility +#' @keywords source_specifications +#' @family source_specifications #' #' @examples #' library(dplyr, warn.conflicts = FALSE) @@ -689,8 +691,7 @@ extend_source_datasets <- function(source_datasets, #' `dataset` which are events or possible censoring time points. #' #' @param date A variable providing the date of the event or censoring. A date, -#' a datetime, or a character variable containing ISO 8601 dates can be -#' specified. An unquoted symbol is expected. +#' or a datetime can be specified. An unquoted symbol is expected. #' #' Refer to `derive_vars_dt()` to impute and derive a date from a date #' character vector to a date object. @@ -707,7 +708,8 @@ extend_source_datasets <- function(source_datasets, #' #' @author Stefan Bundfuss #' -#' @keywords dev_utility +#' @keywords source_specifications +#' @family source_specifications #' #' @seealso [derive_param_tte()], [censor_source()], [event_source()] #' @@ -740,6 +742,7 @@ tte_source <- function(dataset_name, #' #' @author Stefan Bundfuss #' +#' @family source_specifications #' @keywords source_specifications #' #' @seealso [derive_param_tte()], [censor_source()] @@ -784,6 +787,7 @@ event_source <- function(dataset_name, #' #' @author Stefan Bundfuss #' +#' @family source_specifications #' @keywords source_specifications #' #' @seealso [derive_param_tte()], [event_source()] @@ -826,6 +830,11 @@ censor_source <- function(dataset_name, #' #' @return No return value, called for side effects #' +#' @author Thomas Neitmann +#' +#' @keywords internal +#' @family internal +#' #' @export #' #' @seealso [tte_source()], [censor_source()], [event_source()] @@ -845,16 +854,44 @@ print.tte_source <- function(x, ...) { } } -list_tte_source_objects <- function() { - # Get all tte_source objects exported by admiral - exported <- getNamespaceExports("admiral") - objects <- - exported[map_lgl(exported, function(obj_name) { - inherits(get(obj_name), "tte_source") - })] - rows <- lapply(objects, function(obj_name) { - obj <- get(obj_name) +#' List all `tte_source` Objects Available in a Package +#' +#' @param package The name of the package in which to search for `tte_source` objects +#' +#' @return +#' A `data.frame` where each row corresponds to one `tte_source` object or `NULL` +#' if `package` does not contain any `tte_source` objects +#' +#' @author Thomas Neitmann +#' +#' @export +#' +#' @family source_specifications +#' @keywords source_specifications +#' +#' @examples +#' list_tte_source_objects() +list_tte_source_objects <- function(package = "admiral") { + assert_character_scalar(package) + + if (!requireNamespace(package, quietly = TRUE)) { + err_msg <- sprintf( + "No package called '%s' is installed and hence no `tte_source` objects are available", + package + ) + abort(err_msg) + } + + # Get all `tte_source` objects exported by `package` + exports <- getNamespaceExports(package) + is_tte_source <- map_lgl(exports, function(obj_name) { + inherits(getExportedValue(package, obj_name), "tte_source") + }) + tte_sources <- exports[is_tte_source] + + rows <- lapply(tte_sources, function(obj_name) { + obj <- getExportedValue(package, obj_name) data.frame( object = obj_name, dataset_name = obj$dataset_name, diff --git a/R/derive_param_wbc_abs.R b/R/derive_param_wbc_abs.R index 4ffa7e9149..fb623816ec 100644 --- a/R/derive_param_wbc_abs.R +++ b/R/derive_param_wbc_abs.R @@ -71,7 +71,8 @@ #' #' @return The input dataset with the new parameter added #' -#' @keywords derivation bds adlb +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings #' #' @export #' @@ -152,7 +153,7 @@ derive_param_wbc_abs <- function(dataset, # Create new parameter. dataset_new <- dataset_temp %>% - derive_derived_param( + derive_param_computed( parameters = c( wbc_code, diff_code diff --git a/R/derive_summary_records.R b/R/derive_summary_records.R index f6d6c81ced..c67e296c48 100644 --- a/R/derive_summary_records.R +++ b/R/derive_summary_records.R @@ -51,7 +51,10 @@ #' #' @return A data frame with derived records appended to original dataset. #' -#' @keywords bds derivation +#' @family der_prm_bds_findings +#' @keywords der_prm_bds_findings +#' +#' @seealso `get_summary_records()` #' #' @export #' @@ -161,22 +164,16 @@ derive_summary_records <- function(dataset, assert_varval_list(set_values_to, optional = TRUE) } - # Apply filter - if (!quo_is_null(filter)) { - subset_ds <- dataset %>% - group_by(!!!by_vars) %>% - filter(!!filter) %>% - ungroup() - } else { - subset_ds <- dataset - } - # Summarise the analysis value and bind to the original dataset bind_rows( dataset, - subset_ds %>% - group_by(!!!by_vars) %>% - summarise(!!analysis_var := summary_fun(!!analysis_var)) %>% - mutate(!!!set_values_to) + get_summary_records( + dataset, + by_vars = by_vars, + filter = !!filter, + analysis_var = !!analysis_var, + summary_fun = summary_fun, + set_values_to = set_values_to + ) ) } diff --git a/R/derive_var_ady.R b/R/derive_var_ady.R index d3b741b8d1..3d98356c5d 100644 --- a/R/derive_var_ady.R +++ b/R/derive_var_ady.R @@ -36,19 +36,10 @@ #' #' @return The input dataset with `ADY` column added #' -#' @keywords derivation bds timing +#' @keywords deprecated #' #' @export #' derive_var_ady <- function(dataset, reference_date = TRTSDT, date = ADT) { - reference_date <- assert_symbol(enquo(reference_date)) - date <- assert_symbol(enquo(date)) - assert_data_frame(dataset, vars(!!reference_date, !!date)) - deprecate_warn("0.7.0", "derive_var_ady()", "derive_vars_dy()") - - derive_vars_dy( - dataset, - reference_date = !!reference_date, - source_vars = vars(ADY = !!date) - ) + deprecate_stop("0.7.0", "derive_var_ady()", "derive_vars_dy()") } diff --git a/R/derive_var_aendy.R b/R/derive_var_aendy.R index a753232366..f74b8e803f 100644 --- a/R/derive_var_aendy.R +++ b/R/derive_var_aendy.R @@ -37,19 +37,10 @@ #' #' @return The input dataset with `AENDY` column added #' -#' @keywords derivation bds occds timing -#' +#' @keywords deprecated +#' @family deprecated #' @export #' derive_var_aendy <- function(dataset, reference_date = TRTSDT, date = AENDT) { - reference_date <- assert_symbol(enquo(reference_date)) - date <- assert_symbol(enquo(date)) - assert_data_frame(dataset, vars(!!reference_date, !!date)) - deprecate_warn("0.7.0", "derive_var_aendy()", "derive_vars_dy()") - - derive_vars_dy( - dataset, - reference_date = !!reference_date, - source_vars = vars(AENDY = !!date) - ) + deprecate_stop("0.7.0", "derive_var_aendy()", "derive_vars_dy()") } diff --git a/R/derive_var_analysis_ratio.R b/R/derive_var_analysis_ratio.R index 19fc7f5756..42213632e1 100644 --- a/R/derive_var_analysis_ratio.R +++ b/R/derive_var_analysis_ratio.R @@ -32,7 +32,8 @@ #' #' @return The input dataset with a ratio variable appended #' -#' @keywords adam bds adlb derivation +#' @family der_bds_findings +#' @keywords der_bds_findings #' #' @export #' diff --git a/R/derive_var_anrind.R b/R/derive_var_anrind.R index 0624e9e703..d678642319 100644 --- a/R/derive_var_anrind.R +++ b/R/derive_var_anrind.R @@ -19,7 +19,8 @@ #' #' @author Thomas Neitmann #' -#' @keywords bds derivation +#' @family der_bds_findings +#' @keywords der_bds_findings #' #' @export #' diff --git a/R/derive_var_astdy.R b/R/derive_var_astdy.R index 3542013b42..65a4257cc6 100644 --- a/R/derive_var_astdy.R +++ b/R/derive_var_astdy.R @@ -37,19 +37,10 @@ #' #' @return The input dataset with `ASTDY` column added #' -#' @keywords derivation bds occds timing +#' @keywords deprecated #' #' @export #' derive_var_astdy <- function(dataset, reference_date = TRTSDT, date = ASTDT) { - reference_date <- assert_symbol(enquo(reference_date)) - date <- assert_symbol(enquo(date)) - assert_data_frame(dataset, vars(!!reference_date, !!date)) - deprecate_warn("0.7.0", "derive_var_astdy()", "derive_vars_dy()") - - derive_vars_dy( - dataset, - reference_date = !!reference_date, - source_vars = vars(ASTDY = !!date) - ) + deprecate_stop("0.7.0", "derive_var_astdy()", "derive_vars_dy()") } diff --git a/R/derive_var_atirel.R b/R/derive_var_atirel.R index b3024cb2c3..bdb2cabf3e 100644 --- a/R/derive_var_atirel.R +++ b/R/derive_var_atirel.R @@ -31,35 +31,16 @@ #' @return A dataset containing all observations and variables of the input #' dataset and additionally the variable specified by the `new_var` parameter. #' -#' @keywords ADaM Relationship Var ATIREL +#' @keywords deprecated #' #' @export #' derive_var_atirel <- function(dataset, flag_var, new_var) { - # checks - warn(paste( - "`derive_var_atirel` is deprecated as of admiral 0.7.0.", - "Please use `mutate()` and `case_when()` instead.", - sep = "\n" - )) - - flag_var <- assert_symbol(enquo(flag_var)) - assert_data_frame(dataset, - required_vars = vars(STUDYID, USUBJID, TRTSDTM, ASTDTM, AENDTM, !!flag_var) + deprecate_stop( + "0.7.0", + "derive_var_atirel()", + details = "Please use combination of `mutate()` and `case_when()` instead." ) - new_var <- assert_symbol(enquo(new_var)) - warn_if_vars_exist(dataset, quo_text(new_var)) - - # logic to create ATIREL - dataset %>% - mutate(!!new_var := - case_when( - is.na(TRTSDTM) ~ NA_character_, - ASTDTM >= TRTSDTM ~ "CONCOMITANT", - !is.na(AENDTM) & AENDTM < TRTSDTM ~ "PRIOR", - date(ASTDTM) == date(TRTSDTM) & toupper(!!flag_var) %in% c("H", "M") ~ "CONCOMITANT", - TRUE ~ "PRIOR_CONCOMITANT" - )) } diff --git a/R/derive_var_atoxgr.R b/R/derive_var_atoxgr.R new file mode 100644 index 0000000000..5cfef9c12b --- /dev/null +++ b/R/derive_var_atoxgr.R @@ -0,0 +1,299 @@ +#' Derive Lab Toxicity Grade 0 - 4 +#' +#' @description +#' Derives a character lab grade based on severity/toxicity criteria. +#' +#' @param dataset Input data set +#' +#' The columns specified by `tox_description_var` parameter is expected. +#' +#' @param new_var Name of the character grade variable to create, for example, `ATOXGRH` +#' or `ATOXGRL`. +#' +#' @param tox_description_var Variable containing the description of the grading +#' criteria. For example: "Anemia" or "INR Increased". +#' +#' @param meta_criteria Metadata data set holding the criteria (normally a case statement) +#' +#' Default: `atoxgr_criteria_ctcv4` +#' +#' {admiral} metadata data set `atoxgr_criteria_ctcv4` implements +#' [Common Terminology Criteria for Adverse Events (CTCAE) +#' v4.0](https://ctep.cancer.gov/protocoldevelopment/electronic_applications/ctc.htm) +#' +#' The metadata should have the following variables: +#' +#' - `TERM`: variable to hold the term describing the criteria applied to a particular lab test, +#' eg. "Anemia" or "INR Increased". Note: the variable is case insensitive. +#' - `DIRECTION`: variable to hold the direction of the abnormality of a particular lab test +#' value. "L" is for LOW values, "H" is for HIGH values. Note: the variable is case insensitive. +#' - `SI_UNIT_CHECK`: variable to hold unit of particular lab test. Used to check against input data +#' if criteria is based on absolute values. +#' - `VAR_CHECK`: variable to hold comma separated list of variables used in criteria. Used to check +#' against input data that variables exist. +#' - `GRADE_CRITERIA_CODE`: variable to hold code that creates grade based on defined criteria. +#' +#' @param criteria_direction Direction (L= Low, H = High) of toxicity grade. +#' +#' Permitted Values: "L", "H" +#' +#' @param get_unit_expr An expression providing the unit of the parameter +#' +#' The result is used to check the units of the input parameters. Compared with +#' `SI_UNIT_CHECK` in metadata (see `meta_criteria` parameter). +#' +#' Permitted Values: A variable containing unit from the input dataset, or a function call, +#' for example, `get_unit_expr = extract_unit(PARAM)`. +#' +#' +#' @details +#' `new_var` is derived with values NA, "0", "1", "2", "3", "4", where "4" is the most +#' severe grade +#' - "4" is where the lab value satisfies the criteria for grade 4. +#' - "3" is where the lab value satisfies the criteria for grade 3. +#' - "2" is where the lab value satisfies the criteria for grade 2. +#' - "1" is where the lab value satisfies the criteria for grade 1. +#' - "0" is where a grade can be derived and is not grade "1", "2", "3" or "4". +#' - NA is where a grade cannot be derived. +#' +#' @author Gordon Miller +#' +#' @return The input dataset with the character variable added +#' +#' @keywords der_bds_findings +#' +#' @family der_bds_findings +#' +#' @export +#' +#' @examples +#' library(dplyr, warn.conflicts = FALSE) +#' +#' data <- tibble::tribble( +#' ~ATOXDSCL, ~AVAL, ~ANRLO, ~ANRHI, ~PARAM, +#' "Hypoglycemia", 119, 4, 7, "Glucose (mmol/L)", +#' "Hypoglycemia", 120, 4, 7, "Glucose (mmol/L)", +#' "Anemia", 129, 120, 180, "Hemoglobin (g/L)", +#' "White blood cell decreased", 10, 5, 20, "White blood cell (10^9/L)", +#' "White blood cell decreased", 15, 5, 20, "White blood cell (10^9/L)", +#' "Anemia", 140, 120, 180, "Hemoglobin (g/L)" +#' ) +#' +#' derive_var_atoxgr_dir(data, +#' new_var = ATOXGRL, +#' tox_description_var = ATOXDSCL, +#' meta_criteria = atoxgr_criteria_ctcv4, +#' criteria_direction = "L", +#' get_unit_expr = extract_unit(PARAM) +#' ) +#' +#' data <- tibble::tribble( +#' ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~PARAM, +#' "Hyperglycemia", 119, 4, 7, "Glucose (mmol/L)", +#' "Hyperglycemia", 120, 4, 7, "Glucose (mmol/L)", +#' "GGT increased", 129, 0, 30, "Gamma Glutamyl Transferase (U/L)", +#' "Lymphocyte count increased", 4, 1, 4, "Lymphocytes Abs (10^9/L)", +#' "Lymphocyte count increased", 2, 1, 4, "Lymphocytes Abs (10^9/L)", +#' "GGT increased", 140, 120, 180, "Gamma Glutamyl Transferase (U/L)" +#' ) +#' +#' derive_var_atoxgr_dir(data, +#' new_var = ATOXGRH, +#' tox_description_var = ATOXDSCH, +#' meta_criteria = atoxgr_criteria_ctcv4, +#' criteria_direction = "H", +#' get_unit_expr = extract_unit(PARAM) +#' ) +derive_var_atoxgr_dir <- function(dataset, + new_var, + tox_description_var, + meta_criteria = atoxgr_criteria_ctcv4, + criteria_direction, + get_unit_expr) { + new_var <- assert_symbol(enquo(new_var)) + tox_description_var <- assert_symbol(enquo(tox_description_var)) + get_unit_expr <- assert_expr(enquo(get_unit_expr)) + + # check input parameter has correct value + assert_character_scalar(criteria_direction, values = c("L", "H")) + + # Check Grade description variable exists on input data set + assert_data_frame(dataset, required_vars = vars(!!tox_description_var)) + + # Check metadata data set has required variables + assert_data_frame( + meta_criteria, + required_vars = vars(TERM, GRADE_CRITERIA_CODE, DIRECTION, SI_UNIT_CHECK, VAR_CHECK) + ) + # check DIRECTION has expected values L or H + assert_character_vector(meta_criteria$DIRECTION, values = c("L", "H")) + + + # Get list of terms from criteria metadata with particular direction + # L = low (Hypo) H = high (Hyper) + atoxgr_dir <- meta_criteria %>% + filter(!is.na(GRADE_CRITERIA_CODE) & toupper(DIRECTION) == toupper(criteria_direction)) %>% + select(TERM, DIRECTION, SI_UNIT_CHECK, GRADE_CRITERIA_CODE, VAR_CHECK) %>% + mutate( + TERM_UPPER = toupper(TERM), + SI_UNIT_UPPER = toupper(SI_UNIT_CHECK) + ) + + # from ADLB VAD get distinct list of terms to be graded + terms_in_vad <- dataset %>% + filter(!is.na(!!tox_description_var)) %>% + distinct(!!tox_description_var) %>% + mutate( + TERM = !!tox_description_var, + TERM_UPPER = toupper(TERM) + ) + + # only keep terms that exist in both ADLB data and criteria metadata + list_of_terms <- terms_in_vad %>% + semi_join(atoxgr_dir, by = "TERM_UPPER") %>% + arrange(TERM) + + # output lab data not to be graded + # this will be appended to in for loop after each term is graded + out_data <- dataset %>% + filter(!!tox_description_var %notin% (list_of_terms$TERM) | is.na(!!tox_description_var)) %>% + mutate(!!new_var := NA_character_) + + # get lab data to be graded + to_be_graded <- dataset %>% + filter(!!tox_description_var %in% (list_of_terms$TERM)) + + # for each TERM apply criteria and create grade derivation + for (i in seq_along(list_of_terms$TERM)) { + + # filter metadata on a term + meta_this_term <- atoxgr_dir %>% + filter(TERM_UPPER == list_of_terms$TERM_UPPER[i]) + + # Put list of variables required for criteria in a vector + list_of_vars <- gsub("\\s+", "", unlist(strsplit(meta_this_term$VAR_CHECK, ","))) + + # filter lab data on term and apply criteria to derive grade + grade_this_term <- to_be_graded %>% + filter(!!tox_description_var == list_of_terms$TERM[i]) + + # check variables required in criteria exist on data + assert_data_frame(grade_this_term, required_vars = vars(!!!syms(list_of_vars))) + + # apply criteria when SI unit matches + grade_this_term <- grade_this_term %>% + mutate( + temp_flag = meta_this_term$SI_UNIT_UPPER == toupper(!!get_unit_expr) | + is.na(meta_this_term$SI_UNIT_UPPER), + !!new_var := if_else( + temp_flag, eval(parse(text = meta_this_term$GRADE_CRITERIA_CODE)), NA_character_ + ) + ) %>% + select(-temp_flag) + + # remove lab data just graded from data still to be graded + to_be_graded <- to_be_graded %>% + filter(!!tox_description_var != list_of_terms$TERM[i]) + + # append lab data just graded to output data + out_data <- bind_rows(out_data, grade_this_term) + } + + out_data +} + + +#' Derive Lab High toxicity Grade 0 - 4 and Low Toxicity Grades 0 - (-4) +#' +#' @description +#' +#' Derives character lab grade based on high and low severity/toxicity grade(s). +#' +#' @param dataset Input data set +#' +#' The columns `ATOXGRL`, `ATOXGRH` and specified by `lotox_description_var`, +#' and `hitox_description_var` parameters are expected. +#' +#' @param lotox_description_var Variable containing the toxicity grade description +#' for low values, eg. "Anemia" +#' +#' @param hitox_description_var Variable containing the toxicity grade description +#' for low values, eg. "Hemoglobin Increased". +#' +#' @details +#' Created variable `ATOXGR` will contain values "-4", "-3", "-2", "-1" for low values +#' and "1", "2", "3", "4" for high values, and will contain "0" if value is gradable +#' and does not satisfy any of the criteria for high or low values. ATOXGR is set to +#' missing if information not available to give a grade. +#' +#' Function applies the following rules: +#' - High and low missing - overall missing +#' - Low grade not missing and > 0 - overall holds low grade +#' - High grade not missing and > 0 - overall holds high grade +#' - (Only high direction OR low direction is NORMAL) and high grade normal - overall NORMAL +#' - (Only low direction OR high direction is NORMAL) and low grade normal - overall NORMAL +#' - otherwise set to missing +#' +#' @author Gordon Miller +#' +#' @return The input data set with the character variable added +#' +#' @keywords der_bds_findings +#' +#' @family der_bds_findings +#' +#' @export +#' +#' @examples +#' library(dplyr, warn.conflicts = FALSE) +#' +#' adlb <- tibble::tribble( +#' ~ATOXDSCL, ~ATOXDSCH, ~ATOXGRL, ~ATOXGRH, +#' "Hypoglycemia", "Hyperglycemia", NA_character_, "0", +#' "Hypoglycemia", "Hyperglycemia", "0", "1", +#' "Hypoglycemia", "Hyperglycemia", "0", "0", +#' NA_character_, "INR Increased", NA_character_, "0", +#' "Hypophosphatemia", NA_character_, "1", NA_character_ +#' ) +#' +#' derive_var_atoxgr(adlb) +derive_var_atoxgr <- function(dataset, + lotox_description_var = ATOXDSCL, + hitox_description_var = ATOXDSCH) { + lotox_description_var <- assert_symbol(enquo(lotox_description_var)) + hitox_description_var <- assert_symbol(enquo(hitox_description_var)) + + assert_data_frame( + dataset, + required_vars = vars( + !!lotox_description_var, + ATOXGRL, + !!hitox_description_var, + ATOXGRH + ) + ) + + lowgrade_char <- unique(dataset$ATOXGRL) + assert_character_vector(lowgrade_char, values = c("0", "1", "2", "3", "4", NA_character_)) + + highgrade_char <- unique(dataset$ATOXGRH) + assert_character_vector(highgrade_char, values = c("0", "1", "2", "3", "4", NA_character_)) + + + # High and low missing - overall missing + # Low grade not missing and > 0 - overall holds low grade + # High grade not missing and > 0 - overall holds high grade + # (Only high direction OR low direction is NORMAL) and high grade normal - overall NORMAL + # (Only low direction OR high direction is NORMAL) and low grade normal - overall NORMAL + # otherwise set to missing + + dataset %>% + mutate(ATOXGR = case_when( + is.na(ATOXGRL) & is.na(ATOXGRH) ~ NA_character_, + !is.na(ATOXGRL) & ATOXGRL >= "1" ~ paste0("-", ATOXGRL), + !is.na(ATOXGRH) & ATOXGRH >= "1" ~ ATOXGRH, + (ATOXGRL == "0" | is.na(!!lotox_description_var)) & ATOXGRH == "0" ~ "0", + (ATOXGRH == "0" | is.na(!!hitox_description_var)) & ATOXGRL == "0" ~ "0", + TRUE ~ NA_character_ + )) +} diff --git a/R/derive_var_base.R b/R/derive_var_base.R index 872122337c..89b4789ed8 100644 --- a/R/derive_var_base.R +++ b/R/derive_var_base.R @@ -30,7 +30,9 @@ #' #' @author Thomas Neitmann #' -#' @keywords bds derivation +#' @family der_bds_findings +#' +#' @keywords der_bds_findings #' #' @examples #' dataset <- tibble::tribble( diff --git a/R/derive_var_basetype.R b/R/derive_var_basetype.R index c4c4ac049f..9c18ad7bb4 100644 --- a/R/derive_var_basetype.R +++ b/R/derive_var_basetype.R @@ -32,7 +32,9 @@ #' #' @author Thomas Neitmann #' -#' @keywords bds derivation +#' @family der_bds_findings +#' +#' @keywords der_bds_findings #' #' @export #' diff --git a/R/derive_var_chg.R b/R/derive_var_chg.R index 85cfc88248..123244fe71 100644 --- a/R/derive_var_chg.R +++ b/R/derive_var_chg.R @@ -12,10 +12,11 @@ #' @author Thomas Neitmann #' #' @return The input dataset with an additional column named `CHG` -#' @keywords bds derivation -#' @export #' -#' @seealso [derive_var_pchg()] +#' @family der_bds_findings +#' +#' @keywords der_bds_findings +#' @export #' #' @examples #' advs <- tibble::tribble( diff --git a/R/derive_var_confirmation_flag.R b/R/derive_var_confirmation_flag.R new file mode 100644 index 0000000000..75e92166f6 --- /dev/null +++ b/R/derive_var_confirmation_flag.R @@ -0,0 +1,350 @@ +#' Derive Confirmation Flag +#' +#' Derive a flag which depends on other observations of the dataset. For +#' example, flagging events which need to be confirmed by a second event. +#' +#' An example usage might be flagging if a patient received two required +#' medications within a certain timeframe of each other. +#' +#' In the oncology setting, for example, the function could be used to flag if a +#' response value can be confirmed by an other assessment. This is commonly +#' used in endpoints such as best overall response. +#' +#' @param dataset Input dataset +#' +#' The variables specified by the `by_vars` and `join_vars` parameter are +#' expected. +#' +#' @param by_vars By variables +#' +#' The specified variables are used as by variables for joining the input +#' dataset with itself. +#' +#' @param order Order +#' +#' The observations are ordered by the specified order. +#' +#' @param new_var New variable +#' +#' The specified variable is added to the input dataset. +#' +#' @param join_vars Variables to keep from joined dataset +#' +#' The variables needed from the other observations should be specified +#' for this parameter. The specified variables are added to the joined dataset +#' with suffix ".join". For example to flag all observations with `AVALC == +#' "Y"` and `AVALC == "Y"` for at least one subsequent visit `join_vars = +#' vars(AVALC, AVISITN)` and `filter = AVALC == "Y" & AVALC.join == "Y" & +#' AVISITN < AVISITN.join` could be specified. +#' +#' The `*.join` variables are not included in the output dataset. +#' +#' @param join_type Observations to keep after joining +#' +#' The argument determines which of the joined observations are kept with +#' respect to the original observation. For example, if `join_type = "after"` +#' is specified all observations after the original observations are kept. +#' +#' For example for confirmed response or BOR in the oncology setting or +#' confirmed deterioration in questionnaires the confirmatory assessment must +#' be after the assessment to be flagged. Thus `join_type = "after"` could be +#' used. +#' +#' Whereas, sometimes you might allow for confirmatory observations to occur +#' prior to the observation to be flagged. For example, to flag AEs occurring +#' on or after seven days before a COVID AE. Thus `join_type = "all"` could be +#' used. +#' +#' *Permitted Values:* `"before"`, `"after"`, `"all"` +#' +#' @param first_cond Condition for selecting range of data +#' +#' If this argument is specified, the other observations are restricted up to +#' the first observation where the specified condition is fulfilled. If the +#' condition is not fulfilled for any of the other observations, no +#' observations are considered, i.e., the observation is not flagged. +#' +#' This parameter should be specified if `filter` contains summary functions +#' which should not apply to all observations but only up to the confirmation +#' assessment. For an example see the third example below. +#' +#' @param filter Condition for selecting observations +#' +#' The filter is applied to the joined dataset for flagging the confirmed +#' observations. The condition can include summary functions. The joined +#' dataset is grouped by the original observations. I.e., the summary function +#' are applied to all observations up to the confirmation observation. For +#' example, `filter = AVALC == "CR" & all(AVALC.join %in% c("CR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1` selects observations with +#' response "CR" and for all observations up to the confirmation observation +#' the response is "CR" or "NE" and there is at most one "NE". +#' +#' @param check_type Check uniqueness? +#' +#' If `"warning"` or `"error"` is specified, the specified message is issued +#' if the observations of the input dataset are not unique with respect to the +#' by variables and the order. +#' +#' *Default:* `"warning"` +#' +#' *Permitted Values:* `"none"`, `"warning"`, `"error"` +#' +#' @param true_value Value of `new_var` for flagged observations +#' +#' *Default*: `"Y"` +#' +#' @param false_value Value of `new_var` for observations not flagged +#' +#' *Default*: `NA_character_` +#' +#' @details +#' The following steps are performed to produce the output dataset. +#' +#' ## Step 1 +#' +#' The input dataset is joined with itself by the variables specified for +#' `by_vars`. From the right hand side of the join only the variables +#' specified for `join_vars` are kept. The suffix ".join" is added to these +#' variables. +#' +#' For example, for `by_vars = USUBJID`, `join_vars = vars(AVISITN, AVALC)` and input dataset +#' +#' ```{r eval=FALSE} +#' # A tibble: 2 x 4 +#' USUBJID AVISITN AVALC AVAL +#' +#' 1 1 Y 1 +#' 1 2 N 0 +#' ``` +#' +#' the joined dataset is +#' +#' ```{r eval=FALSE} +#' A tibble: 4 x 6 +#' USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join +#' +#' 1 1 Y 1 1 Y +#' 1 1 Y 1 2 N +#' 1 2 N 0 1 Y +#' 1 2 N 0 2 N +#' ``` +#' +#' ## Step 2 +#' +#' The joined dataset is restricted to observations with respect to +#' `join_type` and `order`. +#' +#' The dataset from the example in the previous step with `join_type = +#' "after"` and `order = vars(AVISITN)` is restricted to +#' +#' ```{r eval=FALSE} +#' A tibble: 4 x 6 +#' USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join +#' +#' 1 1 Y 1 2 N +#' ``` +#' +#' ## Step 3 +#' +#' If `first_cond` is specified, for each observation of the input dataset the +#' joined dataset is restricted to observations up to the first observation +#' where `first_cond` is fulfilled (the observation fulfilling the condition +#' is included). If for an observation of the input dataset the condition is +#' not fulfilled, the observation is removed. +#' +#' ## Step 4 +#' +#' The joined dataset is grouped by the observations from the input dataset +#' and restricted to the observations fulfilling the condition specified by +#' `filter`. +#' +#' ## Step 5 +#' +#' The first observation of each group is selected +#' +#' ## Step 6 +#' +#' The variable specified by `new_var` is added to the input dataset. It is +#' set to `true_value` for all observations which were selected in the +#' previous step. For the other observations it is set to `false_value`. +#' +#' @return The input dataset with the variable specified by `new_var` added. +#' +#' @author Stefan Bundfuss +#' +#' @keywords der_gen +#' @family der_gen +#' +#' @seealso [filter_confirmation()] +#' +#' @export +#' +#' @examples +#' library(tibble) +#' library(admiral) +#' +#' # flag observations with a duration longer than 30 and +#' # at, after, or up to 7 days before a COVID AE (ACOVFL == "Y") +#' adae <- tribble( +#' ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, +#' "1", 10, "N", 1, +#' "1", 21, "N", 50, +#' "1", 23, "Y", 14, +#' "1", 32, "N", 31, +#' "1", 42, "N", 20, +#' "2", 11, "Y", 13, +#' "2", 23, "N", 2, +#' "3", 13, "Y", 12, +#' "4", 14, "N", 32, +#' "4", 21, "N", 41 +#' ) +#' +#' derive_var_confirmation_flag( +#' adae, +#' new_var = ALCOVFL, +#' by_vars = vars(USUBJID), +#' join_vars = vars(ACOVFL, ADY), +#' join_type = "all", +#' order = vars(ADY), +#' filter = ADURN > 30 & ACOVFL.join == "Y" & ADY >= ADY.join - 7 +#' ) +#' +#' # flag observations with AVALC == "Y" and AVALC == "Y" at one subsequent visit +#' data <- tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "Y", +#' "1", 2, "N", +#' "1", 3, "Y", +#' "1", 4, "N", +#' "2", 1, "Y", +#' "2", 2, "N", +#' "3", 1, "Y", +#' "4", 1, "N", +#' "4", 2, "N", +#' ) +#' +#' derive_var_confirmation_flag( +#' data, +#' by_vars = vars(USUBJID), +#' new_var = CONFFL, +#' join_vars = vars(AVALC, AVISITN), +#' join_type = "after", +#' order = vars(AVISITN), +#' filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join +#' ) +#' +#' # select observations with AVALC == "CR", AVALC == "CR" at a subsequent visit, +#' # only "CR" or "NE" in between, and at most one "NE" in between +#' data <- tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "PR", +#' "1", 2, "CR", +#' "1", 3, "NE", +#' "1", 4, "CR", +#' "1", 5, "NE", +#' "2", 1, "CR", +#' "2", 2, "PR", +#' "2", 3, "CR", +#' "3", 1, "CR", +#' "4", 1, "CR", +#' "4", 2, "NE", +#' "4", 3, "NE", +#' "4", 4, "CR", +#' "4", 5, "PR" +#' ) +#' +#' derive_var_confirmation_flag( +#' data, +#' by_vars = vars(USUBJID), +#' join_vars = vars(AVALC), +#' join_type = "after", +#' order = vars(AVISITN), +#' new_var = CONFFL, +#' first_cond = AVALC.join == "CR", +#' filter = AVALC == "CR" & all(AVALC.join %in% c("CR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1 +#' ) +#' +#' # flag observations with AVALC == "PR", AVALC == "CR" or AVALC == "PR" +#' # at a subsequent visit at least 20 days later, only "CR", "PR", or "NE" +#' # in between, at most one "NE" in between, and "CR" is not followed by "PR" +#' data <- tribble( +#' ~USUBJID, ~ADY, ~AVALC, +#' "1", 6, "PR", +#' "1", 12, "CR", +#' "1", 24, "NE", +#' "1", 32, "CR", +#' "1", 48, "PR", +#' "2", 3, "PR", +#' "2", 21, "CR", +#' "2", 33, "PR", +#' "3", 11, "PR", +#' "4", 7, "PR", +#' "4", 12, "NE", +#' "4", 24, "NE", +#' "4", 32, "PR", +#' "4", 55, "PR" +#' ) +#' +#' derive_var_confirmation_flag( +#' data, +#' by_vars = vars(USUBJID), +#' join_vars = vars(AVALC, ADY), +#' join_type = "after", +#' order = vars(ADY), +#' new_var = CONFFL, +#' first_cond = AVALC.join %in% c("CR", "PR") & ADY.join - ADY >= 20, +#' filter = AVALC == "PR" & +#' all(AVALC.join %in% c("CR", "PR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1 & +#' ( +#' min_cond(var = ADY.join, cond = AVALC.join == "CR") > +#' max_cond(var = ADY.join, cond = AVALC.join == "PR") | +#' count_vals(var = AVALC.join, val = "CR") == 0 +#' ) +#' ) +derive_var_confirmation_flag <- function(dataset, + by_vars, + order, + new_var, + join_vars, + join_type, + first_cond = NULL, + filter, + true_value = "Y", + false_value = NA_character_, + check_type = "warning") { + new_var <- assert_symbol(enquo(new_var)) + first_cond <- assert_filter_cond(enquo(first_cond), optional = TRUE) + filter <- assert_filter_cond(enquo(filter)) + assert_data_frame(dataset) + + tmp_obs_nr <- get_new_tmp_var(dataset, prefix = "tmp_obs_nr_") + data <- derive_var_obs_number( + dataset, + new_var = !!tmp_obs_nr + ) + + data_filtered <- filter_confirmation( + data, + by_vars = by_vars, + order = order, + join_vars = join_vars, + join_type = join_type, + first_cond = !!first_cond, + filter = !!filter, + check_type = check_type + ) + + derive_var_merged_exist_flag( + data, + dataset_add = data_filtered, + by_vars = vars(!!tmp_obs_nr), + new_var = !!new_var, + condition = TRUE, + true_value = true_value, + false_value = false_value, + missing_value = false_value + ) %>% + remove_tmp_vars() +} diff --git a/R/derive_var_disposition_dt.R b/R/derive_var_disposition_dt.R deleted file mode 100644 index 6d4ceb6222..0000000000 --- a/R/derive_var_disposition_dt.R +++ /dev/null @@ -1,95 +0,0 @@ -#' Derive a Disposition Date -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' This function is *deprecated*, please use `derive_vars_merged_dt()` instead. -#' -#' Derive a disposition status date from the the relevant records in the disposition domain. -#' -#' @param dataset Input dataset -#' -#' @param dataset_ds Datasets containing the disposition information (e.g.: ds) -#' -#' It must contain: -#' - `STUDYID`, `USUBJID`, -#' - The variable(s) specified in the `dtc` -#' - The variables used in `filter_ds`. -#' -#' @param new_var Name of the disposition date variable -#' -#' a variable name is expected -#' -#' @param dtc The character date used to derive/impute the disposition date -#' -#' A character date is expected in a format like yyyy-mm-dd or yyyy-mm-ddThh:mm:ss. -#' If the year part is not recorded (missing date), no imputation is performed. -#' -#' @param filter_ds Filter condition for the disposition data. -#' -#' Filter used to select the relevant disposition data. -#' It is expected that the filter restricts `dataset_ds` such that there is at most -#' one observation per patient. An error is issued otherwise. -#' -#' Permitted Values: logical expression. -#' -#' @param date_imputation The value to impute the day/month when a datepart is missing. -#' -#' If `NULL`: no date imputation is performed and partial dates are returned -#' as missing. -#' -#' Otherwise, a character value is expected, either as a -#' - format with day and month specified as 'mm-dd': e.g. '06-15' for the 15th -#' of June -#' - or as a keyword: 'FIRST', 'MID', 'LAST' to impute to the first/mid/last day/month. -#' -#' Default is `NULL` -#' -#' @param subject_keys Variables to uniquely identify a subject -#' -#' A list of quosures where the expressions are symbols as returned by -#' `vars()` is expected. -#' -#' @inheritParams impute_dtc -#' -#' @return the input dataset with the disposition date (`new_var`) added -#' -#' @keywords adsl timing -#' -#' @author Samia Kabi -#' -#' @export -#' -derive_var_disposition_dt <- function(dataset, - dataset_ds, - new_var, - dtc, - filter_ds, - date_imputation = NULL, - preserve = FALSE, - subject_keys = vars(STUDYID, USUBJID)) { - new_var <- assert_symbol(enquo(new_var)) - dtc <- assert_symbol(enquo(dtc)) - filter_ds <- assert_filter_cond(enquo(filter_ds)) - assert_character_scalar(date_imputation, optional = TRUE) - assert_data_frame(dataset) - assert_data_frame(dataset_ds, quo_c(dtc)) - warn_if_vars_exist(dataset, quo_text(new_var)) - assert_vars(subject_keys) - assert_logical_scalar(preserve) - deprecate_warn("0.7.0", "derive_var_disposition_dt()", "derive_vars_merged_dt()") - - derive_vars_merged_dt( - dataset, - dataset_add = dataset_ds, - filter_add = !!filter_ds, - new_vars_prefix = "temp_", - by_vars = subject_keys, - dtc = !!dtc, - date_imputation = date_imputation, - flag_imputation = "none", - preserve = preserve, - duplicate_msg = "The filter used for DS results in multiple records per patient." - ) %>% - rename(!!new_var := temp_DT) -} diff --git a/R/derive_var_disposition_status.R b/R/derive_var_disposition_status.R index 76c91178ad..f86f7b822a 100644 --- a/R/derive_var_disposition_status.R +++ b/R/derive_var_disposition_status.R @@ -6,14 +6,17 @@ #' @param status the disposition variable used for the mapping (e.g. `DSDECOD`). #' #' @return A `character` vector derived based on the values given in `status`: +#' "NOT STARTED" if `status` is "SCREEN FAILURE" or "SCREENING NOT COMPLETED", #' "COMPLETED" if `status` is "COMPLETED", -#' "DISCONTINUED" if `status` is not "COMPLETED" nor NA, +#' "DISCONTINUED" if `status` is not in ("COMPLETED","SCREEN FAILURE", +#' "SCREENING NOT COMPLETED") nor NA, #' "ONGOING" otherwise. #' #' @author Samia Kabi #' @details Usually this function can not be used with `%>%`. #' @export -#' @keywords user_utility adsl computation +#' @family utils_fmt +#' @keywords utils_fmt #' @seealso [derive_var_disposition_status()] #' @examples #' library(dplyr, warn.conflicts = FALSE) @@ -32,8 +35,10 @@ #' select(STUDYID, USUBJID, EOSSTT) format_eoxxstt_default <- function(status) { case_when( + status %in% c("SCREEN FAILURE", "SCREENING NOT COMPLETED") ~ "NOT STARTED", status == "COMPLETED" ~ "COMPLETED", - status != "COMPLETED" & !is.na(status) ~ "DISCONTINUED", + !status %in% c("COMPLETED", "SCREEN FAILURE", "SCREENING NOT COMPLETED") & + !is.na(status) ~ "DISCONTINUED", TRUE ~ "ONGOING" ) } @@ -62,11 +67,13 @@ format_eoxxstt_default <- function(status) { #' @param format_new_var The format used to derive the status. #' #' Default: `format_eoxxstt_default()` defined as: -#' ``` +#' ``` {r echo=TRUE, eval=FALSE} #' format_eoxxstt_default <- function(status) { #' case_when( +#' status %in% c("SCREEN FAILURE", "SCREENING NOT COMPLETED") ~ "NOT STARTED", #' status == "COMPLETED" ~ "COMPLETED", -#' status != "COMPLETED" & !is.na(status) ~ "DISCONTINUED", +#' !status %in% c("COMPLETED", "SCREEN FAILURE", "SCREENING NOT COMPLETED") +#' & !is.na(status) ~ "DISCONTINUED", #' TRUE ~ "ONGOING" #' ) #' } @@ -89,11 +96,14 @@ format_eoxxstt_default <- function(status) { #' `new_var` is derived based on the values given in `status_var` and according to the format #' defined by `format_new_var` (e.g. when the default format is used, the function will derive #' `new_var` as: +#' "NOT STARTED" if `status` is "SCREEN FAILURE" or "SCREENING NOT COMPLETED", #' "COMPLETED" if `status_var` == "COMPLETED", -#' "DISCONTINUED" if `status_var` is not "COMPLETED" nor NA, +#' "DISCONTINUED" if `status` is not in ("COMPLETED","SCREEN FAILURE", +#' "SCREENING NOT COMPLETED") nor NA, #' "ONGOING" otherwise). #' -#' @keywords adsl +#' @family der_adsl +#' @keywords der_adsl #' #' @author Samia Kabi #' @@ -106,8 +116,10 @@ format_eoxxstt_default <- function(status) { #' data("admiral_ds") #' #' # Default derivation: EOSSTT = -#' #- COMPLETED when status_var = COMPLETED -#' #- DISCONTINUED when status_var is not COMPLETED nor NA +#' #- NOT STARTED when status_var is SCREEN FAILURE or SCREENING NOT COMPLETED +#' #- COMPLETED when status_var is COMPLETED +#' #- DISCONTINUED when status_var is not COMPLETED nor SCREEN FAILURE nor +#' # SCREENING NOT COMPLETED nor NA #' #- ONGOING otherwise #' #' admiral_dm %>% @@ -120,16 +132,20 @@ format_eoxxstt_default <- function(status) { #' select(STUDYID, USUBJID, EOSSTT) #' #' # Specific derivation: EOSSTT = +#' #- NOT STARTED when status_var = SCREEN FAILURE #' #- COMPLETED when status_var = COMPLETED #' #- DISCONTINUED DUE TO AE when status_var = ADVERSE EVENT -#' #- DISCONTINUED NOT DUE TO AE when status_var != ADVERSE EVENT nor COMPLETED nor missing +#' #- DISCONTINUED NOT DUE TO AE when status_var != ADVERSE EVENT nor COMPLETED +#' # nor SCREEN FAILURE nor missing #' #- ONGOING otherwise #' #' format_eoxxstt1 <- function(x) { #' case_when( +#' x == "SCREEN FAILURE" ~ "NOT STARTED", #' x == "COMPLETED" ~ "COMPLETED", #' x == "ADVERSE EVENT" ~ "DISCONTINUED DUE TO AE", -#' !(x %in% c("ADVERSE EVENT", "COMPLETED")) & !is.na(x) ~ "DISCONTINUED NOT DUE TO AE", +#' !(x %in% c("ADVERSE EVENT", "COMPLETED", "SCREEN FAILURE")) & !is.na(x) ~ +#' "DISCONTINUED NOT DUE TO AE", #' TRUE ~ "ONGOING" #' ) #' } @@ -153,7 +169,7 @@ derive_var_disposition_status <- function(dataset, new_var <- assert_symbol(enquo(new_var)) status_var <- assert_symbol(enquo(status_var)) filter_ds <- assert_filter_cond(enquo(filter_ds)) - assert_that(is.function(format_new_var)) + assert_s3_class(format_new_var, "function") assert_data_frame(dataset) assert_data_frame(dataset_ds, quo_c(status_var)) warn_if_vars_exist(dataset, quo_text(new_var)) diff --git a/R/derive_var_dthcaus.R b/R/derive_var_dthcaus.R index 61e57a4c51..fa603c61fc 100644 --- a/R/derive_var_dthcaus.R +++ b/R/derive_var_dthcaus.R @@ -23,41 +23,50 @@ #' equivalent, the first source will be kept, so the user should provide the inputs in #' the preferred order. #' -#' @keywords derivation adsl +#' @family der_adsl +#' @keywords der_adsl #' #' @author -#' Shimeng Huang, Samia Kabi, Thomas Neitmann +#' Shimeng Huang, Samia Kabi, Thomas Neitmann, Tamara Senior #' -#' @return The input dataset with `DTHCAUS` variable added. +#' @return `derive_var_dthcaus()` returns the input dataset with `DTHCAUS` variable added. #' #' @export #' -#' @seealso [dthcaus_source()] -#' #' @examples -#' adsl <- tibble::tribble( -#' ~STUDYID, ~USUBJID, +#' library(tibble) +#' library(dplyr) +#' library(lubridate) +#' +#' adsl <- tribble( +#' ~STUDYID, ~USUBJID, #' "STUDY01", "PAT01", #' "STUDY01", "PAT02", #' "STUDY01", "PAT03" #' ) -#' ae <- tibble::tribble( -#' ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, -#' "STUDY01", "PAT01", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" -#' ) -#' ds <- tibble::tribble( +#' ae <- tribble( +#' ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, +#' "STUDY01", "PAT01", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" +#' ) %>% +#' mutate( +#' AEDTHDT = ymd(AEDTHDTC) +#' ) +#' ds <- tribble( #' ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, #' "STUDY01", "PAT02", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", #' "STUDY01", "PAT02", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", #' "STUDY01", "PAT02", 3, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2022-02-01", #' "STUDY01", "PAT03", 1, "DEATH", "POST STUDY REPORTING OF DEATH", "2022-03-03" -#' ) +#' ) %>% +#' mutate( +#' DSSTDT = ymd(DSSTDTC) +#' ) #' #' # Derive `DTHCAUS` only - for on-study deaths only #' src_ae <- dthcaus_source( #' dataset_name = "ae", #' filter = AEOUT == "FATAL", -#' date = AEDTHDTC, +#' date = AEDTHDT, #' mode = "first", #' dthcaus = AEDECOD #' ) @@ -65,7 +74,7 @@ #' src_ds <- dthcaus_source( #' dataset_name = "ds", #' filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), -#' date = DSSTDTC, +#' date = DSSTDT, #' mode = "first", #' dthcaus = DSTERM #' ) @@ -76,7 +85,7 @@ #' src_ae <- dthcaus_source( #' dataset_name = "ae", #' filter = AEOUT == "FATAL", -#' date = AEDTHDTC, +#' date = AEDTHDT, #' mode = "first", #' dthcaus = AEDECOD, #' traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -85,7 +94,7 @@ #' src_ds <- dthcaus_source( #' dataset_name = "ds", #' filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), -#' date = DSSTDTC, +#' date = DSSTDT, #' mode = "first", #' dthcaus = DSTERM, #' traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) @@ -97,7 +106,7 @@ #' src_ae <- dthcaus_source( #' dataset_name = "ae", #' filter = AEOUT == "FATAL", -#' date = AEDTHDTC, +#' date = AEDTHDT, #' mode = "first", #' dthcaus = AEDECOD, #' traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -106,7 +115,7 @@ #' src_ds <- dthcaus_source( #' dataset_name = "ds", #' filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), -#' date = DSSTDTC, +#' date = DSSTDT, #' mode = "first", #' dthcaus = DSTERM, #' traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) @@ -115,7 +124,7 @@ #' src_ds_post <- dthcaus_source( #' dataset_name = "ds", #' filter = DSDECOD == "DEATH" & DSTERM == "POST STUDY REPORTING OF DEATH", -#' date = DSSTDTC, +#' date = DSSTDT, #' mode = "first", #' dthcaus = "POST STUDY: UNKNOWN CAUSE", #' traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) @@ -153,28 +162,32 @@ derive_var_dthcaus <- function(dataset, for (ii in seq_along(sources)) { source_dataset_name <- sources[[ii]]$dataset_name source_dataset <- source_datasets[[source_dataset_name]] - if (!quo_is_null(sources[[ii]]$filter)) { - add_data[[ii]] <- source_dataset %>% - filter(!!sources[[ii]]$filter) - } else { - add_data[[ii]] <- source_dataset - } + add_data[[ii]] <- source_dataset %>% + filter_if(sources[[ii]]$filter) + + assert_date_var( + dataset = add_data[[ii]], + var = !!sources[[ii]]$date, + dataset_name = source_dataset_name + ) # if several death records, use the first/last according to 'mode' + tmp_source_nr <- get_new_tmp_var(dataset) + tmp_date <- get_new_tmp_var(dataset) add_data[[ii]] <- add_data[[ii]] %>% filter_extreme( - order = vars(!!sources[[ii]]$date), + order = vars(!!sources[[ii]]$date, !!!sources[[ii]]$order), by_vars = subject_keys, mode = sources[[ii]]$mode ) %>% mutate( - temp_source_nr = ii, - temp_date = !!sources[[ii]]$date, + !!tmp_source_nr := ii, + !!tmp_date := !!sources[[ii]]$date, DTHCAUS = !!sources[[ii]]$dthcaus ) # add traceability param if required - # inconsitent traceability lists issue a warning + # inconsistent traceability lists issue a warning if (ii > 1) { warn_if_inconsistent_list( base = sources[[ii - 1]]$traceability, @@ -188,24 +201,24 @@ derive_var_dthcaus <- function(dataset, add_data[[ii]] <- add_data[[ii]] %>% transmute( !!!subject_keys, - temp_source_nr, - temp_date, + !!tmp_source_nr, + !!tmp_date, DTHCAUS, !!!sources[[ii]]$traceability ) } else { add_data[[ii]] <- add_data[[ii]] %>% - select(!!!subject_keys, temp_source_nr, temp_date, DTHCAUS) + select(!!!subject_keys, !!tmp_source_nr, !!tmp_date, DTHCAUS) } } # if a subject has multiple death info, keep the one from the first source dataset_add <- bind_rows(add_data) %>% filter_extreme( - order = vars(temp_date, temp_source_nr), + order = vars(!!tmp_date, !!tmp_source_nr), by_vars = subject_keys, mode = "first" ) %>% - select(-starts_with("temp_")) + remove_tmp_vars() derive_vars_merged(dataset, dataset_add = dataset_add, by_vars = subject_keys) } @@ -217,7 +230,17 @@ derive_var_dthcaus <- function(dataset, #' #' @param filter An expression used for filtering `dataset`. #' -#' @param date A character vector to be used for sorting `dataset`. +#' @param date A date or datetime variable to be used for sorting `dataset`. +#' +#' @param order Sort order +#' +#' Additional variables to be used for sorting the `dataset` which is ordered by the +#' `date` and `order`. Can be used to avoid duplicate record warning. +#' +#' *Default*: `NULL` +#' +#' *Permitted Values*: list of variables or `desc()` function calls +#' created by `vars()`, e.g., `vars(ADT, desc(AVAL))` or `NULL` #' #' @param mode One of `"first"` or `"last"`. #' Either the `"first"` or `"last"` observation is preserved from the `dataset` @@ -236,19 +259,18 @@ derive_var_dthcaus <- function(dataset, #' in the returned dataset. #' These can be either strings or symbols referring to existing variables. #' -#' -#' @author Shimeng Huang +#' @describeIn derive_var_dthcaus Create objects of class "dthcaus_source" #' #' @keywords source_specifications -#' -#' @seealso [derive_var_dthcaus()] +#' @family source_specifications #' #' @export #' -#' @return An object of class "dthcaus_source". +#' @return `dthcaus_source()` returns an object of class "dthcaus_source". dthcaus_source <- function(dataset_name, filter, date, + order = NULL, mode = "first", dthcaus, traceability_vars = NULL) { @@ -256,6 +278,7 @@ dthcaus_source <- function(dataset_name, dataset_name = assert_character_scalar(dataset_name), filter = assert_filter_cond(enquo(filter), optional = TRUE), date = assert_symbol(enquo(date)), + order = assert_order_vars(order, optional = TRUE), mode = assert_character_scalar(mode, values = c("first", "last"), case_sensitive = FALSE), dthcaus = assert_symbol(enquo(dthcaus)) %or% assert_character_scalar(dthcaus), traceability = assert_varval_list(traceability_vars, optional = TRUE) diff --git a/R/derive_var_extreme_date.R b/R/derive_var_extreme_date.R index d30fdb7c8a..e4d8c34579 100644 --- a/R/derive_var_extreme_date.R +++ b/R/derive_var_extreme_date.R @@ -1,48 +1,3 @@ -#' Derive Last Known Alive Date -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' *Deprecated*, please use `derive_var_extreme_dt()` instead. Add the last -#' known alive date (`LSTALVDT`) to the dataset. -#' -#' @param dataset Input dataset -#' -#' The variables specified by `subject_keys` are required. -#' -#' @param source_datasets A named `list` containing datasets in which to search for the -#' last known alive date -#' -#' @param ... Source(s) of known alive dates. One or more `lstalvdt_source()` objects are -#' expected. -#' -#' @param subject_keys Variables to uniquely identify a subject -#' -#' A list of quosures where the expressions are symbols as returned by -#' `vars()` is expected. -#' -#' @author Stefan Bundfuss, Thomas Neitmann -#' -#' @return The input dataset with the `LSTALVDT` variable added. -#' -#' @keywords derivation adsl -#' -#' @export -derive_var_lstalvdt <- function(dataset, - ..., - source_datasets, - subject_keys = vars(STUDYID, USUBJID)) { - deprecate_warn("0.7.0", "derive_var_lstalvdt()", "derive_var_extreme_dt()") - derive_var_extreme_dt( - dataset, - new_var = LSTALVDT, - ..., - source_datasets = source_datasets, - mode = "last", - subject_keys = subject_keys - ) -} - #' Derive First or Last Datetime from Multiple Sources #' #' Add the first or last datetime from multiple sources to the dataset, e.g., @@ -79,11 +34,8 @@ derive_var_lstalvdt <- function(dataset, #' (with respect to `date` and `mode`) is selected. #' #' 1. The new variable is set to the variable specified by the `date` element. -#' If the date variable is a date variable, the time is imputed as specified -#' by the `time_imputation` element. If the source variable is a character -#' variable, it is converted to a datetime. If the date is incomplete, it is -#' imputed as specified by the `date_imputation` and `time_imputation` -#' element. +#' If this is a date variable (rather than datetime), then the time is imputed +#' as `"00:00:00"`. #' #' 1. The variables specified by the `traceability_vars` element are added. #' @@ -98,10 +50,10 @@ derive_var_lstalvdt <- function(dataset, #' #' @author Stefan Bundfuss, Thomas Neitmann #' -#' @keywords derivation adsl +#' @family der_adsl +#' @keywords der_adsl #' #' @seealso [date_source()], [derive_var_extreme_dt()], -#' [derive_vars_merged_dt()], [derive_vars_merged_dtm()], #' [derive_vars_merged()] #' #' @export @@ -117,22 +69,37 @@ derive_var_lstalvdt <- function(dataset, #' # derive last known alive datetime (LSTALVDTM) #' ae_start <- date_source( #' dataset_name = "ae", -#' date = AESTDTC, -#' date_imputation = "first", -#' time_imputation = "first" +#' date = AESTDTM #' ) #' ae_end <- date_source( #' dataset_name = "ae", -#' date = AEENDTC, -#' date_imputation = "first", -#' time_imputation = "first" +#' date = AEENDTM #' ) +#' +#' ae_ext <- admiral_ae %>% +#' derive_vars_dtm( +#' dtc = AESTDTC, +#' new_vars_prefix = "AEST", +#' highest_imputation = "M" +#' ) %>% +#' derive_vars_dtm( +#' dtc = AEENDTC, +#' new_vars_prefix = "AEEN", +#' highest_imputation = "M" +#' ) +#' #' lb_date <- date_source( #' dataset_name = "lb", -#' date = LBDTC, -#' filter = nchar(LBDTC) >= 10, -#' time_imputation = "first" +#' date = LBDTM, +#' filter = !is.na(LBDTM) +#' ) +#' +#' lb_ext <- derive_vars_dtm( +#' admiral_lb, +#' dtc = LBDTC, +#' new_vars_prefix = "LB" #' ) +#' #' adsl_date <- date_source(dataset_name = "adsl", date = TRTEDTM) #' #' admiral_dm %>% @@ -141,7 +108,7 @@ derive_var_lstalvdt <- function(dataset, #' ae_start, ae_end, lb_date, adsl_date, #' source_datasets = list( #' adsl = admiral_adsl, -#' ae = admiral_ae, lb = admiral_lb +#' ae = ae_ext, lb = lb_ext #' ), #' mode = "last" #' ) %>% @@ -150,9 +117,7 @@ derive_var_lstalvdt <- function(dataset, #' # derive last alive datetime and traceability variables #' ae_start <- date_source( #' dataset_name = "ae", -#' date = AESTDTC, -#' date_imputation = "first", -#' time_imputation = "first", +#' date = AESTDTM, #' traceability_vars = vars( #' LALVDOM = "AE", #' LALVSEQ = AESEQ, @@ -162,9 +127,7 @@ derive_var_lstalvdt <- function(dataset, #' #' ae_end <- date_source( #' dataset_name = "ae", -#' date = AEENDTC, -#' date_imputation = "first", -#' time_imputation = "first", +#' date = AEENDTM, #' traceability_vars = vars( #' LALVDOM = "AE", #' LALVSEQ = AESEQ, @@ -173,9 +136,8 @@ derive_var_lstalvdt <- function(dataset, #' ) #' lb_date <- date_source( #' dataset_name = "lb", -#' date = LBDTC, -#' filter = nchar(LBDTC) >= 10, -#' time_imputation = "first", +#' date = LBDTM, +#' filter = !is.na(LBDTM), #' traceability_vars = vars( #' LALVDOM = "LB", #' LALVSEQ = LBSEQ, @@ -199,7 +161,8 @@ derive_var_lstalvdt <- function(dataset, #' ae_start, ae_end, lb_date, adsl_date, #' source_datasets = list( #' adsl = admiral_adsl, -#' ae = admiral_ae, lb = admiral_lb +#' ae = ae_ext, +#' lb = lb_ext #' ), #' mode = "last" #' ) %>% @@ -253,6 +216,11 @@ derive_var_extreme_dtm <- function(dataset, source_dataset <- source_datasets[[source_dataset_name]] date <- quo_get_expr(sources[[i]]$date) + assert_date_var( + dataset = source_dataset, + var = !!date, + dataset_name = source_dataset_name + ) add_data[[i]] <- source_dataset %>% filter_if(sources[[i]]$filter) %>% filter_extreme( @@ -266,12 +234,7 @@ derive_var_extreme_dtm <- function(dataset, add_data[[i]], !!!subject_keys, !!!sources[[i]]$traceability_vars, - !!new_var := convert_date_to_dtm( - !!date, - date_imputation = sources[[i]]$date_imputation, - time_imputation = sources[[i]]$time_imputation, - preserve = sources[[i]]$preserve - ) + !!new_var := convert_date_to_dtm(!!date) ) } @@ -306,11 +269,6 @@ derive_var_extreme_dtm <- function(dataset, #' (with respect to `date` and `mode`) is selected. #' #' 1. The new variable is set to the variable specified by the `date` element. -#' If the date variable is a date variable, the time is imputed as -#' `time_imputation = "first"`. If the source variable is a character -#' variable, it is converted to a datetime. If the date is incomplete, it is -#' imputed as specified by the `date_imputation` element and with -#' `time_imputation = "first"`. #' #' 1. The variables specified by the `traceability_vars` element are added. #' @@ -327,11 +285,10 @@ derive_var_extreme_dtm <- function(dataset, #' #' @author Stefan Bundfuss, Thomas Neitmann #' -#' @keywords derivation adsl +#' @family der_adsl +#' @keywords der_adsl #' -#' @seealso [date_source()], [derive_var_extreme_dtm()], -#' [derive_vars_merged_dt()], [derive_vars_merged_dtm()], -#' [derive_vars_merged()] +#' @seealso [date_source()], [derive_var_extreme_dtm()], [derive_vars_merged()] #' #' @export #' @@ -346,19 +303,37 @@ derive_var_extreme_dtm <- function(dataset, #' # derive last known alive date (LSTALVDT) #' ae_start <- date_source( #' dataset_name = "ae", -#' date = AESTDTC, -#' date_imputation = "first", +#' date = AESTDT #' ) #' ae_end <- date_source( #' dataset_name = "ae", -#' date = AEENDTC, -#' date_imputation = "first", +#' date = AEENDT #' ) +#' +#' ae_ext <- admiral_ae %>% +#' derive_vars_dt( +#' dtc = AESTDTC, +#' new_vars_prefix = "AEST", +#' highest_imputation = "M" +#' ) %>% +#' derive_vars_dt( +#' dtc = AEENDTC, +#' new_vars_prefix = "AEEN", +#' highest_imputation = "M" +#' ) +#' #' lb_date <- date_source( #' dataset_name = "lb", -#' date = LBDTC, -#' filter = nchar(LBDTC) >= 10, +#' date = LBDT, +#' filter = !is.na(LBDT), #' ) +#' +#' lb_ext <- derive_vars_dt( +#' admiral_lb, +#' dtc = LBDTC, +#' new_vars_prefix = "LB" +#' ) +#' #' adsl_date <- date_source(dataset_name = "adsl", date = TRTEDT) #' #' admiral_dm %>% @@ -367,7 +342,8 @@ derive_var_extreme_dtm <- function(dataset, #' ae_start, ae_end, lb_date, adsl_date, #' source_datasets = list( #' adsl = admiral_adsl, -#' ae = admiral_ae, lb = admiral_lb +#' ae = ae_ext, +#' lb = lb_ext #' ), #' mode = "last" #' ) %>% @@ -376,8 +352,7 @@ derive_var_extreme_dtm <- function(dataset, #' # derive last alive date and traceability variables #' ae_start <- date_source( #' dataset_name = "ae", -#' date = AESTDTC, -#' date_imputation = "first", +#' date = AESTDT, #' traceability_vars = vars( #' LALVDOM = "AE", #' LALVSEQ = AESEQ, @@ -387,8 +362,7 @@ derive_var_extreme_dtm <- function(dataset, #' #' ae_end <- date_source( #' dataset_name = "ae", -#' date = AEENDTC, -#' date_imputation = "first", +#' date = AEENDT, #' traceability_vars = vars( #' LALVDOM = "AE", #' LALVSEQ = AESEQ, @@ -397,8 +371,8 @@ derive_var_extreme_dtm <- function(dataset, #' ) #' lb_date <- date_source( #' dataset_name = "lb", -#' date = LBDTC, -#' filter = nchar(LBDTC) >= 10, +#' date = LBDT, +#' filter = !is.na(LBDT), #' traceability_vars = vars( #' LALVDOM = "LB", #' LALVSEQ = LBSEQ, @@ -422,7 +396,8 @@ derive_var_extreme_dtm <- function(dataset, #' ae_start, ae_end, lb_date, adsl_date, #' source_datasets = list( #' adsl = admiral_adsl, -#' ae = admiral_ae, lb = admiral_lb +#' ae = ae_ext, +#' lb = lb_ext #' ), #' mode = "last" #' ) %>% @@ -437,9 +412,6 @@ derive_var_extreme_dt <- function(dataset, sources <- list(...) assert_list_of(sources, "date_source") - for (i in seq_along(sources)) { - sources[[i]]$time_imputation <- "first" - } derive_var_extreme_dtm( dataset, @@ -452,53 +424,6 @@ derive_var_extreme_dt <- function(dataset, mutate(!!new_var := date(!!new_var)) } -#' Create an `lstalvdt_source` object -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' *Deprecated*, please use `date_source()` instead. -#' -#' @param dataset_name The name of the dataset, i.e. a string, used to search for -#' the last known alive date. -#' -#' @param filter An unquoted condition for filtering `dataset`. -#' -#' @param date A variable providing a date where the patient was known to be -#' alive. A date, a datetime, or a character variable containing ISO 8601 -#' dates can be specified. An unquoted symbol is expected. -#' -#' @param date_imputation A string defining the date imputation for `date`. -#' See `date_imputation` parameter of `impute_dtc()` for valid values. -#' -#' @param traceability_vars A named list returned by `vars()` defining the -#' traceability variables, e.g. `vars(LALVDOM = "AE", LALVSEQ = AESEQ, LALVVAR -#' = "AESTDTC")`. The values must be a symbol, a character string, or `NA`. -#' -#' -#' @author Stefan Bundfuss -#' -#' @keywords source_specifications -#' -#' @export -#' -#' @return An object of class `lstalvdt_source`. -lstalvdt_source <- function(dataset_name, - filter = NULL, - date, - date_imputation = NULL, - traceability_vars = NULL) { - deprecate_warn("0.7.0", "lstalvdt_source()", "date_source()") - - date_source( - dataset_name = dataset_name, - filter = !!enquo(filter), - date = !!enquo(date), - date_imputation = date_imputation, - traceability_vars = traceability_vars - ) -} - #' Create a `date_source` object #' #' Create a `date_source` object as input for `derive_var_extreme_dt()` and @@ -509,18 +434,17 @@ lstalvdt_source <- function(dataset_name, #' #' @param filter An unquoted condition for filtering `dataset`. #' -#' @param date A variable providing a date. A date, a datetime, or a character -#' variable containing ISO 8601 dates can be specified. An unquoted symbol is -#' expected. +#' @param date A variable providing a date. A date or a datetime can be +#' specified. An unquoted symbol is expected. #' -#' @param date_imputation A string defining the date imputation for `date`. -#' See `date_imputation` parameter of `impute_dtc()` for valid values. +#' @param date_imputation *Deprecated*, please use `derive_vars_dtm()` to +#' convert DTC variables to datetime variables in the dataset. #' -#' @param time_imputation A string defining the time imputation for `date`. -#' See `time_imputation` parameter of `impute_dtc()` for valid values. +#' @param time_imputation *Deprecated*, please use `derive_vars_dtm()` to +#' convert DTC variables to datetime variables in the dataset. #' -#' @param preserve Should day be preserved if month is imputed for `date`. -#' See `preserve` parameter of `impute_dtc()` for details. +#' @param preserve *Deprecated*, please use `derive_vars_dtm()` to convert DTC +#' variables to datetime variables in the dataset. #' #' @param traceability_vars A named list returned by `vars()` defining the #' traceability variables, e.g. `vars(LALVDOM = "AE", LALVSEQ = AESEQ, LALVVAR @@ -531,6 +455,7 @@ lstalvdt_source <- function(dataset_name, #' #' @seealso [derive_var_extreme_dtm()], [derive_var_extreme_dt()] #' +#' @family source_specifications #' @keywords source_specifications #' #' @export @@ -539,23 +464,44 @@ lstalvdt_source <- function(dataset_name, date_source <- function(dataset_name, filter = NULL, date, - date_imputation = NULL, - time_imputation = NULL, - preserve = FALSE, + date_imputation = deprecated(), + time_imputation = deprecated(), + preserve = deprecated(), traceability_vars = NULL) { - if (!is.null(date_imputation)) { - assert_that(is_valid_date_entry(date_imputation)) + if (!missing(date_imputation)) { + deprecate_stop( + "0.8.0", + "date_source(date_imputation = )", + details = paste0( + "Please use `derive_vars_dtm()` to convert DTC variables", + " to datetime variables in the dataset." + ) + ) } - if (!is.null(time_imputation)) { - assert_that(is_valid_time_entry(time_imputation)) + if (!missing(time_imputation)) { + deprecate_stop( + "0.8.0", + "date_source(time_imputation = )", + details = paste0( + "Please use `derive_vars_dtm()` to convert DTC variables", + " to datetime variables in the dataset." + ) + ) + } + if (!missing(preserve)) { + deprecate_stop( + "0.8.0", + "date_source(preserve = )", + details = paste0( + "Please use `derive_vars_dtm()` to convert DTC variables", + " to datetime variables in the dataset." + ) + ) } out <- list( dataset_name = assert_character_scalar(dataset_name), filter = assert_filter_cond(enquo(filter), optional = TRUE), date = assert_symbol(enquo(date)), - date_imputation = date_imputation, - time_imputation = time_imputation, - preserve = preserve, traceability_vars = assert_varval_list(traceability_vars, optional = TRUE) ) class(out) <- c("date_source", "list") diff --git a/R/derive_var_extreme_flag.R b/R/derive_var_extreme_flag.R index 2855d89c89..875c7e269c 100644 --- a/R/derive_var_extreme_flag.R +++ b/R/derive_var_extreme_flag.R @@ -57,7 +57,8 @@ #' #' @return The input dataset with the new flag variable added #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -198,6 +199,10 @@ derive_var_extreme_flag <- function(dataset, mode, filter = deprecated(), check_type = "warning") { + if (!missing(filter)) { + deprecate_stop("0.7.0", "derive_var_extreme_flag(filter = )", "restrict_derivation(filter = )") + } + new_var <- assert_symbol(enquo(new_var)) assert_vars(by_vars) assert_order_vars(order) @@ -208,28 +213,6 @@ derive_var_extreme_flag <- function(dataset, values = c("none", "warning", "error"), case_sensitive = FALSE ) - if (!missing(filter)) { - warn(paste( - "`filter` is deprecated as of admiral 0.7.0.", - "Please use `restrict_derivation()` instead (see examples).", - sep = "\n" - )) - - filter <- assert_filter_cond(enquo(filter), optional = TRUE) - return( - restrict_derivation( - dataset, - derivation = derive_var_extreme_flag, - args = params( - by_vars = by_vars, - order = order, - new_var = !!new_var, - mode = mode - ), - filter = !!filter - ) - ) - } # Create flag data <- dataset %>% @@ -292,7 +275,8 @@ derive_var_extreme_flag <- function(dataset, #' #' @return The input dataset with the new flag variable added. #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -365,6 +349,9 @@ derive_var_worst_flag <- function(dataset, worst_low, filter = deprecated(), check_type = "warning") { + if (!missing(filter)) { + deprecate_stop("0.7.0", "derive_var_worst_flag(filter = )", "restrict_derivation(filter = )") + } # perform argument checks new_var <- assert_symbol(enquo(new_var)) @@ -378,32 +365,6 @@ derive_var_worst_flag <- function(dataset, ) assert_character_vector(worst_high) assert_character_vector(worst_low) - if (!missing(filter)) { - filter <- assert_filter_cond(enquo(filter), optional = TRUE) - return( - bind_rows( - derive_var_extreme_flag( - dataset = filter(dataset, !!param_var %in% worst_low), - by_vars = by_vars, - order = quo_c(analysis_var, order), - new_var = !!new_var, - mode = "first", - filter = !!filter, - check_type = check_type - ), - derive_var_extreme_flag( - dataset = filter(dataset, !!param_var %in% worst_high), - by_vars = by_vars, - order = quo_c(quo(desc(!!quo_get_expr(analysis_var))), order), - new_var = !!new_var, - mode = "first", - filter = !!filter, - check_type = check_type - ), - filter(dataset, !(!!param_var %in% c(worst_low, worst_high))) - ) - ) - } # additional checks for worstflag - parameters overlap if (length(intersect(worst_high, worst_low)) > 0) { diff --git a/R/derive_var_last_dose_amt.R b/R/derive_var_last_dose_amt.R index d3e09d5f85..df77df5ae8 100644 --- a/R/derive_var_last_dose_amt.R +++ b/R/derive_var_last_dose_amt.R @@ -18,7 +18,8 @@ #' #' @author Annie Yang #' -#' @keywords adam derivation +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -30,33 +31,48 @@ #' data(admiral_ae) #' data(ex_single) #' -#' admiral_ae %>% +#' ex_single <- derive_vars_dtm( +#' head(ex_single, 100), +#' dtc = EXENDTC, +#' new_vars_prefix = "EXEN", +#' flag_imputation = "none" +#' ) +#' +#' adae <- admiral_ae %>% #' head(100) %>% +#' derive_vars_dtm( +#' dtc = AESTDTC, +#' new_vars_prefix = "AST", +#' highest_imputation = "M" +#' ) +#' +#' adae %>% #' derive_var_last_dose_amt( -#' head(ex_single, 100), +#' dataset_ex = ex_single, #' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, -#' dose_date = EXENDTC, -#' analysis_date = AESTDTC, -#' single_dose_condition = (EXSTDTC == EXENDTC), +#' !is.na(EXENDTM), +#' dose_date = EXENDTM, +#' analysis_date = ASTDTM, #' new_var = LDOSE, #' dose_var = EXDOSE #' ) %>% #' select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSE) #' #' # or with traceability variables -#' admiral_ae %>% -#' head(100) %>% +#' adae %>% #' derive_var_last_dose_amt( -#' head(ex_single, 100), +#' dataset_ex = ex_single, #' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, -#' dose_date = EXENDTC, -#' analysis_date = AESTDTC, -#' single_dose_condition = (EXSTDTC == EXENDTC), +#' !is.na(EXENDTM), +#' dose_date = EXENDTM, +#' analysis_date = ASTDTM, #' new_var = LDOSE, #' dose_var = EXDOSE, -#' traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXDOSE") +#' traceability_vars = vars( +#' LDOSEDOM = "EX", +#' LDOSESEQ = EXSEQ, +#' LDOSEVAR = "EXDOSE" +#' ) #' ) %>% #' select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDOM, LDOSESEQ, LDOSEVAR, LDOSE) derive_var_last_dose_amt <- function(dataset, diff --git a/R/derive_var_last_dose_date.R b/R/derive_var_last_dose_date.R index 4bc9b280e3..87ac20a774 100644 --- a/R/derive_var_last_dose_date.R +++ b/R/derive_var_last_dose_date.R @@ -21,7 +21,8 @@ #' #' @author Ben Straub #' -#' @keywords adam derivation +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -33,17 +34,30 @@ #' data(admiral_ae) #' data(ex_single) #' -#' admiral_ae %>% +#' ex_single <- derive_vars_dtm( +#' head(ex_single, 100), +#' dtc = EXENDTC, +#' new_vars_prefix = "EXEN", +#' flag_imputation = "none" +#' ) +#' +#' adae <- admiral_ae %>% #' head(100) %>% +#' derive_vars_dtm( +#' dtc = AESTDTC, +#' new_vars_prefix = "AST", +#' highest_imputation = "M" +#' ) +#' +#' adae %>% #' derive_var_last_dose_date( -#' head(ex_single, 100), +#' dataset_ex = ex_single, #' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, -#' dose_date = EXENDTC, -#' analysis_date = AESTDTC, -#' single_dose_condition = (EXSTDTC == EXENDTC), +#' !is.na(EXENDTM), +#' dose_date = EXENDTM, +#' analysis_date = ASTDTM, #' new_var = LDOSEDTM, -#' traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXDOSE") +#' traceability_vars = vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXDOSE") #' ) %>% #' select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDOM, LDOSESEQ, LDOSEVAR, LDOSEDTM) derive_var_last_dose_date <- function(dataset, diff --git a/R/derive_var_last_dose_grp.R b/R/derive_var_last_dose_grp.R index 3f0a81c15c..3b79c5a051 100644 --- a/R/derive_var_last_dose_grp.R +++ b/R/derive_var_last_dose_grp.R @@ -29,7 +29,8 @@ #' #' @author Ben Straub #' -#' @keywords adam derivation +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -41,23 +42,36 @@ #' data(admiral_ae) #' data(ex_single) #' -#' admiral_ae %>% +#' ex_single <- derive_vars_dtm( +#' head(ex_single, 100), +#' dtc = EXSTDTC, +#' new_vars_prefix = "EXST", +#' flag_imputation = "none" +#' ) +#' +#' adae <- admiral_ae %>% #' head(100) %>% +#' derive_vars_dtm( +#' dtc = AESTDTC, +#' new_vars_prefix = "AST", +#' highest_imputation = "M" +#' ) +#' +#' adae %>% #' derive_var_last_dose_grp( -#' head(ex_single, 100), +#' dataset_ex = ex_single, #' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, +#' !is.na(EXSTDTM), #' by_vars = vars(STUDYID, USUBJID), -#' dose_date = EXSTDTC, +#' dose_date = EXSTDTM, #' new_var = LDGRP, #' grp_brks = c(0, 20, 40, 60), #' grp_lbls = c("Low", "Medium", "High"), #' include_lowest = TRUE, #' right = TRUE, #' dose_var = EXDOSE, -#' analysis_date = AESTDTC, -#' single_dose_condition = (EXSTDTC == EXENDTC), -#' traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") +#' analysis_date = ASTDTM, +#' traceability_vars = vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") #' ) %>% #' select(USUBJID, LDGRP, LDOSEDOM, LDOSESEQ, LDOSEVAR) derive_var_last_dose_grp <- function(dataset, diff --git a/R/derive_var_obs_number.R b/R/derive_var_obs_number.R index af5bb68282..470dbc0dde 100644 --- a/R/derive_var_obs_number.R +++ b/R/derive_var_obs_number.R @@ -44,7 +44,8 @@ #' @return A dataset containing all observations and variables of the input #' dataset and additionally the variable specified by the `new_var` parameter. #' -#' @keywords adam derivation +#' @family der_gen +#' @keywords der_gen #' #' @export #' diff --git a/R/derive_var_ontrtfl.R b/R/derive_var_ontrtfl.R index 1475af8360..64c9afc23d 100644 --- a/R/derive_var_ontrtfl.R +++ b/R/derive_var_ontrtfl.R @@ -84,7 +84,8 @@ #' #' @author Alice Ehmann, Teckla Akinyi #' -#' @keywords bds derivation +#' @family der_bds_findings +#' @keywords der_bds_findings #' #' @return The input dataset with an additional column named `ONTRTFL` with a #' value of `"Y"` or `NA` diff --git a/R/derive_var_pchg.R b/R/derive_var_pchg.R index 0b6c6aa09d..00eb5240ce 100644 --- a/R/derive_var_pchg.R +++ b/R/derive_var_pchg.R @@ -13,7 +13,8 @@ #' @author Thomas Neitmann #' #' @return The input dataset with an additional column named `PCHG` -#' @keywords bds derivation +#' @family der_bds_findings +#' @keywords der_bds_findings #' @export #' #' @seealso [derive_var_chg()] diff --git a/R/derive_var_shift.R b/R/derive_var_shift.R index ede526de65..7fc4a1148e 100644 --- a/R/derive_var_shift.R +++ b/R/derive_var_shift.R @@ -30,7 +30,8 @@ #' #' @return The input dataset with the character shift variable added #' -#' @keywords adam bds adlb derivation +#' @family der_bds_findings +#' @keywords der_bds_findings #' #' @export #' diff --git a/R/derive_var_trtdurd.R b/R/derive_var_trtdurd.R index 3d9979a4eb..b1a7b0f295 100644 --- a/R/derive_var_trtdurd.R +++ b/R/derive_var_trtdurd.R @@ -32,7 +32,9 @@ #' #' @return The input dataset with `TRTDURD` added #' -#' @keywords adsl timing derivation +#' @family der_date_time +#' +#' @keywords der_gen der_date_time #' #' @export #' diff --git a/R/derive_var_trtedtm.R b/R/derive_var_trtedtm.R deleted file mode 100644 index 0b9309a230..0000000000 --- a/R/derive_var_trtedtm.R +++ /dev/null @@ -1,67 +0,0 @@ -#' Derive Datetime of Last Exposure to Treatment -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' This function is *deprecated*, please use `derive_vars_merged_dtm()` instead. -#' -#' Derives datetime of last exposure to treatment (`TRTEDTM`) -#' -#' @param dataset Input dataset -#' -#' The variables specified by the `by_vars` parameter are expected. -#' -#' @param dataset_ex `ex` dataset -#' -#' The variables `EXENDTC`, `EXSEQ`, and those specified by the `filter_ex` -#' parameter are expected. -#' -#' @param filter_ex Filter condition for the ex dataset -#' -#' Only observations of the ex dataset which fulfill the specified condition -#' are considered for the treatment start date. -#' -#' Default: `EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, 'PLACEBO')) & nchar(EXENDTC) >= 10` -#' -#' Permitted Values: logical expression -#' -#' @param subject_keys Variables to uniquely identify a subject -#' -#' A list of quosures where the expressions are symbols as returned by -#' `vars()` is expected. -#' -#' @details For each group (with respect to the variables specified for the -#' `by_vars` parameter) the first observation (with respect to the order -#' specified for the `order` parameter) is included in the output dataset. -#' -#' @author Stefan Bundfuss -#' -#' @return The input dataset with `TRTEDTM` variable added -#' -#' @keywords adsl timing derivation -#' -#' @export -#' -derive_var_trtedtm <- function(dataset, - dataset_ex, - filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, "PLACEBO"))) & nchar(EXENDTC) >= 10, # nolint - subject_keys = vars(STUDYID, USUBJID)) { - assert_data_frame(dataset, subject_keys) - assert_data_frame(dataset_ex, required_vars = quo_c(subject_keys, vars(EXENDTC, EXSEQ))) - filter_ex <- assert_filter_cond(enquo(filter_ex), optional = TRUE) - deprecate_warn("0.7.0", "derive_var_trtedtm()", "derive_vars_merged_dtm()") - - derive_vars_merged_dtm( - dataset, - dataset_add = dataset_ex, - filter_add = !!filter_ex, - new_vars_prefix = "TRTE", - dtc = EXENDTC, - date_imputation = "last", - time_imputation = "last", - flag_imputation = "none", - order = vars(TRTEDTM, EXSEQ), - mode = "last", - by_vars = subject_keys - ) -} diff --git a/R/derive_var_trtsdtm.R b/R/derive_var_trtsdtm.R deleted file mode 100644 index 8b2201bbe9..0000000000 --- a/R/derive_var_trtsdtm.R +++ /dev/null @@ -1,67 +0,0 @@ -#' Derive Datetime of First Exposure to Treatment -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' This function is *deprecated*, please use `derive_vars_merged_dtm()` instead. -#' -#' Derives datetime of first exposure to treatment (`TRTSDTM`) -#' -#' @param dataset Input dataset -#' -#' The variables specified by the `by_vars` parameter are expected. -#' -#' @param dataset_ex `ex` dataset -#' -#' The variables `EXSTDTC`, `EXSEQ`, and those specified by the `filter_ex` -#' parameter are expected. -#' -#' @param filter_ex Filter condition for the ex dataset -#' -#' Only observations of the ex dataset which fulfill the specified condition -#' are considered for the treatment start date. -#' -#' Default: `EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, 'PLACEBO')) & nchar(EXSTDTC) >= 10` -#' -#' Permitted Values: logical expression -#' -#' @param subject_keys Variables to uniquely identify a subject -#' -#' A list of quosures where the expressions are symbols as returned by -#' `vars()` is expected. -#' -#' @details For each group (with respect to the variables specified for the -#' `by_vars` parameter) the first observation (with respect to the order -#' specified for the `order` parameter) is included in the output dataset. -#' -#' @author Stefan Bundfuss -#' -#' @return The input dataset with `TRTSDTM` variable added -#' -#' @export -#' -#' @keywords adsl timing derivation -#' -derive_var_trtsdtm <- function(dataset, - dataset_ex, - filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, "PLACEBO"))) & nchar(EXSTDTC) >= 10, # nolint - subject_keys = vars(STUDYID, USUBJID)) { - assert_data_frame(dataset, subject_keys) - assert_data_frame(dataset_ex, required_vars = quo_c(subject_keys, vars(EXSTDTC, EXSEQ))) - filter_ex <- assert_filter_cond(enquo(filter_ex), optional = TRUE) - deprecate_warn("0.7.0", "derive_var_trtsdtm()", "derive_vars_merged_dtm()") - - derive_vars_merged_dtm( - dataset, - dataset_add = dataset_ex, - filter_add = !!filter_ex, - new_vars_prefix = "TRTS", - dtc = EXSTDTC, - date_imputation = "first", - time_imputation = "first", - flag_imputation = "none", - order = vars(TRTSDTM, EXSEQ), - mode = "first", - by_vars = subject_keys - ) -} diff --git a/R/derive_vars_aage.R b/R/derive_vars_aage.R index ce87c53fbc..069d637c96 100644 --- a/R/derive_vars_aage.R +++ b/R/derive_vars_aage.R @@ -34,12 +34,18 @@ #' Permitted Values: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds' #' #' @details The age is derived as the integer part of the duration from start to -#' end date in the specified unit. +#' end date in the specified unit. When 'years' or 'months' are specified in the `out_unit` +#' parameter, because of the underlying `lubridate::time_length()` function that is used +#' here, results are calculated based on the actual calendar length of months or years +#' rather than assuming equal days every month (30.4375 days) or every year (365.25 days). #' #' @author Stefan Bundfuss #' #' @return The input dataset with ``AAGE`` and ``AAGEU`` added #' +#' @family der_adsl +#' @keywords der_adsl +#' #' @export #' #' @seealso [derive_vars_duration()] @@ -93,6 +99,9 @@ derive_vars_aage <- function(dataset, #' #' @param new_var New AGE variable to be created in years. #' +#' @family der_adsl +#' @keywords der_adsl +#' #' @author Michael Thorpe #' #' @return The input dataset with new_var parameter added in years. @@ -197,22 +206,20 @@ derive_var_age_years <- function(dataset, age_var, age_unit = NULL, new_var) { #' Derive Age Groups #' -#' Functions for deriving standardized age groups. +#' @description +#' `r lifecycle::badge("deprecated")` #' -#' @param dataset Input dataset. -#' @param age_var AGE variable. -#' @param age_unit AGE unit variable. +#' These functions are *deprecated*. #' -#' The AGE unit variable is used to convert AGE to 'years' so that grouping can occur. -#' This is only used when the age_var variable does not have a corresponding unit in the dataset. +#' @param dataset Input dataset #' -#' Default: NULL +#' @param age_var AGE variable #' -#' Permitted Values: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds' +#' @param age_unit AGE unit variable #' -#' @param new_var New variable to be created. +#' @param new_var New variable to create inside `dataset` #' -#' @return `dataset` with new column `new_var` of class factor. +#' @keywords deprecated #' #' @author Ondrej Slama #' @@ -221,33 +228,12 @@ NULL #' @rdname derive_var_agegr_fda #' -#' @export -#' -#' @details -#' `derive_var_agegr_fda()` derives age groups according to FDA guidance. `age_var` -#' will be split in categories: <18, 18-64, >=65. -#' -#' @examples -#' library(dplyr, warn.conflicts = FALSE) -#' library(admiral.test) -#' data(admiral_dm) -#' -#' admiral_dm %>% -#' derive_var_agegr_fda(age_var = AGE, new_var = AGEGR1) %>% -#' select(SUBJID, AGE, AGEGR1) -#' -#' data <- tibble::tribble( -#' ~BRTHDT, ~RANDDT, -#' lubridate::ymd("1984-09-06"), lubridate::ymd("2020-02-24") -#' ) +#' @keywords deprecated #' -#' data %>% -#' derive_vars_aage(unit = "months") %>% -#' derive_var_agegr_fda(AAGE, age_unit = NULL, AGEGR1) -#' -#' data.frame(AGE = 1:100) %>% -#' derive_var_agegr_fda(age_var = AGE, age_unit = "years", new_var = AGEGR1) +#' @export derive_var_agegr_fda <- function(dataset, age_var, age_unit = NULL, new_var) { + deprecate_warn("0.8.0", "derive_var_agegr_ema()") + age_var <- assert_symbol(enquo(age_var)) new_var <- assert_symbol(enquo(new_var)) warn_if_vars_exist(dataset, quo_text(new_var)) @@ -274,29 +260,12 @@ derive_var_agegr_fda <- function(dataset, age_var, age_unit = NULL, new_var) { #' @rdname derive_var_agegr_fda #' -#' @export +#' @keywords deprecated #' -#' @details -#' `derive_var_agegr_ema()` derives age groups according to EMA guidance. -#' `age_var` will be split into categories: 0-27 days (Newborns), 28 days to -#' 23 months (Infants and Toddlers), 2-11 (Children), 12-17 (Adolescents), 18-64, -#' 65-84, >=85. -#' -#' @examples -#' library(dplyr, warn.conflicts = FALSE) -#' library(admiral.test) -#' data(admiral_dm) -#' -#' admiral_dm %>% -#' derive_var_agegr_ema(age_var = AGE, new_var = AGEGR1) %>% -#' select(SUBJID, AGE, AGEGR1) -#' -#' data.frame(AGE = 1:100) %>% -#' derive_var_agegr_ema(age_var = AGE, age_unit = "years", new_var = AGEGR1) -#' -#' data.frame(AGE = 1:20) %>% -#' derive_var_agegr_ema(age_var = AGE, age_unit = "years", new_var = AGEGR1) +#' @export derive_var_agegr_ema <- function(dataset, age_var, age_unit = NULL, new_var) { + deprecate_warn("0.8.0", "derive_var_agegr_ema()") + age_var <- assert_symbol(enquo(age_var)) new_var <- assert_symbol(enquo(new_var)) warn_if_vars_exist(dataset, quo_text(new_var)) diff --git a/R/derive_vars_disposition_reason.R b/R/derive_vars_disposition_reason.R index 2679790481..fefe405922 100644 --- a/R/derive_vars_disposition_reason.R +++ b/R/derive_vars_disposition_reason.R @@ -17,7 +17,8 @@ #' #' @author Samia Kabi #' @export -#' @keywords user_utility adsl computation +#' @family utils_fmt +#' @keywords utils_fmt #' @seealso [derive_vars_disposition_reason()] #' @examples #' library(dplyr, warn.conflicts = FALSE) @@ -130,8 +131,9 @@ format_reason_default <- function(reason, reason_spe = NULL) { #' The details associated with the reason for discontinuation are derived based on #' `reason_var_spe` (e.g. `DSTERM`), `reason_var` and `format_new_vars`. #' +#' @family der_adsl #' @seealso [format_reason_default()] -#' @keywords adsl +#' @keywords der_adsl #' #' @author Samia Kabi #' @@ -185,7 +187,7 @@ derive_vars_disposition_reason <- function(dataset, reason_var <- assert_symbol(enquo(reason_var)) new_var_spe <- assert_symbol(enquo(new_var_spe), optional = T) reason_var_spe <- assert_symbol(enquo(reason_var_spe), optional = T) - assert_that(is.function(format_new_vars)) + assert_s3_class(format_new_vars, "function") filter_ds <- assert_filter_cond(enquo(filter_ds)) assert_vars(subject_keys) assert_data_frame(dataset, required_vars = subject_keys) diff --git a/R/derive_vars_dtm_to_dt.R b/R/derive_vars_dtm_to_dt.R index 28b8a10954..2cfe50d8eb 100644 --- a/R/derive_vars_dtm_to_dt.R +++ b/R/derive_vars_dtm_to_dt.R @@ -13,7 +13,9 @@ #' A data frame containing the input dataset with the corresponding date (`--DT`) #' variable(s) of all datetime variables (`--DTM`) specified in `source_vars.` #' -#' @keywords adam timing +#' @family der_date_time +#' +#' @keywords der_gen der_date_time #' #' @export #' diff --git a/R/derive_vars_dtm_to_tm.R b/R/derive_vars_dtm_to_tm.R index 4cdd8e7fa4..7140867205 100644 --- a/R/derive_vars_dtm_to_tm.R +++ b/R/derive_vars_dtm_to_tm.R @@ -19,7 +19,9 @@ #' (`--TM`) variable(s) of all datetime variables (`--DTM`) specified in #' `source_vars` with the correct name. #' -#' @keywords adam timing +#' @family der_date_time +#' +#' @keywords der_gen der_date_time #' #' @export #' diff --git a/R/derive_vars_duration.R b/R/derive_vars_duration.R index 5d0260a81b..e22db64b29 100644 --- a/R/derive_vars_duration.R +++ b/R/derive_vars_duration.R @@ -79,7 +79,8 @@ #' #' @return The input dataset with the duration and unit variable added #' -#' @keywords adam timing derivation +#' @family der_date_time +#' @keywords der_gen der_date_time #' #' @export #' @@ -89,12 +90,13 @@ #' library(lubridate) #' library(tibble) #' +#' # Derive age in years #' data <- tribble( -#' ~BRTHDT, ~RANDDT, -#' ymd("1984-09-06"), ymd("2020-02-24"), -#' ymd("1985-01-01"), NA, -#' NA, ymd("2021-03-10"), -#' NA, NA +#' ~USUBJID, ~BRTHDT, ~RANDDT, +#' "P01", ymd("1984-09-06"), ymd("2020-02-24"), +#' "P02", ymd("1985-01-01"), NA, +#' "P03", NA, ymd("2021-03-10"), +#' "P04", NA, NA #' ) #' #' derive_vars_duration(data, @@ -106,6 +108,42 @@ #' add_one = FALSE, #' trunc_out = TRUE #' ) +#' +#' # Derive adverse event duration in days +#' data <- tribble( +#' ~USUBJID, ~ASTDT, ~AENDT, +#' "P01", ymd("2021-03-05"), ymd("2021-03-02"), +#' "P02", ymd("2019-09-18"), ymd("2019-09-18"), +#' "P03", ymd("1985-01-01"), NA, +#' "P04", NA, NA +#' ) +#' +#' derive_vars_duration(data, +#' new_var = ADURN, +#' new_var_unit = ADURU, +#' start_date = ASTDT, +#' end_date = AENDT, +#' out_unit = "days" +#' ) +#' +#' # Derive adverse event duration in minutes +#' data <- tribble( +#' ~USUBJID, ~ADTM, ~TRTSDTM, +#' "P01", ymd_hms("2019-08-09T04:30:56"), ymd_hms("2019-08-09T05:00:00"), +#' "P02", ymd_hms("2019-11-11T10:30:00"), ymd_hms("2019-11-11T11:30:00"), +#' "P03", ymd_hms("2019-11-11T00:00:00"), ymd_hms("2019-11-11T04:00:00"), +#' "P04", NA, ymd_hms("2019-11-11T12:34:56"), +#' ) +#' +#' derive_vars_duration(data, +#' new_var = ADURN, +#' new_var_unit = ADURU, +#' start_date = ADTM, +#' end_date = TRTSDTM, +#' in_unit = "minutes", +#' out_unit = "minutes", +#' add_one = FALSE +#' ) derive_vars_duration <- function(dataset, new_var, new_var_unit = NULL, diff --git a/R/derive_vars_dy.R b/R/derive_vars_dy.R index 30bb73e7c3..fad44ed02f 100644 --- a/R/derive_vars_dy.R +++ b/R/derive_vars_dy.R @@ -34,7 +34,8 @@ #' @return The input dataset with `--DY` corresponding to the `--DTM` or `--DT` #' source variable(s) added #' -#' @keywords derivation ADaM timing +#' @family der_date_time +#' @keywords der_gen der_date_time #' #' @export #' diff --git a/R/derive_vars_last_dose.R b/R/derive_vars_last_dose.R index 66e2169d2f..49b9d2b9d0 100644 --- a/R/derive_vars_last_dose.R +++ b/R/derive_vars_last_dose.R @@ -40,12 +40,9 @@ #' These can be either strings or symbols referring to existing variables. #' #' @details -#' All date (date-time) variables can be characters in standard ISO format or -#' of date / date-time class. -#' For ISO format, see [`impute_dtc`] - parameter `dtc` for further details. #' When doing date comparison to identify last dose, date-time imputations are done as follows: -#' * `dose_date`: no date imputation, time imputation to `00:00:00` if time is missing. -#' * `analysis_date`: no date imputation, time imputation to `23:59:59` if time is missing. +#' * `dose_date`: time is imputed to `00:00:00` if the variable is a date variable +#' * `analysis_date`: time is imputed to `23:59:59` if the variable is a date variable #' #' The last dose records are identified as follows: #' @@ -77,7 +74,11 @@ #' #' @author Ondrej Slama, Annie Yang #' -#' @keywords adam derivation user_utility +#' @family der_gen +#' @keywords der_gen +#' +#' @seealso [derive_var_last_dose_amt()], [derive_var_last_dose_date()], +#' [derive_var_last_dose_grp()], [create_single_dose_dataset()] #' #' @export #' @@ -87,30 +88,43 @@ #' data(admiral_ae) #' data(ex_single) #' -#' admiral_ae %>% +#' # create datetime variables in input datasets +#' ex_single <- derive_vars_dtm( +#' head(ex_single, 100), +#' dtc = EXENDTC, +#' new_vars_prefix = "EXEN", +#' flag_imputation = "none" +#' ) +#' +#' adae <- admiral_ae %>% #' head(100) %>% +#' derive_vars_dtm( +#' dtc = AESTDTC, +#' new_vars_prefix = "AST", +#' highest_imputation = "M" +#' ) +#' +#' # add last dose vars +#' adae %>% #' derive_vars_last_dose( -#' head(ex_single, 100), +#' dataset_ex = ex_single, #' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, +#' !is.na(EXENDTM), #' new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDTC, VISIT), -#' dose_date = EXENDTC, -#' analysis_date = AESTDTC, -#' single_dose_condition = (EXSTDTC == EXENDTC) +#' dose_date = EXENDTM, +#' analysis_date = ASTDTM #' ) %>% #' select(STUDYID, USUBJID, AESEQ, AESTDTC, EXDOSE, EXTRT, EXENDTC, EXSEQ, VISIT) #' #' # or with traceability variables -#' admiral_ae %>% -#' head(100) %>% +#' adae %>% #' derive_vars_last_dose( -#' head(ex_single, 100), +#' dataset_ex = ex_single, #' filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & -#' nchar(EXENDTC) >= 10, +#' !is.na(EXENDTM), #' new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDTC, VISIT), -#' dose_date = EXENDTC, -#' analysis_date = AESTDTC, -#' single_dose_condition = (EXSTDTC == EXENDTC), +#' dose_date = EXENDTM, +#' analysis_date = ASTDTM, #' traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") #' ) %>% #' select(STUDYID, USUBJID, AESEQ, AESTDTC, EXDOSE, EXTRT, EXENDTC, LDOSEDOM, LDOSESEQ, LDOSEVAR) @@ -135,10 +149,20 @@ derive_vars_last_dose <- function(dataset, assert_data_frame(dataset, quo_c(by_vars, analysis_date)) if (as_name(dose_date) %in% names(new_vars)) { required_vars <- quo_c(by_vars, new_vars, get_source_vars(traceability_vars)) + dose_date_res <- new_vars[[as_name(dose_date)]] } else { required_vars <- quo_c(by_vars, dose_date, new_vars, get_source_vars(traceability_vars)) + dose_date_res <- dose_date } assert_data_frame(dataset_ex, required_vars) + assert_date_var( + dataset = dataset, + var = !!analysis_date + ) + assert_date_var( + dataset = dataset_ex, + var = !!dose_date_res + ) # vars converted to string by_vars_str <- vars2chr(by_vars) @@ -165,7 +189,6 @@ derive_vars_last_dose <- function(dataset, "Multiple doses exist for the same `dose_date`. Update `dose_id` to identify unique doses." ) - # filter EX based on user-specified condition if (!is.null(quo_get_expr(filter_ex))) { dataset_ex <- dataset_ex %>% @@ -211,8 +234,7 @@ derive_vars_last_dose <- function(dataset, mutate( tmp_analysis_date = convert_date_to_dtm( dt = !!analysis_date, - date_imputation = NULL, - time_imputation = "23:59:59" + time_imputation = "last" ) ) @@ -220,9 +242,7 @@ derive_vars_last_dose <- function(dataset, dataset_ex <- dataset_ex %>% mutate( tmp_dose_date = convert_date_to_dtm( - dt = !!dose_date, - date_imputation = NULL, - time_imputation = "00:00:00" + dt = !!dose_date ) ) diff --git a/R/derive_vars_query.R b/R/derive_vars_query.R index 403736e3df..a86dc8836a 100644 --- a/R/derive_vars_query.R +++ b/R/derive_vars_query.R @@ -1,6 +1,16 @@ #' Derive Query Variables #' -#' @details For each unique element in `VAR_PREFIX`, the corresponding "NAM" +#' @details This function can be used to derive CDISC variables such as +#' `SMQzzNAM`, `SMQzzCD`, `SMQzzSC`, `SMQzzSCN`, and `CQzzNAM` in ADAE and +#' ADMH, and variables such as `SDGzzNAM`, `SDGzzCD`, and `SDGzzSC` in ADCM. +#' An example usage of this function can be found in the +#' [OCCDS vignette](../articles/occds.html). +#' +#' A query dataset is expected as an input to this function. See the +#' [Queries Dataset Documentation vignette](../articles/queries_dataset.html) +#' for descriptions, or call `data("queries")` for an example of a query dataset. +#' +#' For each unique element in `VAR_PREFIX`, the corresponding "NAM" #' variable will be created. For each unique `VAR_PREFIX`, if `QUERY_ID` is #' not "" or NA, then the corresponding "CD" variable is created; similarly, #' if `QUERY_SCOPE` is not "" or NA, then the corresponding "SC" variable will @@ -30,7 +40,8 @@ #' #' @return The input dataset with query variables derived. #' -#' @keywords adae adcm derivation +#' @family der_occds +#' @keywords der_occds #' #' @seealso [create_query_data()] [assert_valid_queries()] #' @@ -200,7 +211,8 @@ derive_vars_query <- function(dataset, dataset_queries) { #' #' @author Shimeng Huang, Ondrej Slama #' -#' @keywords assertion +#' @keywords source_specifications +#' @family source_specifications #' #' @export #' diff --git a/R/derive_vars_suppqual.R b/R/derive_vars_suppqual.R index 925bff1065..225d811f81 100644 --- a/R/derive_vars_suppqual.R +++ b/R/derive_vars_suppqual.R @@ -27,6 +27,8 @@ #' #' @author Vignesh Thanikachalam #' +#' @keywords deprecated +#' #' @export #' diff --git a/R/derive_vars_transposed.R b/R/derive_vars_transposed.R index 70dbdc3c71..61f1154058 100644 --- a/R/derive_vars_transposed.R +++ b/R/derive_vars_transposed.R @@ -30,7 +30,8 @@ #' #' @return The input dataset with transposed variables from `dataset_merge` added #' -#' @keywords derivation adam +#' @family der_gen +#' @keywords der_gen #' #' @export #' @@ -105,18 +106,24 @@ derive_vars_transposed <- function(dataset, #' #' @param dataset_facm FACM dataset #' -#' The variables specified by the `by_vars` parameter, `FAGRPID`, `FATESTCD` and -#' `FASTRESC` are required +#' The variables specified by the `by_vars` and `value_var` parameters, +#' `FAGRPID` and `FATESTCD` are required #' #' @param by_vars Keys used to merge `dataset_facm` with `dataset` #' #' *Permitted Values:* list of variables #' +#' @param value_var The variable of `dataset_facm` containing the values of the +#' transposed variables +#' +#' Default: `FASTRESC` +#' #' @author Thomas Neitmann #' #' @return The input dataset with ATC variables added #' -#' @keywords derivation adcm +#' @family der_occds +#' @keywords der_occds #' #' @export #' @@ -154,17 +161,19 @@ derive_vars_transposed <- function(dataset, #' derive_vars_atc(cm, facm) derive_vars_atc <- function(dataset, dataset_facm, - by_vars = vars(USUBJID, CMREFID = FAREFID)) { + by_vars = vars(USUBJID, CMREFID = FAREFID), + value_var = FASTRESC) { + value_var <- assert_symbol(enquo(value_var)) assert_vars(by_vars) assert_data_frame(dataset, required_vars = replace_values_by_names(by_vars)) - assert_data_frame(dataset_facm, required_vars = vars(!!!by_vars, FAGRPID, FATESTCD, FASTRESC)) + assert_data_frame(dataset_facm, required_vars = vars(!!!by_vars, !!value_var, FAGRPID, FATESTCD)) dataset %>% derive_vars_transposed( - select(dataset_facm, !!!unname(by_vars), FAGRPID, FATESTCD, FASTRESC), + select(dataset_facm, !!!unname(by_vars), !!value_var, FAGRPID, FATESTCD), by_vars = by_vars, key_var = FATESTCD, - value_var = FASTRESC, + value_var = !!value_var, filter = str_detect(FATESTCD, "^CMATC[1-4](CD)?$") ) %>% select(-starts_with("FA")) %>% diff --git a/R/dev_utils.R b/R/dev_utils.R deleted file mode 100644 index ec87a9b75c..0000000000 --- a/R/dev_utils.R +++ /dev/null @@ -1,455 +0,0 @@ -#' Enumerate Multiple Strings -#' -#' @param x A `character` vector -#' @param quote_fun Quoting function, defaults to `backquote`. -#' @param conjunction Character to be used in the message, defaults to "and". -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_enumerate -#' -#' @examples -#' admiral:::enumerate(c("STUDYID", "USUBJID", "PARAMCD")) -#' admiral:::enumerate(letters[1:6], quote_fun = admiral:::squote) -#' admiral:::enumerate( -#' c("date", "time", "both"), -#' quote_fun = admiral:::squote, -#' conjunction = "or" -#' ) -enumerate <- function(x, quote_fun = backquote, conjunction = "and") { - if (length(x) == 1L) { - quote_fun(x) - } else { - paste( - paste0(quote_fun(x[-length(x)]), collapse = ", "), - conjunction, - quote_fun(x[length(x)]) - ) - } -} - -#' Wrap a String in Backquotes -#' -#' @param x A `character` vector -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_backquote -#' -#' @examples -#' admiral:::backquote("USUBJID") -backquote <- function(x) { - paste0("`", x, "`") -} - -#' Wrap a String in Single Quotes -#' -#' @param x A `character` vector -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_squote -#' -#' @examples -#' admiral:::squote("foo") -squote <- function(x) { - paste0("'", x, "'") -} - -#' Wrap a String in Double Quotes -#' -#' Wrap a string in double quotes, e.g., for displaying character values in -#' messages. -#' -#' @param x A character vector -#' -#' @return If the input is `NULL`, the text `"NULL"` is returned. Otherwise, the -#' input in double quotes is returned. -#' -#' @author Stefan Bundfuss -#' -#' @keywords dev_utility -#' -#' @examples -#' admiral:::dquote("foo") -#' admiral:::dquote(NULL) -dquote <- function(x) { - if (is.null(x)) { - "NULL" - } else { - paste0("\"", x, "\"") - } -} - -#' Negated Value Matching -#' -#' Returns a `logical` vector indicating if there is *no* match of the -#' left operand in the right operand. -#' -#' @param x The values to be matched -#' @param table The values to be matched against -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_notin -#' -#' @examples -#' `%notin%` <- admiral:::`%notin%` -#' "a" %notin% c("b", "v", "k") -`%notin%` <- function(x, table) { # nolint - !(x %in% table) -} - -#' Helper Function to Convert Date (or Date-time) Objects to Characters of dtc Format -#' (-DTC type of variable) -#' -#' @param dtm date or date-time -#' -#' @return character -#' -#' @author Ondrej Slama -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_convert_dtm_to_dtc -#' -#' @examples -#' admiral:::convert_dtm_to_dtc(as.POSIXct(Sys.time())) -#' admiral:::convert_dtm_to_dtc(as.Date(Sys.time())) -convert_dtm_to_dtc <- function(dtm) { - stopifnot(lubridate::is.instant(dtm)) - format(dtm, "%Y-%m-%dT%H:%M:%S") -} - -#' Extract Argument Name from an Expression -#' -#' @param expr An expression created inside a function using `substitute()` -#' -#' @author Thomas Neitmann, Ondrej Slama -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_arg_name -#' -#' @examples -#' test_fun <- function(something) { -#' admiral:::arg_name(substitute(something)) -#' } -#' -#' inner_function <- function(x) x -#' test_fun2 <- function(something) { -#' admiral:::arg_name(substitute(inner_function(something))) -#' } -arg_name <- function(expr) { # nolint - if (length(expr) == 1L && is.symbol(expr)) { - deparse(expr) - } else if (length(expr) == 2L && - (expr[[1L]] == quote(enquo) || expr[[1L]] == quote(rlang::enquo)) && - is.symbol(expr[[2L]])) { - deparse(expr[[2L]]) - } else if (is.call(expr) && length(expr) >= 2 && is.symbol(expr[[2]])) { - deparse(expr[[2L]]) - } else if (is.call(expr) && length(expr) >= 2 && is.call(expr[[2]])) { - arg_name(expr[[2L]]) - } else { - abort(paste0("Could not extract argument name from `", deparse(expr), "`")) - } -} - -#' Extract All Symbols from a List of Quosures -#' -#' @param x An `R` object -#' @param side One of `"lhs"` (the default) or `"rhs"` -#' -#' @return A list of `quosures` -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_extract_vars -#' -#' @examples -#' admiral:::extract_vars(vars(STUDYID, USUBJID, desc(ADTM))) -extract_vars <- function(x, side = "lhs") { - if (is.null(x)) { - NULL - } else if (is.list(x)) { - do.call(quo_c, map(x, extract_vars, side)) - } else if (is_quosure(x)) { - env <- quo_get_env(x) - symbols <- syms(all.vars(quo_get_expr(x))) - map(symbols, ~ quo_set_env(quo(!!.x), env)) - } else if (is_formula(x)) { - funs <- list("lhs" = f_lhs, "rhs" = f_rhs) - assert_character_scalar(side, values = names(funs)) - quo_set_env( - quo(!!funs[[side]](x)), - env = attr(x, ".Environment") - ) - } else { - abort() - } -} - -#' Concatenate One or More Quosure(s) -#' -#' @param ... One or more objects of class `quosure` or `quosures` -#' -#' @return An object of class `quosures` -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_quo_c -#' -#' @examples -#' admiral:::quo_c(rlang::quo(USUBJID)) -#' admiral:::quo_c(rlang::quo(STUDYID), rlang::quo(USUBJID)) -#' admiral:::quo_c(vars(USUBJID, ADTM)) -#' admiral:::quo_c(rlang::quo(BASETYPE), vars(USUBJID, PARAM), rlang::quo(ADTM)) -quo_c <- function(...) { - inputs <- unlist(list(...), recursive = TRUE) - stopifnot(all(map_lgl(inputs, is_quosure))) - is_null <- map_lgl(inputs, quo_is_null) - rlang::as_quosures(inputs[!is_null]) -} - -#' What Kind of Object is This? -#' -#' Returns a string describing what kind of object the input is. -#' -#' @param x Any R object -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_what_is_it -#' -#' @examples -#' admiral:::what_is_it(mtcars) -#' admiral:::what_is_it(NA) -#' admiral:::what_is_it(TRUE) -#' admiral:::what_is_it(lm(hp ~ mpg, data = mtcars)) -#' admiral:::what_is_it(letters) -what_is_it <- function(x) { - if (is.null(x)) { - "`NULL`" - } else if (is.factor(x)) { - "a factor" - } else if (is.symbol(x)) { - "a symbol" - } else if (isS4(x)) { - sprintf("a S4 object of class '%s'", class(x)[1L]) - } else if (is.atomic(x) && length(x) == 1L) { - if (is.character(x)) { - paste0("`\"", x, "\"`") - } else { - paste0("`", x, "`") - } - } else if (is.atomic(x) || class(x)[1L] == "list") { - friendly_type_of(x) - } else if (is.data.frame(x)) { - "a data frame" - } else { - sprintf("an object of class '%s'", class(x)[1L]) - } -} - -#' Get Constant Variables -#' -#' @param dataset A data frame. -#' @param by_vars By variables -#' The groups defined by the by variables are considered separately. I.e., if -#' a variable is constant within each by group, it is returned. -#' -#' @param ignore_vars Variables to ignore -#' The specified variables are not considered, i.e., they are not returned -#' even if they are constant (unless they are included in the by variables). -#' -#' *Permitted Values:* A list of variable names or selector function calls -#' like `starts_with("EX")` -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_get_constant_vars -#' -#' @return Variable vector. -#' -#' @examples -#' library(admiral.test) -#' data(admiral_vs) -#' -#' admiral:::get_constant_vars(admiral_vs, by_vars = vars(USUBJID, VSTESTCD)) -#' -#' admiral:::get_constant_vars( -#' admiral_vs, -#' by_vars = vars(USUBJID, VSTESTCD), -#' ignore_vars = vars(DOMAIN, tidyselect::starts_with("VS")) -#' ) -get_constant_vars <- function(dataset, by_vars, ignore_vars = NULL) { - non_by_vars <- setdiff(names(dataset), vars2chr(by_vars)) - - if (!is.null(ignore_vars)) { - non_by_vars <- setdiff( - non_by_vars, - vars_select(non_by_vars, !!!ignore_vars) - ) - } - - # get unique values within each group by variables - unique_count <- dataset %>% - group_by(!!!by_vars) %>% - summarise_at(vars(!!non_by_vars), n_distinct) %>% - ungroup() %>% - select(!!!syms(non_by_vars)) - - # determine variables which are constant within each by group - constant_vars <- unique_count %>% - map_lgl(~ all(.x == 1)) %>% - which() %>% - names() %>% - syms() - - vars(!!!by_vars, !!!constant_vars) -} - -`%or%` <- function(lhs, rhs) { - tryCatch(lhs, error = function(e) rhs) -} - -is_named <- function(x) { - !is.null(names(x)) && all(names(x) != "") -} - -#' Replace Quosure Value with Name -#' -#' @param quosures A list of quosures -#' -#' @author Thomas Neitmann -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_replace_values_by_names -#' -#' @return A list of quosures -#' -#' @examples -#' admiral:::replace_values_by_names(vars(USUBJID, TEST = VSTESTCD)) -replace_values_by_names <- function(quosures) { - vars <- map2(quosures, names(quosures), function(q, n) { - if (n == "") { - return(q) - } - quo_set_env( - quo(!!as.symbol(n)), - quo_get_env(q) - ) - }) - structure(vars, class = "quosures", names = NULL) -} - -get_duplicates <- function(x) { - unique(x[duplicated(x)]) -} - -#' Extract Unit From Parameter Description -#' -#' Extract the unit of a parameter from a description like "Param (unit)". -#' -#' @param x A parameter description -#' -#' @export -#' -#' @keywords user_utility -#' -#' @examples -#' extract_unit("Height (cm)") -#' -#' extract_unit("Diastolic Blood Pressure (mmHg)") -extract_unit <- function(x) { - assert_character_vector(x) - - x %>% - str_extract("\\(.+\\)") %>% - str_remove_all("\\(|\\)") -} - -#' Checks if the argument equals the auto keyword -#' -#' @param arg argument to check -#' -#' @return `TRUE` if the argument equals the auto keyword, i.e., it is a quosure -#' of a symbol named auto. -#' -#' @author Stefan Bundfuss -#' -#' @keywords check -#' -#' @examples -#' -#' example_fun <- function(arg) { -#' arg <- rlang::enquo(arg) -#' if (admiral:::is_auto(arg)) { -#' "auto keyword was specified" -#' } else { -#' arg -#' } -#' } -#' -#' example_fun("Hello World!") -#' -#' example_fun(auto) -is_auto <- function(arg) { - is_quosure(arg) && quo_is_symbol(arg) && quo_get_expr(arg) == expr(auto) -} - -#' Get Source Variables from a List of Quosures -#' -#' @param quosures A list of quosures -#' -#' @author Stefan Bundfuss -#' -#' @keywords dev_utility -#' -#' @rdname dev_util_get_source_vars -#' -#' @return A list of quosures -#' -#' @examples -#' admiral:::get_source_vars(vars(USUBJID, AVISIT = VISIT, SRCDOM = "EX")) -get_source_vars <- function(quosures) { - quo_c(quosures)[lapply(quo_c(quosures), quo_is_symbol) == TRUE] -} - -#' Turn a Quosure into a String -#' -#' @details -#' This function is missing in earlier version of {rlang} which is why we re- -#' implment it here. -#' -#' @noRd -as_name <- function(x) { - if (is_quosure(x)) { - x <- quo_get_expr(x) - } - as_string(x) -} - -valid_time_units <- function() { - c("years", "months", "days", "hours", "minutes", "seconds") -} - -contains_vars <- function(arg) { - inherits(arg, "quosures") && all(map_lgl(arg, quo_is_symbol) | names(arg) != "") -} diff --git a/R/duplicates.R b/R/duplicates.R index d049d5eeff..983ac0a1ca 100644 --- a/R/duplicates.R +++ b/R/duplicates.R @@ -19,7 +19,9 @@ #' #' @return A `data.frame` or `NULL` #' -#' @keywords user_utility +#' @family utils_ds_chk +#' +#' @keywords utils_ds_chk #' #' @examples #' data(admiral_adsl) @@ -43,7 +45,9 @@ get_duplicates_dataset <- function() { #' @return A `data.frame` of duplicate records within `dataset` #' #' @export -#' @keywords dev_utility +#' @family utils_ds_chk +#' +#' @keywords utils_ds_chk #' @author Thomas Neitmann #' #' @examples @@ -82,7 +86,8 @@ extract_duplicate_records <- function(dataset, by_vars) { #' @return No return value, called for side effects #' #' @export -#' @keywords dev_utility +#' @family utils_help +#' @keywords utils_help #' @author Thomas Neitmann #' #' @examples diff --git a/R/filter_confirmation.R b/R/filter_confirmation.R new file mode 100644 index 0000000000..acf9fc2f4f --- /dev/null +++ b/R/filter_confirmation.R @@ -0,0 +1,513 @@ +#' Filter Confirmed Observations +#' +#' @description +#' +#' The function filters observation using a condition taking other observations +#' into account. For example, it could select all observations with `AVALC == +#' "Y"` and `AVALC == "Y"` for at least one subsequent observation. The input +#' dataset is joined with itself to enable conditions taking variables from both +#' the current observation and the other observations into account. The suffix +#' ".join" is added to the variables from the subsequent observations. +#' +#' An example usage might be checking if a patient received two required +#' medications within a certain timeframe of each other. +#' +#' In the oncology setting, for example, we use such processing to check if a +#' response value can be confirmed by a subsequent assessment. This is commonly +#' used in endpoints such as best overall response. +#' +#' @param dataset Input dataset +#' +#' The variables specified for `by_vars`, `join_vars`, and `order` are +#' expected. +#' +#' @param by_vars By variables +#' +#' The specified variables are used as by variables for joining the input +#' dataset with itself. +#' +#' @param join_vars Variables to keep from joined dataset +#' +#' The variables needed from the other observations should be specified for +#' this parameter. The specified variables are added to the joined dataset +#' with suffix ".join". For example to select all observations with `AVALC == +#' "Y"` and `AVALC == "Y"` for at least one subsequent visit `join_vars = +#' vars(AVALC, AVISITN)` and `filter = AVALC == "Y" & AVALC.join == "Y" & +#' AVISITN < AVISITN.join` could be specified. +#' +#' The `*.join` variables are not included in the output dataset. +#' +#' @param join_type Observations to keep after joining +#' +#' The argument determines which of the joined observations are kept with +#' respect to the original observation. For example, if `join_type = +#' "after"` is specified all observations after the original observations are +#' kept. +#' +#' *Permitted Values:* `"before"`, `"after"`, `"all"` +#' +#' @param first_cond Condition for selecting range of data +#' +#' If this argument is specified, the other observations are restricted up to +#' the first observation where the specified condition is fulfilled. If the +#' condition is not fulfilled for any of the subsequent observations, all +#' observations are removed. +#' +#' @param order Order +#' +#' The observations are ordered by the specified order. +#' +#' @param filter Condition for selecting observations +#' +#' The filter is applied to the joined dataset for selecting the confirmed +#' observations. The condition can include summary functions. The joined +#' dataset is grouped by the original observations. I.e., the summary function +#' are applied to all observations up to the confirmation observation. For +#' example in the oncology setting when using this function for confirmed best +#' overall response, `filter = AVALC == "CR" & all(AVALC.join %in% c("CR", +#' "NE")) & count_vals(var = AVALC.join, val = "NE") <= 1` selects +#' observations with response "CR" and for all observations up to the +#' confirmation observation the response is "CR" or "NE" and there is at most +#' one "NE". +#' +#' @param check_type Check uniqueness? +#' +#' If `"warning"` or `"error"` is specified, the specified message is issued +#' if the observations of the input dataset are not unique with respect to the +#' by variables and the order. +#' +#' *Default:* `"none"` +#' +#' *Permitted Values:* `"none"`, `"warning"`, `"error"` +#' +#' @details +#' +#' The following steps are performed to produce the output dataset. +#' +#' ## Step 1 +#' +#' The input dataset is joined with itself by the variables specified for +#' `by_vars`. From the right hand side of the join only the variables +#' specified for `join_vars` are kept. The suffix ".join" is added to these +#' variables. +#' +#' For example, for `by_vars = USUBJID`, `join_vars = vars(AVISITN, AVALC)` and input dataset +#' +#' ```{r eval=FALSE} +#' # A tibble: 2 x 4 +#' USUBJID AVISITN AVALC AVAL +#' +#' 1 1 Y 1 +#' 1 2 N 0 +#' ``` +#' +#' the joined dataset is +#' +#' ```{r eval=FALSE} +#' A tibble: 4 x 6 +#' USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join +#' +#' 1 1 Y 1 1 Y +#' 1 1 Y 1 2 N +#' 1 2 N 0 1 Y +#' 1 2 N 0 2 N +#' ``` +#' +#' ## Step 2 +#' +#' The joined dataset is restricted to observations with respect to +#' `join_type` and `order`. +#' +#' The dataset from the example in the previous step with `join_type = +#' "after"` and order = vars(AVISITN)` is restricted to +#' +#' ```{r eval=FALSE} +#' A tibble: 4 x 6 +#' USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join +#' +#' 1 1 Y 1 2 N +#' ``` +#' +#' ## Step 3 +#' +#' If `first_cond` is specified, for each observation of the input dataset the +#' joined dataset is restricted to observations up to the first observation +#' where `first_cond` is fulfilled (the observation fulfilling the condition +#' is included). If for an observation of the input dataset the condition is +#' not fulfilled, the observation is removed. +#' +#' ## Step 4 +#' +#' The joined dataset is grouped by the observations from the input dataset +#' and restricted to the observations fulfilling the condition specified by +#' `filter`. +#' +#' ## Step 5 +#' +#' The first observation of each group is selected and the `*.join` variables +#' are dropped. +#' +#' @returns A subset of the observations of the input dataset. All variables of +#' the input dataset are included in the output dataset. +#' +#' @author Stefan Bundfuss +#' +#' @keywords utils_fil +#' @family utils_fil +#' +#' @seealso [count_vals()], [min_cond()], [max_cond()] +#' +#' @export +#' +#' @examples +#' +#' library(tibble) +#' library(admiral) +#' +#' # filter observations with a duration longer than 30 and +#' # on or after 7 days before a COVID AE (ACOVFL == "Y") +#' adae <- tribble( +#' ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, +#' "1", 10, "N", 1, +#' "1", 21, "N", 50, +#' "1", 23, "Y", 14, +#' "1", 32, "N", 31, +#' "1", 42, "N", 20, +#' "2", 11, "Y", 13, +#' "2", 23, "N", 2, +#' "3", 13, "Y", 12, +#' "4", 14, "N", 32, +#' "4", 21, "N", 41 +#' ) +#' +#' filter_confirmation( +#' adae, +#' by_vars = vars(USUBJID), +#' join_vars = vars(ACOVFL, ADY), +#' join_type = "all", +#' order = vars(ADY), +#' filter = ADURN > 30 & ACOVFL.join == "Y" & ADY >= ADY.join - 7 +#' ) +#' +#' # filter observations with AVALC == "Y" and AVALC == "Y" at a subsequent visit +#' data <- tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "Y", +#' "1", 2, "N", +#' "1", 3, "Y", +#' "1", 4, "N", +#' "2", 1, "Y", +#' "2", 2, "N", +#' "3", 1, "Y", +#' "4", 1, "N", +#' "4", 2, "N", +#' ) +#' +#' filter_confirmation( +#' data, +#' by_vars = vars(USUBJID), +#' join_vars = vars(AVALC, AVISITN), +#' join_type = "after", +#' order = vars(AVISITN), +#' filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join +#' ) +#' +#' # select observations with AVALC == "CR", AVALC == "CR" at a subsequent visit, +#' # only "CR" or "NE" in between, and at most one "NE" in between +#' data <- tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "PR", +#' "1", 2, "CR", +#' "1", 3, "NE", +#' "1", 4, "CR", +#' "1", 5, "NE", +#' "2", 1, "CR", +#' "2", 2, "PR", +#' "2", 3, "CR", +#' "3", 1, "CR", +#' "4", 1, "CR", +#' "4", 2, "NE", +#' "4", 3, "NE", +#' "4", 4, "CR", +#' "4", 5, "PR" +#' ) +#' +#' filter_confirmation( +#' data, +#' by_vars = vars(USUBJID), +#' join_vars = vars(AVALC), +#' join_type = "after", +#' order = vars(AVISITN), +#' first_cond = AVALC.join == "CR", +#' filter = AVALC == "CR" & all(AVALC.join %in% c("CR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1 +#' ) +#' +#' # select observations with AVALC == "PR", AVALC == "CR" or AVALC == "PR" +#' # at a subsequent visit at least 20 days later, only "CR", "PR", or "NE" +#' # in between, at most one "NE" in between, and "CR" is not followed by "PR" +#' data <- tribble( +#' ~USUBJID, ~ADY, ~AVALC, +#' "1", 6, "PR", +#' "1", 12, "CR", +#' "1", 24, "NE", +#' "1", 32, "CR", +#' "1", 48, "PR", +#' "2", 3, "PR", +#' "2", 21, "CR", +#' "2", 33, "PR", +#' "3", 11, "PR", +#' "4", 7, "PR", +#' "4", 12, "NE", +#' "4", 24, "NE", +#' "4", 32, "PR", +#' "4", 55, "PR" +#' ) +#' +#' filter_confirmation( +#' data, +#' by_vars = vars(USUBJID), +#' join_vars = vars(AVALC, ADY), +#' join_type = "after", +#' order = vars(ADY), +#' first_cond = AVALC.join %in% c("CR", "PR") & ADY.join - ADY >= 20, +#' filter = AVALC == "PR" & +#' all(AVALC.join %in% c("CR", "PR", "NE")) & +#' count_vals(var = AVALC.join, val = "NE") <= 1 & +#' ( +#' min_cond(var = ADY.join, cond = AVALC.join == "CR") > +#' max_cond(var = ADY.join, cond = AVALC.join == "PR") | +#' count_vals(var = AVALC.join, val = "CR") == 0 +#' ) +#' ) +filter_confirmation <- function(dataset, + by_vars, + join_vars, + join_type, + first_cond = NULL, + order, + filter, + check_type = "warning") { + # Check input parameters + assert_vars(by_vars) + assert_vars(join_vars) + join_type <- + assert_character_scalar( + join_type, + values = c("before", "after", "all"), + case_sensitive = FALSE + ) + first_cond <- assert_filter_cond(enquo(first_cond), optional = TRUE) + assert_order_vars(order) + filter <- assert_filter_cond(enquo(filter)) + check_type <- + assert_character_scalar( + check_type, + values = c("none", "warning", "error"), + case_sensitive = FALSE + ) + assert_data_frame( + dataset, + required_vars = quo_c(by_vars, join_vars, extract_vars(order)) + ) + + # number observations of the input dataset to get a unique key + # (by_vars and tmp_obs_nr_filter_confirmation) + data <- dataset %>% + derive_var_obs_number( + new_var = tmp_obs_nr_filter_confirmation, + by_vars = by_vars, + order = order, + check_type = check_type + ) + # join the input dataset with itself such that to each observation of the + # input dataset all following observations are joined + data_joined <- + left_join( + data, + select(data, !!!by_vars, !!!join_vars, tmp_obs_nr_filter_confirmation), + by = vars2chr(by_vars), + suffix = c("", ".join") + ) + if (join_type != "all") { + operator <- c(before = "<", after = ">") + + data_joined <- filter( + data_joined, + !!parse_expr(paste( + "tmp_obs_nr_filter_confirmation.join", + operator[join_type], + "tmp_obs_nr_filter_confirmation" + )) + ) + } + + if (!quo_is_null(first_cond)) { + # select all observations up to the first confirmation observation + data_joined <- filter_relative( + data_joined, + by_vars = vars(!!!by_vars, tmp_obs_nr_filter_confirmation), + condition = !!first_cond, + order = vars(tmp_obs_nr_filter_confirmation.join), + mode = "first", + selection = "before", + inclusive = TRUE, + keep_no_ref_groups = FALSE + ) + } + + # apply confirmation condition, which may include summary functions + data_joined %>% + group_by(!!!by_vars, tmp_obs_nr_filter_confirmation) %>% + filter(!!filter) %>% + # select one observation of each group, as the joined variables are removed + # it doesn't matter which one, so we take just the first one + slice(1L) %>% + ungroup() %>% + select(colnames(dataset)) +} + +#' Count Number of Observations Where a Variable Equals a Value +#' +#' Count number of observations where a variable equals a value. +#' +#' @param var A vector +#' +#' @param val A value +#' +#' @author Stefan Bundfuss +#' +#' @keywords utils_fil +#' @family utils_fil +#' +#' @export +#' +#' @examples +#' +#' library(tibble) +#' library(dplyr) +#' library(admiral) +#' data <- tibble::tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "PR", +#' "1", 2, "CR", +#' "1", 3, "NE", +#' "1", 4, "CR", +#' "1", 5, "NE", +#' "2", 1, "CR", +#' "2", 2, "PR", +#' "2", 3, "CR", +#' "3", 1, "CR", +#' "4", 1, "CR", +#' "4", 2, "NE", +#' "4", 3, "NE", +#' "4", 4, "CR", +#' "4", 5, "PR" +#' ) +#' +#' # add variable providing the number of NEs for each subject +#' group_by(data, USUBJID) %>% +#' mutate(nr_nes = count_vals(var = AVALC, val = "NE")) +count_vals <- function(var, val) { + length(var[var == val]) +} + +#' Minimum Value on a Subset +#' +#' The function derives the minimum value of a vector/column on a subset of +#' entries/observations. +#' +#' @param var A vector +#' +#' @param cond A condition +#' +#' @author Stefan Bundfuss +#' +#' @keywords utils_fil +#' @family utils_fil +#' +#' @export +#' +#' @examples +#' +#' library(tibble) +#' library(dplyr) +#' library(admiral) +#' data <- tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "PR", +#' "1", 2, "CR", +#' "1", 3, "NE", +#' "1", 4, "CR", +#' "1", 5, "NE", +#' "2", 1, "CR", +#' "2", 2, "PR", +#' "2", 3, "CR", +#' ) +#' +#' # In oncology setting, when needing to check the first time a patient had +#' # a Complete Response (CR) to compare to see if any Partial Response (PR) +#' # occurred after this add variable indicating if PR occurred after CR +#' group_by(data, USUBJID) %>% mutate( +#' first_cr_vis = min_cond(var = AVISITN, cond = AVALC == "CR"), +#' last_pr_vis = max_cond(var = AVISITN, cond = AVALC == "PR"), +#' pr_after_cr = last_pr_vis > first_cr_vis +#' ) +min_cond <- function(var, cond) { + assert_filter_cond(enquo(cond)) + if (length(var[cond]) == 0) { + NA + } else { + min(var[cond]) + } +} + +#' Maximum Value on a Subset +#' +#' The function derives the maximum value of a vector/column on a subset of +#' entries/observations. +#' +#' @param var A vector +#' +#' @param cond A condition +#' +#' @author Stefan Bundfuss +#' +#' @keywords utils_fil +#' @family utils_fil +#' +#' @export +#' +#' @examples +#' +#' library(tibble) +#' library(dplyr) +#' library(admiral) +#' data <- tribble( +#' ~USUBJID, ~AVISITN, ~AVALC, +#' "1", 1, "PR", +#' "1", 2, "CR", +#' "1", 3, "NE", +#' "1", 4, "CR", +#' "1", 5, "NE", +#' "2", 1, "CR", +#' "2", 2, "PR", +#' "2", 3, "CR", +#' ) +#' +#' # In oncology setting, when needing to check the first time a patient had +#' # a Complete Response (CR) to compare to see if any Partial Response (PR) +#' # occurred after this add variable indicating if PR occurred after CR +#' group_by(data, USUBJID) %>% mutate( +#' first_cr_vis = min_cond(var = AVISITN, cond = AVALC == "CR"), +#' last_pr_vis = max_cond(var = AVISITN, cond = AVALC == "PR"), +#' pr_after_cr = last_pr_vis > first_cr_vis +#' ) +max_cond <- function(var, cond) { + assert_filter_cond(enquo(cond)) + if (length(var[cond]) == 0) { + NA + } else { + max(var[cond]) + } +} diff --git a/R/filter_extreme.R b/R/filter_extreme.R index 91fa41140f..d54ad3c5c4 100644 --- a/R/filter_extreme.R +++ b/R/filter_extreme.R @@ -47,7 +47,9 @@ #' #' @return A dataset containing the first or last observation of each by group #' -#' @keywords adam user_utility +#' @family utils_fil +#' +#' @keywords utils_fil #' #' @export #' @@ -89,12 +91,13 @@ filter_extreme <- function(dataset, ) # group and sort input dataset + tmp_obs_nr <- get_new_tmp_var(dataset) if (!is.null(by_vars)) { assert_has_variables(dataset, vars2chr(by_vars)) data <- dataset %>% derive_var_obs_number( - new_var = temp_obs_nr, + new_var = !!tmp_obs_nr, order = order, by_vars = by_vars, check_type = check_type @@ -103,7 +106,7 @@ filter_extreme <- function(dataset, } else { data <- dataset %>% derive_var_obs_number( - new_var = temp_obs_nr, + new_var = !!tmp_obs_nr, order = order, check_type = check_type ) @@ -120,5 +123,5 @@ filter_extreme <- function(dataset, } data %>% ungroup() %>% - select(-starts_with("temp_")) + remove_tmp_vars() } diff --git a/R/filter_relative.R b/R/filter_relative.R index ea556a9e06..f2ddbde512 100644 --- a/R/filter_relative.R +++ b/R/filter_relative.R @@ -74,7 +74,9 @@ #' after the observation where the condition was fulfilled the first or last #' time #' -#' @keywords adam user_utility +#' @keywords utils_fil +#' @family utils_fil +#' #' #' @export #' diff --git a/R/get_summary_records.R b/R/get_summary_records.R new file mode 100644 index 0000000000..7453843cc8 --- /dev/null +++ b/R/get_summary_records.R @@ -0,0 +1,175 @@ +#' Create Summary Records +#' +#' @description +#' It is not uncommon to have an analysis need whereby one needs to derive an +#' analysis value (`AVAL`) from multiple records. The ADaM basic dataset +#' structure variable `DTYPE` is available to indicate when a new derived +#' records has been added to a dataset. +#' +#' @details +#' This function only creates derived observations and does not append them +#' to the original dataset observations. If you would like to this instead, +#' see the `derive_summary_records()` function. +#' +#' @param dataset A data frame. +#' +#' @param by_vars Variables to consider for generation of groupwise summary +#' records. Providing the names of variables in [vars()] will create a +#' groupwise summary and generate summary records for the specified groups. +#' +#' @param filter Filter condition as logical expression to apply during +#' summary calculation. By default, filtering expressions are computed within +#' `by_vars` as this will help when an aggregating, lagging, or ranking +#' function is involved. +#' +#' For example, +#' +#' + `filter_rows = (AVAL > mean(AVAL, na.rm = TRUE))` will filter all AVAL +#' values greater than mean of AVAL with in `by_vars`. +#' + `filter_rows = (dplyr::n() > 2)` will filter n count of `by_vars` greater +#' than 2. +#' +#' @param analysis_var Analysis variable. +#' +#' @param summary_fun Function that takes as an input the `analysis_var` and +#' performs the calculation. +#' This can include built-in functions as well as user defined functions, +#' for example `mean` or `function(x) mean(x, na.rm = TRUE)`. +#' +#' @param set_values_to A list of variable name-value pairs. Use this argument +#' if you need to change the values of any newly derived records. +#' +#' Set a list of variables to some specified value for the new observation(s) +#' + LHS refer to a variable. +#' + RHS refers to the values to set to the variable. This can be a string, a symbol, a numeric +#' value or NA. +#' (e.g. `vars(PARAMCD = "TDOSE",PARCAT1 = "OVERALL")`). +#' More general expression are not allowed. +#' +#' @author Pavan Kumar, updated by Alana Harris +#' +#' @return A data frame of derived records. +#' +#' @family der_gen +#' +#' @keywords der_gen +#' +#' @seealso `derive_summary_records()` +#' +#' @export +#' +#' @examples +#' library(dplyr, warn.conflicts = FALSE) +#' adeg <- tibble::tribble( +#' ~USUBJID, ~EGSEQ, ~PARAM, ~AVISIT, ~EGDTC, ~AVAL, ~TRTA, +#' "XYZ-1001", 1, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:50", 385, "", +#' "XYZ-1001", 2, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:52", 399, "", +#' "XYZ-1001", 3, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:56", 396, "", +#' "XYZ-1001", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:45", 384, "Placebo", +#' "XYZ-1001", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:48", 393, "Placebo", +#' "XYZ-1001", 6, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:51", 388, "Placebo", +#' "XYZ-1001", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:45", 385, "Placebo", +#' "XYZ-1001", 8, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:48", 394, "Placebo", +#' "XYZ-1001", 9, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:51", 402, "Placebo", +#' "XYZ-1002", 1, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 399, "", +#' "XYZ-1002", 2, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 410, "", +#' "XYZ-1002", 3, "QTcF Int. (msec)", "Baseline", "2016-02-22T08:01", 392, "", +#' "XYZ-1002", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:50", 401, "Active 20mg", +#' "XYZ-1002", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:53", 407, "Active 20mg", +#' "XYZ-1002", 6, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:56", 400, "Active 20mg", +#' "XYZ-1002", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:50", 412, "Active 20mg", +#' "XYZ-1002", 8, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:53", 414, "Active 20mg", +#' "XYZ-1002", 9, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:56", 402, "Active 20mg", +#' ) +#' +#' # Summarize the average of the triplicate ECG interval values (AVAL) +#' get_summary_records( +#' adeg, +#' by_vars = vars(USUBJID, PARAM, AVISIT), +#' analysis_var = AVAL, +#' summary_fun = function(x) mean(x, na.rm = TRUE), +#' set_values_to = vars(DTYPE = "AVERAGE") +#' ) +#' +#' advs <- tibble::tribble( +#' ~USUBJID, ~VSSEQ, ~PARAM, ~AVAL, ~VSSTRESU, ~VISIT, ~VSDTC, +#' "XYZ-001-001", 1164, "Weight", 99, "kg", "Screening", "2018-03-19", +#' "XYZ-001-001", 1165, "Weight", 101, "kg", "Run-In", "2018-03-26", +#' "XYZ-001-001", 1166, "Weight", 100, "kg", "Baseline", "2018-04-16", +#' "XYZ-001-001", 1167, "Weight", 94, "kg", "Week 24", "2018-09-30", +#' "XYZ-001-001", 1168, "Weight", 92, "kg", "Week 48", "2019-03-17", +#' "XYZ-001-001", 1169, "Weight", 95, "kg", "Week 52", "2019-04-14", +#' ) +#' +#' # Set new values to any variable. Here, `DTYPE = MAXIMUM` refers to `max()` records +#' # and `DTYPE = AVERAGE` refers to `mean()` records. +#' get_summary_records( +#' advs, +#' by_vars = vars(USUBJID, PARAM), +#' analysis_var = AVAL, +#' summary_fun = max, +#' set_values_to = vars(DTYPE = "MAXIMUM") +#' ) %>% +#' get_summary_records( +#' by_vars = vars(USUBJID, PARAM), +#' analysis_var = AVAL, +#' summary_fun = mean, +#' set_values_to = vars(DTYPE = "AVERAGE") +#' ) +#' +#' # Sample ADEG dataset with triplicate record for only AVISIT = 'Baseline' +#' adeg <- tibble::tribble( +#' ~USUBJID, ~EGSEQ, ~PARAM, ~AVISIT, ~EGDTC, ~AVAL, ~TRTA, +#' "XYZ-1001", 1, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:50", 385, "", +#' "XYZ-1001", 2, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:52", 399, "", +#' "XYZ-1001", 3, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:56", 396, "", +#' "XYZ-1001", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:48", 393, "Placebo", +#' "XYZ-1001", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:51", 388, "Placebo", +#' "XYZ-1001", 6, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:48", 394, "Placebo", +#' "XYZ-1001", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:51", 402, "Placebo", +#' "XYZ-1002", 1, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 399, "", +#' "XYZ-1002", 2, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 410, "", +#' "XYZ-1002", 3, "QTcF Int. (msec)", "Baseline", "2016-02-22T08:01", 392, "", +#' "XYZ-1002", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:53", 407, "Active 20mg", +#' "XYZ-1002", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:56", 400, "Active 20mg", +#' "XYZ-1002", 6, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:53", 414, "Active 20mg", +#' "XYZ-1002", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:56", 402, "Active 20mg", +#' ) +#' +#' # Compute the average of AVAL only if there are more than 2 records within the +#' # by group +#' get_summary_records( +#' adeg, +#' by_vars = vars(USUBJID, PARAM, AVISIT), +#' filter = dplyr::n() > 2, +#' analysis_var = AVAL, +#' summary_fun = function(x) mean(x, na.rm = TRUE), +#' set_values_to = vars(DTYPE = "AVERAGE") +#' ) +get_summary_records <- function(dataset, + by_vars, + filter = NULL, + analysis_var, + summary_fun, + set_values_to) { + assert_vars(by_vars) + analysis_var <- assert_symbol(enquo(analysis_var)) + filter <- assert_filter_cond(enquo(filter), optional = TRUE) + assert_s3_class(summary_fun, "function") + assert_data_frame( + dataset, + required_vars = quo_c(by_vars, analysis_var), + check_is_grouped = FALSE + ) + if (!is.null(set_values_to)) { + assert_varval_list(set_values_to, optional = TRUE) + } + + # Summarise the analysis value + dataset %>% + group_by(!!!by_vars) %>% + filter_if(filter) %>% + summarise(!!analysis_var := summary_fun(!!analysis_var)) %>% + mutate(!!!set_values_to) %>% + ungroup() +} diff --git a/R/globals.R b/R/globals.R index 50bcba3932..bff3460b88 100644 --- a/R/globals.R +++ b/R/globals.R @@ -109,5 +109,26 @@ globalVariables(c( "dose_periods", "grpseq", "time_differential", - "temp_flag" + "temp_flag", + "tmp_obs_nr_filter_confirmation", + "tmp_obs_nr_filter_confirmation.join", + "tmp_obs_nr_var_conf_flag", + "AGE", + "SEX", + "SMOKEFL", + "DIABETFL", + "TRTHYPFL", + "ATOXGR", + "ATOXGRL", + "ATOXGRH", + "ATOXDSCL", + "ATOXDSCH", + "GRADE_CRITERIA_CODE", + "DIRECTION", + "SI_UNIT_CHECK", + "SI_UNIT_UPPER", + "VAR_CHECK", + "TERM", + "TERM_UPPER", + "atoxgr_criteria_ctcv4" )) diff --git a/R/joins.R b/R/joins.R deleted file mode 100644 index ec0bc9d076..0000000000 --- a/R/joins.R +++ /dev/null @@ -1,20 +0,0 @@ -anti_join <- function(x, y, by = NULL, copy = FALSE, ...) { - suppress_warning( - dplyr::anti_join(x, y, by = by, copy = copy, ...), - "^Column `.+` has different attributes on LHS and RHS of join$" - ) -} - -inner_join <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { - suppress_warning( - dplyr::inner_join(x, y, by = by, copy = copy, suffix = suffix, ...), - "^Column `.+` has different attributes on LHS and RHS of join$" - ) -} - -left_join <- function(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...) { - suppress_warning( - dplyr::left_join(x, y, by = by, copy = copy, suffix = suffix, ...), - "^Column `.+` has different attributes on LHS and RHS of join$" - ) -} diff --git a/R/reexports.R b/R/reexports.R index 9d41576aaa..4d10b51e8d 100644 --- a/R/reexports.R +++ b/R/reexports.R @@ -1,3 +1,12 @@ +#' @export +admiraldev::filter_if + +#' @export +admiraldev::negate_vars + +#' @export +admiraldev::vars2chr + #' @export dplyr::vars diff --git a/R/restrict_derivation.R b/R/restrict_derivation.R index ad8504799d..90a6729110 100644 --- a/R/restrict_derivation.R +++ b/R/restrict_derivation.R @@ -12,7 +12,8 @@ #' #' @param filter Filter condition #' -#' @keywords user_utility high_order_function +#' @family high_order_function +#' @keywords high_order_function #' #' @author Stefan Bundfuss #' diff --git a/R/slice_derivation.R b/R/slice_derivation.R index 0ae215956e..33ec540f38 100644 --- a/R/slice_derivation.R +++ b/R/slice_derivation.R @@ -36,7 +36,8 @@ #' #' @return The input dataset with the variables derived by the derivation added #' -#' @keywords user_utility high_order_function +#' @family high_order_function +#' @keywords high_order_function #' #' @author Stefan Bundfuss #' @@ -148,7 +149,8 @@ slice_derivation <- function(dataset, #' #' @seealso [slice_derivation()], [params()] #' -#' @keywords source_specifications +#' @family high_order_function +#' @keywords high_order_function #' #' @export #' @@ -172,6 +174,9 @@ derivation_slice <- function(filter, #' #' @export #' +#' @family high_order_function +#' @keywords high_order_function +#' #' @seealso [derivation_slice()] #' #' @examples diff --git a/R/test_helpers.R b/R/test_helpers.R deleted file mode 100644 index 1b1f9af700..0000000000 --- a/R/test_helpers.R +++ /dev/null @@ -1,34 +0,0 @@ -#' Expectation: Are Two Datasets Equal? -#' -#' Uses [diffdf::diffdf()] to compares 2 datasets for any differences -#' -#' @param base Input dataset -#' @param compare Comparison dataset -#' @param keys `character` vector of variables that define a unique row in the -#' `base` and `compare` datasets -#' @param ... Additional arguments passed onto [diffdf::diffdf()] -#' -#' @return -#' An error if `base` and `compare` do not match or `NULL` invisibly if they do -#' -#' @author Thomas Neitmann -#' @keywords test_helper dev_utility -#' @export -#' -#' @examples -#' \dontrun{ -#' testthat::test_that("a missing row is detected", { -#' data(dm) -#' expect_dfs_equal(dm, dm[-1L, ], keys = "USUBJID") -#' }) -#' } -expect_dfs_equal <- function(base, compare, keys, ...) { - diff <- diffdf::diffdf(base, compare, keys, suppress_warnings = TRUE, ...) - if (diffdf::diffdf_has_issues(diff)) { - msg <- capture.output(print(diff)) - testthat::fail(msg) - } else { - testthat::succeed() - invisible() - } -} diff --git a/R/tte_sources.R b/R/tte_sources.R index ef04078a4c..7793426a2b 100644 --- a/R/tte_sources.R +++ b/R/tte_sources.R @@ -1,19 +1,31 @@ #' Pre-Defined Time-to-Event Source Objects #' -#' These pre-defined `tte_source` objects can be used as input to `derive_param_tte()`. +#' These pre-defined `tte_source` objects can be used as input to [derive_param_tte()]. #' #' @details #' To see the definition of the various objects simply print the object in the -#' R console, e.g. `print(death_event)`. +#' R console, e.g. `print(death_event)`. For details of how to use these objects +#' please refer to [derive_param_tte()]. #' #' @seealso [derive_param_tte()], [tte_source()], [event_source()], [censor_source()] #' +#' @format NULL +#' #' @export #' -#' @keywords tte_source +#' @family source_specifications +#' @keywords source_specifications #' #' @rdname tte_source_objects #' +#' @examples +#' # This shows the definition of all pre-defined `tte_source` objects that ship +#' # with {admiral} +#' for (obj in list_tte_source_objects()$object) { +#' cat(obj, "\n") +#' print(get(obj)) +#' cat("\n") +#' } death_event <- event_source( dataset_name = "adsl", filter = DTHFL == "Y", @@ -25,8 +37,9 @@ death_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export lastalive_censor <- censor_source( dataset_name = "adsl", @@ -38,8 +51,9 @@ lastalive_censor <- censor_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_event <- event_source( dataset_name = "adae", @@ -53,8 +67,9 @@ ae_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_ser_event <- event_source( dataset_name = "adae", @@ -68,8 +83,9 @@ ae_ser_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_gr1_event <- event_source( dataset_name = "adae", @@ -83,8 +99,9 @@ ae_gr1_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_gr2_event <- event_source( dataset_name = "adae", @@ -98,8 +115,9 @@ ae_gr2_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_gr3_event <- event_source( dataset_name = "adae", @@ -113,8 +131,9 @@ ae_gr3_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_gr4_event <- event_source( dataset_name = "adae", @@ -128,8 +147,9 @@ ae_gr4_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_gr5_event <- event_source( dataset_name = "adae", @@ -143,8 +163,9 @@ ae_gr5_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_gr35_event <- event_source( dataset_name = "adae", @@ -158,8 +179,9 @@ ae_gr35_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_sev_event <- event_source( dataset_name = "adae", @@ -173,8 +195,9 @@ ae_sev_event <- event_source( ) ) -#' @keywords tte_source +#' @keywords source_specifications #' @rdname tte_source_objects +#' @format NULL #' @export ae_wd_event <- event_source( dataset_name = "adae", diff --git a/R/user_helpers.R b/R/user_helpers.R index 564ec52504..0a19aafd8e 100644 --- a/R/user_helpers.R +++ b/R/user_helpers.R @@ -2,6 +2,7 @@ #' #' @param adam_name An ADaM dataset name. You can use any of the available dataset name `r list_all_templates()`, and the dataset name is case-insensitive. The default dataset name is ADSL. #' @param save_path Path to save the script. +#' @param package The R package in which to look for templates. By default `"admiral"`. #' @param overwrite Whether to overwrite an existing file named `save_path`. #' @param open Whether to open the script right away. #' @@ -11,7 +12,8 @@ #' #' @author Shimeng Huang, Thomas Neitmann #' -#' @keywords user_utility +#' @family utils_examples +#' @keywords utils_examples #' #' @export #' @@ -21,17 +23,22 @@ #' } use_ad_template <- function(adam_name = "adsl", save_path = paste0("./", adam_name, ".R"), + package = "admiral", overwrite = FALSE, open = interactive()) { assert_character_scalar(adam_name) assert_character_scalar(save_path) + assert_character_scalar(package) assert_logical_scalar(overwrite) assert_logical_scalar(open) - if (!toupper(adam_name) %in% list_all_templates()) { - err_msg <- paste0( - sprintf("No template for '%s' available.\n", toupper(adam_name)), - "\u2139 Run `list_all_templates()` to get a list of all available ADaM templates." + if (!toupper(adam_name) %in% list_all_templates(package)) { + err_msg <- sprintf( + paste0( + "No template for '%s' available in package '%s'.\n", + "\u2139 Run `list_all_templates('%s')` to get a list of all available ADaM templates." + ), + toupper(adam_name), package, package ) abort(err_msg) } @@ -47,7 +54,7 @@ use_ad_template <- function(adam_name = "adsl", template_file <- system.file( paste0("templates/ad_", tolower(adam_name), ".R"), - package = "admiral" + package = package ) if (file.copy(template_file, save_path, overwrite = TRUE)) { @@ -63,9 +70,12 @@ use_ad_template <- function(adam_name = "adsl", #' List All Available ADaM Templates #' +#' @param package The R package in which to look for templates. By default `"admiral"`. +#' #' @author Shimeng Huang, Thomas Neitmann #' -#' @keywords user_utility +#' @family utils_examples +#' @keywords utils_examples #' #' @return A `character` vector of all available templates #' @@ -73,15 +83,46 @@ use_ad_template <- function(adam_name = "adsl", #' #' @examples #' list_all_templates() -list_all_templates <- function() { - list.files(system.file("templates", package = "admiral")) %>% +list_all_templates <- function(package = "admiral") { + assert_character_scalar(package) + + if (!requireNamespace(package, quietly = TRUE)) { + err_msg <- sprintf("No package called '%s' is installed and hence no templates are available", package) + abort(err_msg) + } + + list.files(system.file("templates", package = package)) %>% str_remove(".R$") %>% str_remove("^ad_") %>% toupper() %>% - structure(class = c("adam_templates", "character")) + structure(class = c("adam_templates", "character"), package = package) } +#' Print `adam_templates` Objects +#' +#' @param x A `adam_templates` object +#' @param ... Not used +#' +#' @return No return value, called for side effects +#' +#' @author Thomas Neitmann +#' +#' @export +#' +#' @keywords internal +#' @family internal +#' +#' @seealso [list_all_templates()] +#' +#' @examples +#' templates <- list_all_templates() +#' print(templates) print.adam_templates <- function(x, ...) { - cat("Existing templates:\n") - cat(paste0("\U2022 ", x), sep = "\n") + pkg <- attr(x, "package") + if (length(x) == 0L) { + cat("No ADaM templates available in package '", pkg, "'\n", sep = "") + } else { + cat("Existing ADaM templates in package '", pkg, "':\n", sep = "") + cat(paste0("\U2022 ", x), sep = "\n") + } } diff --git a/R/user_utils.R b/R/user_utils.R index 608ad5667e..314323ac5b 100644 --- a/R/user_utils.R +++ b/R/user_utils.R @@ -1,85 +1,3 @@ -#' Turn a List of Quosures into a Character Vector -#' -#' @param quosures A `list` of `quosures` created using [`vars()`] -#' -#' @return A character vector -#' -#' @author Thomas Neitmann -#' -#' @export -#' -#' @keywords user_utility -#' -#' @examples -#' vars2chr(vars(USUBJID, AVAL)) -vars2chr <- function(quosures) { - rlang::set_names( - map_chr(quosures, ~ as_string(quo_get_expr(.x))), - names(quosures) - ) -} - -#' Negate List of Variables -#' -#' The function adds a minus sign as prefix to each variable. -#' -#' This is useful if a list of variables should be removed from a dataset, -#' e.g., `select(!!!negate_vars(by_vars))` removes all by variables. -#' -#' @param vars List of variables created by `vars()` -#' -#' @return A list of `quosures` -#' -#' @author Stefan Bundfuss -#' -#' @export -#' -#' @keywords user_utility -#' -#' @examples -#' negate_vars(vars(USUBJID, STUDYID)) -negate_vars <- function(vars = NULL) { - assert_vars(vars, optional = TRUE) - if (is.null(vars)) { - NULL - } else { - lapply(vars, function(var) expr(-!!quo_get_expr(var))) - } -} - -#' Optional Filter -#' -#' Filters the input dataset if the provided expression is not `NULL` -#' -#' @param dataset Input dataset -#' @param filter A filter condition. Must be a quosure. -#' -#' @return A `data.frame` containing all rows in `dataset` matching `filter` or -#' just `dataset` if `filter` is `NULL` -#' -#' @author Thomas Neitmann -#' -#' @export -#' -#' @keywords user_utility -#' -#' @examples -#' library(admiral.test) -#' data(admiral_vs) -#' -#' admiral::filter_if(admiral_vs, rlang::quo(NULL)) -#' admiral::filter_if(admiral_vs, rlang::quo(VSTESTCD == "WEIGHT")) -filter_if <- function(dataset, filter) { - assert_data_frame(dataset) - assert_filter_cond(filter, optional = TRUE) - - if (quo_is_null(filter)) { - dataset - } else { - filter(dataset, !!filter) - } -} - #' Extract Unit From Parameter Description #' #' Extract the unit of a parameter from a description like "Param (unit)". @@ -90,7 +8,8 @@ filter_if <- function(dataset, filter) { #' #' @export #' -#' @keywords user_utility +#' @keywords utils_help +#' @family utils_help #' #' @examples #' extract_unit("Height (cm)") @@ -121,7 +40,8 @@ extract_unit <- function(x) { #' #' @author Thomas Neitmann #' -#' @keywords user_utility +#' @family utils_fmt +#' @keywords utils_fmt #' #' @export #' @@ -183,7 +103,8 @@ convert_blanks_to_na.data.frame <- function(x) { # nolint #' #' @return A `data.frame` or `NULL` #' -#' @keywords user_utility +#' @family utils_ds_chk +#' @keywords utils_ds_chk #' #' @examples #' data(admiral_adsl) @@ -194,7 +115,7 @@ convert_blanks_to_na.data.frame <- function(x) { # nolint #' #' get_one_to_many_dataset() get_one_to_many_dataset <- function() { - .datasets$one_to_many + get_dataset("one_to_many") } #' Get Many to One Values that Led to a Prior Error @@ -215,7 +136,8 @@ get_one_to_many_dataset <- function() { #' #' @return A `data.frame` or `NULL` #' -#' @keywords user_utility +#' @family utils_ds_chk +#' @keywords utils_ds_chk #' #' @examples #' data(admiral_adsl) @@ -226,5 +148,32 @@ get_one_to_many_dataset <- function() { #' #' get_many_to_one_dataset() get_many_to_one_dataset <- function() { - .datasets$many_to_one + get_dataset("many_to_one") +} + +#' Map `"Y"` and `"N"` to Numeric Values +#' +#' Map `"Y"` and `"N"` to numeric values. +#' +#' @param arg Character vector +#' +#' @author Stefan Bundfuss +#' +#' @keywords utils_fmt +#' @family utils_fmt +#' +#' @export +#' +#' @return `1` if `arg` equals `"Y"`, `0` if `arg` equals `"N"`, `NA_real_` otherwise +#' +#' @examples +#' +#' yn_to_numeric(c("Y", "N", NA_character_)) +yn_to_numeric <- function(arg) { + assert_character_vector(arg) + case_when( + arg == "Y" ~ 1, + arg == "N" ~ 0, + TRUE ~ NA_real_ + ) } diff --git a/R/warnings.R b/R/warnings.R deleted file mode 100644 index d14dabf326..0000000000 --- a/R/warnings.R +++ /dev/null @@ -1,231 +0,0 @@ -#' Warn If a Variable Already Exists -#' -#' Warn if a variable already exists inside a dataset -#' -#' @param dataset A `data.frame` -#' @param vars `character` vector of columns to check for in `dataset` -#' -#' @return No return value, called for side effects -#' -#' @author Thomas Neitmann -#' -#' @keywords warning -#' -#' @export -#' -#' @examples -#' library(admiral.test) -#' data(admiral_dm) -#' -#' ## No warning as `AAGE` doesn't exist in `dm` -#' warn_if_vars_exist(admiral_dm, "AAGE") -#' -#' ## Issues a warning -#' warn_if_vars_exist(admiral_dm, "ARM") -warn_if_vars_exist <- function(dataset, vars) { - existing_vars <- vars[vars %in% colnames(dataset)] - if (length(existing_vars) == 1L) { - msg <- paste("Variable", backquote(existing_vars), "already exists in the dataset") - warn(msg) - } else if (length(existing_vars) > 1L) { - msg <- paste("Variables", enumerate(existing_vars), "already exist in the dataset") - warn(msg) - } else { - invisible(NULL) - } -} - -is_valid_dtc <- function(arg) { - pattern <- paste( - "^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}).(\\d{3})$", - "^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})$", - "^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})$", - "^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2})$", - "^(\\d{4})-(\\d{2})-(\\d{2})$", - "^(\\d{4})-(\\d{2})$", - "^(\\d{4})$", - "^(\\d{4})---(\\d{2})$", - sep = "|" - ) - - grepl(pattern, arg) | arg == "" | is.na(arg) -} - -#' Warn If a Vector Contains Unknown Datetime Format -#' -#' Warn if the vector contains unknown datetime format such as -#' "2003-12-15T-:15:18", "2003-12-15T13:-:19","--12-15","-----T07:15" -#' -#' @param dtc a character vector containing the dates -#' @param is_valid a logical vector indicating whether elements in `dtc` are valid -#' -#' @return No return value, called for side effects -#' -#' @author Samia Kabi -#' -#' @keywords warning -#' -#' @export -#' -#' @examples -#' -#' ## No warning as `dtc` is a valid date format -#' warn_if_invalid_dtc(dtc = "2021-04-06") -#' -#' ## Issues a warning -#' warn_if_invalid_dtc(dtc = "2021-04-06T-:30:30") -warn_if_invalid_dtc <- function(dtc, is_valid = is_valid_dtc(dtc)) { - if (!all(is_valid)) { - incorrect_dtc <- dtc[!is_valid] - incorrect_dtc_row <- rownames(as.data.frame(dtc))[!is_valid] - tbl <- paste("Row", incorrect_dtc_row, ": --DTC =", incorrect_dtc) - main_msg <- paste( - "Dataset contains incorrect datetime format:", - "--DTC may be incorrectly imputed on row(s)" - ) - - info <- paste0( - "The following ISO representations are handled: \n", - "2003-12-15T13:15:17.123\n", - "2003-12-15T13:15:17\n", - "2003-12-15T13:15\n", - "2003-12-15T13\n", - "2003-12-15\n", - "2003-12\n", - "2003\n", - "2003---15\n\n", - "The following ISO representations, and any other representation are NOT handled: \n", - "2003-12-15T-:15:18\n", - "2003-12-15T13:-:19\n", - "--12-15\n", - "-----T07:15" - ) - warn(paste(main_msg, tbl, info, sep = "\n")) - } -} - -warn_if_incomplete_dtc <- function(dtc, n) { - is_complete_dtc <- (nchar(dtc) >= n | is.na(dtc)) - if (n == 10) { - dt_dtm <- "date" - funtext <- "convert_dtc_to_dt" - } else if (n == 19) { - dt_dtm <- "datetime" - funtext <- "convert_dtc_to_dtm" - } - if (!all(is_complete_dtc)) { - incomplete_dtc <- dtc[!is_complete_dtc] - incomplete_dtc_row <- rownames(as.data.frame(dtc))[!is_complete_dtc] - tbl <- paste("Row", incomplete_dtc_row, ": --DTC = ", incomplete_dtc) - msg <- paste0( - "Dataset contains partial ", dt_dtm, " format. ", - "The function ", funtext, " expect a complete ", dt_dtm, ". ", - "Please use the function `impute_dtc()` to build a complete ", dt_dtm, "." - ) - warn(msg) - warn(paste(capture.output(print(tbl)), collapse = "\n")) - } -} - - -#' Warn If Two Lists are Inconsistent -#' -#' Checks if two list inputs have the same names and same number of elements and -#' issues a warning otherwise. -#' -#' @param base A named list -#' -#' @param compare A named list -#' -#' @param list_name A string -#' the name of the list -#' -#' @param i the index id to compare the 2 lists -#' -#' @author Samia Kabi -#' -#' @return a `warning` if the 2 lists have different names or length -#' -#' @keywords warning -#' -#' @export -#' -#' @examples -#' # no warning -#' warn_if_inconsistent_list( -#' base = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), -#' compare = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), -#' list_name = "Test" -#' ) -#' # warning -#' warn_if_inconsistent_list( -#' base = vars(DTHDOM = "DM", DTHSEQ = DMSEQ, DTHVAR = "text"), -#' compare = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), -#' list_name = "Test" -#' ) -warn_if_inconsistent_list <- function(base, compare, list_name, i = 2) { - if (paste(sort(names(base)), collapse = " ") != paste(sort(names(compare)), collapse = " ")) { - warn( - paste0( - "The variables used for traceability in `", list_name, - "` are not consistent, please check:\n", - paste( - "source", i - 1, ", Variables are given as:", - paste(sort(names(base)), collapse = " "), "\n" - ), - paste( - "source", i, ", Variables are given as:", - paste(sort(names(compare)), collapse = " ") - ) - ) - ) - } -} - -#' Suppress Specific Warnings -#' -#' Suppress certain warnings issued by an expression. -#' -#' @param expr Expression to be executed -#' -#' @param regexpr Regular expression matching warnings to suppress -#' -#' @author -#' - Thomas Neitmann -#' - Stefan Bundfuss -#' -#' @return Return value of the expression -#' -#' @keywords warning -#' -#' @details -#' All warnings which are issued by the expression and match the regular expression -#' are suppressed. -#' -#' @export -#' -#' @examples -#' library(dplyr, warn.conflicts = FALSE) -#' library(admiral.test) -#' data(admiral_adsl) -#' data(admiral_vs) -#' -#' # Remove label -#' attr(admiral_vs$USUBJID, "label") <- NULL -#' -#' left_join(admiral_adsl, admiral_vs, by = "USUBJID") -#' -#' suppress_warning( -#' left_join(admiral_adsl, admiral_vs, by = "USUBJID"), -#' "^Column `USUBJID` has different attributes on LHS and RHS of join$" -#' ) -suppress_warning <- function(expr, regexpr) { - withCallingHandlers( - expr, - warning = function(w) { - if (grepl(regexpr, w$message)) { - invokeRestart("muffleWarning") - } - } - ) -} diff --git a/README.md b/README.md index c827cd65c3..890d390695 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,25 @@ remotes::install_github("pharmaverse/admiral.test", ref = "devel") # This is a r remotes::install_github("pharmaverse/admiral", ref = "devel") ``` +## Release Schedule + +`{admiral}` releases are targeted for the first Monday of the last month of each quarter. Pull Requests will be frozen the week before a release. +The `admiral` family has several downstream and upstream dependencies and so this release shall be done in three +Phases: + +* Phase 1 release is for `{admiraldev}` and `{admiral.test}`, which feed into all `admiral` packages +* Phase 2 release is only for core `{admiral}` +* Phase 3 release is extension packages, e.g. `{admiralonco}` + +| Release Schedule | Phase 1- Date and Packages | Phase 2- Date and Packages | Phase 3- Date and Packages | +|----------------------------|------------------------------|---------------------------- |------------------------------| +| Q4-2022 | November 28th | December 5th | December 12th | +| | `{admiraldev}` | `{admiral}` | `{admiralonco}` | +| | `{admiral.test}` | | | +| Q1-2023 | February 27th | March 6th | March 12th | +| | `{admiraldev}` | `{admiral}` | `{admiralonco}` | +| | `{admiral.test}` | | | + ## Main Goal Provide users with an open source, modularized toolbox with which to create ADaM datasets @@ -40,7 +59,7 @@ automate ADaM._ One of the key aspects of `{admiral}` is its development by the users for the users. It gives an entry point for all to collaborate, co-create and contribute to a -harmonised approach of developing ADaMs in R across the pharmaceutical industry. +harmonized approach of developing ADaMs in R across the pharmaceutical industry. ## Scope @@ -75,7 +94,7 @@ specific to algorithms and requirements for that particular TA (e.g. `{admiralon ## Admiral Manifesto -For `{admiral}` and all extension packages, we prioritise providing our users with a **simple to adopt** toolkit +For `{admiral}` and all extension packages, we prioritize providing our users with a **simple to adopt** toolkit that enables them to produce **readable** and **easily constructible** ADaM programs. The following explains our philosophy, which we try to adhere to across the `{admiral}` family of packages. There isn’t always a clear single, straightforward rule, but there are guiding principles we adhere to for `{admiral}`. @@ -116,7 +135,7 @@ so this needs to be considered carefully to keep the nesting of 3-4 functions an All `{admiral}` functions are easily findable. -* In a growing codebase, across a family of packages, we make every effort to make our functions easily findable. +* In a growing code base, across a family of packages, we make every effort to make our functions easily findable. * We use consistent naming conventions across all our functions, and provide vignettes and ADaM templates that help users to get started and build familiarity. Each `{admiral}` family package website is searchable. * We avoid repetitive functions that will do similar tasks (as explained above with study day example). @@ -125,7 +144,7 @@ extension packages will be moved to the core `{admiral}` package. ### Readability -All `{admiral}` functions follow the [Programming Strategy](https://pharmaverse.github.io/admiral/articles/programming_strategy.html) +All `{admiral}` functions follow the [Programming Strategy](https://pharmaverse.github.io/admiraldev/main/articles/programming_strategy.html) that all our developers and contributors must follow, so that all our code has a high degree of consistency and readability. * We mandate use of tidyverse (e.g. dplyr) over similar functionality existing in base R. @@ -133,14 +152,15 @@ that all our developers and contributors must follow, so that all our code has a we try to limit nesting of too many dependencies or functions. * Modularity is a focus---we don’t try to achieve too many steps in one. * All code has to be well commented. -* We recognise that a user or a Health Authority reviewer may have the wish to delve into the codebase (especially +* We recognize that a user or a Health Authority reviewer may have the wish to delve into the code base (especially given this open source setting), or users may need to extend/adapt the code for their study specific needs. We therefore want any module to be understandable to all, not only the `{admiral}` developers. ## References and Documentation * Please go to [Get Started](https://pharmaverse.github.io/admiral/articles/admiral.html) section to start using `{admiral}` -* Please see the [Programming Strategy](https://pharmaverse.github.io/admiral/articles/programming_strategy.html) to understand how functions are created +* Please see the [pharmaverse YouTube channel](https://www.youtube.com/channel/UCxQFEv8HNqM01DXzdQLCy6Q) for videos related to `{admiral}`. +* Please see the [Programming Strategy](https://pharmaverse.github.io/admiraldev/main/articles/programming_strategy.html) to understand how functions are created * Please see the [FAQ](https://pharmaverse.github.io/admiral/articles/faq.html) for the most frequent questions * Please see the [Contribution Model](https://pharmaverse.github.io/admiral/articles/contribution_model.html) for how to get involved with making contributions * Please see [FAQ: R and Package Versions](https://pharmaverse.github.io/admiral/articles/faq.html#why-do-we-use-a-certain-r-version-and-package-versions-for-development) for why we develop with certain R and package versions. diff --git a/_pkgdown.yml b/_pkgdown.yml index 43d44390ea..a3226ae031 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,4 +1,4 @@ -url: https://pharmaverse.github.io/admiral/ +url: https://pharmaverse.github.io/admiral template: bootstrap: 5 @@ -13,142 +13,128 @@ repo: issue: https://github.com/pharmaverse/admiral/issues/ user: https://github.com/ news: - cran_dates: false + cran_dates: true reference: -- title: Derivations - desc: Derivations add one or more variables or parameters to the input - dataset and return the extended dataset. +- title: Derivations for Adding Variables +- subtitle: All ADaMs - contents: - - starts_with("derive") - -- title: Computations - desc: Computations expect vectors as input and return a vector - contents: - - has_keyword('computation') - -- title: CDISC Hierarchy -- subtitle: ADaM - desc: General functions which can be used for any ADaM dataset - contents: - - has_keyword('adam') -- subtitle: BDS - desc: BDS specific functions - contents: - - has_keyword('bds') -- subtitle: OCCDS - desc: OCCDS specific functions - contents: - - has_keyword('occds') -- subtitle: ADSL - desc: Functions specific for ADSL - contents: - - has_keyword('adsl') -- subtitle: ADEG - desc: Functions specific for ADEG - contents: - - has_keyword('adeg') -- subtitle: ADVS - desc: Functions specific for ADVS - contents: - - has_keyword('advs') -- subtitle: ADEX - desc: Functions specific for ADEX - contents: - - has_keyword('adex') -- subtitle: ADAE - desc: Functions specific for ADAE - contents: - - has_keyword('adae') -- subtitle: ADCM - desc: Functions specific for ADCM - contents: - - has_keyword('adcm') -- subtitle: ADLB - desc: Functions specific for ADLB - contents: - - has_keyword('adlb') + - has_keyword("der_gen") +- subtitle: ADSL-specific + desc: Derivation Functions helpful for building the ADSL dataset +- contents: + - has_keyword("der_adsl") +- subtitle: BDS-specific + desc: Derivation Functions helpful for building the BDS datasets (e.g. advs, adlb, adeg, adex) +- contents: + - has_keyword("der_bds_gen") + - has_keyword("der_bds_findings") +- subtitle: OCCDS-specific + desc: Derivation Functions helpful for building the OCCDS datasets (e.g. adae, adcm, admh) +- contents: + - has_keyword("der_occds") -- title: Other Keywords -- subtitle: Timing - desc: Function related to timing, e.g. deriving dates, imputing dates, converting dates, deriving duration, ... - contents: - - has_keyword('timing') +- title: Derivations for Adding Parameters/Records +- subtitle: BDS-specific +- contents: + - has_keyword("der_prm_bds_findings") +- subtitle: TTE-specific +- contents: + - has_keyword("der_prm_tte") -- subtitle: High Order Functions - desc: Functions which call other derivations, e.g. on subset of input dataset or multiple times with varying parameters. - contents: - - has_keyword('high_order_function') +- title: Advanced Functions +- subtitle: Higher Order +- contents: + - has_keyword("high_order_function") +- subtitle: Metadata +- contents: + - has_keyword("metadata") +- subtitle: Other +- contents: + - has_keyword("source_specifications") -- subtitle: Source Specifications - desc: Classes defining sources for derivations where variables or observations from more than one dataset are considered, e.g., like in `derive_var_lstalvdt()` +- title: Computation Functions for Vectors +- subtitle: All ADaMs contents: - - has_keyword('source_specifications') - -- subtitle: Pre-Defined Time-to-Event Sources - desc: '`tte_source` objects defined by {admiral} that can be used as input for `derive_param_tte()`' + - has_keyword('com_date_time') +- subtitle: BDS-specific contents: - - has_keyword('tte_source') + - has_keyword('com_bds_findings') -- subtitle: Metadata - desc: Functions related to expanding or providing further context for other data - contents: - - has_keyword('metadata') -- title: ADaM Datasets +- title: Utility Functions +- subtitle: Utilities for Formatting Observations - contents: - - starts_with("admiral_") - -- title: User Utilities + - has_keyword('utils_fmt') +- subtitle: Utilities for Dataset Checking - contents: - - has_keyword("user_utility") - -- title: Developer Utilities -- subtitle: Assertions + - has_keyword('utils_ds_chk') +- subtitle: Utilities used within Derivation Functions - contents: - - has_keyword("assertion") -- subtitle: Checks + - has_keyword('utils_help') +- subtitle: Utilities for Filtering Observations - contents: - - has_keyword("check") -- subtitle: Warnings + - has_keyword('utils_fil') +- subtitle: Utilities used for Date Imputation - contents: - - has_keyword("warning") -- subtitle: Various + - has_keyword('utils_impute') +- subtitle: Utilities used for Examples and Template Scripts - contents: - - has_keyword("dev_utility") + - has_keyword('utils_examples') -- title: Miscellaneous -- contents: - - ex_single - - get_terms_from_db - - print.derivation_slice - - print.tte_source - - queries - - validate_query - - validate_sdg_select - - validate_smq_select - - assert_db_requirements -articles: -- title: User Guides - navbar: User Guides +- title: Example Datasets + desc: You can run `use_ad_template()` to produce additional datasets contents: - - admiral - - adsl - - occds - - bds_finding - - bds_exposure - - bds_tte - - imputation - - queries_dataset - - faq + - has_keyword('datasets') -- title: Developer Guides - navbar: Developer Guides - contents: - - contribution_model - - development_process - - programming_strategy - - git_usage - - writing_vignettes - - pr_review_guidance - - unit_test_guidance +- title: Deprecated +- desc: Look out for Warning and Error messages. These will guide you to deprecated functions replacements. +- contents: + - has_keyword("deprecated") + +navbar: + structure: + left: [getstarted, reference, articles, community, news] + components: + getstarted: + text: Get Started + href: articles/admiral.html + reference: + text: Reference + href: reference/index.html + community: + text: Community + menu: + - text: Contribute to admiral + href: articles/contribution_model.html + - text: Find us on Slack + href: https://app.slack.com/client/T028PB489D3/C02M8KN8269 + - text: Welcome to the Pharmaverse + href: https://pharmaverse.org/ + articles: + text: User Guides + menu: + - text: Getting Started + - text: Creating a basic ADSL + href: articles/adsl.html + - text: FAQ + href: articles/faq.html + - text: ------- + - text: "Deep Dives on ADaMs" + - text: Creating an OCCDS ADaM + href: articles/occds.html + - text: Creating a BDS Findings ADAM + href: articles/bds_finding.html + - text: Creating a BDS Exposure ADaM + href: articles/bds_exposure.html + - text: Creating a BDS Time-to-Event ADaM + href: articles/bds_tte.html + - text: "Advanced User Guides" + - text: Date and Time Imputation + href: articles/imputation.html + - text: Higher Order Functions + href: articles/higher_order.html + - text: Queries Dataset Documentation + href: articles/queries_dataset.html + - text: Lab Grading + href: articles/lab_grading.html diff --git a/data/admiral_adae.rda b/data/admiral_adae.rda deleted file mode 100644 index 0e7e346f54..0000000000 Binary files a/data/admiral_adae.rda and /dev/null differ diff --git a/data/admiral_adcm.rda b/data/admiral_adcm.rda deleted file mode 100644 index 18e90a95aa..0000000000 Binary files a/data/admiral_adcm.rda and /dev/null differ diff --git a/data/admiral_adeg.rda b/data/admiral_adeg.rda deleted file mode 100644 index 3f31ad325d..0000000000 Binary files a/data/admiral_adeg.rda and /dev/null differ diff --git a/data/admiral_adex.rda b/data/admiral_adex.rda deleted file mode 100644 index ffdfeafae9..0000000000 Binary files a/data/admiral_adex.rda and /dev/null differ diff --git a/data/admiral_adpp.rda b/data/admiral_adpp.rda deleted file mode 100644 index 74c8ef7147..0000000000 Binary files a/data/admiral_adpp.rda and /dev/null differ diff --git a/data/admiral_adsl.rda b/data/admiral_adsl.rda index aa9d19dc24..79f5921fdb 100644 Binary files a/data/admiral_adsl.rda and b/data/admiral_adsl.rda differ diff --git a/data/admiral_advs.rda b/data/admiral_advs.rda deleted file mode 100644 index 4c69c2c77d..0000000000 Binary files a/data/admiral_advs.rda and /dev/null differ diff --git a/data/atoxgr_criteria_ctcv4.rda b/data/atoxgr_criteria_ctcv4.rda new file mode 100644 index 0000000000..531581e255 Binary files /dev/null and b/data/atoxgr_criteria_ctcv4.rda differ diff --git a/data/ex_single.rda b/data/ex_single.rda index ed742b1f74..ae359ca0db 100644 Binary files a/data/ex_single.rda and b/data/ex_single.rda differ diff --git a/data/queries_mh.rda b/data/queries_mh.rda new file mode 100644 index 0000000000..43b98fa5a5 Binary files /dev/null and b/data/queries_mh.rda differ diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml new file mode 100644 index 0000000000..c5c96d0b83 --- /dev/null +++ b/docs/pkgdown.yml @@ -0,0 +1,20 @@ +pandoc: 2.11.4 +pkgdown: 2.0.3 +pkgdown_sha: ~ +articles: + admiral: admiral.html + adsl: adsl.html + bds_exposure: bds_exposure.html + bds_finding: bds_finding.html + bds_tte: bds_tte.html + contribution_model: contribution_model.html + higher_order: higher_order.html + imputation: imputation.html + lab_grading: lab_grading.html + occds: occds.html + queries_dataset: queries_dataset.html +last_built: 2022-08-26T19:51Z +urls: + reference: https://pharmaverse.github.io/admiral/reference + article: https://pharmaverse.github.io/admiral/articles + diff --git a/inst/WORDLIST b/inst/WORDLIST index fcf2adfd1f..2b0d6085f8 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,86 +1,221 @@ -ADaM ADAE ADCM -ADDV -ADEC ADEG ADEX +ADJAE ADLB -ADMH +ADPP ADSL +ADT ADVS +ADaM +ADaMIG +ADaMs AE -analytics +AEDECOD +AELLTCD +AENDT +AENDTM +AEs +ASEQ +ASTDT +ASTDTM ATC -Bazett -Bazett's +AVISITN +BASETYPE BDS -Biologics BMI +BOR +Bazett +Bazett's Biologics CDISC -Changelog -censorings -codebase -CRF +CMD +COVID CQ -cyclomatic -datepart -datetime -developers’ -dtc +CRF +CTCAE +Changelog +Codebase +Cyclomatic +DIABP +DT DTC +DTF +DTM +DURD +Datetime DuBois -durations +Durations EMA +EOSSTT FACM +Findability +Framingham Fridericia Fridericia's Fujimoto -functions’ -funder +GSK +GUILLAIN Gehan -GitHub +Github GlaxoSmithKline -groupwise -GSK Guillain -GUILLAIN GxP Hoffmann -https IG -knitr -linter LLC MedDRA +Metatools +Modularity +Mosteller +NUM +OCCDS +PARAM +PARAMCD +PARAMN +PHUSE +PLDOSE +PRs +Pandoc +Param +Pharma +Pharmacokinetics +Pre +Quosure +Quosures +RANDFL +README +ROxygen +RStudio +SAFFL +SCE +SCN +SDG +SDGs +SDTM +SDTMs +SMQ +SMQs +SUPPAE +SUPPDM +SUPPEX +SUPPQUAL +SYSBP +Sagie +Sagie's +TADJ +TADJAE +TDOSE +TDURD +TMF +TNDOSINT +TPDOSE +TTE +Takahira +Timepoint +USUBJID +VSDTC +VSTESTCD +WBC +XXXXXXXX +XYZ +adam +adamig +adeg +adex +adlb +adm +admiraldev +admiralxxx +adsl +advs +analytics +basetypes +bds +behaviour +breaks’ +cdisc +censorings +chk +constructible +cyclomatic +datepart +datetime +datetimes +der +dev +developers’ +dplyr +dropdown +ds +dtc +dthcaus +endoint +english +fil +findable +flexibilities +fmt +fulfil +func +funcs +functions’ +funder +github +groupwise +hms +https +insightsengineering +labelled +lapply +linter +lintr +lockfile +measureable metacore metatools mmHg modularized -Mosteller msec -OCCDS +nd +occds +onwards optionality +parttime pharmaverse -PHUSE +pkgs +pre +prm quosure quosures -README -RStudio -Sagie -Sagie's -SDTM -SDQ -SMQ -SMQs -stylesheet +recoded +repo +reproducibility +reults +roche +roxygen +submittable summarization -Takahira +testthat +th +tibble tidyverse +timeframe timepart timepoint +tte ungrouped unmerged +unstyled +useR +usethis +validatoR +www +xportr xpt -YAML +xxxx +yyyyy +Framingham +CTCAE +admh diff --git a/inst/adlb_grading/adlb_grading_spec.xlsx b/inst/adlb_grading/adlb_grading_spec.xlsx new file mode 100755 index 0000000000..fbcf65b908 Binary files /dev/null and b/inst/adlb_grading/adlb_grading_spec.xlsx differ diff --git a/inst/adlb_grading/atoxgr_sources.R b/inst/adlb_grading/atoxgr_sources.R new file mode 100644 index 0000000000..71dcb32f5b --- /dev/null +++ b/inst/adlb_grading/atoxgr_sources.R @@ -0,0 +1,8 @@ +atoxgr_criteria_ctcv4 <- system.file("adlb_grading/adlb_grading_spec.xlsx", package = "admiral") %>% + # Contrary to our usual convention the use of `::` here is explicit. This way we + # avoid having to list {readxl} in "Imports" and instead get away with just + # listing it in "Depends". + readxl::read_excel(sheet = "NCICTCAEv4") %>% + dplyr::mutate(GRADE_CRITERIA_CODE = gsub("[\r\n]", " ", GRADE_CRITERIA_CODE)) + +save(atoxgr_criteria_ctcv4, file = "data/atoxgr_criteria_ctcv4.rda") diff --git a/inst/dev_dependencies.R b/inst/dev_dependencies.R new file mode 100644 index 0000000000..366fe6463a --- /dev/null +++ b/inst/dev_dependencies.R @@ -0,0 +1,14 @@ +# This is a list of packages which are used when *developing* +# {admiral}. They are placed here for {renv} to recognize them. +library(covr) +library(devtools) +library(diffdf) +library(knitr) +library(lintr) +library(pkgdown) +library(rmarkdown) +library(roxygen2) +library(spelling) +library(staged.dependencies) +library(styler) +library(usethis) diff --git a/inst/example_scripts/derive_single_dose.R b/inst/example_scripts/derive_single_dose.R index eca78afa50..e94bb13d2c 100644 --- a/inst/example_scripts/derive_single_dose.R +++ b/inst/example_scripts/derive_single_dose.R @@ -1,7 +1,9 @@ +library(admiral.test) library(admiral) library(dplyr) -data(ex) +data(admiral_ex) +ex <- admiral_ex # check that there is only one start/end date of exposure per subject and visit check_cond <- ex %>% @@ -26,20 +28,30 @@ attr(dates$VISIT, "label") <- "Visit Name" ex_single <- dates %>% left_join(filter(ex, EXDOSE %in% c(0, 54)), by = c("USUBJID", "VISIT")) %>% # set dates as single doses - mutate(EXSTDTC = seq_dates, - EXENDTC = seq_dates) %>% + mutate( + EXSTDTC = seq_dates, + EXENDTC = seq_dates + ) %>% group_by(USUBJID) %>% # adjust exseq arrange(seq_dates) %>% mutate(EXSEQ = row_number()) %>% # adjust EXSTDY and EXENDY - mutate(EXSTDY = ifelse(EXSTDTC == min(EXSTDTC), 1, as.numeric(EXSTDTC - min(EXSTDTC)) + 1), - EXENDY = EXSTDY) %>% + mutate( + EXSTDY = ifelse(EXSTDTC == min(EXSTDTC), 1, as.numeric(EXSTDTC - min(EXSTDTC)) + 1), + EXENDY = EXSTDY + ) %>% ungroup() %>% - mutate(seq_dates = NULL, - EXENDTC = as.character(EXENDTC), - EXSTDTC = as.character(EXSTDTC)) + mutate( + seq_dates = NULL, + EXENDTC = as.character(EXENDTC), + EXSTDTC = as.character(EXSTDTC), + EXDOSFRQ = "ONCE" + ) attr(ex_single$EXSEQ, "label") <- attr(ex$EXSEQ, "label") attr(ex_single$EXSTDTC, "label") <- attr(ex$EXSTDTC, "label") attr(ex_single$EXENDTC, "label") <- attr(ex$EXENDTC, "label") +attr(ex_single$EXDOSFRQ, "label") <- attr(ex$EXDOSFRQ, "label") + +save(ex_single, file = file.path("data", "ex_single.rda"), compress = "bzip2") diff --git a/inst/example_scripts/example_query_source.R b/inst/example_scripts/example_query_source.R index 1402923893..10ca2c186d 100644 --- a/inst/example_scripts/example_query_source.R +++ b/inst/example_scripts/example_query_source.R @@ -56,3 +56,11 @@ adae <- tibble::tribble( # try below: derive_vars_query(adae, queries) + + +# example to use for ADMH: +queries_mh <- queries %>% + filter(TERM_LEVEL %in% c("AELLT", "AEDECOD")) %>% + mutate(TERM_LEVEL = ifelse(TERM_LEVEL == "AELLT", "MHLLT", "MHDECOD")) + +derive_vars_query(admh, queries_mh) diff --git a/inst/templates/ad_adae.R b/inst/templates/ad_adae.R index f1bcce77ea..d836cf9d35 100644 --- a/inst/templates/ad_adae.R +++ b/inst/templates/ad_adae.R @@ -8,7 +8,7 @@ library(admiral.test) # Contains example datasets from the CDISC pilot project library(dplyr) library(lubridate) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -22,11 +22,16 @@ adsl <- admiral_adsl ae <- admiral_ae suppae <- admiral_suppae +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + ae <- convert_blanks_to_na(ae) ex <- convert_blanks_to_na(ex_single) -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTEDT, DTHDT, EOSDT) @@ -38,30 +43,30 @@ adae <- ae %>% new_vars = adsl_vars, by = vars(STUDYID, USUBJID) ) %>% - # derive analysis start time + ## Derive analysis start time ---- derive_vars_dtm( dtc = AESTDTC, new_vars_prefix = "AST", - date_imputation = "first", - time_imputation = "first", + highest_imputation = "M", min_dates = vars(TRTSDT) ) %>% - # derive analysis end time + ## Derive analysis end time ---- derive_vars_dtm( dtc = AEENDTC, new_vars_prefix = "AEN", + highest_imputation = "M", date_imputation = "last", time_imputation = "last", max_dates = vars(DTHDT, EOSDT) ) %>% - # derive analysis end/start date + ## Derive analysis end/start date ---- derive_vars_dtm_to_dt(vars(ASTDTM, AENDTM)) %>% - # derive analysis start relative day and analysis end relative day + ## Derive analysis start relative day and analysis end relative day ---- derive_vars_dy( reference_date = TRTSDT, source_vars = vars(ASTDT, AENDT) ) %>% - # derive analysis duration (value and unit) + ## Derive analysis duration (value and unit) ---- derive_vars_duration( new_var = ADURN, new_var_unit = ADURU, @@ -73,29 +78,35 @@ adae <- ae %>% trunc_out = FALSE ) +ex_ext <- derive_vars_dtm( + ex, + dtc = EXSTDTC, + new_vars_prefix = "EXST", + flag_imputation = "none" +) adae <- adae %>% - # derive last dose date/time + ## Derive last dose date/time ---- derive_var_last_dose_date( - ex, + ex_ext, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_date = EXSTDTC, + !is.na(EXSTDTM), + dose_date = EXSTDTM, analysis_date = ASTDT, new_var = LDOSEDTM, single_dose_condition = (EXSTDTC == EXENDTC), output_datetime = TRUE ) %>% - # derive severity / causality / ... + ## Derive severity / causality / ... ---- mutate( ASEV = AESEV, AREL = AEREL ) %>% - # derive treatment emergent flag + ## Derive treatment emergent flag ---- mutate( TRTEMFL = ifelse(ASTDT >= TRTSDT & ASTDT <= TRTEDT + days(30), "Y", NA_character_) ) %>% - # derive occurrence flags: first occurence of most severe AE + ## Derive occurrence flags: first occurence of most severe AE ---- # create numeric value ASEVN for severity mutate( ASEVN = as.integer(factor(ASEV, levels = c("MILD", "MODERATE", "SEVERE", "DEATH THREATENING"))) @@ -104,9 +115,9 @@ adae <- adae %>% derivation = derive_var_extreme_flag, args = params( by_vars = vars(USUBJID), - order = vars(ASTDTM, AESEQ), + order = vars(desc(ASEVN), ASTDTM, AESEQ), new_var = AOCCIFL, - mode = "last" + mode = "first" ), filter = TRTEMFL == "Y" ) @@ -119,7 +130,7 @@ adae <- adae %>% ) -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adae, file = file.path(dir, "adae.rda"), compress = "bzip2") diff --git a/inst/templates/ad_adcm.R b/inst/templates/ad_adcm.R index bab7b090d4..fb7c648897 100644 --- a/inst/templates/ad_adcm.R +++ b/inst/templates/ad_adcm.R @@ -8,7 +8,7 @@ library(admiral.test) # Contains example datasets from the CDISC pilot project library(dplyr) library(lubridate) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -20,14 +20,18 @@ data("admiral_adsl") adsl <- admiral_adsl cm <- admiral_cm +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + cm <- convert_blanks_to_na(cm) -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTEDT, DTHDT, EOSDT, TRT01P, TRT01A) -# Derive flags adcm <- cm %>% # Join ADSL with CM (only ADSL vars required for derivations) derive_vars_merged( @@ -35,30 +39,30 @@ adcm <- cm %>% new_vars = adsl_vars, by = vars(STUDYID, USUBJID) ) %>% - # Derive analysis start time + ## Derive analysis start time ---- derive_vars_dtm( dtc = CMSTDTC, new_vars_prefix = "AST", - date_imputation = "first", - time_imputation = "first", + highest_imputation = "M", min_dates = vars(TRTSDT) ) %>% - # Derive analysis end time + ## Derive analysis end time ---- derive_vars_dtm( dtc = CMENDTC, new_vars_prefix = "AEN", + highest_imputation = "M", date_imputation = "last", time_imputation = "last", max_dates = vars(DTHDT, EOSDT) ) %>% - # Derive analysis end/start date + ## Derive analysis end/start date ----- derive_vars_dtm_to_dt(vars(ASTDTM, AENDTM)) %>% - # Derive analysis start relative day and analysis end relative day + ## Derive analysis start relative day and analysis end relative day ---- derive_vars_dy( reference_date = TRTSDT, source_vars = vars(ASTDT, AENDT) ) %>% - # Derive analysis duration (value and unit) + ## Derive analysis duration (value and unit) ---- derive_vars_duration( new_var = ADURN, new_var_unit = ADURU, @@ -70,7 +74,7 @@ adcm <- cm %>% trunc_out = FALSE ) -# Derive flags +## Derive flags ---- adcm <- adcm %>% # Derive On-Treatment flag # Set `span_period = "Y"` if you want occurrences that started prior to drug @@ -102,7 +106,7 @@ adcm <- adcm %>% ) -# Derive Aphase and Aphasen Variable +## Derive APHASE and APHASEN Variable ---- # Other timing variable can be derived similarly. adcm <- adcm %>% mutate( @@ -131,7 +135,7 @@ adcm <- adcm %>% ) -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adcm, file = file.path(dir, "adcm.rda"), compress = "bzip2") diff --git a/inst/templates/ad_adeg.R b/inst/templates/ad_adeg.R index de79cdb76e..c2317dc369 100644 --- a/inst/templates/ad_adeg.R +++ b/inst/templates/ad_adeg.R @@ -11,7 +11,7 @@ library(dplyr) library(lubridate) library(stringr) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. `haven::read_sas()` to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -23,14 +23,19 @@ data("admiral_eg") adsl <- admiral_adsl eg <- admiral_eg +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + eg <- convert_blanks_to_na(eg) -# ---- Lookup tables ---- +# Lookup tables ---- # Assign PARAMCD, PARAM, and PARAMN param_lookup <- tibble::tribble( ~EGTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, - "EGINTP", "EGINTP", "ECG Interpretation", 1, + "ECGINT", "EGINTP", "ECG Interpretation", 1, "HR", "HR", "Heart Rate (beats/min)", 2, "RR", "RR", "RR Duration (msec)", 3, "RRR", "RRR", "RR Duration Rederived (msec)", 4, @@ -90,7 +95,7 @@ format_chgcat1n <- function(paramcd, chg) { } -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTEDT, TRT01A, TRT01P) @@ -102,29 +107,29 @@ adeg <- eg %>% new_vars = adsl_vars, by_vars = vars(STUDYID, USUBJID) ) %>% - # Calculate ADTM, ADY + ## Calculate ADTM, ADY ---- derive_vars_dtm( new_vars_prefix = "A", dtc = EGDTC, - flag_imputation = "auto" ) %>% derive_vars_dy(reference_date = TRTSDT, source_vars = vars(ADTM)) adeg <- adeg %>% - # Add PARAMCD only (add PARAM, etc later) - derive_vars_merged( + ## Add PARAMCD only (add PARAM, etc later) ---- + derive_vars_merged_lookup( dataset_add = param_lookup, new_vars = vars(PARAMCD), by_vars = vars(EGTESTCD) ) %>% - # Calculate AVAL and AVALC + ## Calculate AVAL and AVALC ---- mutate( AVAL = EGSTRESN, AVALC = EGSTRESC ) %>% - # Derive new parameters based on existing records. Note that, for the following - # four `derive_param_*()` functions, only the variables specified in `by_vars` will - # be populated in the newly created records. + ## Derive new parameters based on existing records ---- + # Note that, for the following four `derive_param_*()` functions, only the + # variables specified in `by_vars` will be populated in the newly created + # records. # Derive RRR derive_param_rr( @@ -165,7 +170,7 @@ adeg <- adeg %>% filter = EGSTAT != "NOT DONE" | is.na(EGSTAT) ) -# get visit info +## Get visit info ---- adeg <- adeg %>% # Derive Timing mutate( @@ -186,8 +191,8 @@ adeg <- adeg %>% ), ) -# Derive a summary records representing the mean of the triplicates at each visit (if least 2 -# records available) for all parameter except EGINTP +## Derive a summary records representing the mean of the triplicates at each visit ---- +# (if least 2 records available) for all parameter except EGINTP adeg <- adeg %>% derive_summary_records( by_vars = vars(STUDYID, USUBJID, !!!adsl_vars, PARAMCD, AVISITN, AVISIT, ADT), @@ -198,7 +203,7 @@ adeg <- adeg %>% ) adeg <- adeg %>% - # Calculate ONTRTFL: from trt start up to 30 days after trt ends + ## Calculate ONTRTFL: from trt start up to 30 days after trt ends ---- derive_var_ontrtfl( start_date = ADT, ref_start_date = TRTSDT, @@ -207,7 +212,7 @@ adeg <- adeg %>% filter_pre_timepoint = AVISIT == "Baseline" ) -# Calculate ANRIND: requires the reference ranges ANRLO, ANRHI +## Calculate ANRIND: requires the reference ranges ANRLO, ANRHI ---- # Also accommodates the ranges A1LO, A1HI adeg <- adeg %>% derive_vars_merged( @@ -217,7 +222,7 @@ adeg <- adeg %>% # Calculate ANRIND derive_var_anrind() -# Derive baseline flags +## Derive baseline flags ---- adeg <- adeg %>% # Calculate BASETYPE derive_var_basetype( @@ -243,7 +248,7 @@ adeg <- adeg %>% ) ) -# Derive baseline information +## Derive baseline information ---- adeg <- adeg %>% # Calculate BASE derive_var_base( @@ -268,7 +273,7 @@ adeg <- adeg %>% # Calculate PCHG derive_var_pchg() -# ANL01FL: Flag last result within an AVISIT and ATPT for post-baseline records +## ANL01FL: Flag last result within an AVISIT and ATPT for post-baseline records ---- adeg <- adeg %>% restrict_derivation( derivation = derive_var_extreme_flag, @@ -281,13 +286,13 @@ adeg <- adeg %>% filter = !is.na(AVISITN) & ONTRTFL == "Y" ) -# Get treatment information +## Get treatment information ---- adeg <- adeg %>% # Assign TRTA, TRTP mutate(TRTP = TRT01P, TRTA = TRT01A) -# Get ASEQ and AVALCAT1/CHGCAT1 and add PARAM/PARAMN +## Get ASEQ and AVALCAT1/CHGCAT1 and add PARAM/PARAMN ---- adeg <- adeg %>% # Calculate ASEQ derive_var_obs_number( @@ -319,7 +324,7 @@ adeg <- adeg %>% ) -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adeg, file = file.path(dir, "adeg.rda"), compress = "bzip2") diff --git a/inst/templates/ad_adex.R b/inst/templates/ad_adex.R index 789acd707f..61760ed60d 100644 --- a/inst/templates/ad_adex.R +++ b/inst/templates/ad_adex.R @@ -11,6 +11,7 @@ library(dplyr) library(lubridate) library(stringr) +# Load source datasets ---- # Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. # The CDISC pilot datasets are used for demonstration purpose. @@ -20,6 +21,11 @@ data("admiral_ex") adsl <- admiral_adsl ex <- admiral_ex +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + ex <- convert_blanks_to_na(ex) # The CDISC pilot data does not contain EXADJ,nor a SUPPEX dataset @@ -46,7 +52,7 @@ ex <- ex %>% mutate(EXPLDOS = if_else(EXTRT == "PLACEBO", 0, 54)) -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTSDTM, TRTEDTM) @@ -60,22 +66,30 @@ adex0 <- ex %>% new_vars = adsl_vars, by_vars = vars(STUDYID, USUBJID) ) %>% - # Calculate ASTDTM, AENDTM using `derive_vars_dtm()` - - derive_vars_dtm(dtc = EXSTDTC, date_imputation = "first", new_vars_prefix = "AST") %>% - derive_vars_dtm(dtc = EXENDTC, date_imputation = "last", new_vars_prefix = "AEN") %>% - # Calculate ASTDY, AENDY + ## Calculate ASTDTM, AENDTM using `derive_vars_dtm()` ---- + derive_vars_dtm( + dtc = EXSTDTC, + highest_imputation = "M", + new_vars_prefix = "AST" + ) %>% + derive_vars_dtm( + dtc = EXENDTC, + highest_imputation = "M", + date_imputation = "last", + new_vars_prefix = "AEN" + ) %>% + ## Calculate ASTDY, AENDY ---- derive_vars_dy( reference_date = TRTSDTM, source_vars = vars(ASTDTM, AENDTM) ) %>% - # add EXDUR, the duration of trt for each record + ## Add EXDUR, the duration of trt for each record ---- derive_vars_duration( new_var = EXDURD, start_date = ASTDTM, end_date = AENDTM ) %>% - # Derive analysis end/start date + ## Derive analysis end/start date ---- derive_vars_dtm_to_dt(vars(ASTDTM, AENDTM)) %>% mutate( # Compute the cumulative dose @@ -83,8 +97,7 @@ adex0 <- ex %>% PDOSEO = EXPLDOS * EXDURD ) -# Part 2 -# 1:1 mapping +# Part 2: 1:1 mapping ---- adex <- bind_rows( adex0 %>% mutate(PARAMCD = "DURD", AVAL = EXDURD), @@ -95,10 +108,10 @@ adex <- bind_rows( ) %>% mutate(PARCAT1 = "INDIVIDUAL") -# Part 3 -# Derive summary parameters. Note that, for the functions `derive_param_exposure()`, -# `derive_param_doseint()` and `derive_derived_param()`, only the variables specified -# in `by_vars` will be populated in the newly created records. +# Part 3: Derive summary parameters ---- +# Note that, for the functions `derive_param_exposure()`, +# `derive_param_doseint()` and `derive_param_computed()`, only the variables +# specified in `by_vars` will be populated in the newly created records. adex <- adex %>% # Overall exposure @@ -189,7 +202,7 @@ adex <- adex %>% ) %>% # Overall/W2-24 Average daily dose call_derivation( - derivation = derive_derived_param, + derivation = derive_param_computed, variable_params = list( params( parameters = c("TDOSE", "TDURD"), @@ -207,8 +220,7 @@ adex <- adex %>% ) ) -# Part 4 -# Derive/Assign the last required variables +# Part 4: Derive/Assign the last required variables ---- # Assign PARAMCD, PARAM, and PARAMN # ---- Lookup tables ---- @@ -235,8 +247,7 @@ param_lookup <- tibble::tribble( "PDOSINT", "W2-24 dose intensity (%)", 91 ) - -# ---- User defined functions ---- +# User defined functions ---- # Derive AVALCAT1 # Here are some examples of how you can create your own functions that # operates on vectors, which can be used in `mutate()`. @@ -278,7 +289,7 @@ adex <- adex %>% # This process will be based on your metadata, no example given for this reason # ... -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adex, file = file.path(dir, "adex.rda"), compress = "bzip2") diff --git a/inst/templates/ad_adlb.R b/inst/templates/ad_adlb.R index 09ea8b98f3..b26321d7ec 100644 --- a/inst/templates/ad_adlb.R +++ b/inst/templates/ad_adlb.R @@ -9,7 +9,7 @@ library(dplyr) library(lubridate) library(stringr) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -21,9 +21,14 @@ data("admiral_adsl") lb <- admiral_lb adsl <- admiral_adsl +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + lb <- convert_blanks_to_na(lb) -# ---- Look-up tables ---- +# Look-up tables ---- # Assign PARAMCD, PARAM, and PARAMN param_lookup <- tibble::tribble( @@ -33,48 +38,52 @@ param_lookup <- tibble::tribble( "ALT", "ALT", "Alanine Aminotransferase (U/L)", 3, "ANISO", "ANISO", "Anisocytes", 4, "AST", "AST", "Aspartate Aminotransferase (U/L)", 5, - "BASO", "BASO", "Basophils (GI/L)", 6, - "BILI", "BILI", "Bilirubin (umol/L)", 7, - "BUN", "BUN", "Blood Urea Nitrogen (mmol/L)", 8, - "CA", "CA", "Calcium (mmol/L)", 9, - "CHOLES", "CHOLES", "Cholesterol (mmol/L)", 10, - "CK", "CK", "Creatinine Kinase (U/L)", 11, - "CL", "CL", "Chloride (mmol/L)", 12, - "COLOR", "COLOR", "Color", 13, - "CREAT", "CREAT", "Creatinine (umol/L)", 14, - "EOS", "EOS", "Eosinophils (GI/L)", 15, - "GGT", "GGT", "Gamma Glutamyl Transferase (U/L)", 16, - "GLUC", "GLUC", "Glucose (mmol/L)", 17, - "HBA1C", "HBA1C", "Hemoglobin A1C (1)", 18, - "HCT", "HCT", "Hematocrit (1)", 19, - "HGB", "HGB", "Hemoglobin (mmol/L)", 20, - "K", "POTAS", "Potassium (mmol/L)", 21, - "KETONES", "KETON", "Ketones", 22, - "LYM", "LYMPH", "Lymphocytes (GI/L)", 23, - "MACROCY", "MACROC", "Macrocytes", 24, - "MCH", "MCH", "Ery. Mean Corpuscular Hemoglobin (fmol(Fe))", 25, - "MCHC", "MCHC", "Ery. Mean Corpuscular HGB Concentration (mmol/L)", 26, - "MCV", "MCV", "Ery. Mean Corpuscular Volume (f/L)", 27, - "MICROCY", "MICROC", "Microcytes", 28, - "MONO", "MONO", "Monocytes (GI/L)", 29, - "PH", "PH", "pH", 30, - "PHOS", "PHOS", "Phosphate (mmol/L)", 31, - "PLAT", "PLAT", "Platelet (GI/L)", 32, - "POIKILO", "POIKIL", "Poikilocytes", 33, - "POLYCHR", "POLYCH", "Polychromasia", 34, - "PROT", "PROT", "Protein (g/L)", 35, - "RBC", "RBC", "Erythrocytes (TI/L)", 36, - "SODIUM", "SODIUM", "Sodium (mmol/L)", 37, - "SPGRAV", "SPGRAV", "Specific Gravity", 38, - "TSH", "TSH", "Thyrotropin (mU/L)", 39, - "URATE", "URATE", "Urate (umol/L)", 40, - "UROBIL", "UROBIL", "Urobilinogen", 41, - "VITB12", "VITB12", "Vitamin B12 (pmol/L)", 42, - "WBC", "WBC", "Leukocytes (GI/L)", 43 + "BASO", "BASO", "Basophils (10^9/L)", 6, + "BASOLE", "BASOLE", "Basophils/Leukocytes (FRACTION)", 7, + "BILI", "BILI", "Bilirubin (umol/L)", 8, + "BUN", "BUN", "Blood Urea Nitrogen (mmol/L)", 9, + "CA", "CA", "Calcium (mmol/L)", 10, + "CHOL", "CHOLES", "Cholesterol (mmol/L)", 11, + "CK", "CK", "Creatinine Kinase (U/L)", 12, + "CL", "CL", "Chloride (mmol/L)", 13, + "COLOR", "COLOR", "Color", 14, + "CREAT", "CREAT", "Creatinine (umol/L)", 15, + "EOS", "EOS", "Eosinophils (10^9/L)", 16, + "EOSLE", "EOSLE", "Eosinophils/Leukocytes (FRACTION)", 17, + "GGT", "GGT", "Gamma Glutamyl Transferase (U/L)", 18, + "GLUC", "GLUC", "Glucose (mmol/L)", 19, + "HBA1C", "HBA1C", "Hemoglobin A1C (1)", 20, + "HCT", "HCT", "Hematocrit (1)", 21, + "HGB", "HGB", "Hemoglobin (mmol/L)", 22, + "K", "POTAS", "Potassium (mmol/L)", 23, + "KETONES", "KETON", "Ketones", 24, + "LYM", "LYMPH", "Lymphocytes (10^9/L)", 25, + "LYMLE", "LYMPHLE", "Lymphocytes/Leukocytes (FRACTION)", 26, + "MACROCY", "MACROC", "Macrocytes", 27, + "MCH", "MCH", "Ery. Mean Corpuscular Hemoglobin (fmol(Fe))", 28, + "MCHC", "MCHC", "Ery. Mean Corpuscular HGB Concentration (mmol/L)", 29, + "MCV", "MCV", "Ery. Mean Corpuscular Volume (f/L)", 30, + "MICROCY", "MICROC", "Microcytes", 31, + "MONO", "MONO", "Monocytes (10^9/L)", 32, + "MONOLE", "MONOLE", "Monocytes/Leukocytes (FRACTION)", 33, + "PH", "PH", "pH", 34, + "PHOS", "PHOS", "Phosphate (mmol/L)", 35, + "PLAT", "PLAT", "Platelet (10^9/L)", 36, + "POIKILO", "POIKIL", "Poikilocytes", 37, + "POLYCHR", "POLYCH", "Polychromasia", 38, + "PROT", "PROT", "Protein (g/L)", 39, + "RBC", "RBC", "Erythrocytes (TI/L)", 40, + "SODIUM", "SODIUM", "Sodium (mmol/L)", 41, + "SPGRAV", "SPGRAV", "Specific Gravity", 42, + "TSH", "TSH", "Thyrotropin (mU/L)", 43, + "URATE", "URATE", "Urate (umol/L)", 44, + "UROBIL", "UROBIL", "Urobilinogen", 45, + "VITB12", "VITB12", "Vitamin B12 (pmol/L)", 46, + "WBC", "WBC", "Leukocytes (10^9/L)", 47 ) -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTEDT, TRT01A, TRT01P) @@ -86,7 +95,7 @@ adlb <- lb %>% new_vars = adsl_vars, by_vars = vars(STUDYID, USUBJID) ) %>% - # Calculate ADT, ADY + ## Calculate ADT, ADY ---- derive_vars_dt( new_vars_prefix = "A", dtc = LBDTC @@ -94,14 +103,16 @@ adlb <- lb %>% derive_vars_dy(reference_date = TRTSDT, source_vars = vars(ADT)) adlb <- adlb %>% - # Add PARAMCD PARAM and PARAMN - from LOOK-UP table + ## Add PARAMCD PARAM and PARAMN - from LOOK-UP table ---- # Replace with PARAMCD lookup function - derive_vars_merged( + derive_vars_merged_lookup( dataset_add = param_lookup, new_vars = vars(PARAMCD, PARAM, PARAMN), - by_vars = vars(LBTESTCD) + by_vars = vars(LBTESTCD), + check_type = "none", + print_not_mapped = FALSE ) %>% - # Calculate PARCAT1 AVAL AVALC ANRLO ANRHI + ## Calculate PARCAT1 AVAL AVALC ANRLO ANRHI ---- mutate( PARCAT1 = LBCAT, AVAL = LBSTRESN, @@ -110,7 +121,38 @@ adlb <- adlb %>% ANRHI = LBSTNRHI ) -# Get Visit Info +# Derive Absolute values from fractional Differentials using WBC +# Only derive where absolute values do not already exist +# Need to populate ANRLO and ANRHI for newly created records +adlb <- adlb %>% + # Derive absolute Basophils + derive_param_wbc_abs( + by_vars = vars(STUDYID, USUBJID, !!!adsl_vars, DOMAIN, VISIT, VISITNUM, ADT, ADY), + set_values_to = vars( + PARAMCD = "BASO", + PARAM = "Basophils Abs (10^9/L)", + PARAMN = 6, + DTYPE = "CALCULATION", + PARCAT1 = "HEMATOLOGY" + ), + get_unit_expr = extract_unit(PARAM), + diff_code = "BASOLE" + ) %>% + # Derive absolute Lymphocytes + derive_param_wbc_abs( + by_vars = vars(STUDYID, USUBJID, !!!adsl_vars, DOMAIN, VISIT, VISITNUM, ADT, ADY), + set_values_to = vars( + PARAMCD = "LYMPH", + PARAM = "Lymphocytes Abs (10^9/L)", + PARAMN = 25, + DTYPE = "CALCULATION", + PARCAT1 = "HEMATOLOGY" + ), + get_unit_expr = extract_unit(PARAM), + diff_code = "LYMPHLE" + ) + +## Get Visit Info ---- adlb <- adlb %>% # Derive Timing mutate( @@ -126,7 +168,7 @@ adlb <- adlb %>% ) adlb <- adlb %>% - # Calculate ONTRTFL + ## Calculate ONTRTFL ---- derive_var_ontrtfl( start_date = ADT, ref_start_date = TRTSDT, @@ -134,11 +176,11 @@ adlb <- adlb %>% filter_pre_timepoint = AVISIT == "Baseline" ) -# Calculate ANRIND : requires the reference ranges ANRLO, ANRHI +## Calculate ANRIND : requires the reference ranges ANRLO, ANRHI ---- adlb <- adlb %>% derive_var_anrind() -# Derive baseline flags +## Derive baseline flags ---- adlb <- adlb %>% # Calculate BASETYPE mutate( @@ -156,7 +198,7 @@ adlb <- adlb %>% filter = (!is.na(AVAL) & ADT <= TRTSDT & !is.na(BASETYPE)) ) -# Derive baseline information +## Derive baseline information ---- adlb <- adlb %>% # Calculate BASE derive_var_base( @@ -182,7 +224,77 @@ adlb <- adlb %>% derive_var_pchg() -# Calculate R2BASE, R2ANRLO and R2ANRHI +## Calculate lab grading ---- + +# Assign ATOXDSCL and ATOXDSCH to hold lab grading terms +# ATOXDSCL and ATOXDSCH hold terms defined by NCI-CTCAEv4. +grade_lookup <- tibble::tribble( + ~PARAMCD, ~ATOXDSCL, ~ATOXDSCH, + "ALB", "Hypoalbuminemia", NA_character_, + "ALKPH", NA_character_, "Alkaline phosphatase increased", + "ALT", NA_character_, "Alanine aminotransferase increased", + "AST", NA_character_, "Aspartate aminotransferase increased", + "BILI", NA_character_, "Blood bilirubin increased", + "CA", "Hypocalcemia", "Hypercalcemia", + "CHOLES", NA_character_, "Cholesterol high", + "CK", NA_character_, "CPK increased", + "CREAT", NA_character_, "Creatinine increased", + "GGT", NA_character_, "GGT increased", + "GLUC", "Hypoglycemia", "Hyperglycemia", + "HGB", "Anemia", "Hemoglobin increased", + "POTAS", "Hypokalemia", "Hyperkalemia", + "LYMPH", "CD4 lymphocytes decreased", NA_character_, + "PHOS", "Hypophosphatemia", NA_character_, + "PLAT", "Platelet count decreased", NA_character_, + "SODIUM", "Hyponatremia", "Hypernatremia", + "WBC", "White blood cell decreased", "Leukocytosis", +) + +# Add ATOXDSCL and ATOXDSCH +adlb <- adlb %>% + derive_vars_merged( + dataset_add = grade_lookup, + by_vars = vars(PARAMCD) + ) %>% + # Derive toxicity grade for low values ATOXGRL + # default metadata atoxgr_criteria_ctcv4 used + derive_var_atoxgr_dir( + new_var = ATOXGRL, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = extract_unit(PARAM) + ) %>% + # Derive toxicity grade for low values ATOXGRH + # default metadata atoxgr_criteria_ctcv4 used + derive_var_atoxgr_dir( + new_var = ATOXGRH, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = extract_unit(PARAM) + ) %>% + # (Optional) derive overall grade ATOXGR (combining ATOXGRL and ATOXGRH) + derive_var_atoxgr() %>% + # Derive baseline toxicity grade for low values BTOXGRL + derive_var_base( + by_vars = vars(STUDYID, USUBJID, PARAMCD, BASETYPE), + source_var = ATOXGRL, + new_var = BTOXGRL + ) %>% + # Derive baseline toxicity grade for high values BTOXGRH + derive_var_base( + by_vars = vars(STUDYID, USUBJID, PARAMCD, BASETYPE), + source_var = ATOXGRH, + new_var = BTOXGRH + ) %>% + # Derive baseline toxicity grade for for overall grade BTOXGR + derive_var_base( + by_vars = vars(STUDYID, USUBJID, PARAMCD, BASETYPE), + source_var = ATOXGR, + new_var = BTOXGR + ) + + +## Calculate R2BASE, R2ANRLO and R2ANRHI ---- adlb <- adlb %>% derive_var_analysis_ratio( numer_var = AVAL, @@ -197,14 +309,26 @@ adlb <- adlb %>% denom_var = ANRHI ) -# SHIFT derivation +## SHIFT derivation ---- adlb <- adlb %>% + # Derive shift from baseline for analysis indicator derive_var_shift( new_var = SHIFT1, from_var = BNRIND, to_var = ANRIND + ) %>% + # Derive shift from baseline for overall grade + restrict_derivation( + derivation = derive_var_shift, + args = params( + new_var = SHIFT2, + from_var = BTOXGR, + to_var = ATOXGR + ), + filter = !is.na(ATOXDSCL) | !is.na(ATOXDSCH) ) +## Flag variables (ANL01FL, LVOTFL) ---- # ANL01FL: Flag last result within an AVISIT for post-baseline records # LVOTFL: Flag last valid on-treatment record adlb <- adlb %>% @@ -229,7 +353,7 @@ adlb <- adlb %>% filter = ONTRTFL == "Y" ) -# Get treatment information +## Get treatment information ---- adlb <- adlb %>% # Assign TRTA, TRTP mutate( @@ -237,7 +361,7 @@ adlb <- adlb %>% TRTA = TRT01A ) -# Get extreme values +## Get extreme values ---- adlb <- adlb %>% # get MINIMUM value derive_extreme_records( @@ -279,7 +403,7 @@ adlb <- adlb %>% ) ) -# Get ASEQ +## Get ASEQ ---- adlb <- adlb %>% # Calculate ASEQ derive_var_obs_number( @@ -300,7 +424,7 @@ adlb <- adlb %>% # This process will be based on your metadata, no example given for this reason # ... -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adlb, file = file.path(dir, "adlb.rda"), compress = "bzip2") diff --git a/inst/templates/ad_admh.R b/inst/templates/ad_admh.R new file mode 100644 index 0000000000..9c7fba9e44 --- /dev/null +++ b/inst/templates/ad_admh.R @@ -0,0 +1,171 @@ +# Name: ADMH +# +# Label: Medical History Analysis Dataset +# +# Input: mh, adsl +library(admiral) +library(admiral.test) # Contains example datasets from the CDISC pilot project +library(dplyr) +library(lubridate) + +# ---- Load source datasets ---- + +# Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions +# as needed and assign to the variables below. +# For illustration purposes read in admiral test data +data("admiral_mh") +data("admiral_adsl") +data("queries_mh") + +adsl <- admiral_adsl +mh <- admiral_mh + +mh <- convert_blanks_to_na(mh) + + +# ---- Look-up tables ---- + +# Creating a look-up table for assigning MHTERMN (for derivation of company specific variable) +# (this is set to align with the order of pre-printed terms on the CRF) +mhtermn_lookup <- tibble::tribble( + ~MHTERM, ~MHTERMN, + "ALZHEIMER'S DISEASE", 1 +) + +# ---- Derivations ---- + +# Get list of ADSL vars required for derivations +adsl_vars <- vars(TRTSDT, TRTEDT, TRT01A, TRT01P, DTHDT, EOSDT) + +admh <- mh %>% + # join ADSL with MH + derive_vars_merged( + dataset_add = adsl, + new_vars = adsl_vars, + by_vars = vars(STUDYID, USUBJID) + ) %>% + # Derive analysis start date and flag + derive_vars_dt( + dtc = MHSTDTC, + new_vars_prefix = "AST", + date_imputation = "first" + ) %>% + # Derive analysis end date and flag + derive_vars_dt( + dtc = MHENDTC, + new_vars_prefix = "AEN", + date_imputation = "last", + max_dates = vars(DTHDT, EOSDT) + ) %>% + # Derive analysis start relative day and analysis end relative day + derive_vars_dy( + reference_date = TRTSDT, + source_vars = vars(ASTDT, AENDT) + ) %>% + # Derive analysis date of medical history collection - ADT (company specific variable derivation) + derive_vars_dt( + dtc = MHDTC, + new_vars_prefix = "A" + ) %>% + # Derive analysis relative day - ADY (company specific variable derivation) + derive_vars_dy( + reference_date = TRTSDT, + source_vars = vars(ADT) + ) %>% + # Derive query variables + derive_vars_query(queries_mh) %>% + # Assign the AHIST (company specific variable derivation) + mutate(AHIST = case_when( + MHENRF == "BEFORE" ~ "Past", + MHENRF %in% c("DURING", "AFTER") ~ "Current", + MHSTAT == "Not Done" ~ "Not Assessed" + )) %>% + # Derive occurrence flags + derive_var_extreme_flag( + by_vars = vars(USUBJID), + order = vars(ASTDT, MHSEQ), + new_var = AOCCFL, + mode = "first" + ) %>% + derive_var_extreme_flag( + by_vars = vars(USUBJID, MHBODSYS), + order = vars(USUBJID, MHBODSYS, MHCAT, MHDECOD, MHTERM, ASTDT, MHSEQ), + new_var = AOCCSFL, + mode = "first" + ) %>% + derive_var_extreme_flag( + by_vars = vars(USUBJID, MHDECOD), + order = vars(USUBJID, MHBODSYS, MHCAT, MHDECOD, MHTERM, ASTDT, MHSEQ), + new_var = AOCCPFL, + mode = "first" + ) %>% + # (company specific occurrence flag variables derivation) + derive_var_extreme_flag( + by_vars = vars(USUBJID), + order = vars(USUBJID, AHIST, ASTDT, MHSEQ), + new_var = AOCPFL, + mode = "first" + ) %>% + derive_var_extreme_flag( + by_vars = vars(USUBJID, MHBODSYS), + order = vars(USUBJID, AHIST, MHBODSYS, MHCAT, ASTDT, MHSEQ), + new_var = AOCPSFL, + mode = "first" + ) %>% + derive_var_extreme_flag( + by_vars = vars(USUBJID, MHDECOD), + order = vars(USUBJID, AHIST, MHBODSYS, MHCAT, MHDECOD, MHTERM, ASTDT, MHSEQ), + new_var = AOCPPFL, + mode = "first" + ) %>% + # Derive analysis flag (company specific variable derivation) + mutate(ANL01FL = ifelse(MHOCCUR != "N", "Y", NA_character_)) %>% + # Assign TRTA, TRTP (company specific variables derivation) + mutate( + TRTP = TRT01P, + TRTA = TRT01A + ) %>% + # Assign APHASE and APHASEN Variable (company specific variable derivation) + mutate( + APHASE = case_when( + ADT < TRTSDT ~ "Screening", + ADT > TRTEDT ~ "Post-Treatment", + ADT <= TRTSDT & ADT >= TRTEDT ~ "On-Treatment" + ), + APHASEN = case_when( + ADT < TRTSDT ~ 1, + ADT > TRTEDT ~ 2, + ADT <= TRTSDT & ADT >= TRTEDT ~ 3 + ) + ) + +# Derive MHTERMN (company specific variable derivation) +admh <- restrict_derivation( + admh, + derivation = derive_vars_merged, + args = params( + dataset_add = mhtermn_lookup, + by_vars = vars(MHTERM), + new_vars = vars(MHTERMN) + ), + filter = (MHPRESP == "Y") +) + + + +# Add all ADSL variables +admh <- admh %>% + derive_vars_merged( + dataset_add = select(adsl, !!!negate_vars(adsl_vars)), + by_vars = vars(STUDYID, USUBJID) + ) + + +# Final Steps, Select final variables and Add labels +# This process will be based on your metadata, no example given for this reason +# ... + +# ---- Save output ---- + +dir <- tempdir() # Change to whichever directory you want to save the dataset in +save(admh, file = file.path(dir, "admh.rda"), compress = "bzip2") diff --git a/inst/templates/ad_adpp.R b/inst/templates/ad_adpp.R index cffabbf775..775908489f 100644 --- a/inst/templates/ad_adpp.R +++ b/inst/templates/ad_adpp.R @@ -12,7 +12,7 @@ library(lubridate) library(stringr) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -22,9 +22,14 @@ library(stringr) data("admiral_pp") data("admiral_adsl") +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + pp <- convert_blanks_to_na(admiral_pp) -# ---- Lookup tables ---- +# Lookup tables ---- param_lookup <- tibble::tribble( ~PPTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, "AUCALL", "AUCALL", "AUC All", 1, @@ -58,7 +63,7 @@ avalcat_lookup <- tibble::tribble( attr(param_lookup$PPTESTCD, "label") <- "Parameter Short Name" -# ---- User defined functions ---- +# User defined functions ---- # Here are some examples of how you can create your own functions that # operates on vectors, which can be used in `mutate`. @@ -70,7 +75,7 @@ format_avalcat1n <- function(param, aval) { ) } -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTEDT, DTHDT, EOSDT, TRT01P, TRT01A) @@ -81,7 +86,7 @@ adpp <- pp %>% select(admiral_adsl, STUDYID, USUBJID, !!!adsl_vars), by = c("STUDYID", "USUBJID") ) %>% - # Calculate ADT, ADY + ## Calculate ADT, ADY ---- derive_vars_dt( new_vars_prefix = "A", dtc = PPRFDTC @@ -89,12 +94,12 @@ adpp <- pp %>% derive_vars_dy(reference_date = TRTSDT, source_vars = vars(ADT)) adpp <- adpp %>% - # Add PARAMCD only - add PARAM etc later + ## Add PARAMCD only - add PARAM etc later ---- left_join( select(param_lookup, PPTESTCD, PARAMCD), by = "PPTESTCD" ) %>% - # Calculate AVAL and AVALC + ## Calculate AVAL and AVALC ---- mutate( AVAL = PPSTRESN, AVALC = PPSTRESC @@ -109,7 +114,7 @@ adpp <- adpp %>% ) %>% select(-DOMAIN, -PPSEQ) -# get visit info +## Get visit info ---- adpp <- adpp %>% # Derive Timing mutate( @@ -122,25 +127,25 @@ adpp <- adpp %>% ), AVISITN = VISITNUM ) %>% - # Assign TRTA, TRTP + ## Assign TRTA, TRTP ---- mutate( TRTP = TRT01P, TRTA = TRT01A ) %>% - # Derive AVALCA1N and AVALCAT1 + ## Derive AVALCA1N and AVALCAT1 ---- mutate(AVALCA1N = format_avalcat1n(param = PARAMCD, aval = AVAL)) %>% derive_vars_merged(dataset_add = avalcat_lookup, by_vars = vars(PARAMCD, AVALCA1N)) # Add all ADSL variables adpp <- adpp %>% - left_join(select(admiral_adsl, !!!admiral:::negate_vars(adsl_vars)), + left_join(admiral_adsl, by = c("STUDYID", "USUBJID") ) # Final Steps, Select final variables and Add labels # This process will be based on your metadata, no example given for this reason # ... -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adpp, file = file.path(dir, "adpp.rda"), compress = "bzip2") diff --git a/inst/templates/ad_adsl.R b/inst/templates/ad_adsl.R index 07b50dcacd..85f636a542 100644 --- a/inst/templates/ad_adsl.R +++ b/inst/templates/ad_adsl.R @@ -9,7 +9,7 @@ library(dplyr) library(lubridate) library(stringr) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. haven::read_sas to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -27,13 +27,18 @@ ex <- admiral_ex ae <- admiral_ae lb <- admiral_lb +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + dm <- convert_blanks_to_na(dm) ds <- convert_blanks_to_na(ds) ex <- convert_blanks_to_na(ex) ae <- convert_blanks_to_na(ae) lb <- convert_blanks_to_na(lb) -# ---- User defined functions ---- +# User defined functions ---- # Here are some examples of how you can create your own functions that # operates on vectors, which can be used in `mutate`. @@ -41,8 +46,8 @@ lb <- convert_blanks_to_na(lb) # Grouping format_racegr1 <- function(x) { case_when( - !is.na(x) & x == "WHITE" ~ "White", - !is.na(x) & x != "WHITE" ~ "Non-white", + x == "WHITE" ~ "White", + x != "WHITE" ~ "Non-white", TRUE ~ "Missing" ) } @@ -57,7 +62,7 @@ format_region1 <- function(x) { format_lddthgr1 <- function(x) { case_when( - !is.na(x) & x <= 30 ~ "<= 30", + x <= 30 ~ "<= 30", x > 30 ~ "> 30", TRUE ~ NA_character_ ) @@ -73,78 +78,100 @@ format_eoxxstt <- function(x) { ) } -# ---- Derivations ---- +# Derivations ---- +# impute start and end time of exposure to first and last respectively, do not impute date +ex_ext <- ex %>% + derive_vars_dtm( + dtc = EXSTDTC, + new_vars_prefix = "EXST" + ) %>% + derive_vars_dtm( + dtc = EXENDTC, + new_vars_prefix = "EXEN", + time_imputation = "last" + ) adsl <- dm %>% - # derive treatment variables (TRT01P, TRT01A) + ## derive treatment variables (TRT01P, TRT01A) ---- mutate(TRT01P = ARM, TRT01A = ACTARM) %>% - # derive treatment start date (TRTSDTM) - derive_vars_merged_dtm( - dataset_add = ex, + ## derive treatment start date (TRTSDTM) ---- + derive_vars_merged( + dataset_add = ex_ext, filter_add = (EXDOSE > 0 | (EXDOSE == 0 & - str_detect(EXTRT, "PLACEBO"))) & nchar(EXSTDTC) >= 10, - new_vars_prefix = "TRTS", - dtc = EXSTDTC, - order = vars(TRTSDTM, EXSEQ), + str_detect(EXTRT, "PLACEBO"))) & + !is.na(EXSTDTM), + new_vars = vars(TRTSDTM = EXSTDTM, TRTSTMF = EXSTTMF), + order = vars(EXSTDTM, EXSEQ), mode = "first", by_vars = vars(STUDYID, USUBJID) ) %>% - # derive treatment end date (TRTEDTM) - derive_vars_merged_dtm( - dataset_add = ex, + ## derive treatment end date (TRTEDTM) ---- + derive_vars_merged( + dataset_add = ex_ext, filter_add = (EXDOSE > 0 | (EXDOSE == 0 & - str_detect(EXTRT, "PLACEBO"))) & nchar(EXENDTC) >= 10, - new_vars_prefix = "TRTE", - dtc = EXENDTC, - time_imputation = "last", - order = vars(TRTEDTM, EXSEQ), + str_detect(EXTRT, "PLACEBO"))) & !is.na(EXENDTM), + new_vars = vars(TRTEDTM = EXENDTM, TRTETMF = EXENTMF), + order = vars(EXENDTM, EXSEQ), mode = "last", by_vars = vars(STUDYID, USUBJID) ) %>% - # Derive treatment end/start date TRTSDT/TRTEDT + ## Derive treatment end/start date TRTSDT/TRTEDT ---- derive_vars_dtm_to_dt(source_vars = vars(TRTSDTM, TRTEDTM)) %>% - # derive treatment duration (TRTDURD) - derive_var_trtdurd() %>% - # Disposition dates, status - # Screen fail date - derive_vars_merged_dt( - dataset_add = ds, + ## derive treatment duration (TRTDURD) ---- + derive_var_trtdurd() + +## Disposition dates, status ---- +# convert character date to numeric date without imputation +ds_ext <- derive_vars_dt( + ds, + dtc = DSSTDTC, + new_vars_prefix = "DSST" +) + +# Screen fail date +adsl <- adsl %>% + derive_vars_merged( + dataset_add = ds_ext, by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "SCRF", - dtc = DSSTDTC, + new_vars = vars(SCRFDT = DSSTDT), filter_add = DSCAT == "DISPOSITION EVENT" & DSDECOD == "SCREEN FAILURE" ) %>% - derive_vars_merged_dt( - dataset_add = ds, + derive_vars_merged( + dataset_add = ds_ext, by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "EOS", - dtc = DSSTDTC, + new_vars = vars(EOSDT = DSSTDT), filter_add = DSCAT == "DISPOSITION EVENT" & DSDECOD != "SCREEN FAILURE" ) %>% # EOS status derive_var_disposition_status( - dataset_ds = ds, + dataset_ds = ds_ext, new_var = EOSSTT, status_var = DSDECOD, format_new_var = format_eoxxstt, filter_ds = DSCAT == "DISPOSITION EVENT" ) %>% # Last retrieval date - derive_vars_merged_dt( - dataset_add = ds, + derive_vars_merged( + dataset_add = ds_ext, by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "FRV", - dtc = DSSTDTC, + new_vars = vars(FRVDT = DSSTDT), filter_add = DSCAT == "OTHER EVENT" & DSDECOD == "FINAL RETRIEVAL VISIT" ) %>% + # Derive Randomization Date + derive_vars_merged( + dataset_add = ds_ext, + filter_add = DSDECOD == "RANDOMIZED", + by_vars = vars(STUDYID, USUBJID), + new_vars = vars(RANDDT = DSSTDT) + ) %>% # Death date - impute partial date to first day/month derive_vars_dt( new_vars_prefix = "DTH", dtc = DTHDTC, - flag_imputation = "none", - date_imputation = "FIRST" + highest_imputation = "M", + date_imputation = "first" ) %>% # Relative Day of Death derive_vars_duration( @@ -160,47 +187,66 @@ adsl <- dm %>% add_one = FALSE ) -# Last known alive date -ae_start <- date_source( +## Last known alive date ---- +ae_start_date <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first" + date = AESTDT ) -ae_end <- date_source( +ae_end_date <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first" + date = AEENDT ) lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = nchar(LBDTC) >= 10 + date = LBDT, + filter = !is.na(LBDT) ) -adsl_date <- date_source( +trt_end_date <- date_source( dataset_name = "adsl", date = TRTEDT ) +# impute AE start and end date to first +ae_ext <- ae %>% + derive_vars_dt( + dtc = AESTDTC, + new_vars_prefix = "AEST", + highest_imputation = "M" + ) %>% + derive_vars_dt( + dtc = AEENDTC, + new_vars_prefix = "AEEN", + highest_imputation = "M" + ) + +# impute LB date to first +lb_ext <- derive_vars_dt( + lb, + dtc = LBDTC, + new_vars_prefix = "LB", + highest_imputation = "M" +) + adsl <- adsl %>% derive_var_extreme_dt( new_var = LSTALVDT, - ae_start, ae_end, lb_date, adsl_date, - source_datasets = list(ae = ae, lb = lb, adsl = adsl), + ae_start_date, ae_end_date, lb_date, trt_end_date, + source_datasets = list(ae = ae_ext, lb = lb_ext, adsl = adsl), mode = "last" ) %>% - # Age group + ## Age group ---- derive_var_agegr_fda( age_var = AGE, new_var = AGEGR1 ) %>% - # Safety population + ## Safety population ---- derive_var_merged_exist_flag( dataset_add = ex, by_vars = vars(STUDYID, USUBJID), new_var = SAFFL, condition = (EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, "PLACEBO"))) ) %>% - # Groupings and others variables + ## Groupings and others variables ---- mutate( RACEGR1 = format_racegr1(RACE), REGION1 = format_region1(COUNTRY), @@ -211,7 +257,8 @@ adsl <- adsl %>% DOMAIN = NULL ) -# ---- Save output ---- + +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(adsl, file = file.path(dir, "adsl.rda"), compress = "bzip2") diff --git a/inst/templates/ad_advs.R b/inst/templates/ad_advs.R index 71e07e5d4c..eb94c5fcb1 100644 --- a/inst/templates/ad_advs.R +++ b/inst/templates/ad_advs.R @@ -9,7 +9,7 @@ library(dplyr) library(lubridate) library(stringr) -# ---- Load source datasets ---- +# Load source datasets ---- # Use e.g. `haven::read_sas()` to read in .sas7bdat, or other suitable functions # as needed and assign to the variables below. @@ -21,9 +21,14 @@ data("admiral_adsl") adsl <- admiral_adsl vs <- admiral_vs +# When SAS datasets are imported into R using haven::read_sas(), missing +# character values from SAS appear as "" characters in R, instead of appearing +# as NA values. Further details can be obtained via the following link: +# https://pharmaverse.github.io/admiral/articles/admiral.html#handling-of-missing-values + vs <- convert_blanks_to_na(vs) -# ---- Lookup tables ---- +# Lookup tables ---- # Assign PARAMCD, PARAM, and PARAMN param_lookup <- tibble::tribble( @@ -56,7 +61,7 @@ avalcat_lookup <- tibble::tribble( "HEIGHT", 2, "<= 100 cm" ) -# ---- User defined functions ---- +# User defined functions ---- # Here are some examples of how you can create your own functions that # operates on vectors, which can be used in `mutate()`. @@ -67,7 +72,7 @@ format_avalcat1n <- function(param, aval) { ) } -# ---- Derivations ---- +# Derivations ---- # Get list of ADSL vars required for derivations adsl_vars <- vars(TRTSDT, TRTEDT, TRT01A, TRT01P) @@ -79,29 +84,29 @@ advs <- vs %>% new_vars = adsl_vars, by_vars = vars(STUDYID, USUBJID) ) %>% - # Calculate ADT, ADY + ## Calculate ADT, ADY ---- derive_vars_dt( new_vars_prefix = "A", - dtc = VSDTC, - flag_imputation = "none" + dtc = VSDTC ) %>% derive_vars_dy(reference_date = TRTSDT, source_vars = vars(ADT)) advs <- advs %>% - # Add PARAMCD only - add PARAM etc later - derive_vars_merged( + ## Add PARAMCD only - add PARAM etc later ---- + derive_vars_merged_lookup( dataset_add = param_lookup, new_vars = vars(PARAMCD), by_vars = vars(VSTESTCD) ) %>% - # Calculate AVAL and AVALC + ## Calculate AVAL and AVALC ---- mutate( AVAL = VSSTRESN, AVALC = VSSTRESC ) %>% - # Derive new parameters based on existing records. Note that, for the following - # three `derive_param_*()` functions, only the variables specified in `by_vars` will - # be populated in the newly created records. + ## Derive new parameters based on existing records ---- + # Note that, for the following three `derive_param_*()` functions, only the + # variables specified in `by_vars` will be populated in the newly created + # records. # Derive Mean Arterial Pressure derive_param_map( @@ -118,7 +123,7 @@ advs <- advs %>% get_unit_expr = VSSTRESU, filter = VSSTAT != "NOT DONE" | is.na(VSSTAT) ) %>% - # Derive Body Surface Area + # Derive Body Mass Index derive_param_bmi( by_vars = vars(STUDYID, USUBJID, !!!adsl_vars, VISIT, VISITNUM, ADT, ADY, VSTPT, VSTPTNUM), set_values_to = vars(PARAMCD = "BMI"), @@ -127,7 +132,7 @@ advs <- advs %>% ) -# get visit info +## Get visit info ---- advs <- advs %>% # Derive Timing mutate( @@ -145,7 +150,7 @@ advs <- advs %>% )) ) -# Derive a new record as a summary record (e.g. mean of the triplicates at each time point) +## Derive a new record as a summary record (e.g. mean of the triplicates at each time point) ---- advs <- advs %>% derive_summary_records( by_vars = vars(STUDYID, USUBJID, !!!adsl_vars, PARAMCD, AVISITN, AVISIT, ADT, ADY), @@ -156,7 +161,7 @@ advs <- advs %>% ) advs <- advs %>% - # Calculate ONTRTFL + ## Calculate ONTRTFL ---- derive_var_ontrtfl( start_date = ADT, ref_start_date = TRTSDT, @@ -164,14 +169,14 @@ advs <- advs %>% filter_pre_timepoint = AVISIT == "Baseline" ) -# Calculate ANRIND : requires the reference ranges ANRLO, ANRHI +## Calculate ANRIND : requires the reference ranges ANRLO, ANRHI ---- # Also accommodates the ranges A1LO, A1HI advs <- advs %>% derive_vars_merged(dataset_add = range_lookup, by_vars = vars(PARAMCD)) %>% # Calculate ANRIND derive_var_anrind() -# Derive baseline flags +## Derive baseline flags ---- advs <- advs %>% # Calculate BASETYPE derive_var_basetype( @@ -195,7 +200,7 @@ advs <- advs %>% ADT <= TRTSDT & !is.na(BASETYPE) & is.na(DTYPE)) ) -# Derive baseline information +## Derive baseline information ---- advs <- advs %>% # Calculate BASE derive_var_base( @@ -221,7 +226,7 @@ advs <- advs %>% derive_var_pchg() -# ANL01FL: Flag last result within an AVISIT and ATPT for post-baseline records +## ANL01FL: Flag last result within an AVISIT and ATPT for post-baseline records ---- advs <- advs %>% restrict_derivation( derivation = derive_var_extreme_flag, @@ -234,13 +239,9 @@ advs <- advs %>% filter = !is.na(AVISITN) & ONTRTFL == "Y" ) -# Get treatment information +## Get treatment information ---- advs <- advs %>% # Assign TRTA, TRTP - mutate( - TRTP = TRT01P, - TRTA = TRT01A - ) %>% # Create End of Treatment Record restrict_derivation( derivation = derive_var_extreme_flag, @@ -259,9 +260,13 @@ advs <- advs %>% AVISITN = 99 ) %>% union_all(advs) %>% - select(-EOTFL) + select(-EOTFL) %>% + mutate( + TRTP = TRT01P, + TRTA = TRT01A + ) -# Get ASEQ and AVALCATx and add PARAM/PARAMN +## Get ASEQ and AVALCATx and add PARAM/PARAMN ---- advs <- advs %>% # Calculate ASEQ derive_var_obs_number( @@ -288,7 +293,7 @@ advs <- advs %>% # This process will be based on your metadata, no example given for this reason # ... -# ---- Save output ---- +# Save output ---- dir <- tempdir() # Change to whichever directory you want to save the dataset in save(advs, file = file.path(dir, "advs.rda"), compress = "bzip2") diff --git a/man/admiral-package.Rd b/man/admiral-package.Rd index f99e32160e..8f414b42e1 100644 --- a/man/admiral-package.Rd +++ b/man/admiral-package.Rd @@ -13,10 +13,14 @@ A toolbox for programming Clinical Data Standards Interchange Consortium (CDISC) \seealso{ Useful links: \itemize{ - \item \url{https://pharmaverse.github.io/admiral/index.html} - \item \url{https://github.com/pharmaverse/admiral/} + \item \url{https://pharmaverse.github.io/admiral/} + \item \url{https://github.com/pharmaverse/admiral} } + +Other internal: +\code{\link{print.adam_templates}()}, +\code{\link{print.tte_source}()} } \author{ \strong{Maintainer}: Thomas Neitmann \email{thomas.neitmann@roche.com} @@ -57,9 +61,22 @@ Other contributors: \item Ondrej Slama [contributor] \item Shimeng Huang [contributor] \item James Kim [contributor] + \item Shan Lee [contributor] + \item Bill Denney [contributor] + \item Syed Mubasheer [contributor] + \item Wenyi Liu [contributor] + \item Dinakar Kulkarni [contributor] + \item Tamara Senior [contributor] + \item Jordanna Morrish [contributor] + \item Anthony Howard [contributor] + \item Barbara O'Reilly [contributor] + \item John Kirkpatrick [contributor] + \item James Black [contributor] + \item Leena Khatri [contributor] \item F. Hoffmann-La Roche AG [copyright holder, funder] \item GlaxoSmithKline LLC [copyright holder, funder] } } +\concept{internal} \keyword{internal} diff --git a/man/admiral_adae.Rd b/man/admiral_adae.Rd deleted file mode 100644 index ef52641985..0000000000 --- a/man/admiral_adae.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{admiral_adae} -\alias{admiral_adae} -\title{Adverse Event Analysis Dataset} -\format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 1191 rows and 101 columns. -} -\source{ -Derived from the \code{adsl} and \code{ae} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adae.R}) -} -\usage{ -admiral_adae -} -\description{ -An example adverse event analysis dataset -} -\keyword{datasets} diff --git a/man/admiral_adcm.Rd b/man/admiral_adcm.Rd deleted file mode 100644 index 62e1ce7a6e..0000000000 --- a/man/admiral_adcm.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{admiral_adcm} -\alias{admiral_adcm} -\title{Concomitant Medication Analysis Dataset} -\format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 7510 rows and 89 columns. -} -\source{ -Derived from the \code{adsl} and \code{cm} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adcm.R}) -} -\usage{ -admiral_adcm -} -\description{ -An example concomitant medication analysis dataset -} -\keyword{datasets} diff --git a/man/admiral_adeg.Rd b/man/admiral_adeg.Rd deleted file mode 100644 index a2441add6e..0000000000 --- a/man/admiral_adeg.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{admiral_adeg} -\alias{admiral_adeg} -\title{ECG Analysis Dataset} -\format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 78614 rows and 104 columns. -} -\source{ -Derived from the \code{adsl} and \code{eg} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adeg.R}) -} -\usage{ -admiral_adeg -} -\description{ -An example ECG analysis dataset -} -\keyword{datasets} diff --git a/man/admiral_adex.Rd b/man/admiral_adex.Rd deleted file mode 100644 index d6356be56c..0000000000 --- a/man/admiral_adex.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{admiral_adex} -\alias{admiral_adex} -\title{Exposure Analysis Dataset} -\format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 6315 rows and 87 columns. -} -\source{ -Derived from the \code{adsl} and \code{ex} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adex.R}) -} -\usage{ -admiral_adex -} -\description{ -An example exposure analysis dataset -} -\keyword{datasets} diff --git a/man/admiral_adpp.Rd b/man/admiral_adpp.Rd deleted file mode 100644 index 5c11a3e4dd..0000000000 --- a/man/admiral_adpp.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{admiral_adpp} -\alias{admiral_adpp} -\title{Pharmacokinetics Parameters Analysis Dataset} -\format{ -An object of class \code{data.frame} with 1344 rows and 72 columns. -} -\source{ -Derived from the \code{adsl} and \code{pp} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adpp.R}) -} -\usage{ -admiral_adpp -} -\description{ -An example pharmacokinetics parameters analysis dataset -} -\keyword{datasets} diff --git a/man/admiral_adsl.Rd b/man/admiral_adsl.Rd index ea193b9ae8..c106548f43 100644 --- a/man/admiral_adsl.Rd +++ b/man/admiral_adsl.Rd @@ -5,7 +5,7 @@ \alias{admiral_adsl} \title{Subject Level Analysis Dataset} \format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 306 rows and 48 columns. +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 306 rows and 50 columns. } \source{ Derived from the \code{dm} and \code{ds} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adsl.R}) @@ -16,4 +16,12 @@ admiral_adsl \description{ An example subject level analysis dataset } +\seealso{ +Other datasets: +\code{\link{atoxgr_criteria_ctcv4}}, +\code{\link{ex_single}}, +\code{\link{queries_mh}}, +\code{\link{queries}} +} +\concept{datasets} \keyword{datasets} diff --git a/man/admiral_advs.Rd b/man/admiral_advs.Rd deleted file mode 100644 index 4537ac3e62..0000000000 --- a/man/admiral_advs.Rd +++ /dev/null @@ -1,19 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{admiral_advs} -\alias{admiral_advs} -\title{Vital Signs Analysis Dataset} -\format{ -An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 57763 rows and 102 columns. -} -\source{ -Derived from the \code{adsl} and \code{vs} datasets using \code{{admiral}} (\url{https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_advs.R}) -} -\usage{ -admiral_advs -} -\description{ -An example vital signs analysis dataset -} -\keyword{datasets} diff --git a/man/assert_character_scalar.Rd b/man/assert_character_scalar.Rd deleted file mode 100644 index 0592cddded..0000000000 --- a/man/assert_character_scalar.Rd +++ /dev/null @@ -1,63 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_character_scalar} -\alias{assert_character_scalar} -\title{Is an Argument a Character Scalar (String)?} -\usage{ -assert_character_scalar( - arg, - values = NULL, - case_sensitive = TRUE, - optional = FALSE -) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{values}{A \code{character} vector of valid values for \code{arg}} - -\item{case_sensitive}{Should the argument be handled case-sensitive? -If set to \code{FALSE}, the argument is converted to lower case for checking the -permitted values and returning the argument.} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a character vector or if \code{arg} -is a character vector but of length > 1 or if its value is not one of the \code{values} -specified. Otherwise, the input is returned invisibly. -} -\description{ -Checks if an argument is a character scalar and (optionally) whether it matches -one of the provided \code{values}. -} -\examples{ -example_fun <- function(msg_type) { - assert_character_scalar(msg_type, values = c("warning", "error")) -} - -example_fun("warning") - -try(example_fun("message")) - -try(example_fun(TRUE)) - -# handling parameters case-insensitive -example_fun2 <- function(msg_type) { - msg_type <- assert_character_scalar( - msg_type, - values = c("warning", "error"), - case_sensitive = FALSE - ) - if (msg_type == "warning") { - print("A warning was requested.") - } -} - -example_fun2("Warning") -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_character_vector.Rd b/man/assert_character_vector.Rd deleted file mode 100644 index 78725d1aa4..0000000000 --- a/man/assert_character_vector.Rd +++ /dev/null @@ -1,37 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_character_vector} -\alias{assert_character_vector} -\title{Is an Argument a Character Vector?} -\usage{ -assert_character_vector(arg, values = NULL, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{values}{A \code{character} vector of valid values for \code{arg}} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a character vector or if -any element is not included in the list of valid values. Otherwise, the input -is returned invisibly. -} -\description{ -Checks if an argument is a character vector -} -\examples{ -example_fun <- function(chr) { - assert_character_vector(chr) -} - -example_fun(letters) - -try(example_fun(1:10)) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_data_frame.Rd b/man/assert_data_frame.Rd deleted file mode 100644 index 99021a6a05..0000000000 --- a/man/assert_data_frame.Rd +++ /dev/null @@ -1,50 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_data_frame} -\alias{assert_data_frame} -\title{Is an Argument a Data Frame?} -\usage{ -assert_data_frame( - arg, - required_vars = NULL, - check_is_grouped = TRUE, - optional = FALSE -) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{required_vars}{A list of variables created using \code{vars()}} - -\item{check_is_grouped}{Throw an error is \code{dataset} is grouped? Defaults to \code{TRUE}.} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a data frame or if \code{arg} -is a data frame but misses any variable specified in \code{required_vars}. Otherwise, -the input is returned invisibly. -} -\description{ -Checks if an argument is a data frame and (optionally) whether is contains -a set of required variables -} -\examples{ -library(admiral.test) -data(admiral_dm) - -example_fun <- function(dataset) { - assert_data_frame(dataset, required_vars = vars(STUDYID, USUBJID)) -} - -example_fun(admiral_dm) - -try(example_fun(dplyr::select(admiral_dm, -STUDYID))) - -try(example_fun("Not a dataset")) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_db_requirements.Rd b/man/assert_db_requirements.Rd index 3488aeb37d..88127d2a3a 100644 --- a/man/assert_db_requirements.Rd +++ b/man/assert_db_requirements.Rd @@ -38,6 +38,31 @@ An error is issued if \code{version} or \code{fun} is null. If SMQs or SDGs are requested, the version and a function to access the database must be provided. The function checks these requirements. } +\seealso{ +Source Specifications: +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} +} \author{ Stefan Bundfuss } +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/assert_filter_cond.Rd b/man/assert_filter_cond.Rd deleted file mode 100644 index 6cf3bad83d..0000000000 --- a/man/assert_filter_cond.Rd +++ /dev/null @@ -1,42 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_filter_cond} -\alias{assert_filter_cond} -\title{Is an Argument a Filter Condition?} -\usage{ -assert_filter_cond(arg, optional = FALSE) -} -\arguments{ -\item{arg}{Quosure - filtering condition.} - -\item{optional}{Logical - is the argument optional? Defaults to \code{FALSE}.} -} -\value{ -Performs necessary checks and returns \code{arg} if all pass. -Otherwise throws an informative error. -} -\description{ -Is an Argument a Filter Condition? -} -\details{ -Check if \code{arg} is a suitable filtering condition to be used in -functions like \code{subset} or \code{dplyr::filter}. -} -\examples{ -library(admiral.test) -data(admiral_dm) - -# typical usage in a function as a parameter check -example_fun <- function(dat, x) { - x <- assert_filter_cond(rlang::enquo(x)) - dplyr::filter(dat, !!x) -} - -example_fun(admiral_dm, AGE == 64) - -try(example_fun(admiral_dm, USUBJID)) -} -\author{ -Ondrej Slama -} -\keyword{assertion} diff --git a/man/assert_function.Rd b/man/assert_function.Rd deleted file mode 100644 index 99ce451916..0000000000 --- a/man/assert_function.Rd +++ /dev/null @@ -1,44 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_function} -\alias{assert_function} -\title{Is Argument a Function?} -\usage{ -assert_function(arg, params = NULL, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{params}{A character vector of expected parameter names} - -\item{optional}{Is the checked parameter optional? - -If set to \code{FALSE} and \code{arg} is \code{NULL} then an error is thrown.} -} -\value{ -The function throws an error -\itemize{ -\item if the argument is not a function or -\item if the function does not provide all parameters as specified for the -\code{params} parameter. -} -} -\description{ -Checks if the argument is a function and if all expected parameters are -provided by the function. -} -\examples{ -example_fun <- function(fun) { - assert_function(fun, params = c("x")) -} - -example_fun(mean) - -try(example_fun(1)) - -try(example_fun(sum)) -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_has_variables.Rd b/man/assert_has_variables.Rd deleted file mode 100644 index fad96b1df6..0000000000 --- a/man/assert_has_variables.Rd +++ /dev/null @@ -1,32 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_has_variables} -\alias{assert_has_variables} -\title{Does a Dataset Contain All Required Variables?} -\usage{ -assert_has_variables(dataset, required_vars) -} -\arguments{ -\item{dataset}{A \code{data.frame}} - -\item{required_vars}{A \code{character} vector of variable names} -} -\value{ -The function throws an error if any of the required variables are -missing in the input dataset. Otherwise, the dataset is returned invisibly. -} -\description{ -Checks if a dataset contains all required variables -} -\examples{ -library(admiral.test) -data(admiral_dm) - -assert_has_variables(admiral_dm, "STUDYID") - -try(assert_has_variables(admiral_dm, "AVAL")) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_integer_scalar.Rd b/man/assert_integer_scalar.Rd deleted file mode 100644 index d8b24d2aed..0000000000 --- a/man/assert_integer_scalar.Rd +++ /dev/null @@ -1,42 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_integer_scalar} -\alias{assert_integer_scalar} -\title{Is an Argument an Integer Scalar?} -\usage{ -assert_integer_scalar(arg, subset = "none", optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{subset}{A subset of integers that \code{arg} should be part of. Should be one -of \code{"none"} (the default), \code{"positive"}, \code{"non-negative"} or \code{"negative"}.} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not an integer belonging to the -specified \code{subset}. Otherwise, the input is returned invisibly. -} -\description{ -Checks if an argument is an integer scalar -} -\examples{ -example_fun <- function(num1, num2) { - assert_integer_scalar(num1, subset = "positive") - assert_integer_scalar(num2, subset = "negative") -} - -example_fun(1, -9) - -try(example_fun(1.5, -9)) - -try(example_fun(2, 0)) - -try(example_fun("2", 0)) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_list_element.Rd b/man/assert_list_element.Rd deleted file mode 100644 index 7fb469e495..0000000000 --- a/man/assert_list_element.Rd +++ /dev/null @@ -1,86 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_list_element} -\alias{assert_list_element} -\title{Is an Element of a List of Lists/Classes Fulfilling a Condition?} -\usage{ -assert_list_element(list, element, condition, message_text, ...) -} -\arguments{ -\item{list}{A list to be checked - -A list of named lists or classes is expected.} - -\item{element}{The name of an element of the lists/classes - -A character scalar is expected.} - -\item{condition}{Condition to be fulfilled - -The condition is evaluated for each element of the list. The element of the -lists/classes can be referred to by its name, e.g., \code{censor == 0} to check -the \code{censor} field of a class.} - -\item{message_text}{Text to be displayed in the message - -The text should describe the condition to be fulfilled, e.g., "For events -the censor values must be zero.".} - -\item{...}{Objects required to evaluate the condition - -If the condition contains objects apart from the element, they have to be -passed to the function. See the second example below.} -} -\value{ -An error if the condition is not meet. The input otherwise. -} -\description{ -Checks if the elements of a list of named lists/classes fulfill a certain -condition. If not, an error is issued and all elements of the list not -fulfilling the condition are listed. -} -\examples{ -death <- event_source( - dataset_name = "adsl", - filter = DTHFL == "Y", - date = DTHDT, - set_values_to = vars( - EVNTDESC = "DEATH", - SRCDOM = "ADSL", - SRCVAR = "DTHDT" - ) -) - -lstalv <- censor_source( - dataset_name = "adsl", - date = LSTALVDT, - set_values_to = vars( - EVNTDESC = "LAST KNOWN ALIVE DATE", - SRCDOM = "ADSL", - SRCVAR = "LSTALVDT" - ) -) -events <- list(death, lstalv) -try(assert_list_element( - list = events, - element = "censor", - condition = censor == 0, - message_text = "For events the censor values must be zero." -)) - -valid_datasets <- c("adrs", "adae") -try(assert_list_element( - list = events, - element = "dataset_name", - condition = dataset_name \%in\% valid_datasets, - valid_datasets = valid_datasets, - message_text = paste0( - "The dataset name must be one of the following:\n", - paste(valid_datasets, collapse = ", ") - ) -)) -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_list_of.Rd b/man/assert_list_of.Rd deleted file mode 100644 index a917d9e7c0..0000000000 --- a/man/assert_list_of.Rd +++ /dev/null @@ -1,39 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_list_of} -\alias{assert_list_of} -\title{Is an Argument a List of Objects of a Specific S3 Class?} -\usage{ -assert_list_of(arg, class, optional = TRUE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{class}{The S3 class to check for} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a list or if \code{arg} is a list but its -elements are not objects inheriting from \code{class}. Otherwise, the input is returned -invisibly. -} -\description{ -Checks if an argument is a \code{list} of objects inheriting from the S3 class specified. -} -\examples{ -example_fun <- function(list) { - assert_list_of(list, "data.frame") -} - -example_fun(list(mtcars, iris)) - -try(example_fun(list(letters, 1:10))) - -try(example_fun(c(TRUE, FALSE))) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_logical_scalar.Rd b/man/assert_logical_scalar.Rd deleted file mode 100644 index 670e7fc6d7..0000000000 --- a/man/assert_logical_scalar.Rd +++ /dev/null @@ -1,40 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_logical_scalar} -\alias{assert_logical_scalar} -\title{Is an Argument a Logical Scalar (Boolean)?} -\usage{ -assert_logical_scalar(arg, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{optional}{Is the checked parameter optional? - -If set to \code{FALSE} and \code{arg} is \code{NULL} then an error is thrown. Otherwise, -\code{NULL} is considered as valid value.} -} -\value{ -The function throws an error if \code{arg} is neither \code{TRUE} or \code{FALSE}. Otherwise, -the input is returned invisibly. -} -\description{ -Checks if an argument is a logical scalar -} -\examples{ -example_fun <- function(flag) { - assert_logical_scalar(flag) -} - -example_fun(FALSE) - -try(example_fun(NA)) - -try(example_fun(c(TRUE, FALSE, FALSE))) - -try(example_fun(1:10)) -} -\author{ -Thomas Neitmann, Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_numeric_vector.Rd b/man/assert_numeric_vector.Rd deleted file mode 100644 index 0dc067e11f..0000000000 --- a/man/assert_numeric_vector.Rd +++ /dev/null @@ -1,34 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_numeric_vector} -\alias{assert_numeric_vector} -\title{Is an Argument a Numeric Vector?} -\usage{ -assert_numeric_vector(arg, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a numeric vector. -Otherwise, the input is returned invisibly. -} -\description{ -Checks if an argument is a numeric vector -} -\examples{ -example_fun <- function(num) { - assert_numeric_vector(num) -} - -example_fun(1:10) - -try(example_fun(letters)) -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_one_to_one.Rd b/man/assert_one_to_one.Rd deleted file mode 100644 index 905fc15708..0000000000 --- a/man/assert_one_to_one.Rd +++ /dev/null @@ -1,33 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_one_to_one} -\alias{assert_one_to_one} -\title{Is There a One to One Mapping between Variables?} -\usage{ -assert_one_to_one(dataset, vars1, vars2) -} -\arguments{ -\item{dataset}{Dataset to be checked - -The variables specified for \code{vars1} and \code{vars2} are expected.} - -\item{vars1}{First list of variables} - -\item{vars2}{Second list of variables} -} -\value{ -An error if the condition is not meet. The input otherwise. -} -\description{ -Checks if there is a one to one mapping between two lists of variables. -} -\examples{ -data(admiral_adsl) -try( - assert_one_to_one(admiral_adsl, vars(SEX), vars(RACE)) -) -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_order_vars.Rd b/man/assert_order_vars.Rd deleted file mode 100644 index 750d842eab..0000000000 --- a/man/assert_order_vars.Rd +++ /dev/null @@ -1,38 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_order_vars} -\alias{assert_order_vars} -\title{Is an Argument a List of Order Variables?} -\usage{ -assert_order_vars(arg, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a list of variables or \code{desc()} -calls created using \code{vars()} and returns the input invisibly otherwise. -} -\description{ -Checks if an argument is a valid list of order variables created using \code{vars()} -} -\examples{ -example_fun <- function(by_vars) { - assert_order_vars(by_vars) -} - -example_fun(vars(USUBJID, PARAMCD, desc(AVISITN))) - -try(example_fun(rlang::exprs(USUBJID, PARAMCD))) - -try(example_fun(c("USUBJID", "PARAMCD", "VISIT"))) - -try(example_fun(vars(USUBJID, toupper(PARAMCD), -AVAL))) -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_param_does_not_exist.Rd b/man/assert_param_does_not_exist.Rd deleted file mode 100644 index 82026c15c6..0000000000 --- a/man/assert_param_does_not_exist.Rd +++ /dev/null @@ -1,29 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_param_does_not_exist} -\alias{assert_param_does_not_exist} -\title{Asserts That a Parameter Does Not Exist in the Dataset} -\usage{ -assert_param_does_not_exist(dataset, param) -} -\arguments{ -\item{dataset}{A \code{data.frame}} - -\item{param}{Parameter code to check} -} -\value{ -The function throws an error if the parameter exists in the input -dataset. Otherwise, the dataset is returned invisibly. -} -\description{ -Checks if a parameter (\code{PARAMCD}) does not exist in a dataset. -} -\examples{ -data(admiral_advs) -assert_param_does_not_exist(admiral_advs, param = "HR") -try(assert_param_does_not_exist(admiral_advs, param = "WEIGHT")) -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_s3_class.Rd b/man/assert_s3_class.Rd deleted file mode 100644 index 5fdb41907b..0000000000 --- a/man/assert_s3_class.Rd +++ /dev/null @@ -1,38 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_s3_class} -\alias{assert_s3_class} -\title{Is an Argument an Object of a Specific S3 Class?} -\usage{ -assert_s3_class(arg, class, optional = TRUE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{class}{The S3 class to check for} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is an object which does \emph{not} inherit from \code{class}. -Otherwise, the input is returned invisibly. -} -\description{ -Checks if an argument is an object inheriting from the S3 class specified. -} -\examples{ -example_fun <- function(obj) { - assert_s3_class(obj, "factor") -} - -example_fun(as.factor(letters)) - -try(example_fun(letters)) - -try(example_fun(1:10)) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_symbol.Rd b/man/assert_symbol.Rd deleted file mode 100644 index fa7bba9d76..0000000000 --- a/man/assert_symbol.Rd +++ /dev/null @@ -1,42 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_symbol} -\alias{assert_symbol} -\title{Is an Argument a Symbol?} -\usage{ -assert_symbol(arg, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked. Must be a \code{quosure}. See examples.} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a symbol and returns the input -invisibly otherwise. -} -\description{ -Checks if an argument is a symbol -} -\examples{ -library(admiral.test) -data(admiral_dm) - -example_fun <- function(dat, var) { - var <- assert_symbol(rlang::enquo(var)) - dplyr::select(dat, !!var) -} - -example_fun(admiral_dm, USUBJID) - -try(example_fun(admiral_dm)) - -try(example_fun(admiral_dm, "USUBJID")) - -try(example_fun(admiral_dm, toupper(PARAMCD))) -} -\author{ -Thomas Neitmann -} -\keyword{assertion} diff --git a/man/assert_terms.Rd b/man/assert_terms.Rd index ee6ab55b89..eebe3d3895 100644 --- a/man/assert_terms.Rd +++ b/man/assert_terms.Rd @@ -47,8 +47,31 @@ try( } \seealso{ \code{\link[=create_query_data]{create_query_data()}}, \code{\link[=query]{query()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } -\keyword{assertion} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/assert_unit.Rd b/man/assert_unit.Rd deleted file mode 100644 index b9c5a218d9..0000000000 --- a/man/assert_unit.Rd +++ /dev/null @@ -1,37 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_unit} -\alias{assert_unit} -\title{Asserts That a Parameter is Provided in the Expected Unit} -\usage{ -assert_unit(dataset, param, required_unit, get_unit_expr) -} -\arguments{ -\item{dataset}{A \code{data.frame}} - -\item{param}{Parameter code of the parameter to check} - -\item{required_unit}{Expected unit} - -\item{get_unit_expr}{Expression used to provide the unit of \code{param}} -} -\value{ -The function throws an error if the unit variable differs from the -unit for any observation of the parameter in the input dataset. Otherwise, the -dataset is returned invisibly. -} -\description{ -Checks if a parameter (\code{PARAMCD}) in a dataset is provided in the expected -unit. -} -\examples{ -data(admiral_advs) -assert_unit(admiral_advs, param = "WEIGHT", required_unit = "kg", get_unit_expr = VSSTRESU) -\dontrun{ -assert_unit(admiral_advs, param = "WEIGHT", required_unit = "g", get_unit_expr = VSSTRESU) -} -} -\author{ -Stefan Bundfuss -} -\keyword{assertion} diff --git a/man/assert_valid_queries.Rd b/man/assert_valid_queries.Rd index 72f31d657b..6eea29965d 100644 --- a/man/assert_valid_queries.Rd +++ b/man/assert_valid_queries.Rd @@ -36,7 +36,31 @@ where \code{TERM_NAME} is non-NA data("queries") assert_valid_queries(queries, "queries") } +\seealso{ +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} +} \author{ Shimeng Huang, Ondrej Slama } -\keyword{assertion} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/assert_vars.Rd b/man/assert_vars.Rd deleted file mode 100644 index 089d66558c..0000000000 --- a/man/assert_vars.Rd +++ /dev/null @@ -1,38 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_vars} -\alias{assert_vars} -\title{Is an Argument a List of Variables?} -\usage{ -assert_vars(arg, optional = FALSE) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown} -} -\value{ -The function throws an error if \code{arg} is not a list of variables created using \code{vars()} -and returns the input invisibly otherwise. -} -\description{ -Checks if an argument is a valid list of variables created using \code{vars()} -} -\examples{ -example_fun <- function(by_vars) { - assert_vars(by_vars) -} - -example_fun(vars(USUBJID, PARAMCD)) - -try(example_fun(rlang::exprs(USUBJID, PARAMCD))) - -try(example_fun(c("USUBJID", "PARAMCD", "VISIT"))) - -try(example_fun(vars(USUBJID, toupper(PARAMCD), desc(AVAL)))) -} -\author{ -Samia Kabi -} -\keyword{assertion} diff --git a/man/assert_varval_list.Rd b/man/assert_varval_list.Rd deleted file mode 100644 index 5253b77755..0000000000 --- a/man/assert_varval_list.Rd +++ /dev/null @@ -1,48 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/assertions.R -\name{assert_varval_list} -\alias{assert_varval_list} -\title{Is an Argument a Variable-Value List?} -\usage{ -assert_varval_list( - arg, - required_elements = NULL, - accept_expr = FALSE, - accept_var = FALSE, - optional = FALSE -) -} -\arguments{ -\item{arg}{A function argument to be checked} - -\item{required_elements}{A \code{character} vector of names that must be present in \code{arg}} - -\item{accept_expr}{Should expressions on the right hand side be accepted?} - -\item{accept_var}{Should unnamed variable names (e.g. \code{vars(USUBJID)}) on the -right hand side be accepted?} - -\item{optional}{Is the checked parameter optional? If set to \code{FALSE} and \code{arg} -is \code{NULL} then an error is thrown.} -} -\value{ -The function throws an error if \code{arg} is not a list of variable-value expressions. -Otherwise, the input it returned invisibly. -} -\description{ -Checks if the argument is a list of \code{quosures} where the expressions are -variable-value pairs. The value can be a symbol, a string, a numeric, or -\code{NA}. More general expression are not allowed. -} -\examples{ -example_fun <- function(vars) { - assert_varval_list(vars) -} -example_fun(vars(DTHDOM = "AE", DTHSEQ = AESEQ)) - -try(example_fun(vars("AE", DTSEQ = AESEQ))) -} -\author{ -Stefan Bundfuss, Thomas Neitmann -} -\keyword{assertion} diff --git a/man/atoxgr_criteria_ctcv4.Rd b/man/atoxgr_criteria_ctcv4.Rd new file mode 100644 index 0000000000..adec72a788 --- /dev/null +++ b/man/atoxgr_criteria_ctcv4.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{atoxgr_criteria_ctcv4} +\alias{atoxgr_criteria_ctcv4} +\title{Metadata Holding Grading Criteria for NCI-CTCAEv4} +\format{ +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 40 rows and 13 columns. +} +\usage{ +atoxgr_criteria_ctcv4 +} +\description{ +Metadata Holding Grading Criteria for NCI-CTCAEv4 +} +\details{ +This metadata has its origin in the ADLB Grading Spec Excel file which ships with \code{{admiral}} +and can be accessed using \code{system.file("adlb_grading/adlb_grading_spec.xlsx", package = "admiral")}. +The dataset contained in there has the following columns: +\itemize{ +\item \code{SOC}: variable to hold the SOC of the lab test criteria. +\item \code{TERM}: variable to hold the term describing the criteria applied to a particular lab test, +eg. 'Anemia' or 'INR Increased'. Note: the variable is case insensitive. +\item \verb{Grade 1}: Criteria defining lab value as Grade 1. +\item \verb{Grade 2}: Criteria defining lab value as Grade 2. +\item \verb{Grade 3}: Criteria defining lab value as Grade 3. +\item \verb{Grade 4}: Criteria defining lab value as Grade 4. +\item \verb{Grade 5}: Criteria defining lab value as Grade 5. +\item \code{Definition}: Holds the definition of the lab test abnormality. +\item \code{GRADE_CRITERIA_CODE}: variable to hold code that creates grade based on defined criteria. +\item \code{SI_UNIT_CHECK}: variable to hold unit of particular lab test. Used to check against input data +if criteria is based on absolute values. +\item \code{VAR_CHECK}: List of variables required to implement lab grade criteria. Use to check against +input data. +\item \code{DIRECTION}: variable to hold the direction of the abnormality of a particular lab test +value. 'L' is for LOW values, 'H' is for HIGH values. Note: the variable is case insensitive. +\item \code{COMMENT}: Holds any information regarding rationale behind implementation of grading criteria. +} + +Note: Variables \code{SOC}, \code{TERM}, \verb{Grade 1}, \verb{Grade 2},\verb{Grade 3},\verb{Grade 4},\verb{Grade 5}, \code{Definition} +are from the source document on NCI-CTC website defining the grading criteria. +From these variables only 'TERM' is used in the {admiral} code, the rest are for information and +tracability only. +} +\seealso{ +Other datasets: +\code{\link{admiral_adsl}}, +\code{\link{ex_single}}, +\code{\link{queries_mh}}, +\code{\link{queries}} +} +\author{ +Gordon Miller +} +\concept{datasets} +\keyword{datasets} diff --git a/man/call_derivation.Rd b/man/call_derivation.Rd index b53d9e822a..b7ba0b9965 100644 --- a/man/call_derivation.Rd +++ b/man/call_derivation.Rd @@ -72,9 +72,15 @@ adae \%>\% } \seealso{ \code{\link[=params]{params()}} + +Higher Order Functions: +\code{\link{derivation_slice}()}, +\code{\link{print.derivation_slice}()}, +\code{\link{restrict_derivation}()}, +\code{\link{slice_derivation}()} } \author{ Thomas Neitmann, Stefan Bundfuss, Tracey Wang } +\concept{high_order_function} \keyword{high_order_function} -\keyword{user_utility} diff --git a/man/call_user_fun.Rd b/man/call_user_fun.Rd index df9dc9dcae..fe5bb34399 100644 --- a/man/call_user_fun.Rd +++ b/man/call_user_fun.Rd @@ -27,7 +27,14 @@ try(call_user_fun(compute_bmi( weight = "hallo" ))) } +\seealso{ +Utilities used within Derivation functions: +\code{\link{extract_unit}()}, +\code{\link{get_not_mapped}()}, +\code{\link{signal_duplicate_records}()} +} \author{ Stefan Bundfuss } -\keyword{dev_utility} +\concept{utils_help} +\keyword{utils_help} diff --git a/man/censor_source.Rd b/man/censor_source.Rd index 92926ab67f..f59cd2083b 100644 --- a/man/censor_source.Rd +++ b/man/censor_source.Rd @@ -22,8 +22,7 @@ of \code{derive_param_tte()}.} \code{dataset} which are events or possible censoring time points.} \item{date}{A variable providing the date of the event or censoring. A date, -a datetime, or a character variable containing ISO 8601 dates can be -specified. An unquoted symbol is expected. +or a datetime can be specified. An unquoted symbol is expected. Refer to \code{derive_vars_dt()} to impute and derive a date from a date character vector to a date object.} @@ -58,8 +57,31 @@ censor_source( } \seealso{ \code{\link[=derive_param_tte]{derive_param_tte()}}, \code{\link[=event_source]{event_source()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/compute_bmi.Rd b/man/compute_bmi.Rd index ea095c1bad..fdb6f8f0e5 100644 --- a/man/compute_bmi.Rd +++ b/man/compute_bmi.Rd @@ -31,9 +31,18 @@ Usually this computation function can not be used with \verb{\%>\%}. \examples{ compute_bmi(height = 170, weight = 75) } +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bsa}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_rr}()} +} \author{ Pavan Kumar } -\keyword{BMI} -\keyword{adam} -\keyword{computation} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_bsa.Rd b/man/compute_bsa.Rd index 939e41bab0..94de6bc401 100644 --- a/man/compute_bsa.Rd +++ b/man/compute_bsa.Rd @@ -61,9 +61,18 @@ compute_bsa( method = "DuBois-DuBois" ) } +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_rr}()} +} \author{ Eric Simms } -\keyword{BSA} -\keyword{adam} -\keyword{computation} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_dtf.Rd b/man/compute_dtf.Rd index e47718aade..f56adc7c7a 100644 --- a/man/compute_dtf.Rd +++ b/man/compute_dtf.Rd @@ -29,8 +29,18 @@ Usually this computation function can not be used with \verb{\%>\%}. compute_dtf(dtc = "2019-07", dt = as.Date("2019-07-18")) compute_dtf(dtc = "2019", dt = as.Date("2019-07-18")) } +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_duration}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dtm}()}, +\code{\link{impute_dtc_dt}()} +} \author{ Samia Kabi } -\keyword{computation} -\keyword{timing} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/compute_duration.Rd b/man/compute_duration.Rd index 5134fb7127..0212e3ccbe 100644 --- a/man/compute_duration.Rd +++ b/man/compute_duration.Rd @@ -20,14 +20,18 @@ compute_duration( A date or date-time object is expected. Refer to \code{derive_vars_dt()} to impute and derive a date from a date -character vector to a date object.} +character vector to a date object. + +Refer to \code{convert_dtc_to_dt()} to obtain a vector of imputed dates.} \item{end_date}{The end date A date or date-time object is expected. Refer to \code{derive_vars_dt()} to impute and derive a date from a date -character vector to a date object.} +character vector to a date object. + +Refer to \code{convert_dtc_to_dt()} to obtain a vector of imputed dates.} \item{in_unit}{Input unit @@ -86,13 +90,13 @@ start to end date in the specified unit. If the end date is before the start date, the duration is negative. } \examples{ -# derive duration in days (integer), i.e., relative day +# Derive duration in days (integer), i.e., relative day compute_duration( start_date = lubridate::ymd_hms("2020-12-06T15:00:00"), end_date = lubridate::ymd_hms("2020-12-24T08:15:00") ) -# derive duration in days (float) +# Derive duration in days (float) compute_duration( start_date = lubridate::ymd_hms("2020-12-06T15:00:00"), end_date = lubridate::ymd_hms("2020-12-24T08:15:00"), @@ -100,7 +104,7 @@ compute_duration( add_one = FALSE ) -# derive age +# Derive age in years compute_duration( start_date = lubridate::ymd("1984-09-06"), end_date = lubridate::ymd("2020-02-24"), @@ -108,10 +112,28 @@ compute_duration( out_unit = "years", add_one = FALSE ) + +# Derive duration in hours +compute_duration( + start_date = lubridate::ymd_hms("2020-12-06T9:00:00"), + end_date = lubridate::ymd_hms("2020-12-06T13:30:00"), + out_unit = "hours", + floor_in = FALSE, + add_one = FALSE, +) +} +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dtm}()}, +\code{\link{impute_dtc_dt}()} } \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{computation} -\keyword{timing} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/compute_framingham.Rd b/man/compute_framingham.Rd new file mode 100644 index 0000000000..fcebbca281 --- /dev/null +++ b/man/compute_framingham.Rd @@ -0,0 +1,131 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compute_framingham.R +\name{compute_framingham} +\alias{compute_framingham} +\title{Compute Framingham Heart Study Cardiovascular Disease 10-Year Risk Score} +\usage{ +compute_framingham(sysbp, chol, cholhdl, age, sex, smokefl, diabetfl, trthypfl) +} +\arguments{ +\item{sysbp}{Systolic blood pressure + +A numeric vector is expected.} + +\item{chol}{Total serum cholesterol (mg/dL) + +A numeric vector is expected.} + +\item{cholhdl}{HDL serum cholesterol (mg/dL) + +A numeric vector is expected.} + +\item{age}{Age (years) + +A numeric vector is expected.} + +\item{sex}{Gender + +A character vector is expected. +Expected Values: 'M' 'F'} + +\item{smokefl}{Smoking Status + +A character vector is expected. +Expected Values: 'Y' 'N'} + +\item{diabetfl}{Diabetic Status + +A character vector is expected. +Expected Values: 'Y' 'N'} + +\item{trthypfl}{Treated for hypertension status + +A character vector is expected. +Expected Values: 'Y' 'N'} +} +\value{ +A numeric vector of Framingham values +} +\description{ +Computes Framingham Heart Study Cardiovascular Disease 10-Year Risk Score +(FCVD101) based on systolic blood pressure, total serum cholesterol (mg/dL), +HDL serum cholesterol (mg/dL), sex, smoking status, diabetic status, +and treated for hypertension flag. +} +\details{ +The predicted probability of having cardiovascular disease (CVD) +within 10-years according to Framingham formula +\href{https://www.ahajournals.org/doi/pdf/10.1161/CIRCULATIONAHA.107.699579}{D'Agostino, 2008} is: # nolint + +\strong{For Women:} + +\tabular{rr}{ +\strong{Factor} \tab \strong{Amount} \cr +Age \tab 2.32888 \cr +Total Chol \tab 1.20904 \cr +HDL Chol \tab -0.70833 \cr +Sys BP \tab 2.76157 \cr +Sys BP + Hypertension Meds \tab 2.82263 \cr +Smoker \tab 0.52873 \cr +Non-Smoker \tab 0 \cr +Diabetic \tab 0.69154 \cr +Not Diabetic \tab 0 \cr +Average Risk \tab 26.1931 \cr +Risk Period \tab 0.95012 \cr +} + +\strong{For Men:} + +\tabular{rr}{ +\strong{Factor} \tab \strong{Amount} \cr +Age \tab 3.06117 \cr +Total Chol \tab 1.12370 \cr +HDL Chol \tab -0.93263 \cr +Sys BP \tab 1.93303 \cr +Sys BP + Hypertension Meds \tab 2.99881 \cr +Smoker \tab .65451 \cr +Non-Smoker \tab 0 \cr +Diabetic \tab 0.57367 \cr +Not Diabetic \tab 0 \cr +Average Risk \tab 23.9802 \cr +Risk Period \tab 0.88936 \cr +} + +\strong{The equation for calculating risk:} + +\deqn{RiskFactors = (log(Age) * AgeFactor) ++ (log(TotalChol) * TotalCholFactor) ++ (log(CholHDL) * CholHDLFactor) \\ ++ (log(SysBP) * SysBPFactor) + Smoker ++ Diabetes Present - AvgRisk} + +\deqn{Risk = 100 * (1 - RiskPeriodFactor ^ exp(RiskFactors))} +} +\examples{ +compute_framingham( + sysbp = 133, chol = 216.16, cholhdl = 54.91, age = 53, + sex = "M", smokefl = "N", diabetfl = "N", trthypfl = "N" +) + +compute_framingham( + sysbp = 161, chol = 186.39, cholhdl = 64.19, age = 52, + sex = "F", smokefl = "Y", diabetfl = "N", trthypfl = "Y" +) +} +\seealso{ +\code{\link[=derive_param_framingham]{derive_param_framingham()}} + +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_rr}()} +} +\author{ +Alice Ehmann +} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_map.Rd b/man/compute_map.Rd index 7a4e0ffa8a..83604a58c4 100644 --- a/man/compute_map.Rd +++ b/man/compute_map.Rd @@ -42,8 +42,18 @@ compute_map(diabp = 51, sysbp = 121) # Compute MAP based on diastolic and systolic blood pressure and heart rate compute_map(diabp = 51, sysbp = 121, hr = 59) } +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_rr}()} +} \author{ Stefan Bundfuss } -\keyword{advs} -\keyword{computation} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_qtc.Rd b/man/compute_qtc.Rd index 8cbd0b75c4..82fa6cf1f6 100644 --- a/man/compute_qtc.Rd +++ b/man/compute_qtc.Rd @@ -44,8 +44,18 @@ compute_qtc(qt = 350, rr = 56.54, method = "Fridericia") compute_qtc(qt = 350, rr = 56.54, method = "Sagie") } +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_rr}()} +} \author{ Stefan Bundfuss } -\keyword{adeg} -\keyword{computation} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_qual_imputation.Rd b/man/compute_qual_imputation.Rd new file mode 100644 index 0000000000..dcb23c4b71 --- /dev/null +++ b/man/compute_qual_imputation.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compute_qual_imputation.R +\name{compute_qual_imputation} +\alias{compute_qual_imputation} +\title{Function to Impute Values When Qualifier Exists in Character Result} +\usage{ +compute_qual_imputation(character_value, imputation_type = 1, factor = 0) +} +\arguments{ +\item{character_value}{Character version of value to be imputed} + +\item{imputation_type}{(default value=1) +Valid Values: +1: Strip <, >, = and convert to numeric. +2: imputation_type=1 and if the character value contains a < or >, the number of +of decimals associated with the character value is found and then a factor of +1/10^(number of decimals + 1) will be added/subtracted from the numeric value. +If no decimals exists, a factor of 1/10 will be added/subtracted from the value.} + +\item{factor}{Numeric value (default=0), when using \code{imputation_type} = 1, this +value can be added or subtracted when the qualifier is removed.} +} +\value{ +The imputed value +} +\description{ +Derive an imputed value +} +\examples{ +compute_qual_imputation("<40") +} +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_rr}()} +} +\author{ +Alice Ehmann Ojesh Upadhyay +} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_qual_imputation_dec.Rd b/man/compute_qual_imputation_dec.Rd new file mode 100644 index 0000000000..e8fe1a9072 --- /dev/null +++ b/man/compute_qual_imputation_dec.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/compute_qual_imputation.R +\name{compute_qual_imputation_dec} +\alias{compute_qual_imputation_dec} +\title{Compute Factor for Value Imputations When Character Value Contains < or >} +\usage{ +compute_qual_imputation_dec(character_value_decimal) +} +\arguments{ +\item{character_value_decimal}{Character value to determine decimal precision} +} +\value{ +Decimal precision value to add or subtract +} +\description{ +Function to compute factor for value imputation when character +value contains < or >. The factor is calculated using the number of decimals. +If there are no decimals, the factor is 1, otherwise the factor = 1/10^decimal +place. For example, the factor for 100 = 1, the factor for 5.4 = 1/10^1, +the factor for 5.44 = 1/10^2. This results in no additional false precision +added to the value. This is an intermediate function. +} +\details{ +Derive an imputed value +} +\examples{ +compute_qual_imputation_dec("<40.1") +} +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation}()}, +\code{\link{compute_rr}()} +} +\author{ +Alice Ehmann Ojesh Upadhyay +} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_rr.Rd b/man/compute_rr.Rd index 3609ade1e1..58b5051ed2 100644 --- a/man/compute_rr.Rd +++ b/man/compute_rr.Rd @@ -25,8 +25,18 @@ Usually this computation function can not be used with \verb{\%>\%}. \examples{ compute_rr(hr = 70.14) } +\seealso{ +BDS-Findings Functions that returns a vector: +\code{\link{compute_bmi}()}, +\code{\link{compute_bsa}()}, +\code{\link{compute_framingham}()}, +\code{\link{compute_map}()}, +\code{\link{compute_qtc}()}, +\code{\link{compute_qual_imputation_dec}()}, +\code{\link{compute_qual_imputation}()} +} \author{ Stefan Bundfuss } -\keyword{adeg} -\keyword{computation} +\concept{com_bds_findings} +\keyword{com_bds_findings} diff --git a/man/compute_tmf.Rd b/man/compute_tmf.Rd index ce8ce43329..61e4f448e4 100644 --- a/man/compute_tmf.Rd +++ b/man/compute_tmf.Rd @@ -39,8 +39,18 @@ compute_tmf(dtc = "2019-07-18T15:25", dtm = as.POSIXct("2019-07-18T15:25:00")) compute_tmf(dtc = "2019-07-18T15", dtm = as.POSIXct("2019-07-18T15:25:00")) compute_tmf(dtc = "2019-07-18", dtm = as.POSIXct("2019-07-18")) } +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_duration}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dtm}()}, +\code{\link{impute_dtc_dt}()} +} \author{ -Samia Kabi +Samia Kabi, Stefan Bundfuss } -\keyword{computation} -\keyword{timing} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/convert_blanks_to_na.Rd b/man/convert_blanks_to_na.Rd index c1bdee1af8..539538cd8f 100644 --- a/man/convert_blanks_to_na.Rd +++ b/man/convert_blanks_to_na.Rd @@ -46,7 +46,14 @@ df <- tibble::tibble( print(df) convert_blanks_to_na(df) } +\seealso{ +Utilities for Formatting Observations: +\code{\link{format_eoxxstt_default}()}, +\code{\link{format_reason_default}()}, +\code{\link{yn_to_numeric}()} +} \author{ Thomas Neitmann } -\keyword{user_utility} +\concept{utils_fmt} +\keyword{utils_fmt} diff --git a/man/convert_date_to_dtm.Rd b/man/convert_date_to_dtm.Rd index 59a37a60d3..537eec6091 100644 --- a/man/convert_date_to_dtm.Rd +++ b/man/convert_date_to_dtm.Rd @@ -6,8 +6,9 @@ \usage{ convert_date_to_dtm( dt, - date_imputation = NULL, - time_imputation = "00:00:00", + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", min_dates = NULL, max_dates = NULL, preserve = FALSE @@ -18,21 +19,44 @@ convert_date_to_dtm( A date or character date is expected in a format like \code{yyyy-mm-ddThh:mm:ss}.} -\item{date_imputation}{The value to impute the day/month when a datepart is -missing. +\item{highest_imputation}{Highest imputation level + +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified, no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. + +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. + +\emph{Default}: \code{"h"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"h"} (hour), \code{"m"} (minute), \code{"s"} (second), \code{"n"} (none, lowest +level)} -If \code{NULL}: no date imputation is performed and partial dates are returned as +\item{date_imputation}{The value to impute the day/month when a datepart is missing. -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}.} \item{time_imputation}{The value to impute the time when a timepart is missing. @@ -41,10 +65,12 @@ A character value is expected, either as a \itemize{ \item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} for the start of the day, -\item or as a keyword: \code{"FIRST"},\code{"LAST"} to impute to the start/end of a day. +\item or as a keyword: \code{"first"},\code{"last"} to impute to the start/end of a day. } -Default is \code{"00:00:00"}.} +The argument is ignored if \code{highest_imputation = "n"}. + +\emph{Default}: \code{"first"}.} \item{min_dates}{Minimum dates @@ -57,36 +83,44 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore \code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would -have changed the month although it is not missing (in the \code{dtc} date).} +have changed the month although it is not missing (in the \code{dtc} date). + +For date variables (not datetime) in the list the time is imputed to +\code{"00:00:00"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{max_dates}{Maximum dates A list of dates is expected. It is ensured that the imputed date is not after any of the specified dates, e.g., that the imputed date is not after the data cut off date. Only dates which are in the range of possible dates are -considered. A date or date-time object is expected.} +considered. A date or date-time object is expected. + +For date variables (not datetime) in the list the time is imputed to +\code{"23:59:59"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{preserve}{Preserve day if month is missing and day is present For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} -(and \code{date_imputation = "MID"}). +(and \code{date_imputation = "mid"}). Permitted Values: \code{TRUE}, \code{FALSE} -Default: \code{FALSE}} +\emph{Default}: \code{FALSE}} } \value{ A datetime object @@ -104,8 +138,18 @@ convert_date_to_dtm(as.Date("2019-07-18"), time_imputation = "23:59:59") convert_date_to_dtm("2019-07-18", time_imputation = "23:59:59") convert_date_to_dtm("2019-07-18") } +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_duration}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dtm}()}, +\code{\link{impute_dtc_dt}()} +} \author{ Samia Kabi } -\keyword{computation} -\keyword{timing} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/convert_dtc_to_dt.Rd b/man/convert_dtc_to_dt.Rd index aeebff1523..160a43b561 100644 --- a/man/convert_dtc_to_dt.Rd +++ b/man/convert_dtc_to_dt.Rd @@ -6,35 +6,53 @@ \usage{ convert_dtc_to_dt( dtc, - date_imputation = NULL, + highest_imputation = "n", + date_imputation = "first", min_dates = NULL, max_dates = NULL, preserve = FALSE ) } \arguments{ -\item{dtc}{The --DTC date to convert. +\item{dtc}{The --DTC date to convert.} -A character date is expected in a format like yyyy-mm-dd or yyyy-mm-ddThh:mm:ss. -A partial date will return a NA date and a warning will be issued: -'All formats failed to parse. No formats found.'. -Note: you can use impute_dtc function to build a complete date.} +\item{highest_imputation}{Highest imputation level -\item{date_imputation}{The value to impute the day/month when a datepart is -missing. +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. + +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. -If \code{NULL}: no date imputation is performed and partial dates are returned as +\emph{Default}: \code{"n"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"n"} (none, lowest level)} + +\item{date_imputation}{The value to impute the day/month when a datepart is missing. -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}} \item{min_dates}{Minimum dates @@ -47,13 +65,13 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} @@ -91,8 +109,18 @@ Usually this computation function can not be used with \verb{\%>\%}. convert_dtc_to_dt("2019-07-18") convert_dtc_to_dt("2019-07") } +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_duration}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{impute_dtc_dtm}()}, +\code{\link{impute_dtc_dt}()} +} \author{ Samia Kabi } -\keyword{computation} -\keyword{timing} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/convert_dtc_to_dtm.Rd b/man/convert_dtc_to_dtm.Rd index 5b73e25524..942cf5c12d 100644 --- a/man/convert_dtc_to_dtm.Rd +++ b/man/convert_dtc_to_dtm.Rd @@ -6,35 +6,55 @@ \usage{ convert_dtc_to_dtm( dtc, - date_imputation = NULL, - time_imputation = "00:00:00", + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", min_dates = NULL, max_dates = NULL, preserve = FALSE ) } \arguments{ -\item{dtc}{The \code{'--DTC'} date to convert. +\item{dtc}{The \code{'--DTC'} date to convert.} -A character date is expected in a format like \code{yyyy-mm-ddThh:mm:ss}. -A partial datetime will issue a warning. -Note: you can use \code{\link[=impute_dtc]{impute_dtc()}} function to build a complete datetime.} +\item{highest_imputation}{Highest imputation level -\item{date_imputation}{The value to impute the day/month when a datepart is -missing. +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified, no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. + +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. + +\emph{Default}: \code{"h"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"h"} (hour), \code{"m"} (minute), \code{"s"} (second), \code{"n"} (none, lowest +level)} -If \code{NULL}: no date imputation is performed and partial dates are returned as +\item{date_imputation}{The value to impute the day/month when a datepart is missing. -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}.} \item{time_imputation}{The value to impute the time when a timepart is missing. @@ -43,10 +63,12 @@ A character value is expected, either as a \itemize{ \item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} for the start of the day, -\item or as a keyword: \code{"FIRST"},\code{"LAST"} to impute to the start/end of a day. +\item or as a keyword: \code{"first"},\code{"last"} to impute to the start/end of a day. } -Default is \code{"00:00:00"}.} +The argument is ignored if \code{highest_imputation = "n"}. + +\emph{Default}: \code{"first"}.} \item{min_dates}{Minimum dates @@ -59,36 +81,44 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore \code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would -have changed the month although it is not missing (in the \code{dtc} date).} +have changed the month although it is not missing (in the \code{dtc} date). + +For date variables (not datetime) in the list the time is imputed to +\code{"00:00:00"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{max_dates}{Maximum dates A list of dates is expected. It is ensured that the imputed date is not after any of the specified dates, e.g., that the imputed date is not after the data cut off date. Only dates which are in the range of possible dates are -considered. A date or date-time object is expected.} +considered. A date or date-time object is expected. + +For date variables (not datetime) in the list the time is imputed to +\code{"23:59:59"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{preserve}{Preserve day if month is missing and day is present For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} -(and \code{date_imputation = "MID"}). +(and \code{date_imputation = "mid"}). Permitted Values: \code{TRUE}, \code{FALSE} -Default: \code{FALSE}} +\emph{Default}: \code{FALSE}} } \value{ A datetime object @@ -104,8 +134,18 @@ convert_dtc_to_dtm("2019-07-18T15:25:00") convert_dtc_to_dtm("2019-07-18T00:00:00") # note Time = 00:00:00 is not printed convert_dtc_to_dtm("2019-07-18") } +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_duration}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dtm}()}, +\code{\link{impute_dtc_dt}()} +} \author{ -Samia Kabi +Samia Kabi, Stefan Bundfuss } -\keyword{computation} -\keyword{timing} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/count_vals.Rd b/man/count_vals.Rd new file mode 100644 index 0000000000..b83512a2f8 --- /dev/null +++ b/man/count_vals.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/filter_confirmation.R +\name{count_vals} +\alias{count_vals} +\title{Count Number of Observations Where a Variable Equals a Value} +\usage{ +count_vals(var, val) +} +\arguments{ +\item{var}{A vector} + +\item{val}{A value} +} +\description{ +Count number of observations where a variable equals a value. +} +\examples{ + +library(tibble) +library(dplyr) +library(admiral) +data <- tibble::tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", + "3", 1, "CR", + "4", 1, "CR", + "4", 2, "NE", + "4", 3, "NE", + "4", 4, "CR", + "4", 5, "PR" +) + +# add variable providing the number of NEs for each subject +group_by(data, USUBJID) \%>\% + mutate(nr_nes = count_vals(var = AVALC, val = "NE")) +} +\seealso{ +Utilities for Filtering Observations: +\code{\link{filter_confirmation}()}, +\code{\link{filter_extreme}()}, +\code{\link{filter_relative}()}, +\code{\link{max_cond}()}, +\code{\link{min_cond}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_fil} +\keyword{utils_fil} diff --git a/man/create_query_data.Rd b/man/create_query_data.Rd index 1f0ef945c1..a4fe663d03 100644 --- a/man/create_query_data.Rd +++ b/man/create_query_data.Rd @@ -245,10 +245,15 @@ create_query_data( } \seealso{ \code{\link[=derive_vars_query]{derive_vars_query()}}, \code{\link[=query]{query()}}, \code{\link[=smq_select]{smq_select()}}, \code{\link[=sdg_select]{sdg_select()}}, \href{../articles/queries_dataset.html}{Queries Dataset Documentation} + +OCCDS Functions: +\code{\link{create_single_dose_dataset}()}, +\code{\link{derive_vars_atc}()}, +\code{\link{derive_vars_query}()}, +\code{\link{get_terms_from_db}()} } \author{ Stefan Bundfuss } -\keyword{adae} -\keyword{adcm} -\keyword{user_utility} +\concept{der_occds} +\keyword{der_occds} diff --git a/man/create_single_dose_dataset.Rd b/man/create_single_dose_dataset.Rd index 5f3a38f58c..39abbd53e8 100644 --- a/man/create_single_dose_dataset.Rd +++ b/man/create_single_dose_dataset.Rd @@ -8,9 +8,12 @@ create_single_dose_dataset( dataset, dose_freq = EXDOSFRQ, start_date = ASTDT, + start_datetime = ASTDTM, end_date = AENDT, + end_datetime = AENDTM, lookup_table = dose_freq_lookup, - lookup_column = CDISC_VALUE + lookup_column = CDISC_VALUE, + keep_source_vars = vars(USUBJID, EXDOSFRQ, ASTDT, ASTDTM, AENDT, AENDTM) ) } \arguments{ @@ -29,13 +32,22 @@ Permitted Values: defined by lookup table.} \item{start_date}{The start date -A date or date-time object is expected. This object cannot contain \code{NA} values. +A date object is expected. This object cannot contain \code{NA} values. Refer to \code{derive_vars_dt()} to impute and derive a date from a date character vector to a date object. Default: \code{ASTDT}} +\item{start_datetime}{The start date-time + +A date-time object is expected. This object cannot contain \code{NA} values. + +Refer to \code{derive_vars_dtm()} to impute and derive a date-time from a date +character vector to a date object. + +Default: \code{ASTDTM}} + \item{end_date}{The end date A date or date-time object is expected. This object cannot contain \code{NA} values. @@ -45,6 +57,15 @@ character vector to a date object. Default: \code{AENDT}} +\item{end_datetime}{The end date-time + +A date-time object is expected. This object cannot contain \code{NA} values. + +Refer to \code{derive_vars_dtm()} to impute and derive a date-time from a date +character vector to a date object. + +Default: \code{AENDTM}} + \item{lookup_table}{The dose frequency value lookup table The table used to look up \code{dose_freq} values and determine the appropriate @@ -63,6 +84,10 @@ Permitted Values for \code{DOSE_WINDOW}: \code{"MINUTE"}, \code{"HOUR"}, \code{" The column of \code{lookup_table}. Default: \code{CDISC_VALUE} (column of \code{dose_freq_lookup})} + +\item{keep_source_vars}{List of variables to be retained from source dataset + +Default: vars(USUBJID, EXDOSFRQ, ASTDT, ASTDTM, AENDT, AENDTM)} } \value{ The input dataset with a single dose per row. @@ -75,25 +100,31 @@ or deriving a total dose parameter in \code{ADEX} when \code{EXDOSFRQ != ONCE}. \details{ Each aggregate dose row is split into multiple rows which each represent a single dose.The number of completed dose periods between -\code{start_date} and \code{end_date} is calculated with \code{compute_duration} and -multiplied by \code{DOSE_COUNT}. For \code{DOSE_WINDOW} values of \code{"WEEK"}, -\code{"MONTH"}, and \code{"YEAR"}, \code{CONVERSION_FACTOR} is used to convert into days -the time object to be added to \code{start_date}. +\code{start_date} or \code{start_datetime} and \code{end_date} or \code{end_datetime} is +calculated with \code{compute_duration} and multiplied by \code{DOSE_COUNT}. +For \code{DOSE_WINDOW} values of \code{"WEEK"}, \code{"MONTH"}, and \code{"YEAR"}, +\code{CONVERSION_FACTOR} is used to convert into days the time object +to be added to \code{start_date}. } \examples{ # Example with default lookup library(lubridate) +library(stringr) data <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "Q2D", ymd("2021-01-01"), ymd("2021-01-07"), - "P01", "Q3D", ymd("2021-01-08"), ymd("2021-01-15"), - "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), ymd("2021-01-29") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "Q2D", ymd("2021-01-01"), ymd_hms("2021-01-01 10:30:00"), + ymd("2021-01-07"), ymd_hms("2021-01-07 11:30:00"), + "P01", "Q3D", ymd("2021-01-08"), ymd_hms("2021-01-08 12:00:00"), + ymd("2021-01-14"), ymd_hms("2021-01-14 14:00:00"), + "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), ymd_hms("2021-01-15 09:57:00"), + ymd("2021-01-29"), ymd_hms("2021-01-29 10:57:00") ) create_single_dose_dataset(data) + # Example with custom lookup custom_lookup <- tibble::tribble( @@ -103,21 +134,27 @@ custom_lookup <- tibble::tribble( ) data <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDTM, ~AENDTM, - "P01", "Q30MIN", ymd_hms("2021-01-01T06:00:00"), ymd_hms("2021-01-01T07:00:00"), - "P02", "Q90MIN", ymd_hms("2021-01-01T06:00:00"), ymd_hms("2021-01-01T09:00:00") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "Q30MIN", ymd("2021-01-01"), ymd_hms("2021-01-01T06:00:00"), + ymd("2021-01-01"), ymd_hms("2021-01-01T07:00:00"), + "P02", "Q90MIN", ymd("2021-01-01"), ymd_hms("2021-01-01T06:00:00"), + ymd("2021-01-01"), ymd_hms("2021-01-01T09:00:00") ) create_single_dose_dataset(data, lookup_table = custom_lookup, - lookup_column = Value, - start_date = ASTDTM, - end_date = AENDTM + lookup_column = Value ) } +\seealso{ +OCCDS Functions: +\code{\link{create_query_data}()}, +\code{\link{derive_vars_atc}()}, +\code{\link{derive_vars_query}()}, +\code{\link{get_terms_from_db}()} +} \author{ Michael Thorpe, Andrew Smith } -\keyword{adae} -\keyword{adex} -\keyword{user_utility} +\concept{der_occds} +\keyword{der_occds} diff --git a/man/dataset_vignette.Rd b/man/dataset_vignette.Rd deleted file mode 100644 index cd1d32a734..0000000000 --- a/man/dataset_vignette.Rd +++ /dev/null @@ -1,46 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dataset_vignette.R -\name{dataset_vignette} -\alias{dataset_vignette} -\title{Output a Dataset in a Vignette in the admiral Format} -\usage{ -dataset_vignette(dataset, display_vars = NULL, filter = NULL) -} -\arguments{ -\item{dataset}{Dataset to output in the vignette} - -\item{display_vars}{Variables selected to demonstrate the outcome of the derivation - -Permitted Values: list of variables - -Default is NULL - -If \code{display_vars} is not NULL, only the selected variables are visible in the vignette while the -other variables are hidden. They can be made visible by clicking the\verb{Choose the columns to display} button.} - -\item{filter}{Filter condition - -The specified condition is applied to the dataset before it is displayed. - -Permitted Values: a condition} -} -\value{ -A HTML table -} -\description{ -Output a dataset in a vignette with the pre-specified admiral format. -} -\examples{ -library(admiral) -library(DT) -library(dplyr) -library(admiral.test) -data("admiral_dm") - -dataset_vignette(admiral_dm) -dataset_vignette(admiral_dm, - display_vars = vars(USUBJID, RFSTDTC, DTHDTC), - filter = ARMCD == "Pbo" -) -} -\keyword{dev_utility} diff --git a/man/date_source.Rd b/man/date_source.Rd index 461cf8d4c1..5087e73318 100644 --- a/man/date_source.Rd +++ b/man/date_source.Rd @@ -8,9 +8,9 @@ date_source( dataset_name, filter = NULL, date, - date_imputation = NULL, - time_imputation = NULL, - preserve = FALSE, + date_imputation = deprecated(), + time_imputation = deprecated(), + preserve = deprecated(), traceability_vars = NULL ) } @@ -20,18 +20,17 @@ the date.} \item{filter}{An unquoted condition for filtering \code{dataset}.} -\item{date}{A variable providing a date. A date, a datetime, or a character -variable containing ISO 8601 dates can be specified. An unquoted symbol is -expected.} +\item{date}{A variable providing a date. A date or a datetime can be +specified. An unquoted symbol is expected.} -\item{date_imputation}{A string defining the date imputation for \code{date}. -See \code{date_imputation} parameter of \code{impute_dtc()} for valid values.} +\item{date_imputation}{\emph{Deprecated}, please use \code{derive_vars_dtm()} to +convert DTC variables to datetime variables in the dataset.} -\item{time_imputation}{A string defining the time imputation for \code{date}. -See \code{time_imputation} parameter of \code{impute_dtc()} for valid values.} +\item{time_imputation}{\emph{Deprecated}, please use \code{derive_vars_dtm()} to +convert DTC variables to datetime variables in the dataset.} -\item{preserve}{Should day be preserved if month is imputed for \code{date}. -See \code{preserve} parameter of \code{impute_dtc()} for details.} +\item{preserve}{\emph{Deprecated}, please use \code{derive_vars_dtm()} to convert DTC +variables to datetime variables in the dataset.} \item{traceability_vars}{A named list returned by \code{vars()} defining the traceability variables, e.g. \code{vars(LALVDOM = "AE", LALVSEQ = AESEQ, LALVVAR = "AESTDTC")}. The values must be a symbol, a character string, a numeric, @@ -46,8 +45,31 @@ Create a \code{date_source} object as input for \code{derive_var_extreme_dt()} a } \seealso{ \code{\link[=derive_var_extreme_dtm]{derive_var_extreme_dtm()}}, \code{\link[=derive_var_extreme_dt]{derive_var_extreme_dt()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/default_qtc_paramcd.Rd b/man/default_qtc_paramcd.Rd index db9eafc109..ed38b62288 100644 --- a/man/default_qtc_paramcd.Rd +++ b/man/default_qtc_paramcd.Rd @@ -21,7 +21,25 @@ Get Default Parameter Code for Corrected QT \examples{ default_qtc_paramcd("Sagie") } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Thomas Neitmann } -\keyword{user_utility} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derivation_slice.Rd b/man/derivation_slice.Rd index c891441847..d6080f1659 100644 --- a/man/derivation_slice.Rd +++ b/man/derivation_slice.Rd @@ -24,8 +24,15 @@ Create a \code{derivation_slice} object as input for \code{slice_derivation()}. } \seealso{ \code{\link[=slice_derivation]{slice_derivation()}}, \code{\link[=params]{params()}} + +Higher Order Functions: +\code{\link{call_derivation}()}, +\code{\link{print.derivation_slice}()}, +\code{\link{restrict_derivation}()}, +\code{\link{slice_derivation}()} } \author{ Stefan Bundfuss } -\keyword{source_specifications} +\concept{high_order_function} +\keyword{high_order_function} diff --git a/man/derive_derived_param.Rd b/man/derive_derived_param.Rd index 4f5dc91b04..4bd2d1eb84 100644 --- a/man/derive_derived_param.Rd +++ b/man/derive_derived_param.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_derived_param.R +% Please edit documentation in R/derive_param_computed.R \name{derive_derived_param} \alias{derive_derived_param} \title{Adds a Parameter Computed from the Analysis Value of Other Parameters} @@ -87,79 +87,16 @@ The input dataset with the new parameter added. Note, a variable will only be populated in the new parameter rows if it is specified in \code{by_vars}. } \description{ -Adds a parameter computed from the analysis value of other parameters. It is -expected that the analysis value of the new parameter is defined by an -expression using the analysis values of other parameters. For example mean -arterial pressure (MAP) can be derived from systolic (SYSBP) and diastolic -blood pressure (DIABP) with the formula -\deqn{MAP = \frac{SYSBP + 2 DIABP}{3}}{MAP = (SYSBP + 2 DIABP) / 3} -} -\details{ -For each group (with respect to the variables specified for the -\code{by_vars} parameter) an observation is added to the output dataset if the -filtered input dataset contains exactly one observation for each parameter -code specified for \code{parameters}. - -For the new observations \code{AVAL} is set to the value specified by -\code{analysis_value} and the variables specified for \code{set_values_to} are set to -the provided values. The values of the other variables of the input dataset -are set to \code{NA}. -} -\examples{ -# Example 1: Derive MAP -advs <- tibble::tribble( - ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, ~VISIT, - "01-701-1015", "DIABP", "Diastolic Blood Pressure (mmHg)", 51, "mmHg", "BASELINE", - "01-701-1015", "DIABP", "Diastolic Blood Pressure (mmHg)", 50, "mmHg", "WEEK 2", - "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "BASELINE", - "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "WEEK 2", - "01-701-1028", "DIABP", "Diastolic Blood Pressure (mmHg)", 79, "mmHg", "BASELINE", - "01-701-1028", "DIABP", "Diastolic Blood Pressure (mmHg)", 80, "mmHg", "WEEK 2", - "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 130, "mmHg", "BASELINE", - "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 132, "mmHg", "WEEK 2" -) - -derive_derived_param( - advs, - by_vars = vars(USUBJID, VISIT), - parameters = c("SYSBP", "DIABP"), - analysis_value = (AVAL.SYSBP + 2 * AVAL.DIABP) / 3, - set_values_to = vars( - PARAMCD = "MAP", - PARAM = "Mean Arterial Pressure (mmHg)", - AVALU = "mmHg" - ) -) +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} -# Example 2: Derive BMI where height is measured only once -advs <- tibble::tribble( - ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, ~VISIT, - "01-701-1015", "HEIGHT", "Height (cm)", 147, "cm", "SCREENING", - "01-701-1015", "WEIGHT", "Weight (kg)", 54.0, "kg", "SCREENING", - "01-701-1015", "WEIGHT", "Weight (kg)", 54.4, "kg", "BASELINE", - "01-701-1015", "WEIGHT", "Weight (kg)", 53.1, "kg", "WEEK 2", - "01-701-1028", "HEIGHT", "Height (cm)", 163, "cm", "SCREENING", - "01-701-1028", "WEIGHT", "Weight (kg)", 78.5, "kg", "SCREENING", - "01-701-1028", "WEIGHT", "Weight (kg)", 80.3, "kg", "BASELINE", - "01-701-1028", "WEIGHT", "Weight (kg)", 80.7, "kg", "WEEK 2" -) - -derive_derived_param( - advs, - by_vars = vars(USUBJID, VISIT), - parameters = "WEIGHT", - analysis_value = AVAL.WEIGHT / (AVAL.HEIGHT / 100)^2, - set_values_to = vars( - PARAMCD = "BMI", - PARAM = "Body Mass Index (kg/m^2)", - AVALU = "kg/m^2" - ), - constant_parameters = c("HEIGHT"), - constant_by_vars = vars(USUBJID) -) +This function is deprecated. Please use \code{derive_param-computed()} instead. +} +\seealso{ +Other deprecated: +\code{\link{derive_var_aendy}()} } \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} +\concept{deprecated} +\keyword{deprecated} diff --git a/man/derive_extreme_records.Rd b/man/derive_extreme_records.Rd index 24d0a944d7..c3b08fed5c 100644 --- a/man/derive_extreme_records.Rd +++ b/man/derive_extreme_records.Rd @@ -150,8 +150,25 @@ derive_extreme_records( ) ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_last_dose.Rd b/man/derive_last_dose.Rd deleted file mode 100644 index 679c9b4b76..0000000000 --- a/man/derive_last_dose.Rd +++ /dev/null @@ -1,137 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_last_dose.R -\name{derive_last_dose} -\alias{derive_last_dose} -\title{Derive Last Dose Date(-time)} -\usage{ -derive_last_dose( - dataset, - dataset_ex, - filter_ex = NULL, - by_vars = vars(STUDYID, USUBJID), - dose_start, - dose_end, - analysis_date, - dataset_seq_var, - new_var, - output_datetime = TRUE, - check_dates_only = FALSE, - traceability_vars = NULL -) -} -\arguments{ -\item{dataset}{Input dataset.} - -\item{dataset_ex}{Input EX dataset.} - -\item{filter_ex}{Filtering condition applied to EX dataset. -For example, it can be used to filter for valid dose. -Defaults to NULL.} - -\item{by_vars}{Variables to join by (created by \code{dplyr::vars}).} - -\item{dose_start}{The dose start date variable.} - -\item{dose_end}{The dose end date variable.} - -\item{analysis_date}{The analysis date variable.} - -\item{dataset_seq_var}{The sequence variable -(this together with \code{by_vars} creates the keys of \code{dataset}).} - -\item{new_var}{The output variable.} - -\item{output_datetime}{Logical. Should only date or date-time variable be returned? -Defaults to \code{TRUE} (i.e. date-time variable).} - -\item{check_dates_only}{Logical. -An assumption that start and end dates of treatment match is checked. -By default (\code{FALSE}), the date as well as the time component is checked. -If set to \code{TRUE}, then only the date component of those variables is checked.} - -\item{traceability_vars}{A named list returned by \code{\link[=vars]{vars()}} listing the traceability variables, -e.g. \code{vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ)}. -The left-hand side (names of the list elements) gives the names of the traceability variables -in the returned dataset. -The right-hand side (values of the list elements) gives the values of the traceability variables -in the returned dataset. -These can be either strings or symbols referring to existing variables.} -} -\value{ -Input dataset with additional column \code{new_var}. -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} - -\emph{Deprecated}, please use \code{derive_var_last_dose_date()} instead. -} -\details{ -All date (date-time) variables can be characters in standard ISO format or -of date / date-time class. -For ISO format, see \code{\link{impute_dtc}} - parameter \code{dtc} for further details. -Date-time imputations are done as follows: -\itemize{ -\item \code{dose_end}: no date imputation, time imputation to \code{00:00:00} if time is missing. -\item \code{analysis_date}: no date imputation, time imputation to \code{23:59:59} if time is missing. -} - -The last dose date is derived as follows: -\enumerate{ -\item The \code{dataset_ex} is filtered using \code{filter_ex}, if provided. -This is useful for, for example, filtering for valid dose only. -\item The datasets \code{dataset} and \code{dataset_ex} are joined using \code{by_vars}. -\item The last dose date is derived: -the last dose date is the maximum date where \code{dose_end} is lower to or equal to -\code{analysis_date}, subject to both date values are non-NA. -The last dose date is derived per \code{by_vars} and \code{dataset_seq_var}. -\item The last dose date is appended to the \code{dataset} and returned to the user. -} - -Furthermore, the following assumption is checked: start and end dates (datetimes) need to match. -Use \code{check_dates_only} to control whether only dates or whole date-times need to be equal. -} -\examples{ -library(dplyr, warn.conflicts = FALSE) -library(admiral.test) -data(admiral_ae) -data(ex_single) - -admiral_ae \%>\% - head(100) \%>\% - derive_last_dose( - head(ex_single, 100), - filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_start = EXSTDTC, - dose_end = EXENDTC, - analysis_date = AESTDTC, - dataset_seq_var = AESEQ, - new_var = LDOSEDTM, - output_datetime = TRUE, - check_dates_only = FALSE - ) \%>\% - select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDTM) - -# or with traceability variables -admiral_ae \%>\% - head(100) \%>\% - derive_last_dose( - head(ex_single, 100), - filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_start = EXSTDTC, - dose_end = EXENDTC, - analysis_date = AESTDTC, - dataset_seq_var = AESEQ, - new_var = LDOSEDTM, - output_datetime = TRUE, - check_dates_only = FALSE, - traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXSTDTC") - ) \%>\% - select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDTM, LDOSEDOM, LDOSESEQ, LDOSEVAR) -} -\author{ -Ondrej Slama -} -\keyword{adae} -\keyword{derivation} diff --git a/man/derive_param_bmi.Rd b/man/derive_param_bmi.Rd index e12d76f610..467251ce76 100644 --- a/man/derive_param_bmi.Rd +++ b/man/derive_param_bmi.Rd @@ -105,8 +105,25 @@ derive_param_bmi( get_unit_expr = extract_unit(PARAM) ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Pavan Kumar } -\keyword{advs} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_bsa.Rd b/man/derive_param_bsa.Rd index 2348ed4c9a..ae4612dea1 100644 --- a/man/derive_param_bsa.Rd +++ b/man/derive_param_bsa.Rd @@ -131,8 +131,25 @@ derive_param_bsa( get_unit_expr = extract_unit(PARAM) ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Eric Simms } -\keyword{advs} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_computed.Rd b/man/derive_param_computed.Rd new file mode 100644 index 0000000000..3c7cb7667d --- /dev/null +++ b/man/derive_param_computed.Rd @@ -0,0 +1,182 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_param_computed.R +\name{derive_param_computed} +\alias{derive_param_computed} +\title{Adds a Parameter Computed from the Analysis Value of Other Parameters} +\usage{ +derive_param_computed( + dataset, + by_vars, + parameters, + analysis_value, + set_values_to, + filter = NULL, + constant_by_vars = NULL, + constant_parameters = NULL +) +} +\arguments{ +\item{dataset}{Input dataset + +The variables specified by the \code{by_vars} parameter, \code{PARAMCD}, and \code{AVAL} +are expected. + +The variable specified by \code{by_vars} and \code{PARAMCD} must be a unique key of +the input dataset after restricting it by the filter condition (\code{filter} +parameter) and to the parameters specified by \code{parameters}.} + +\item{by_vars}{Grouping variables + +For each group defined by \code{by_vars} an observation is added to the output +dataset. Only variables specified in \code{by_vars} will be populated +in the newly created records. + +\emph{Permitted Values:} list of variables} + +\item{parameters}{Required parameter codes + +It is expected that all parameter codes (\code{PARAMCD}) which are required to +derive the new parameter are specified for this parameter or the +\code{constant_parameters} parameter. + +\emph{Permitted Values:} A character vector of \code{PARAMCD} values} + +\item{analysis_value}{Definition of the analysis value + +An expression defining the analysis value (\code{AVAL}) of the new parameter is +expected. The analysis values of the parameters specified by \code{parameters} +can be accessed using \verb{AVAL.}, e.g., \code{AVAL.SYSBP}. + +\emph{Permitted Values:} An unquoted expression} + +\item{set_values_to}{Variables to be set + +The specified variables are set to the specified values for the new +observations. For example \code{vars(PARAMCD = "MAP")} defines the parameter +code for the new parameter. + +\emph{Permitted Values:} List of variable-value pairs} + +\item{filter}{Filter condition + +The specified condition is applied to the input dataset before deriving the +new parameter, i.e., only observations fulfilling the condition are taken +into account. + +\emph{Permitted Values:} a condition} + +\item{constant_by_vars}{By variables for constant parameters + +The constant parameters (parameters that are measured only once) are merged +to the other parameters using the specified variables. (Refer to Example 2) + +\emph{Permitted Values:} list of variables} + +\item{constant_parameters}{Required constant parameter codes + +It is expected that all the parameter codes (\code{PARAMCD}) which are required +to derive the new parameter and are measured only once are specified here. +For example if BMI should be derived and height is measured only once while +weight is measured at each visit. Height could be specified in the +\code{constant_parameters} parameter. (Refer to Example 2) + +\emph{Permitted Values:} A character vector of \code{PARAMCD} values} +} +\value{ +The input dataset with the new parameter added. Note, a variable will only +be populated in the new parameter rows if it is specified in \code{by_vars}. +} +\description{ +Adds a parameter computed from the analysis value of other parameters. It is +expected that the analysis value of the new parameter is defined by an +expression using the analysis values of other parameters. For example mean +arterial pressure (MAP) can be derived from systolic (SYSBP) and diastolic +blood pressure (DIABP) with the formula +\deqn{MAP = \frac{SYSBP + 2 DIABP}{3}}{MAP = (SYSBP + 2 DIABP) / 3} +} +\details{ +For each group (with respect to the variables specified for the +\code{by_vars} parameter) an observation is added to the output dataset if the +filtered input dataset contains exactly one observation for each parameter +code specified for \code{parameters}. + +For the new observations \code{AVAL} is set to the value specified by +\code{analysis_value} and the variables specified for \code{set_values_to} are set to +the provided values. The values of the other variables of the input dataset +are set to \code{NA}. +} +\examples{ +# Example 1: Derive MAP +advs <- tibble::tribble( + ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, ~VISIT, + "01-701-1015", "DIABP", "Diastolic Blood Pressure (mmHg)", 51, "mmHg", "BASELINE", + "01-701-1015", "DIABP", "Diastolic Blood Pressure (mmHg)", 50, "mmHg", "WEEK 2", + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "BASELINE", + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "WEEK 2", + "01-701-1028", "DIABP", "Diastolic Blood Pressure (mmHg)", 79, "mmHg", "BASELINE", + "01-701-1028", "DIABP", "Diastolic Blood Pressure (mmHg)", 80, "mmHg", "WEEK 2", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 130, "mmHg", "BASELINE", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 132, "mmHg", "WEEK 2" +) + +derive_param_computed( + advs, + by_vars = vars(USUBJID, VISIT), + parameters = c("SYSBP", "DIABP"), + analysis_value = (AVAL.SYSBP + 2 * AVAL.DIABP) / 3, + set_values_to = vars( + PARAMCD = "MAP", + PARAM = "Mean Arterial Pressure (mmHg)", + AVALU = "mmHg" + ) +) + +# Example 2: Derive BMI where height is measured only once +advs <- tibble::tribble( + ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, ~VISIT, + "01-701-1015", "HEIGHT", "Height (cm)", 147, "cm", "SCREENING", + "01-701-1015", "WEIGHT", "Weight (kg)", 54.0, "kg", "SCREENING", + "01-701-1015", "WEIGHT", "Weight (kg)", 54.4, "kg", "BASELINE", + "01-701-1015", "WEIGHT", "Weight (kg)", 53.1, "kg", "WEEK 2", + "01-701-1028", "HEIGHT", "Height (cm)", 163, "cm", "SCREENING", + "01-701-1028", "WEIGHT", "Weight (kg)", 78.5, "kg", "SCREENING", + "01-701-1028", "WEIGHT", "Weight (kg)", 80.3, "kg", "BASELINE", + "01-701-1028", "WEIGHT", "Weight (kg)", 80.7, "kg", "WEEK 2" +) + +derive_param_computed( + advs, + by_vars = vars(USUBJID, VISIT), + parameters = "WEIGHT", + analysis_value = AVAL.WEIGHT / (AVAL.HEIGHT / 100)^2, + set_values_to = vars( + PARAMCD = "BMI", + PARAM = "Body Mass Index (kg/m^2)", + AVALU = "kg/m^2" + ), + constant_parameters = c("HEIGHT"), + constant_by_vars = vars(USUBJID) +) +} +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} +\author{ +Stefan Bundfuss +} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_doseint.Rd b/man/derive_param_doseint.Rd index 636c0aa528..9dc6b8b82c 100644 --- a/man/derive_param_doseint.Rd +++ b/man/derive_param_doseint.Rd @@ -135,8 +135,25 @@ derive_param_doseint( zero_doses = "100" ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Alice Ehmann } -\keyword{adex} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_exist_flag.Rd b/man/derive_param_exist_flag.Rd index 092e31c994..3da1ca7ebf 100644 --- a/man/derive_param_exist_flag.Rd +++ b/man/derive_param_exist_flag.Rd @@ -179,8 +179,25 @@ derive_param_exist_flag( ) ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_exposure.Rd b/man/derive_param_exposure.Rd index e2723007e1..42295c800b 100644 --- a/man/derive_param_exposure.Rd +++ b/man/derive_param_exposure.Rd @@ -147,9 +147,25 @@ adex \%>\% ) \%>\% select(-ASTDTM, -AENDTM) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Samia Kabi } -\keyword{adex} -\keyword{bds} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_first_event.Rd b/man/derive_param_first_event.Rd index edfc6f5fc6..123cc87172 100644 --- a/man/derive_param_first_event.Rd +++ b/man/derive_param_first_event.Rd @@ -162,8 +162,25 @@ derive_param_first_event( ) ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_framingham.Rd b/man/derive_param_framingham.Rd new file mode 100644 index 0000000000..43a75758c2 --- /dev/null +++ b/man/derive_param_framingham.Rd @@ -0,0 +1,257 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_param_framingham.R +\name{derive_param_framingham} +\alias{derive_param_framingham} +\title{Adds a Parameter for Framingham Heart Study Cardiovascular Disease +10-Year Risk Score} +\usage{ +derive_param_framingham( + dataset, + by_vars, + set_values_to = vars(PARAMCD = "FCVD101"), + sysbp_code = "SYSBP", + chol_code = "CHOL", + cholhdl_code = "CHOLHDL", + age = AGE, + sex = SEX, + smokefl = SMOKEFL, + diabetfl = DIABETFL, + trthypfl = TRTHYPFL, + get_unit_expr, + filter = NULL +) +} +\arguments{ +\item{dataset}{Input dataset + +The variables specified by the \code{by_vars} parameter, \code{PARAMCD}, and +\code{AVAL} are expected. + +The variable specified by \code{by_vars} and \code{PARAMCD} must be a unique key of +the input dataset after restricting it by the filter condition (\code{filter} +parameter) and to the parameters specified by \code{sysbp_code}, \code{chol_code} +and \code{hdl_code}.} + +\item{by_vars}{Grouping variables + +For each group defined by \code{by_vars} an observation is added to the output +dataset. Only variables specified in \code{by_vars} will be populated +in the newly created records. + +\emph{Permitted Values:} list of variables} + +\item{set_values_to}{Variables to be set + +The specified variables are set to the specified values for the new +observations. For example \code{vars(PARAMCD = "MAP")} defines the parameter +code for the new parameter. + +\emph{Permitted Values:} List of variable-value pairs} + +\item{sysbp_code}{Systolic blood pressure parameter code + +The observations where \code{PARAMCD} equals the specified value are considered +as the systolic blood pressure assessments. + +\emph{Permitted Values:} character value} + +\item{chol_code}{Total serum cholesterol code + +The observations where \code{PARAMCD} equals the specified value are considered +as the total cholesterol assessments. This must be measured in mg/dL. + +\emph{Permitted Values:} character value} + +\item{cholhdl_code}{HDL serum cholesterol code + +The observations where \code{PARAMCD} equals the specified value are considered +as the HDL cholesterol assessments. This must be measured in mg/dL. + +\emph{Permitted Values:} character value} + +\item{age}{Subject age + +A variable containing the subject's age. + +\emph{Permitted Values:} A numeric variable name that refers to a subject age +column of the input dataset} + +\item{sex}{Subject sex + +A variable containing the subject's sex. + +\emph{Permitted Values:} A character variable name that refers to a subject sex +column of the input dataset} + +\item{smokefl}{Smoking status flag + +A flag indicating smoking status. + +\emph{Permitted Values:} A character variable name that refers to a smoking status +column of the input dataset.} + +\item{diabetfl}{Diabetic flag + +A flag indicating diabetic status. + +\emph{Permitted Values:} A character variable name that refers to a diabetic +status column of the input dataset} + +\item{trthypfl}{Treated with hypertension medication flag + +A flag indicating if a subject was treated with hypertension medication. + +\emph{Permitted Values:} A character variable name that refers to a column that +indicates whether a subject is treated for high blood +pressure} + +\item{get_unit_expr}{An expression providing the unit of the parameter + +The result is used to check the units of the input parameters. + +Permitted Values: A variable of the input dataset or a function call} + +\item{filter}{Filter condition + +The specified condition is applied to the input dataset before deriving the +new parameter, i.e., only observations fulfilling the condition are taken +into account. + +\emph{Permitted Values:} a condition} +} +\value{ +The input dataset with the new parameter added +} +\description{ +Adds a record for framingham score (FCVD101) for each by group +(e.g., subject and visit) where the source parameters are available. +} +\details{ +The values of \code{age}, \code{sex}, \code{smokefl}, \code{diabetfl} and \code{trthypfl} will be +added to the \code{by_vars} list. +The predicted probability of having cardiovascular disease (CVD) +within 10-years according to Framingham formula +\href{https://www.ahajournals.org/doi/pdf/10.1161/CIRCULATIONAHA.107.699579}{D'Agostino, 2008} is: # nolint +\strong{For Women:} + +\tabular{rr}{ +\strong{Factor} \tab \strong{Amount} \cr +Age \tab 2.32888 \cr +Total Chol \tab 1.20904 \cr +HDL Chol \tab -0.70833 \cr +Sys BP \tab 2.76157 \cr +Sys BP + Hypertension Meds \tab 2.82263 \cr +Smoker \tab 0.52873 \cr +Non-Smoker \tab 0 \cr +Diabetic \tab 0.69154 \cr +Not Diabetic \tab 0 \cr +Average Risk \tab 26.1931 \cr +Risk Period \tab 0.95012 \cr +} + +\strong{For Men:} + +\tabular{rr}{ +\strong{Factor} \tab \strong{Amount} \cr +Age \tab 3.06117 \cr +Total Chol \tab 1.12370 \cr +HDL Chol \tab -0.93263 \cr +Sys BP \tab 1.93303 \cr +Sys BP + Hypertension Meds \tab 2.99881 \cr +Smoker \tab .65451 \cr +Non-Smoker \tab 0 \cr +Diabetic \tab 0.57367 \cr +Not Diabetic \tab 0 \cr +Average Risk \tab 23.9802 \cr +Risk Period \tab 0.88936 \cr +} + +\strong{The equation for calculating risk:} + +\deqn{RiskFactors = (log(Age) * AgeFactor) ++ (log(TotalChol) * TotalCholFactor) ++ (log(CholHDL) * CholHDLFactor) \\ ++ (log(SysBP) * SysBPFactor) + Smoker ++ Diabetes Present - AvgRisk} + +\deqn{Risk = 100 * (1 - RiskPeriodFactor^{RiskFactors})} +} +\examples{ +library(dplyr, warn.conflicts = FALSE) + +adcvrisk <- tibble::tribble( + ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, + ~VISIT, ~AGE, ~SEX, ~SMOKEFL, ~DIABETFL, ~TRTHYPFL, + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, + "mmHg", "BASELINE", 44, "F", "N", "N", "N", + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 115, + "mmHg", "WEEK 2", 44, "F", "N", "N", "Y", + "01-701-1015", "CHOL", "Total Cholesterol (mg/dL)", 216.16, + "mg/dL", "BASELINE", 44, "F", "N", "N", "N", + "01-701-1015", "CHOL", "Total Cholesterol (mg/dL)", 210.78, + "mg/dL", "WEEK 2", 44, "F", "N", "N", "Y", + "01-701-1015", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 54.91, + "mg/dL", "BASELINE", 44, "F", "N", "N", "N", + "01-701-1015", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 26.72, + "mg/dL", "WEEK 2", 44, "F", "N", "N", "Y", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 119, + "mmHg", "BASELINE", 55, "M", "Y", "Y", "Y", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 101, + "mmHg", "WEEK 2", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOL", "Total Cholesterol (mg/dL)", 292.01, + "mg/dL", "BASELINE", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOL", "Total Cholesterol (mg/dL)", 246.73, + "mg/dL", "WEEK 2", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 65.55, + "mg/dL", "BASELINE", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOLHDL", "Cholesterol/HDL-Cholesterol (mg/dL)", 44.62, + "mg/dL", "WEEK 2", 55, "M", "Y", "Y", "Y" +) + + +adcvrisk \%>\% + derive_param_framingham( + by_vars = vars(USUBJID, VISIT), + set_values_to = vars( + PARAMCD = "FCVD101", + PARAM = "FCVD1-Framingham CVD 10-Year Risk Score (\%)" + ), + get_unit_expr = AVALU + ) + +derive_param_framingham( + adcvrisk, + by_vars = vars(USUBJID, VISIT), + set_values_to = vars( + PARAMCD = "FCVD101", + PARAM = "FCVD1-Framingham CVD 10-Year Risk Score (\%)" + ), + get_unit_expr = extract_unit(PARAM) +) +} +\seealso{ +\code{\link[=compute_framingham]{compute_framingham()}} + +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} +\author{ +Alice Ehmann +Jack McGavigan +Ben Straub +} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_map.Rd b/man/derive_param_map.Rd index 2be3205297..12c98af266 100644 --- a/man/derive_param_map.Rd +++ b/man/derive_param_map.Rd @@ -136,8 +136,25 @@ derive_param_map( get_unit_expr = extract_unit(PARAM) ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{advs} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_qtc.Rd b/man/derive_param_qtc.Rd index 4d8b26b1df..3601047529 100644 --- a/man/derive_param_qtc.Rd +++ b/man/derive_param_qtc.Rd @@ -133,9 +133,25 @@ derive_param_qtc( } \seealso{ \code{\link[=compute_qtc]{compute_qtc()}} + +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} } \author{ Stefan Bundfuss } -\keyword{adeg} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_rr.Rd b/man/derive_param_rr.Rd index f8e1801a5b..3861c41a30 100644 --- a/man/derive_param_rr.Rd +++ b/man/derive_param_rr.Rd @@ -97,8 +97,25 @@ derive_param_rr( get_unit_expr = AVALU ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_wbc_abs}()}, +\code{\link{derive_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{adeg} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_param_tte.Rd b/man/derive_param_tte.Rd index 59b0f673c2..3d1e65b756 100644 --- a/man/derive_param_tte.Rd +++ b/man/derive_param_tte.Rd @@ -96,9 +96,7 @@ first observation (with respect to \code{date}) is selected. \item The \code{ADT} variable is set to the variable specified by the \code{date} element. If the date variable is a datetime variable, only -the datepart is copied. If the source variable is a character variable, it -is converted to a date. If the date is incomplete, it is imputed as the -first possible date. +the datepart is copied. \item The \code{CNSR} variable is added and set to the \code{censor} element. @@ -119,9 +117,7 @@ last observation (with respect to \code{date}) is selected. \item The \code{ADT} variable is set to the variable specified by the \code{date} element. If the date variable is a datetime variable, only -the datepart is copied. If the source variable is a character variable, it -is converted to a date. If the date is incomplete, it is imputed as the -first possible date. +the datepart is copied. \item The \code{CNSR} variable is added and set to the \code{censor} element. @@ -149,6 +145,7 @@ joined from the ADSL dataset, } } \examples{ +library(tibble) library(dplyr, warn.conflicts = FALSE) library(lubridate) data("admiral_adsl") @@ -190,14 +187,14 @@ derive_param_tte( filter(row_number() \%in\% 20:30) # derive time to adverse event for each preferred term # -adsl <- tibble::tribble( +adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") ) \%>\% mutate(STUDYID = "AB42") -ae <- tibble::tribble( +ae <- tribble( ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, "01", "2021-01-03T10:56", 1, "Flu", "01", "2021-03-04", 2, "Cough", @@ -205,9 +202,17 @@ ae <- tibble::tribble( ) \%>\% mutate(STUDYID = "AB42") +ae_ext <- derive_vars_dt( + ae, + dtc = AESTDTC, + new_vars_prefix = "AEST", + highest_imputation = "M", + flag_imputation = "none" +) + ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVNTDESC = "AE", SRCDOM = "AE", @@ -232,7 +237,7 @@ derive_param_tte( start_date = TRTSDT, event_conditions = list(ttae), censor_conditions = list(eos), - source_datasets = list(adsl = adsl, ae = ae), + source_datasets = list(adsl = adsl, ae = ae_ext), set_values_to = vars( PARAMCD = paste0("TTAE", as.numeric(as.factor(AEDECOD))), PARAM = paste("Time to First", AEDECOD, "Adverse Event"), @@ -245,5 +250,5 @@ derive_param_tte( \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} +\concept{der_prm_tte} +\keyword{der_prm_tte} diff --git a/man/derive_param_wbc_abs.Rd b/man/derive_param_wbc_abs.Rd index 8612ade54d..9f0361c04b 100644 --- a/man/derive_param_wbc_abs.Rd +++ b/man/derive_param_wbc_abs.Rd @@ -112,9 +112,25 @@ derive_param_wbc_abs( diff_type = "fraction" ) } +\seealso{ +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_summary_records}()} +} \author{ Annie Yang } -\keyword{adlb} -\keyword{bds} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_summary_records.Rd b/man/derive_summary_records.Rd index 96489aecce..060b1c5922 100644 --- a/man/derive_summary_records.Rd +++ b/man/derive_summary_records.Rd @@ -155,8 +155,27 @@ derive_summary_records( set_values_to = vars(DTYPE = "AVERAGE") ) } +\seealso{ +\code{get_summary_records()} + +BDS-Findings Functions for adding Parameters/Records: +\code{\link{default_qtc_paramcd}()}, +\code{\link{derive_extreme_records}()}, +\code{\link{derive_param_bmi}()}, +\code{\link{derive_param_bsa}()}, +\code{\link{derive_param_computed}()}, +\code{\link{derive_param_doseint}()}, +\code{\link{derive_param_exist_flag}()}, +\code{\link{derive_param_exposure}()}, +\code{\link{derive_param_first_event}()}, +\code{\link{derive_param_framingham}()}, +\code{\link{derive_param_map}()}, +\code{\link{derive_param_qtc}()}, +\code{\link{derive_param_rr}()}, +\code{\link{derive_param_wbc_abs}()} +} \author{ Vignesh Thanikachalam, Ondrej Slama } -\keyword{bds} -\keyword{derivation} +\concept{der_prm_bds_findings} +\keyword{der_prm_bds_findings} diff --git a/man/derive_var_ady.Rd b/man/derive_var_ady.Rd index 91905f1182..58a146add8 100644 --- a/man/derive_var_ady.Rd +++ b/man/derive_var_ady.Rd @@ -46,6 +46,4 @@ start date is 1. \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} -\keyword{timing} +\keyword{deprecated} diff --git a/man/derive_var_aendy.Rd b/man/derive_var_aendy.Rd index c119083ac1..02609ab2e5 100644 --- a/man/derive_var_aendy.Rd +++ b/man/derive_var_aendy.Rd @@ -44,10 +44,12 @@ The study day is derived as number of days from the start date to the end date. If it is nonnegative, one is added. I.e., the study day of the start date is 1. } +\seealso{ +Other deprecated: +\code{\link{derive_derived_param}()} +} \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} -\keyword{occds} -\keyword{timing} +\concept{deprecated} +\keyword{deprecated} diff --git a/man/derive_var_age_years.Rd b/man/derive_var_age_years.Rd index 20d908ecf8..0d745225b7 100644 --- a/man/derive_var_age_years.Rd +++ b/man/derive_var_age_years.Rd @@ -47,6 +47,17 @@ data \%>\% data.frame(AGE = c(12, 24, 36, 48)) \%>\% derive_var_age_years(., AGE, age_unit = "months", new_var = AAGE) } +\seealso{ +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_disposition_status}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{derive_var_extreme_dtm}()}, +\code{\link{derive_var_extreme_dt}()}, +\code{\link{derive_vars_aage}()}, +\code{\link{derive_vars_disposition_reason}()} +} \author{ Michael Thorpe } +\concept{der_adsl} +\keyword{der_adsl} diff --git a/man/derive_var_agegr_fda.Rd b/man/derive_var_agegr_fda.Rd index b19b9e9825..38ebdaba2d 100644 --- a/man/derive_var_agegr_fda.Rd +++ b/man/derive_var_agegr_fda.Rd @@ -10,70 +10,20 @@ derive_var_agegr_fda(dataset, age_var, age_unit = NULL, new_var) derive_var_agegr_ema(dataset, age_var, age_unit = NULL, new_var) } \arguments{ -\item{dataset}{Input dataset.} +\item{dataset}{Input dataset} -\item{age_var}{AGE variable.} +\item{age_var}{AGE variable} -\item{age_unit}{AGE unit variable. +\item{age_unit}{AGE unit variable} -The AGE unit variable is used to convert AGE to 'years' so that grouping can occur. -This is only used when the age_var variable does not have a corresponding unit in the dataset. - -Default: NULL - -Permitted Values: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'} - -\item{new_var}{New variable to be created.} -} -\value{ -\code{dataset} with new column \code{new_var} of class factor. +\item{new_var}{New variable to create inside \code{dataset}} } \description{ -Functions for deriving standardized age groups. -} -\details{ -\code{derive_var_agegr_fda()} derives age groups according to FDA guidance. \code{age_var} -will be split in categories: <18, 18-64, >=65. - -\code{derive_var_agegr_ema()} derives age groups according to EMA guidance. -\code{age_var} will be split into categories: 0-27 days (Newborns), 28 days to -23 months (Infants and Toddlers), 2-11 (Children), 12-17 (Adolescents), 18-64, -65-84, >=85. -} -\examples{ -library(dplyr, warn.conflicts = FALSE) -library(admiral.test) -data(admiral_dm) - -admiral_dm \%>\% - derive_var_agegr_fda(age_var = AGE, new_var = AGEGR1) \%>\% - select(SUBJID, AGE, AGEGR1) - -data <- tibble::tribble( - ~BRTHDT, ~RANDDT, - lubridate::ymd("1984-09-06"), lubridate::ymd("2020-02-24") -) - -data \%>\% - derive_vars_aage(unit = "months") \%>\% - derive_var_agegr_fda(AAGE, age_unit = NULL, AGEGR1) - -data.frame(AGE = 1:100) \%>\% - derive_var_agegr_fda(age_var = AGE, age_unit = "years", new_var = AGEGR1) -library(dplyr, warn.conflicts = FALSE) -library(admiral.test) -data(admiral_dm) - -admiral_dm \%>\% - derive_var_agegr_ema(age_var = AGE, new_var = AGEGR1) \%>\% - select(SUBJID, AGE, AGEGR1) - -data.frame(AGE = 1:100) \%>\% - derive_var_agegr_ema(age_var = AGE, age_unit = "years", new_var = AGEGR1) +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} -data.frame(AGE = 1:20) \%>\% - derive_var_agegr_ema(age_var = AGE, age_unit = "years", new_var = AGEGR1) +These functions are \emph{deprecated}. } \author{ Ondrej Slama } +\keyword{deprecated} diff --git a/man/derive_var_analysis_ratio.Rd b/man/derive_var_analysis_ratio.Rd index 4f0f3b0411..2a0064fc86 100644 --- a/man/derive_var_analysis_ratio.Rd +++ b/man/derive_var_analysis_ratio.Rd @@ -65,10 +65,20 @@ data \%>\% derive_var_analysis_ratio(numer_var = AVAL, denom_var = ANRLO, new_var = R01ANRLO) \%>\% derive_var_analysis_ratio(numer_var = AVAL, denom_var = ANRHI, new_var = R01ANRHI) } +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} \author{ Ben Straub } -\keyword{adam} -\keyword{adlb} -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_anrind.Rd b/man/derive_var_anrind.Rd index 9cfa2e554b..10b4a79d80 100644 --- a/man/derive_var_anrind.Rd +++ b/man/derive_var_anrind.Rd @@ -51,8 +51,20 @@ admiral_vs \%>\% derive_var_anrind() \%>\% select(USUBJID, PARAMCD, AVAL, ANRLO:ANRIND) } +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} \author{ Thomas Neitmann } -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_astdy.Rd b/man/derive_var_astdy.Rd index 1a7a8ca411..aa3221747d 100644 --- a/man/derive_var_astdy.Rd +++ b/man/derive_var_astdy.Rd @@ -47,7 +47,4 @@ start date is 1. \author{ Stefan Bundfuss } -\keyword{bds} -\keyword{derivation} -\keyword{occds} -\keyword{timing} +\keyword{deprecated} diff --git a/man/derive_var_atirel.Rd b/man/derive_var_atirel.Rd index 45a5bee7da..3a5a60f1c3 100644 --- a/man/derive_var_atirel.Rd +++ b/man/derive_var_atirel.Rd @@ -45,7 +45,4 @@ the Analysis Start Time Imputation Flag is 'H' or 'M', \author{ Teckla Akinyi } -\keyword{ADaM} -\keyword{ATIREL} -\keyword{Relationship} -\keyword{Var} +\keyword{deprecated} diff --git a/man/derive_var_atoxgr.Rd b/man/derive_var_atoxgr.Rd new file mode 100644 index 0000000000..ab37a77d28 --- /dev/null +++ b/man/derive_var_atoxgr.Rd @@ -0,0 +1,77 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_var_atoxgr.R +\name{derive_var_atoxgr} +\alias{derive_var_atoxgr} +\title{Derive Lab High toxicity Grade 0 - 4 and Low Toxicity Grades 0 - (-4)} +\usage{ +derive_var_atoxgr( + dataset, + lotox_description_var = ATOXDSCL, + hitox_description_var = ATOXDSCH +) +} +\arguments{ +\item{dataset}{Input data set + +The columns \code{ATOXGRL}, \code{ATOXGRH} and specified by \code{lotox_description_var}, +and \code{hitox_description_var} parameters are expected.} + +\item{lotox_description_var}{Variable containing the toxicity grade description +for low values, eg. "Anemia"} + +\item{hitox_description_var}{Variable containing the toxicity grade description +for low values, eg. "Hemoglobin Increased".} +} +\value{ +The input data set with the character variable added +} +\description{ +Derives character lab grade based on high and low severity/toxicity grade(s). +} +\details{ +Created variable \code{ATOXGR} will contain values "-4", "-3", "-2", "-1" for low values +and "1", "2", "3", "4" for high values, and will contain "0" if value is gradable +and does not satisfy any of the criteria for high or low values. ATOXGR is set to +missing if information not available to give a grade. + +Function applies the following rules: +\itemize{ +\item High and low missing - overall missing +\item Low grade not missing and > 0 - overall holds low grade +\item High grade not missing and > 0 - overall holds high grade +\item (Only high direction OR low direction is NORMAL) and high grade normal - overall NORMAL +\item (Only low direction OR high direction is NORMAL) and low grade normal - overall NORMAL +\item otherwise set to missing +} +} +\examples{ +library(dplyr, warn.conflicts = FALSE) + +adlb <- tibble::tribble( + ~ATOXDSCL, ~ATOXDSCH, ~ATOXGRL, ~ATOXGRH, + "Hypoglycemia", "Hyperglycemia", NA_character_, "0", + "Hypoglycemia", "Hyperglycemia", "0", "1", + "Hypoglycemia", "Hyperglycemia", "0", "0", + NA_character_, "INR Increased", NA_character_, "0", + "Hypophosphatemia", NA_character_, "1", NA_character_ +) + +derive_var_atoxgr(adlb) +} +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} +\author{ +Gordon Miller +} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_atoxgr_dir.Rd b/man/derive_var_atoxgr_dir.Rd new file mode 100644 index 0000000000..96d67ce127 --- /dev/null +++ b/man/derive_var_atoxgr_dir.Rd @@ -0,0 +1,132 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_var_atoxgr.R +\name{derive_var_atoxgr_dir} +\alias{derive_var_atoxgr_dir} +\title{Derive Lab Toxicity Grade 0 - 4} +\usage{ +derive_var_atoxgr_dir( + dataset, + new_var, + tox_description_var, + meta_criteria = atoxgr_criteria_ctcv4, + criteria_direction, + get_unit_expr +) +} +\arguments{ +\item{dataset}{Input data set + +The columns specified by \code{tox_description_var} parameter is expected.} + +\item{new_var}{Name of the character grade variable to create, for example, \code{ATOXGRH} +or \code{ATOXGRL}.} + +\item{tox_description_var}{Variable containing the description of the grading +criteria. For example: "Anemia" or "INR Increased".} + +\item{meta_criteria}{Metadata data set holding the criteria (normally a case statement) + +Default: \code{atoxgr_criteria_ctcv4} + +{admiral} metadata data set \code{atoxgr_criteria_ctcv4} implements +\href{https://ctep.cancer.gov/protocoldevelopment/electronic_applications/ctc.htm}{Common Terminology Criteria for Adverse Events (CTCAE) v4.0} + +The metadata should have the following variables: +\itemize{ +\item \code{TERM}: variable to hold the term describing the criteria applied to a particular lab test, +eg. "Anemia" or "INR Increased". Note: the variable is case insensitive. +\item \code{DIRECTION}: variable to hold the direction of the abnormality of a particular lab test +value. "L" is for LOW values, "H" is for HIGH values. Note: the variable is case insensitive. +\item \code{SI_UNIT_CHECK}: variable to hold unit of particular lab test. Used to check against input data +if criteria is based on absolute values. +\item \code{VAR_CHECK}: variable to hold comma separated list of variables used in criteria. Used to check +against input data that variables exist. +\item \code{GRADE_CRITERIA_CODE}: variable to hold code that creates grade based on defined criteria. +}} + +\item{criteria_direction}{Direction (L= Low, H = High) of toxicity grade. + +Permitted Values: "L", "H"} + +\item{get_unit_expr}{An expression providing the unit of the parameter + +The result is used to check the units of the input parameters. Compared with +\code{SI_UNIT_CHECK} in metadata (see \code{meta_criteria} parameter). + +Permitted Values: A variable containing unit from the input dataset, or a function call, +for example, \code{get_unit_expr = extract_unit(PARAM)}.} +} +\value{ +The input dataset with the character variable added +} +\description{ +Derives a character lab grade based on severity/toxicity criteria. +} +\details{ +\code{new_var} is derived with values NA, "0", "1", "2", "3", "4", where "4" is the most +severe grade +\itemize{ +\item "4" is where the lab value satisfies the criteria for grade 4. +\item "3" is where the lab value satisfies the criteria for grade 3. +\item "2" is where the lab value satisfies the criteria for grade 2. +\item "1" is where the lab value satisfies the criteria for grade 1. +\item "0" is where a grade can be derived and is not grade "1", "2", "3" or "4". +\item NA is where a grade cannot be derived. +} +} +\examples{ +library(dplyr, warn.conflicts = FALSE) + +data <- tibble::tribble( + ~ATOXDSCL, ~AVAL, ~ANRLO, ~ANRHI, ~PARAM, + "Hypoglycemia", 119, 4, 7, "Glucose (mmol/L)", + "Hypoglycemia", 120, 4, 7, "Glucose (mmol/L)", + "Anemia", 129, 120, 180, "Hemoglobin (g/L)", + "White blood cell decreased", 10, 5, 20, "White blood cell (10^9/L)", + "White blood cell decreased", 15, 5, 20, "White blood cell (10^9/L)", + "Anemia", 140, 120, 180, "Hemoglobin (g/L)" +) + +derive_var_atoxgr_dir(data, + new_var = ATOXGRL, + tox_description_var = ATOXDSCL, + meta_criteria = atoxgr_criteria_ctcv4, + criteria_direction = "L", + get_unit_expr = extract_unit(PARAM) +) + +data <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~PARAM, + "Hyperglycemia", 119, 4, 7, "Glucose (mmol/L)", + "Hyperglycemia", 120, 4, 7, "Glucose (mmol/L)", + "GGT increased", 129, 0, 30, "Gamma Glutamyl Transferase (U/L)", + "Lymphocyte count increased", 4, 1, 4, "Lymphocytes Abs (10^9/L)", + "Lymphocyte count increased", 2, 1, 4, "Lymphocytes Abs (10^9/L)", + "GGT increased", 140, 120, 180, "Gamma Glutamyl Transferase (U/L)" +) + +derive_var_atoxgr_dir(data, + new_var = ATOXGRH, + tox_description_var = ATOXDSCH, + meta_criteria = atoxgr_criteria_ctcv4, + criteria_direction = "H", + get_unit_expr = extract_unit(PARAM) +) +} +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} +\author{ +Gordon Miller +} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_base.Rd b/man/derive_var_base.Rd index fdd016001d..82b51af3ae 100644 --- a/man/derive_var_base.Rd +++ b/man/derive_var_base.Rd @@ -83,8 +83,20 @@ if (FALSE) { ) } } +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} \author{ Thomas Neitmann } -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_basetype.Rd b/man/derive_var_basetype.Rd index 41a06e60f5..9e6044e068 100644 --- a/man/derive_var_basetype.Rd +++ b/man/derive_var_basetype.Rd @@ -93,8 +93,20 @@ print(bds_with_basetype, n = Inf) dplyr::count(bds_with_basetype, BASETYPE, name = "Number of Records") } +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} \author{ Thomas Neitmann } -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_chg.Rd b/man/derive_var_chg.Rd index 2e7861318a..b3bd66b41e 100644 --- a/man/derive_var_chg.Rd +++ b/man/derive_var_chg.Rd @@ -32,10 +32,19 @@ advs <- tibble::tribble( derive_var_chg(advs) } \seealso{ -\code{\link[=derive_var_pchg]{derive_var_pchg()}} +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} } \author{ Thomas Neitmann } -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_confirmation_flag.Rd b/man/derive_var_confirmation_flag.Rd new file mode 100644 index 0000000000..e8fdfc6927 --- /dev/null +++ b/man/derive_var_confirmation_flag.Rd @@ -0,0 +1,339 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_var_confirmation_flag.R +\name{derive_var_confirmation_flag} +\alias{derive_var_confirmation_flag} +\title{Derive Confirmation Flag} +\usage{ +derive_var_confirmation_flag( + dataset, + by_vars, + order, + new_var, + join_vars, + join_type, + first_cond = NULL, + filter, + true_value = "Y", + false_value = NA_character_, + check_type = "warning" +) +} +\arguments{ +\item{dataset}{Input dataset + +The variables specified by the \code{by_vars} and \code{join_vars} parameter are +expected.} + +\item{by_vars}{By variables + +The specified variables are used as by variables for joining the input +dataset with itself.} + +\item{order}{Order + +The observations are ordered by the specified order.} + +\item{new_var}{New variable + +The specified variable is added to the input dataset.} + +\item{join_vars}{Variables to keep from joined dataset + +The variables needed from the other observations should be specified +for this parameter. The specified variables are added to the joined dataset +with suffix ".join". For example to flag all observations with \code{AVALC == "Y"} and \code{AVALC == "Y"} for at least one subsequent visit \code{join_vars = vars(AVALC, AVISITN)} and \code{filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join} could be specified. + +The \verb{*.join} variables are not included in the output dataset.} + +\item{join_type}{Observations to keep after joining + +The argument determines which of the joined observations are kept with +respect to the original observation. For example, if \code{join_type = "after"} +is specified all observations after the original observations are kept. + +For example for confirmed response or BOR in the oncology setting or +confirmed deterioration in questionnaires the confirmatory assessment must +be after the assessment to be flagged. Thus \code{join_type = "after"} could be +used. + +Whereas, sometimes you might allow for confirmatory observations to occur +prior to the observation to be flagged. For example, to flag AEs occurring +on or after seven days before a COVID AE. Thus \code{join_type = "all"} could be +used. + +\emph{Permitted Values:} \code{"before"}, \code{"after"}, \code{"all"}} + +\item{first_cond}{Condition for selecting range of data + +If this argument is specified, the other observations are restricted up to +the first observation where the specified condition is fulfilled. If the +condition is not fulfilled for any of the other observations, no +observations are considered, i.e., the observation is not flagged. + +This parameter should be specified if \code{filter} contains summary functions +which should not apply to all observations but only up to the confirmation +assessment. For an example see the third example below.} + +\item{filter}{Condition for selecting observations + +The filter is applied to the joined dataset for flagging the confirmed +observations. The condition can include summary functions. The joined +dataset is grouped by the original observations. I.e., the summary function +are applied to all observations up to the confirmation observation. For +example, \code{filter = AVALC == "CR" & all(AVALC.join \%in\% c("CR", "NE")) & count_vals(var = AVALC.join, val = "NE") <= 1} selects observations with +response "CR" and for all observations up to the confirmation observation +the response is "CR" or "NE" and there is at most one "NE".} + +\item{true_value}{Value of \code{new_var} for flagged observations + +\emph{Default}: \code{"Y"}} + +\item{false_value}{Value of \code{new_var} for observations not flagged + +\emph{Default}: \code{NA_character_}} + +\item{check_type}{Check uniqueness? + +If \code{"warning"} or \code{"error"} is specified, the specified message is issued +if the observations of the input dataset are not unique with respect to the +by variables and the order. + +\emph{Default:} \code{"warning"} + +\emph{Permitted Values:} \code{"none"}, \code{"warning"}, \code{"error"}} +} +\value{ +The input dataset with the variable specified by \code{new_var} added. +} +\description{ +Derive a flag which depends on other observations of the dataset. For +example, flagging events which need to be confirmed by a second event. +} +\details{ +An example usage might be flagging if a patient received two required +medications within a certain timeframe of each other. + +In the oncology setting, for example, the function could be used to flag if a +response value can be confirmed by an other assessment. This is commonly +used in endpoints such as best overall response. + +The following steps are performed to produce the output dataset. +\subsection{Step 1}{ + +The input dataset is joined with itself by the variables specified for +\code{by_vars}. From the right hand side of the join only the variables +specified for \code{join_vars} are kept. The suffix ".join" is added to these +variables. + +For example, for \code{by_vars = USUBJID}, \code{join_vars = vars(AVISITN, AVALC)} and input dataset + +\if{html}{\out{
}}\preformatted{# A tibble: 2 x 4 +USUBJID AVISITN AVALC AVAL + +1 1 Y 1 +1 2 N 0 +}\if{html}{\out{
}} + +the joined dataset is + +\if{html}{\out{
}}\preformatted{A tibble: 4 x 6 +USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join + +1 1 Y 1 1 Y +1 1 Y 1 2 N +1 2 N 0 1 Y +1 2 N 0 2 N +}\if{html}{\out{
}} +} + +\subsection{Step 2}{ + +The joined dataset is restricted to observations with respect to +\code{join_type} and \code{order}. + +The dataset from the example in the previous step with \code{join_type = "after"} and \code{order = vars(AVISITN)} is restricted to + +\if{html}{\out{
}}\preformatted{A tibble: 4 x 6 +USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join + +1 1 Y 1 2 N +}\if{html}{\out{
}} +} + +\subsection{Step 3}{ + +If \code{first_cond} is specified, for each observation of the input dataset the +joined dataset is restricted to observations up to the first observation +where \code{first_cond} is fulfilled (the observation fulfilling the condition +is included). If for an observation of the input dataset the condition is +not fulfilled, the observation is removed. +} + +\subsection{Step 4}{ + +The joined dataset is grouped by the observations from the input dataset +and restricted to the observations fulfilling the condition specified by +\code{filter}. +} + +\subsection{Step 5}{ + +The first observation of each group is selected +} + +\subsection{Step 6}{ + +The variable specified by \code{new_var} is added to the input dataset. It is +set to \code{true_value} for all observations which were selected in the +previous step. For the other observations it is set to \code{false_value}. +} +} +\examples{ +library(tibble) +library(admiral) + +# flag observations with a duration longer than 30 and +# at, after, or up to 7 days before a COVID AE (ACOVFL == "Y") +adae <- tribble( + ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, + "1", 10, "N", 1, + "1", 21, "N", 50, + "1", 23, "Y", 14, + "1", 32, "N", 31, + "1", 42, "N", 20, + "2", 11, "Y", 13, + "2", 23, "N", 2, + "3", 13, "Y", 12, + "4", 14, "N", 32, + "4", 21, "N", 41 +) + +derive_var_confirmation_flag( + adae, + new_var = ALCOVFL, + by_vars = vars(USUBJID), + join_vars = vars(ACOVFL, ADY), + join_type = "all", + order = vars(ADY), + filter = ADURN > 30 & ACOVFL.join == "Y" & ADY >= ADY.join - 7 +) + +# flag observations with AVALC == "Y" and AVALC == "Y" at one subsequent visit +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "Y", + "1", 2, "N", + "1", 3, "Y", + "1", 4, "N", + "2", 1, "Y", + "2", 2, "N", + "3", 1, "Y", + "4", 1, "N", + "4", 2, "N", +) + +derive_var_confirmation_flag( + data, + by_vars = vars(USUBJID), + new_var = CONFFL, + join_vars = vars(AVALC, AVISITN), + join_type = "after", + order = vars(AVISITN), + filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join +) + +# select observations with AVALC == "CR", AVALC == "CR" at a subsequent visit, +# only "CR" or "NE" in between, and at most one "NE" in between +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", + "3", 1, "CR", + "4", 1, "CR", + "4", 2, "NE", + "4", 3, "NE", + "4", 4, "CR", + "4", 5, "PR" +) + +derive_var_confirmation_flag( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + order = vars(AVISITN), + new_var = CONFFL, + first_cond = AVALC.join == "CR", + filter = AVALC == "CR" & all(AVALC.join \%in\% c("CR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1 +) + +# flag observations with AVALC == "PR", AVALC == "CR" or AVALC == "PR" +# at a subsequent visit at least 20 days later, only "CR", "PR", or "NE" +# in between, at most one "NE" in between, and "CR" is not followed by "PR" +data <- tribble( + ~USUBJID, ~ADY, ~AVALC, + "1", 6, "PR", + "1", 12, "CR", + "1", 24, "NE", + "1", 32, "CR", + "1", 48, "PR", + "2", 3, "PR", + "2", 21, "CR", + "2", 33, "PR", + "3", 11, "PR", + "4", 7, "PR", + "4", 12, "NE", + "4", 24, "NE", + "4", 32, "PR", + "4", 55, "PR" +) + +derive_var_confirmation_flag( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC, ADY), + join_type = "after", + order = vars(ADY), + new_var = CONFFL, + first_cond = AVALC.join \%in\% c("CR", "PR") & ADY.join - ADY >= 20, + filter = AVALC == "PR" & + all(AVALC.join \%in\% c("CR", "PR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1 & + ( + min_cond(var = ADY.join, cond = AVALC.join == "CR") > + max_cond(var = ADY.join, cond = AVALC.join == "PR") | + count_vals(var = AVALC.join, val = "CR") == 0 + ) +) +} +\seealso{ +\code{\link[=filter_confirmation]{filter_confirmation()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} +\author{ +Stefan Bundfuss +} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_disposition_dt.Rd b/man/derive_var_disposition_dt.Rd deleted file mode 100644 index 9238864e09..0000000000 --- a/man/derive_var_disposition_dt.Rd +++ /dev/null @@ -1,89 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_var_disposition_dt.R -\name{derive_var_disposition_dt} -\alias{derive_var_disposition_dt} -\title{Derive a Disposition Date} -\usage{ -derive_var_disposition_dt( - dataset, - dataset_ds, - new_var, - dtc, - filter_ds, - date_imputation = NULL, - preserve = FALSE, - subject_keys = vars(STUDYID, USUBJID) -) -} -\arguments{ -\item{dataset}{Input dataset} - -\item{dataset_ds}{Datasets containing the disposition information (e.g.: ds) - -It must contain: -\itemize{ -\item \code{STUDYID}, \code{USUBJID}, -\item The variable(s) specified in the \code{dtc} -\item The variables used in \code{filter_ds}. -}} - -\item{new_var}{Name of the disposition date variable - -a variable name is expected} - -\item{dtc}{The character date used to derive/impute the disposition date - -A character date is expected in a format like yyyy-mm-dd or yyyy-mm-ddThh:mm:ss. -If the year part is not recorded (missing date), no imputation is performed.} - -\item{filter_ds}{Filter condition for the disposition data. - -Filter used to select the relevant disposition data. -It is expected that the filter restricts \code{dataset_ds} such that there is at most -one observation per patient. An error is issued otherwise. - -Permitted Values: logical expression.} - -\item{date_imputation}{The value to impute the day/month when a datepart is missing. - -If \code{NULL}: no date imputation is performed and partial dates are returned -as missing. - -Otherwise, a character value is expected, either as a -\itemize{ -\item format with day and month specified as 'mm-dd': e.g. '06-15' for the 15th -of June -\item or as a keyword: 'FIRST', 'MID', 'LAST' to impute to the first/mid/last day/month. -} - -Default is \code{NULL}} - -\item{preserve}{Preserve day if month is missing and day is present - -For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} -(and \code{date_imputation = "MID"}). - -Permitted Values: \code{TRUE}, \code{FALSE} - -Default: \code{FALSE}} - -\item{subject_keys}{Variables to uniquely identify a subject - -A list of quosures where the expressions are symbols as returned by -\code{vars()} is expected.} -} -\value{ -the input dataset with the disposition date (\code{new_var}) added -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} - -This function is \emph{deprecated}, please use \code{derive_vars_merged_dt()} instead. - -Derive a disposition status date from the the relevant records in the disposition domain. -} -\author{ -Samia Kabi -} -\keyword{adsl} -\keyword{timing} diff --git a/man/derive_var_disposition_status.Rd b/man/derive_var_disposition_status.Rd index fada918367..f8db7bfdc6 100644 --- a/man/derive_var_disposition_status.Rd +++ b/man/derive_var_disposition_status.Rd @@ -38,10 +38,12 @@ A variable name is expected (e.g. \code{DSDECOD}).} Default: \code{format_eoxxstt_default()} defined as: -\if{html}{\out{
}}\preformatted{format_eoxxstt_default <- function(status) \{ +\if{html}{\out{
}}\preformatted{format_eoxxstt_default <- function(status) \{ case_when( + status \%in\% c("SCREEN FAILURE", "SCREENING NOT COMPLETED") ~ "NOT STARTED", status == "COMPLETED" ~ "COMPLETED", - status != "COMPLETED" & !is.na(status) ~ "DISCONTINUED", + !status \%in\% c("COMPLETED", "SCREEN FAILURE", "SCREENING NOT COMPLETED") + & !is.na(status) ~ "DISCONTINUED", TRUE ~ "ONGOING" ) \} @@ -65,8 +67,10 @@ The input dataset with the disposition status (\code{new_var}) added. \code{new_var} is derived based on the values given in \code{status_var} and according to the format defined by \code{format_new_var} (e.g. when the default format is used, the function will derive \code{new_var} as: +"NOT STARTED" if \code{status} is "SCREEN FAILURE" or "SCREENING NOT COMPLETED", "COMPLETED" if \code{status_var} == "COMPLETED", -"DISCONTINUED" if \code{status_var} is not "COMPLETED" nor NA, +"DISCONTINUED" if \code{status} is not in ("COMPLETED","SCREEN FAILURE", +"SCREENING NOT COMPLETED") nor NA, "ONGOING" otherwise). } \description{ @@ -79,8 +83,10 @@ data("admiral_dm") data("admiral_ds") # Default derivation: EOSSTT = -#- COMPLETED when status_var = COMPLETED -#- DISCONTINUED when status_var is not COMPLETED nor NA +#- NOT STARTED when status_var is SCREEN FAILURE or SCREENING NOT COMPLETED +#- COMPLETED when status_var is COMPLETED +#- DISCONTINUED when status_var is not COMPLETED nor SCREEN FAILURE nor +# SCREENING NOT COMPLETED nor NA #- ONGOING otherwise admiral_dm \%>\% @@ -93,16 +99,20 @@ admiral_dm \%>\% select(STUDYID, USUBJID, EOSSTT) # Specific derivation: EOSSTT = +#- NOT STARTED when status_var = SCREEN FAILURE #- COMPLETED when status_var = COMPLETED #- DISCONTINUED DUE TO AE when status_var = ADVERSE EVENT -#- DISCONTINUED NOT DUE TO AE when status_var != ADVERSE EVENT nor COMPLETED nor missing +#- DISCONTINUED NOT DUE TO AE when status_var != ADVERSE EVENT nor COMPLETED +# nor SCREEN FAILURE nor missing #- ONGOING otherwise format_eoxxstt1 <- function(x) { case_when( + x == "SCREEN FAILURE" ~ "NOT STARTED", x == "COMPLETED" ~ "COMPLETED", x == "ADVERSE EVENT" ~ "DISCONTINUED DUE TO AE", - !(x \%in\% c("ADVERSE EVENT", "COMPLETED")) & !is.na(x) ~ "DISCONTINUED NOT DUE TO AE", + !(x \%in\% c("ADVERSE EVENT", "COMPLETED", "SCREEN FAILURE")) & !is.na(x) ~ + "DISCONTINUED NOT DUE TO AE", TRUE ~ "ONGOING" ) } @@ -117,7 +127,17 @@ admiral_dm \%>\% ) \%>\% select(STUDYID, USUBJID, EOSSTT) } +\seealso{ +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_age_years}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{derive_var_extreme_dtm}()}, +\code{\link{derive_var_extreme_dt}()}, +\code{\link{derive_vars_aage}()}, +\code{\link{derive_vars_disposition_reason}()} +} \author{ Samia Kabi } -\keyword{adsl} +\concept{der_adsl} +\keyword{der_adsl} diff --git a/man/derive_var_dthcaus.Rd b/man/derive_var_dthcaus.Rd index 623bdf1ba3..067700c434 100644 --- a/man/derive_var_dthcaus.Rd +++ b/man/derive_var_dthcaus.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/derive_var_dthcaus.R \name{derive_var_dthcaus} \alias{derive_var_dthcaus} +\alias{dthcaus_source} \title{Derive Death Cause} \usage{ derive_var_dthcaus( @@ -10,6 +11,16 @@ derive_var_dthcaus( source_datasets, subject_keys = vars(STUDYID, USUBJID) ) + +dthcaus_source( + dataset_name, + filter, + date, + order = NULL, + mode = "first", + dthcaus, + traceability_vars = NULL +) } \arguments{ \item{dataset}{Input dataset. @@ -25,9 +36,45 @@ death cause} A list of quosures where the expressions are symbols as returned by \code{vars()} is expected.} + +\item{dataset_name}{The name of the dataset, i.e. a string, used to search for +the death cause.} + +\item{filter}{An expression used for filtering \code{dataset}.} + +\item{date}{A date or datetime variable to be used for sorting \code{dataset}.} + +\item{order}{Sort order + +Additional variables to be used for sorting the \code{dataset} which is ordered by the +\code{date} and \code{order}. Can be used to avoid duplicate record warning. + +\emph{Default}: \code{NULL} + +\emph{Permitted Values}: list of variables or \verb{desc()} function calls +created by \code{vars()}, e.g., \code{vars(ADT, desc(AVAL))} or \code{NULL}} + +\item{mode}{One of \code{"first"} or \code{"last"}. +Either the \code{"first"} or \code{"last"} observation is preserved from the \code{dataset} +which is ordered by \code{date}.} + +\item{dthcaus}{A variable name or a string literal --- if a variable name, e.g., \code{AEDECOD}, +it is the variable in the source dataset to be used to assign values to +\code{DTHCAUS}; if a string literal, e.g. \code{"Adverse Event"}, it is the fixed value +to be assigned to \code{DTHCAUS}.} + +\item{traceability_vars}{A named list returned by \code{\link[=vars]{vars()}} listing the traceability variables, +e.g. \code{vars(DTHDOM = "DS", DTHSEQ = DSSEQ)}. +The left-hand side (names of the list elements) gives the names of the traceability variables +in the returned dataset. +The right-hand side (values of the list elements) gives the values of the traceability variables +in the returned dataset. +These can be either strings or symbols referring to existing variables.} } \value{ -The input dataset with \code{DTHCAUS} variable added. +\code{derive_var_dthcaus()} returns the input dataset with \code{DTHCAUS} variable added. + +\code{dthcaus_source()} returns an object of class "dthcaus_source". } \description{ Derive death cause (\code{DTHCAUS}) and add traceability variables if required. @@ -39,30 +86,45 @@ the one from the source with the earliest death date will be used. If dates are equivalent, the first source will be kept, so the user should provide the inputs in the preferred order. } +\section{Functions}{ +\itemize{ +\item \code{dthcaus_source}: Create objects of class "dthcaus_source" +}} + \examples{ -adsl <- tibble::tribble( - ~STUDYID, ~USUBJID, +library(tibble) +library(dplyr) +library(lubridate) + +adsl <- tribble( + ~STUDYID, ~USUBJID, "STUDY01", "PAT01", "STUDY01", "PAT02", "STUDY01", "PAT03" ) -ae <- tibble::tribble( - ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, - "STUDY01", "PAT01", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" -) -ds <- tibble::tribble( +ae <- tribble( + ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, + "STUDY01", "PAT01", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" +) \%>\% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) +ds <- tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, "STUDY01", "PAT02", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", "STUDY01", "PAT02", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "STUDY01", "PAT02", 3, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2022-02-01", "STUDY01", "PAT03", 1, "DEATH", "POST STUDY REPORTING OF DEATH", "2022-03-03" -) +) \%>\% + mutate( + DSSTDT = ymd(DSSTDTC) + ) # Derive `DTHCAUS` only - for on-study deaths only src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = AEDECOD ) @@ -70,7 +132,7 @@ src_ae <- dthcaus_source( src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM ) @@ -81,7 +143,7 @@ derive_var_dthcaus(adsl, src_ae, src_ds, source_datasets = list(ae = ae, ds = ds src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = AEDECOD, traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -90,7 +152,7 @@ src_ae <- dthcaus_source( src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM, traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) @@ -102,7 +164,7 @@ derive_var_dthcaus(adsl, src_ae, src_ds, source_datasets = list(ae = ae, ds = ds src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = AEDECOD, traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -111,7 +173,7 @@ src_ae <- dthcaus_source( src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM, traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) @@ -120,7 +182,7 @@ src_ds <- dthcaus_source( src_ds_post <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & DSTERM == "POST STUDY REPORTING OF DEATH", - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = "POST STUDY: UNKNOWN CAUSE", traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) @@ -129,10 +191,40 @@ src_ds_post <- dthcaus_source( derive_var_dthcaus(adsl, src_ae, src_ds, src_ds_post, source_datasets = list(ae = ae, ds = ds)) } \seealso{ -\code{\link[=dthcaus_source]{dthcaus_source()}} +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_age_years}()}, +\code{\link{derive_var_disposition_status}()}, +\code{\link{derive_var_extreme_dtm}()}, +\code{\link{derive_var_extreme_dt}()}, +\code{\link{derive_vars_aage}()}, +\code{\link{derive_vars_disposition_reason}()} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ -Shimeng Huang, Samia Kabi, Thomas Neitmann +Shimeng Huang, Samia Kabi, Thomas Neitmann, Tamara Senior } -\keyword{adsl} -\keyword{derivation} +\concept{der_adsl} +\concept{source_specifications} +\keyword{der_adsl} +\keyword{source_specifications} diff --git a/man/derive_var_extreme_dt.Rd b/man/derive_var_extreme_dt.Rd index 6f9086724b..44255f3614 100644 --- a/man/derive_var_extreme_dt.Rd +++ b/man/derive_var_extreme_dt.Rd @@ -52,11 +52,6 @@ The following steps are performed to create the output dataset: element are selected. Then for each patient the first or last observation (with respect to \code{date} and \code{mode}) is selected. \item The new variable is set to the variable specified by the \code{date} element. -If the date variable is a date variable, the time is imputed as -\code{time_imputation = "first"}. If the source variable is a character -variable, it is converted to a datetime. If the date is incomplete, it is -imputed as specified by the \code{date_imputation} element and with -\code{time_imputation = "first"}. \item The variables specified by the \code{traceability_vars} element are added. \item The selected observations of all source datasets are combined into a single dataset. @@ -77,19 +72,37 @@ data("admiral_adsl") # derive last known alive date (LSTALVDT) ae_start <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first", + date = AESTDT ) ae_end <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first", + date = AEENDT ) + +ae_ext <- admiral_ae \%>\% + derive_vars_dt( + dtc = AESTDTC, + new_vars_prefix = "AEST", + highest_imputation = "M" + ) \%>\% + derive_vars_dt( + dtc = AEENDTC, + new_vars_prefix = "AEEN", + highest_imputation = "M" + ) + lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = nchar(LBDTC) >= 10, + date = LBDT, + filter = !is.na(LBDT), ) + +lb_ext <- derive_vars_dt( + admiral_lb, + dtc = LBDTC, + new_vars_prefix = "LB" +) + adsl_date <- date_source(dataset_name = "adsl", date = TRTEDT) admiral_dm \%>\% @@ -98,7 +111,8 @@ admiral_dm \%>\% ae_start, ae_end, lb_date, adsl_date, source_datasets = list( adsl = admiral_adsl, - ae = admiral_ae, lb = admiral_lb + ae = ae_ext, + lb = lb_ext ), mode = "last" ) \%>\% @@ -107,8 +121,7 @@ admiral_dm \%>\% # derive last alive date and traceability variables ae_start <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first", + date = AESTDT, traceability_vars = vars( LALVDOM = "AE", LALVSEQ = AESEQ, @@ -118,8 +131,7 @@ ae_start <- date_source( ae_end <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first", + date = AEENDT, traceability_vars = vars( LALVDOM = "AE", LALVSEQ = AESEQ, @@ -128,8 +140,8 @@ ae_end <- date_source( ) lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = nchar(LBDTC) >= 10, + date = LBDT, + filter = !is.na(LBDT), traceability_vars = vars( LALVDOM = "LB", LALVSEQ = LBSEQ, @@ -153,19 +165,26 @@ admiral_dm \%>\% ae_start, ae_end, lb_date, adsl_date, source_datasets = list( adsl = admiral_adsl, - ae = admiral_ae, lb = admiral_lb + ae = ae_ext, + lb = lb_ext ), mode = "last" ) \%>\% select(USUBJID, LSTALVDT, LALVDOM, LALVSEQ, LALVVAR) } \seealso{ -\code{\link[=date_source]{date_source()}}, \code{\link[=derive_var_extreme_dtm]{derive_var_extreme_dtm()}}, -\code{\link[=derive_vars_merged_dt]{derive_vars_merged_dt()}}, \code{\link[=derive_vars_merged_dtm]{derive_vars_merged_dtm()}}, -\code{\link[=derive_vars_merged]{derive_vars_merged()}} +\code{\link[=date_source]{date_source()}}, \code{\link[=derive_var_extreme_dtm]{derive_var_extreme_dtm()}}, \code{\link[=derive_vars_merged]{derive_vars_merged()}} + +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_age_years}()}, +\code{\link{derive_var_disposition_status}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{derive_var_extreme_dtm}()}, +\code{\link{derive_vars_aage}()}, +\code{\link{derive_vars_disposition_reason}()} } \author{ Stefan Bundfuss, Thomas Neitmann } -\keyword{adsl} -\keyword{derivation} +\concept{der_adsl} +\keyword{der_adsl} diff --git a/man/derive_var_extreme_dtm.Rd b/man/derive_var_extreme_dtm.Rd index e4a0c558ed..95e3de280a 100644 --- a/man/derive_var_extreme_dtm.Rd +++ b/man/derive_var_extreme_dtm.Rd @@ -52,11 +52,8 @@ The following steps are performed to create the output dataset: element are selected. Then for each patient the first or last observation (with respect to \code{date} and \code{mode}) is selected. \item The new variable is set to the variable specified by the \code{date} element. -If the date variable is a date variable, the time is imputed as specified -by the \code{time_imputation} element. If the source variable is a character -variable, it is converted to a datetime. If the date is incomplete, it is -imputed as specified by the \code{date_imputation} and \code{time_imputation} -element. +If this is a date variable (rather than datetime), then the time is imputed +as \code{"00:00:00"}. \item The variables specified by the \code{traceability_vars} element are added. \item The selected observations of all source datasets are combined into a single dataset. @@ -76,22 +73,37 @@ data("admiral_adsl") # derive last known alive datetime (LSTALVDTM) ae_start <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first", - time_imputation = "first" + date = AESTDTM ) ae_end <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first", - time_imputation = "first" + date = AEENDTM ) + +ae_ext <- admiral_ae \%>\% + derive_vars_dtm( + dtc = AESTDTC, + new_vars_prefix = "AEST", + highest_imputation = "M" + ) \%>\% + derive_vars_dtm( + dtc = AEENDTC, + new_vars_prefix = "AEEN", + highest_imputation = "M" + ) + lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = nchar(LBDTC) >= 10, - time_imputation = "first" + date = LBDTM, + filter = !is.na(LBDTM) ) + +lb_ext <- derive_vars_dtm( + admiral_lb, + dtc = LBDTC, + new_vars_prefix = "LB" +) + adsl_date <- date_source(dataset_name = "adsl", date = TRTEDTM) admiral_dm \%>\% @@ -100,7 +112,7 @@ admiral_dm \%>\% ae_start, ae_end, lb_date, adsl_date, source_datasets = list( adsl = admiral_adsl, - ae = admiral_ae, lb = admiral_lb + ae = ae_ext, lb = lb_ext ), mode = "last" ) \%>\% @@ -109,9 +121,7 @@ admiral_dm \%>\% # derive last alive datetime and traceability variables ae_start <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first", - time_imputation = "first", + date = AESTDTM, traceability_vars = vars( LALVDOM = "AE", LALVSEQ = AESEQ, @@ -121,9 +131,7 @@ ae_start <- date_source( ae_end <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first", - time_imputation = "first", + date = AEENDTM, traceability_vars = vars( LALVDOM = "AE", LALVSEQ = AESEQ, @@ -132,9 +140,8 @@ ae_end <- date_source( ) lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = nchar(LBDTC) >= 10, - time_imputation = "first", + date = LBDTM, + filter = !is.na(LBDTM), traceability_vars = vars( LALVDOM = "LB", LALVSEQ = LBSEQ, @@ -158,7 +165,8 @@ admiral_dm \%>\% ae_start, ae_end, lb_date, adsl_date, source_datasets = list( adsl = admiral_adsl, - ae = admiral_ae, lb = admiral_lb + ae = ae_ext, + lb = lb_ext ), mode = "last" ) \%>\% @@ -166,11 +174,18 @@ admiral_dm \%>\% } \seealso{ \code{\link[=date_source]{date_source()}}, \code{\link[=derive_var_extreme_dt]{derive_var_extreme_dt()}}, -\code{\link[=derive_vars_merged_dt]{derive_vars_merged_dt()}}, \code{\link[=derive_vars_merged_dtm]{derive_vars_merged_dtm()}}, \code{\link[=derive_vars_merged]{derive_vars_merged()}} + +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_age_years}()}, +\code{\link{derive_var_disposition_status}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{derive_var_extreme_dt}()}, +\code{\link{derive_vars_aage}()}, +\code{\link{derive_vars_disposition_reason}()} } \author{ Stefan Bundfuss, Thomas Neitmann } -\keyword{adsl} -\keyword{derivation} +\concept{der_adsl} +\keyword{der_adsl} diff --git a/man/derive_var_extreme_flag.Rd b/man/derive_var_extreme_flag.Rd index f68e0dce0f..18588353b3 100644 --- a/man/derive_var_extreme_flag.Rd +++ b/man/derive_var_extreme_flag.Rd @@ -204,9 +204,25 @@ admiral_ae \%>\% } \seealso{ \code{\link[=derive_var_worst_flag]{derive_var_worst_flag()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} } \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_last_dose_amt.Rd b/man/derive_var_last_dose_amt.Rd index f18a73e5f2..f2504b0423 100644 --- a/man/derive_var_last_dose_amt.Rd +++ b/man/derive_var_last_dose_amt.Rd @@ -75,41 +75,72 @@ library(admiral.test) data(admiral_ae) data(ex_single) -admiral_ae \%>\% +ex_single <- derive_vars_dtm( + head(ex_single, 100), + dtc = EXENDTC, + new_vars_prefix = "EXEN", + flag_imputation = "none" +) + +adae <- admiral_ae \%>\% head(100) \%>\% + derive_vars_dtm( + dtc = AESTDTC, + new_vars_prefix = "AST", + highest_imputation = "M" + ) + +adae \%>\% derive_var_last_dose_amt( - head(ex_single, 100), + dataset_ex = ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_date = EXENDTC, - analysis_date = AESTDTC, - single_dose_condition = (EXSTDTC == EXENDTC), + !is.na(EXENDTM), + dose_date = EXENDTM, + analysis_date = ASTDTM, new_var = LDOSE, dose_var = EXDOSE ) \%>\% select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSE) # or with traceability variables -admiral_ae \%>\% - head(100) \%>\% +adae \%>\% derive_var_last_dose_amt( - head(ex_single, 100), + dataset_ex = ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_date = EXENDTC, - analysis_date = AESTDTC, - single_dose_condition = (EXSTDTC == EXENDTC), + !is.na(EXENDTM), + dose_date = EXENDTM, + analysis_date = ASTDTM, new_var = LDOSE, dose_var = EXDOSE, - traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXDOSE") + traceability_vars = vars( + LDOSEDOM = "EX", + LDOSESEQ = EXSEQ, + LDOSEVAR = "EXDOSE" + ) ) \%>\% select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDOM, LDOSESEQ, LDOSEVAR, LDOSE) } \seealso{ \code{\link[=derive_vars_last_dose]{derive_vars_last_dose()}}, \code{\link[=create_single_dose_dataset]{create_single_dose_dataset()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} } \author{ Annie Yang } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_last_dose_date.Rd b/man/derive_var_last_dose_date.Rd index c9794a6e3d..d5ed130666 100644 --- a/man/derive_var_last_dose_date.Rd +++ b/man/derive_var_last_dose_date.Rd @@ -78,25 +78,54 @@ library(admiral.test) data(admiral_ae) data(ex_single) -admiral_ae \%>\% +ex_single <- derive_vars_dtm( + head(ex_single, 100), + dtc = EXENDTC, + new_vars_prefix = "EXEN", + flag_imputation = "none" +) + +adae <- admiral_ae \%>\% head(100) \%>\% + derive_vars_dtm( + dtc = AESTDTC, + new_vars_prefix = "AST", + highest_imputation = "M" + ) + +adae \%>\% derive_var_last_dose_date( - head(ex_single, 100), + dataset_ex = ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_date = EXENDTC, - analysis_date = AESTDTC, - single_dose_condition = (EXSTDTC == EXENDTC), + !is.na(EXENDTM), + dose_date = EXENDTM, + analysis_date = ASTDTM, new_var = LDOSEDTM, - traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXDOSE") + traceability_vars = vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXDOSE") ) \%>\% select(STUDYID, USUBJID, AESEQ, AESTDTC, LDOSEDOM, LDOSESEQ, LDOSEVAR, LDOSEDTM) } \seealso{ \code{\link[=derive_vars_last_dose]{derive_vars_last_dose()}}, \code{\link[=create_single_dose_dataset]{create_single_dose_dataset()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} } \author{ Ben Straub } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_last_dose_grp.Rd b/man/derive_var_last_dose_grp.Rd index cea4d2dc8b..18d3c45ddd 100644 --- a/man/derive_var_last_dose_grp.Rd +++ b/man/derive_var_last_dose_grp.Rd @@ -95,31 +95,60 @@ library(admiral.test) data(admiral_ae) data(ex_single) -admiral_ae \%>\% +ex_single <- derive_vars_dtm( + head(ex_single, 100), + dtc = EXSTDTC, + new_vars_prefix = "EXST", + flag_imputation = "none" +) + +adae <- admiral_ae \%>\% head(100) \%>\% + derive_vars_dtm( + dtc = AESTDTC, + new_vars_prefix = "AST", + highest_imputation = "M" + ) + +adae \%>\% derive_var_last_dose_grp( - head(ex_single, 100), + dataset_ex = ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, + !is.na(EXSTDTM), by_vars = vars(STUDYID, USUBJID), - dose_date = EXSTDTC, + dose_date = EXSTDTM, new_var = LDGRP, grp_brks = c(0, 20, 40, 60), grp_lbls = c("Low", "Medium", "High"), include_lowest = TRUE, right = TRUE, dose_var = EXDOSE, - analysis_date = AESTDTC, - single_dose_condition = (EXSTDTC == EXENDTC), - traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") + analysis_date = ASTDTM, + traceability_vars = vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") ) \%>\% select(USUBJID, LDGRP, LDOSEDOM, LDOSESEQ, LDOSEVAR) } \seealso{ \code{\link[=derive_vars_last_dose]{derive_vars_last_dose()}}, \code{\link[=cut]{cut()}}, \code{\link[=create_single_dose_dataset]{create_single_dose_dataset()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} } \author{ Ben Straub } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_lstalvdt.Rd b/man/derive_var_lstalvdt.Rd deleted file mode 100644 index c75c951a02..0000000000 --- a/man/derive_var_lstalvdt.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_var_extreme_date.R -\name{derive_var_lstalvdt} -\alias{derive_var_lstalvdt} -\title{Derive Last Known Alive Date} -\usage{ -derive_var_lstalvdt( - dataset, - ..., - source_datasets, - subject_keys = vars(STUDYID, USUBJID) -) -} -\arguments{ -\item{dataset}{Input dataset - -The variables specified by \code{subject_keys} are required.} - -\item{...}{Source(s) of known alive dates. One or more \code{lstalvdt_source()} objects are -expected.} - -\item{source_datasets}{A named \code{list} containing datasets in which to search for the -last known alive date} - -\item{subject_keys}{Variables to uniquely identify a subject - -A list of quosures where the expressions are symbols as returned by -\code{vars()} is expected.} -} -\value{ -The input dataset with the \code{LSTALVDT} variable added. -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} - -\emph{Deprecated}, please use \code{derive_var_extreme_dt()} instead. Add the last -known alive date (\code{LSTALVDT}) to the dataset. -} -\author{ -Stefan Bundfuss, Thomas Neitmann -} -\keyword{adsl} -\keyword{derivation} diff --git a/man/derive_var_merged_cat.Rd b/man/derive_var_merged_cat.Rd index 06eef57dea..13da2437ed 100644 --- a/man/derive_var_merged_cat.Rd +++ b/man/derive_var_merged_cat.Rd @@ -148,8 +148,25 @@ derive_var_merged_cat( ) \%>\% select(STUDYID, USUBJID, AGE, AGEU, WGTBLCAT) } +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_merged_character.Rd b/man/derive_var_merged_character.Rd index b637ce904f..7404f4e0b9 100644 --- a/man/derive_var_merged_character.Rd +++ b/man/derive_var_merged_character.Rd @@ -128,8 +128,25 @@ derive_var_merged_character( ) \%>\% select(STUDYID, USUBJID, AGE, AGEU, DISPSTAT) } +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_merged_exist_flag.Rd b/man/derive_var_merged_exist_flag.Rd index f3f9117723..0ace6e64d2 100644 --- a/man/derive_var_merged_exist_flag.Rd +++ b/man/derive_var_merged_exist_flag.Rd @@ -118,8 +118,25 @@ derive_var_merged_exist_flag( ) \%>\% select(STUDYID, USUBJID, AGE, AGEU, WTBLHIFL) } +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_obs_number.Rd b/man/derive_var_obs_number.Rd index 546264992b..d9ec2e4a76 100644 --- a/man/derive_var_obs_number.Rd +++ b/man/derive_var_obs_number.Rd @@ -71,8 +71,25 @@ admiral_vs \%>\% order = vars(VISITNUM, VSTPTNUM) ) } +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_var_ontrtfl.Rd b/man/derive_var_ontrtfl.Rd index 8e9b52e95a..4b161383da 100644 --- a/man/derive_var_ontrtfl.Rd +++ b/man/derive_var_ontrtfl.Rd @@ -185,8 +185,20 @@ derive_var_ontrtfl( span_period = "Y" ) } +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_pchg}()}, +\code{\link{derive_var_shift}()} +} \author{ Alice Ehmann, Teckla Akinyi } -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_pchg.Rd b/man/derive_var_pchg.Rd index eaf74044ef..94e017edaf 100644 --- a/man/derive_var_pchg.Rd +++ b/man/derive_var_pchg.Rd @@ -34,9 +34,20 @@ derive_var_pchg(advs) } \seealso{ \code{\link[=derive_var_chg]{derive_var_chg()}} + +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_shift}()} } \author{ Thomas Neitmann } -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_shift.Rd b/man/derive_var_shift.Rd index 326dfe88c0..ce4826a983 100644 --- a/man/derive_var_shift.Rd +++ b/man/derive_var_shift.Rd @@ -79,10 +79,20 @@ data \%>\% filter = is.na(ABLFL) ) } +\seealso{ +BDS-Findings Functions that returns variable appended to dataset: +\code{\link{derive_var_analysis_ratio}()}, +\code{\link{derive_var_anrind}()}, +\code{\link{derive_var_atoxgr_dir}()}, +\code{\link{derive_var_atoxgr}()}, +\code{\link{derive_var_basetype}()}, +\code{\link{derive_var_base}()}, +\code{\link{derive_var_chg}()}, +\code{\link{derive_var_ontrtfl}()}, +\code{\link{derive_var_pchg}()} +} \author{ Annie Yang } -\keyword{adam} -\keyword{adlb} -\keyword{bds} -\keyword{derivation} +\concept{der_bds_findings} +\keyword{der_bds_findings} diff --git a/man/derive_var_trtdurd.Rd b/man/derive_var_trtdurd.Rd index 5dfbaa866e..12f9fb4afb 100644 --- a/man/derive_var_trtdurd.Rd +++ b/man/derive_var_trtdurd.Rd @@ -50,10 +50,18 @@ derive_var_trtdurd(data) } \seealso{ \code{\link[=derive_vars_duration]{derive_vars_duration()}} + +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_vars_dtm_to_dt}()}, +\code{\link{derive_vars_dtm_to_tm}()}, +\code{\link{derive_vars_dtm}()}, +\code{\link{derive_vars_dt}()}, +\code{\link{derive_vars_duration}()}, +\code{\link{derive_vars_dy}()} } \author{ Stefan Bundfuss } -\keyword{adsl} -\keyword{derivation} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_var_trtedtm.Rd b/man/derive_var_trtedtm.Rd deleted file mode 100644 index 4dea08b11e..0000000000 --- a/man/derive_var_trtedtm.Rd +++ /dev/null @@ -1,59 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_var_trtedtm.R -\name{derive_var_trtedtm} -\alias{derive_var_trtedtm} -\title{Derive Datetime of Last Exposure to Treatment} -\usage{ -derive_var_trtedtm( - dataset, - dataset_ex, - filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, "PLACEBO"))) & - nchar(EXENDTC) >= 10, - subject_keys = vars(STUDYID, USUBJID) -) -} -\arguments{ -\item{dataset}{Input dataset - -The variables specified by the \code{by_vars} parameter are expected.} - -\item{dataset_ex}{\code{ex} dataset - -The variables \code{EXENDTC}, \code{EXSEQ}, and those specified by the \code{filter_ex} -parameter are expected.} - -\item{filter_ex}{Filter condition for the ex dataset - -Only observations of the ex dataset which fulfill the specified condition -are considered for the treatment start date. - -Default: \code{EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, 'PLACEBO')) & nchar(EXENDTC) >= 10} - -Permitted Values: logical expression} - -\item{subject_keys}{Variables to uniquely identify a subject - -A list of quosures where the expressions are symbols as returned by -\code{vars()} is expected.} -} -\value{ -The input dataset with \code{TRTEDTM} variable added -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} - -This function is \emph{deprecated}, please use \code{derive_vars_merged_dtm()} instead. - -Derives datetime of last exposure to treatment (\code{TRTEDTM}) -} -\details{ -For each group (with respect to the variables specified for the -\code{by_vars} parameter) the first observation (with respect to the order -specified for the \code{order} parameter) is included in the output dataset. -} -\author{ -Stefan Bundfuss -} -\keyword{adsl} -\keyword{derivation} -\keyword{timing} diff --git a/man/derive_var_trtsdtm.Rd b/man/derive_var_trtsdtm.Rd deleted file mode 100644 index 03de147b9a..0000000000 --- a/man/derive_var_trtsdtm.Rd +++ /dev/null @@ -1,59 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_var_trtsdtm.R -\name{derive_var_trtsdtm} -\alias{derive_var_trtsdtm} -\title{Derive Datetime of First Exposure to Treatment} -\usage{ -derive_var_trtsdtm( - dataset, - dataset_ex, - filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, "PLACEBO"))) & - nchar(EXSTDTC) >= 10, - subject_keys = vars(STUDYID, USUBJID) -) -} -\arguments{ -\item{dataset}{Input dataset - -The variables specified by the \code{by_vars} parameter are expected.} - -\item{dataset_ex}{\code{ex} dataset - -The variables \code{EXSTDTC}, \code{EXSEQ}, and those specified by the \code{filter_ex} -parameter are expected.} - -\item{filter_ex}{Filter condition for the ex dataset - -Only observations of the ex dataset which fulfill the specified condition -are considered for the treatment start date. - -Default: \code{EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, 'PLACEBO')) & nchar(EXSTDTC) >= 10} - -Permitted Values: logical expression} - -\item{subject_keys}{Variables to uniquely identify a subject - -A list of quosures where the expressions are symbols as returned by -\code{vars()} is expected.} -} -\value{ -The input dataset with \code{TRTSDTM} variable added -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} - -This function is \emph{deprecated}, please use \code{derive_vars_merged_dtm()} instead. - -Derives datetime of first exposure to treatment (\code{TRTSDTM}) -} -\details{ -For each group (with respect to the variables specified for the -\code{by_vars} parameter) the first observation (with respect to the order -specified for the \code{order} parameter) is included in the output dataset. -} -\author{ -Stefan Bundfuss -} -\keyword{adsl} -\keyword{derivation} -\keyword{timing} diff --git a/man/derive_var_worst_flag.Rd b/man/derive_var_worst_flag.Rd index 752c22f2bc..cb0aea7e7f 100644 --- a/man/derive_var_worst_flag.Rd +++ b/man/derive_var_worst_flag.Rd @@ -139,9 +139,25 @@ restrict_derivation( } \seealso{ \code{\link[=derive_var_extreme_flag]{derive_var_extreme_flag()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} } \author{ Ondrej Slama } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_vars_aage.Rd b/man/derive_vars_aage.Rd index 78bcbacc8b..e7e3413f51 100644 --- a/man/derive_vars_aage.Rd +++ b/man/derive_vars_aage.Rd @@ -51,7 +51,10 @@ Derives analysis age (\code{AAGE}) and analysis age unit (\code{AAGEU}) } \details{ The age is derived as the integer part of the duration from start to -end date in the specified unit. +end date in the specified unit. When 'years' or 'months' are specified in the \code{out_unit} +parameter, because of the underlying \code{lubridate::time_length()} function that is used +here, results are calculated based on the actual calendar length of months or years +rather than assuming equal days every month (30.4375 days) or every year (365.25 days). } \examples{ data <- tibble::tribble( @@ -63,7 +66,17 @@ derive_vars_aage(data) } \seealso{ \code{\link[=derive_vars_duration]{derive_vars_duration()}} + +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_age_years}()}, +\code{\link{derive_var_disposition_status}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{derive_var_extreme_dtm}()}, +\code{\link{derive_var_extreme_dt}()}, +\code{\link{derive_vars_disposition_reason}()} } \author{ Stefan Bundfuss } +\concept{der_adsl} +\keyword{der_adsl} diff --git a/man/derive_vars_atc.Rd b/man/derive_vars_atc.Rd index be58b04159..e0858a5f27 100644 --- a/man/derive_vars_atc.Rd +++ b/man/derive_vars_atc.Rd @@ -7,7 +7,8 @@ derive_vars_atc( dataset, dataset_facm, - by_vars = vars(USUBJID, CMREFID = FAREFID) + by_vars = vars(USUBJID, CMREFID = FAREFID), + value_var = FASTRESC ) } \arguments{ @@ -17,12 +18,17 @@ The variables specified by the \code{by_vars} parameter are required} \item{dataset_facm}{FACM dataset -The variables specified by the \code{by_vars} parameter, \code{FAGRPID}, \code{FATESTCD} and -\code{FASTRESC} are required} +The variables specified by the \code{by_vars} and \code{value_var} parameters, +\code{FAGRPID} and \code{FATESTCD} are required} \item{by_vars}{Keys used to merge \code{dataset_facm} with \code{dataset} \emph{Permitted Values:} list of variables} + +\item{value_var}{The variable of \code{dataset_facm} containing the values of the +transposed variables + +Default: \code{FASTRESC}} } \value{ The input dataset with ATC variables added @@ -63,8 +69,15 @@ facm <- tibble::tribble( derive_vars_atc(cm, facm) } +\seealso{ +OCCDS Functions: +\code{\link{create_query_data}()}, +\code{\link{create_single_dose_dataset}()}, +\code{\link{derive_vars_query}()}, +\code{\link{get_terms_from_db}()} +} \author{ Thomas Neitmann } -\keyword{adcm} -\keyword{derivation} +\concept{der_occds} +\keyword{der_occds} diff --git a/man/derive_vars_disposition_reason.Rd b/man/derive_vars_disposition_reason.Rd index 4c078a6c44..0c3fe7bc24 100644 --- a/man/derive_vars_disposition_reason.Rd +++ b/man/derive_vars_disposition_reason.Rd @@ -144,8 +144,17 @@ admiral_dm \%>\% } \seealso{ \code{\link[=format_reason_default]{format_reason_default()}} + +ADSL Functions that returns variable appended to dataset: +\code{\link{derive_var_age_years}()}, +\code{\link{derive_var_disposition_status}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{derive_var_extreme_dtm}()}, +\code{\link{derive_var_extreme_dt}()}, +\code{\link{derive_vars_aage}()} } \author{ Samia Kabi } -\keyword{adsl} +\concept{der_adsl} +\keyword{der_adsl} diff --git a/man/derive_vars_dt.Rd b/man/derive_vars_dt.Rd index 1f58ec7484..c858386ce9 100644 --- a/man/derive_vars_dt.Rd +++ b/man/derive_vars_dt.Rd @@ -8,7 +8,8 @@ derive_vars_dt( dataset, new_vars_prefix, dtc, - date_imputation = NULL, + highest_imputation = "n", + date_imputation = "first", flag_imputation = "auto", min_dates = NULL, max_dates = NULL, @@ -29,29 +30,51 @@ the specified prefix and for the date imputation flag "DTF". I.e., for \item{dtc}{The \code{'--DTC'} date to impute A character date is expected in a format like \code{yyyy-mm-dd} or -\code{yyyy-mm-ddThh:mm:ss}. If the year part is not recorded (missing date), no -imputation is performed.} +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} -\item{date_imputation}{The value to impute the day/month when a datepart is -missing. +\item{highest_imputation}{Highest imputation level + +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. + +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. -If \code{NULL}: no date imputation is performed and partial dates are returned as +\emph{Default}: \code{"n"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"n"} (none, lowest level)} + +\item{date_imputation}{The value to impute the day/month when a datepart is missing. -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}} \item{flag_imputation}{Whether the date imputation flag must also be derived. If \code{"auto"} is specified, the date imputation flag is derived if the -\code{date_imputation} parameter is not null. +\code{date_imputation} argument is not null. \emph{Default}: \code{"auto"} @@ -68,13 +91,13 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} @@ -105,17 +128,22 @@ if requested) added. } \description{ Derive a date (\code{'--DT'}) from a date character vector (\verb{'--DTC}'). -The date can be imputed (see \code{date_imputation} parameter) +The date can be imputed (see \code{date_imputation} argument) and the date imputation flag ('\verb{--DTF'}) can be added. } \details{ +In {admiral} we don't allow users to pick any single part of the date/time to +impute, we only enable to impute up to a highest level, i.e. you couldn't +choose to say impute months, but not days. + The presence of a \code{'--DTF'} variable is checked and if it already exists in the input dataset, a warning is issued and \code{'--DTF'} will be overwritten. } \examples{ +library(tibble) library(lubridate) -mhdt <- tibble::tribble( +mhdt <- tribble( ~MHSTDTC, "2019-07-18T15:25:40", "2019-07-18T15:25", @@ -127,7 +155,7 @@ mhdt <- tibble::tribble( ) # Create ASTDT and ASTDTF -# no imputation for partial date +# No imputation for partial date derive_vars_dt( mhdt, new_vars_prefix = "AST", @@ -140,7 +168,7 @@ derive_vars_dt( mhdt, new_vars_prefix = "AST", dtc = MHSTDTC, - date_imputation = "FIRST" + highest_imputation = "M" ) # Impute partial dates to 6th of April @@ -148,6 +176,7 @@ derive_vars_dt( mhdt, new_vars_prefix = "AST", dtc = MHSTDTC, + highest_imputation = "M", date_imputation = "04-06" ) @@ -157,7 +186,8 @@ derive_vars_dt( mhdt, new_vars_prefix = "AEN", dtc = MHSTDTC, - date_imputation = "LAST" + highest_imputation = "M", + date_imputation = "last" ) # Create BIRTHDT @@ -166,13 +196,14 @@ derive_vars_dt( mhdt, new_vars_prefix = "BIRTH", dtc = MHSTDTC, - date_imputation = "MID", + highest_imputation = "M", + date_imputation = "mid", flag_imputation = "none" ) # Impute AE start date to the first date and ensure that the imputed date # is not before the treatment start date -adae <- tibble::tribble( +adae <- tribble( ~AESTDTC, ~TRTSDTM, "2020-12", ymd_hms("2020-12-06T12:12:12"), "2020-11", ymd_hms("2020-12-06T12:12:12") @@ -182,11 +213,11 @@ derive_vars_dt( adae, dtc = AESTDTC, new_vars_prefix = "AST", - date_imputation = "first", + highest_imputation = "M", min_dates = vars(TRTSDTM) ) -# A user imputing dates as middle month/day, i.e. date_imputation = "MID" can +# A user imputing dates as middle month/day, i.e. date_imputation = "mid" can # use preserve argument to "preserve" partial dates. For example, "2019---07", # will be displayed as "2019-06-07" rather than 2019-06-15 with preserve = TRUE @@ -194,13 +225,23 @@ derive_vars_dt( mhdt, new_vars_prefix = "AST", dtc = MHSTDTC, - date_imputation = "MID", + highest_imputation = "M", + date_imputation = "mid", preserve = TRUE ) } +\seealso{ +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_var_trtdurd}()}, +\code{\link{derive_vars_dtm_to_dt}()}, +\code{\link{derive_vars_dtm_to_tm}()}, +\code{\link{derive_vars_dtm}()}, +\code{\link{derive_vars_duration}()}, +\code{\link{derive_vars_dy}()} +} \author{ Samia Kabi } -\keyword{adam} -\keyword{derivation} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_vars_dtm.Rd b/man/derive_vars_dtm.Rd index 978ab04ade..11c31a022e 100644 --- a/man/derive_vars_dtm.Rd +++ b/man/derive_vars_dtm.Rd @@ -8,8 +8,9 @@ derive_vars_dtm( dataset, new_vars_prefix, dtc, - date_imputation = NULL, - time_imputation = "00:00:00", + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", flag_imputation = "auto", min_dates = NULL, max_dates = NULL, @@ -32,24 +33,47 @@ imputation flag "TMF". I.e., for \code{new_vars_prefix = "AST"} the variables \item{dtc}{The \code{'--DTC'} date to impute A character date is expected in a format like \code{yyyy-mm-dd} or -\code{yyyy-mm-ddThh:mm:ss}. If the year part is not recorded (missing date), no -imputation is performed.} +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} -\item{date_imputation}{The value to impute the day/month when a datepart is -missing. +\item{highest_imputation}{Highest imputation level + +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified, no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. -If \code{NULL}: no date imputation is performed and partial dates are returned as +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. + +\emph{Default}: \code{"h"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"h"} (hour), \code{"m"} (minute), \code{"s"} (second), \code{"n"} (none, lowest +level)} + +\item{date_imputation}{The value to impute the day/month when a datepart is missing. -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}.} \item{time_imputation}{The value to impute the time when a timepart is missing. @@ -58,16 +82,18 @@ A character value is expected, either as a \itemize{ \item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} for the start of the day, -\item or as a keyword: \code{"FIRST"},\code{"LAST"} to impute to the start/end of a day. +\item or as a keyword: \code{"first"},\code{"last"} to impute to the start/end of a day. } -Default is \code{"00:00:00"}.} +The argument is ignored if \code{highest_imputation = "n"}. + +\emph{Default}: \code{"first"}.} \item{flag_imputation}{Whether the date/time imputation flag(s) must also be derived. If \code{"auto"} is specified, the date imputation flag is derived if the -\code{date_imputation} parameter is not null and the time imputation flag is -derived if the \code{time_imputation} parameter is not null +\code{date_imputation} argument is not null and the time imputation flag is +derived if the \code{time_imputation} argument is not null \emph{Default}: \code{"auto"} @@ -84,36 +110,44 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore \code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would -have changed the month although it is not missing (in the \code{dtc} date).} +have changed the month although it is not missing (in the \code{dtc} date). + +For date variables (not datetime) in the list the time is imputed to +\code{"00:00:00"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{max_dates}{Maximum dates A list of dates is expected. It is ensured that the imputed date is not after any of the specified dates, e.g., that the imputed date is not after the data cut off date. Only dates which are in the range of possible dates are -considered. A date or date-time object is expected.} +considered. A date or date-time object is expected. + +For date variables (not datetime) in the list the time is imputed to +\code{"23:59:59"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{preserve}{Preserve day if month is missing and day is present For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} -(and \code{date_imputation = "MID"}). +(and \code{date_imputation = "mid"}). Permitted Values: \code{TRUE}, \code{FALSE} -Default: \code{FALSE}} +\emph{Default}: \code{FALSE}} \item{ignore_seconds_flag}{ADaM IG states that given SDTM (\code{'--DTC'}) variable, if only hours and minutes are ever collected, and seconds are imputed in @@ -130,18 +164,23 @@ flag \code{'--DTF'}, \code{'--TMF'}) added. } \description{ Derive a datetime object (\code{'--DTM'}) from a date character vector (\code{'--DTC'}). -The date and time can be imputed (see \code{date_imputation}/\code{time_imputation} parameters) +The date and time can be imputed (see \code{date_imputation}/\code{time_imputation} arguments) and the date/time imputation flag (\code{'--DTF'}, \code{'--TMF'}) can be added. } \details{ +In {admiral} we don't allow users to pick any single part of the date/time to +impute, we only enable to impute up to a highest level, i.e. you couldn't +choose to say impute months, but not days. + The presence of a \code{'--DTF'} variable is checked and the variable is not derived if it already exists in the input dataset. However, if \code{'--TMF'} already exists in the input dataset, a warning is issued and \code{'--TMF'} will be overwritten. } \examples{ +library(tibble) library(lubridate) -mhdt <- tibble::tribble( +mhdt <- tribble( ~MHSTDTC, "2019-07-18T15:25:40", "2019-07-18T15:25", @@ -156,13 +195,12 @@ derive_vars_dtm( mhdt, new_vars_prefix = "AST", dtc = MHSTDTC, - date_imputation = "FIRST", - time_imputation = "FIRST" + highest_imputation = "M" ) # Impute AE end date to the last date and ensure that the imputed date is not # after the death or data cut off date -adae <- tibble::tribble( +adae <- tribble( ~AEENDTC, ~DTHDT, ~DCUTDT, "2020-12", ymd("2020-12-06"), ymd("2020-12-24"), "2020-11", ymd("2020-12-06"), ymd("2020-12-24") @@ -172,6 +210,7 @@ derive_vars_dtm( adae, dtc = AEENDTC, new_vars_prefix = "AEN", + highest_imputation = "M", date_imputation = "last", time_imputation = "last", max_dates = vars(DTHDT, DCUTDT) @@ -179,7 +218,7 @@ derive_vars_dtm( # Seconds has been removed from the input dataset. Function now uses # ignore_seconds_flag to remove the 'S' from the --TMF variable. -mhdt <- tibble::tribble( +mhdt <- tribble( ~MHSTDTC, "2019-07-18T15:25", "2019-07-18T15:25", @@ -194,8 +233,7 @@ derive_vars_dtm( mhdt, new_vars_prefix = "AST", dtc = MHSTDTC, - date_imputation = "FIRST", - time_imputation = "FIRST", + highest_imputation = "M", ignore_seconds_flag = TRUE ) @@ -207,13 +245,23 @@ derive_vars_dtm( mhdt, new_vars_prefix = "AST", dtc = MHSTDTC, - date_imputation = "MID", + highest_imputation = "M", + date_imputation = "mid", preserve = TRUE ) } +\seealso{ +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_var_trtdurd}()}, +\code{\link{derive_vars_dtm_to_dt}()}, +\code{\link{derive_vars_dtm_to_tm}()}, +\code{\link{derive_vars_dt}()}, +\code{\link{derive_vars_duration}()}, +\code{\link{derive_vars_dy}()} +} \author{ Samia Kabi } -\keyword{adam} -\keyword{derivation} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_vars_dtm_to_dt.Rd b/man/derive_vars_dtm_to_dt.Rd index 3da14b8f38..d497fd545f 100644 --- a/man/derive_vars_dtm_to_dt.Rd +++ b/man/derive_vars_dtm_to_dt.Rd @@ -41,8 +41,18 @@ adcm \%>\% derive_vars_dtm_to_dt(vars(TRTSDTM, ASTDTM, AENDTM)) \%>\% select(USUBJID, starts_with("TRT"), starts_with("AST"), starts_with("AEN")) } +\seealso{ +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_var_trtdurd}()}, +\code{\link{derive_vars_dtm_to_tm}()}, +\code{\link{derive_vars_dtm}()}, +\code{\link{derive_vars_dt}()}, +\code{\link{derive_vars_duration}()}, +\code{\link{derive_vars_dy}()} +} \author{ Teckla Akinyi } -\keyword{adam} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_vars_dtm_to_tm.Rd b/man/derive_vars_dtm_to_tm.Rd index e130cfbeb3..9e5be733c2 100644 --- a/man/derive_vars_dtm_to_tm.Rd +++ b/man/derive_vars_dtm_to_tm.Rd @@ -51,8 +51,18 @@ adcm \%>\% derive_vars_dtm_to_tm(vars(TRTSDTM, ASTDTM, AENDTM)) \%>\% select(USUBJID, starts_with("TRT"), starts_with("AS"), starts_with("AE")) } +\seealso{ +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_var_trtdurd}()}, +\code{\link{derive_vars_dtm_to_dt}()}, +\code{\link{derive_vars_dtm}()}, +\code{\link{derive_vars_dt}()}, +\code{\link{derive_vars_duration}()}, +\code{\link{derive_vars_dy}()} +} \author{ Teckla Akinyi } -\keyword{adam} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_vars_duration.Rd b/man/derive_vars_duration.Rd index 444fcebf05..d526017ec0 100644 --- a/man/derive_vars_duration.Rd +++ b/man/derive_vars_duration.Rd @@ -102,12 +102,13 @@ input dataset. library(lubridate) library(tibble) +# Derive age in years data <- tribble( - ~BRTHDT, ~RANDDT, - ymd("1984-09-06"), ymd("2020-02-24"), - ymd("1985-01-01"), NA, - NA, ymd("2021-03-10"), - NA, NA + ~USUBJID, ~BRTHDT, ~RANDDT, + "P01", ymd("1984-09-06"), ymd("2020-02-24"), + "P02", ymd("1985-01-01"), NA, + "P03", NA, ymd("2021-03-10"), + "P04", NA, NA ) derive_vars_duration(data, @@ -119,13 +120,57 @@ derive_vars_duration(data, add_one = FALSE, trunc_out = TRUE ) + +# Derive adverse event duration in days +data <- tribble( + ~USUBJID, ~ASTDT, ~AENDT, + "P01", ymd("2021-03-05"), ymd("2021-03-02"), + "P02", ymd("2019-09-18"), ymd("2019-09-18"), + "P03", ymd("1985-01-01"), NA, + "P04", NA, NA +) + +derive_vars_duration(data, + new_var = ADURN, + new_var_unit = ADURU, + start_date = ASTDT, + end_date = AENDT, + out_unit = "days" +) + +# Derive adverse event duration in minutes +data <- tribble( + ~USUBJID, ~ADTM, ~TRTSDTM, + "P01", ymd_hms("2019-08-09T04:30:56"), ymd_hms("2019-08-09T05:00:00"), + "P02", ymd_hms("2019-11-11T10:30:00"), ymd_hms("2019-11-11T11:30:00"), + "P03", ymd_hms("2019-11-11T00:00:00"), ymd_hms("2019-11-11T04:00:00"), + "P04", NA, ymd_hms("2019-11-11T12:34:56"), +) + +derive_vars_duration(data, + new_var = ADURN, + new_var_unit = ADURU, + start_date = ADTM, + end_date = TRTSDTM, + in_unit = "minutes", + out_unit = "minutes", + add_one = FALSE +) } \seealso{ \code{\link[=compute_duration]{compute_duration()}} + +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_var_trtdurd}()}, +\code{\link{derive_vars_dtm_to_dt}()}, +\code{\link{derive_vars_dtm_to_tm}()}, +\code{\link{derive_vars_dtm}()}, +\code{\link{derive_vars_dt}()}, +\code{\link{derive_vars_dy}()} } \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_vars_dy.Rd b/man/derive_vars_dy.Rd index ba77815bbc..3681f8ee43 100644 --- a/man/derive_vars_dy.Rd +++ b/man/derive_vars_dy.Rd @@ -77,9 +77,18 @@ derive_vars_dy( source_vars = vars(TRTSDT, DEATHDY = DTHDT) ) } +\seealso{ +Date/Time Derivation Functions that returns variable appended to dataset: +\code{\link{derive_var_trtdurd}()}, +\code{\link{derive_vars_dtm_to_dt}()}, +\code{\link{derive_vars_dtm_to_tm}()}, +\code{\link{derive_vars_dtm}()}, +\code{\link{derive_vars_dt}()}, +\code{\link{derive_vars_duration}()} +} \author{ Teckla Akinyi } -\keyword{ADaM} -\keyword{derivation} -\keyword{timing} +\concept{der_date_time} +\keyword{der_date_time} +\keyword{der_gen} diff --git a/man/derive_vars_last_dose.Rd b/man/derive_vars_last_dose.Rd index 2f3b857ae7..b6c3740b0d 100644 --- a/man/derive_vars_last_dose.Rd +++ b/man/derive_vars_last_dose.Rd @@ -62,13 +62,10 @@ Input dataset with EX source variables from last dose added. Add EX source variables from last dose to the input dataset. } \details{ -All date (date-time) variables can be characters in standard ISO format or -of date / date-time class. -For ISO format, see \code{\link{impute_dtc}} - parameter \code{dtc} for further details. When doing date comparison to identify last dose, date-time imputations are done as follows: \itemize{ -\item \code{dose_date}: no date imputation, time imputation to \code{00:00:00} if time is missing. -\item \code{analysis_date}: no date imputation, time imputation to \code{23:59:59} if time is missing. +\item \code{dose_date}: time is imputed to \code{00:00:00} if the variable is a date variable +\item \code{analysis_date}: time is imputed to \code{23:59:59} if the variable is a date variable } The last dose records are identified as follows: @@ -104,37 +101,69 @@ library(admiral.test) data(admiral_ae) data(ex_single) -admiral_ae \%>\% +# create datetime variables in input datasets +ex_single <- derive_vars_dtm( + head(ex_single, 100), + dtc = EXENDTC, + new_vars_prefix = "EXEN", + flag_imputation = "none" +) + +adae <- admiral_ae \%>\% head(100) \%>\% + derive_vars_dtm( + dtc = AESTDTC, + new_vars_prefix = "AST", + highest_imputation = "M" + ) + +# add last dose vars +adae \%>\% derive_vars_last_dose( - head(ex_single, 100), + dataset_ex = ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, + !is.na(EXENDTM), new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDTC, VISIT), - dose_date = EXENDTC, - analysis_date = AESTDTC, - single_dose_condition = (EXSTDTC == EXENDTC) + dose_date = EXENDTM, + analysis_date = ASTDTM ) \%>\% select(STUDYID, USUBJID, AESEQ, AESTDTC, EXDOSE, EXTRT, EXENDTC, EXSEQ, VISIT) # or with traceability variables -admiral_ae \%>\% - head(100) \%>\% +adae \%>\% derive_vars_last_dose( - head(ex_single, 100), + dataset_ex = ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, + !is.na(EXENDTM), new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDTC, VISIT), - dose_date = EXENDTC, - analysis_date = AESTDTC, - single_dose_condition = (EXSTDTC == EXENDTC), + dose_date = EXENDTM, + analysis_date = ASTDTM, traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") ) \%>\% select(STUDYID, USUBJID, AESEQ, AESTDTC, EXDOSE, EXTRT, EXENDTC, LDOSEDOM, LDOSESEQ, LDOSEVAR) } +\seealso{ +\code{\link[=derive_var_last_dose_amt]{derive_var_last_dose_amt()}}, \code{\link[=derive_var_last_dose_date]{derive_var_last_dose_date()}}, +\code{\link[=derive_var_last_dose_grp]{derive_var_last_dose_grp()}}, \code{\link[=create_single_dose_dataset]{create_single_dose_dataset()}} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} \author{ Ondrej Slama, Annie Yang } -\keyword{adam} -\keyword{derivation} -\keyword{user_utility} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_vars_merged.Rd b/man/derive_vars_merged.Rd index 42e207902d..e124ba63c2 100644 --- a/man/derive_vars_merged.Rd +++ b/man/derive_vars_merged.Rd @@ -149,7 +149,7 @@ library(dplyr, warn.conflicts = FALSE) data("admiral_vs") data("admiral_dm") -# merging all dm variables to vs +# Merging all dm variables to vs derive_vars_merged( admiral_vs, dataset_add = select(admiral_dm, -DOMAIN), @@ -157,7 +157,7 @@ derive_vars_merged( ) \%>\% select(STUDYID, USUBJID, VSTESTCD, VISIT, VSTPT, VSSTRESN, AGE, AGEU) -# merge last weight to adsl +# Merge last weight to adsl data("admiral_adsl") derive_vars_merged( admiral_adsl, @@ -170,9 +170,89 @@ derive_vars_merged( match_flag = vsdatafl ) \%>\% select(STUDYID, USUBJID, AGE, AGEU, LASTWGT, LASTWGTU, vsdatafl) + +# Derive treatment start datetime (TRTSDTM) +data(admiral_ex) + +## Impute exposure start date to first date/time +ex_ext <- derive_vars_dtm( + admiral_ex, + dtc = EXSTDTC, + new_vars_prefix = "EXST", + highest_imputation = "M", +) + +## Add first exposure datetime and imputation flags to adsl +derive_vars_merged( + select(admiral_dm, STUDYID, USUBJID), + dataset_add = ex_ext, + by_vars = vars(STUDYID, USUBJID), + new_vars = vars(TRTSDTM = EXSTDTM, TRTSDTF = EXSTDTF, TRTSTMF = EXSTTMF), + order = vars(EXSTDTM), + mode = "first" +) + +# Derive treatment start datetime (TRTSDTM) +data(admiral_ex) + +## Impute exposure start date to first date/time +ex_ext <- derive_vars_dtm( + admiral_ex, + dtc = EXSTDTC, + new_vars_prefix = "EXST", + highest_imputation = "M", +) + +## Add first exposure datetime and imputation flags to adsl +derive_vars_merged( + select(admiral_dm, STUDYID, USUBJID), + dataset_add = ex_ext, + filter_add = !is.na(EXSTDTM), + by_vars = vars(STUDYID, USUBJID), + new_vars = vars(TRTSDTM = EXSTDTM, TRTSDTF = EXSTDTF, TRTSTMF = EXSTTMF), + order = vars(EXSTDTM), + mode = "first" +) + +# Derive treatment end datetime (TRTEDTM) +## Impute exposure end datetime to last time, no date imputation +ex_ext <- derive_vars_dtm( + admiral_ex, + dtc = EXENDTC, + new_vars_prefix = "EXEN", + time_imputation = "last", +) + +## Add last exposure datetime and imputation flag to adsl +derive_vars_merged( + select(admiral_dm, STUDYID, USUBJID), + dataset_add = ex_ext, + filter_add = !is.na(EXENDTM), + by_vars = vars(STUDYID, USUBJID), + new_vars = vars(TRTEDTM = EXENDTM, TRTETMF = EXENTMF), + order = vars(EXENDTM), + mode = "last" +) +} +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} } \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_vars_merged_dt.Rd b/man/derive_vars_merged_dt.Rd index 12cb43188e..b487f97561 100644 --- a/man/derive_vars_merged_dt.Rd +++ b/man/derive_vars_merged_dt.Rd @@ -87,29 +87,30 @@ If the \code{order} parameter is not specified, the \code{mode} parameter is ign \item{dtc}{The \code{'--DTC'} date to impute A character date is expected in a format like \code{yyyy-mm-dd} or -\code{yyyy-mm-ddThh:mm:ss}. If the year part is not recorded (missing date), no -imputation is performed.} +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} \item{date_imputation}{The value to impute the day/month when a datepart is missing. -If \code{NULL}: no date imputation is performed and partial dates are returned as -missing. - -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}} \item{flag_imputation}{Whether the date imputation flag must also be derived. If \code{"auto"} is specified, the date imputation flag is derived if the -\code{date_imputation} parameter is not null. +\code{date_imputation} argument is not null. \emph{Default}: \code{"auto"} @@ -126,13 +127,13 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} @@ -184,6 +185,11 @@ optionally the variable \verb{DTF} derived from the additional dataset (\code{dataset_add}). } \description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} + +This function is \emph{deprecated}, please use \code{derive_vars_dt()} and +\code{derive_vars_merged()} instead. + Merge a imputed date variable and date imputation flag from a dataset to the input dataset. The observations to merge can be selected by a condition and/or selecting the first or last observation for each by group. @@ -199,38 +205,7 @@ the additional dataset. \item The date and flag variables are merged to the input dataset. } } -\examples{ -library(admiral.test) -library(dplyr, warn.conflicts = FALSE) -data("admiral_dm") -data("admiral_ex") - -# derive treatment start date (TRTSDT) -derive_vars_merged_dt( - select(admiral_dm, STUDYID, USUBJID), - dataset_add = admiral_ex, - by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "TRTS", - dtc = EXSTDTC, - date_imputation = "first", - order = vars(TRTSDT), - mode = "first" -) - -# derive treatment end date (TRTEDT) (without imputation) -derive_vars_merged_dt( - select(admiral_dm, STUDYID, USUBJID), - dataset_add = admiral_ex, - by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "TRTE", - dtc = EXENDTC, - order = vars(TRTEDT), - mode = "last" -) -} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} -\keyword{timing} +\keyword{deprecated} diff --git a/man/derive_vars_merged_dtm.Rd b/man/derive_vars_merged_dtm.Rd index cfe84773a5..fd72dd2e1d 100644 --- a/man/derive_vars_merged_dtm.Rd +++ b/man/derive_vars_merged_dtm.Rd @@ -84,24 +84,25 @@ If the \code{order} parameter is not specified, the \code{mode} parameter is ign \item{dtc}{The \code{'--DTC'} date to impute A character date is expected in a format like \code{yyyy-mm-dd} or -\code{yyyy-mm-ddThh:mm:ss}. If the year part is not recorded (missing date), no -imputation is performed.} +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} \item{date_imputation}{The value to impute the day/month when a datepart is missing. -If \code{NULL}: no date imputation is performed and partial dates are returned as -missing. - -Otherwise, a character value is expected, either as a +A character value is expected, either as a \itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last day/month. } -Default is \code{NULL}.} +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}.} \item{time_imputation}{The value to impute the time when a timepart is missing. @@ -110,16 +111,18 @@ A character value is expected, either as a \itemize{ \item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} for the start of the day, -\item or as a keyword: \code{"FIRST"},\code{"LAST"} to impute to the start/end of a day. +\item or as a keyword: \code{"first"},\code{"last"} to impute to the start/end of a day. } -Default is \code{"00:00:00"}.} +The argument is ignored if \code{highest_imputation = "n"}. + +\emph{Default}: \code{"first"}.} \item{flag_imputation}{Whether the date/time imputation flag(s) must also be derived. If \code{"auto"} is specified, the date imputation flag is derived if the -\code{date_imputation} parameter is not null and the time imputation flag is -derived if the \code{time_imputation} parameter is not null +\code{date_imputation} argument is not null and the time imputation flag is +derived if the \code{time_imputation} argument is not null \emph{Default}: \code{"auto"} @@ -136,36 +139,44 @@ ensures that the non-missing parts of the \code{dtc} date are not changed. A date or date-time object is expected. For example -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" ) }\if{html}{\out{
}} returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore \code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would -have changed the month although it is not missing (in the \code{dtc} date).} +have changed the month although it is not missing (in the \code{dtc} date). + +For date variables (not datetime) in the list the time is imputed to +\code{"00:00:00"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{max_dates}{Maximum dates A list of dates is expected. It is ensured that the imputed date is not after any of the specified dates, e.g., that the imputed date is not after the data cut off date. Only dates which are in the range of possible dates are -considered. A date or date-time object is expected.} +considered. A date or date-time object is expected. + +For date variables (not datetime) in the list the time is imputed to +\code{"23:59:59"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} \item{preserve}{Preserve day if month is missing and day is present For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} -(and \code{date_imputation = "MID"}). +(and \code{date_imputation = "mid"}). Permitted Values: \code{TRUE}, \code{FALSE} -Default: \code{FALSE}} +\emph{Default}: \code{FALSE}} \item{check_type}{Check uniqueness? @@ -194,6 +205,11 @@ optionally the variables \verb{DTF} and \verb{ derived from the additional dataset (\code{dataset_add}). } \description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} + +This function is \emph{deprecated}, please use \code{derive_vars_dtm()} and +\code{derive_vars_merged()} instead. + Merge a imputed datetime variable, date imputation flag, and time imputation flag from a dataset to the input dataset. The observations to merge can be selected by a condition and/or selecting the first or last observation for @@ -210,40 +226,7 @@ time imputation flag is added to the additional dataset. \item The date and flag variables are merged to the input dataset. } } -\examples{ -library(admiral.test) -library(dplyr, warn.conflicts = FALSE) -data("admiral_dm") -data("admiral_ex") - -# derive treatment start datetime (TRTSDTM) -derive_vars_merged_dtm( - select(admiral_dm, STUDYID, USUBJID), - dataset_add = admiral_ex, - by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "TRTS", - dtc = EXSTDTC, - date_imputation = "first", - time_imputation = "first", - order = vars(TRTSDTM), - mode = "first" -) - -# derive treatment end datetime (TRTEDTM) (without date imputation) -derive_vars_merged_dtm( - select(admiral_dm, STUDYID, USUBJID), - dataset_add = admiral_ex, - by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "TRTE", - dtc = EXENDTC, - time_imputation = "last", - order = vars(TRTEDTM), - mode = "last" -) -} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{derivation} -\keyword{timing} +\keyword{deprecated} diff --git a/man/derive_vars_merged_lookup.Rd b/man/derive_vars_merged_lookup.Rd new file mode 100644 index 0000000000..d5084bb7a6 --- /dev/null +++ b/man/derive_vars_merged_lookup.Rd @@ -0,0 +1,170 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_merged.R +\name{derive_vars_merged_lookup} +\alias{derive_vars_merged_lookup} +\title{Merge Lookup Table with Source Dataset} +\usage{ +derive_vars_merged_lookup( + dataset, + dataset_add, + by_vars, + order = NULL, + new_vars = NULL, + mode = NULL, + filter_add = NULL, + check_type = "warning", + duplicate_msg = NULL, + print_not_mapped = TRUE +) +} +\arguments{ +\item{dataset}{Input dataset + +The variables specified by the \code{by_vars} parameter are expected.} + +\item{dataset_add}{Lookup table + +The variables specified by the \code{by_vars} parameter are expected.} + +\item{by_vars}{Grouping variables + +The input dataset and the selected observations from the additional dataset +are merged by the specified by variables. The by variables must be a unique +key of the selected observations. + +\emph{Permitted Values}: list of variables created by \code{vars()}} + +\item{order}{Sort order + +If the parameter is set to a non-null value, for each by group the first or +last observation from the additional dataset is selected with respect to the +specified order. + +\emph{Default}: \code{NULL} + +\emph{Permitted Values}: list of variables or \verb{desc()} function calls +created by \code{vars()}, e.g., \code{vars(ADT, desc(AVAL))} or \code{NULL}} + +\item{new_vars}{Variables to add + +The specified variables from the additional dataset are added to the output +dataset. Variables can be renamed by naming the element, i.e., \verb{new_vars = vars( = )}. + +For example \code{new_vars = vars(var1, var2)} adds variables \code{var1} and \code{var2} +from \code{dataset_add} to the input dataset. + +And \code{new_vars = vars(var1, new_var2 = old_var2)} takes \code{var1} and +\code{old_var2} from \code{dataset_add} and adds them to the input dataset renaming +\code{old_var2} to \code{new_var2}. + +If the parameter is not specified or set to \code{NULL}, all variables from the +additional dataset (\code{dataset_add}) are added. + +\emph{Default}: \code{NULL} + +\emph{Permitted Values}: list of variables created by \code{vars()}} + +\item{mode}{Selection mode + +Determines if the first or last observation is selected. If the \code{order} +parameter is specified, \code{mode} must be non-null. + +If the \code{order} parameter is not specified, the \code{mode} parameter is ignored. + +\emph{Default}: \code{NULL} + +\emph{Permitted Values}: \code{"first"}, \code{"last"}, \code{NULL}} + +\item{filter_add}{Filter for additional dataset (\code{dataset_add}) + +Only observations fulfilling the specified condition are taken into account +for merging. If the parameter is not specified, all observations are +considered. + +\emph{Default}: \code{NULL} + +\emph{Permitted Values}: a condition} + +\item{check_type}{Check uniqueness? + +If \code{"warning"} or \code{"error"} is specified, the specified message is issued +if the observations of the (restricted) additional dataset are not unique +with respect to the by variables and the order. + +\emph{Default}: \code{"warning"} + +\emph{Permitted Values}: \code{"none"}, \code{"warning"}, \code{"error"}} + +\item{duplicate_msg}{Message of unique check + +If the uniqueness check fails, the specified message is displayed. + +\emph{Default}: + +\if{html}{\out{
}}\preformatted{paste("Dataset `dataset_add` contains duplicate records with respect to", + enumerate(vars2chr(by_vars))) +}\if{html}{\out{
}}} + +\item{print_not_mapped}{Print a list of unique \code{by_vars} values that do not +have corresponding records from the lookup table? + +\emph{Default}: \code{TRUE} + +\emph{Permitted Values}: \code{TRUE}, \code{FALSE}} +} +\value{ +The output dataset contains all observations and variables of the +input dataset, and add the variables specified in \code{new_vars} from the lookup +table specified in \code{dataset_add}. Optionally prints a list of unique +\code{by_vars} values that do not have corresponding records +from the lookup table (by specifying \code{print_not_mapped = TRUE}). +} +\description{ +Merge user-defined lookup table with the input dataset. Optionally print a +list of records from the input dataset that do not have corresponding +mapping from the lookup table. +} +\examples{ +library(admiral.test) +library(dplyr, warn.conflicts = FALSE) +data("admiral_vs") +param_lookup <- tibble::tribble( + ~VSTESTCD, ~VSTEST, ~PARAMCD, ~PARAM, + "SYSBP", "Systolic Blood Pressure", "SYSBP", "Systolic Blood Pressure (mmHg)", + "WEIGHT", "Weight", "WEIGHT", "Weight (kg)", + "HEIGHT", "Height", "HEIGHT", "Height (cm)", + "TEMP", "Temperature", "TEMP", "Temperature (C)", + "MAP", "Mean Arterial Pressure", "MAP", "Mean Arterial Pressure (mmHg)", + "BMI", "Body Mass Index", "BMI", "Body Mass Index(kg/m^2)", + "BSA", "Body Surface Area", "BSA", "Body Surface Area(m^2)" +) +derive_vars_merged_lookup( + dataset = admiral_vs, + dataset_add = param_lookup, + by_vars = vars(VSTESTCD), + new_vars = vars(PARAMCD), + print_not_mapped = TRUE +) +} +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()}, +\code{\link{get_summary_records}()} +} +\author{ +Annie Yang +} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/derive_vars_query.Rd b/man/derive_vars_query.Rd index d555f9f1de..5e0b214083 100644 --- a/man/derive_vars_query.Rd +++ b/man/derive_vars_query.Rd @@ -24,6 +24,16 @@ The input dataset with query variables derived. Derive Query Variables } \details{ +This function can be used to derive CDISC variables such as +\code{SMQzzNAM}, \code{SMQzzCD}, \code{SMQzzSC}, \code{SMQzzSCN}, and \code{CQzzNAM} in ADAE and +ADMH, and variables such as \code{SDGzzNAM}, \code{SDGzzCD}, and \code{SDGzzSC} in ADCM. +An example usage of this function can be found in the +\href{../articles/occds.html}{OCCDS vignette}. + +A query dataset is expected as an input to this function. See the +\href{../articles/queries_dataset.html}{Queries Dataset Documentation vignette} +for descriptions, or call \code{data("queries")} for an example of a query dataset. + For each unique element in \code{VAR_PREFIX}, the corresponding "NAM" variable will be created. For each unique \code{VAR_PREFIX}, if \code{QUERY_ID} is not "" or NA, then the corresponding "CD" variable is created; similarly, @@ -57,10 +67,15 @@ derive_vars_query(adae, queries) } \seealso{ \code{\link[=create_query_data]{create_query_data()}} \code{\link[=assert_valid_queries]{assert_valid_queries()}} + +OCCDS Functions: +\code{\link{create_query_data}()}, +\code{\link{create_single_dose_dataset}()}, +\code{\link{derive_vars_atc}()}, +\code{\link{get_terms_from_db}()} } \author{ Ondrej Slama, Shimeng Huang } -\keyword{adae} -\keyword{adcm} -\keyword{derivation} +\concept{der_occds} +\keyword{der_occds} diff --git a/man/derive_vars_suppqual.Rd b/man/derive_vars_suppqual.Rd index a5b804d830..f3a41bc8fd 100644 --- a/man/derive_vars_suppqual.Rd +++ b/man/derive_vars_suppqual.Rd @@ -35,3 +35,4 @@ dataset, specify two character\code{domain} value. \author{ Vignesh Thanikachalam } +\keyword{deprecated} diff --git a/man/derive_vars_transposed.Rd b/man/derive_vars_transposed.Rd index d9e438bbc3..a8eba59ada 100644 --- a/man/derive_vars_transposed.Rd +++ b/man/derive_vars_transposed.Rd @@ -86,8 +86,25 @@ cm \%>\% ) \%>\% select(USUBJID, CMDECOD, starts_with("CMATC")) } +\seealso{ +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{get_summary_records}()} +} \author{ Thomas Neitmann } -\keyword{adam} -\keyword{derivation} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/dev_util_arg_name.Rd b/man/dev_util_arg_name.Rd deleted file mode 100644 index 6f0b792a4b..0000000000 --- a/man/dev_util_arg_name.Rd +++ /dev/null @@ -1,28 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{arg_name} -\alias{arg_name} -\title{Extract Argument Name from an Expression} -\usage{ -arg_name(expr) -} -\arguments{ -\item{expr}{An expression created inside a function using \code{substitute()}} -} -\description{ -Extract Argument Name from an Expression -} -\examples{ -test_fun <- function(something) { - admiral:::arg_name(substitute(something)) -} - -inner_function <- function(x) x -test_fun2 <- function(something) { - admiral:::arg_name(substitute(inner_function(something))) -} -} -\author{ -Thomas Neitmann, Ondrej Slama -} -\keyword{dev_utility} diff --git a/man/dev_util_backquote.Rd b/man/dev_util_backquote.Rd deleted file mode 100644 index d02112f11d..0000000000 --- a/man/dev_util_backquote.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{backquote} -\alias{backquote} -\title{Wrap a String in Backquotes} -\usage{ -backquote(x) -} -\arguments{ -\item{x}{A \code{character} vector} -} -\description{ -Wrap a String in Backquotes -} -\examples{ -admiral:::backquote("USUBJID") -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_convert_dtm_to_dtc.Rd b/man/dev_util_convert_dtm_to_dtc.Rd deleted file mode 100644 index ebe170b4d5..0000000000 --- a/man/dev_util_convert_dtm_to_dtc.Rd +++ /dev/null @@ -1,27 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{convert_dtm_to_dtc} -\alias{convert_dtm_to_dtc} -\title{Helper Function to Convert Date (or Date-time) Objects to Characters of dtc Format -(-DTC type of variable)} -\usage{ -convert_dtm_to_dtc(dtm) -} -\arguments{ -\item{dtm}{date or date-time} -} -\value{ -character -} -\description{ -Helper Function to Convert Date (or Date-time) Objects to Characters of dtc Format -(-DTC type of variable) -} -\examples{ -admiral:::convert_dtm_to_dtc(as.POSIXct(Sys.time())) -admiral:::convert_dtm_to_dtc(as.Date(Sys.time())) -} -\author{ -Ondrej Slama -} -\keyword{dev_utility} diff --git a/man/dev_util_enumerate.Rd b/man/dev_util_enumerate.Rd deleted file mode 100644 index e87f5e245b..0000000000 --- a/man/dev_util_enumerate.Rd +++ /dev/null @@ -1,31 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{enumerate} -\alias{enumerate} -\title{Enumerate Multiple Strings} -\usage{ -enumerate(x, quote_fun = backquote, conjunction = "and") -} -\arguments{ -\item{x}{A \code{character} vector} - -\item{quote_fun}{Quoting function, defaults to \code{backquote}.} - -\item{conjunction}{Character to be used in the message, defaults to "and".} -} -\description{ -Enumerate Multiple Strings -} -\examples{ -admiral:::enumerate(c("STUDYID", "USUBJID", "PARAMCD")) -admiral:::enumerate(letters[1:6], quote_fun = admiral:::squote) -admiral:::enumerate( - c("date", "time", "both"), - quote_fun = admiral:::squote, - conjunction = "or" -) -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_extract_vars.Rd b/man/dev_util_extract_vars.Rd deleted file mode 100644 index 1f9ace6d6c..0000000000 --- a/man/dev_util_extract_vars.Rd +++ /dev/null @@ -1,26 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{extract_vars} -\alias{extract_vars} -\title{Extract All Symbols from a List of Quosures} -\usage{ -extract_vars(x, side = "lhs") -} -\arguments{ -\item{x}{An \code{R} object} - -\item{side}{One of \code{"lhs"} (the default) or \code{"rhs"}} -} -\value{ -A list of \code{quosures} -} -\description{ -Extract All Symbols from a List of Quosures -} -\examples{ -admiral:::extract_vars(vars(STUDYID, USUBJID, desc(ADTM))) -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_get_constant_vars.Rd b/man/dev_util_get_constant_vars.Rd deleted file mode 100644 index 0ef21632a8..0000000000 --- a/man/dev_util_get_constant_vars.Rd +++ /dev/null @@ -1,41 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{get_constant_vars} -\alias{get_constant_vars} -\title{Get Constant Variables} -\usage{ -get_constant_vars(dataset, by_vars, ignore_vars = NULL) -} -\arguments{ -\item{dataset}{A data frame.} - -\item{by_vars}{By variables -The groups defined by the by variables are considered separately. I.e., if -a variable is constant within each by group, it is returned.} - -\item{ignore_vars}{Variables to ignore -The specified variables are not considered, i.e., they are not returned -even if they are constant (unless they are included in the by variables). - -\emph{Permitted Values:} A list of variable names or selector function calls -like \code{starts_with("EX")}} -} -\value{ -Variable vector. -} -\description{ -Get Constant Variables -} -\examples{ -library(admiral.test) -data(admiral_vs) - -admiral:::get_constant_vars(admiral_vs, by_vars = vars(USUBJID, VSTESTCD)) - -admiral:::get_constant_vars( - admiral_vs, - by_vars = vars(USUBJID, VSTESTCD), - ignore_vars = vars(DOMAIN, tidyselect::starts_with("VS")) -) -} -\keyword{dev_utility} diff --git a/man/dev_util_get_source_vars.Rd b/man/dev_util_get_source_vars.Rd deleted file mode 100644 index d3c30829c0..0000000000 --- a/man/dev_util_get_source_vars.Rd +++ /dev/null @@ -1,24 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{get_source_vars} -\alias{get_source_vars} -\title{Get Source Variables from a List of Quosures} -\usage{ -get_source_vars(quosures) -} -\arguments{ -\item{quosures}{A list of quosures} -} -\value{ -A list of quosures -} -\description{ -Get Source Variables from a List of Quosures -} -\examples{ -admiral:::get_source_vars(vars(USUBJID, AVISIT = VISIT, SRCDOM = "EX")) -} -\author{ -Stefan Bundfuss -} -\keyword{dev_utility} diff --git a/man/dev_util_notin.Rd b/man/dev_util_notin.Rd deleted file mode 100644 index d62440b5ec..0000000000 --- a/man/dev_util_notin.Rd +++ /dev/null @@ -1,25 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{\%notin\%} -\alias{\%notin\%} -\title{Negated Value Matching} -\usage{ -x \%notin\% table -} -\arguments{ -\item{x}{The values to be matched} - -\item{table}{The values to be matched against} -} -\description{ -Returns a \code{logical} vector indicating if there is \emph{no} match of the -left operand in the right operand. -} -\examples{ -`\%notin\%` <- admiral:::`\%notin\%` -"a" \%notin\% c("b", "v", "k") -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_quo_c.Rd b/man/dev_util_quo_c.Rd deleted file mode 100644 index 2d74e9e475..0000000000 --- a/man/dev_util_quo_c.Rd +++ /dev/null @@ -1,27 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{quo_c} -\alias{quo_c} -\title{Concatenate One or More Quosure(s)} -\usage{ -quo_c(...) -} -\arguments{ -\item{...}{One or more objects of class \code{quosure} or \code{quosures}} -} -\value{ -An object of class \code{quosures} -} -\description{ -Concatenate One or More Quosure(s) -} -\examples{ -admiral:::quo_c(rlang::quo(USUBJID)) -admiral:::quo_c(rlang::quo(STUDYID), rlang::quo(USUBJID)) -admiral:::quo_c(vars(USUBJID, ADTM)) -admiral:::quo_c(rlang::quo(BASETYPE), vars(USUBJID, PARAM), rlang::quo(ADTM)) -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_replace_values_by_names.Rd b/man/dev_util_replace_values_by_names.Rd deleted file mode 100644 index b91cafcbf9..0000000000 --- a/man/dev_util_replace_values_by_names.Rd +++ /dev/null @@ -1,24 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{replace_values_by_names} -\alias{replace_values_by_names} -\title{Replace Quosure Value with Name} -\usage{ -replace_values_by_names(quosures) -} -\arguments{ -\item{quosures}{A list of quosures} -} -\value{ -A list of quosures -} -\description{ -Replace Quosure Value with Name -} -\examples{ -admiral:::replace_values_by_names(vars(USUBJID, TEST = VSTESTCD)) -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_squote.Rd b/man/dev_util_squote.Rd deleted file mode 100644 index cb36d84a74..0000000000 --- a/man/dev_util_squote.Rd +++ /dev/null @@ -1,21 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{squote} -\alias{squote} -\title{Wrap a String in Single Quotes} -\usage{ -squote(x) -} -\arguments{ -\item{x}{A \code{character} vector} -} -\description{ -Wrap a String in Single Quotes -} -\examples{ -admiral:::squote("foo") -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dev_util_what_is_it.Rd b/man/dev_util_what_is_it.Rd deleted file mode 100644 index ef93b331e5..0000000000 --- a/man/dev_util_what_is_it.Rd +++ /dev/null @@ -1,25 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{what_is_it} -\alias{what_is_it} -\title{What Kind of Object is This?} -\usage{ -what_is_it(x) -} -\arguments{ -\item{x}{Any R object} -} -\description{ -Returns a string describing what kind of object the input is. -} -\examples{ -admiral:::what_is_it(mtcars) -admiral:::what_is_it(NA) -admiral:::what_is_it(TRUE) -admiral:::what_is_it(lm(hp ~ mpg, data = mtcars)) -admiral:::what_is_it(letters) -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} diff --git a/man/dose_freq_lookup.Rd b/man/dose_freq_lookup.Rd index 4770a2f8a0..531ac0a01f 100644 --- a/man/dose_freq_lookup.Rd +++ b/man/dose_freq_lookup.Rd @@ -43,4 +43,5 @@ To see the entire table in the console, run \code{print(dose_freq_lookup)}. \seealso{ \code{\link[=create_single_dose_dataset]{create_single_dose_dataset()}} } +\concept{metadata} \keyword{metadata} diff --git a/man/dquote.Rd b/man/dquote.Rd deleted file mode 100644 index 4d3b40e53a..0000000000 --- a/man/dquote.Rd +++ /dev/null @@ -1,27 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{dquote} -\alias{dquote} -\title{Wrap a String in Double Quotes} -\usage{ -dquote(x) -} -\arguments{ -\item{x}{A character vector} -} -\value{ -If the input is \code{NULL}, the text \code{"NULL"} is returned. Otherwise, the -input in double quotes is returned. -} -\description{ -Wrap a string in double quotes, e.g., for displaying character values in -messages. -} -\examples{ -admiral:::dquote("foo") -admiral:::dquote(NULL) -} -\author{ -Stefan Bundfuss -} -\keyword{dev_utility} diff --git a/man/dt_level.Rd b/man/dt_level.Rd new file mode 100644 index 0000000000..0e7f9e89c6 --- /dev/null +++ b/man/dt_level.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{dt_level} +\alias{dt_level} +\title{Create a \code{dt_level} object} +\usage{ +dt_level(level) +} +\arguments{ +\item{level}{Date level + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"n"} (none, lowest level)} +} +\value{ +A \code{dt_level} object +} +\description{ +Create a \code{dt_level} object +} +\details{ +A \code{dt_level} object is an ordered factor, i.e., two objects can be +compared. +} +\seealso{ +Utilities used for date imputation: +\code{\link{dtm_level}()}, +\code{\link{get_imputation_target_date}()}, +\code{\link{get_imputation_target_time}()}, +\code{\link{get_partialdatetime}()}, +\code{\link{restrict_imputed_dtc_dtm}()}, +\code{\link{restrict_imputed_dtc_dt}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/dthcaus_source.Rd b/man/dthcaus_source.Rd deleted file mode 100644 index b2fac29eae..0000000000 --- a/man/dthcaus_source.Rd +++ /dev/null @@ -1,53 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_var_dthcaus.R -\name{dthcaus_source} -\alias{dthcaus_source} -\title{Create a \code{dthcaus_source} Object} -\usage{ -dthcaus_source( - dataset_name, - filter, - date, - mode = "first", - dthcaus, - traceability_vars = NULL -) -} -\arguments{ -\item{dataset_name}{The name of the dataset, i.e. a string, used to search for -the death cause.} - -\item{filter}{An expression used for filtering \code{dataset}.} - -\item{date}{A character vector to be used for sorting \code{dataset}.} - -\item{mode}{One of \code{"first"} or \code{"last"}. -Either the \code{"first"} or \code{"last"} observation is preserved from the \code{dataset} -which is ordered by \code{date}.} - -\item{dthcaus}{A variable name or a string literal --- if a variable name, e.g., \code{AEDECOD}, -it is the variable in the source dataset to be used to assign values to -\code{DTHCAUS}; if a string literal, e.g. \code{"Adverse Event"}, it is the fixed value -to be assigned to \code{DTHCAUS}.} - -\item{traceability_vars}{A named list returned by \code{\link[=vars]{vars()}} listing the traceability variables, -e.g. \code{vars(DTHDOM = "DS", DTHSEQ = DSSEQ)}. -The left-hand side (names of the list elements) gives the names of the traceability variables -in the returned dataset. -The right-hand side (values of the list elements) gives the values of the traceability variables -in the returned dataset. -These can be either strings or symbols referring to existing variables.} -} -\value{ -An object of class "dthcaus_source". -} -\description{ -Create a \code{dthcaus_source} Object -} -\seealso{ -\code{\link[=derive_var_dthcaus]{derive_var_dthcaus()}} -} -\author{ -Shimeng Huang -} -\keyword{source_specifications} diff --git a/man/dtm_level.Rd b/man/dtm_level.Rd new file mode 100644 index 0000000000..ede3074237 --- /dev/null +++ b/man/dtm_level.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{dtm_level} +\alias{dtm_level} +\title{Create a \code{dtm_level} object} +\usage{ +dtm_level(level) +} +\arguments{ +\item{level}{Datetime level + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"h"} (hour), \code{"m"} (minute), \code{"s"} (second, lowest level), \code{"n"} +(none)} +} +\value{ +A \code{dtm_level} object +} +\description{ +Create a \code{dtm_level} object +} +\details{ +A \code{dtm_level} object is an ordered factor, i.e., two objects can be +compared. +} +\seealso{ +Utilities used for date imputation: +\code{\link{dt_level}()}, +\code{\link{get_imputation_target_date}()}, +\code{\link{get_imputation_target_time}()}, +\code{\link{get_partialdatetime}()}, +\code{\link{restrict_imputed_dtc_dtm}()}, +\code{\link{restrict_imputed_dtc_dt}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/event_source.Rd b/man/event_source.Rd index 3052eba446..563b4e9a17 100644 --- a/man/event_source.Rd +++ b/man/event_source.Rd @@ -16,8 +16,7 @@ of \code{derive_param_tte()}.} \code{dataset} which are events or possible censoring time points.} \item{date}{A variable providing the date of the event or censoring. A date, -a datetime, or a character variable containing ISO 8601 dates can be -specified. An unquoted symbol is expected. +or a datetime can be specified. An unquoted symbol is expected. Refer to \code{derive_vars_dt()} to impute and derive a date from a date character vector to a date object.} @@ -48,8 +47,31 @@ event_source( } \seealso{ \code{\link[=derive_param_tte]{derive_param_tte()}}, \code{\link[=censor_source]{censor_source()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/ex_single.Rd b/man/ex_single.Rd index e1be866981..b90d4aad02 100644 --- a/man/ex_single.Rd +++ b/man/ex_single.Rd @@ -16,4 +16,12 @@ ex_single \description{ A derived dataset with single dose per date. } +\seealso{ +Other datasets: +\code{\link{admiral_adsl}}, +\code{\link{atoxgr_criteria_ctcv4}}, +\code{\link{queries_mh}}, +\code{\link{queries}} +} +\concept{datasets} \keyword{datasets} diff --git a/man/expect_dfs_equal.Rd b/man/expect_dfs_equal.Rd deleted file mode 100644 index a7c643c736..0000000000 --- a/man/expect_dfs_equal.Rd +++ /dev/null @@ -1,37 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/test_helpers.R -\name{expect_dfs_equal} -\alias{expect_dfs_equal} -\title{Expectation: Are Two Datasets Equal?} -\usage{ -expect_dfs_equal(base, compare, keys, ...) -} -\arguments{ -\item{base}{Input dataset} - -\item{compare}{Comparison dataset} - -\item{keys}{\code{character} vector of variables that define a unique row in the -\code{base} and \code{compare} datasets} - -\item{...}{Additional arguments passed onto \code{\link[diffdf:diffdf]{diffdf::diffdf()}}} -} -\value{ -An error if \code{base} and \code{compare} do not match or \code{NULL} invisibly if they do -} -\description{ -Uses \code{\link[diffdf:diffdf]{diffdf::diffdf()}} to compares 2 datasets for any differences -} -\examples{ -\dontrun{ -testthat::test_that("a missing row is detected", { - data(dm) - expect_dfs_equal(dm, dm[-1L, ], keys = "USUBJID") -}) -} -} -\author{ -Thomas Neitmann -} -\keyword{dev_utility} -\keyword{test_helper} diff --git a/man/extend_source_datasets.Rd b/man/extend_source_datasets.Rd index a9d8000fbe..1a989d78d2 100644 --- a/man/extend_source_datasets.Rd +++ b/man/extend_source_datasets.Rd @@ -54,7 +54,31 @@ extend_source_datasets( by_vars = vars(AEDECOD) ) } +\seealso{ +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} +} \author{ Stefan Bundfuss } -\keyword{dev_utility} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/extract_duplicate_records.Rd b/man/extract_duplicate_records.Rd index 6b8464143e..e0af918469 100644 --- a/man/extract_duplicate_records.Rd +++ b/man/extract_duplicate_records.Rd @@ -26,7 +26,14 @@ adsl <- rbind(admiral_adsl[1L, ], admiral_adsl) extract_duplicate_records(adsl, vars(USUBJID)) } +\seealso{ +Utilities for Dataset Checking: +\code{\link{get_duplicates_dataset}()}, +\code{\link{get_many_to_one_dataset}()}, +\code{\link{get_one_to_many_dataset}()} +} \author{ Thomas Neitmann } -\keyword{dev_utility} +\concept{utils_ds_chk} +\keyword{utils_ds_chk} diff --git a/man/extract_unit.Rd b/man/extract_unit.Rd index 0b5874391d..cc3f41f7d4 100644 --- a/man/extract_unit.Rd +++ b/man/extract_unit.Rd @@ -1,11 +1,9 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R, R/user_utils.R +% Please edit documentation in R/user_utils.R \name{extract_unit} \alias{extract_unit} \title{Extract Unit From Parameter Description} \usage{ -extract_unit(x) - extract_unit(x) } \arguments{ @@ -15,16 +13,18 @@ extract_unit(x) A string } \description{ -Extract the unit of a parameter from a description like "Param (unit)". - Extract the unit of a parameter from a description like "Param (unit)". } \examples{ extract_unit("Height (cm)") extract_unit("Diastolic Blood Pressure (mmHg)") -extract_unit("Height (cm)") - -extract_unit("Diastolic Blood Pressure (mmHg)") } -\keyword{user_utility} +\seealso{ +Utilities used within Derivation functions: +\code{\link{call_user_fun}()}, +\code{\link{get_not_mapped}()}, +\code{\link{signal_duplicate_records}()} +} +\concept{utils_help} +\keyword{utils_help} diff --git a/man/filter_confirmation.Rd b/man/filter_confirmation.Rd new file mode 100644 index 0000000000..a0040c1da6 --- /dev/null +++ b/man/filter_confirmation.Rd @@ -0,0 +1,298 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/filter_confirmation.R +\name{filter_confirmation} +\alias{filter_confirmation} +\title{Filter Confirmed Observations} +\usage{ +filter_confirmation( + dataset, + by_vars, + join_vars, + join_type, + first_cond = NULL, + order, + filter, + check_type = "warning" +) +} +\arguments{ +\item{dataset}{Input dataset + +The variables specified for \code{by_vars}, \code{join_vars}, and \code{order} are +expected.} + +\item{by_vars}{By variables + +The specified variables are used as by variables for joining the input +dataset with itself.} + +\item{join_vars}{Variables to keep from joined dataset + +The variables needed from the other observations should be specified for +this parameter. The specified variables are added to the joined dataset +with suffix ".join". For example to select all observations with \code{AVALC == "Y"} and \code{AVALC == "Y"} for at least one subsequent visit \code{join_vars = vars(AVALC, AVISITN)} and \code{filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join} could be specified. + +The \verb{*.join} variables are not included in the output dataset.} + +\item{join_type}{Observations to keep after joining + +The argument determines which of the joined observations are kept with +respect to the original observation. For example, if \code{join_type = "after"} is specified all observations after the original observations are +kept. + +\emph{Permitted Values:} \code{"before"}, \code{"after"}, \code{"all"}} + +\item{first_cond}{Condition for selecting range of data + +If this argument is specified, the other observations are restricted up to +the first observation where the specified condition is fulfilled. If the +condition is not fulfilled for any of the subsequent observations, all +observations are removed.} + +\item{order}{Order + +The observations are ordered by the specified order.} + +\item{filter}{Condition for selecting observations + +The filter is applied to the joined dataset for selecting the confirmed +observations. The condition can include summary functions. The joined +dataset is grouped by the original observations. I.e., the summary function +are applied to all observations up to the confirmation observation. For +example in the oncology setting when using this function for confirmed best +overall response, \code{filter = AVALC == "CR" & all(AVALC.join \%in\% c("CR", "NE")) & count_vals(var = AVALC.join, val = "NE") <= 1} selects +observations with response "CR" and for all observations up to the +confirmation observation the response is "CR" or "NE" and there is at most +one "NE".} + +\item{check_type}{Check uniqueness? + +If \code{"warning"} or \code{"error"} is specified, the specified message is issued +if the observations of the input dataset are not unique with respect to the +by variables and the order. + +\emph{Default:} \code{"none"} + +\emph{Permitted Values:} \code{"none"}, \code{"warning"}, \code{"error"}} +} +\value{ +A subset of the observations of the input dataset. All variables of +the input dataset are included in the output dataset. +} +\description{ +The function filters observation using a condition taking other observations +into account. For example, it could select all observations with \code{AVALC == "Y"} and \code{AVALC == "Y"} for at least one subsequent observation. The input +dataset is joined with itself to enable conditions taking variables from both +the current observation and the other observations into account. The suffix +".join" is added to the variables from the subsequent observations. + +An example usage might be checking if a patient received two required +medications within a certain timeframe of each other. + +In the oncology setting, for example, we use such processing to check if a +response value can be confirmed by a subsequent assessment. This is commonly +used in endpoints such as best overall response. +} +\details{ +The following steps are performed to produce the output dataset. +\subsection{Step 1}{ + +The input dataset is joined with itself by the variables specified for +\code{by_vars}. From the right hand side of the join only the variables +specified for \code{join_vars} are kept. The suffix ".join" is added to these +variables. + +For example, for \code{by_vars = USUBJID}, \code{join_vars = vars(AVISITN, AVALC)} and input dataset + +\if{html}{\out{
}}\preformatted{# A tibble: 2 x 4 +USUBJID AVISITN AVALC AVAL + +1 1 Y 1 +1 2 N 0 +}\if{html}{\out{
}} + +the joined dataset is + +\if{html}{\out{
}}\preformatted{A tibble: 4 x 6 +USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join + +1 1 Y 1 1 Y +1 1 Y 1 2 N +1 2 N 0 1 Y +1 2 N 0 2 N +}\if{html}{\out{
}} +} + +\subsection{Step 2}{ + +The joined dataset is restricted to observations with respect to +\code{join_type} and \code{order}. + +The dataset from the example in the previous step with \code{join_type = "after"} and order = vars(AVISITN)` is restricted to + +\if{html}{\out{
}}\preformatted{A tibble: 4 x 6 +USUBJID AVISITN AVALC AVAL AVISITN.join AVALC.join + +1 1 Y 1 2 N +}\if{html}{\out{
}} +} + +\subsection{Step 3}{ + +If \code{first_cond} is specified, for each observation of the input dataset the +joined dataset is restricted to observations up to the first observation +where \code{first_cond} is fulfilled (the observation fulfilling the condition +is included). If for an observation of the input dataset the condition is +not fulfilled, the observation is removed. +} + +\subsection{Step 4}{ + +The joined dataset is grouped by the observations from the input dataset +and restricted to the observations fulfilling the condition specified by +\code{filter}. +} + +\subsection{Step 5}{ + +The first observation of each group is selected and the \verb{*.join} variables +are dropped. +} +} +\examples{ + +library(tibble) +library(admiral) + +# filter observations with a duration longer than 30 and +# on or after 7 days before a COVID AE (ACOVFL == "Y") +adae <- tribble( + ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, + "1", 10, "N", 1, + "1", 21, "N", 50, + "1", 23, "Y", 14, + "1", 32, "N", 31, + "1", 42, "N", 20, + "2", 11, "Y", 13, + "2", 23, "N", 2, + "3", 13, "Y", 12, + "4", 14, "N", 32, + "4", 21, "N", 41 +) + +filter_confirmation( + adae, + by_vars = vars(USUBJID), + join_vars = vars(ACOVFL, ADY), + join_type = "all", + order = vars(ADY), + filter = ADURN > 30 & ACOVFL.join == "Y" & ADY >= ADY.join - 7 +) + +# filter observations with AVALC == "Y" and AVALC == "Y" at a subsequent visit +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "Y", + "1", 2, "N", + "1", 3, "Y", + "1", 4, "N", + "2", 1, "Y", + "2", 2, "N", + "3", 1, "Y", + "4", 1, "N", + "4", 2, "N", +) + +filter_confirmation( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC, AVISITN), + join_type = "after", + order = vars(AVISITN), + filter = AVALC == "Y" & AVALC.join == "Y" & AVISITN < AVISITN.join +) + +# select observations with AVALC == "CR", AVALC == "CR" at a subsequent visit, +# only "CR" or "NE" in between, and at most one "NE" in between +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", + "3", 1, "CR", + "4", 1, "CR", + "4", 2, "NE", + "4", 3, "NE", + "4", 4, "CR", + "4", 5, "PR" +) + +filter_confirmation( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + order = vars(AVISITN), + first_cond = AVALC.join == "CR", + filter = AVALC == "CR" & all(AVALC.join \%in\% c("CR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1 +) + +# select observations with AVALC == "PR", AVALC == "CR" or AVALC == "PR" +# at a subsequent visit at least 20 days later, only "CR", "PR", or "NE" +# in between, at most one "NE" in between, and "CR" is not followed by "PR" +data <- tribble( + ~USUBJID, ~ADY, ~AVALC, + "1", 6, "PR", + "1", 12, "CR", + "1", 24, "NE", + "1", 32, "CR", + "1", 48, "PR", + "2", 3, "PR", + "2", 21, "CR", + "2", 33, "PR", + "3", 11, "PR", + "4", 7, "PR", + "4", 12, "NE", + "4", 24, "NE", + "4", 32, "PR", + "4", 55, "PR" +) + +filter_confirmation( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC, ADY), + join_type = "after", + order = vars(ADY), + first_cond = AVALC.join \%in\% c("CR", "PR") & ADY.join - ADY >= 20, + filter = AVALC == "PR" & + all(AVALC.join \%in\% c("CR", "PR", "NE")) & + count_vals(var = AVALC.join, val = "NE") <= 1 & + ( + min_cond(var = ADY.join, cond = AVALC.join == "CR") > + max_cond(var = ADY.join, cond = AVALC.join == "PR") | + count_vals(var = AVALC.join, val = "CR") == 0 + ) +) +} +\seealso{ +\code{\link[=count_vals]{count_vals()}}, \code{\link[=min_cond]{min_cond()}}, \code{\link[=max_cond]{max_cond()}} + +Utilities for Filtering Observations: +\code{\link{count_vals}()}, +\code{\link{filter_extreme}()}, +\code{\link{filter_relative}()}, +\code{\link{max_cond}()}, +\code{\link{min_cond}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_fil} +\keyword{utils_fil} diff --git a/man/filter_date_sources.Rd b/man/filter_date_sources.Rd index d3f00e1072..3ddec8feb7 100644 --- a/man/filter_date_sources.Rd +++ b/man/filter_date_sources.Rd @@ -76,27 +76,31 @@ single dataset. \code{ADT} variable) from the single dataset is selected. } } \examples{ +library(tibble) library(dplyr, warn.conflicts = FALSE) library(lubridate) -adsl <- tibble::tribble( +adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") ) \%>\% mutate(STUDYID = "AB42") -ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, - "01", "2021-01-03T10:56", 1, "Flu", - "01", "2021-03-04", 2, "Cough", - "01", "2021", 3, "Flu" +ae <- tribble( + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) \%>\% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVNTDESC = "AE", SRCDOM = "AE", @@ -114,7 +118,31 @@ filter_date_sources( mode = "first" ) } +\seealso{ +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} +} \author{ Stefan Bundfuss } -\keyword{dev_utility} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/filter_extreme.Rd b/man/filter_extreme.Rd index c59a3e3c00..1b801e6586 100644 --- a/man/filter_extreme.Rd +++ b/man/filter_extreme.Rd @@ -80,8 +80,16 @@ admiral_ex \%>\% ) \%>\% select(USUBJID, EXTRT, EXDOSE) } +\seealso{ +Utilities for Filtering Observations: +\code{\link{count_vals}()}, +\code{\link{filter_confirmation}()}, +\code{\link{filter_relative}()}, +\code{\link{max_cond}()}, +\code{\link{min_cond}()} +} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{user_utility} +\concept{utils_fil} +\keyword{utils_fil} diff --git a/man/filter_if.Rd b/man/filter_if.Rd deleted file mode 100644 index ada419998f..0000000000 --- a/man/filter_if.Rd +++ /dev/null @@ -1,31 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/user_utils.R -\name{filter_if} -\alias{filter_if} -\title{Optional Filter} -\usage{ -filter_if(dataset, filter) -} -\arguments{ -\item{dataset}{Input dataset} - -\item{filter}{A filter condition. Must be a quosure.} -} -\value{ -A \code{data.frame} containing all rows in \code{dataset} matching \code{filter} or -just \code{dataset} if \code{filter} is \code{NULL} -} -\description{ -Filters the input dataset if the provided expression is not \code{NULL} -} -\examples{ -library(admiral.test) -data(admiral_vs) - -admiral::filter_if(admiral_vs, rlang::quo(NULL)) -admiral::filter_if(admiral_vs, rlang::quo(VSTESTCD == "WEIGHT")) -} -\author{ -Thomas Neitmann -} -\keyword{user_utility} diff --git a/man/filter_relative.Rd b/man/filter_relative.Rd index 31b44656a2..d3829d9220 100644 --- a/man/filter_relative.Rd +++ b/man/filter_relative.Rd @@ -155,8 +155,16 @@ response \%>\% inclusive = TRUE ) } +\seealso{ +Utilities for Filtering Observations: +\code{\link{count_vals}()}, +\code{\link{filter_confirmation}()}, +\code{\link{filter_extreme}()}, +\code{\link{max_cond}()}, +\code{\link{min_cond}()} +} \author{ Stefan Bundfuss } -\keyword{adam} -\keyword{user_utility} +\concept{utils_fil} +\keyword{utils_fil} diff --git a/man/format.sdg_select.Rd b/man/format.sdg_select.Rd index bcf1f749d1..f331666d1c 100644 --- a/man/format.sdg_select.Rd +++ b/man/format.sdg_select.Rd @@ -28,8 +28,31 @@ format( } \seealso{ \code{\link[=sdg_select]{sdg_select()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } -\keyword{dev_utility} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/format.smq_select.Rd b/man/format.smq_select.Rd index 469240fe2b..5d0332bd59 100644 --- a/man/format.smq_select.Rd +++ b/man/format.smq_select.Rd @@ -24,8 +24,31 @@ format(smq_select(id = 42, scope = "NARROW")) } \seealso{ \code{\link[=smq_select]{smq_select()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } -\keyword{dev_utility} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/format_eoxxstt_default.Rd b/man/format_eoxxstt_default.Rd index 918436a263..0bb499af2c 100644 --- a/man/format_eoxxstt_default.Rd +++ b/man/format_eoxxstt_default.Rd @@ -11,8 +11,10 @@ format_eoxxstt_default(status) } \value{ A \code{character} vector derived based on the values given in \code{status}: +"NOT STARTED" if \code{status} is "SCREEN FAILURE" or "SCREENING NOT COMPLETED", "COMPLETED" if \code{status} is "COMPLETED", -"DISCONTINUED" if \code{status} is not "COMPLETED" nor NA, +"DISCONTINUED" if \code{status} is not in ("COMPLETED","SCREEN FAILURE", +"SCREENING NOT COMPLETED") nor NA, "ONGOING" otherwise. } \description{ @@ -40,10 +42,14 @@ admiral_dm \%>\% } \seealso{ \code{\link[=derive_var_disposition_status]{derive_var_disposition_status()}} + +Utilities for Formatting Observations: +\code{\link{convert_blanks_to_na}()}, +\code{\link{format_reason_default}()}, +\code{\link{yn_to_numeric}()} } \author{ Samia Kabi } -\keyword{adsl} -\keyword{computation} -\keyword{user_utility} +\concept{utils_fmt} +\keyword{utils_fmt} diff --git a/man/format_reason_default.Rd b/man/format_reason_default.Rd index 59697c5a22..0c1bfdb800 100644 --- a/man/format_reason_default.Rd +++ b/man/format_reason_default.Rd @@ -44,10 +44,14 @@ admiral_dm \%>\% } \seealso{ \code{\link[=derive_vars_disposition_reason]{derive_vars_disposition_reason()}} + +Utilities for Formatting Observations: +\code{\link{convert_blanks_to_na}()}, +\code{\link{format_eoxxstt_default}()}, +\code{\link{yn_to_numeric}()} } \author{ Samia Kabi } -\keyword{adsl} -\keyword{computation} -\keyword{user_utility} +\concept{utils_fmt} +\keyword{utils_fmt} diff --git a/man/get_duplicates_dataset.Rd b/man/get_duplicates_dataset.Rd index 0d35861dbe..b28e943d73 100644 --- a/man/get_duplicates_dataset.Rd +++ b/man/get_duplicates_dataset.Rd @@ -33,7 +33,14 @@ signal_duplicate_records(adsl, vars(USUBJID), cnd_type = "warning") get_duplicates_dataset() } +\seealso{ +Utilities for Dataset Checking: +\code{\link{extract_duplicate_records}()}, +\code{\link{get_many_to_one_dataset}()}, +\code{\link{get_one_to_many_dataset}()} +} \author{ Thomas Neitmann } -\keyword{user_utility} +\concept{utils_ds_chk} +\keyword{utils_ds_chk} diff --git a/man/get_imputation_target_date.Rd b/man/get_imputation_target_date.Rd new file mode 100644 index 0000000000..b0cf4e0f33 --- /dev/null +++ b/man/get_imputation_target_date.Rd @@ -0,0 +1,58 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{get_imputation_target_date} +\alias{get_imputation_target_date} +\title{Get Date Imputation Targets} +\usage{ +get_imputation_target_date(date_imputation, month) +} +\arguments{ +\item{date_imputation}{The value to impute the day/month when a datepart is +missing. + +A character value is expected, either as a +\itemize{ +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th +of June, +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last +day/month. +}} + +\item{month}{Month component of the partial date} +} +\value{ +A list of character vectors. The elements of the list are named +"year", "month", "day". +} +\description{ +Get Date Imputation Targets +} +\details{ +\itemize{ +\item For \code{date_imputation = "first"} \code{"0000"}, \code{"01"}, \code{"01"} are returned. +\item For \code{date_imputation = "mid"} \code{"xxxx"}, \code{"06"}, \code{"30"} if \code{month} is \code{NA} +and \code{"15"} otherwise are returned. +\item For \code{date_imputation = "last"} \code{"9999"}, \code{"12"}, \code{"31"} are returned. +\item For \code{date_imputation = "-
"} \code{"xxxx"}, \code{""}, \code{"
"} are returned. +} + +\code{"xxxx"} indicates that the component is undefined. If an undefined +component occurs in the imputed DTC value, the imputed DTC value is set to +\code{NA_character_} in the imputation functions. +} +\seealso{ +\code{\link[=impute_dtc_dtm]{impute_dtc_dtm()}}, \code{\link[=impute_dtc_dt]{impute_dtc_dt()}} + +Utilities used for date imputation: +\code{\link{dt_level}()}, +\code{\link{dtm_level}()}, +\code{\link{get_imputation_target_time}()}, +\code{\link{get_partialdatetime}()}, +\code{\link{restrict_imputed_dtc_dtm}()}, +\code{\link{restrict_imputed_dtc_dt}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/get_imputation_target_time.Rd b/man/get_imputation_target_time.Rd new file mode 100644 index 0000000000..c24c73e5fc --- /dev/null +++ b/man/get_imputation_target_time.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{get_imputation_target_time} +\alias{get_imputation_target_time} +\title{Get Time Imputation Targets} +\usage{ +get_imputation_target_time(time_imputation) +} +\arguments{ +\item{time_imputation}{The value to impute the time when a timepart is +missing. + +A character value is expected, either as a +\itemize{ +\item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} +for the start of the day, +\item or as a keyword: \code{"first"},\code{"last"} to impute to the start/end of a day. +}} +} +\value{ +A list of character vectors. The elements of the list are named +"hour", "minute", "second". +} +\description{ +Get Time Imputation Targets +} +\details{ +\itemize{ +\item For \code{time_imputation = "first"} \code{"00"}, \code{"00"}, \code{"00"} are returned. +\item For \code{time_imputation = "last"} \code{"23"}, \code{"59"}, \code{"59"} are returned. +\item For \code{time_imputation = "::"} \code{""}, \code{""}, \code{""} are returned. +} +} +\seealso{ +\code{\link[=impute_dtc_dtm]{impute_dtc_dtm()}} + +Utilities used for date imputation: +\code{\link{dt_level}()}, +\code{\link{dtm_level}()}, +\code{\link{get_imputation_target_date}()}, +\code{\link{get_partialdatetime}()}, +\code{\link{restrict_imputed_dtc_dtm}()}, +\code{\link{restrict_imputed_dtc_dt}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/get_many_to_one_dataset.Rd b/man/get_many_to_one_dataset.Rd index a21624c4f4..6cdf057547 100644 --- a/man/get_many_to_one_dataset.Rd +++ b/man/get_many_to_one_dataset.Rd @@ -31,7 +31,14 @@ try( get_many_to_one_dataset() } +\seealso{ +Utilities for Dataset Checking: +\code{\link{extract_duplicate_records}()}, +\code{\link{get_duplicates_dataset}()}, +\code{\link{get_one_to_many_dataset}()} +} \author{ Stefan Bundfuss } -\keyword{user_utility} +\concept{utils_ds_chk} +\keyword{utils_ds_chk} diff --git a/man/get_not_mapped.Rd b/man/get_not_mapped.Rd new file mode 100644 index 0000000000..d9976a87ff --- /dev/null +++ b/man/get_not_mapped.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_merged.R +\name{get_not_mapped} +\alias{get_not_mapped} +\title{Get list of records not mapped from the lookup table.} +\usage{ +get_not_mapped() +} +\value{ +A \code{data.frame} or \code{NULL} +} +\description{ +Get list of records not mapped from the lookup table. +} +\seealso{ +Utilities used within Derivation functions: +\code{\link{call_user_fun}()}, +\code{\link{extract_unit}()}, +\code{\link{signal_duplicate_records}()} +} +\concept{utils_help} +\keyword{utils_help} diff --git a/man/get_one_to_many_dataset.Rd b/man/get_one_to_many_dataset.Rd index a33249b948..0ce60fb149 100644 --- a/man/get_one_to_many_dataset.Rd +++ b/man/get_one_to_many_dataset.Rd @@ -31,7 +31,14 @@ try( get_one_to_many_dataset() } +\seealso{ +Utilities for Dataset Checking: +\code{\link{extract_duplicate_records}()}, +\code{\link{get_duplicates_dataset}()}, +\code{\link{get_many_to_one_dataset}()} +} \author{ Stefan Bundfuss } -\keyword{user_utility} +\concept{utils_ds_chk} +\keyword{utils_ds_chk} diff --git a/man/get_partialdatetime.Rd b/man/get_partialdatetime.Rd new file mode 100644 index 0000000000..96598ffaf8 --- /dev/null +++ b/man/get_partialdatetime.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{get_partialdatetime} +\alias{get_partialdatetime} +\title{Parse DTC variable and Determine Components} +\usage{ +get_partialdatetime(dtc) +} +\arguments{ +\item{dtc}{The \code{'--DTC'} date to parse + +A character date is expected in a format like \code{yyyy-mm-dd} or +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid value for any component.} +} +\value{ +A list of character vectors. The elements of the list are named +"year", "month", "day", "hour", "minute", and "second". Missing components +are set to \code{NA_character_}. +} +\description{ +Parse DTC variable and Determine Components +} +\details{ +The function can be replaced by the parttime parser once it is +available. +} +\seealso{ +\code{\link[=impute_dtc_dtm]{impute_dtc_dtm()}}, \code{\link[=impute_dtc_dt]{impute_dtc_dt()}} + +Utilities used for date imputation: +\code{\link{dt_level}()}, +\code{\link{dtm_level}()}, +\code{\link{get_imputation_target_date}()}, +\code{\link{get_imputation_target_time}()}, +\code{\link{restrict_imputed_dtc_dtm}()}, +\code{\link{restrict_imputed_dtc_dt}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/get_summary_records.Rd b/man/get_summary_records.Rd new file mode 100644 index 0000000000..5d45e8bd14 --- /dev/null +++ b/man/get_summary_records.Rd @@ -0,0 +1,181 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_summary_records.R +\name{get_summary_records} +\alias{get_summary_records} +\title{Create Summary Records} +\usage{ +get_summary_records( + dataset, + by_vars, + filter = NULL, + analysis_var, + summary_fun, + set_values_to +) +} +\arguments{ +\item{dataset}{A data frame.} + +\item{by_vars}{Variables to consider for generation of groupwise summary +records. Providing the names of variables in \code{\link[=vars]{vars()}} will create a +groupwise summary and generate summary records for the specified groups.} + +\item{filter}{Filter condition as logical expression to apply during +summary calculation. By default, filtering expressions are computed within +\code{by_vars} as this will help when an aggregating, lagging, or ranking +function is involved. + +For example, +\itemize{ +\item \code{filter_rows = (AVAL > mean(AVAL, na.rm = TRUE))} will filter all AVAL +values greater than mean of AVAL with in \code{by_vars}. +\item \code{filter_rows = (dplyr::n() > 2)} will filter n count of \code{by_vars} greater +than 2. +}} + +\item{analysis_var}{Analysis variable.} + +\item{summary_fun}{Function that takes as an input the \code{analysis_var} and +performs the calculation. +This can include built-in functions as well as user defined functions, +for example \code{mean} or \code{function(x) mean(x, na.rm = TRUE)}.} + +\item{set_values_to}{A list of variable name-value pairs. Use this argument +if you need to change the values of any newly derived records. + +Set a list of variables to some specified value for the new observation(s) +\itemize{ +\item LHS refer to a variable. +\item RHS refers to the values to set to the variable. This can be a string, a symbol, a numeric +value or NA. +(e.g. \code{vars(PARAMCD = "TDOSE",PARCAT1 = "OVERALL")}). +More general expression are not allowed. +}} +} +\value{ +A data frame of derived records. +} +\description{ +It is not uncommon to have an analysis need whereby one needs to derive an +analysis value (\code{AVAL}) from multiple records. The ADaM basic dataset +structure variable \code{DTYPE} is available to indicate when a new derived +records has been added to a dataset. +} +\details{ +This function only creates derived observations and does not append them +to the original dataset observations. If you would like to this instead, +see the \code{derive_summary_records()} function. +} +\examples{ +library(dplyr, warn.conflicts = FALSE) +adeg <- tibble::tribble( + ~USUBJID, ~EGSEQ, ~PARAM, ~AVISIT, ~EGDTC, ~AVAL, ~TRTA, + "XYZ-1001", 1, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:50", 385, "", + "XYZ-1001", 2, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:52", 399, "", + "XYZ-1001", 3, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:56", 396, "", + "XYZ-1001", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:45", 384, "Placebo", + "XYZ-1001", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:48", 393, "Placebo", + "XYZ-1001", 6, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:51", 388, "Placebo", + "XYZ-1001", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:45", 385, "Placebo", + "XYZ-1001", 8, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:48", 394, "Placebo", + "XYZ-1001", 9, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:51", 402, "Placebo", + "XYZ-1002", 1, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 399, "", + "XYZ-1002", 2, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 410, "", + "XYZ-1002", 3, "QTcF Int. (msec)", "Baseline", "2016-02-22T08:01", 392, "", + "XYZ-1002", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:50", 401, "Active 20mg", + "XYZ-1002", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:53", 407, "Active 20mg", + "XYZ-1002", 6, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:56", 400, "Active 20mg", + "XYZ-1002", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:50", 412, "Active 20mg", + "XYZ-1002", 8, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:53", 414, "Active 20mg", + "XYZ-1002", 9, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:56", 402, "Active 20mg", +) + +# Summarize the average of the triplicate ECG interval values (AVAL) +get_summary_records( + adeg, + by_vars = vars(USUBJID, PARAM, AVISIT), + analysis_var = AVAL, + summary_fun = function(x) mean(x, na.rm = TRUE), + set_values_to = vars(DTYPE = "AVERAGE") +) + +advs <- tibble::tribble( + ~USUBJID, ~VSSEQ, ~PARAM, ~AVAL, ~VSSTRESU, ~VISIT, ~VSDTC, + "XYZ-001-001", 1164, "Weight", 99, "kg", "Screening", "2018-03-19", + "XYZ-001-001", 1165, "Weight", 101, "kg", "Run-In", "2018-03-26", + "XYZ-001-001", 1166, "Weight", 100, "kg", "Baseline", "2018-04-16", + "XYZ-001-001", 1167, "Weight", 94, "kg", "Week 24", "2018-09-30", + "XYZ-001-001", 1168, "Weight", 92, "kg", "Week 48", "2019-03-17", + "XYZ-001-001", 1169, "Weight", 95, "kg", "Week 52", "2019-04-14", +) + +# Set new values to any variable. Here, `DTYPE = MAXIMUM` refers to `max()` records +# and `DTYPE = AVERAGE` refers to `mean()` records. +get_summary_records( + advs, + by_vars = vars(USUBJID, PARAM), + analysis_var = AVAL, + summary_fun = max, + set_values_to = vars(DTYPE = "MAXIMUM") +) \%>\% + get_summary_records( + by_vars = vars(USUBJID, PARAM), + analysis_var = AVAL, + summary_fun = mean, + set_values_to = vars(DTYPE = "AVERAGE") + ) + +# Sample ADEG dataset with triplicate record for only AVISIT = 'Baseline' +adeg <- tibble::tribble( + ~USUBJID, ~EGSEQ, ~PARAM, ~AVISIT, ~EGDTC, ~AVAL, ~TRTA, + "XYZ-1001", 1, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:50", 385, "", + "XYZ-1001", 2, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:52", 399, "", + "XYZ-1001", 3, "QTcF Int. (msec)", "Baseline", "2016-02-24T07:56", 396, "", + "XYZ-1001", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:48", 393, "Placebo", + "XYZ-1001", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-08T09:51", 388, "Placebo", + "XYZ-1001", 6, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:48", 394, "Placebo", + "XYZ-1001", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-22T10:51", 402, "Placebo", + "XYZ-1002", 1, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 399, "", + "XYZ-1002", 2, "QTcF Int. (msec)", "Baseline", "2016-02-22T07:58", 410, "", + "XYZ-1002", 3, "QTcF Int. (msec)", "Baseline", "2016-02-22T08:01", 392, "", + "XYZ-1002", 4, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:53", 407, "Active 20mg", + "XYZ-1002", 5, "QTcF Int. (msec)", "Visit 2", "2016-03-06T09:56", 400, "Active 20mg", + "XYZ-1002", 6, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:53", 414, "Active 20mg", + "XYZ-1002", 7, "QTcF Int. (msec)", "Visit 3", "2016-03-24T10:56", 402, "Active 20mg", +) + +# Compute the average of AVAL only if there are more than 2 records within the +# by group +get_summary_records( + adeg, + by_vars = vars(USUBJID, PARAM, AVISIT), + filter = dplyr::n() > 2, + analysis_var = AVAL, + summary_fun = function(x) mean(x, na.rm = TRUE), + set_values_to = vars(DTYPE = "AVERAGE") +) +} +\seealso{ +\code{derive_summary_records()} + +General Derivation Functions for all ADaMs that returns variable appended to dataset: +\code{\link{derive_var_confirmation_flag}()}, +\code{\link{derive_var_extreme_flag}()}, +\code{\link{derive_var_last_dose_amt}()}, +\code{\link{derive_var_last_dose_date}()}, +\code{\link{derive_var_last_dose_grp}()}, +\code{\link{derive_var_merged_cat}()}, +\code{\link{derive_var_merged_character}()}, +\code{\link{derive_var_merged_exist_flag}()}, +\code{\link{derive_var_obs_number}()}, +\code{\link{derive_var_worst_flag}()}, +\code{\link{derive_vars_last_dose}()}, +\code{\link{derive_vars_merged_lookup}()}, +\code{\link{derive_vars_merged}()}, +\code{\link{derive_vars_transposed}()} +} +\author{ +Pavan Kumar, updated by Alana Harris +} +\concept{der_gen} +\keyword{der_gen} diff --git a/man/get_terms_from_db.Rd b/man/get_terms_from_db.Rd index b64eac74d1..46e9007f9c 100644 --- a/man/get_terms_from_db.Rd +++ b/man/get_terms_from_db.Rd @@ -62,6 +62,15 @@ The function checks if all requirements to access the database are fulfilled reads the terms from the database, and checks if the dataset with the terms is in the expected format (see \code{assert_terms()}). } +\seealso{ +OCCDS Functions: +\code{\link{create_query_data}()}, +\code{\link{create_single_dose_dataset}()}, +\code{\link{derive_vars_atc}()}, +\code{\link{derive_vars_query}()} +} \author{ Stefan Bundfuss } +\concept{der_occds} +\keyword{der_occds} diff --git a/man/impute_dtc.Rd b/man/impute_dtc.Rd deleted file mode 100644 index f3cddd5c29..0000000000 --- a/man/impute_dtc.Rd +++ /dev/null @@ -1,177 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_date_vars.R -\name{impute_dtc} -\alias{impute_dtc} -\title{Impute Partial Date(-time) Portion of a \code{'--DTC'} Variable} -\usage{ -impute_dtc( - dtc, - date_imputation = NULL, - time_imputation = "00:00:00", - min_dates = NULL, - max_dates = NULL, - preserve = FALSE -) -} -\arguments{ -\item{dtc}{The \code{'--DTC'} date to impute - -A character date is expected in a format like \code{yyyy-mm-dd} or -\code{yyyy-mm-ddThh:mm:ss}. If the year part is not recorded (missing date), no -imputation is performed.} - -\item{date_imputation}{The value to impute the day/month when a datepart is -missing. - -If \code{NULL}: no date imputation is performed and partial dates are returned as -missing. - -Otherwise, a character value is expected, either as a -\itemize{ -\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the 15th -of June, -\item or as a keyword: \code{"FIRST"}, \code{"MID"}, \code{"LAST"} to impute to the first/mid/last -day/month. -} - -Default is \code{NULL}.} - -\item{time_imputation}{The value to impute the time when a timepart is -missing. - -A character value is expected, either as a -\itemize{ -\item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} -for the start of the day, -\item or as a keyword: \code{"FIRST"},\code{"LAST"} to impute to the start/end of a day. -} - -Default is \code{"00:00:00"}.} - -\item{min_dates}{Minimum dates - -A list of dates is expected. It is ensured that the imputed date is not -before any of the specified dates, e.g., that the imputed adverse event start -date is not before the first treatment date. Only dates which are in the -range of possible dates of the \code{dtc} value are considered. The possible dates -are defined by the missing parts of the \code{dtc} date (see example below). This -ensures that the non-missing parts of the \code{dtc} date are not changed. -A date or date-time object is expected. -For example - -\if{html}{\out{
}}\preformatted{impute_dtc( -"2020-11", -min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") -), -date_imputation = "first" -) -}\if{html}{\out{
}} - -returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} -range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore -\code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would -have changed the month although it is not missing (in the \code{dtc} date).} - -\item{max_dates}{Maximum dates - -A list of dates is expected. It is ensured that the imputed date is not after -any of the specified dates, e.g., that the imputed date is not after the data -cut off date. Only dates which are in the range of possible dates are -considered. A date or date-time object is expected.} - -\item{preserve}{Preserve day if month is missing and day is present - -For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} -(and \code{date_imputation = "MID"}). - -Permitted Values: \code{TRUE}, \code{FALSE} - -Default: \code{FALSE}} -} -\value{ -A character vector -} -\description{ -Imputation partial date/time portion of a \code{'--DTC'} variable. based on user -input. -} -\details{ -Usually this computation function can not be used with \verb{\%>\%}. -} -\examples{ -library(lubridate) - -dates <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019", - "2019---07", - "" -) - -# No date imputation (date_imputation defaulted to NULL) -# Missing time part imputed with 00:00:00 portion by default -impute_dtc(dtc = dates) - -# No date imputation (date_imputation defaulted to NULL) -# Missing time part imputed with 23:59:59 portion -impute_dtc( - dtc = dates, - time_imputation = "23:59:59" -) - -# Same as above -impute_dtc( - dtc = dates, - time_imputation = "LAST" -) - -# Impute to first day/month if date is partial -# Missing time part imputed with 00:00:00 portion by default -impute_dtc( - dtc = dates, - date_imputation = "01-01" -) -# same as above -impute_dtc( - dtc = dates, - date_imputation = "FIRST" -) - -# Impute to last day/month if date is partial -# Missing time part imputed with 23:59:59 portion -impute_dtc( - dtc = dates, - date_imputation = "LAST", - time_imputation = "LAST" -) - -# Impute to mid day/month if date is partial -# Missing time part imputed with 00:00:00 portion by default -impute_dtc( - dtc = dates, - date_imputation = "MID" -) - -# Impute a date and ensure that the imputed date is not before a list of -# minimum dates -impute_dtc( - "2020-12", - min_dates = list( - ymd_hms("2020-12-06T12:12:12"), - ymd_hms("2020-11-11T11:11:11") - ), - date_imputation = "first" -) -} -\author{ -Samia Kabi -} -\keyword{computation} -\keyword{timing} diff --git a/man/impute_dtc_dt.Rd b/man/impute_dtc_dt.Rd new file mode 100644 index 0000000000..33f7d33bac --- /dev/null +++ b/man/impute_dtc_dt.Rd @@ -0,0 +1,191 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{impute_dtc_dt} +\alias{impute_dtc_dt} +\title{Impute Partial Date Portion of a \code{'--DTC'} Variable} +\usage{ +impute_dtc_dt( + dtc, + highest_imputation = "n", + date_imputation = "first", + min_dates = NULL, + max_dates = NULL, + preserve = FALSE +) +} +\arguments{ +\item{dtc}{The \code{'--DTC'} date to impute + +A character date is expected in a format like \code{yyyy-mm-dd} or +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} + +\item{highest_imputation}{Highest imputation level + +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. + +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. + +\emph{Default}: \code{"n"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"n"} (none, lowest level)} + +\item{date_imputation}{The value to impute the day/month when a datepart is +missing. + +A character value is expected, either as a +\itemize{ +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last +day/month. +} + +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}} + +\item{min_dates}{Minimum dates + +A list of dates is expected. It is ensured that the imputed date is not +before any of the specified dates, e.g., that the imputed adverse event start +date is not before the first treatment date. Only dates which are in the +range of possible dates of the \code{dtc} value are considered. The possible dates +are defined by the missing parts of the \code{dtc} date (see example below). This +ensures that the non-missing parts of the \code{dtc} date are not changed. +A date or date-time object is expected. +For example + +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" +) +}\if{html}{\out{
}} + +returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} +range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore +\code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would +have changed the month although it is not missing (in the \code{dtc} date).} + +\item{max_dates}{Maximum dates + +A list of dates is expected. It is ensured that the imputed date is not after +any of the specified dates, e.g., that the imputed date is not after the data +cut off date. Only dates which are in the range of possible dates are +considered. A date or date-time object is expected.} + +\item{preserve}{Preserve day if month is missing and day is present + +For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} +(and \code{date_imputation = "MID"}). + +Permitted Values: \code{TRUE}, \code{FALSE} + +Default: \code{FALSE}} +} +\value{ +A character vector +} +\description{ +Imputation partial date portion of a \code{'--DTC'} variable based on user input. +} +\details{ +Usually this computation function can not be used with \verb{\%>\%}. +} +\examples{ +library(lubridate) + +dates <- c( + "2019-07-18T15:25:40", + "2019-07-18T15:25", + "2019-07-18T15", + "2019-07-18", + "2019-02", + "2019", + "2019", + "2019---07", + "" +) + +# No date imputation (highest_imputation defaulted to "n") +impute_dtc_dt(dtc = dates) + +# Impute to first day/month if date is partial +impute_dtc_dt( + dtc = dates, + highest_imputation = "M" +) +# Same as above +impute_dtc_dtm( + dtc = dates, + highest_imputation = "M", + date_imputation = "01-01" +) + +# Impute to last day/month if date is partial +impute_dtc_dt( + dtc = dates, + highest_imputation = "M", + date_imputation = "last", +) + +# Impute to mid day/month if date is partial +impute_dtc_dt( + dtc = dates, + highest_imputation = "M", + date_imputation = "mid" +) + +# Impute a date and ensure that the imputed date is not before a list of +# minimum dates +impute_dtc_dt( + "2020-12", + min_dates = list( + ymd("2020-12-06"), + ymd("2020-11-11") + ), + highest_imputation = "M" +) + +# Impute completely missing dates (only possible if min_dates or max_dates is specified) +impute_dtc_dt( + c("2020-12", NA_character_), + min_dates = list( + ymd("2020-12-06"), + ymd("2020-11-11") + ), + highest_imputation = "Y" +) +} +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_duration}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dtm}()} +} +\author{ +Samia Kabi, Stefan Bundfuss +} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/impute_dtc_dtm.Rd b/man/impute_dtc_dtm.Rd new file mode 100644 index 0000000000..8e7d6f95ca --- /dev/null +++ b/man/impute_dtc_dtm.Rd @@ -0,0 +1,233 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{impute_dtc_dtm} +\alias{impute_dtc_dtm} +\title{Impute Partial Date(-time) Portion of a \code{'--DTC'} Variable} +\usage{ +impute_dtc_dtm( + dtc, + highest_imputation = "h", + date_imputation = "first", + time_imputation = "first", + min_dates = NULL, + max_dates = NULL, + preserve = FALSE +) +} +\arguments{ +\item{dtc}{The \code{'--DTC'} date to impute + +A character date is expected in a format like \code{yyyy-mm-dd} or +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} + +\item{highest_imputation}{Highest imputation level + +The \code{highest_imputation} argument controls which components of the DTC +value are imputed if they are missing. All components up to the specified +level are imputed. + +If a component at a higher level than the highest imputation level is +missing, \code{NA_character_} is returned. For example, for \code{highest_imputation = "D"} \code{"2020"} results in \code{NA_character_} because the month is missing. + +If \code{"n"} is specified, no imputation is performed, i.e., if any component is +missing, \code{NA_character_} is returned. + +If \code{"Y"} is specified, \code{date_imputation} should be \code{"first"} or \code{"last"} +and \code{min_dates} or \code{max_dates} should be specified respectively. Otherwise, +\code{NA_character_} is returned if the year component is missing. + +\emph{Default}: \code{"h"} + +\emph{Permitted Values}: \code{"Y"} (year, highest level), \code{"M"} (month), \code{"D"} +(day), \code{"h"} (hour), \code{"m"} (minute), \code{"s"} (second), \code{"n"} (none, lowest +level)} + +\item{date_imputation}{The value to impute the day/month when a datepart is +missing. + +A character value is expected, either as a +\itemize{ +\item format with month and day specified as \code{"mm-dd"}: e.g. \code{"06-15"} for the +15th of June (The year can not be specified; for imputing the year +\code{"first"} or \code{"last"} together with \code{min_dates} or \code{max_dates} argument can +be used (see examples).), +\item or as a keyword: \code{"first"}, \code{"mid"}, \code{"last"} to impute to the first/mid/last +day/month. +} + +The argument is ignored if \code{highest_imputation} is less then \code{"D"}. + +\emph{Default}: \code{"first"}.} + +\item{time_imputation}{The value to impute the time when a timepart is +missing. + +A character value is expected, either as a +\itemize{ +\item format with hour, min and sec specified as \code{"hh:mm:ss"}: e.g. \code{"00:00:00"} +for the start of the day, +\item or as a keyword: \code{"first"},\code{"last"} to impute to the start/end of a day. +} + +The argument is ignored if \code{highest_imputation = "n"}. + +\emph{Default}: \code{"first"}.} + +\item{min_dates}{Minimum dates + +A list of dates is expected. It is ensured that the imputed date is not +before any of the specified dates, e.g., that the imputed adverse event start +date is not before the first treatment date. Only dates which are in the +range of possible dates of the \code{dtc} value are considered. The possible dates +are defined by the missing parts of the \code{dtc} date (see example below). This +ensures that the non-missing parts of the \code{dtc} date are not changed. +A date or date-time object is expected. +For example + +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" +) +}\if{html}{\out{
}} + +returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} +range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore +\code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would +have changed the month although it is not missing (in the \code{dtc} date). + +For date variables (not datetime) in the list the time is imputed to +\code{"00:00:00"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} + +\item{max_dates}{Maximum dates + +A list of dates is expected. It is ensured that the imputed date is not after +any of the specified dates, e.g., that the imputed date is not after the data +cut off date. Only dates which are in the range of possible dates are +considered. A date or date-time object is expected. + +For date variables (not datetime) in the list the time is imputed to +\code{"23:59:59"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} + +\item{preserve}{Preserve day if month is missing and day is present + +For example \code{"2019---07"} would return \verb{"2019-06-07} if \code{preserve = TRUE} +(and \code{date_imputation = "mid"}). + +Permitted Values: \code{TRUE}, \code{FALSE} + +\emph{Default}: \code{FALSE}} +} +\value{ +A character vector +} +\description{ +Imputation partial date/time portion of a \code{'--DTC'} variable. based on user +input. +} +\details{ +Usually this computation function can not be used with \verb{\%>\%}. +} +\examples{ +library(lubridate) + +dates <- c( + "2019-07-18T15:25:40", + "2019-07-18T15:25", + "2019-07-18T15", + "2019-07-18", + "2019-02", + "2019", + "2019", + "2019---07", + "" +) + +# No date imputation (highest_imputation defaulted to "h") +# Missing time part imputed with 00:00:00 portion by default +impute_dtc_dtm(dtc = dates) + +# No date imputation (highest_imputation defaulted to "h") +# Missing time part imputed with 23:59:59 portion +impute_dtc_dtm( + dtc = dates, + time_imputation = "23:59:59" +) + +# Same as above +impute_dtc_dtm( + dtc = dates, + time_imputation = "last" +) + +# Impute to first day/month if date is partial +# Missing time part imputed with 00:00:00 portion by default +impute_dtc_dtm( + dtc = dates, + highest_imputation = "M" +) +# same as above +impute_dtc_dtm( + dtc = dates, + highest_imputation = "M", + date_imputation = "01-01" +) + +# Impute to last day/month if date is partial +# Missing time part imputed with 23:59:59 portion +impute_dtc_dtm( + dtc = dates, + date_imputation = "last", + time_imputation = "last" +) + +# Impute to mid day/month if date is partial +# Missing time part imputed with 00:00:00 portion by default +impute_dtc_dtm( + dtc = dates, + highest_imputation = "M", + date_imputation = "mid" +) + +# Impute a date and ensure that the imputed date is not before a list of +# minimum dates +impute_dtc_dtm( + "2020-12", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" +) + +# Impute completely missing dates (only possible if min_dates or max_dates is specified) +impute_dtc_dtm( + c("2020-12", NA_character_), + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "Y" +) +} +\seealso{ +Date/Time Computation Functions that returns a vector: +\code{\link{compute_dtf}()}, +\code{\link{compute_duration}()}, +\code{\link{compute_tmf}()}, +\code{\link{convert_date_to_dtm}()}, +\code{\link{convert_dtc_to_dtm}()}, +\code{\link{convert_dtc_to_dt}()}, +\code{\link{impute_dtc_dt}()} +} +\author{ +Samia Kabi, Stefan Bundfuss +} +\concept{com_date_time} +\keyword{com_date_time} diff --git a/man/is_auto.Rd b/man/is_auto.Rd deleted file mode 100644 index ac01bae1fa..0000000000 --- a/man/is_auto.Rd +++ /dev/null @@ -1,37 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dev_utils.R -\name{is_auto} -\alias{is_auto} -\title{Checks if the argument equals the auto keyword} -\usage{ -is_auto(arg) -} -\arguments{ -\item{arg}{argument to check} -} -\value{ -\code{TRUE} if the argument equals the auto keyword, i.e., it is a quosure -of a symbol named auto. -} -\description{ -Checks if the argument equals the auto keyword -} -\examples{ - -example_fun <- function(arg) { - arg <- rlang::enquo(arg) - if (admiral:::is_auto(arg)) { - "auto keyword was specified" - } else { - arg - } -} - -example_fun("Hello World!") - -example_fun(auto) -} -\author{ -Stefan Bundfuss -} -\keyword{check} diff --git a/man/list_all_templates.Rd b/man/list_all_templates.Rd index 3a70f26396..1ecd52e025 100644 --- a/man/list_all_templates.Rd +++ b/man/list_all_templates.Rd @@ -4,7 +4,10 @@ \alias{list_all_templates} \title{List All Available ADaM Templates} \usage{ -list_all_templates() +list_all_templates(package = "admiral") +} +\arguments{ +\item{package}{The R package in which to look for templates. By default \code{"admiral"}.} } \value{ A \code{character} vector of all available templates @@ -15,7 +18,12 @@ List All Available ADaM Templates \examples{ list_all_templates() } +\seealso{ +Utilities used for examples and template scripts: +\code{\link{use_ad_template}()} +} \author{ Shimeng Huang, Thomas Neitmann } -\keyword{user_utility} +\concept{utils_examples} +\keyword{utils_examples} diff --git a/man/list_tte_source_objects.Rd b/man/list_tte_source_objects.Rd new file mode 100644 index 0000000000..2f81fdb41e --- /dev/null +++ b/man/list_tte_source_objects.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_param_tte.R +\name{list_tte_source_objects} +\alias{list_tte_source_objects} +\title{List all \code{tte_source} Objects Available in a Package} +\usage{ +list_tte_source_objects(package = "admiral") +} +\arguments{ +\item{package}{The name of the package in which to search for \code{tte_source} objects} +} +\value{ +A \code{data.frame} where each row corresponds to one \code{tte_source} object or \code{NULL} +if \code{package} does not contain any \code{tte_source} objects +} +\description{ +List all \code{tte_source} Objects Available in a Package +} +\examples{ +list_tte_source_objects() +} +\seealso{ +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} +} +\author{ +Thomas Neitmann +} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/lstalvdt_source.Rd b/man/lstalvdt_source.Rd deleted file mode 100644 index 47d88451f4..0000000000 --- a/man/lstalvdt_source.Rd +++ /dev/null @@ -1,42 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_var_extreme_date.R -\name{lstalvdt_source} -\alias{lstalvdt_source} -\title{Create an \code{lstalvdt_source} object} -\usage{ -lstalvdt_source( - dataset_name, - filter = NULL, - date, - date_imputation = NULL, - traceability_vars = NULL -) -} -\arguments{ -\item{dataset_name}{The name of the dataset, i.e. a string, used to search for -the last known alive date.} - -\item{filter}{An unquoted condition for filtering \code{dataset}.} - -\item{date}{A variable providing a date where the patient was known to be -alive. A date, a datetime, or a character variable containing ISO 8601 -dates can be specified. An unquoted symbol is expected.} - -\item{date_imputation}{A string defining the date imputation for \code{date}. -See \code{date_imputation} parameter of \code{impute_dtc()} for valid values.} - -\item{traceability_vars}{A named list returned by \code{vars()} defining the -traceability variables, e.g. \code{vars(LALVDOM = "AE", LALVSEQ = AESEQ, LALVVAR = "AESTDTC")}. The values must be a symbol, a character string, or \code{NA}.} -} -\value{ -An object of class \code{lstalvdt_source}. -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} - -\emph{Deprecated}, please use \code{date_source()} instead. -} -\author{ -Stefan Bundfuss -} -\keyword{source_specifications} diff --git a/man/max_cond.Rd b/man/max_cond.Rd new file mode 100644 index 0000000000..48c4a9fc38 --- /dev/null +++ b/man/max_cond.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/filter_confirmation.R +\name{max_cond} +\alias{max_cond} +\title{Maximum Value on a Subset} +\usage{ +max_cond(var, cond) +} +\arguments{ +\item{var}{A vector} + +\item{cond}{A condition} +} +\description{ +The function derives the maximum value of a vector/column on a subset of +entries/observations. +} +\examples{ + +library(tibble) +library(dplyr) +library(admiral) +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", +) + +# In oncology setting, when needing to check the first time a patient had +# a Complete Response (CR) to compare to see if any Partial Response (PR) +# occurred after this add variable indicating if PR occurred after CR +group_by(data, USUBJID) \%>\% mutate( + first_cr_vis = min_cond(var = AVISITN, cond = AVALC == "CR"), + last_pr_vis = max_cond(var = AVISITN, cond = AVALC == "PR"), + pr_after_cr = last_pr_vis > first_cr_vis +) +} +\seealso{ +Utilities for Filtering Observations: +\code{\link{count_vals}()}, +\code{\link{filter_confirmation}()}, +\code{\link{filter_extreme}()}, +\code{\link{filter_relative}()}, +\code{\link{min_cond}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_fil} +\keyword{utils_fil} diff --git a/man/min_cond.Rd b/man/min_cond.Rd new file mode 100644 index 0000000000..1d7dad26d7 --- /dev/null +++ b/man/min_cond.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/filter_confirmation.R +\name{min_cond} +\alias{min_cond} +\title{Minimum Value on a Subset} +\usage{ +min_cond(var, cond) +} +\arguments{ +\item{var}{A vector} + +\item{cond}{A condition} +} +\description{ +The function derives the minimum value of a vector/column on a subset of +entries/observations. +} +\examples{ + +library(tibble) +library(dplyr) +library(admiral) +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", +) + +# In oncology setting, when needing to check the first time a patient had +# a Complete Response (CR) to compare to see if any Partial Response (PR) +# occurred after this add variable indicating if PR occurred after CR +group_by(data, USUBJID) \%>\% mutate( + first_cr_vis = min_cond(var = AVISITN, cond = AVALC == "CR"), + last_pr_vis = max_cond(var = AVISITN, cond = AVALC == "PR"), + pr_after_cr = last_pr_vis > first_cr_vis +) +} +\seealso{ +Utilities for Filtering Observations: +\code{\link{count_vals}()}, +\code{\link{filter_confirmation}()}, +\code{\link{filter_extreme}()}, +\code{\link{filter_relative}()}, +\code{\link{max_cond}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_fil} +\keyword{utils_fil} diff --git a/man/negate_vars.Rd b/man/negate_vars.Rd deleted file mode 100644 index bf0bc98605..0000000000 --- a/man/negate_vars.Rd +++ /dev/null @@ -1,28 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/user_utils.R -\name{negate_vars} -\alias{negate_vars} -\title{Negate List of Variables} -\usage{ -negate_vars(vars = NULL) -} -\arguments{ -\item{vars}{List of variables created by \code{vars()}} -} -\value{ -A list of \code{quosures} -} -\description{ -The function adds a minus sign as prefix to each variable. -} -\details{ -This is useful if a list of variables should be removed from a dataset, -e.g., \code{select(!!!negate_vars(by_vars))} removes all by variables. -} -\examples{ -negate_vars(vars(USUBJID, STUDYID)) -} -\author{ -Stefan Bundfuss -} -\keyword{user_utility} diff --git a/man/params.Rd b/man/params.Rd index 4e89902f25..5ca0a94103 100644 --- a/man/params.Rd +++ b/man/params.Rd @@ -68,8 +68,31 @@ call_derivation( } \seealso{ \code{\link[=call_derivation]{call_derivation()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Thomas Neitmann, Tracey Wang } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/print.adam_templates.Rd b/man/print.adam_templates.Rd new file mode 100644 index 0000000000..69c350ab1b --- /dev/null +++ b/man/print.adam_templates.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/user_helpers.R +\name{print.adam_templates} +\alias{print.adam_templates} +\title{Print \code{adam_templates} Objects} +\usage{ +\method{print}{adam_templates}(x, ...) +} +\arguments{ +\item{x}{A \code{adam_templates} object} + +\item{...}{Not used} +} +\value{ +No return value, called for side effects +} +\description{ +Print \code{adam_templates} Objects +} +\examples{ +templates <- list_all_templates() +print(templates) +} +\seealso{ +\code{\link[=list_all_templates]{list_all_templates()}} + +Other internal: +\code{\link{admiral}}, +\code{\link{print.tte_source}()} +} +\author{ +Thomas Neitmann +} +\concept{internal} +\keyword{internal} diff --git a/man/print.derivation_slice.Rd b/man/print.derivation_slice.Rd index c64ffd4bd2..be231a5295 100644 --- a/man/print.derivation_slice.Rd +++ b/man/print.derivation_slice.Rd @@ -22,4 +22,12 @@ print(death_event) } \seealso{ \code{\link[=derivation_slice]{derivation_slice()}} + +Higher Order Functions: +\code{\link{call_derivation}()}, +\code{\link{derivation_slice}()}, +\code{\link{restrict_derivation}()}, +\code{\link{slice_derivation}()} } +\concept{high_order_function} +\keyword{high_order_function} diff --git a/man/print.tte_source.Rd b/man/print.tte_source.Rd index 335154e326..285618a80e 100644 --- a/man/print.tte_source.Rd +++ b/man/print.tte_source.Rd @@ -22,4 +22,13 @@ print(death_event) } \seealso{ \code{\link[=tte_source]{tte_source()}}, \code{\link[=censor_source]{censor_source()}}, \code{\link[=event_source]{event_source()}} + +Other internal: +\code{\link{admiral}}, +\code{\link{print.adam_templates}()} +} +\author{ +Thomas Neitmann } +\concept{internal} +\keyword{internal} diff --git a/man/queries.Rd b/man/queries.Rd index 62506f82ef..f7d637cbc3 100644 --- a/man/queries.Rd +++ b/man/queries.Rd @@ -7,10 +7,21 @@ \format{ An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 15 rows and 8 columns. } +\source{ +An example of standard query dataset to be used in deriving variables in ADAE and ADCM +} \usage{ queries } \description{ -An example of standard query dataset to be used in deriving variables in ADAE and ADCM +Queries Dataset +} +\seealso{ +Other datasets: +\code{\link{admiral_adsl}}, +\code{\link{atoxgr_criteria_ctcv4}}, +\code{\link{ex_single}}, +\code{\link{queries_mh}} } +\concept{datasets} \keyword{datasets} diff --git a/man/queries_mh.Rd b/man/queries_mh.Rd new file mode 100644 index 0000000000..68b6e3317a --- /dev/null +++ b/man/queries_mh.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{queries_mh} +\alias{queries_mh} +\title{Queries MH Dataset} +\format{ +An object of class \code{tbl_df} (inherits from \code{tbl}, \code{data.frame}) with 14 rows and 8 columns. +} +\source{ +An example of standard query MH dataset to be used in deriving variables in ADMH +} +\usage{ +queries_mh +} +\description{ +Queries MH Dataset +} +\seealso{ +Other datasets: +\code{\link{admiral_adsl}}, +\code{\link{atoxgr_criteria_ctcv4}}, +\code{\link{ex_single}}, +\code{\link{queries}} +} +\concept{datasets} +\keyword{datasets} diff --git a/man/query.Rd b/man/query.Rd index f260b4dacf..47228b7dcd 100644 --- a/man/query.Rd +++ b/man/query.Rd @@ -69,7 +69,7 @@ An object of class \code{query}. } \description{ A \code{query} object defines a query, e.g., a Standard MedDRA Query (SMQ), a -Standardised Drug Grouping (SDG), or a customized query (CQ). It is used +Standardized Drug Grouping (SDG), or a customized query (CQ). It is used as input to \code{create_query_data()}. } \examples{ @@ -130,8 +130,31 @@ query( } \seealso{ \code{\link[=create_query_data]{create_query_data()}}, \code{\link[=smq_select]{smq_select()}}, \code{\link[=sdg_select]{sdg_select()}}, \href{../articles/queries_dataset.html}{Queries Dataset Documentation} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/reexports.Rd b/man/reexports.Rd index af251f2467..06a6ec6b26 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -3,6 +3,9 @@ \docType{import} \name{reexports} \alias{reexports} +\alias{filter_if} +\alias{negate_vars} +\alias{vars2chr} \alias{vars} \alias{desc} \title{Objects exported from other packages} @@ -12,6 +15,8 @@ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ + \item{admiraldev}{\code{\link[admiraldev]{filter_if}}, \code{\link[admiraldev]{negate_vars}}, \code{\link[admiraldev]{vars2chr}}} + \item{dplyr}{\code{\link[dplyr]{desc}}, \code{\link[dplyr]{vars}}} }} diff --git a/man/restrict_derivation.Rd b/man/restrict_derivation.Rd index e433002d19..7633b04aad 100644 --- a/man/restrict_derivation.Rd +++ b/man/restrict_derivation.Rd @@ -65,9 +65,15 @@ restrict_derivation( } \seealso{ \code{\link[=params]{params()}} \code{\link[=slice_derivation]{slice_derivation()}} + +Higher Order Functions: +\code{\link{call_derivation}()}, +\code{\link{derivation_slice}()}, +\code{\link{print.derivation_slice}()}, +\code{\link{slice_derivation}()} } \author{ Stefan Bundfuss } +\concept{high_order_function} \keyword{high_order_function} -\keyword{user_utility} diff --git a/man/restrict_imputed_dtc_dt.Rd b/man/restrict_imputed_dtc_dt.Rd new file mode 100644 index 0000000000..c9fb490500 --- /dev/null +++ b/man/restrict_imputed_dtc_dt.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{restrict_imputed_dtc_dt} +\alias{restrict_imputed_dtc_dt} +\title{Restrict Imputed DTC date to Minimum/Maximum Dates} +\usage{ +restrict_imputed_dtc_dt(dtc, imputed_dtc, min_dates, max_dates) +} +\arguments{ +\item{dtc}{The \code{'--DTC'} date to impute + +A character date is expected in a format like \code{yyyy-mm-dd} or +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} + +\item{imputed_dtc}{The imputed DTC date} + +\item{min_dates}{Minimum dates + +A list of dates is expected. It is ensured that the imputed date is not +before any of the specified dates, e.g., that the imputed adverse event start +date is not before the first treatment date. Only dates which are in the +range of possible dates of the \code{dtc} value are considered. The possible dates +are defined by the missing parts of the \code{dtc} date (see example below). This +ensures that the non-missing parts of the \code{dtc} date are not changed. +A date or date-time object is expected. +For example + +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" +) +}\if{html}{\out{
}} + +returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} +range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore +\code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would +have changed the month although it is not missing (in the \code{dtc} date).} + +\item{max_dates}{Maximum dates + +A list of dates is expected. It is ensured that the imputed date is not after +any of the specified dates, e.g., that the imputed date is not after the data +cut off date. Only dates which are in the range of possible dates are +considered. A date or date-time object is expected.} +} +\value{ +\itemize{ +\item The last of the minimum dates (\code{min_dates}) which are in the range of the +partial DTC date (\code{dtc}) +\item The first of the maximum dates (\code{max_dates}) which are in the range of the +partial DTC date (\code{dtc}) +\item \code{imputed_dtc} if the partial DTC date (\code{dtc}) is not in range of any of +the minimum or maximum dates. +} +} +\description{ +Restrict Imputed DTC date to Minimum/Maximum Dates +} +\seealso{ +\code{\link[=impute_dtc_dtm]{impute_dtc_dtm()}}, \code{\link[=impute_dtc_dt]{impute_dtc_dt()}} + +Utilities used for date imputation: +\code{\link{dt_level}()}, +\code{\link{dtm_level}()}, +\code{\link{get_imputation_target_date}()}, +\code{\link{get_imputation_target_time}()}, +\code{\link{get_partialdatetime}()}, +\code{\link{restrict_imputed_dtc_dtm}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/restrict_imputed_dtc_dtm.Rd b/man/restrict_imputed_dtc_dtm.Rd new file mode 100644 index 0000000000..df742f65ba --- /dev/null +++ b/man/restrict_imputed_dtc_dtm.Rd @@ -0,0 +1,87 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/derive_date_vars.R +\name{restrict_imputed_dtc_dtm} +\alias{restrict_imputed_dtc_dtm} +\title{Restrict Imputed DTC date to Minimum/Maximum Dates} +\usage{ +restrict_imputed_dtc_dtm(dtc, imputed_dtc, min_dates, max_dates) +} +\arguments{ +\item{dtc}{The \code{'--DTC'} date to impute + +A character date is expected in a format like \code{yyyy-mm-dd} or +\code{yyyy-mm-ddThh:mm:ss}. Trailing components can be omitted and \code{-} is a +valid "missing" value for any component.} + +\item{imputed_dtc}{The imputed DTC date} + +\item{min_dates}{Minimum dates + +A list of dates is expected. It is ensured that the imputed date is not +before any of the specified dates, e.g., that the imputed adverse event start +date is not before the first treatment date. Only dates which are in the +range of possible dates of the \code{dtc} value are considered. The possible dates +are defined by the missing parts of the \code{dtc} date (see example below). This +ensures that the non-missing parts of the \code{dtc} date are not changed. +A date or date-time object is expected. +For example + +\if{html}{\out{
}}\preformatted{impute_dtc_dtm( + "2020-11", + min_dates = list( + ymd_hms("2020-12-06T12:12:12"), + ymd_hms("2020-11-11T11:11:11") + ), + highest_imputation = "M" +) +}\if{html}{\out{
}} + +returns \code{"2020-11-11T11:11:11"} because the possible dates for \code{"2020-11"} +range from \code{"2020-11-01T00:00:00"} to \code{"2020-11-30T23:59:59"}. Therefore +\code{"2020-12-06T12:12:12"} is ignored. Returning \code{"2020-12-06T12:12:12"} would +have changed the month although it is not missing (in the \code{dtc} date). + +For date variables (not datetime) in the list the time is imputed to +\code{"00:00:00"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} + +\item{max_dates}{Maximum dates + +A list of dates is expected. It is ensured that the imputed date is not after +any of the specified dates, e.g., that the imputed date is not after the data +cut off date. Only dates which are in the range of possible dates are +considered. A date or date-time object is expected. + +For date variables (not datetime) in the list the time is imputed to +\code{"23:59:59"}. Specifying date variables makes sense only if the date is +imputed. If only time is imputed, date variables do not affect the result.} +} +\value{ +\itemize{ +\item The last of the minimum dates (\code{min_dates}) which are in the range of the +partial DTC date (\code{dtc}) +\item The first of the maximum dates (\code{max_dates}) which are in the range of the +partial DTC date (\code{dtc}) +\item \code{imputed_dtc} if the partial DTC date (\code{dtc}) is not in range of any of +the minimum or maximum dates. +} +} +\description{ +Restrict Imputed DTC date to Minimum/Maximum Dates +} +\seealso{ +\code{\link[=impute_dtc_dtm]{impute_dtc_dtm()}}, \code{\link[=impute_dtc_dt]{impute_dtc_dt()}} + +Utilities used for date imputation: +\code{\link{dt_level}()}, +\code{\link{dtm_level}()}, +\code{\link{get_imputation_target_date}()}, +\code{\link{get_imputation_target_time}()}, +\code{\link{get_partialdatetime}()}, +\code{\link{restrict_imputed_dtc_dt}()} +} +\author{ +Stefan Bundfuss +} +\concept{utils_impute} +\keyword{utils_impute} diff --git a/man/roxygen/meta.R b/man/roxygen/meta.R new file mode 100644 index 0000000000..db3d081fc2 --- /dev/null +++ b/man/roxygen/meta.R @@ -0,0 +1,23 @@ +list( + rd_family_title = list( + der_date_time = "Date/Time Derivation Functions that returns variable appended to dataset: ", + der_bds_gen = "General BDS Functions: ", + der_bds_findings = "BDS-Findings Functions that returns variable appended to dataset: ", + der_prm_bds_findings = "BDS-Findings Functions for adding Parameters/Records: ", + der_adsl = "ADSL Functions that returns variable appended to dataset: ", + der_tte = "TTE Functions that returns variable appended to dataset:", + der_prm_tte = "TTE Functions for adding Parameters: ", + der_gen = "General Derivation Functions for all ADaMs that returns variable appended to dataset:", + der_occds = "OCCDS Functions: ", + com_date_time = "Date/Time Computation Functions that returns a vector: ", + com_bds_findings = "BDS-Findings Functions that returns a vector: ", + utils_ds_chk = "Utilities for Dataset Checking: ", + utils_fil = "Utilities for Filtering Observations: ", + utils_fmt = "Utilities for Formatting Observations: ", + utils_help = "Utilities used within Derivation functions: ", + utils_examples = "Utilities used for examples and template scripts: ", + utils_impute = "Utilities used for date imputation: ", + source_specifications = "Source Specifications: ", + high_order_function = "Higher Order Functions: " + ) +) diff --git a/man/sdg_select.Rd b/man/sdg_select.Rd index c848103af1..28796de2b7 100644 --- a/man/sdg_select.Rd +++ b/man/sdg_select.Rd @@ -24,8 +24,31 @@ Exactly one \code{name} or \code{id} must be specified. } \seealso{ \code{\link[=create_query_data]{create_query_data()}}, \code{\link[=query]{query()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/signal_duplicate_records.Rd b/man/signal_duplicate_records.Rd index 18d1e37f9b..409f0fd50d 100644 --- a/man/signal_duplicate_records.Rd +++ b/man/signal_duplicate_records.Rd @@ -37,7 +37,14 @@ adsl <- rbind(admiral_adsl[1L, ], admiral_adsl) signal_duplicate_records(adsl, vars(USUBJID), cnd_type = "message") } +\seealso{ +Utilities used within Derivation functions: +\code{\link{call_user_fun}()}, +\code{\link{extract_unit}()}, +\code{\link{get_not_mapped}()} +} \author{ Thomas Neitmann } -\keyword{dev_utility} +\concept{utils_help} +\keyword{utils_help} diff --git a/man/slice_derivation.Rd b/man/slice_derivation.Rd index b3d99f2186..e665a310cf 100644 --- a/man/slice_derivation.Rd +++ b/man/slice_derivation.Rd @@ -75,9 +75,15 @@ slice_derivation( } \seealso{ \code{\link[=params]{params()}} \code{\link[=restrict_derivation]{restrict_derivation()}} + +Higher Order Functions: +\code{\link{call_derivation}()}, +\code{\link{derivation_slice}()}, +\code{\link{print.derivation_slice}()}, +\code{\link{restrict_derivation}()} } \author{ Stefan Bundfuss } +\concept{high_order_function} \keyword{high_order_function} -\keyword{user_utility} diff --git a/man/smq_select.Rd b/man/smq_select.Rd index 6e06e3f9ed..716d17c0a6 100644 --- a/man/smq_select.Rd +++ b/man/smq_select.Rd @@ -29,8 +29,31 @@ Exactly one of \code{name} or \code{id} must be specified. } \seealso{ \code{\link[=create_query_data]{create_query_data()}}, \code{\link[=query]{query()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} \keyword{source_specifications} diff --git a/man/suppress_warning.Rd b/man/suppress_warning.Rd deleted file mode 100644 index c0d304b9e0..0000000000 --- a/man/suppress_warning.Rd +++ /dev/null @@ -1,46 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/warnings.R -\name{suppress_warning} -\alias{suppress_warning} -\title{Suppress Specific Warnings} -\usage{ -suppress_warning(expr, regexpr) -} -\arguments{ -\item{expr}{Expression to be executed} - -\item{regexpr}{Regular expression matching warnings to suppress} -} -\value{ -Return value of the expression -} -\description{ -Suppress certain warnings issued by an expression. -} -\details{ -All warnings which are issued by the expression and match the regular expression -are suppressed. -} -\examples{ -library(dplyr, warn.conflicts = FALSE) -library(admiral.test) -data(admiral_adsl) -data(admiral_vs) - -# Remove label -attr(admiral_vs$USUBJID, "label") <- NULL - -left_join(admiral_adsl, admiral_vs, by = "USUBJID") - -suppress_warning( - left_join(admiral_adsl, admiral_vs, by = "USUBJID"), - "^Column `USUBJID` has different attributes on LHS and RHS of join$" -) -} -\author{ -\itemize{ -\item Thomas Neitmann -\item Stefan Bundfuss -} -} -\keyword{warning} diff --git a/man/tte_source.Rd b/man/tte_source.Rd index 77564a51b9..59f0f3c943 100644 --- a/man/tte_source.Rd +++ b/man/tte_source.Rd @@ -16,8 +16,7 @@ of \code{derive_param_tte()}.} \code{dataset} which are events or possible censoring time points.} \item{date}{A variable providing the date of the event or censoring. A date, -a datetime, or a character variable containing ISO 8601 dates can be -specified. An unquoted symbol is expected. +or a datetime can be specified. An unquoted symbol is expected. Refer to \code{derive_vars_dt()} to impute and derive a date from a date character vector to a date object.} @@ -39,8 +38,31 @@ The \code{tte_source} object is used to define events and possible censorings. } \seealso{ \code{\link[=derive_param_tte]{derive_param_tte()}}, \code{\link[=censor_source]{censor_source()}}, \code{\link[=event_source]{event_source()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } -\keyword{dev_utility} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/tte_source_objects.Rd b/man/tte_source_objects.Rd index abbaec94ce..66e0e82106 100644 --- a/man/tte_source_objects.Rd +++ b/man/tte_source_objects.Rd @@ -15,31 +15,6 @@ \alias{ae_sev_event} \alias{ae_wd_event} \title{Pre-Defined Time-to-Event Source Objects} -\format{ -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{censor_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. - -An object of class \code{event_source} (inherits from \code{tte_source}, \code{source}, \code{list}) of length 5. -} \usage{ death_event @@ -66,13 +41,46 @@ ae_sev_event ae_wd_event } \description{ -These pre-defined \code{tte_source} objects can be used as input to \code{derive_param_tte()}. +These pre-defined \code{tte_source} objects can be used as input to \code{\link[=derive_param_tte]{derive_param_tte()}}. } \details{ To see the definition of the various objects simply print the object in the -R console, e.g. \code{print(death_event)}. +R console, e.g. \code{print(death_event)}. For details of how to use these objects +please refer to \code{\link[=derive_param_tte]{derive_param_tte()}}. +} +\examples{ +# This shows the definition of all pre-defined `tte_source` objects that ship +# with {admiral} +for (obj in list_tte_source_objects()$object) { + cat(obj, "\n") + print(get(obj)) + cat("\n") +} } \seealso{ \code{\link[=derive_param_tte]{derive_param_tte()}}, \code{\link[=tte_source]{tte_source()}}, \code{\link[=event_source]{event_source()}}, \code{\link[=censor_source]{censor_source()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } -\keyword{tte_source} +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/use_ad_template.Rd b/man/use_ad_template.Rd index 70ec7be700..5385aea2b7 100644 --- a/man/use_ad_template.Rd +++ b/man/use_ad_template.Rd @@ -7,15 +7,18 @@ use_ad_template( adam_name = "adsl", save_path = paste0("./", adam_name, ".R"), + package = "admiral", overwrite = FALSE, open = interactive() ) } \arguments{ -\item{adam_name}{An ADaM dataset name. You can use any of the available dataset name ADAE, ADCM, ADEG, ADEX, ADLB, ADPP, ADSL, ADVS, and the dataset name is case-insensitive. The default dataset name is ADSL.} +\item{adam_name}{An ADaM dataset name. You can use any of the available dataset name ADAE, ADCM, ADEG, ADEX, ADLB, ADMH, ADPP, ADSL, ADVS, and the dataset name is case-insensitive. The default dataset name is ADSL.} \item{save_path}{Path to save the script.} +\item{package}{The R package in which to look for templates. By default \code{"admiral"}.} + \item{overwrite}{Whether to overwrite an existing file named \code{save_path}.} \item{open}{Whether to open the script right away.} @@ -34,7 +37,12 @@ if (interactive()) { use_ad_template("adsl") } } +\seealso{ +Utilities used for examples and template scripts: +\code{\link{list_all_templates}()} +} \author{ Shimeng Huang, Thomas Neitmann } -\keyword{user_utility} +\concept{utils_examples} +\keyword{utils_examples} diff --git a/man/validate_query.Rd b/man/validate_query.Rd index b12277ea69..c62bc7b6d4 100644 --- a/man/validate_query.Rd +++ b/man/validate_query.Rd @@ -17,7 +17,31 @@ Validate an object is indeed a \code{query} object } \seealso{ \code{\link[=query]{query()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_sdg_select}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/validate_sdg_select.Rd b/man/validate_sdg_select.Rd index 71610eef43..b45e0bb83b 100644 --- a/man/validate_sdg_select.Rd +++ b/man/validate_sdg_select.Rd @@ -17,7 +17,31 @@ Validate an object is indeed a \code{sdg_select} object } \seealso{ \code{\link[=sdg_select]{sdg_select()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_smq_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/validate_smq_select.Rd b/man/validate_smq_select.Rd index a86907a722..cec3da0a0c 100644 --- a/man/validate_smq_select.Rd +++ b/man/validate_smq_select.Rd @@ -17,7 +17,31 @@ Validate an object is indeed a \code{smq_select} object } \seealso{ \code{\link[=smq_select]{smq_select()}} + +Source Specifications: +\code{\link{assert_db_requirements}()}, +\code{\link{assert_terms}()}, +\code{\link{assert_valid_queries}()}, +\code{\link{censor_source}()}, +\code{\link{date_source}()}, +\code{\link{death_event}}, +\code{\link{derive_var_dthcaus}()}, +\code{\link{event_source}()}, +\code{\link{extend_source_datasets}()}, +\code{\link{filter_date_sources}()}, +\code{\link{format.sdg_select}()}, +\code{\link{format.smq_select}()}, +\code{\link{list_tte_source_objects}()}, +\code{\link{params}()}, +\code{\link{query}()}, +\code{\link{sdg_select}()}, +\code{\link{smq_select}()}, +\code{\link{tte_source}()}, +\code{\link{validate_query}()}, +\code{\link{validate_sdg_select}()} } \author{ Stefan Bundfuss } +\concept{source_specifications} +\keyword{source_specifications} diff --git a/man/vars2chr.Rd b/man/vars2chr.Rd deleted file mode 100644 index c9c2a0af35..0000000000 --- a/man/vars2chr.Rd +++ /dev/null @@ -1,24 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/user_utils.R -\name{vars2chr} -\alias{vars2chr} -\title{Turn a List of Quosures into a Character Vector} -\usage{ -vars2chr(quosures) -} -\arguments{ -\item{quosures}{A \code{list} of \code{quosures} created using \code{\link[=vars]{vars()}}} -} -\value{ -A character vector -} -\description{ -Turn a List of Quosures into a Character Vector -} -\examples{ -vars2chr(vars(USUBJID, AVAL)) -} -\author{ -Thomas Neitmann -} -\keyword{user_utility} diff --git a/man/warn_if_inconsistent_list.Rd b/man/warn_if_inconsistent_list.Rd deleted file mode 100644 index db9ed03de2..0000000000 --- a/man/warn_if_inconsistent_list.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/warnings.R -\name{warn_if_inconsistent_list} -\alias{warn_if_inconsistent_list} -\title{Warn If Two Lists are Inconsistent} -\usage{ -warn_if_inconsistent_list(base, compare, list_name, i = 2) -} -\arguments{ -\item{base}{A named list} - -\item{compare}{A named list} - -\item{list_name}{A string -the name of the list} - -\item{i}{the index id to compare the 2 lists} -} -\value{ -a \code{warning} if the 2 lists have different names or length -} -\description{ -Checks if two list inputs have the same names and same number of elements and -issues a warning otherwise. -} -\examples{ -# no warning -warn_if_inconsistent_list( - base = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), - compare = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), - list_name = "Test" -) -# warning -warn_if_inconsistent_list( - base = vars(DTHDOM = "DM", DTHSEQ = DMSEQ, DTHVAR = "text"), - compare = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), - list_name = "Test" -) -} -\author{ -Samia Kabi -} -\keyword{warning} diff --git a/man/warn_if_invalid_dtc.Rd b/man/warn_if_invalid_dtc.Rd deleted file mode 100644 index 1fd6a102dc..0000000000 --- a/man/warn_if_invalid_dtc.Rd +++ /dev/null @@ -1,32 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/warnings.R -\name{warn_if_invalid_dtc} -\alias{warn_if_invalid_dtc} -\title{Warn If a Vector Contains Unknown Datetime Format} -\usage{ -warn_if_invalid_dtc(dtc, is_valid = is_valid_dtc(dtc)) -} -\arguments{ -\item{dtc}{a character vector containing the dates} - -\item{is_valid}{a logical vector indicating whether elements in \code{dtc} are valid} -} -\value{ -No return value, called for side effects -} -\description{ -Warn if the vector contains unknown datetime format such as -"2003-12-15T-:15:18", "2003-12-15T13:-:19","--12-15","-----T07:15" -} -\examples{ - -## No warning as `dtc` is a valid date format -warn_if_invalid_dtc(dtc = "2021-04-06") - -## Issues a warning -warn_if_invalid_dtc(dtc = "2021-04-06T-:30:30") -} -\author{ -Samia Kabi -} -\keyword{warning} diff --git a/man/warn_if_vars_exist.Rd b/man/warn_if_vars_exist.Rd deleted file mode 100644 index 68e092abb8..0000000000 --- a/man/warn_if_vars_exist.Rd +++ /dev/null @@ -1,33 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/warnings.R -\name{warn_if_vars_exist} -\alias{warn_if_vars_exist} -\title{Warn If a Variable Already Exists} -\usage{ -warn_if_vars_exist(dataset, vars) -} -\arguments{ -\item{dataset}{A \code{data.frame}} - -\item{vars}{\code{character} vector of columns to check for in \code{dataset}} -} -\value{ -No return value, called for side effects -} -\description{ -Warn if a variable already exists inside a dataset -} -\examples{ -library(admiral.test) -data(admiral_dm) - -## No warning as `AAGE` doesn't exist in `dm` -warn_if_vars_exist(admiral_dm, "AAGE") - -## Issues a warning -warn_if_vars_exist(admiral_dm, "ARM") -} -\author{ -Thomas Neitmann -} -\keyword{warning} diff --git a/man/yn_to_numeric.Rd b/man/yn_to_numeric.Rd index a7fa0e4a57..e3f5b43ce8 100644 --- a/man/yn_to_numeric.Rd +++ b/man/yn_to_numeric.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/derive_param_exist_flag.R +% Please edit documentation in R/user_utils.R \name{yn_to_numeric} \alias{yn_to_numeric} \title{Map \code{"Y"} and \code{"N"} to Numeric Values} @@ -19,7 +19,14 @@ Map \code{"Y"} and \code{"N"} to numeric values. yn_to_numeric(c("Y", "N", NA_character_)) } +\seealso{ +Utilities for Formatting Observations: +\code{\link{convert_blanks_to_na}()}, +\code{\link{format_eoxxstt_default}()}, +\code{\link{format_reason_default}()} +} \author{ Stefan Bundfuss } -\keyword{user_utility} +\concept{utils_fmt} +\keyword{utils_fmt} diff --git a/renv.lock b/renv.lock index 8932865594..0b1c785214 100644 --- a/renv.lock +++ b/renv.lock @@ -65,17 +65,23 @@ "Repository": "CRAN", "Hash": "f3ca785924863b0e4c8cb23b6a5c75a1" }, - "admiral.test": { + "admiral.test": { "Package": "admiral.test", "Version": "0.2.0", "Source": "GitHub", "RemoteType": "github", "RemoteHost": "api.github.com", - "RemoteRepo": "admiral.test", "RemoteUsername": "pharmaverse", + "RemoteRepo": "admiral.test", "RemoteRef": "devel", - "RemoteSha": "4f3bdd59a5a01bd0314a42e1d3ef32ca4d1745fe", - "Hash": "8378c5762f6be5a61854e880d6a8a223" + "RemoteSha": "b1f4a44a76410e5ef285f607e038f4c5b9f02dc9", + "Hash": "c6002d9280db2b0c00c49320fe00684b" + }, + "admiraldev": { + "Package": "admiraldev", + "Version": "0.1.0", + "Source": "Repository", + "Repository": "CRAN" }, "askpass": { "Package": "askpass", @@ -133,6 +139,13 @@ "Repository": "CRAN", "Hash": "461aa75a11ce2400245190ef5d3995df" }, + "cellranger": { + "Package": "cellranger", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "f61dbaec772ccd2e17705c1e872e9e7c" + }, "cli": { "Package": "cli", "Version": "3.3.0", @@ -170,10 +183,10 @@ }, "cpp11": { "Package": "cpp11", - "Version": "0.3.1", + "Version": "0.4.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "e02edab2bc389c5e4b12949b13df44f2" + "Hash": "fa53ce256cd280f468c080a58ea5ba8c" }, "crayon": { "Package": "crayon", @@ -357,13 +370,6 @@ "Repository": "CRAN", "Hash": "41bace23583fbc25089edae324de2dc3" }, - "httpuv": { - "Package": "httpuv", - "Version": "1.5.2", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "f793dad2c9ae14fbb1d22f16f23f8326" - }, "httr": { "Package": "httr", "Version": "1.4.2", @@ -462,13 +468,6 @@ "Repository": "CRAN", "Hash": "e87a35ec73b157552814869f45a63aa3" }, - "miniUI": { - "Package": "miniUI", - "Version": "0.1.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "fec5f52652d60615fdb3957b3d74324a" - }, "openssl": { "Package": "openssl", "Version": "1.4.1", @@ -539,6 +538,13 @@ "Repository": "CRAN", "Hash": "0cbca2bc4d16525d009c4dbba156b37c" }, + "progress": { + "Package": "progress", + "Version": "1.2.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "14dc9f7a3c91ebb14ec5bb9208a07061" + }, "promises": { "Package": "promises", "Version": "1.1.0", @@ -581,6 +587,20 @@ "Repository": "CRAN", "Hash": "ed95895886dab6d2a584da45503555da" }, + "readxl": { + "Package": "readxl", + "Version": "1.4.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "170c35f745563bb307e963bde0197e4f" + }, + "rematch": { + "Package": "rematch", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "c66b930d20bb6d858cd18e1cebcfae5c" + }, "rematch2": { "Package": "rematch2", "Version": "2.1.0", @@ -665,20 +685,6 @@ "Repository": "CRAN", "Hash": "308013098befe37484df72c39cf90d6e" }, - "shiny": { - "Package": "shiny", - "Version": "1.4.0", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "6ca23724bb2c804c1d0b3db4862a39c7" - }, - "sourcetools": { - "Package": "sourcetools", - "Version": "0.1.7", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "947e4e02a79effa5d512473e10f41797" - }, "spelling": { "Package": "spelling", "Version": "2.1", @@ -686,6 +692,18 @@ "Repository": "CRAN", "Hash": "b3a5ecc3351f41eb30ef87f65cbff390" }, + "staged.dependencies": { + "Package": "staged.dependencies", + "Version": "0.2.7", + "Source": "GitHub", + "RemoteType": "github", + "RemoteHost": "api.github.com", + "RemoteUsername": "openpharma", + "RemoteRepo": "staged.dependencies", + "RemoteRef": "v0.2.7", + "RemoteSha": "669f45a95d8772899551ad51fc3b38a3b5a1056a", + "Hash": "972f56f7ffa72007c91794e68bfb7d8a" + }, "stringi": { "Package": "stringi", "Version": "1.4.6", @@ -833,13 +851,6 @@ "Repository": "CRAN", "Hash": "6c85f015dee9cc7710ddd20f86881f58" }, - "xtable": { - "Package": "xtable", - "Version": "1.8-4", - "Source": "Repository", - "Repository": "CRAN", - "Hash": "b8acdf8af494d9ec19ccb2481a9b11c2" - }, "yaml": { "Package": "yaml", "Version": "2.2.1", diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml new file mode 100644 index 0000000000..0a29129f89 --- /dev/null +++ b/staged_dependencies.yaml @@ -0,0 +1,12 @@ +--- +upstream_repos: +- repo: pharmaverse/admiral.test + host: https://github.com +- repo: pharmaverse/admiraldev + host: https://github.com + +downstream_repos: + +current_repo: + repo: pharmaverse/admiral + host: https://github.com diff --git a/tests/testthat/test-assertions.R b/tests/testthat/test-assertions.R deleted file mode 100644 index 593e69596e..0000000000 --- a/tests/testthat/test-assertions.R +++ /dev/null @@ -1,114 +0,0 @@ -library(admiral.test) - -test_that("Test 1 : `assert_has_variables` an error is thrown if a required - variable is missing", { - data(admiral_dm) - - expect_error( - assert_has_variables(admiral_dm, "TRT01P"), - "Required variable `TRT01P` is missing." - ) -}) - -test_that("Test 2 : `assert_has_variables` no error is thrown if a required - variable exists", { - data(admiral_dm) - - expect_error(assert_has_variables(admiral_dm, "USUBJID"), NA) -}) - -test_that("Test 3 : `assert_filter_cond` works as expected", { - fc <- quo(AGE == 64) - expect_identical( - assert_filter_cond(fc), - fc - ) - - fc <- quo() - expect_error( - assert_filter_cond(arg = fc), - "Argument `fc` is missing, with no default" - ) - - expect_identical( - assert_filter_cond(arg = fc, optional = TRUE), - fc - ) - - fc <- quo("string") - expect_error( - assert_filter_cond(arg = fc), - "`fc` must be a filter condition but is `\"string\"`" - ) -}) - -test_that("Test 4 : is_valid_sec_min works as expected", { - expect_true(is_valid_sec_min(59)) -}) - -test_that("Test 5 : is_valid_hour works as expected", { - expect_true(is_valid_hour(23)) -}) - -test_that("Test 6 : `assert_data_frame` throws an error if not a dataframe", { - example_fun <- function(dataset) { - assert_data_frame(dataset, required_vars = vars(STUDYID, USUBJID)) - } - expect_error( - example_fun(c(1, 2, 3)) - ) -}) - -test_that("Test 7 : `assert_data_frame` throws an error if dataframe is grouped", { - example_fun <- function(dataset) { - assert_data_frame(dataset, required_vars = vars(STUDYID, USUBJID)) - } - - admiral_dm <- admiral_dm %>% group_by(ARMCD) - - expect_error( - example_fun(admiral_dm) - ) -}) - -test_that("Test 8 : `assert_character_scalar` throws an error if not a character scaler string", { - example_fun2 <- function(msg_type) { - msg_type <- assert_character_scalar(msg_type, - values = c("warning", "error"), case_sensitive = FALSE - ) - - if (msg_type == "warning") { - print("A warning was requested.") - } - } - expect_error(example_fun2(2)) -}) - -test_that("Test 9 : `assert_character_scalar` throws an error if input is a vector", { - example_fun2 <- function(msg_type) { - msg_type <- assert_character_scalar(msg_type, - values = c("warning", "error"), case_sensitive = FALSE - ) - - if (msg_type == "warning") { - print("A warning was requested.") - } - } - expect_error(example_fun2(c("admiral", "admiralonco"))) -}) - -test_that("Test 10 : `quo_not_missing` returns TRUE if no missing argument", { - test_fun <- function(x) { - x <- rlang::enquo(x) - assertthat::assert_that(quo_not_missing(x)) - } - expect_true(test_fun(my_variable)) -}) - -test_that("Test 11 : `quo_not_missing` throws and Error if missing argument", { - test_fun <- function(x) { - x <- rlang::enquo(x) - assertthat::assert_that(quo_not_missing(x)) - } - expect_error(test_fun()) # missing argument -> throws error -}) diff --git a/tests/testthat/test-compat_friendly_type.R b/tests/testthat/test-compat_friendly_type.R deleted file mode 100644 index 5c775ce2a3..0000000000 --- a/tests/testthat/test-compat_friendly_type.R +++ /dev/null @@ -1,37 +0,0 @@ -test_that("friendly_type_of() supports objects", { - expect_equal(friendly_type_of(mtcars), "a object") - expect_equal(friendly_type_of(quo(1)), "a object") -}) - -test_that("friendly_type_of() supports matrices and arrays (#141)", { - expect_equal(friendly_type_of(list()), "an empty list") - expect_equal(friendly_type_of(matrix(list(1, 2))), "a list matrix") - expect_equal(friendly_type_of(array(list(1, 2, 3), dim = 1:3)), "a list array") - - expect_equal(friendly_type_of(matrix(1:3)), "an integer matrix") - expect_equal(friendly_type_of(array(1:3, dim = 1:3)), "an integer array") - - expect_equal(friendly_type_of(matrix(letters)), "a character matrix") - expect_equal(friendly_type_of(array(letters[1:3], dim = 1:3)), "a character array") -}) - - -test_that("friendly_type_of() handles scalars", { - expect_equal(friendly_type_of(NA), "`NA`") - - expect_equal(friendly_type_of(TRUE), "`TRUE`") - expect_equal(friendly_type_of(FALSE), "`FALSE`") - - expect_equal(friendly_type_of(1L), "an integer") - expect_equal(friendly_type_of(1.0), "a number") - expect_equal(friendly_type_of(1i), "a complex number") - expect_equal(friendly_type_of(as.raw(1)), "a raw value") - - expect_equal(friendly_type_of("foo"), "a string") - expect_equal(friendly_type_of(""), "`\"\"`") - - expect_equal(friendly_type_of(list(1)), "a list") - - expect_equal(friendly_type_of(matrix(NA)), "a logical matrix") - expect_equal(friendly_type_of(matrix(1)), "a double matrix") -}) diff --git a/tests/testthat/test-compute_dtf.R b/tests/testthat/test-compute_dtf.R deleted file mode 100644 index fbd6bf25c6..0000000000 --- a/tests/testthat/test-compute_dtf.R +++ /dev/null @@ -1,27 +0,0 @@ -inputdtc <- c( - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) -inputdt <- c( - as.Date("2019-07-18"), - as.Date("2019-02-01"), - as.Date("2019-01-01"), - as.Date("2019-01-01") -) -test_that("compute DTF", { - expected_output <- c( - NA_character_, - "D", - "M", - "M" - ) - expect_equal( - compute_dtf( - dtc = inputdtc, - dt = inputdt - ), - expected_output - ) -}) diff --git a/tests/testthat/test-compute_duration.R b/tests/testthat/test-compute_duration.R index f0044dbc09..1fd49c0307 100644 --- a/tests/testthat/test-compute_duration.R +++ b/tests/testthat/test-compute_duration.R @@ -1,4 +1,4 @@ -test_that("default duration, i.e., relative day", { +test_that("compute_duration Test 1: Default duration, i.e., relative day", { expect_equal( compute_duration( ymd_hms("2020-12-06T15:00:00"), @@ -8,7 +8,7 @@ test_that("default duration, i.e., relative day", { ) }) -test_that("fractional duration", { +test_that("compute_duration Test 2: Fractional duration", { expect_equal( compute_duration( ymd_hms("2020-12-06T15:00:00"), @@ -20,7 +20,7 @@ test_that("fractional duration", { ) }) -test_that("age in years", { +test_that("compute_duration Test 3: Age in years", { expect_equal( compute_duration( ymd("1984-09-06"), @@ -33,7 +33,7 @@ test_that("age in years", { ) }) -test_that("age in months", { +test_that("compute_duration Test 4: Age in months", { expect_equal( compute_duration( ymd("1984-09-06"), @@ -46,7 +46,7 @@ test_that("age in months", { ) }) -test_that("age in weeks", { +test_that("compute_duration Test 5: Age in weeks", { expect_equal( compute_duration( ymd("2020-02-03"), @@ -58,3 +58,16 @@ test_that("age in weeks", { 3 ) }) + +test_that("compute_duration Test 6: Duration in hours", { + expect_equal( + compute_duration( + ymd_hms("2020-12-06T9:00:00"), + ymd_hms("2020-12-06T13:30:00"), + out_unit = "hours", + floor_in = FALSE, + add_one = FALSE + ), + 4.5 + ) +}) diff --git a/tests/testthat/test-compute_framingham.R b/tests/testthat/test-compute_framingham.R new file mode 100644 index 0000000000..c5bc844802 --- /dev/null +++ b/tests/testthat/test-compute_framingham.R @@ -0,0 +1,179 @@ +test_that("compute_framingham Test 1: Framingham Equation - Male, + Treated for Hypertension = N", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "M", + smokefl = "N", + diabetfl = "N", + trthypfl = "N" + ), + ((1 - (0.88936^exp((3.06117 * log(53)) + + (1.12370 * log(216.16)) + - (0.93263 * log(54.91)) + + (1.93303 * log(133)) + - 23.9802) + )) * 100) + ) +}) + +test_that("compute_framingham Test 2: Framingham Equation - Male, + Treated for Hypertension = Y", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "M", + smokefl = "N", + diabetfl = "N", + trthypfl = "Y" + ), + ((1 - (0.88936^exp((3.06117 * log(53)) + + (1.12370 * log(216.16)) + - (0.93263 * log(54.91)) + + (1.99881 * log(133)) + - 23.9802) + )) * 100) + ) +}) + +test_that("compute_framingham Test 3: Framingham Equation - Male, + Treated for Hypertension = N, Diabetic = Y", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "M", + smokefl = "N", + diabetfl = "Y", + trthypfl = "N" + ), + ((1 - (0.88936^exp((3.06117 * log(53)) + + (1.12370 * log(216.16)) + - (0.93263 * log(54.91)) + + (1.93303 * log(133)) + + 0.57367 + - 23.9802) + )) * 100) + ) +}) + +test_that("compute_framingham Test 4: Framingham Equation - Male, + Treated for Hypertension = N, Smoker = Y", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "M", + smokefl = "Y", + diabetfl = "N", + trthypfl = "N" + ), + ((1 - (0.88936^exp((3.06117 * log(53)) + + (1.12370 * log(216.16)) + - (0.93263 * log(54.91)) + + (1.93303 * log(133)) + + 0.65451 + - 23.9802) + )) * 100) + ) +}) + +test_that("compute_framingham Test 5: Framingham Equation - FeMale, + Treated for Hypertension = N", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "F", + smokefl = "N", + diabetfl = "N", + trthypfl = "N" + ), + ((1 - (0.95012^exp((2.32888 * log(53)) + + (1.20904 * log(216.16)) + - (.70833 * log(54.91)) + + (2.76157 * log(133)) + - 26.1931) + )) * 100) + ) +}) + +test_that("compute_framingham Test 6: Framingham Equation - FeMale, + Treated for Hypertension = Y", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "F", + smokefl = "N", + diabetfl = "N", + trthypfl = "Y" + ), + ((1 - (0.95012^exp((2.32888 * log(53)) + + (1.20904 * log(216.16)) + - (.70833 * log(54.91)) + + (2.82263 * log(133)) + - 26.1931) + )) * 100) + ) +}) + +test_that("compute_framingham Test 7: Framingham Equation - FeMale, + Treated for Hypertension = N, Diabetic = Y", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "F", + smokefl = "N", + diabetfl = "Y", + trthypfl = "N" + ), + ((1 - (0.95012^exp((2.32888 * log(53)) + + (1.20904 * log(216.16)) + - (.70833 * log(54.91)) + + (2.76157 * log(133)) + + 0.69154 + - 26.1931) + )) * 100) + ) +}) + +test_that("compute_framingham Test 8: Framingham Equation - FeMale, + Treated for Hypertension = N, Diabetic = Y", { + expect_equal( + compute_framingham( + sysbp = 133, + chol = 216.16, + cholhdl = 54.91, + age = 53, + sex = "F", + smokefl = "Y", + diabetfl = "N", + trthypfl = "N" + ), + ((1 - (0.95012^exp((2.32888 * log(53)) + + (1.20904 * log(216.16)) + - (.70833 * log(54.91)) + + (2.76157 * log(133)) + + 0.52873 + - 26.1931) + )) * 100) + ) +}) diff --git a/tests/testthat/test-compute_qual_imputation.R b/tests/testthat/test-compute_qual_imputation.R new file mode 100644 index 0000000000..e5e0e5c4a3 --- /dev/null +++ b/tests/testthat/test-compute_qual_imputation.R @@ -0,0 +1,109 @@ +test_that("compute_qual_imputation Test 1: Method 1 , > Qualifier Test", { + expect_equal(compute_qual_imputation(">40.1", imputation_type = 1), 40.1) +}) + +test_that("compute_qual_imputation Test 2: Method 1, >= Qualifier", { + expect_equal(compute_qual_imputation(">=40.1", imputation_type = 1), 40.1) +}) + +test_that("compute_qual_imputation Test 3: Method 1, > Qualifier", { + expect_equal(compute_qual_imputation(">40", imputation_type = 1), 40) +}) + +test_that("compute_qual_imputation Test 4: Method 1, >= Qualifier", { + expect_equal(compute_qual_imputation(">=40", imputation_type = 1), 40) +}) + +test_that("compute_qual_imputation Test 5: Method 1, < Qualifier", { + expect_equal(compute_qual_imputation("<40.1", imputation_type = 1), 40.1) +}) + +test_that("compute_qual_imputation Test 6: Method 1, <= Qualifier", { + expect_equal(compute_qual_imputation("<=40.1", imputation_type = 1), 40.1) +}) + +test_that("compute_qual_imputation Test 7: Method 1, < Qualifier", { + expect_equal(compute_qual_imputation("<40", imputation_type = 1), 40) +}) + +test_that("compute_qual_imputation Test 8: Method 1, <= Qualifier", { + expect_equal(compute_qual_imputation("<=40", imputation_type = 1), 40) +}) + +############### + +test_that("compute_qual_imputation Test 9: Method 1, > Qualifier, Factor=10", { + expect_equal(compute_qual_imputation(">40.1", imputation_type = 1, factor = 10), 50.1) +}) + +test_that("compute_qual_imputation Test 10: Method 1, >= Qualifier, Factor=10", { + expect_equal(compute_qual_imputation(">=40.1", imputation_type = 1, factor = 10), 40.1) +}) + +test_that("compute_qual_imputation Test 11: Method 1, > Qualifier, Factor=10", { + expect_equal(compute_qual_imputation(">40", imputation_type = 1, factor = 10), 50) +}) + +test_that("compute_qual_imputation Test 12: Method 1, >= Qualifier, Factor=10", { + expect_equal(compute_qual_imputation(">=40", imputation_type = 1, factor = 10), 40) +}) + +test_that("compute_qual_imputation Test 13: Method 1, < Qualifier, Factor=10", { + expect_equal(compute_qual_imputation("<40.1", imputation_type = 1, factor = 10), 30.1) +}) + +test_that("compute_qual_imputation Test 14: Method 1, <= Qualifier, Factor=10", { + expect_equal(compute_qual_imputation("<=40.1", imputation_type = 1, factor = 10), 40.1) +}) + +test_that("compute_qual_imputation Test 15: Method 1, < Qualifier, Factor=10", { + expect_equal(compute_qual_imputation("<40", imputation_type = 1, factor = 10), 30) +}) + +test_that("compute_qual_imputation Test 16: Method 1, <= Qualifier, Factor=10", { + expect_equal(compute_qual_imputation("<=40", imputation_type = 1, factor = 10), 40) +}) + +############### + +test_that("compute_qual_imputation Test 17: Method 2, > Qualifier", { + expect_equal(compute_qual_imputation(">40.1", imputation_type = 2), 40.2) +}) + +test_that("compute_qual_imputation Test 18: Method 2, >= Qualifier", { + expect_equal(compute_qual_imputation(">=40.1", imputation_type = 2), 40.1) +}) + +test_that("compute_qual_imputation Test 19: Method 2, > Qualifier", { + expect_equal(compute_qual_imputation(">40", imputation_type = 2), 41) +}) + +test_that("compute_qual_imputation Test 20: Method 2, >= Qualifier", { + expect_equal(compute_qual_imputation(">=40", imputation_type = 2), 40) +}) + +test_that("compute_qual_imputation Test 21: Method 2, < Qualifier", { + expect_equal(compute_qual_imputation("<40.1", imputation_type = 2), 40.0) +}) + +test_that("compute_qual_imputation Test 22: Method 2, <= Qualifier", { + expect_equal(compute_qual_imputation("<=40.1", imputation_type = 2), 40.1) +}) + +test_that("compute_qual_imputation Test 23: Method 2, < Qualifier", { + expect_equal(compute_qual_imputation("<40", imputation_type = 2), 39) +}) + +test_that("compute_qual_imputation Test 24: Method 2, <= Qualifier", { + expect_equal(compute_qual_imputation("<=40", imputation_type = 2), 40) +}) + +############ + +test_that("compute_qual_imputation Test 25: Method 2, No Qualifier", { + expect_equal(compute_qual_imputation("40", imputation_type = 2), 40) +}) + +test_that("compute_qual_imputation Test 26: Method 2, No Qualifier, Character Value", { + expect_equal(compute_qual_imputation("AB", imputation_type = 1), NA_real_) +}) diff --git a/tests/testthat/test-compute_tmf.R b/tests/testthat/test-compute_tmf.R deleted file mode 100644 index dfd163f286..0000000000 --- a/tests/testthat/test-compute_tmf.R +++ /dev/null @@ -1,37 +0,0 @@ -test_that("compute TMF", { - input_dtc <- c( - "2019-07-18T15:25:52", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019---07" - ) - input_dtm <- c( - as.POSIXct("2019-07-18T15:25:52"), - as.POSIXct("2019-07-18T15:25:00"), - as.POSIXct("2019-07-18T15:00:00"), - as.POSIXct("2019-07-18"), - as.POSIXct("2019-02-01"), - as.POSIXct("2019-01-01"), - as.POSIXct("2019-01-01") - ) - expected_output <- c( - NA_character_, - "S", - "M", - "H", - "H", - "H", - "H" - ) - - expect_equal( - compute_tmf( - dtc = input_dtc, - dtm = input_dtm - ), - expected_output - ) -}) diff --git a/tests/testthat/test-convert_date_to_dtm.R b/tests/testthat/test-convert_date_to_dtm.R deleted file mode 100644 index ab728d179d..0000000000 --- a/tests/testthat/test-convert_date_to_dtm.R +++ /dev/null @@ -1,27 +0,0 @@ -test_that("Convert a complete -- DTC into a date time object", { - expect_equal( - convert_date_to_dtm("2019-07-18T15:25:52"), - ymd_hms("2019-07-18T15:25:52") - ) -}) - -test_that("Impute incomplete -- DTC into a date time object", { - expect_equal( - convert_date_to_dtm("2019-07-18", time_imputation = "23:59:59"), - ymd_hms("2019-07-18T23:59:59") - ) -}) - -test_that("Convert -- DT into a date time object", { - expect_equal( - convert_date_to_dtm(as.Date("2019-07-18"), time_imputation = "23:59:59"), - ymd_hms("2019-07-18T23:59:59") - ) -}) - -test_that("Keep -- DTM as the original date time object", { - expect_equal( - convert_date_to_dtm(ymd_hms("2019-07-18T15:25:52"), time_imputation = "23:59:59"), - ymd_hms("2019-07-18T15:25:52") - ) -}) diff --git a/tests/testthat/test-convert_dtc_to_dt.R b/tests/testthat/test-convert_dtc_to_dt.R deleted file mode 100644 index a71557e83c..0000000000 --- a/tests/testthat/test-convert_dtc_to_dt.R +++ /dev/null @@ -1,15 +0,0 @@ -inputdtc <- c( - "2019-07-18T15:25:52", - "2019-07-18" -) - -test_that("Convert a complete -- DTC into a date object", { - expected_output <- c( - as.Date("2019-07-18"), - as.Date("2019-07-18") - ) - expect_equal( - convert_dtc_to_dt(dtc = inputdtc), - expected_output - ) -}) diff --git a/tests/testthat/test-convert_dtc_to_dtm.R b/tests/testthat/test-convert_dtc_to_dtm.R deleted file mode 100644 index c47d058f7c..0000000000 --- a/tests/testthat/test-convert_dtc_to_dtm.R +++ /dev/null @@ -1,6 +0,0 @@ -test_that("Convert a complete -- DTC into a date time object", { - expect_equal( - convert_dtc_to_dtm("2019-07-18T15:25:52"), - ymd_hms("2019-07-18T15:25:52") - ) -}) diff --git a/tests/testthat/test-create_single_dose_dataset.R b/tests/testthat/test-create_single_dose_dataset.R index 83d91ea3f7..09f622b334 100644 --- a/tests/testthat/test-create_single_dose_dataset.R +++ b/tests/testthat/test-create_single_dose_dataset.R @@ -1,18 +1,33 @@ test_that("create_single_dose_dataset works as expected for Q#/EVERY # cases", { input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "Q2D", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-03"), - "P01", "Q3D", lubridate::ymd("2021-01-08"), lubridate::ymd("2021-01-12"), - "P01", "EVERY 2 WEEKS", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-29") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "Q2D", ymd("2021-01-01"), ymd_hms("2021-01-01 10:30:00"), + ymd("2021-01-07"), ymd_hms("2021-01-07 11:30:00"), + "P01", "Q3D", ymd("2021-01-08"), ymd_hms("2021-01-08 12:00:00"), + ymd("2021-01-14"), ymd_hms("2021-01-14 14:00:00"), + "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), ymd_hms("2021-01-15 09:57:00"), + ymd("2021-01-29"), ymd_hms("2021-01-29 10:57:00") ) expected_output <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-01"), - "P01", "ONCE", lubridate::ymd("2021-01-03"), lubridate::ymd("2021-01-03"), - "P01", "ONCE", lubridate::ymd("2021-01-08"), lubridate::ymd("2021-01-08"), - "P01", "ONCE", lubridate::ymd("2021-01-11"), lubridate::ymd("2021-01-11"), - "P01", "ONCE", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-15"), - "P01", "ONCE", lubridate::ymd("2021-01-29"), lubridate::ymd("2021-01-29") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:30:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 11:30:00"), + "P01", "ONCE", lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 10:30:00"), + lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 11:30:00"), + "P01", "ONCE", lubridate::ymd("2021-01-05"), lubridate::ymd_hms("2021-01-05 10:30:00"), + lubridate::ymd("2021-01-05"), lubridate::ymd_hms("2021-01-05 11:30:00"), + "P01", "ONCE", lubridate::ymd("2021-01-07"), lubridate::ymd_hms("2021-01-07 10:30:00"), + lubridate::ymd("2021-01-07"), lubridate::ymd_hms("2021-01-07 11:30:00"), + "P01", "ONCE", lubridate::ymd("2021-01-08"), lubridate::ymd_hms("2021-01-08 12:00:00"), + lubridate::ymd("2021-01-08"), lubridate::ymd_hms("2021-01-08 14:00:00"), + "P01", "ONCE", lubridate::ymd("2021-01-11"), lubridate::ymd_hms("2021-01-11 12:00:00"), + lubridate::ymd("2021-01-11"), lubridate::ymd_hms("2021-01-11 14:00:00"), + "P01", "ONCE", lubridate::ymd("2021-01-14"), lubridate::ymd_hms("2021-01-14 12:00:00"), + lubridate::ymd("2021-01-14"), lubridate::ymd_hms("2021-01-14 14:00:00"), + "P01", "ONCE", lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 09:57:00"), + lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 10:57:00"), + "P01", "ONCE", lubridate::ymd("2021-01-29"), lubridate::ymd_hms("2021-01-29 09:57:00"), + lubridate::ymd("2021-01-29"), lubridate::ymd_hms("2021-01-29 10:57:00") ) expect_equal(create_single_dose_dataset(input), expected_output) @@ -21,34 +36,64 @@ test_that("create_single_dose_dataset works as expected for Q#/EVERY # cases", { test_that("create_single_dose_dataset works as expected for # TIMES PER cases", { input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "2 TIMES PER YEAR", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-07-01"), - "P02", "2 TIMES PER YEAR", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-12-31"), - "P03", "4 TIMES PER MONTH", lubridate::ymd("2021-02-01"), lubridate::ymd("2021-03-01"), - "P04", "4 TIMES PER MONTH", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-20"), - "P05", "5 TIMES PER WEEK", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-17"), - "P06", "5 TIMES PER WEEK", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-21") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "2 TIMES PER YEAR", + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:00:00"), + lubridate::ymd("2021-07-01"), lubridate::ymd_hms("2021-07-01 10:00:00"), + "P02", "2 TIMES PER YEAR", + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:30:00"), + lubridate::ymd("2021-12-31"), lubridate::ymd_hms("2021-12-31 10:30:00"), + "P03", "4 TIMES PER MONTH", + lubridate::ymd("2021-02-01"), lubridate::ymd_hms("2021-02-01 11:00:00"), + lubridate::ymd("2021-03-01"), lubridate::ymd_hms("2021-03-01 11:00:00"), + "P04", "4 TIMES PER MONTH", + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 11:30:00"), + lubridate::ymd("2021-01-20"), lubridate::ymd_hms("2021-01-20 11:30:00"), + "P05", "5 TIMES PER WEEK", + lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 12:00:00"), + lubridate::ymd("2021-01-17"), lubridate::ymd_hms("2021-01-17 12:00:00"), + "P06", "5 TIMES PER WEEK", + lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 12:30:00"), + lubridate::ymd("2021-01-21"), lubridate::ymd_hms("2021-01-21 12:30:00"), ) expected_output <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-01"), - "P02", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-01"), - "P02", "ONCE", lubridate::ymd("2021-07-02"), lubridate::ymd("2021-07-02"), - "P03", "ONCE", lubridate::ymd("2021-02-01"), lubridate::ymd("2021-02-01"), - "P03", "ONCE", lubridate::ymd("2021-02-08"), lubridate::ymd("2021-02-08"), - "P03", "ONCE", lubridate::ymd("2021-02-16"), lubridate::ymd("2021-02-16"), - "P03", "ONCE", lubridate::ymd("2021-02-23"), lubridate::ymd("2021-02-23"), - "P04", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-01"), - "P04", "ONCE", lubridate::ymd("2021-01-08"), lubridate::ymd("2021-01-08"), - "P04", "ONCE", lubridate::ymd("2021-01-16"), lubridate::ymd("2021-01-16"), - "P05", "ONCE", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-15"), - "P05", "ONCE", lubridate::ymd("2021-01-16"), lubridate::ymd("2021-01-16"), - "P05", "ONCE", lubridate::ymd("2021-01-17"), lubridate::ymd("2021-01-17"), - "P06", "ONCE", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-15"), - "P06", "ONCE", lubridate::ymd("2021-01-16"), lubridate::ymd("2021-01-16"), - "P06", "ONCE", lubridate::ymd("2021-01-17"), lubridate::ymd("2021-01-17"), - "P06", "ONCE", lubridate::ymd("2021-01-19"), lubridate::ymd("2021-01-19"), - "P06", "ONCE", lubridate::ymd("2021-01-20"), lubridate::ymd("2021-01-20"), + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:00:00"), + "P02", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:30:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 10:30:00"), + "P02", "ONCE", lubridate::ymd("2021-07-02"), lubridate::ymd_hms("2021-07-02 10:30:00"), + lubridate::ymd("2021-07-02"), lubridate::ymd_hms("2021-07-02 10:30:00"), + "P03", "ONCE", lubridate::ymd("2021-02-01"), lubridate::ymd_hms("2021-02-01 11:00:00"), + lubridate::ymd("2021-02-01"), lubridate::ymd_hms("2021-02-01 11:00:00"), + "P03", "ONCE", lubridate::ymd("2021-02-08"), lubridate::ymd_hms("2021-02-08 11:00:00"), + lubridate::ymd("2021-02-08"), lubridate::ymd_hms("2021-02-08 11:00:00"), + "P03", "ONCE", lubridate::ymd("2021-02-16"), lubridate::ymd_hms("2021-02-16 11:00:00"), + lubridate::ymd("2021-02-16"), lubridate::ymd_hms("2021-02-16 11:00:00"), + "P03", "ONCE", lubridate::ymd("2021-02-23"), lubridate::ymd_hms("2021-02-23 11:00:00"), + lubridate::ymd("2021-02-23"), lubridate::ymd_hms("2021-02-23 11:00:00"), + "P04", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 11:30:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 11:30:00"), + "P04", "ONCE", lubridate::ymd("2021-01-08"), lubridate::ymd_hms("2021-01-08 11:30:00"), + lubridate::ymd("2021-01-08"), lubridate::ymd_hms("2021-01-08 11:30:00"), + "P04", "ONCE", lubridate::ymd("2021-01-16"), lubridate::ymd_hms("2021-01-16 11:30:00"), + lubridate::ymd("2021-01-16"), lubridate::ymd_hms("2021-01-16 11:30:00"), + "P05", "ONCE", lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 12:00:00"), + lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 12:00:00"), + "P05", "ONCE", lubridate::ymd("2021-01-16"), lubridate::ymd_hms("2021-01-16 12:00:00"), + lubridate::ymd("2021-01-16"), lubridate::ymd_hms("2021-01-16 12:00:00"), + "P05", "ONCE", lubridate::ymd("2021-01-17"), lubridate::ymd_hms("2021-01-17 12:00:00"), + lubridate::ymd("2021-01-17"), lubridate::ymd_hms("2021-01-17 12:00:00"), + "P06", "ONCE", lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 12:30:00"), + lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15 12:30:00"), + "P06", "ONCE", lubridate::ymd("2021-01-16"), lubridate::ymd_hms("2021-01-16 12:30:00"), + lubridate::ymd("2021-01-16"), lubridate::ymd_hms("2021-01-16 12:30:00"), + "P06", "ONCE", lubridate::ymd("2021-01-17"), lubridate::ymd_hms("2021-01-17 12:30:00"), + lubridate::ymd("2021-01-17"), lubridate::ymd_hms("2021-01-17 12:30:00"), + "P06", "ONCE", lubridate::ymd("2021-01-19"), lubridate::ymd_hms("2021-01-19 12:30:00"), + lubridate::ymd("2021-01-19"), lubridate::ymd_hms("2021-01-19 12:30:00"), + "P06", "ONCE", lubridate::ymd("2021-01-20"), lubridate::ymd_hms("2021-01-20 12:30:00"), + lubridate::ymd("2021-01-20"), lubridate::ymd_hms("2021-01-20 12:30:00") ) expect_equal(create_single_dose_dataset(input), expected_output) @@ -56,25 +101,32 @@ test_that("create_single_dose_dataset works as expected for # TIMES PER cases", test_that("create_single_dose_dataset works for different treatments", { input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, ~EXTRT, - "P01", "Q2D", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-03"), "XANOMELINE", - "P01", "QOD", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-05"), "PLACEBO" + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, ~EXTRT, + "P01", "Q2D", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 09:00:00"), + lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 09:00:00"), "XANOMELINE", + "P01", "QOD", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 09:15:00"), + lubridate::ymd("2021-01-05"), lubridate::ymd_hms("2021-01-05 09:15:00"), "PLACEBO" ) expected_output <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, ~EXTRT, - "P01", "ONCE", lubridate::ymd("2021-01-01"), - lubridate::ymd("2021-01-01"), "XANOMELINE", - "P01", "ONCE", lubridate::ymd("2021-01-03"), - lubridate::ymd("2021-01-03"), "XANOMELINE", - "P01", "ONCE", lubridate::ymd("2021-01-01"), - lubridate::ymd("2021-01-01"), "PLACEBO", - "P01", "ONCE", lubridate::ymd("2021-01-03"), - lubridate::ymd("2021-01-03"), "PLACEBO", - "P01", "ONCE", lubridate::ymd("2021-01-05"), - lubridate::ymd("2021-01-05"), "PLACEBO" + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, ~EXTRT, + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 09:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 09:00:00"), "XANOMELINE", + "P01", "ONCE", lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 09:00:00"), + lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 09:00:00"), "XANOMELINE", + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 09:15:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01 09:15:00"), "PLACEBO", + "P01", "ONCE", lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 09:15:00"), + lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03 09:15:00"), "PLACEBO", + "P01", "ONCE", lubridate::ymd("2021-01-05"), lubridate::ymd_hms("2021-01-05 09:15:00"), + lubridate::ymd("2021-01-05"), lubridate::ymd_hms("2021-01-05 09:15:00"), "PLACEBO" ) - expect_equal(create_single_dose_dataset(input), expected_output) + expect_equal( + create_single_dose_dataset(input, + keep_source_vars = vars(USUBJID, EXDOSFRQ, ASTDT, ASTDTM, AENDT, AENDTM, EXTRT) + ), + expected_output + ) }) test_that("custom lookup works", { @@ -85,35 +137,33 @@ test_that("custom lookup works", { ) input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDTM, ~AENDTM, - "P01", "Q30MIN", lubridate::ymd_hms("2021-01-01T06:00:00"), - lubridate::ymd_hms("2021-01-01T07:00:00"), - "P02", "Q90MIN", lubridate::ymd_hms("2021-01-01T06:00:00"), - lubridate::ymd_hms("2021-01-01T09:00:00") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "Q30MIN", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T07:00:00"), + "P02", "Q90MIN", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T09:00:00") ) expected_output <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDTM, ~AENDTM, - "P01", "ONCE", lubridate::ymd_hms("2021-01-01T06:00:00"), - lubridate::ymd_hms("2021-01-01T06:00:00"), - "P01", "ONCE", lubridate::ymd_hms("2021-01-01T06:30:00"), - lubridate::ymd_hms("2021-01-01T06:30:00"), - "P01", "ONCE", lubridate::ymd_hms("2021-01-01T07:00:00"), - lubridate::ymd_hms("2021-01-01T07:00:00"), - "P02", "ONCE", lubridate::ymd_hms("2021-01-01T06:00:00"), - lubridate::ymd_hms("2021-01-01T06:00:00"), - "P02", "ONCE", lubridate::ymd_hms("2021-01-01T07:30:00"), - lubridate::ymd_hms("2021-01-01T07:30:00"), - "P02", "ONCE", lubridate::ymd_hms("2021-01-01T09:00:00"), - lubridate::ymd_hms("2021-01-01T09:00:00") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:00:00"), + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:30:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:30:00"), + "P01", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T07:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T07:00:00"), + "P02", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T06:00:00"), + "P02", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T07:30:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T07:30:00"), + "P02", "ONCE", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T09:00:00"), + lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T09:00:00") ) expect_equal( create_single_dose_dataset(input, lookup_table = custom_lookup, - lookup_column = VALUE, - start_date = ASTDTM, - end_date = AENDTM + lookup_column = VALUE ), expected_output ) @@ -121,46 +171,27 @@ test_that("custom lookup works", { test_that("Warning is returned when values in EXDOSFRQ does not appear in lookup table", { input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "1", lubridate::ymd("2021-01-01"), lubridate::ymd("2021-01-03"), - "P01", "1", lubridate::ymd("2021-01-08"), lubridate::ymd("2021-01-12"), - "P01", "1", lubridate::ymd("2021-01-15"), lubridate::ymd("2021-01-29") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "1", lubridate::ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T09:00:00"), + lubridate::ymd("2021-01-03"), lubridate::ymd_hms("2021-01-03T09:00:00"), + "P01", "1", lubridate::ymd("2021-01-08"), lubridate::ymd_hms("2021-01-08T09:00:00"), + lubridate::ymd("2021-01-12"), lubridate::ymd_hms("2021-01-12T09:00:00"), + "P01", "1", lubridate::ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15T09:00:00"), + lubridate::ymd("2021-01-29"), lubridate::ymd_hms("2021-01-29T09:00:00") ) expect_error( create_single_dose_dataset(input) ) }) -test_that("Error is returned when a date variable is supplied rather than - a datetime variable", { - custom_lookup <- tibble::tribble( - ~VALUE, ~DOSE_COUNT, ~DOSE_WINDOW, ~CONVERSION_FACTOR, - "Q30MIN", (1 / 30), "MINUTE", 1, - "Q90MIN", (1 / 90), "MINUTE", 1 - ) - - input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDTM, ~AENDTM, - "P01", "Q30MIN", lubridate::ymd("2021-01-01"), - lubridate::ymd_hms("2021-01-01T07:00:00"), - "P02", "Q90MIN", lubridate::ymd("2021-01-01"), - lubridate::ymd_hms("2021-01-01T09:00:00") - ) - - expect_error(create_single_dose_dataset(input, - lookup_table = custom_lookup, - lookup_column = VALUE, - start_date = ASTDTM, - end_date = AENDTM - )) -}) - test_that("Error is returned when a date variable contains NA values", { input <- tibble::tribble( - ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~AENDT, - "P01", "Q2D", ymd("2021-01-01"), NA, - "P01", "Q3D", ymd("2021-01-08"), ymd("2021-01-15"), - "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), ymd("2021-01-29") + ~USUBJID, ~EXDOSFRQ, ~ASTDT, ~ASTDTM, ~AENDT, ~AENDTM, + "P01", "Q2D", ymd("2021-01-01"), lubridate::ymd_hms("2021-01-01T09:00:00"), NA, NA, + "P01", "Q3D", ymd("2021-01-08"), lubridate::ymd_hms("2021-01-08T09:00:00"), + ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15T09:00:00"), + "P01", "EVERY 2 WEEKS", ymd("2021-01-15"), lubridate::ymd_hms("2021-01-15T09:00:00"), + ymd("2021-01-29"), lubridate::ymd_hms("2021-01-29T09:00:00") ) expect_error( create_single_dose_dataset(input), diff --git a/tests/testthat/test-dataset_vignette.R b/tests/testthat/test-dataset_vignette.R deleted file mode 100644 index 78f0374945..0000000000 --- a/tests/testthat/test-dataset_vignette.R +++ /dev/null @@ -1,18 +0,0 @@ -test_that("a 'knitr_kable' object is output when run outside pkgdown", { - expect_s3_class(dataset_vignette(head(admiral_adsl)), "knitr_kable") -}) - -test_that("a 'datatables' object is output when run inside pkgdown", { - Sys.setenv(IN_PKGDOWN = "true") - on.exit(Sys.setenv(IN_PKGDOWN = "")) - expect_s3_class(dataset_vignette(head(admiral_adsl)), "datatables") -}) - -test_that("a 'knitr_kable' object is output when run outside pkgdown", { - Sys.setenv(IN_PKGDOWN = "false") - on.exit(Sys.setenv(IN_PKGDOWN = "")) - expect_s3_class( - dataset_vignette(head(admiral_adsl), display_vars = vars(STUDYID, USUBJID)), - "knitr_kable" - ) -}) diff --git a/tests/testthat/test-deprecation.R b/tests/testthat/test-deprecation.R index c19356bada..ce9c42503b 100644 --- a/tests/testthat/test-deprecation.R +++ b/tests/testthat/test-deprecation.R @@ -1,233 +1,214 @@ -test_that("a warning is issued when using `derive_var_extreme_flag()` with `filter` argument", { - input <- tibble::tribble( - ~USUBJID, ~AVISITN, ~AVAL, - 1, 1, 12, - 1, 3, 9, - 2, 2, 42, - 3, 3, 14, - 3, 3, 10 - ) - - expect_warning( +library(admiral.test) +library(rlang) +library(tibble) +data("admiral_dm") + +adsl <- tribble( + ~USUBJID, ~SEX, ~COUNTRY, + "ST42-1", "F", "AUT", + "ST42-2", "M", "MWI", + "ST42-3", "M", "NOR", + "ST42-4", "F", "UGA" +) %>% mutate(STUDYID = "ST42") + +ex <- tribble( + ~USUBJID, ~EXSTDTC, + "ST42-1", "2020-12-07", + "ST42-1", "2020-12-14", + "ST42-2", "2021-01-12T12:00:00", + "ST42-2", "2021-01-26T13:21", + "ST42-3", "2021-03-02" +) %>% mutate(STUDYID = "ST42") + +test_that("An error is thrown if `derive_var_extreme_flag()` with `filter` argument is called", { + expect_error( derive_var_extreme_flag( - input, - by_vars = vars(USUBJID), - order = vars(AVISITN, desc(AVAL)), - new_var = firstfl, - mode = "first", filter = !is.na(AVAL) ), - paste( - "`filter` is deprecated as of admiral 0.7.0.", - "Please use `restrict_derivation()` instead (see examples).", - sep = "\n" - ), - fixed = TRUE + "deprecated", + fixed = TRUE, + class = "lifecycle_error_deprecated" ) }) -test_that("a warning is issued when using `derive_var_worst_flag()` with `filter` argument", { - input_worst_flag <- tibble::tribble( - ~STUDYID, ~USUBJID, ~PARAMCD, ~AVISIT, ~ADT, ~AVAL, - "TEST01", "PAT01", "PARAM01", "BASELINE", as.Date("2021-04-27"), 15.0, - "TEST01", "PAT01", "PARAM01", "BASELINE", as.Date("2021-04-25"), 14.0, - "TEST01", "PAT01", "PARAM01", "BASELINE", as.Date("2021-04-23"), 15.0, - "TEST01", "PAT01", "PARAM01", "WEEK 1", as.Date("2021-04-27"), 10.0, - "TEST01", "PAT01", "PARAM01", "WEEK 2", as.Date("2021-04-30"), 12.0, - "TEST01", "PAT02", "PARAM01", "SCREENING", as.Date("2021-04-27"), 15.0, - "TEST01", "PAT02", "PARAM01", "BASELINE", as.Date("2021-04-25"), 14.0, - "TEST01", "PAT02", "PARAM01", "BASELINE", as.Date("2021-04-23"), 15.0, - "TEST01", "PAT02", "PARAM01", "WEEK 1", as.Date("2021-04-27"), 10.0, - "TEST01", "PAT02", "PARAM01", "WEEK 2", as.Date("2021-04-30"), 12.0, - "TEST01", "PAT01", "PARAM02", "SCREENING", as.Date("2021-04-27"), 15.0, - "TEST01", "PAT01", "PARAM02", "SCREENING", as.Date("2021-04-25"), 14.0, - "TEST01", "PAT01", "PARAM02", "SCREENING", as.Date("2021-04-23"), 15.0, - "TEST01", "PAT01", "PARAM02", "BASELINE", as.Date("2021-04-27"), 10.0, - "TEST01", "PAT01", "PARAM02", "WEEK 2", as.Date("2021-04-30"), 12.0, - "TEST01", "PAT02", "PARAM02", "SCREENING", as.Date("2021-04-27"), 15.0, - "TEST01", "PAT02", "PARAM02", "BASELINE", as.Date("2021-04-25"), 14.0, - "TEST01", "PAT02", "PARAM02", "WEEK 1", as.Date("2021-04-23"), 15.0, - "TEST01", "PAT02", "PARAM02", "WEEK 1", as.Date("2021-04-27"), 10.0, - "TEST01", "PAT02", "PARAM02", "BASELINE", as.Date("2021-04-30"), 12.0, - "TEST01", "PAT02", "PARAM03", "SCREENING", as.Date("2021-04-27"), 15.0, - "TEST01", "PAT02", "PARAM03", "BASELINE", as.Date("2021-04-25"), 14.0, - "TEST01", "PAT02", "PARAM03", "WEEK 1", as.Date("2021-04-23"), 15.0, - "TEST01", "PAT02", "PARAM03", "WEEK 1", as.Date("2021-04-27"), 10.0, - "TEST01", "PAT02", "PARAM03", "BASELINE", as.Date("2021-04-30"), 12.0 - ) - - expect_warning( +test_that("An error is thrown if `derive_var_worst_flag()` with `filter` argument is called", { + expect_error( derive_var_worst_flag( - input_worst_flag, - by_vars = vars(USUBJID, PARAMCD, AVISIT), - order = vars(desc(ADT)), - new_var = WORSTFL, - param_var = PARAMCD, - analysis_var = AVAL, - worst_high = c("PARAM01", "PARAM03"), - worst_low = "PARAM02", filter = !is.na(AVAL) ), - paste( - "`filter` is deprecated as of admiral 0.7.0.", - "Please use `restrict_derivation()` instead (see examples).", - sep = "\n" - ), - fixed = TRUE + "deprecated", + fixed = TRUE, + class = "lifecycle_error_deprecated" ) }) -test_that("derive_var_ady() Test 1: A warning is issued when using `derive_var_ady()`", { - input <- tibble::tribble( - ~TRTSDT, ~ADT, - ymd("2020-01-01"), ymd("2020-02-24"), - ymd("2020-01-01"), ymd("2020-01-01"), - ymd("2020-02-24"), ymd("2020-01-01") - ) - - expect_warning( - derive_var_ady(input), +test_that("derive_var_ady() Test 1: An error is thrown if `derive_var_ady()` is called", { + expect_error( + derive_var_ady(), "deprecated", - fixed = TRUE + fixed = TRUE, + class = "lifecycle_error_deprecated" ) }) -test_that("derive_var_aendy Test 1: A warning is issued when using `derive_var_aendy()`", { - input <- tibble::tribble( - ~TRTSDT, ~AENDT, - ymd("2020-01-01"), ymd("2020-02-24"), - ymd("2020-01-01"), ymd("2020-01-01"), - ymd("2020-02-24"), ymd("2020-01-01") +test_that("derive_var_aendy Test 1: An error is thrown if `derive_var_aendy()` is called", { + expect_error( + derive_var_aendy(), + "deprecated", + fixed = TRUE, + class = "lifecycle_error_deprecated" ) +}) - expect_warning( - derive_var_aendy(input), +test_that("derive_var_astdy Test 1: An error is thrown if `derive_var_astdy()` is called", { + expect_error( + derive_var_astdy(), "deprecated", - fixed = TRUE + fixed = TRUE, + class = "lifecycle_error_deprecated" ) }) -test_that("derive_var_astdy Test 1: A warning is issued when using `derive_var_astdy()`", { - input <- tibble::tribble( - ~TRTSDT, ~ASTDT, - ymd("2020-01-01"), ymd("2020-02-24"), - ymd("2020-01-01"), ymd("2020-01-01"), - ymd("2020-02-24"), ymd("2020-01-01") +test_that("derive_var_atirel Test 1: An error is thrown if `derive_var_atirel()` is called", { + expect_error( + derive_var_atirel(), + "deprecated", + fixed = TRUE, + class = "lifecycle_error_deprecated" ) +}) - expect_warning( - derive_var_astdy(input), +test_that("derive_vars_suppqual Test 1: An error is thrown if `derive_vars_suppqual()` is called", { + expect_error( + derive_vars_suppqual(), "deprecated", fixed = TRUE ) }) -test_that("derive_var_trtedtm Test 1: A warning is issued when using `derive_var_trtedtm()`", { - adsl <- tibble::tibble(STUDYID = "STUDY", USUBJID = 1:3) - ex <- tibble::tribble( - ~USUBJID, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, - 1L, "2020-01-01", 1, 12, "ACTIVE", - 1L, "2020-02-03", 2, 9, "ACTIVE", - 2L, "2020-01-02", 1, 0, "PLACEBO", - 3L, "2020-03-13", 1, 14, "ACTIVE", - 3L, "2020-03-21", 2, 0, "ACTIVE" +test_that("derive_derived_param Test 1: A warning is issued if `derive_derived_param()` is called", { # nolint + input <- tibble::tribble( + ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, ~VISIT, + "01-701-1015", "DIABP", "Diastolic Blood Pressure (mmHg)", 51, "mmHg", "BASELINE", + "01-701-1015", "DIABP", "Diastolic Blood Pressure (mmHg)", 50, "mmHg", "WEEK 2", + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "BASELINE", + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "WEEK 2", + "01-701-1028", "DIABP", "Diastolic Blood Pressure (mmHg)", 79, "mmHg", "BASELINE", + "01-701-1028", "DIABP", "Diastolic Blood Pressure (mmHg)", 80, "mmHg", "WEEK 2", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 130, "mmHg", "BASELINE", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 132, "mmHg", "WEEK 2" ) - expect_warning(derive_var_trtedtm( - adsl, - dataset_ex = ex, - subject_keys = vars(USUBJID) - ), - "deprecated", - fixed = TRUE + expect_warning( + derive_derived_param( + input, + parameters = c("SYSBP", "DIABP"), + by_vars = vars(USUBJID, VISIT), + analysis_value = (AVAL.SYSBP + 2 * AVAL.DIABP) / 3, + set_values_to = vars( + PARAMCD = "MAP", + PARAM = "Mean arterial pressure (mmHg)", + AVALU = "mmHg" + ) + ), + "deprecated", + fixed = TRUE ) }) -test_that("derive_var_trtsdtm Test 1: A warning is issued when using `derive_var_trtsdtm()`", { - adsl <- tibble::tibble(STUDYID = "STUDY", USUBJID = 1:3) - ex <- tibble::tribble( - ~USUBJID, ~EXSTDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, - 1L, "2020-01-01", 1, 12, "ACTIVE", - 1L, "2020-02-03", 2, 9, "ACTIVE", - 2L, "2020-01-02", 1, 0, "PLACEBO", - 3L, "2020-03-13", 1, 14, "ACTIVE", - 3L, "2020-03-21", 2, 0, "ACTIVE" - ) - - expect_warning(derive_var_trtsdtm( - adsl, - dataset_ex = ex, - subject_keys = vars(USUBJID) - ), - "deprecated", - fixed = TRUE +test_that("derive_vars_merged_dt: a deprecation warning is issued", { + expect_warning( + derive_vars_merged_dt( + adsl, + dataset_add = ex, + order = vars(TRTSDT), + flag_imputation = "date", + by_vars = vars(STUDYID, USUBJID), + dtc = EXSTDTC, + new_vars_prefix = "TRTS", + mode = "first" + ), + "deprecated" ) }) -test_that("derive_var_disposition Test 1: A warning is issued when using `derive_var_disposition_dt()`", { # nolint - adsl <- tibble::tribble( - ~STUDYID, ~USUBJID, - "TEST01", "PAT01", - "TEST01", "PAT02" - ) - - ds <- tibble::tribble( - ~STUDYID, ~USUBJID, ~DSCAT, ~DSDECOD, ~DSSTDTC, - "TEST01", "PAT01", "PROTOCOL MILESTONE", "INFORMED CONSENT OBTAINED", "2021-04-01", - "TEST01", "PAT01", "PROTOCOL MILESTONE", "RANDOMIZATION", "2021-04-11", - "TEST01", "PAT01", "DISPOSITION EVENT", "ADVERSE EVENT", "2021-12-01", - "TEST01", "PAT01", "OTHER EVENT", "DEATH", "2022-02-01", - "TEST01", "PAT02", "PROTOCOL MILESTONE", "INFORMED CONSENT OBTAINED", "2021-04-02", - "TEST01", "PAT02", "PROTOCOL MILESTONE", "RANDOMIZATION", "2021-04-11", - "TEST01", "PAT02", "DISPOSITION EVENT", "COMPLETED", "2021-12-01", - "TEST01", "PAT02", "OTHER EVENT", "DEATH", "2022-04" - ) - +test_that("derive_vars_merged_dtm: a deprecation warning is issued", { expect_warning( - derive_var_disposition_dt( + derive_vars_merged_dtm( adsl, - dataset_ds = ds, - new_var = RFICDT, - dtc = DSSTDTC, - filter = DSCAT == "PROTOCOL MILESTONE" & - DSDECOD == "INFORMED CONSENT OBTAINED", - date_imputation = NULL + dataset_add = ex, + order = vars(TRTSDTM), + by_vars = vars(STUDYID, USUBJID), + dtc = EXSTDTC, + new_vars_prefix = "TRTS", + time_imputation = "first", + mode = "first" ), - "deprecated", - fixed = TRUE + "deprecated" ) }) -test_that("derive_var_atirel Test 1: A warning is issued when using `derive_var_atirel()`", { - input <- tibble::tribble( - ~STUDYID, ~USUBJID, ~TRTSDTM, ~ASTDTM, ~AENDTM, ~ASTTMF, # nolint - "TEST01", "PAT01", "2012-02-25 23:00:00", "2012-03-28 19:00:00", "2012-05-25 23:00:00", "", - "TEST01", "PAT01", "", "2012-02-28 19:00:00", "", "", - "TEST01", "PAT01", "2017-02-25 23:00:00", "2013-02-25 19:00:00", "2014-02-25 19:00:00", "", - "TEST01", "PAT01", "2017-02-25 23:00:00", "2017-02-25 14:00:00", "2017-03-25 23:00:00", "m", - "TEST01", "PAT01", "2017-02-25 23:00:00", "2017-01-25 14:00:00", "2018-04-29 14:00:00", "", - ) %>% mutate( - TRTSDTM = lubridate::as_datetime(TRTSDTM), - ASTDTM = lubridate::as_datetime(ASTDTM), - AENDTM = lubridate::as_datetime(AENDTM) +test_that("date_source: errors when date_imputation is specified", { + expect_error( + date_source( + dataset_name = "ae", + date = ASTDTM, + date_imputation = "first" + ), + paste( + "The `date_imputation` argument of `date_source\\(\\)` .* deprecated .* admiral 0.8.0.*", + "Please use `derive_vars_dtm\\(\\)` to convert DTC variables to datetime variables in the dataset.", # nolint + sep = "\n" + ) ) +}) - expect_warning( - derive_var_atirel( - dataset = input, - flag_var = ASTTMF, - new_var = ATIREL +test_that("date_source: errors when time_imputation is specified", { + expect_error( + date_source( + dataset_name = "ae", + date = ASTDTM, + time_imputation = "first" ), - "deprecated", - fixed = TRUE + paste( + "The `time_imputation` argument of `date_source\\(\\)` .* deprecated .* admiral 0.8.0.*", + "Please use `derive_vars_dtm\\(\\)` to convert DTC variables to datetime variables in the dataset.", # nolint + sep = "\n" + ) ) }) -test_that("derive_vars_suppqual Test 1: An error is thrown if `derive_vars_suppqual()` is called", { +test_that("date_source: errors when preserve is specified", { expect_error( - derive_vars_suppqual(), - "deprecated", - fixed = TRUE, - class = "lifecycle_error_deprecated" + date_source( + dataset_name = "ae", + date = ASTDTM, + preserve = TRUE + ), + paste( + "The `preserve` argument of `date_source\\(\\)` .* deprecated .* admiral 0.8.0.*", + "Please use `derive_vars_dtm\\(\\)` to convert DTC variables to datetime variables in the dataset.", # nolint + sep = "\n" + ) ) }) + +test_that("derive_var_agegr_ema Test 1: A warning is issued if `derive_var_agegr_ema()` is called", { # nolint + with_options(lifecycle_verbosity = "warning", { + expect_warning( + derive_var_agegr_ema(admiral_dm, age_var = AGE, new_var = AGEGR1), + "deprecated", + fixed = TRUE + ) + }) +}) + +test_that("derive_var_agegr_fda Test 1: A warning is issued if `derive_var_agegr_fda()` is called", { # nolint + with_options(lifecycle_verbosity = "warning", { + expect_warning( + derive_var_agegr_fda(admiral_dm, age_var = AGE, new_var = AGEGR1), + "deprecated", + fixed = TRUE + ) + }) +}) diff --git a/tests/testthat/test-derive_date_vars.R b/tests/testthat/test-derive_date_vars.R new file mode 100644 index 0000000000..9b46b6c282 --- /dev/null +++ b/tests/testthat/test-derive_date_vars.R @@ -0,0 +1,895 @@ +library(lubridate) +library(dplyr) + +input <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25", + "2019-07-18", + "2019-02", + "2019", + "2019---07", + "2003-12-15T-:15:18", + "2003-12-15T13:-:19", + "2020-07--T00:00" +) + +# impute_dtc_dtm ---- +## Test 1: default: no date imputation, time part set to 00:00:00 ---- +test_that("impute_dtc_dtm Test 1: default: no date imputation, time part set to 00:00:00", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:00", + "2019-07-18T00:00:00", + NA_character_, + NA_character_, + NA_character_, + "2003-12-15T00:00:00", + "2003-12-15T13:00:00", + NA_character_ + ) + expect_equal(impute_dtc_dtm(dtc = input), expected_output) +}) + +## Test 2: no date imputation, min and sec imputed with 59 ---- +test_that("impute_dtc_dtm Test 2: no date imputation, min and sec imputed with 59", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:59", + NA_character_, + NA_character_, + NA_character_, + NA_character_, + NA_character_, + "2003-12-15T13:59:59", + NA_character_ + ) + expect_equal( + impute_dtc_dtm( + dtc = input, + highest_imputation = "m", + time_imputation = "23:59:59" + ), + expected_output + ) + + expect_equal( + impute_dtc_dtm( + dtc = input, + highest_imputation = "m", + time_imputation = "LAST" + ), + expected_output + ) +}) + +## Test 3: impute month and day to first, time to 00:00:00 ---- +test_that("impute_dtc_dtm Test 3: impute month and day to first, time to 00:00:00", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:00", + "2019-07-18T00:00:00", + "2019-02-01T00:00:00", + "2019-01-01T00:00:00", + "2019-01-01T00:00:00", + "2003-12-15T00:00:00", + "2003-12-15T13:00:00", + "2020-07-01T00:00:00" + ) + + expect_equal( + impute_dtc_dtm( + dtc = input, + highest_imputation = "M", + date_imputation = "first" + ), + expected_output + ) + + expect_equal( + impute_dtc_dtm( + dtc = input, + highest_imputation = "M", + date_imputation = "01-01" + ), + expected_output + ) +}) + +## Test 4: impute day to last, time to 23:59:59 ---- +test_that("impute_dtc_dtm Test 4: impute day to last, time to 23:59:59", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:59", + "2019-07-18T23:59:59", + "2019-02-28T23:59:59", + NA_character_, + NA_character_, + "2003-12-15T23:59:59", + "2003-12-15T13:59:59", + "2020-07-31T23:59:59" + ) + expect_equal( + impute_dtc_dtm( + dtc = input, + highest_imputation = "D", + date_imputation = "last", + time_imputation = "last" + ), + expected_output + ) +}) + +## Test 5: impute month, day to last, time to 23:59:59, preserve = TRUE ---- +test_that("impute_dtc_dtm Test 5: impute month, day to last, time to 23:59:59, preserve = TRUE", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:59", + "2019-07-18T23:59:59", + "2019-02-28T23:59:59", + "2019-12-31T23:59:59", + "2019-12-07T23:59:59", + "2003-12-15T23:15:18", + "2003-12-15T13:59:19", + "2020-07-31T00:00:59" + ) + expect_equal( + imputes <- impute_dtc_dtm( + dtc = input, + highest_imputation = "M", + date_imputation = "last", + time_imputation = "last", + preserve = TRUE + ), + expected_output + ) +}) + +## Test 6: no date imputation, impute second to 59 ---- +test_that("impute_dtc_dtm Test 6: no date imputation, impute second to 59", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:59", + NA_character_, + NA_character_, + NA_character_, + NA_character_, + NA_character_, + NA_character_, + NA_character_ + ) + + expect_equal( + imputes <- impute_dtc_dtm( + dtc = input, + highest_imputation = "s", + time_imputation = "LAST", + preserve = FALSE + ), + expected_output + ) +}) + +## Test 7: impute month and day to mid, time to first ---- +test_that("impute_dtc_dtm Test 7: impute month and day to mid, time to first", { + expected_output <- c( + "2019-07-18T15:25:40.243", + "2019-07-18T15:25:40", + "2019-07-18T15:25:00", + "2019-07-18T00:00:00", + "2019-02-15T00:00:00", + "2019-06-30T00:00:00", + "2019-06-30T00:00:00", + "2003-12-15T00:00:00", + "2003-12-15T13:00:00", + "2020-07-15T00:00:00" + ) + expect_equal( + imputes <- impute_dtc_dtm( + dtc = input, + highest_imputation = "M", + date_imputation = "mid", + time_imputation = "first", + preserve = FALSE + ), + expected_output + ) +}) + +## Test 8: min_dates parameter works ---- +test_that("impute_dtc_dtm Test 8: min_dates parameter works", { + expect_equal( + impute_dtc_dtm(c("2020-12", "2020-11", NA_character_), + min_dates = list( + c( + ymd_hms("2020-12-06T12:12:12"), + NA, + NA + ), + c( + ymd_hms("2020-11-11T11:11:11"), + ymd_hms("2020-11-11T11:11:11"), + ymd_hms("2020-11-11T11:11:11") + ) + ), + highest_imputation = "Y", + date_imputation = "first" + ), + c("2020-12-06T12:12:12", "2020-11-11T11:11:11", "2020-11-11T11:11:11") + ) +}) + +## Test 9: max_dates parameter works ---- +test_that("impute_dtc_dtm Test 9: max_dates parameter works", { + expect_equal( + impute_dtc_dtm(c("2020-12", "2020-11", NA_character_, "2020-02-02"), + max_dates = list( + c(ymd_hms("2020-12-06T12:12:12"), NA, ymd_hms("2020-09-13T08:30:00"), NA), + c(ymd(""), ymd("2020-11-11"), ymd(""), ymd("2020-02-02")) + ), + highest_imputation = "Y", + date_imputation = "last", + time_imputation = "last" + ), + c("2020-12-06T12:12:12", "2020-11-11T23:59:59", "2020-09-13T08:30:00", "2020-02-02T23:59:59") + ) +}) + +# impute_dtc_dt ---- +input <- c( + "2019-07-18", + "2019-02", + "2019", + "2019---07" +) + +## Test 10: default: no date imputation ---- +test_that("impute_dtc_dt Test 10: default: no date imputation", { + expected_output <- c( + "2019-07-18", + NA_character_, + NA_character_, + NA_character_ + ) + expect_equal(impute_dtc_dt(dtc = input), expected_output) +}) + +## Test 11: impute month and day to first ---- +test_that("impute_dtc_dt Test 11: impute month and day to first", { + expected_output <- c( + "2019-07-18", + "2019-02-01", + "2019-01-01", + "2019-01-01" + ) + + expect_equal( + impute_dtc_dt( + dtc = input, + highest_imputation = "M", + date_imputation = "first" + ), + expected_output + ) + + expect_equal( + impute_dtc_dt( + dtc = input, + highest_imputation = "M", + date_imputation = "01-01" + ), + expected_output + ) +}) + +## Test 12: impute day to last ---- +test_that("impute_dtc_dt Test 12: impute day to last", { + expected_output <- c( + "2019-07-18", + "2019-02-28", + NA_character_, + NA_character_ + ) + expect_equal( + impute_dtc_dt( + dtc = input, + highest_imputation = "D", + date_imputation = "LAST", + preserve = FALSE + ), + expected_output + ) +}) + +## Test 13: impute month and day to last and preserve = TRUE ---- +test_that("impute_dtc_dt Test 13: impute month and day to last and preserve = TRUE", { + expected_output <- c( + "2019-07-18", + "2019-02-28", + "2019-12-31", + "2019-12-07" + ) + expect_equal( + imputes <- impute_dtc_dt( + dtc = input, + highest_imputation = "M", + date_imputation = "LAST", + preserve = TRUE + ), + expected_output + ) +}) + + +## Test 14: impute month and day to mid ---- +test_that("impute_dtc_dt Test 14: impute month and day to mid", { + expected_output <- c( + "2019-07-18", + "2019-02-15", + "2019-06-30", + "2019-06-30" + ) + expect_equal( + imputes <- impute_dtc_dt( + dtc = input, + highest_imputation = "M", + date_imputation = "mid" + ), + expected_output + ) +}) + +## Test 15: min_dates parameter works ---- +test_that("impute_dtc_dt Test 15: min_dates parameter works", { + expect_equal( + impute_dtc_dt( + c("2020-12", "2020-11", NA_character_), + min_dates = list( + c( + ymd("2020-12-06"), + NA, + NA + ), + c( + ymd("2020-11-11"), + ymd("2020-11-11"), + ymd("2020-11-11") + ) + ), + highest_imputation = "Y", + date_imputation = "first" + ), + c("2020-12-06", "2020-11-11", "2020-11-11") + ) +}) + +## Test 16: max_dates parameter works ---- +test_that("impute_dtc_dt Test 16: max_dates parameter works", { + expect_equal( + impute_dtc_dt(c("2020-12", "2020-11", NA_character_), + max_dates = list( + c(ymd("2020-12-06"), NA, ymd("2020-09-13")), + c(ymd(""), ymd("2020-11-11"), ymd("")) + ), + highest_imputation = "Y", + date_imputation = "last" + ), + c("2020-12-06", "2020-11-11", "2020-09-13") + ) +}) + +# convert_dtc_to_dtm ---- +## Test 17: Convert a complete -- DTC into a date time object ---- +test_that("convert_dtc_to_dtm Test 17: Convert a complete -- DTC into a date time object", { + expect_equal( + convert_dtc_to_dtm("2019-07-18T15:25:52"), + ymd_hms("2019-07-18T15:25:52") + ) +}) + +# convert_dtc_to_dt ---- +inputdtc <- c( + "2019-07-18T15:25:52", + "2019-07-18" +) + +## Test 18: Convert a complete -- DTC into a date object ---- +test_that("convert_dtc_to_dt Test 18: Convert a complete -- DTC into a date object", { + expected_output <- c( + as.Date("2019-07-18"), + as.Date("2019-07-18") + ) + expect_equal( + convert_dtc_to_dt(dtc = inputdtc), + expected_output + ) +}) + +# convert_date_to_dtm +## Test 19: Convert a complete -- DTC into a date time object ---- +test_that("convert_date_to_dtm Test 19: Convert a complete -- DTC into a date time object", { + expect_equal( + convert_date_to_dtm("2019-07-18T15:25:52"), + ymd_hms("2019-07-18T15:25:52") + ) +}) + +## Test 20: Impute incomplete -- DTC into a date time object ---- +test_that("convert_date_to_dtm Test 20: Impute incomplete -- DTC into a date time object", { + expect_equal( + convert_date_to_dtm("2019-07-18", time_imputation = "23:59:59"), + ymd_hms("2019-07-18T23:59:59") + ) +}) + +## Test 21: Convert -- DT into a date time object ---- +test_that("convert_date_to_dtm Test 21: Convert -- DT into a date time object", { + expect_equal( + convert_date_to_dtm(as.Date("2019-07-18"), time_imputation = "23:59:59"), + ymd_hms("2019-07-18T23:59:59") + ) +}) + +## Test 22: Keep -- DTM as the original date time object ---- +test_that("convert_date_to_dtm Test 22: Keep -- DTM as the original date time object", { + expect_equal( + convert_date_to_dtm(ymd_hms("2019-07-18T15:25:52"), time_imputation = "23:59:59"), + ymd_hms("2019-07-18T15:25:52") + ) +}) + +# compute_dtf ---- + +inputdtc <- c( + "2019-07-18", + "2019-02", + "2019", + "2019---07" +) +inputdt <- c( + as.Date("2019-07-18"), + as.Date("2019-02-01"), + as.Date("2019-01-01"), + as.Date("2019-01-01") +) + +## Test 23: compute DTF ---- +test_that("compute_dtf Test 23: compute DTF", { + expected_output <- c( + NA_character_, + "D", + "M", + "M" + ) + expect_equal( + compute_dtf( + dtc = inputdtc, + dt = inputdt + ), + expected_output + ) +}) + +# compute_tmf ---- +## Test 24: compute TMF ---- +test_that("compute_tmf Test 24: compute TMF", { + input_dtc <- c( + "2019-07-18T15:25:52", + "2019-07-18T15:25", + "2019-07-18T15", + "2019-07-18", + "2019-02", + "2019", + "2019---07", + "2003-12-15T-:15:18", + "2003-12-15T13:-:19", + "2020-07--T00:00", + "2020-07--T00:00:00" + ) + input_dtm <- c( + as.POSIXct("2019-07-18T15:25:52"), + as.POSIXct("2019-07-18T15:25:00"), + as.POSIXct("2019-07-18T15:00:00"), + as.POSIXct("2019-07-18"), + as.POSIXct("2019-02-01"), + as.POSIXct(NA_character_), + as.POSIXct(NA_character_), + as.POSIXct("2003-12-15T23:15:18"), + as.POSIXct("2003-12-15T13:59:19"), + as.POSIXct("2020-07-31T00:00:59"), + as.POSIXct("2020-07-31T00:00:59") + ) + expected_output <- c( + NA_character_, + "S", + "M", + "H", + "H", + NA_character_, + NA_character_, + "H", + "M", + "S", + NA_character_ + ) + + expect_equal( + compute_tmf( + dtc = input_dtc, + dtm = input_dtm + ), + expected_output + ) +}) + +## Test 25: throws ERROR when ignore_seconds_flag = T and seconds are present ---- +test_that("compute_tmf Test 25: throws ERROR when ignore_seconds_flag = T and seconds are present", { # nolint + expect_error(compute_tmf( + dtc = c("2020-11-11T11:11:11", "2020-11-11T11:11"), + dtm = ymd_hms(c( + "2020-11-11T11:11:11", "2020-11-11T11:11:00" + )), + ignore_seconds_flag = TRUE + ), + regexp = "Seconds detected in data while ignore_seconds_flag is invoked" + ) +}) + +## Test 26: ignore_seconds_flag = TRUE ---- +test_that("compute_tmf Test 26: ignore_seconds_flag = TRUE", { + expect_equal( + compute_tmf( + dtc = c("2020-11-11T11:11", "2020-11-11T11"), + dtm = ymd_hms(c( + "2020-11-11T11:11:00", "2020-11-11T11:00:00" + )), + ignore_seconds_flag = TRUE + ), + c(NA_character_, "M") + ) +}) + +# derive_vars_dt ---- + +date <- tibble::tribble( + ~XXSTDTC, + "2019-07-18T15:25:40", + "2019-07-18", + "2019-02", + "2019", + "2019---07" +) + +## Test 27: default behavior ---- +test_that("derive_vars_dt Test 27: default behavior", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDT, + "2019-07-18T15:25:40", as.Date("2019-07-18"), + "2019-07-18", as.Date("2019-07-18"), + "2019-02", as.Date(NA), + "2019", as.Date(NA), + "2019---07", as.Date(NA) + ) + + actual_output <- derive_vars_dt( + date, + new_vars_prefix = "AST", + dtc = XXSTDTC + ) + + expect_dfs_equal( + expected_output, + actual_output, + "XXSTDTC" + ) +}) + +## Test 28: no date imputation, add DTF ---- +test_that("derive_vars_dt Test 28: no date imputation, add DTF", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDT, ~ASTDTF, + "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, + "2019-07-18", as.Date("2019-07-18"), NA_character_, + "2019-02", as.Date(NA), NA_character_, + "2019", as.Date(NA), NA_character_, + "2019---07", as.Date(NA), NA_character_ + ) + + actual_output <- derive_vars_dt( + date, + new_vars_prefix = "AST", + flag_imputation = "date", + dtc = XXSTDTC + ) + + expect_dfs_equal( + expected_output, + actual_output, + "XXSTDTC" + ) +}) + +## Test 29: date imputed to first, auto DTF ---- +test_that("derive_vars_dt Test 29: date imputed to first, auto DTF", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDT, ~ASTDTF, + "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, + "2019-07-18", as.Date("2019-07-18"), NA_character_, + "2019-02", as.Date("2019-02-01"), "D", + "2019", as.Date("2019-01-01"), "M", + "2019---07", as.Date("2019-01-01"), "M" + ) + + actual_output <- derive_vars_dt( + date, + new_vars_prefix = "AST", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "first" + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +## Test 30: date imputed to last, no DTF ---- +test_that("derive_vars_dt Test 30: date imputed to last, no DTF", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~AENDT, + "2019-07-18T15:25:40", as.Date("2019-07-18"), + "2019-07-18", as.Date("2019-07-18"), + "2019-02", as.Date("2019-02-28"), + "2019", as.Date("2019-12-31"), + "2019---07", as.Date("2019-12-31") + ) + + actual_output <- derive_vars_dt( + date, + new_vars_prefix = "AEN", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "last", + flag_imputation = "none" + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +# derive_vars_dtm ---- + +input <- tibble::tribble( + ~XXSTDTC, + "2019-07-18T15:25:40", + "2019-07-18T15:25", + "2019-07-18T15", + "2019-07-18", + "2019-02", + "2019", + "2019---07" +) + +## Test 31: default behavior ---- +test_that("derive_vars_dtm Test 31: default behavior", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDTM, ~ASTTMF, + "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, + "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), "S", + "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), "M", + "2019-07-18", ymd_hms("2019-07-18T00:00:00"), "H", + "2019-02", ymd_hms(NA), NA_character_, + "2019", ymd_hms(NA), NA_character_, + "2019---07", ymd_hms(NA), NA_character_ + ) + + actual_output <- derive_vars_dtm( + input, + new_vars_prefix = "AST", + dtc = XXSTDTC + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +## Test 32: date imputed to first, auto DTM/TMF ---- +test_that("derive_vars_dtm Test 32: date imputed to first, auto DTF/TMF", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, + "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, + "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", + "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", + "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", + "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", + "2019", ymd_hms("2019-01-01T00:00:00"), "M", "H", + "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" + ) + + actual_output <- derive_vars_dtm( + input, + new_vars_prefix = "AST", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "first" + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +## Test 33: date and time imputed to last, no DTF/TMF ---- +test_that("derive_vars_dtm Test 33: date and time imputed to last, no DTF/TMF", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~AENDTM, + "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), + "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:59"), + "2019-07-18T15", ymd_hms("2019-07-18T15:59:59"), + "2019-07-18", ymd_hms("2019-07-18T23:59:59"), + "2019-02", ymd_hms("2019-02-28T23:59:59"), + "2019", ymd_hms("2019-12-31T23:59:59"), + "2019---07", ymd_hms("2019-12-31T23:59:59") + ) + + actual_output <- derive_vars_dtm( + input, + new_vars_prefix = "AEN", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "LAST", + time_imputation = "LAST", + flag_imputation = "none" + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +## Test 34: date and time imputed to last, DTF only ---- +test_that("derive_vars_dtm Test 34: date and time imputed to last, DTF only", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~AENDTM, ~AENDTF, + "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, + "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:59"), NA_character_, + "2019-07-18T15", ymd_hms("2019-07-18T15:59:59"), NA_character_, + "2019-07-18", ymd_hms("2019-07-18T23:59:59"), NA_character_, + "2019-02", ymd_hms("2019-02-28T23:59:59"), "D", + "2019", ymd_hms("2019-12-31T23:59:59"), "M", + "2019---07", ymd_hms("2019-12-31T23:59:59"), "M" + ) + + actual_output <- derive_vars_dtm( + input, + new_vars_prefix = "AEN", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "last", + time_imputation = "last", + flag_imputation = "date" + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +## Test 35: date imputed to MID, time to first, TMF only ---- +test_that("derive_vars_dtm Test 35: date imputed to MID, time to first, TMF only", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDTM, ~ASTTMF, + "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, + "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), "S", + "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), "M", + "2019-07-18", ymd_hms("2019-07-18T00:00:00"), "H", + "2019-02", ymd_hms("2019-02-15T00:00:00"), "H", + "2019", ymd_hms("2019-06-30T00:00:00"), "H", + "2019---07", ymd_hms("2019-06-07T00:00:00"), "H" + ) + + actual_output <- derive_vars_dtm( + input, + new_vars_prefix = "AST", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "mid", + flag_imputation = "time", + preserve = TRUE + ) + + expect_dfs_equal( + base = expected_output, + comp = actual_output, + keys = c("XXSTDTC") + ) +}) + +## Test 36: No re-derivation is done if --DTF variable already exists ---- +test_that("derive_vars_dtm Test 36: No re-derivation is done if --DTF variable already exists", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, + "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, + "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", + "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", + "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", + "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", + "2019", ymd_hms("2019-01-01T00:00:00"), "MD", "H", + "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" + ) %>% + select(XXSTDTC, ASTDTF, everything()) + + actual_output <- expect_message( + derive_vars_dtm( + mutate(input, ASTDTF = c(NA, NA, NA, NA, "D", "MD", "M")), + new_vars_prefix = "AST", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "first" + ), + regexp = "^The .* variable is already present in the input dataset and will not be re-derived." + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = "XXSTDTC" + ) +}) + +## Test 37: max_dates parameter works as expected ---- +test_that("derive_vars_dtm Test 37: max_dates parameter works as expected", { + expected_output <- tibble::tribble( + ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, + "2019-02", ymd_hms("2019-02-10T00:00:00"), "D", "H", + "2019", ymd_hms("2019-02-10T00:00:00"), "M", "H", + "2019---07", ymd_hms("2019-02-10T00:00:00"), "M", "H" + ) %>% + mutate(DCUTDT = ymd_hms("2019-02-10T00:00:00")) + + actual_output <- derive_vars_dtm( + select(expected_output, XXSTDTC, DCUTDT), + new_vars_prefix = "AST", + dtc = XXSTDTC, + highest_imputation = "M", + date_imputation = "last", + max_dates = vars(DCUTDT) + ) + + expect_dfs_equal( + base = expected_output, + compare = actual_output, + keys = c("XXSTDTC") + ) +}) + +input_secs <- tibble::tribble( + ~XXSTDTC, + "2019-07-18T15:25:40", + "2019-07-18T15:25", + "2019-07-18T15", + "2019-07-18", + "2019-02", + "2019", + "2019---07" +) diff --git a/tests/testthat/test-derive_merged.R b/tests/testthat/test-derive_merged.R index 19055c2ccf..9d4fea987b 100644 --- a/tests/testthat/test-derive_merged.R +++ b/tests/testthat/test-derive_merged.R @@ -24,9 +24,20 @@ ex <- tibble::tribble( "ST42-2", "2021-01-26T13:21", "ST42-3", "2021-03-02" ) %>% mutate(STUDYID = "ST42") + +vs <- tibble::tribble( + ~USUBJID, ~VSTESTCD, ~VSTEST, ~VSORRES, ~VSSEQ, + "ST42-1", "DIABP", "Diastolic Blood Pressure", 64, 1, + "ST42-1", "DIABP", "Diastolic Blood Pressure", 83, 2, + "ST42-1", "WEIGHT", "Weight", 120, 3, + "ST42-2", "WEIGHT", "Weight", 110, 1, + "ST42-2", "HEIGHT", "Height", 58, 2 +) %>% mutate(STUDYID = "ST42") + + # derive_vars_merged ---- -## derive_vars_merged: merge all variables ---- -test_that("derive_vars_merged: merge all variables", { +## Test 1: merge all variables ---- +test_that("derive_vars_merged Test 1: merge all variables", { actual <- derive_vars_merged(advs, dataset_add = adsl, by_vars = vars(STUDYID, USUBJID) @@ -41,8 +52,8 @@ test_that("derive_vars_merged: merge all variables", { ) }) -## derive_vars_merged: merge selected variables ---- -test_that("derive_vars_merged: merge selected variables", { +## Test 2: merge selected variables ---- +test_that("derive_vars_merged Test 2: merge selected variables", { actual <- derive_vars_merged(advs, dataset_add = adsl, by_vars = vars(USUBJID), @@ -58,8 +69,8 @@ test_that("derive_vars_merged: merge selected variables", { ) }) -## derive_vars_merged: merge last value and flag matched by groups ---- -test_that("derive_vars_merged: merge last value and flag matched by groups", { +## Test 3: merge last value and flag matched by groups ---- +test_that("derive_vars_merged Test 3: merge last value and flag matched by groups", { actual <- derive_vars_merged(adsl, dataset_add = advs, order = vars(AVAL), @@ -80,8 +91,8 @@ test_that("derive_vars_merged: merge last value and flag matched by groups", { ) }) -## derive_vars_merged: error if variable in both datasets ---- -test_that("derive_vars_merged: error if variable in both datasets", { +## Test 4: error if variable in both datasets ---- +test_that("derive_vars_merged Test 4: error if variable in both datasets", { expect_error(derive_vars_merged(advs, dataset_add = adsl, by_vars = vars(USUBJID) @@ -91,17 +102,20 @@ test_that("derive_vars_merged: error if variable in both datasets", { }) # derive_vars_merged_dt ---- -## derive_vars_merged_dt: merge first date ---- -test_that("derive_vars_merged_dt: merge first date", { - actual <- derive_vars_merged_dt( - adsl, - dataset_add = ex, - order = vars(TRTSDT), - flag_imputation = "date", - by_vars = vars(STUDYID, USUBJID), - dtc = EXSTDTC, - new_vars_prefix = "TRTS", - mode = "first" +## Test 5: merge first date ---- +test_that("derive_vars_merged_dt Test 5: merge first date", { + suppress_warning( + actual <- derive_vars_merged_dt( + adsl, + dataset_add = ex, + order = vars(TRTSDT), + flag_imputation = "date", + by_vars = vars(STUDYID, USUBJID), + dtc = EXSTDTC, + new_vars_prefix = "TRTS", + mode = "first" + ), + "deprecated" ) expected <- adsl %>% mutate( @@ -119,17 +133,20 @@ test_that("derive_vars_merged_dt: merge first date", { }) # derive_vars_merged_dtm ---- -## derive_vars_merged_dtm: merge first date ---- -test_that("derive_vars_merged_dt: merge first date", { - actual <- derive_vars_merged_dtm( - adsl, - dataset_add = ex, - order = vars(TRTSDTM), - by_vars = vars(STUDYID, USUBJID), - dtc = EXSTDTC, - new_vars_prefix = "TRTS", - time_imputation = "first", - mode = "first" +## Test 6: merge first date ---- +test_that("derive_vars_merged_dtm Test 6: merge first date", { + suppress_warning( + actual <- derive_vars_merged_dtm( + adsl, + dataset_add = ex, + order = vars(TRTSDTM), + by_vars = vars(STUDYID, USUBJID), + dtc = EXSTDTC, + new_vars_prefix = "TRTS", + time_imputation = "first", + mode = "first" + ), + "deprecated" ) expected <- adsl %>% mutate( @@ -152,8 +169,8 @@ test_that("derive_vars_merged_dt: merge first date", { }) # derive_var_merged_cat ---- -## derive_var_merged_cat: merge categorized variable ---- -test_that("derive_vars_merged_cat: merge categorized variable", { +## Test 7: merge categorized variable ---- +test_that("derive_vars_merged_cat Test 7: merge categorized variable", { get_region <- function(x) { if_else(x %in% c("AUT", "NOR"), "EUROPE", "AFRICA") } @@ -179,8 +196,8 @@ test_that("derive_vars_merged_cat: merge categorized variable", { ) }) -## derive_var_merged_cat: define value for non-matched by groups ---- -test_that("derive_vars_merged_cat: define value for non-matched by groups", { +## Test 8: define value for non-matched by groups ---- +test_that("derive_vars_merged_cat Test 8: define value for non-matched by groups", { get_vscat <- function(x) { if_else(x == "BASELINE", "BASELINE", "POST-BASELINE") } @@ -210,8 +227,8 @@ test_that("derive_vars_merged_cat: define value for non-matched by groups", { }) # derive_var_merged_exist_flag ---- -## derive_var_merged_exist_flag: merge existence flag ---- -test_that("derive_vars_merged_exist_flag: merge existence flag", { +## Test 9: merge existence flag ---- +test_that("derive_vars_merged_exist_flag Test 9: merge existence flag", { actual <- derive_var_merged_exist_flag( adsl, dataset_add = advs, @@ -232,8 +249,8 @@ test_that("derive_vars_merged_exist_flag: merge existence flag", { }) # derive_var_merged_character ---- -## derive_var_merged_character: merge character variable, no transformation ---- -test_that("derive_var_merged_character: merge character variable, no transformation", { +## Test 10: merge character variable, no transformation ---- +test_that("derive_var_merged_character Test 10: merge character variable, no transformation", { actual <- derive_var_merged_character( adsl, dataset_add = advs, @@ -255,8 +272,8 @@ test_that("derive_var_merged_character: merge character variable, no transformat ) }) -## derive_var_merged_character: merge character variable, upper case ---- -test_that("derive_var_merged_character: merge character variable, upper case", { +## Test 11: merge character variable, upper case ---- +test_that("derive_var_merged_character Test 11: merge character variable, upper case", { actual <- derive_var_merged_character( adsl, dataset_add = advs, @@ -280,8 +297,8 @@ test_that("derive_var_merged_character: merge character variable, upper case", { ) }) -## derive_var_merged_character: merge character variable, lower case ---- -test_that("derive_var_merged_character: merge character variable, lower case", { +## Test 12: merge character variable, lower case ---- +test_that("derive_var_merged_character Test 12: merge character variable, lower case", { actual <- derive_var_merged_character( adsl, dataset_add = advs, @@ -304,8 +321,8 @@ test_that("derive_var_merged_character: merge character variable, lower case", { ) }) -## derive_var_merged_character: merge character variable, title case ---- -test_that("derive_var_merged_character: merge character variable, title case", { +## Test 13: merge character variable, title case ---- +test_that("derive_var_merged_character Test 13: merge character variable, title case", { actual <- derive_var_merged_character( adsl, dataset_add = advs, @@ -327,3 +344,38 @@ test_that("derive_var_merged_character: merge character variable, title case", { keys = "USUBJID" ) }) + + + +## Test 14: merge lookup table +test_that("derive_vars_merged_lookup Test 14: merge lookup table", { + param_lookup <- tibble::tribble( + ~VSTESTCD, ~VSTEST, ~PARAMCD, ~DESCRIPTION, + "WEIGHT", "Weight", "WEIGHT", "Weight (kg)", + "HEIGHT", "Height", "HEIGHT", "Height (cm)", + "BMI", "Body Mass Index", "BMI", "Body Mass Index(kg/m^2)" + ) + + attr(param_lookup$VSTESTCD, "label") <- "Vital Signs Test Short Name" + attr(param_lookup$VSTEST, "label") <- "Vital Signs Test Name" + + + actual <- derive_vars_merged_lookup( + vs, + dataset_add = param_lookup, + by_vars = vars(VSTESTCD, VSTEST), + new_var = vars(PARAMCD, PARAM = DESCRIPTION), + print_not_mapped = TRUE + ) + + expected <- + left_join(vs, param_lookup, by = c("VSTESTCD", "VSTEST")) %>% + rename(PARAM = DESCRIPTION) + + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "VSSEQ", "VSTESTCD") + ) +}) diff --git a/tests/testthat/test-derive_derived_param.R b/tests/testthat/test-derive_param_computed.R similarity index 98% rename from tests/testthat/test-derive_derived_param.R rename to tests/testthat/test-derive_param_computed.R index 598799d33b..7260ddbcf2 100644 --- a/tests/testthat/test-derive_derived_param.R +++ b/tests/testthat/test-derive_param_computed.R @@ -27,7 +27,7 @@ test_that("new observations are derived correctly", { expected_output <- bind_rows(input, new_obs) expect_dfs_equal( - derive_derived_param( + derive_param_computed( input, parameters = c("SYSBP", "DIABP"), by_vars = vars(USUBJID, VISIT), @@ -72,7 +72,7 @@ test_that("new observations are derived correctly with constant parameters", { expected_output <- bind_rows(input, new_obs) expect_dfs_equal( - derive_derived_param( + derive_param_computed( input, parameters = c("WEIGHT"), by_vars = vars(USUBJID, VISIT), @@ -104,7 +104,7 @@ test_that("no new observations are added if filtered dataset is empty", { ) expect_warning( - derive_derived_param( + derive_param_computed( input, filter = VISIT == "WEEK 24", parameters = c("SYSBP", "DIABP"), @@ -137,7 +137,7 @@ test_that("no new observations are added if a parameter is missing", { ) expect_warning( - derive_derived_param( + derive_param_computed( input, filter = PARAMCD == "DIABP", parameters = c("SYSBP", "DIABP"), diff --git a/tests/testthat/test-derive_param_framingham.R b/tests/testthat/test-derive_param_framingham.R new file mode 100644 index 0000000000..3f50fcff11 --- /dev/null +++ b/tests/testthat/test-derive_param_framingham.R @@ -0,0 +1,63 @@ +input <- tibble::tribble( + # nolint start + ~USUBJID, ~PARAMCD, ~PARAM, ~AVAL, ~AVALU, ~VISIT, ~AGE, ~SEX, ~SMOKEFL, ~DIABETFL, ~TRTHYPFL, + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 121, "mmHg", "BASELINE", 44, "F", "N", "N", "N", + "01-701-1015", "SYSBP", "Systolic Blood Pressure (mmHg)", 115, "mmHg", "WEEK 2", 44, "F", "N", "N", "Y", + "01-701-1015", "CHOL", "Total Cholesterol (mg/dL)", 216.16, "mg/dL", "BASELINE", 44, "F", "N", "N", "N", + "01-701-1015", "CHOL", "Total Cholesterol (mg/dL)", 210.78, "mg/dL", "WEEK 2", 44, "F", "N", "N", "Y", + "01-701-1015", "CHOLHDL", "Cholesteral/HDL-Cholesterol (mg/dL)", 54.91, "mg/dL", "BASELINE", 44, "F", "N", "N", "N", + "01-701-1015", "CHOLHDL", "Cholesteral/HDL-Cholesterol (mg/dL)", 26.72, "mg/dL", "WEEK 2", 44, "F", "N", "N", "Y", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 119, "mmHg", "BASELINE", 55, "M", "Y", "Y", "Y", + "01-701-1028", "SYSBP", "Systolic Blood Pressure (mmHg)", 101, "mmHg", "WEEK 2", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOL", "Total Cholesterol (mg/dL)", 292.01, "mg/dL", "BASELINE", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOL", "Total Cholesterol (mg/dL)", 246.73, "mg/dL", "WEEK 2", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOLHDL", "Cholesteral/HDL-Cholesterol (mg/dL)", 65.55, "mg/dL", "BASELINE", 55, "M", "Y", "Y", "Y", + "01-701-1028", "CHOLHDL", "Cholesteral/HDL-Cholesterol (mg/dL)", 44.62, "mg/dL", "WEEK 2", 55, "M", "Y", "Y", "Y" + # nolint end +) + +test_that("derive_param_framingham Test 1: New observations are derived correctly", { + new_obs <- select( + filter(input, PARAMCD == "SYSBP"), USUBJID, VISIT, AVAL, AVALU, AGE, SEX, SMOKEFL, DIABETFL, TRTHYPFL # nolint + ) %>% + rename(AVAL_SYSBP = AVAL) %>% + left_join(select( + filter(input, PARAMCD == "CHOL"), USUBJID, VISIT, AVAL + ), by = c("USUBJID", "VISIT")) %>% + rename(AVAL_CHOL = AVAL) %>% + left_join(select( + filter(input, PARAMCD == "CHOLHDL"), USUBJID, VISIT, AVAL + ), by = c("USUBJID", "VISIT")) %>% + rename(AVAL_CHOLHDL = AVAL) %>% + mutate( + AVAL = compute_framingham( + sysbp = AVAL_SYSBP, + chol = AVAL_CHOL, + cholhdl = AVAL_CHOLHDL, + age = AGE, + sex = SEX, + smokefl = SMOKEFL, + diabetfl = DIABETFL, + trthypfl = TRTHYPFL + ), + PARAMCD = "FCVD101", + PARAM = "FCVD1-Framingham CVD 10-Year Risk Score (%)", + AVALU = NA_character_ + ) %>% + select(-AVAL_CHOLHDL, -AVAL_CHOL, -AVAL_SYSBP) + expected_output <- bind_rows(input, new_obs) + + expect_dfs_equal( + derive_param_framingham( + input, + by_vars = vars(USUBJID, VISIT), + set_values_to = vars( + PARAMCD = "FCVD101", + PARAM = "FCVD1-Framingham CVD 10-Year Risk Score (%)" + ), + get_unit_expr = AVALU + ), + expected_output, + keys = c("USUBJID", "PARAMCD", "VISIT") + ) +}) diff --git a/tests/testthat/test-derive_param_tte.R b/tests/testthat/test-derive_param_tte.R index ab3113aa29..c5fd6d5268 100644 --- a/tests/testthat/test-derive_param_tte.R +++ b/tests/testthat/test-derive_param_tte.R @@ -1,5 +1,10 @@ -test_that("new observations with analysis date are derived correctly", { - adsl <- tibble::tribble( +library(tibble) +library(lubridate) + +# derive_param_tte ---- +## Test 1: new observations with analysis date are derived correctly ---- +test_that("derive_param_tte Test 1: new observations with analysis date are derived correctly", { + adsl <- tribble( ~USUBJID, ~DTHFL, ~DTHDT, ~LSTALVDT, ~TRTSDT, ~TRTSDTF, "03", "Y", ymd("2021-08-21"), ymd("2021-08-21"), ymd("2021-08-10"), NA, "04", "N", NA, ymd("2021-05-24"), ymd("2021-02-03"), NA @@ -28,7 +33,7 @@ test_that("new observations with analysis date are derived correctly", { ) ) - expected_output <- tibble::tribble( + expected_output <- tribble( ~USUBJID, ~ADT, ~CNSR, ~EVENTDESC, ~SRCDOM, ~SRCVAR, "03", ymd("2021-08-21"), 0L, "DEATH", "ADSL", "DTHDT", "04", ymd("2021-05-24"), 1L, "LAST KNOWN ALIVE DATE", "ADSL", "LSTALVDT" @@ -59,7 +64,8 @@ test_that("new observations with analysis date are derived correctly", { ) }) -test_that("new observations with analysis datetime are derived correctly", { +## Test 2: new parameter with analysis datetime is derived correctly ---- +test_that("derive_param_tte Test 2: new parameter with analysis datetime is derived correctly", { adsl <- tibble::tribble( ~USUBJID, ~DTHFL, ~DTHDT, ~TRTSDTM, ~TRTSDTF, ~TRTSTMF, "01", "Y", ymd("2021-06-12"), ymd_hms("2021-01-01 00:00:00"), "M", "H", @@ -128,7 +134,7 @@ test_that("new observations with analysis datetime are derived correctly", { ) # nolint start - expected_output <- tibble::tribble( + expected_output <- tribble( ~USUBJID, ~ADTM, ~CNSR, ~EVENTDESC, ~SRCDOM, ~SRCVAR, ~SRCSEQ, "01", ymd_hms("2021-05-05 12:02:00"), 0L, "PD", "ADRS", "ADTM", 3, "02", ymd_hms("2021-02-03 10:56:00"), 0L, "PD", "ADRS", "ADTM", 1, @@ -167,7 +173,8 @@ test_that("new observations with analysis datetime are derived correctly", { ) }) -test_that("new observations based on DTC variables are derived correctly", { +## Test 3: error is issued if DTC variables specified for date ---- +test_that("derive_param_tte Test 3: error is issued if DTC variables specified for date", { adsl <- tibble::tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), @@ -205,19 +212,7 @@ test_that("new observations based on DTC variables are derived correctly", { ) ) - expected_output <- tibble::tribble( - ~USUBJID, ~ADT, ~CNSR, ~EVENTDESC, ~SRCDOM, ~SRCVAR, ~SRCSEQ, - "01", ymd("2021-01-01"), 0L, "AE", "AE", "AESTDTC", 3, - "02", ymd("2021-02-03"), 1L, "END OF STUDY", "ADSL", "EOSDT", NA - ) %>% - mutate( - STUDYID = "AB42", - PARAMCD = "TTAE", - PARAM = "Time to First Adverse Event" - ) %>% - left_join(select(adsl, USUBJID, STARTDT = TRTSDT), by = "USUBJID") - - expect_dfs_equal( + expect_error( derive_param_tte( dataset_adsl = adsl, start_date = TRTSDT, @@ -229,12 +224,12 @@ test_that("new observations based on DTC variables are derived correctly", { PARAM = "Time to First Adverse Event" ) ), - expected_output, - keys = c("USUBJID", "PARAMCD") + regexp = "`AESTDTC` in dataset `ae` is not a date or datetime variable but is a character vector" # nolint ) }) -test_that("by_vars parameter works correctly", { +## Test 4: by_vars parameter works correctly ---- +test_that("derive_param_tte Test 4: by_vars parameter works correctly", { adsl <- tibble::tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), @@ -243,16 +238,19 @@ test_that("by_vars parameter works correctly", { mutate(STUDYID = "AB42") ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, - "01", "2021-01-03T10:56", 1, "Flu", - "01", "2021-03-04", 2, "Cough", - "01", "2021", 3, "Flu" + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVENTDESC = "AE", SRCDOM = "AE", @@ -308,25 +306,29 @@ test_that("by_vars parameter works correctly", { ) }) -test_that("an error is issued if some of the by variables are missing", { - adsl <- tibble::tribble( +## Test 5: an error is issued if some of the by variables are missing ---- +test_that("derive_param_tte Test 5: an error is issued if some of the by variables are missing", { + adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") ) %>% mutate(STUDYID = "AB42") - ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, - "01", "2021-01-03T10:56", 1, "Flu", - "01", "2021-03-04", 2, "Cough", - "01", "2021", 3, "Flu" + ae <- tribble( + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVENTDESC = "AE", SRCDOM = "AE", @@ -364,8 +366,10 @@ test_that("an error is issued if some of the by variables are missing", { regexp = "^Only AEDECOD are included in source dataset.*" ) }) -test_that("an error is issued all by variables are missing in all source datasets", { - adsl <- tibble::tribble( + +## Test 6: errors if all by vars are missing in all source datasets ---- +test_that("derive_param_tte Test 6: errors if all by vars are missing in all source datasets", { + adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") @@ -373,16 +377,19 @@ test_that("an error is issued all by variables are missing in all source dataset mutate(STUDYID = "AB42") ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, - "01", "2021-01-03T10:56", 1, "Flu", - "01", "2021-03-04", 2, "Cough", - "01", "2021", 3, "Flu" + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVENTDESC = "AE", SRCDOM = "AE", @@ -422,25 +429,29 @@ test_that("an error is issued all by variables are missing in all source dataset ) }) -test_that("an error is issued if there is no one to one mapping between PARAMCD and by_vars", { - adsl <- tibble::tribble( +## Test 7: errors if PARAMCD and by_vars are not one to one ---- +test_that("derive_param_tte Test 7: errors if PARAMCD and by_vars are not one to one", { + adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") ) %>% mutate(STUDYID = "AB42") - ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, - "01", "2021-01-03T10:56", 1, "Flu", - "01", "2021-03-04", 2, "Cough", - "01", "2021", 3, "Flu" + ae <- tribble( + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVENTDESC = "AE", SRCDOM = "AE", @@ -481,25 +492,29 @@ test_that("an error is issued if there is no one to one mapping between PARAMCD ) }) -test_that("an error if issued set_values_to contains invalid expressions", { - adsl <- tibble::tribble( +## Test 8: errors if set_values_to contains invalid expressions ---- +test_that("derive_param_tte Test 8: errors if set_values_to contains invalid expressions", { + adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") ) %>% mutate(STUDYID = "AB42") - ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, - "01", "2021-01-03T10:56", 1, "Flu", - "01", "2021-03-04", 2, "Cough", - "01", "2021", 3, "Flu" + ae <- tribble( + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVENTDESC = "AE", SRCDOM = "AE", @@ -548,95 +563,29 @@ test_that("an error if issued set_values_to contains invalid expressions", { ) }) -test_that("new observations analysis datetime based on DTC variables are derived correctly", { - adsl <- tibble::tribble( - ~USUBJID, ~TRTSDTM, ~EOSDT, - "01", ymd_hms("2020-12-06 14:23:00"), ymd("2021-03-06"), - "02", ymd_hms("2021-01-16 13:09:00"), ymd("2021-02-03") - ) %>% - mutate(STUDYID = "AB42") - - ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, - "01", "2021-01-03T10:56", 1, - "01", "2021-03-04", 2, - "01", "2021-03", 3 - ) %>% - mutate(STUDYID = "AB42") - - ttae <- event_source( - dataset_name = "ae", - date = AESTDTC, - set_values_to = vars( - EVENTDESC = "AE", - SRCDOM = "AE", - SRCVAR = "AESTDTC", - SRCSEQ = AESEQ - ) - ) - - eos <- censor_source( - dataset_name = "adsl", - date = EOSDT, - censor = 1, - set_values_to = vars( - EVENTDESC = "END OF STUDY", - SRCDOM = "ADSL", - SRCVAR = "EOSDT" - ) - ) - - expected_output <- tibble::tribble( - ~USUBJID, ~ADTM, ~CNSR, ~EVENTDESC, ~SRCDOM, ~SRCVAR, ~SRCSEQ, - "01", ymd_hms("2021-01-03 10:56:00"), 0L, "AE", "AE", "AESTDTC", 1, - "02", ymd_hms("2021-02-03 00:00:00"), 1L, "END OF STUDY", "ADSL", "EOSDT", NA - ) %>% - mutate( - STUDYID = "AB42", - PARAMCD = "TTAE", - PARAM = "Time to First Adverse Event" - ) %>% - left_join(select(adsl, USUBJID, STARTDTM = TRTSDTM), by = "USUBJID") - - actual_output <- derive_param_tte( - dataset_adsl = adsl, - start_date = TRTSDTM, - event_conditions = list(ttae), - censor_conditions = list(eos), - source_datasets = list(adsl = adsl, ae = ae), - create_datetime = TRUE, - set_values_to = vars( - PARAMCD = "TTAE", - PARAM = "Time to First Adverse Event" - ) - ) - - expect_dfs_equal( - actual_output, - expected_output, - keys = c("USUBJID", "PARAMCD") - ) -}) - -test_that("error is issued if parameter code already exists", { - adsl <- tibble::tribble( +## Test 9: error is issued if parameter code already exists ---- +test_that("derive_param_tte Test 9: error is issued if parameter code already exists", { + adsl <- tribble( ~USUBJID, ~TRTSDT, ~EOSDT, "01", ymd("2020-12-06"), ymd("2021-03-06"), "02", ymd("2021-01-16"), ymd("2021-02-03") ) %>% mutate(STUDYID = "AB42") - ae <- tibble::tribble( - ~USUBJID, ~AESTDTC, ~AESEQ, - "01", "2021-01-03T10:56", 1, - "01", "2021-03-04", 2, - "01", "2021", 3 + ae <- tribble( + ~USUBJID, ~AESTDTC, ~AESEQ, ~AEDECOD, + "01", "2021-01-03", 1, "Flu", + "01", "2021-03-04", 2, "Cough", + "01", "2021-01-01", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = ymd(AESTDTC) + ) ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVENTDESC = "AE", SRCDOM = "AE", @@ -685,7 +634,9 @@ test_that("error is issued if parameter code already exists", { ) }) -test_that("`tte_source` objects are printed as intended", { +# print.tte_source ---- +## Test 10: tte_source` objects are printed as intended ---- +test_that("`print.tte_source Test 10: tte_source` objects are printed as intended", { ttae <- event_source( dataset_name = "ae", date = AESTDTC, diff --git a/tests/testthat/test-derive_var_atoxgr.R b/tests/testthat/test-derive_var_atoxgr.R new file mode 100644 index 0000000000..f4f48d4f95 --- /dev/null +++ b/tests/testthat/test-derive_var_atoxgr.R @@ -0,0 +1,2069 @@ + +# derive_var_atoxgr ---- + +test_that("derive_var_atoxgr: Test 1 ATOXGR cannot be graded", { + exp_out_1 <- tibble::tribble( + ~ATOXDSCL, ~ATOXDSCH, ~ATOXGRL, ~ATOXGRH, ~ATOXGR, + NA_character_, NA_character_, NA_character_, NA_character_, NA_character_, + "Hypophosphatemia", NA_character_, NA_character_, NA_character_, NA_character_, + NA_character_, "Hyperglycemia", NA_character_, NA_character_, NA_character_, + "Hypoglycemia", "Hyperglycemia", NA_character_, NA_character_, NA_character_, + # ATOXGRL is ungradable so cannot say ATOXGR is normal + "Hypoglycemia", "Hyperglycemia", NA_character_, "0", NA_character_, + # ATOXGRH is ungradable so cannot say ATOXGR is normal + "Hypoglycemia", "Hyperglycemia", "0", NA_character_, NA_character_, + ) + + input_1 <- exp_out_1 %>% + select(-ATOXGR) + + expect_equal( + derive_var_atoxgr( + input_1, + lotox_description_var = ATOXDSCL, + hitox_description_var = ATOXDSCH + ), + exp_out_1 + ) +}) + +test_that("derive_var_atoxgr: Test 2 ATOXGR = 0 (normal)", { + exp_out_2 <- tibble::tribble( + ~ATOXDSCL, ~ATOXDSCH, ~ATOXGRL, ~ATOXGRH, ~ATOXGR, + "Hypoglycemia", "Hyperglycemia", "0", "0", "0", + NA_character_, "INR Increased", NA_character_, "0", "0", + "Hypophosphatemia", NA_character_, "0", NA_character_, "0", + ) + + input_2 <- exp_out_2 %>% + select(-ATOXGR) + + expect_equal( + derive_var_atoxgr( + input_2, + lotox_description_var = ATOXDSCL, + hitox_description_var = ATOXDSCH + ), + exp_out_2 + ) +}) + +test_that("derive_var_atoxgr: Test 3 ATOXGR > 0 (HYPER)", { + exp_out_3 <- tibble::tribble( + ~ATOXDSCL, ~ATOXDSCH, ~ATOXGRL, ~ATOXGRH, ~ATOXGR, + "Hypoglycemia", "Hyperglycemia", NA_character_, "1", "1", + "Hypoglycemia", "Hyperglycemia", "0", "4", "4", + NA_character_, "INR Increased", NA_character_, "2", "2", + ) + + input_3 <- exp_out_3 %>% + select(-ATOXGR) + + expect_equal( + derive_var_atoxgr( + input_3, + lotox_description_var = ATOXDSCL, + hitox_description_var = ATOXDSCH + ), + exp_out_3 + ) +}) + +test_that("derive_var_atoxgr: Test 4 ATOXGR < 0 (HYPO)", { + exp_out_4 <- tibble::tribble( + ~ATOXDSCL, ~ATOXDSCH, ~ATOXGRL, ~ATOXGRH, ~ATOXGR, + "Hypoglycemia", "Hyperglycemia", "3", NA_character_, "-3", + "Hypoglycemia", "Hyperglycemia", "1", "0", "-1", + "Hypophosphatemia", NA_character_, "4", NA_character_, "-4", + ) + + input_4 <- exp_out_4 %>% + select(-ATOXGR) + + expect_equal( + derive_var_atoxgr( + input_4, + lotox_description_var = ATOXDSCL, + hitox_description_var = ATOXDSCH + ), + exp_out_4 + ) +}) + + +## Blood and lymphatic system disorders ---- +## Grade 3: <80 g/L +## Grade 2: <100 - 80g/L +## Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_1 <- derive_var_atoxgr_dir( + input_ctcv4_1, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_1, + compare = actual_output_ctcv4_1, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 2. Leukocytosis ---- +### Grade 3: >100,000/mm3 + +test_that("derive_var_atoxgr_dir: Test 2 NCICTCAEv4 Leukocytosis", { + exp_out_ctcv4_2 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 99, 0, NA, "10^9/L", NA, + NA, 99, 0, NA, "10^9/L", NA, + "Leukocytosis", 101, 0, 40, "10^9/L", "3", + "leukocytosis", 100, 0, 40, "10^9/L", "0", + "Leukocytosis", 99, 0, NA, "10^9/L", "0", + # wrong UNIT - GRADE should be missing + "Leukocytosis", 99, 0, 40, "10^9/M", NA, + # Unit missing cannot grade + "Leukocytosis", 99, 0, 40, NA, NA, + # AVAL missing cannot grade + "Leukocytosis", NA, 0, 40, "10^9/L", NA, + ) + input_ctcv4_2 <- exp_out_ctcv4_2 %>% + select(-ATOXGRH) + + actual_output_ctcv4_2 <- derive_var_atoxgr_dir( + input_ctcv4_2, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_2, + compare = actual_output_ctcv4_2, + keys = c("ATOXDSCH", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +## Investigations ---- + +### 3. Activated partial thromboplastin time prolonged ---- +### Grade 3: >2.5 x ULN +### Grade 2: >1.5 - 2.5 x ULN +### Grade 1: >ULN - 1.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 3 CTCAEv4 Activated partial thromboplastin time prolonged", { + exp_out_ctcv4_3 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 100, NA_character_, NA, + NA_character_, 60, 100, NA_character_, NA, + "Activated partial thromboplastin time prolonged", 251, 100, NA_character_, "3", + "Activated Partial thromboplastin time prolonged", 250, 100, NA_character_, "2", + "Activated partial Thromboplastin time prolonged", 151, 100, NA_character_, "2", + "Activated partial thromboplastin time prolonged", 150, 100, NA_character_, "1", + "Activated partial thromboplastin Time prolonged", 101, 100, NA_character_, "1", + "Activated partial thromboplastin time prolonged", 100, 100, NA_character_, "0", + # ANRHI missing - cannot grade + "Activated partial thromboplastin time prolonged", 100, NA, NA_character_, NA, + # AVAL missing cannot grade + "Activated partial thromboplastin time prolonged", NA, 100, NA_character_, NA, + ) + input_ctcv4_3 <- exp_out_ctcv4_3 %>% + select(-ATOXGRH) + + actual_output_ctcv4_3 <- derive_var_atoxgr_dir( + input_ctcv4_3, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_3, + compare = actual_output_ctcv4_3, + keys = c("ATOXDSCH", "AVAL", "ANRHI", "AVALU") + ) +}) + +### 4. Alanine aminotransferase increased ---- +### Grade 4: >20.0 x ULN +### Grade 3: >5.0 - 20.0 x ULN +### Grade 2: >3.0 - 5.0 x ULN +### Grade 1: >ULN - 3.0 x ULN + +test_that("derive_var_atoxgr_dir: Test 4 NCICTCAEv4 Alanine aminotransferase increased", { + exp_out_ctcv4_4 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 40, NA_character_, NA, + NA_character_, 60, 40, NA_character_, NA, + "Alanine aminotransferase Increased", 801, 40, NA_character_, "4", + "Alanine aminotransferase Increased", 800, 40, NA_character_, "3", + "Alanine aminotransferase Increased", 201, 40, NA_character_, "3", + "Alanine aminotransferase Increased", 200, 40, NA_character_, "2", + "Alanine aminotransferase Increased", 121, 40, NA_character_, "2", + "Alanine aminotransferase Increased", 120, 40, NA_character_, "1", + "Alanine aminotransferase Increased", 41, 40, NA_character_, "1", + "Alanine aminotransferase Increased", 40, 40, NA_character_, "0", + # ANRHI missing - cannot grade + "Alanine aminotransferase Increased", 100, NA, NA_character_, NA, + # AVAL missing cannot grade + "Alanine aminotransferase Increased", NA, 40, NA_character_, NA, + ) + input_ctcv4_4 <- exp_out_ctcv4_4 %>% + select(-ATOXGRH) + + actual_output_ctcv4_4 <- derive_var_atoxgr_dir( + input_ctcv4_4, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_4, + compare = actual_output_ctcv4_4, + keys = c("ATOXDSCH", "AVAL", "ANRHI", "AVALU") + ) +}) + +### 5. Alkaline phosphatase increased ---- +### Grade 4: >20.0 x ULN +### Grade 3: >5.0 - 20.0 x ULN +### Grade 2: >2.5 - 5.0 x ULN +### Grade 1: >ULN - 2.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 5 NCICTCAEv4 Alkaline phosphatase increased", { + exp_out_ctcv4_5 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 40, NA_character_, NA, + NA_character_, 60, 40, NA_character_, NA, + "Alkaline phosphatase increased", 801, 40, NA_character_, "4", + "Alkaline phosphatase increased", 800, 40, NA_character_, "3", + "Alkaline phosphatase increased", 201, 40, NA_character_, "3", + "Alkaline phosphatase increased", 200, 40, NA_character_, "2", + "Alkaline phosphatase increased", 101, 40, NA_character_, "2", + "Alkaline phosphatase increased", 100, 40, NA_character_, "1", + "Alkaline phosphatase increased", 41, 40, NA_character_, "1", + "Alkaline phosphatase increased", 40, 40, NA_character_, "0", + # ANRHI missing - cannot grade + "Alkaline phosphatase increased", 100, NA, NA_character_, NA, + # AVAL missing cannot grade + "Alkaline phosphatase increased", NA, 40, NA_character_, NA, + ) + input_ctcv4_5 <- exp_out_ctcv4_5 %>% + select(-ATOXGRH) + + actual_output_ctcv4_5 <- derive_var_atoxgr_dir( + input_ctcv4_5, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_5, + compare = actual_output_ctcv4_5, + keys = c("ATOXDSCH", "AVAL", "ANRHI", "AVALU") + ) +}) + +### 6. Aspartate aminotransferase increased ---- +### Grade 4: >20.0 x ULN +### Grade 3: >5.0 - 20.0 x ULN +### Grade 2: >3.0 - 5.0 x ULN +### Grade 1: >ULN - 3.0 x ULN + +test_that("derive_var_atoxgr_dir: Test 6 NCICTCAEv4 Aspartate aminotransferase increased", { + exp_out_ctcv4_6 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 40, NA_character_, NA, + NA_character_, 60, 40, NA_character_, NA, + "Aspartate aminotransferase Increased", 801, 40, NA_character_, "4", + "Aspartate aminotransferase Increased", 800, 40, NA_character_, "3", + "Aspartate aminotransferase Increased", 201, 40, NA_character_, "3", + "Aspartate aminotransferase Increased", 200, 40, NA_character_, "2", + "Aspartate aminotransferase Increased", 121, 40, NA_character_, "2", + "Aspartate aminotransferase Increased", 120, 40, NA_character_, "1", + "Aspartate aminotransferase Increased", 41, 40, NA_character_, "1", + "Aspartate aminotransferase Increased", 40, 40, NA_character_, "0", + # ANRHI missing - cannot grade + "Aspartate aminotransferase Increased", 100, NA, NA_character_, NA, + # AVAL missing cannot grade + "Aspartate aminotransferase Increased", NA, 40, NA_character_, NA, + ) + input_ctcv4_6 <- exp_out_ctcv4_6 %>% + select(-ATOXGRH) + + actual_output_ctcv4_6 <- derive_var_atoxgr_dir( + input_ctcv4_6, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_6, + compare = actual_output_ctcv4_6, + keys = c("ATOXDSCH", "AVAL", "ANRHI", "AVALU") + ) +}) + +### 7. Blood bilirubin increased ---- +### Grade 4: >10.0 x ULN +### Grade 3: >3.0 - 10.0 x ULN +### Grade 2: >3.0 - 1.5 x ULN +### Grade 1: >ULN - 1.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 7 NCICTCAEv4 Blood bilirubin increased", { + exp_out_ctcv4_7 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 40, NA_character_, NA, + NA_character_, 60, 40, NA_character_, NA, + "Blood bilirubin increased", 401, 40, NA_character_, "4", + "Blood bilirubin increased", 400, 40, NA_character_, "3", + "Blood bilirubin increased", 121, 40, NA_character_, "3", + "Blood bilirubin increased", 120, 40, NA_character_, "2", + "Blood bilirubin increased", 61, 40, NA_character_, "2", + "Blood bilirubin increased", 60, 40, NA_character_, "1", + "Blood bilirubin increased", 41, 40, NA_character_, "1", + "Blood bilirubin increased", 40, 40, NA_character_, "0", + # ANRHI missing - cannot grade + "Blood bilirubin increased", 100, NA, NA_character_, NA, + # AVAL missing cannot grade + "Blood bilirubin increased", NA, 40, NA_character_, NA, + ) + input_ctcv4_7 <- exp_out_ctcv4_7 %>% + select(-ATOXGRH) + + actual_output_ctcv4_7 <- derive_var_atoxgr_dir( + input_ctcv4_7, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_7, + compare = actual_output_ctcv4_7, + keys = c("ATOXDSCH", "AVAL", "ANRHI", "AVALU") + ) +}) + +### 8. CD4 Lymphocytes decreased ---- +### Grade 4: <0.05 x 10e9 /L +### Grade 3: <0.2 x 0.05 - 10e9 /L +### Grade 2: <0.5 - 0.2 x 10e9 /L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_8 <- derive_var_atoxgr_dir( + input_ctcv4_8, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_8, + compare = actual_output_ctcv4_8, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "AVALU") + ) +}) + +### 9. Cholesterol high ---- +### Grade 4: >12.92 mmol/L +### Grade 3: >10.34 - 12.92 mmol/L +### Grade 2: >7.75 - 10.34 mmol/L +### Grade 1: >ULN - 7.75 mmol/L + +test_that("derive_var_atoxgr_dir: Test 9 NCICTCAEv4 Cholesterol high", { + exp_out_ctcv4_9 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 8, 0, 5, "mmol/L", NA, + NA_character_, 10, 0, 5, "mmol/L", NA, + "Cholesterol high", 12.93, 0, 5, "mmol/L", "4", + "Cholesterol High", 12.92, 0, 5, "mmol/L", "3", + "Cholesterol high", 10.35, 0, 5, "Mmol/L", "3", + # wrong unit - grade missing + "Cholesterol high", 10.35, 0, 5, "umol/L", NA, + "Cholesterol high", 10.34, 0, 5, "mmol/L", "2", + "Cholesterol high", 7.76, 0, 5, "mmol/L", "2", + "Cholesterol high", 7.75, 0, 5, "mmol/L", "1", + "Cholesterol high", 5.1, 0, 5, "mmol/L", "1", + "Cholesterol high", 5, 0, 5, "mmol/L", "0", + # ANRHI missing - AVAL satisfies grade 2 - 4 + "Cholesterol high", 12.93, 0, NA, "mmol/L", "4", + "Cholesterol High", 12.92, 0, NA, "mmol/L", "3", + "Cholesterol high", 10.35, 0, NA, "Mmol/L", "3", + "Cholesterol high", 10.34, 0, NA, "mmol/L", "2", + "Cholesterol high", 7.76, 0, NA, "mmol/L", "2", + # ANRHI missing - AVAL does NOT satisfies grade 2 - 4 + "Cholesterol high", 7.75, 0, NA, "mmol/L", NA, + "Cholesterol high", 5.1, 0, NA, "mmol/L", NA, + "Cholesterol high", 5, 0, NA, "mmol/L", NA, + # Unit missing - cannot grade + "Cholesterol high", 5, 0, 5, NA, NA, + # AVAL missing cannot grade + "Cholesterol high", NA, 0, 5, "mmol/L", NA, + ) + input_ctcv4_9 <- exp_out_ctcv4_9 %>% + select(-ATOXGRH) + + actual_output_ctcv4_9 <- derive_var_atoxgr_dir( + input_ctcv4_9, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_9, + compare = actual_output_ctcv4_9, + keys = c("ATOXDSCH", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 10. CPK increased ---- +### Grade 4: >10.0 x ULN +### Grade 3: >5.0 - 10.0 x ULN +### Grade 2: >2.5 - 5.0 x ULN +### Grade 1: >ULN - 2.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 10 NCICTCAEv4 CPK increased", { + exp_out_ctcv4_10 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 0, 40, NA_character_, NA, + NA_character_, 60, 0, 40, NA_character_, NA, + "CPK increased", 401, 0, 40, NA_character_, "4", + "CPK increased", 400, 0, 40, NA_character_, "3", + "CPK increased", 201, 0, 40, NA_character_, "3", + "CPK increased", 200, 0, 40, NA_character_, "2", + "CPK increased", 101, 0, 40, NA_character_, "2", + "CPK increased", 100, 0, 40, NA_character_, "1", + "CPK increased", 41, 0, 40, NA_character_, "1", + "CPK increased", 40, 0, 40, NA_character_, "0", + # ANRHI missing - cannot grade + "CPK increased", 100, 0, NA, NA_character_, NA, + # AVAL missing cannot grade + "CPK increased", NA, 0, 40, NA_character_, NA, + ) + input_ctcv4_10 <- exp_out_ctcv4_10 %>% + select(-ATOXGRH) + + actual_output_ctcv4_10 <- derive_var_atoxgr_dir( + input_ctcv4_10, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_10, + compare = actual_output_ctcv4_10, + keys = c("ATOXDSCH", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 11. Creatinine increased ---- +### Grade 4: >6.0 x ULN +### Grade 3: >3.0 baseline; >3.0 - 6.0 x ULN +### Grade 2: >1.5 - 3.0 x baseline; >1.5 - 3.0 x ULN +### Grade 1: >1 - 1.5 x baseline; >ULN - 1.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 10 NCICTCAEv4 Creatinine increased", { + exp_out_ctcv4_11 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~BASE, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 80, 40, NA_character_, NA, + NA_character_, 60, 60, 40, NA_character_, NA, + # GRADE derived from AVAL against ANRHI + "Creatinine increased", 241, 241, 40, NA_character_, "4", + "Creatinine increased", 240, 230, 40, NA_character_, "3", + "Creatinine increased", 121, 120, 40, NA_character_, "3", + "Creatinine increased", 120, 119, 40, NA_character_, "2", + "Creatinine increased", 61, 60, 40, NA_character_, "2", + "Creatinine increased", 60, 60, 40, NA_character_, "1", + "Creatinine increased", 41, 41, 40, NA_character_, "1", + "Creatinine increased", 40, 40, 40, NA_character_, "0", + # GRADE derived from AVAL against BASE + "Creatinine increased", 42, 6, 40, NA_character_, "3", + "Creatinine increased", 42, 13.9, 40, NA_character_, "3", + "Creatinine increased", 42, 14, 40, NA_character_, "2", + "Creatinine increased", 42.1, 28, 40, NA_character_, "2", + "Creatinine increased", 42, 28, 42, NA_character_, "1", + "Creatinine increased", 42, 41, 42, NA_character_, "1", + "Creatinine increased", 42, 42, 42, NA_character_, "0", + # BASE missing - AVAL <= ANRLO cannot grade as NORMAL + "Creatinine increased", 42, NA, 42, NA_character_, NA, + # ANRHI missing - AVAL <= BASE cannot grade as NORMAL + "Creatinine increased", 42, 42, NA, NA_character_, NA, + # AVAL missing cannot grade + "Creatinine increased", NA, 0, 40, NA_character_, NA, + ) + input_ctcv4_11 <- exp_out_ctcv4_11 %>% + select(-ATOXGRH) + + actual_output_ctcv4_11 <- derive_var_atoxgr_dir( + input_ctcv4_11, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_11, + compare = actual_output_ctcv4_11, + keys = c("ATOXDSCH", "AVAL", "BASE", "ANRHI", "AVALU") + ) +}) + +### 12. Fibrinogen decreased decreased ---- +### Grade 4: <0.25 x LLN or 75% decrease from baseline or absolute value <50 mg/dL +### Grade 3: <0.5 - 0.25 x LLN or 50 - <75% decrease from baseline +### Grade 2: <0.75 - 0.5 x LLN or 25 - <50% decrease from baseline +### Grade 1: <1.0 - 0.75 x LLN or <25% decrease from baseline + +test_that("derive_var_atoxgr_dir: Test 12 NCICTCAEv4 Fibrinogen decreased", { + exp_out_ctcv4_12 <- tibble::tribble( + ~ATOXDSCL, ~AVAL, ~ANRLO, ~PCHG, ~AVALU, ~ATOXGRL, + "Not a term", 9, 10, 40, "g/L", NA, + NA_character_, 10, 10, 40, "g/L", NA, + # Satisfies < 0.5 for grade 4 - other criteria missing + "Fibrinogen decreased", 0.49, NA, NA, "g/L", "4", + # Satisfies < 0.5 for grade 4 - satisfies grade 3 for other criteria + "Fibrinogen decreased", 0.49, 1, -51, "g/L", "4", + # Satisfies < 0.25*LLN for grade 4 - PCHG missing + "Fibrinogen decreased", 0.5, 2.1, NA, "g/L", "4", + # Satisfies < 0.25*LLN for grade 4 - PCHG satisfies grade 3 + "Fibrinogen decreased", 0.5, 2.1, -51, "g/L", "4", + # Satisfies <=75% decrease for grade 4 - LLN missing + "Fibrinogen decreased", 1, NA, -75, "g/L", "4", + # Satisfies <=75% decrease for grade 4 - LLN satisfies grade 3 + "Fibrinogen decreased", 1, 0.49, -75, "g/L", "4", + # Satisfies < 0.5*LLN for grade 3 - PCHG missing + "Fibrinogen decreased", 1, 2.1, NA, "g/L", "3", + # Satisfies < 0.5*LLN for grade 3 - PCHG satisfies grade 2 + "Fibrinogen decreased", 1, 2.1, -49, "g/L", "3", + # Satisfies <=50% decrease for grade 3 - LLN missing + "Fibrinogen decreased", 1, NA, -50, "g/L", "3", + # Satisfies <=50% decrease for grade 3 - LLN satisfies grade 2 + "Fibrinogen decreased", 1, 2, -50, "g/L", "3", + # Satisfies < 0.75*LLN for grade 2 - PCHG missing + "Fibrinogen decreased", 1.5, 2.1, NA, "g/L", "2", + # Satisfies < 0.75*LLN for grade 2 - PCHG satisfies grade 1 + "Fibrinogen decreased", 1.5, 2.1, -10, "g/L", "2", + # Satisfies <=25% for grade 2 - LLN missing + "Fibrinogen decreased", 1.5, NA, -25, "g/L", "2", + # Satisfies <=25% for grade 2 - LLN satisfies grade 1 + "Fibrinogen decreased", 1.5, 1.6, -25, "g/L", "2", + # Satisfies < LLN for grade 1 - PCHG missing + "Fibrinogen decreased", 2, 2.1, NA, "g/L", "1", + # Satisfies < LLN for grade 1 - PCHG satisfies grade 0 + "Fibrinogen decreased", 2, 2.1, 10, "g/L", "1", + # Satisfies % decrease for grade 1 - LLN missing + "Fibrinogen decreased", 1.5, NA, -1, "g/L", "1", + # Satisfies % decrease for grade 1 - AVAL = LLN + "Fibrinogen decreased", 1.5, 1.5, -1, "g/L", "1", + # Satisfies grade 0 - AVAL >= LLN AND no % descrease + "Fibrinogen decreased", 1.5, 1.5, 0, "g/L", "0", + # AVAL >= LLN BUT PCT missing cannot grade as NORMAL + "Fibrinogen decreased", 1.5, 1.5, NA, "g/L", NA, + # PCT >= 0 BUT LLN missing cannot grade as NORMAL + "Fibrinogen decreased", 1.5, NA, 10, "g/L", NA, + # AVAL missing cannot grade + "Fibrinogen decreased", NA, 1.5, 10, "g/L", NA, + # wrong unit cannot grade as it may satisfy grade 4 + "Fibrinogen decreased", 1.5, 1.5, 0, "g/dL", NA, + # missing unit cannot grade as it may satisfy grade 4 + "Fibrinogen decreased", 1.5, 1.5, 0, NA, NA, + ) + input_ctcv4_12 <- exp_out_ctcv4_12 %>% + select(-ATOXGRL) + + actual_output_ctcv4_12 <- derive_var_atoxgr_dir( + input_ctcv4_12, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_12, + compare = actual_output_ctcv4_12, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "PCHG", "AVALU") + ) +}) + +### 13. GGT increased ---- +### Grade 4: >20.0 x ULN +### Grade 3: >5.0 - 20.0 x ULN +### Grade 2: >2.5 - 5.0 x ULN +### Grade 1: >ULN - 2.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 13 NCICTCAEv4 GGT increased", { + exp_out_ctcv4_13 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 0, 40, NA_character_, NA, + NA_character_, 60, 0, 40, NA_character_, NA, + "GGT increased", 801, 0, 40, NA_character_, "4", + "GGT increased", 800, 0, 40, NA_character_, "3", + "GGT increased", 201, 0, 40, NA_character_, "3", + "GGT increased", 200, 0, 40, NA_character_, "2", + "GGT increased", 101, 0, 40, NA_character_, "2", + "GGT increased", 100, 0, 40, NA_character_, "1", + "GGT increased", 41, 0, 40, NA_character_, "1", + "GGT increased", 40, 0, 40, NA_character_, "0", + # ANRHI missing - cannot grade + "GGT increased", 100, 0, NA, NA_character_, NA, + # AVAL missing cannot grade + "GGT increased", NA, 0, NA, NA_character_, NA, + ) + input_ctcv4_13 <- exp_out_ctcv4_13 %>% + select(-ATOXGRH) + + actual_output_ctcv4_13 <- derive_var_atoxgr_dir( + input_ctcv4_13, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_13, + compare = actual_output_ctcv4_13, + keys = c("ATOXDSCH", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 14. Haptoglobin decreased ---- +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_14 <- derive_var_atoxgr_dir( + input_ctcv4_14, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_14, + compare = actual_output_ctcv4_14, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 15. Hemoglobin increased ---- +### Grade 3: Increase in >4 gm/dL above ULN or above baseline if baseline is above ULN +### Grade 2: Increase in >2 - 4 gm/dL above ULN or above baseline if baseline is above ULN +### Grade 1: Increase in >0 - 2 gm/dL above ULN or above baseline if baseline is above ULN + +test_that("derive_var_atoxgr_dir: Test 15 NCICTCAEv4 Hemoglobin increased", { + exp_out_ctcv4_15 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~BASE, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 80, 120, 200, "g/L", NA, 1, + NA_character_, 60, 50, 100, "g/L", NA, 2, + # BASE greater than ANRHI + "Hemoglobin increased", 106, 65, 60, "g/L", "3", 3, + "Hemoglobin increased", 105, 65, 60, "g/L", "2", 4, + "Hemoglobin increased", 86, 65, 60, "g/L", "2", 5, + "Hemoglobin increased", 85, 65, 60, "g/L", "1", 6, + "Hemoglobin increased", 66, 65, 60, "g/L", "1", 7, + "Hemoglobin increased", 65, 65, 60, "g/L", "0", 8, + "Hemoglobin increased", NA, 65, 60, "g/L", NA, 9, + # BASE less than or equal to ANRHI + "Hemoglobin increased", 106, 60, 65, "g/L", "3", 10, + "Hemoglobin increased", 105, 60, 65, "g/L", "2", 11, + "Hemoglobin increased", 86, 60, 65, "g/L", "2", 12, + "Hemoglobin increased", 85, 60, 65, "g/L", "1", 13, + "Hemoglobin increased", 66, 60, 65, "g/L", "1", 14, + "Hemoglobin increased", 65, 60, 65, "g/L", "0", 15, + "Hemoglobin increased", NA, 60, 65, "g/L", NA, 16, + # BASE missing + "Hemoglobin increased", 106, NA, 65, "g/L", "3", 17, + "Hemoglobin increased", 105, NA, 65, "g/L", "2", 18, + "Hemoglobin increased", 86, NA, 65, "g/L", "2", 19, + "Hemoglobin increased", 85, NA, 65, "g/L", "1", 20, + "Hemoglobin increased", 66, NA, 65, "g/L", "1", 21, + "Hemoglobin increased", 65, NA, 65, "g/L", "0", 22, + "Hemoglobin increased", NA, NA, 65, "g/L", NA, 23, + # Unit missing cannot grade + "Hemoglobin increased", 200, 61, 65, NA, NA, 24, + # ANRHI missing - cannot grade + "Hemoglobin increased", 200, 60, NA, "g/L", NA, 25, + # AVAL missing cannot grade + "Hemoglobin increased", NA, 60, 65, "g/L", NA, 26, + ) + input_ctcv4_15 <- exp_out_ctcv4_15 %>% + select(-ATOXGRH) + + actual_output_ctcv4_15 <- derive_var_atoxgr_dir( + input_ctcv4_15, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_15, + compare = actual_output_ctcv4_15, + keys = c("TESTNUM") + ) +}) + +### 16. INR increased ---- +### Grade 3: >2.5 x ULN; >2.5 times above baseline if on anticoagulation +### Grade 2: >1.5 - 2.5 x ULN; >1.5 - 2.5 times above baseline if on anticoagulation +### Grade 1: >1 - 1.5 x ULN; >1 - 1.5 times above baseline if on anticoagulation + +test_that("derive_var_atoxgr_dir: Test 16 NCICTCAEv4 INR increased", { + exp_out_ctcv4_16 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~BASE, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 80, 120, 200, NA_character_, NA, 1, + NA_character_, 60, 50, 100, NA_character_, NA, 2, + # GRADE derived from AVAL against ANRHI + "INR IncreaSed", 251, 200, 100, NA_character_, "3", 3, + "INR Increased", 250, 199, 100, NA_character_, "2", 4, + "INR Increased", 151, 150, 100, NA_character_, "2", 5, + "INR Increased", 150, 150, 100, NA_character_, "1", 6, + "INR Increased", 101, 150, 100, NA_character_, "1", 7, + "INR Increased", 100, 100, 100, NA_character_, "0", 8, + # GRADE derived from AVAL against BASE + "INR IncreaSed", 251, 100, 200, NA_character_, "3", 9, + "INR Increased", 250, 100, 199, NA_character_, "2", 10, + "INR Increased", 151, 100, 150, NA_character_, "2", 11, + "INR Increased", 150, 100, 150, NA_character_, "1", 12, + "INR Increased", 101, 100, 150, NA_character_, "1", 13, + "INR Increased", 100, 100, 100, NA_character_, "0", 14, + # BASE missing - AVAL <= ANRLO cannot grade as NORMAL + "INR Increased", 100, NA, 100, NA_character_, NA, 15, + # ANRHI missing - AVAL <= BASE cannot grade as NORMAL + "INR Increased", 100, 100, NA, NA_character_, NA, 16, + # AVAL missing cannot grade + "INR Increased", NA, 100, 100, NA_character_, NA, 17, + ) + input_ctcv4_16 <- exp_out_ctcv4_16 %>% + select(-ATOXGRH) + + actual_output_ctcv4_16 <- derive_var_atoxgr_dir( + input_ctcv4_16, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_16, + compare = actual_output_ctcv4_16, + keys = c("TESTNUM") + ) +}) + +### 17. Lipase increased ---- +### Grade 4: >5.0 x ULN +### Grade 3: >2.0 - 5.0 x ULN +### Grade 2: >1.5 - 2.0 x ULN +### Grade 1: >ULN - 1.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 17 NCICTCAEv4 Lipase increased", { + exp_out_ctcv4_17 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 120, 200, NA_character_, NA, + NA_character_, 60, 50, 100, NA_character_, NA, + "Lipase IncreaSed", 501, 0, 100, NA_character_, "4", + "Lipase Increased", 500, 0, 100, NA_character_, "3", + "Lipase Increased", 201, 0, 100, NA_character_, "3", + "Lipase Increased", 200, 0, 100, NA_character_, "2", + "Lipase Increased", 151, 0, 100, NA_character_, "2", + "Lipase Increased", 150, 0, 100, NA_character_, "1", + "Lipase Increased", 101, 0, 100, NA_character_, "1", + "Lipase Increased", 100, 0, 100, NA_character_, "0", + # ANRHI missing cannot grade + "Lipase Increased", 200, 0, NA, NA_character_, NA, + # AVAL missing cannot grade + "Lipase Increased", NA, 0, 100, NA_character_, NA, + ) + input_ctcv4_17 <- exp_out_ctcv4_17 %>% + select(-ATOXGRH) + + actual_output_ctcv4_17 <- derive_var_atoxgr_dir( + input_ctcv4_17, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_17, + compare = actual_output_ctcv4_17, + keys = c("AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 18. Lymphocyte count decreased ---- +### Grade 4: <0.2 x 10e9 /L +### Grade 3: <0.5 - 0.2 x 10e9 /L +### Grade 2: <0.8 - 0.5 x 10e9 /L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_18 <- derive_var_atoxgr_dir( + input_ctcv4_18, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_18, + compare = actual_output_ctcv4_18, + keys = c("TESTNUM") + ) +}) + +### 19. Lymphocyte count increased ---- +### Grade 3: >20,000/mm3 +### Grade 2: >4000/mm3 - 20,000/mm3 +test_that("derive_var_atoxgr_dir: Test 19 NCICTCAEv4 Lymphocyte count increased", { + exp_out_ctcv4_19 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 120, 200, "10^9/L", NA, + NA_character_, 60, 50, 100, "10^9/L", NA, + "Lymphocyte count increased", 21, NA, NA, "10^9/L", "3", + "Lymphocyte count increased", 20, NA, NA, "10^9/L", "2", + "Lymphocyte count increased", 4.1, NA, NA, "10^9/L", "2", + "Lymphocyte count increased", 4, NA, NA, "10^9/L", "0", + # Unit missing cannot grade + "Lymphocyte count increased", 4, NA, NA, NA, NA, + # AVAL missing cannot grade + "Lymphocyte count increased", NA, NA, NA, "10^9/L", NA, + ) + input_ctcv4_19 <- exp_out_ctcv4_19 %>% + select(-ATOXGRH) + + actual_output_ctcv4_19 <- derive_var_atoxgr_dir( + input_ctcv4_19, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_19, + compare = actual_output_ctcv4_19, + keys = c("AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 20. Neutrophil count decreased ---- +### Grade 4: <25.0 x 10e9 /L +### Grade 3: <1.0 - 0.5 x 10e9 /L +### Grade 2: <1.5 - 1.0 x 10e9 /L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_20 <- derive_var_atoxgr_dir( + input_ctcv4_20, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_20, + compare = actual_output_ctcv4_20, + keys = c("AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 21. Platelet count decreased ---- +### Grade 4: <25.0 x 10e9 /L +### Grade 3: <50.0 - 25.0 x 10e9 /L +### Grade 2: <75.0 - 50.0 x 10e9 /L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_21 <- derive_var_atoxgr_dir( + input_ctcv4_21, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_21, + compare = actual_output_ctcv4_21, + keys = c("TESTNUM") + ) +}) + +### 22. Serum amylase increased ---- +### Grade 4: >5.0 x ULN +### Grade 3: >2.0 - 5.0 x ULN +### Grade 2: >1.5 - 2.0 x ULN +### Grade 1: >ULN - 1.5 x ULN + +test_that("derive_var_atoxgr_dir: Test 22 NCICTCAEv4 Serum amylase increased", { + exp_out_ctcv4_22 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 80, 120, 200, NA_character_, NA, + NA_character_, 60, 50, 100, NA_character_, NA, + "Serum amylase increased", 501, 0, 100, NA_character_, "4", + "Serum amylase increased", 500, 0, 100, NA_character_, "3", + "Serum amylase increased", 201, 0, 100, NA_character_, "3", + "Serum amylase increased", 200, 0, 100, NA_character_, "2", + "Serum amylase increased", 151, 0, 100, NA_character_, "2", + "Serum amylase increased", 150, 0, 100, NA_character_, "1", + "Serum amylase increased", 101, 0, 100, NA_character_, "1", + "Serum amylase increased", 100, 0, 100, NA_character_, "0", + # ANRHI missing cannot grade + "Serum amylase increased", 200, 0, NA, NA_character_, NA, + # AVAL missing cannot grade + "Serum amylase increased", NA, 0, 100, NA_character_, NA, + ) + input_ctcv4_22 <- exp_out_ctcv4_22 %>% + select(-ATOXGRH) + + actual_output_ctcv4_22 <- derive_var_atoxgr_dir( + input_ctcv4_22, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_22, + compare = actual_output_ctcv4_22, + keys = c("AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 23. White blood cell decreased ---- +### Grade 4: <1.0 x 10e9 /L +### Grade 3: <2.0 - 1.0 x 10e9 /L +### Grade 2: <3.0 - 2.0 x 10e9 /L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_23 <- derive_var_atoxgr_dir( + input_ctcv4_23, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_23, + compare = actual_output_ctcv4_23, + keys = c("TESTNUM") + ) +}) + +# Metabolism and nutrition disorders ---- + +### 24. Hypercalcemia ---- +### Grade 4: >3.4 mmol/L +### Grade 3: >3.1 - 3.4 mmol/L +### Grade 2: >2.9 - 3.1 mmol/L +### Grade 1: >ULN - 2.9 mmol/L + +test_that("derive_var_atoxgr_dir: Test 24 NCICTCAEv4 Hypercalcemia", { + exp_out_ctcv4_24 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 3.5, 0, 2.5, "mmol/L", NA, 1, + NA_character_, 3.5, 0, 2.5, "mmol/L", NA, 2, + # ANRHI not missing + "Hypercalcemia", 3.5, 0, 2.5, "mmol/L", "4", 3, + "Hypercalcemia", 3.4, 0, 2.5, "mmol/L", "3", 4, + "Hypercalcemia", 3.2, 0, 2.5, "mmol/L", "3", 5, + "Hypercalcemia", 3.1, 0, 2.5, "mmol/L", "2", 6, + "Hypercalcemia", 3, 0, 2.5, "mmol/L", "2", 7, + "Hypercalcemia", 2.9, 0, 2.5, "mmol/L", "1", 8, + "Hypercalcemia", 2.6, 0, 2.5, "mmol/L", "1", 9, + "Hypercalcemia", 2.5, 0, 2.5, "mmol/L", "0", 10, + # ANRHI missing - can grade 2-4 + "Hypercalcemia", 3.5, 0, NA, "mmol/L", "4", 11, + "Hypercalcemia", 3.4, 0, NA, "mmol/L", "3", 12, + "Hypercalcemia", 3.2, 0, NA, "mmol/L", "3", 13, + "Hypercalcemia", 3.1, 0, NA, "mmol/L", "2", 14, + "Hypercalcemia", 3, 0, NA, "mmol/L", "2", 15, + # ANRHI missing - can NOT grade 0 or 1 + "Hypercalcemia", 2.9, 0, NA, "mmol/L", NA, 16, + "Hypercalcemia", 2.6, 0, NA, "mmol/L", NA, 17, + "Hypercalcemia", 2.5, 0, NA, "mmol/L", NA, 18, + # Unit missing cannot grade + "Hypercalcemia", 2.5, 0, 2.5, NA, NA, 19, + # AVAL missing cannot grade + "Hypercalcemia", NA, 0, 2.5, "mmol/L", NA, 20, + ) + input_ctcv4_24 <- exp_out_ctcv4_24 %>% + select(-ATOXGRH) + + actual_output_ctcv4_24 <- derive_var_atoxgr_dir( + input_ctcv4_24, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_24, + compare = actual_output_ctcv4_24, + keys = c("TESTNUM") + ) +}) + +### 25. Hypercalcemia (Ionized) ---- +### Grade 4: >1.8 mmol/L +### Grade 3: >1.6 - 1.8 mmol/L +### Grade 2: >1.5 - 1.6 mmol/L +### Grade 1: >ULN - 1.5 mmol/L + +test_that("derive_var_atoxgr_dir: Test 25 NCICTCAEv4 Hypercalcemia (Ionized)", { + exp_out_ctcv4_25 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 1.9, 0, 1.3, "mmol/L", NA, 1, + NA_character_, 1.9, 0, 1.3, "mmol/L", NA, 2, + # ANRHI not missing + "Hypercalcemia (Ionized)", 1.9, 0, 1.3, "mmol/L", "4", 3, + "Hypercalcemia (Ionized)", 1.8, 0, 1.3, "mmol/L", "3", 4, + "Hypercalcemia (Ionized)", 1.7, 0, 1.3, "mmol/L", "3", 5, + "Hypercalcemia (Ionized)", 1.6, 0, 1.3, "mmol/L", "2", 6, + "Hypercalcemia (Ionized)", 1.51, 0, 1.3, "mmol/L", "2", 7, + "Hypercalcemia (Ionized)", 1.5, 0, 1.3, "mmol/L", "1", 8, + "Hypercalcemia (Ionized)", 1.4, 0, 1.3, "mmol/L", "1", 9, + "Hypercalcemia (Ionized)", 1.3, 0, 1.3, "mmol/L", "0", 10, + # ANRHI missing - can grade 2-4 + "Hypercalcemia (Ionized)", 1.9, 0, NA, "mmol/L", "4", 11, + "Hypercalcemia (Ionized)", 1.8, 0, NA, "mmol/L", "3", 12, + "Hypercalcemia (Ionized)", 1.7, 0, NA, "mmol/L", "3", 13, + "Hypercalcemia (Ionized)", 1.6, 0, NA, "mmol/L", "2", 14, + "Hypercalcemia (Ionized)", 1.51, 0, NA, "mmol/L", "2", 15, + # ANRHI missing - can NOT grade 0 or 1 + "Hypercalcemia (Ionized)", 1.5, 0, NA, "mmol/L", NA, 16, + "Hypercalcemia (Ionized)", 1.4, 0, NA, "mmol/L", NA, 17, + "Hypercalcemia (Ionized)", 1.3, 0, NA, "mmol/L", NA, 18, + # Unit missing cannot grade 1, + "Hypercalcemia (Ionized)", 1.3, 0, 1.3, NA, NA, 19, + # AVAL missing cannot grade + "Hypercalcemia (Ionized)", NA, 0, 1.3, "mmol/L", NA, 20, + ) + input_ctcv4_25 <- exp_out_ctcv4_25 %>% + select(-ATOXGRH) + + actual_output_ctcv4_25 <- derive_var_atoxgr_dir( + input_ctcv4_25, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_25, + compare = actual_output_ctcv4_25, + keys = c("TESTNUM") + ) +}) + +### 26. Hyperglycemia (Fasting) ---- +### Grade 4: >27.8 mmol/L +### Grade 3: >13.9 - 27.8 mmol/L +### Grade 2: >8.9 - 13.9 mmol/L +### Grade 1: >ULN - 8.9 mmol/L + +test_that("derive_var_atoxgr_dir: Test 26 NCICTCAEv4 Hyperglycemia (Fasting)", { + exp_out_ctcv4_26 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 27.9, 0, 5.3, "mmol/L", NA, 1, + NA_character_, 27.9, 0, 5.3, "mmol/L", NA, 2, + # ANRHI not missing + "Hyperglycemia (Fasting)", 27.9, 0, 5.3, "mmol/L", "4", 3, + "Hyperglycemia (Fasting)", 27.8, 0, 5.3, "mmol/L", "3", 4, + "Hyperglycemia (Fasting)", 14, 0, 5.3, "mmol/L", "3", 5, + "Hyperglycemia (Fasting)", 13.9, 0, 5.3, "mmol/L", "2", 6, + "Hyperglycemia (Fasting)", 9, 0, 5.3, "mmol/L", "2", 7, + "Hyperglycemia (Fasting)", 8.9, 0, 5.3, "mmol/L", "1", 8, + "Hyperglycemia (Fasting)", 5.4, 0, 5.3, "mmol/L", "1", 9, + "Hyperglycemia (Fasting)", 5.3, 0, 5.3, "mmol/L", "0", 10, + # ANRHI missing - can grade 2-4 + "Hyperglycemia (Fasting)", 27.9, 0, NA, "mmol/L", "4", 11, + "Hyperglycemia (Fasting)", 27.8, 0, NA, "mmol/L", "3", 12, + "Hyperglycemia (Fasting)", 14, 0, NA, "mmol/L", "3", 13, + "Hyperglycemia (Fasting)", 13.9, 0, NA, "mmol/L", "2", 14, + "Hyperglycemia (Fasting)", 9, 0, NA, "mmol/L", "2", 15, + # ANRHI missing - can NOT grade 0 or 1 + "Hyperglycemia (Fasting)", 8.9, 0, NA, "mmol/L", NA, 16, + "Hyperglycemia (Fasting)", 5.4, 0, NA, "mmol/L", NA, 17, + "Hyperglycemia (Fasting)", 5.3, 0, NA, "mmol/L", NA, 18, + # Unit missing cannot grade + "Hyperglycemia (Fasting)", 5.3, 0, 5.3, NA, NA, 19, + # AVAL missing cannot grade + "Hyperglycemia (Fasting)", NA, 0, 5.3, "mmol/L", NA, 20, + ) + input_ctcv4_26 <- exp_out_ctcv4_26 %>% + select(-ATOXGRH) + + actual_output_ctcv4_26 <- derive_var_atoxgr_dir( + input_ctcv4_26, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_26, + compare = actual_output_ctcv4_26, + keys = c("TESTNUM") + ) +}) + +### 27. Hyperglycemia ---- +### Grade 4: >27.8 mmol/L +### Grade 3: >13.9 - 27.8 mmol/L + +test_that("derive_var_atoxgr_dir: Test 27 NCICTCAEv4 Hyperglycemia", { + exp_out_ctcv4_27 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 27.9, 0, 5.3, "mmol/L", NA, 1, + NA_character_, 27.9, 0, 5.3, "mmol/L", NA, 2, + "Hyperglycemia", 27.9, 0, 5.3, "mmol/L", "4", 3, + "Hyperglycemia", 27.8, 0, 5.3, "mmol/L", "3", 4, + "Hyperglycemia", 14, 0, 5.3, "mmol/L", "3", 5, + "Hyperglycemia", 13.9, 0, 5.3, "mmol/L", "0", 6, + "Hyperglycemia", 5.3, 0, NA, "mmol/L", "0", 7, + # Unit missing cannot grade + "Hyperglycemia", 5.3, 0, 5.3, NA, NA, 8, + # AVAL missing cannot grade + "Hyperglycemia", NA, 0, 5.3, "mmol/L", NA, 9, + ) + input_ctcv4_27 <- exp_out_ctcv4_27 %>% + select(-ATOXGRH) + + actual_output_ctcv4_27 <- derive_var_atoxgr_dir( + input_ctcv4_27, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_27, + compare = actual_output_ctcv4_27, + keys = c("TESTNUM") + ) +}) + +### 28. Hyperkalemia ---- +### Grade 4: >7.0 mmol/L +### Grade 3: >6.0 - 7.0 mmol/L +### Grade 2: >5.5 - 6.0 mmol/L +### Grade 1: >ULN - 5.5 mmol/L + +test_that("derive_var_atoxgr_dir: Test 28 NCICTCAEv4 Hyperkalemia", { + exp_out_ctcv4_28 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 7.1, 0, 5.1, "mmol/L", NA, 1, + NA_character_, 7.1, 0, 5.1, "mmol/L", NA, 2, + # ANRHI not missing + "Hyperkalemia", 7.1, 0, 5.1, "mmol/L", "4", 3, + "Hyperkalemia", 7, 0, 5.1, "mmol/L", "3", 4, + "Hyperkalemia", 6.1, 0, 5.1, "mmol/L", "3", 5, + "Hyperkalemia", 6, 0, 5.1, "mmol/L", "2", 6, + "Hyperkalemia", 5.6, 0, 5.1, "mmol/L", "2", 7, + "Hyperkalemia", 5.5, 0, 5.1, "mmol/L", "1", 8, + "Hyperkalemia", 5.2, 0, 5.1, "mmol/L", "1", 9, + "Hyperkalemia", 5.1, 0, 5.1, "mmol/L", "0", 10, + # ANRHI missing - can grade 2-4 + "Hyperkalemia", 7.1, 0, NA, "mmol/L", "4", 11, + "Hyperkalemia", 7, 0, NA, "mmol/L", "3", 12, + "Hyperkalemia", 6.1, 0, NA, "mmol/L", "3", 13, + "Hyperkalemia", 6, 0, NA, "mmol/L", "2", 14, + "Hyperkalemia", 5.6, 0, NA, "mmol/L", "2", 15, + # ANRHI missing - can NOT grade 0 or 1 + "Hyperkalemia", 5.5, 0, NA, "mmol/L", NA, 16, + "Hyperkalemia", 5.2, 0, NA, "mmol/L", NA, 17, + "Hyperkalemia", 5.1, 0, NA, "mmol/L", NA, 18, + # Unit missing cannot grade + "Hyperkalemia", 5.1, 0, 5.1, NA, NA, 19, + # AVAL missing cannot grade + "Hyperkalemia", NA, 0, 5.1, "mmol/L", NA, 20, + ) + input_ctcv4_28 <- exp_out_ctcv4_28 %>% + select(-ATOXGRH) + + actual_output_ctcv4_28 <- derive_var_atoxgr_dir( + input_ctcv4_28, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_28, + compare = actual_output_ctcv4_28, + keys = c("TESTNUM") + ) +}) + +### 29. Hypermagnesemia ---- +### Grade 4: >3.30 mmol/L +### Grade 3: >1.23 - 3.30 mmol/L +### Grade 1: >ULN - 1.23 mmol/L + +test_that("derive_var_atoxgr_dir: Test 29 NCICTCAEv4 Hypermagnesemia", { + exp_out_ctcv4_29 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 3.4, 0, 0.8, "mmol/L", NA, 1, + NA_character_, 3.4, 0, 0.8, "mmol/L", NA, 2, + # ANRHI not missing + "Hypermagnesemia", 3.4, 0, 0.8, "mmol/L", "4", 3, + "Hypermagnesemia", 3.3, 0, 0.8, "mmol/L", "3", 4, + "Hypermagnesemia", 1.24, 0, 0.8, "mmol/L", "3", 5, + "Hypermagnesemia", 1.23, 0, 0.8, "mmol/L", "1", 6, + "Hypermagnesemia", 0.81, 0, 0.8, "mmol/L", "1", 7, + "Hypermagnesemia", 0.8, 0, 0.8, "mmol/L", "0", 8, + # ANRHI missing - can grade 3-4 + "Hypermagnesemia", 3.4, 0, NA, "mmol/L", "4", 9, + "Hypermagnesemia", 3.3, 0, NA, "mmol/L", "3", 10, + "Hypermagnesemia", 1.24, 0, NA, "mmol/L", "3", 11, + # ANRHI missing - can NOT grade 0 or 1 + "Hypermagnesemia", 1.23, 0, NA, "mmol/L", NA, 12, + "Hypermagnesemia", 0.81, 0, NA, "mmol/L", NA, 13, + "Hypermagnesemia", 0.8, 0, NA, "mmol/L", NA, 14, + # Unit missing cannot grade + "Hypermagnesemia", 0.8, 0, 0.8, NA, NA, 15, + # AVAL missing cannot grade + "Hypermagnesemia", NA, 0, 0.8, "mmol/L", NA, 16, + ) + input_ctcv4_29 <- exp_out_ctcv4_29 %>% + select(-ATOXGRH) + + actual_output_ctcv4_29 <- derive_var_atoxgr_dir( + input_ctcv4_29, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_29, + compare = actual_output_ctcv4_29, + keys = c("TESTNUM") + ) +}) + +### 30. Hypernatremia ---- +### Grade 4: >160 mmol/L +### Grade 3: >155 - 160 mmol/L +### Grade 2: >150 - 155 mmol/L +### Grade 1: >ULN - 150 mmol/L + +test_that("derive_var_atoxgr_dir: Test 30 NCICTCAEv4 Hypernatremia", { + exp_out_ctcv4_30 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, ~TESTNUM, + "Not a term", 161, 0, 140, "mmol/L", NA, 1, + NA_character_, 161, 0, 140, "mmol/L", NA, 2, + # ANRHI not missing + "Hypernatremia", 161, 0, 140, "mmol/L", "4", 3, + "Hypernatremia", 160, 0, 140, "mmol/L", "3", 4, + "Hypernatremia", 156, 0, 140, "mmol/L", "3", 5, + "Hypernatremia", 155, 0, 140, "mmol/L", "2", 6, + "Hypernatremia", 151, 0, 140, "mmol/L", "2", 7, + "Hypernatremia", 150, 0, 140, "mmol/L", "1", 8, + "Hypernatremia", 141, 0, 140, "mmol/L", "1", 9, + "Hypernatremia", 140, 0, 140, "mmol/L", "0", 10, + # ANRHI missing - can grade 3-4 + "Hypernatremia", 161, 0, NA, "mmol/L", "4", 11, + "Hypernatremia", 160, 0, NA, "mmol/L", "3", 12, + "Hypernatremia", 156, 0, NA, "mmol/L", "3", 13, + "Hypernatremia", 155, 0, NA, "mmol/L", "2", 14, + "Hypernatremia", 151, 0, NA, "mmol/L", "2", 15, + # ANRHI missing - can NOT grade 0 or 1 + "Hypernatremia", 150, 0, NA, "mmol/L", NA, 16, + "Hypernatremia", 141, 0, NA, "mmol/L", NA, 17, + "Hypernatremia", 140, 0, NA, "mmol/L", NA, 18, + # Unit missing cannot grade + "Hypernatremia", 140, 0, 140, NA, NA, 19, + # AVAL missing cannot grade + "Hypernatremia", NA, 0, 140, "mmol/L", NA, 20, + ) + input_ctcv4_30 <- exp_out_ctcv4_30 %>% + select(-ATOXGRH) + + actual_output_ctcv4_30 <- derive_var_atoxgr_dir( + input_ctcv4_30, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_30, + compare = actual_output_ctcv4_30, + keys = c("TESTNUM") + ) +}) + +### 31. Hypertriglyceridemia ---- +### Grade 4: >11.4 mmol/L +### Grade 3: >5.7 mmol/L - 11.4 mmol/L +### Grade 2: >3.42 mmol/L - 5.7 mmol/L +### Grade 1: 1.71 mmol/L - 3.42 mmol/L + +test_that("derive_var_atoxgr_dir: Test 31 NCICTCAEv4 Hypertriglyceridemia", { + exp_out_ctcv4_31 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 11.5, 0, 2.1, "mmol/L", NA, + NA_character_, 11.5, 0, 2.1, "mmol/L", NA, + "Hypertriglyceridemia", 11.5, 0, 2.1, "mmol/L", "4", + "Hypertriglyceridemia", 11.4, 0, 2.1, "mmol/L", "3", + "Hypertriglyceridemia", 5.8, 0, 2.1, "mmol/L", "3", + "Hypertriglyceridemia", 5.7, 0, 2.1, "mmol/L", "2", + "Hypertriglyceridemia", 3.43, 0, 2.1, "mmol/L", "2", + "Hypertriglyceridemia", 3.42, 0, 2.1, "mmol/L", "1", + "Hypertriglyceridemia", 1.72, 0, 2.1, "mmol/L", "1", + "Hypertriglyceridemia", 1.71, 0, 2.1, "mmol/L", "0", + # Unit missing cannot grade + "Hypertriglyceridemia", 1.71, 0, 2.1, NA, NA, + # AVAL missing cannot grade + "Hypertriglyceridemia", NA, 0, 2.1, "mmol/L", NA, + ) + input_ctcv4_31 <- exp_out_ctcv4_31 %>% + select(-ATOXGRH) + + actual_output_ctcv4_31 <- derive_var_atoxgr_dir( + input_ctcv4_31, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_31, + compare = actual_output_ctcv4_31, + keys = c("ATOXDSCH", "AVAL", "AVALU") + ) +}) + +### 32. Hyperuricemia ---- +### Grade 4: >0.59 mmol/L; +### Grade 3: >ULN - 10 mg/dL (0.59 mmol/L) + +test_that("derive_var_atoxgr_dir: Test 32 NCICTCAEv4 Hyperuricemia", { + exp_out_ctcv4_32 <- tibble::tribble( + ~ATOXDSCH, ~AVAL, ~ANRLO, ~ANRHI, ~AVALU, ~ATOXGRH, + "Not a term", 591, 0, 200, "umol/L", NA, + NA_character_, 591, 0, 200, "umol/L", NA, + # ANRHI not missing + "Hyperuricemia", 591, 0, 200, "umol/L", "4", + "Hyperuricemia", 590, 0, 200, "umol/L", "3", + "Hyperuricemia", 201, 0, 200, "umol/L", "3", + "Hyperuricemia", 200, 0, 200, "umol/L", "0", + # ANRHI missing - can grade 4 + "Hyperuricemia", 591, 0, NA, "umol/L", "4", + # ANRHI missing - can NOT grade 0 or 3 + "Hyperuricemia", 590, 0, NA, "umol/L", NA, + "Hyperuricemia", 201, 0, NA, "umol/L", NA, + "Hyperuricemia", 200, 0, NA, "umol/L", NA, + # Unit missing cannot grade + "Hyperuricemia", 200, 0, 200, NA, NA, + # AVAL missing cannot grade + "Hyperuricemia", NA, 0, 200, "umol/L", NA, + ) + input_ctcv4_32 <- exp_out_ctcv4_32 %>% + select(-ATOXGRH) + + + actual_output_ctcv4_32 <- derive_var_atoxgr_dir( + input_ctcv4_32, + new_var = ATOXGRH, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_32, + compare = actual_output_ctcv4_32, + keys = c("ATOXDSCH", "AVAL", "ANRHI", "AVALU") + ) +}) + +### 33. Hypoalbuminemia ---- +### Grade 3: <20 g/L +### Grade 2: <30 - 20 g/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_33 <- derive_var_atoxgr_dir( + input_ctcv4_33, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_33, + compare = actual_output_ctcv4_33, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + + +### 34. Hypocalcemia ---- +### Grade 4: <1.5 mmol/L +### Grade 3: <1.75 - 1.5 mmol/L +### Grade 2: <2.0 - 1.75 mmol/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_34 <- derive_var_atoxgr_dir( + input_ctcv4_34, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_34, + compare = actual_output_ctcv4_34, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 35. Hypocalcemia (Ionized) ---- +### Grade 4: <0.8 mmol/L +### Grade 3: <0.9 - 0.8 mmol/L +### Grade 2: <1.0 - 0.9 mmol/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_35 <- derive_var_atoxgr_dir( + input_ctcv4_35, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_35, + compare = actual_output_ctcv4_35, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 36. Hypoglycemia ---- +### Grade 4: <1.7 mmol/L +### Grade 3: <2.2 - 1.7 mmol/L +### Grade 2: <3.0 - 2.2 mmol/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_36 <- derive_var_atoxgr_dir( + input_ctcv4_36, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_36, + compare = actual_output_ctcv4_36, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 37. Hypokalemia ---- +### Grade 4: <2.5 mmol/L +### Grade 3: <3.0 - 2.5 mmol/L +### Grade 2: % + select(-ATOXGRL) + + actual_output_ctcv4_37 <- derive_var_atoxgr_dir( + input_ctcv4_37, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_37, + compare = actual_output_ctcv4_37, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 38. Hypomagnesemia ---- +### Grade 4: <0.3 mmol/L +### Grade 3: <0.4 - 0.3 mmol/L +### Grade 2: <0.5 - 0.4 mmol/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_38 <- derive_var_atoxgr_dir( + input_ctcv4_38, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_38, + compare = actual_output_ctcv4_38, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 39. Hyponatremia ---- +### Grade 4: <120 mmol/L +### Grade 3: <130 - 120 mmol/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_39 <- derive_var_atoxgr_dir( + input_ctcv4_39, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_39, + compare = actual_output_ctcv4_39, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) + +### 40. Hypophosphatemia ---- +### Grade 4: <0.3 mmol/L +### Grade 3: <0.6 - 0.3 mmol/L +### Grade 2: <0.8 - 0.6 mmol/L +### Grade 1: % + select(-ATOXGRL) + + actual_output_ctcv4_40 <- derive_var_atoxgr_dir( + input_ctcv4_40, + new_var = ATOXGRL, + meta_criteria = atoxgr_criteria_ctcv4, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = AVALU + ) + + expect_dfs_equal( + base = exp_out_ctcv4_40, + compare = actual_output_ctcv4_40, + keys = c("ATOXDSCL", "AVAL", "ANRLO", "ANRHI", "AVALU") + ) +}) diff --git a/tests/testthat/test-derive_var_confirmation_flag.R b/tests/testthat/test-derive_var_confirmation_flag.R new file mode 100644 index 0000000000..618c503134 --- /dev/null +++ b/tests/testthat/test-derive_var_confirmation_flag.R @@ -0,0 +1,247 @@ +library(tibble) +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "CR", + "1", 4, "SD", + "1", 5, "NE", + "2", 1, "SD", + "2", 2, "PR", + "2", 3, "PD", + "3", 1, "SD", + "4", 1, "PR", + "4", 2, "PD", + "4", 3, "SD", + "4", 4, "SD", + "4", 5, "PR" +) + +# derive_var_confirmation_flag ---- +## Test 1: filter without first_cond ---- +## Flagging any patient PR value that is followed by a CR or PR +test_that("derive_var_confirmation_flag Test 1: filter without first_cond", { + actual <- + derive_var_confirmation_flag( + data, + new_var = CONFFL, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + order = vars(AVISITN), + filter = AVALC == "PR" & AVALC.join %in% c("CR", "PR") + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, ~CONFFL, + "1", 1, "PR", "Y", + "1", 2, "CR", NA_character_, + "1", 3, "CR", NA_character_, + "1", 4, "SD", NA_character_, + "1", 5, "NE", NA_character_, + "2", 1, "SD", NA_character_, + "2", 2, "PR", NA_character_, + "2", 3, "PD", NA_character_, + "3", 1, "SD", NA_character_, + "4", 1, "PR", "Y", + "4", 2, "PD", NA_character_, + "4", 3, "SD", NA_character_, + "4", 4, "SD", NA_character_, + "4", 5, "PR", NA_character_ + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +## Test 2: filter with first_cond ---- +## Flagging any patient CR value that is followed by a CR +test_that("derive_var_confirmation_flag Test 2: filter with first_cond", { + data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "CR", + "1", 4, "SD", + "1", 5, "NE", + "2", 1, "SD", + "2", 2, "PR", + "2", 3, "PD", + "3", 1, "CR", + "4", 1, "CR", + "4", 2, "SD", + "4", 3, "CR", + "4", 4, "CR" + ) + actual <- + derive_var_confirmation_flag( + data, + new_var = CONFFL, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + first_cond = AVALC == "CR" & + AVALC.join == "CR", + order = vars(AVISITN), + filter = TRUE + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, ~CONFFL, + "1", 1, "PR", NA_character_, + "1", 2, "CR", "Y", + "1", 3, "CR", NA_character_, + "1", 4, "SD", NA_character_, + "1", 5, "NE", NA_character_, + "2", 1, "SD", NA_character_, + "2", 2, "PR", NA_character_, + "2", 3, "PD", NA_character_, + "3", 1, "CR", NA_character_, + "4", 1, "CR", "Y", + "4", 2, "SD", NA_character_, + "4", 3, "CR", "Y", + "4", 4, "CR", NA_character_ + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +## Test 3: filter with first_cond and summary function ---- +## Flagging any patient PR value that is followed by a CR or PR +## and at most one SD in between +test_that("derive_var_confirmation_flag Test 3: filter with first_cond and summary function", { + actual <- + derive_var_confirmation_flag( + data, + new_var = CONFFL, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + first_cond = AVALC == "PR" & + AVALC.join %in% c("CR", "PR"), + order = vars(AVISITN), + filter = count_vals(AVALC.join, "SD") <= 1, + false_value = "N" + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, ~CONFFL, + "1", 1, "PR", "Y", + "1", 2, "CR", "N", + "1", 3, "CR", "N", + "1", 4, "SD", "N", + "1", 5, "NE", "N", + "2", 1, "SD", "N", + "2", 2, "PR", "N", + "2", 3, "PD", "N", + "3", 1, "SD", "N", + "4", 1, "PR", "N", + "4", 2, "PD", "N", + "4", 3, "SD", "N", + "4", 4, "SD", "N", + "4", 5, "PR", "N" + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +## Test 4: join_type = "all" ---- +## Flagging observations with a duration longer than 30 and +## on or after 7 days of a COVID AE (ACOVFL == "Y") +test_that("derive_var_confirmation_flag, Test 4: join_type = 'all'", { + adae <- tribble( + ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, + "1", 10, "N", 1, + "1", 21, "N", 50, + "1", 23, "Y", 14, + "1", 32, "N", 31, + "1", 42, "N", 20, + "2", 2, "N", 33, + "2", 11, "Y", 13, + "2", 23, "N", 2, + "3", 13, "Y", 12, + "4", 14, "N", 32, + "4", 21, "N", 41 + ) + + actual <- derive_var_confirmation_flag( + adae, + by_vars = vars(USUBJID), + new_var = ALCOVFL, + join_vars = vars(ACOVFL, ADY), + join_type = "all", + order = vars(ADY), + filter = ADURN > 30 & ACOVFL.join == "Y" & ADY >= ADY.join - 7 + ) + + expected <- tribble( + ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, ~ALCOVFL, + "1", 10, "N", 1, NA_character_, + "1", 21, "N", 50, "Y", + "1", 23, "Y", 14, NA_character_, + "1", 32, "N", 31, "Y", + "1", 42, "N", 20, NA_character_, + "2", 2, "N", 33, NA_character_, + "2", 11, "Y", 13, NA_character_, + "2", 23, "N", 2, NA_character_, + "3", 13, "Y", 12, NA_character_, + "4", 14, "N", 32, NA_character_, + "4", 21, "N", 41, NA_character_ + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "ADY") + ) +}) + +## Test 5: join_type = "before" ---- +## Flagging observations with AVALC = Y and an observation with CRIT1FL = Y before +test_that("derive_var_confirmation_flag, Test 5: join_type = 'before'", { + data <- tribble( + ~USUBJID, ~ASEQ, ~AVALC, ~CRIT1FL, + "1", 1, "Y", "Y", + "1", 2, "N", "N", + "1", 3, "Y", "N", + "2", 1, "Y", "Y", + "3", 1, "N", "Y" + ) + + actual <- derive_var_confirmation_flag( + data, + by_vars = vars(USUBJID), + order = vars(ASEQ), + new_var = CONFFL, + join_vars = vars(CRIT1FL), + join_type = "before", + filter = AVALC == "Y" & CRIT1FL.join == "Y", + false_value = "N" + ) + + expected <- tribble( + ~USUBJID, ~ASEQ, ~AVALC, ~CRIT1FL, ~CONFFL, + "1", 1, "Y", "Y", "N", + "1", 2, "N", "N", "N", + "1", 3, "Y", "N", "Y", + "2", 1, "Y", "Y", "N", + "3", 1, "N", "Y", "N" + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "ASEQ") + ) +}) diff --git a/tests/testthat/test-derive_var_disposition_status.R b/tests/testthat/test-derive_var_disposition_status.R index d567d3f320..c1f9851830 100644 --- a/tests/testthat/test-derive_var_disposition_status.R +++ b/tests/testthat/test-derive_var_disposition_status.R @@ -3,7 +3,8 @@ dm <- tibble::tribble( "TEST01", "PAT01", "TEST01", "PAT02", "TEST01", "PAT03", - "TEST01", "PAT04" + "TEST01", "PAT04", + "TEST01", "PAT05" ) ds <- tibble::tribble( @@ -21,7 +22,8 @@ ds <- tibble::tribble( "TEST01", "PAT03", "DISPOSITION EVENT", "PROGRESSIVE DISEASE", "2021-05-01", "TEST01", "PAT03", "OTHER EVENT", "DEATH", "2022-04", "TEST01", "PAT04", "PROTOCOL MILESTONE", "INFORMED CONSENT OBTAINED", "2021-04-02", - "TEST01", "PAT04", "PROTOCOL MILESTONE", "RANDOMIZATION", "2021-04-11" + "TEST01", "PAT04", "PROTOCOL MILESTONE", "RANDOMIZATION", "2021-04-11", + "TEST01", "PAT05", "DISPOSITION EVENT", "SCREEN FAILURE", "2021-03-01" ) @@ -32,7 +34,8 @@ test_that("Derive EOSSTT using default mapping", { "TEST01", "PAT01", "DISCONTINUED", "TEST01", "PAT02", "COMPLETED", "TEST01", "PAT03", "DISCONTINUED", - "TEST01", "PAT04", "ONGOING" + "TEST01", "PAT04", "ONGOING", + "TEST01", "PAT05", "NOT STARTED" ) actual_output <- derive_var_disposition_status( @@ -53,9 +56,11 @@ test_that("Derive EOSSTT using default mapping", { test_that("Derive EOTSTT using a study specific mapping", { format_eosstt <- function(x) { case_when( + x == "SCREEN FAILURE" ~ "NOT STARTED", x == "COMPLETED" ~ "COMPLETED", x == "ADVERSE EVENT" ~ "DISCONTINUED DUE TO AE", - x %notin% c("ADVERSE EVENT", "COMPLETED") & !is.na(x) ~ "DISCONTINUED NOT DUE TO AE", + x %notin% c("ADVERSE EVENT", "COMPLETED", "SCREEN FAILURE") & + !is.na(x) ~ "DISCONTINUED NOT DUE TO AE", TRUE ~ "ONGOING" ) } @@ -64,7 +69,8 @@ test_that("Derive EOTSTT using a study specific mapping", { "TEST01", "PAT01", "DISCONTINUED DUE TO AE", "TEST01", "PAT02", "COMPLETED", "TEST01", "PAT03", "DISCONTINUED NOT DUE TO AE", - "TEST01", "PAT04", "ONGOING" + "TEST01", "PAT04", "ONGOING", + "TEST01", "PAT05", "NOT STARTED" ) actual_output <- derive_var_disposition_status( diff --git a/tests/testthat/test-derive_var_dthcaus.R b/tests/testthat/test-derive_var_dthcaus.R index 0548b10541..df5f2ad2ba 100644 --- a/tests/testthat/test-derive_var_dthcaus.R +++ b/tests/testthat/test-derive_var_dthcaus.R @@ -1,4 +1,10 @@ -test_that("error on a dthcaus_source object with invalid mode", { +library(tibble) +library(dplyr) +library(lubridate) + +# dthcaus_source ---- +## Test 1: error on invalid mode ---- +test_that("dthcaus_source Test 1: error on invalid mode", { expect_error(dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", @@ -8,20 +14,25 @@ test_that("error on a dthcaus_source object with invalid mode", { )) }) -test_that("DTHCAUS is added from AE and DS", { - adsl <- tibble::tribble( +# derive_var_dthcaus ---- +## Test 2: DTHCAUS is added from AE and DS ---- +test_that("derive_var_dthcaus Test 2: DTHCAUS is added from AE and DS", { + adsl <- tribble( ~STUDYID, ~USUBJID, "TEST01", "PAT01", "TEST01", "PAT02", "TEST01", "PAT03" ) - ae <- tibble::tribble( + ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, "TEST01", "PAT03", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" - ) + ) %>% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) - ds <- tibble::tribble( + ds <- tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-01", "TEST01", "PAT01", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", @@ -33,12 +44,15 @@ test_that("DTHCAUS is added from AE and DS", { "TEST01", "PAT03", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", "TEST01", "PAT03", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT03", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01" - ) + ) %>% + mutate( + DSSTDT = ymd(DSSTDTC) + ) src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = AEDECOD ) @@ -46,12 +60,12 @@ test_that("DTHCAUS is added from AE and DS", { src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM ) - expected_output <- tibble::tribble( + expected_output <- tribble( ~STUDYID, ~USUBJID, ~DTHCAUS, "TEST01", "PAT01", "DEATH DUE TO PROGRESSION OF DISEASE", "TEST01", "PAT02", NA, @@ -67,19 +81,23 @@ test_that("DTHCAUS is added from AE and DS", { expect_dfs_equal(expected_output, actual_output, keys = "USUBJID") }) -test_that("`dthcaus` handles symbols and string literals correctly", { - adsl <- tibble::tribble( +## Test 3: `dthcaus` handles symbols and string literals correctly ---- +test_that("derive_var_dthcaus Test 3: `dthcaus` handles symbols and string literals correctly", { + adsl <- tribble( ~STUDYID, ~USUBJID, "TEST01", "PAT01", "TEST01", "PAT02" ) - ae <- tibble::tribble( + ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, "TEST01", "PAT01", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" - ) + ) %>% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) - ds <- tibble::tribble( + ds <- tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-02", "TEST01", "PAT01", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", @@ -88,12 +106,15 @@ test_that("`dthcaus` handles symbols and string literals correctly", { "TEST01", "PAT02", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT02", 3, "ADVERSE EVENT", "ADVERSE EVENT", "2021-12-01", "TEST01", "PAT02", 4, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2022-02-01" - ) + ) %>% + mutate( + DSSTDT = ymd(DSSTDTC) + ) src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = "Adverse Event" ) @@ -101,12 +122,12 @@ test_that("`dthcaus` handles symbols and string literals correctly", { src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM ) - expected_output <- tibble::tribble( + expected_output <- tribble( ~STUDYID, ~USUBJID, ~DTHCAUS, "TEST01", "PAT01", "Adverse Event", "TEST01", "PAT02", "DEATH DUE TO PROGRESSION OF DISEASE" @@ -121,20 +142,24 @@ test_that("`dthcaus` handles symbols and string literals correctly", { expect_dfs_equal(expected_output, actual_output, keys = "USUBJID") }) -test_that("DTHCAUS and traceability variables are added from AE and DS", { - adsl <- tibble::tribble( +## Test 4: DTHCAUS and traceability vars are added from AE and DS ---- +test_that("derive_var_dthcaus Test 4: DTHCAUS and traceability vars are added from AE and DS", { + adsl <- tribble( ~STUDYID, ~USUBJID, "TEST01", "PAT01", "TEST01", "PAT02", "TEST01", "PAT03" ) - ae <- tibble::tribble( + ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, "TEST01", "PAT03", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" - ) + ) %>% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) - ds <- tibble::tribble( + ds <- tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-01", "TEST01", "PAT01", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", @@ -146,12 +171,15 @@ test_that("DTHCAUS and traceability variables are added from AE and DS", { "TEST01", "PAT03", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", "TEST01", "PAT03", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT03", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01" - ) + ) %>% + mutate( + DSSTDT = ymd(DSSTDTC) + ) src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = AEDECOD, traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -160,13 +188,13 @@ test_that("DTHCAUS and traceability variables are added from AE and DS", { src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM, traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) ) - expected_output <- tibble::tribble( + expected_output <- tribble( ~STUDYID, ~USUBJID, ~DTHCAUS, ~DTHDOM, ~DTHSEQ, "TEST01", "PAT01", "DEATH DUE TO PROGRESSION OF DISEASE", "DS", 4, "TEST01", "PAT02", NA, NA, NA, @@ -182,21 +210,25 @@ test_that("DTHCAUS and traceability variables are added from AE and DS", { expect_dfs_equal(expected_output, actual_output, keys = "USUBJID") }) -test_that("DTHCAUS/traceabiity are added from AE and DS, info available in 2 input datasets", { - adsl <- tibble::tribble( +## Test 5: DTHCAUS/traceabiity are added from 2 input datasets ---- +test_that("derive_var_dthcaus Test 5: DTHCAUS/traceabiity are added from 2 input datasets", { + adsl <- tribble( ~STUDYID, ~USUBJID, "TEST01", "PAT01", "TEST01", "PAT02", "TEST01", "PAT03" ) - ae <- tibble::tribble( + ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, "TEST01", "PAT01", 14, "SUDDEN DEATH", "FATAL", "2021-04-04", "TEST01", "PAT03", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" - ) + ) %>% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) - ds <- tibble::tribble( + ds <- tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-01", "TEST01", "PAT01", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", @@ -208,12 +240,15 @@ test_that("DTHCAUS/traceabiity are added from AE and DS, info available in 2 inp "TEST01", "PAT03", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", "TEST01", "PAT03", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT03", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01" - ) + ) %>% + mutate( + DSSTDT = ymd(DSSTDTC) + ) src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", dthcaus = AEDECOD, traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -222,13 +257,13 @@ test_that("DTHCAUS/traceabiity are added from AE and DS, info available in 2 inp src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM, traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) ) - expected_output <- tibble::tribble( + expected_output <- tribble( ~STUDYID, ~USUBJID, ~DTHCAUS, ~DTHDOM, ~DTHSEQ, "TEST01", "PAT01", "DEATH DUE TO PROGRESSION OF DISEASE", "DS", 4, "TEST01", "PAT02", NA, NA, NA, @@ -244,57 +279,63 @@ test_that("DTHCAUS/traceabiity are added from AE and DS, info available in 2 inp expect_dfs_equal(expected_output, actual_output, keys = "USUBJID") }) -test_that("DTHCAUS/traceabiity are added from AE and DS, info available in 2 input datasets, partial dates", { # nolint - adsl <- tibble::tribble( +## Test 6: DTHCAUS is added from AE and DS if filter is not specified ---- +test_that("derive_var_dthcaus Test 6: DTHCAUS is added from AE and DS if filter is not specified", { + # test based on covr report - the case for unspecified filter has not been tested + + adsl <- tribble( ~STUDYID, ~USUBJID, "TEST01", "PAT01", "TEST01", "PAT02", "TEST01", "PAT03" ) - ae <- tibble::tribble( + ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, - "TEST01", "PAT01", 14, "SUDDEN DEATH", "FATAL", "2021-05", "TEST01", "PAT03", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" - ) + ) %>% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) - ds <- tibble::tribble( + ds <- tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-01", "TEST01", "PAT01", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT01", 3, "ADVERSE EVENT", "ADVERSE EVENT", "2021-12-01", - "TEST01", "PAT01", 4, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2021-02-03", + "TEST01", "PAT01", 4, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2022-02-01", "TEST01", "PAT02", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-02", "TEST01", "PAT02", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT02", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01", "TEST01", "PAT03", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", "TEST01", "PAT03", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", "TEST01", "PAT03", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01" - ) + ) %>% + mutate( + DSSTDT = ymd(DSSTDTC) + ) src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", - dthcaus = AEDECOD, - traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) + dthcaus = AEDECOD ) src_ds <- dthcaus_source( dataset_name = "ds", - filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + filter = NULL, + date = DSSTDT, mode = "first", - dthcaus = DSTERM, - traceability = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) + dthcaus = DSTERM ) - expected_output <- tibble::tribble( - ~STUDYID, ~USUBJID, ~DTHCAUS, ~DTHDOM, ~DTHSEQ, - "TEST01", "PAT01", "DEATH DUE TO PROGRESSION OF DISEASE", "DS", 4, - "TEST01", "PAT02", NA, NA, NA, - "TEST01", "PAT03", "SUDDEN DEATH", "AE", 12 + expected_output <- tribble( + ~STUDYID, ~USUBJID, ~DTHCAUS, + "TEST01", "PAT01", "INFORMED CONSENT OBTAINED", + "TEST01", "PAT02", "INFORMED CONSENT OBTAINED", + "TEST01", "PAT03", "INFORMED CONSENT OBTAINED" ) actual_output <- derive_var_dthcaus( @@ -306,56 +347,69 @@ test_that("DTHCAUS/traceabiity are added from AE and DS, info available in 2 inp expect_dfs_equal(expected_output, actual_output, keys = "USUBJID") }) -test_that("DTHCAUS is added from AE and DS if filter is not specified", { - # test based on covr report - the case for unspecified filter has not been tested +## Test 7: error on a dthcaus_source object with invalid order ---- +test_that("dthcaus_source Test 7: error on a dthcaus_source object with invalid order", { + expect_error(dthcaus_source( + dataset_name = "ae", + filter = AEOUT == "FATAL", + date = AEDTHDTC, + order = c(AESEQ), + mode = "first", + dthcaus = AEDECOD + )) +}) +## Test 8: dataset` is sorted using the `order` parameter ---- +test_that("derive_var_dthcaus Test 8: `dataset` is sorted using the `order` parameter", { adsl <- tibble::tribble( ~STUDYID, ~USUBJID, "TEST01", "PAT01", - "TEST01", "PAT02", - "TEST01", "PAT03" + "TEST01", "PAT02" ) ae <- tibble::tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AEDECOD, ~AEOUT, ~AEDTHDTC, - "TEST01", "PAT03", 12, "SUDDEN DEATH", "FATAL", "2021-04-04" - ) + "TEST01", "PAT01", 12, "SUDDEN DEATH", "FATAL", "2021-02-04" + ) %>% + mutate( + AEDTHDT = ymd(AEDTHDTC) + ) ds <- tibble::tribble( ~STUDYID, ~USUBJID, ~DSSEQ, ~DSDECOD, ~DSTERM, ~DSSTDTC, - "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-01", + "TEST01", "PAT01", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-02", "TEST01", "PAT01", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", - "TEST01", "PAT01", 3, "ADVERSE EVENT", "ADVERSE EVENT", "2021-12-01", - "TEST01", "PAT01", 4, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2022-02-01", - "TEST01", "PAT02", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-02", + "TEST01", "PAT01", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01", + "TEST01", "PAT02", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-01", "TEST01", "PAT02", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", - "TEST01", "PAT02", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01", - "TEST01", "PAT03", 1, "INFORMED CONSENT OBTAINED", "INFORMED CONSENT OBTAINED", "2021-04-03", - "TEST01", "PAT03", 2, "RANDOMIZATION", "RANDOMIZATION", "2021-04-11", - "TEST01", "PAT03", 3, "COMPLETED", "PROTOCOL COMPLETED", "2021-12-01" - ) + "TEST01", "PAT02", 3, "DEATH", "DEATH DUE TO ADVERSE EVENT", "2022-02-02", + "TEST01", "PAT02", 4, "DEATH", "DEATH DUE TO PROGRESSION OF DISEASE", "2022-02-02" + ) %>% + mutate( + DSSTDT = ymd(DSSTDTC) + ) src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AEDTHDTC, + date = AEDTHDT, mode = "first", - dthcaus = AEDECOD + dthcaus = "Adverse Event" ) src_ds <- dthcaus_source( dataset_name = "ds", - filter = NULL, - date = DSSTDTC, - mode = "first", + filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), + date = DSSTDT, + order = vars(DSSEQ), + mode = "last", dthcaus = DSTERM ) expected_output <- tibble::tribble( ~STUDYID, ~USUBJID, ~DTHCAUS, - "TEST01", "PAT01", "INFORMED CONSENT OBTAINED", - "TEST01", "PAT02", "INFORMED CONSENT OBTAINED", - "TEST01", "PAT03", "INFORMED CONSENT OBTAINED" + "TEST01", "PAT01", "Adverse Event", + "TEST01", "PAT02", "DEATH DUE TO PROGRESSION OF DISEASE" ) actual_output <- derive_var_dthcaus( diff --git a/tests/testthat/test-derive_var_extreme_date.R b/tests/testthat/test-derive_var_extreme_date.R index 8d089d5007..439bc3186b 100644 --- a/tests/testthat/test-derive_var_extreme_date.R +++ b/tests/testthat/test-derive_var_extreme_date.R @@ -1,31 +1,39 @@ -adsl <- tibble::tribble( - ~STUDYID, ~USUBJID, ~TRTEDTM, ~DTHDTC, - "STUDY01", "1", ymd_hms("2020-01-01T12:00:00"), NA_character_, - "STUDY01", "2", NA, "2020-06", - "STUDY01", "3", ymd_hms("2020-04-12T13:15:00"), NA_character_ -) - -ae <- tibble::tribble( - ~STUDYID, ~USUBJID, ~AESTDTC, ~AEENDTC, ~AESEQ, - "STUDY01", "1", "2019-11", "2019-11-23", 1, - "STUDY01", "1", "2020-02", "2020-02", 2, - "STUDY01", "3", "2020-02-02", "2020-02-03", 1, - "STUDY01", "3", "2020-04-11", NA_character_, 2 -) +library(tibble) +adsl <- tribble( + ~STUDYID, ~USUBJID, ~TRTEDTM, ~DTHDTC, + "STUDY01", "1", ymd_hms("2020-01-01T12:00:00"), NA_character_, + "STUDY01", "2", NA, "2020-06", + "STUDY01", "3", ymd_hms("2020-04-12T13:15:00"), NA_character_ +) %>% + mutate( + DTHDT = c(ymd(""), ymd("2020-06-01"), ymd("")) + ) + +ae <- tribble( + ~STUDYID, ~USUBJID, ~AESTDTC, ~AEENDTC, ~AESEQ, + "STUDY01", "1", "2019-11-01", "2019-11-23", 1, + "STUDY01", "1", "2020-02-01", "2020-02-01", 2, + "STUDY01", "3", "2020-02-02", "2020-02-03", 1, + "STUDY01", "3", "2020-04-11", NA_character_, 2 +) %>% + mutate( + AESTDT = ymd(AESTDTC), + AEENDT = ymd(AEENDTC), + AESTDTM = ymd_hms(paste(AESTDTC, "12:00:00")), + AEENDTM = ymd_hms(if_else(is.na(AEENDTC), "", paste(AEENDTC, "12:00:00"))) + ) # derive_var_extreme_dt ---- -## derive_var_extreme_dt: LSTALVDT is derived ---- -test_that("derive_var_extreme_dt: LSTALVDT is derived", { +## Test 1: LSTALVDT is derived ---- +test_that("derive_var_extreme_dt Test 1: LSTALVDT is derived", { ae_start <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first" + date = AESTDTM ) ae_end <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first" + date = AEENDTM ) adsl_trtdate <- date_source( @@ -35,7 +43,7 @@ test_that("derive_var_extreme_dt: LSTALVDT is derived", { adsl_dthdate <- date_source( dataset_name = "adsl", - date = DTHDTC, + date = DTHDT, filter = nchar(DTHDTC) >= 10 ) @@ -56,8 +64,8 @@ test_that("derive_var_extreme_dt: LSTALVDT is derived", { ) }) -## derive_var_extreme_dt: LSTALVDT is derived for Date class as well ---- -test_that("derive_var_extreme_dt: LSTALVDT is derived for Date class as well", { +## Test 2: LSTALVDT is derived for Date class as well ---- +test_that("derive_var_extreme_dt Test 2: LSTALVDT is derived for Date class as well", { adsl <- tibble::tribble( ~STUDYID, ~USUBJID, ~TRTEDTM, "STUDY01", "1", ymd_hms("2020-01-01T12:00:00"), @@ -90,13 +98,11 @@ test_that("derive_var_extreme_dt: LSTALVDT is derived for Date class as well", { }) # derive_var_extreme_dtm ---- -## derive_var_extreme_dtm: LSTALVDTM and traceability variables are derived ---- -test_that("derive_var_extreme_dtm: LSTALVDTM and traceability variables are derived", { +## Test 3: LSTALVDTM and traceability variables are derived ---- +test_that("derive_var_extreme_dtm Test 3: LSTALVDTM and traceability variables are derived", { ae_start <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "first", - time_imputation = "first", + date = AESTDTM, traceability_vars = vars( LALVDOM = "AE", LALVSEQ = AESEQ, @@ -106,9 +112,7 @@ test_that("derive_var_extreme_dtm: LSTALVDTM and traceability variables are deri ae_end <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "first", - time_imputation = "first", + date = AEENDTM, traceability_vars = vars( LALVDOM = "AE", LALVSEQ = AESEQ, @@ -128,8 +132,7 @@ test_that("derive_var_extreme_dtm: LSTALVDTM and traceability variables are deri adsl_dthdate <- date_source( dataset_name = "adsl", - date = DTHDTC, - time_imputation = "first", + date = DTHDT, filter = nchar(DTHDTC) >= 10, traceability_vars = vars( LALVDOM = "ADSL", @@ -140,7 +143,7 @@ test_that("derive_var_extreme_dtm: LSTALVDTM and traceability variables are deri expected_output <- adsl %>% mutate( - LSTALVDTM = c(ymd_hms("2020-02-01T00:00:00"), NA, ymd_hms("2020-04-12T13:15:00")), + LSTALVDTM = c(ymd_hms("2020-02-01T12:00:00"), NA, ymd_hms("2020-04-12T13:15:00")), LALVDOM = c("AE", NA_character_, "ADSL"), LALVSEQ = c(2, NA_integer_, NA_integer_), LALVVAR = c("AEENDTC", NA_character_, "TRTEDTM") @@ -160,3 +163,27 @@ test_that("derive_var_extreme_dtm: LSTALVDTM and traceability variables are deri keys = c("USUBJID") ) }) + +## Test 4: error is issued if DTC variable is specified ---- +test_that("derive_var_extreme_dtm Test 4: error is issued if DTC variable is specified", { + ae_start <- date_source( + dataset_name = "ae", + date = AESTDTC, + traceability_vars = vars( + LALVDOM = "AE", + LALVSEQ = AESEQ, + LALVVAR = "AESTDTC" + ) + ) + + expect_error( + derive_var_extreme_dtm( + adsl, + new_var = LSTALVDTM, + source_datasets = list(ae = ae), + ae_start, + mode = "last" + ), + regexp = "`AESTDTC` in dataset `ae` is not a date or datetime variable but is a character vector" # nolint + ) +}) diff --git a/tests/testthat/test-derive_var_last_dose_amt.R b/tests/testthat/test-derive_var_last_dose_amt.R index eb3b8a6567..20ca6c2fbf 100644 --- a/tests/testthat/test-derive_var_last_dose_amt.R +++ b/tests/testthat/test-derive_var_last_dose_amt.R @@ -1,15 +1,22 @@ -input_ae <- tibble::tribble( - ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, - "my_study", "subject1", 1, "2020-01-02", - "my_study", "subject1", 2, "2020-08-31", - "my_study", "subject1", 3, "2020-10-10", - "my_study", "subject2", 1, "2019-05-15", - "my_study", "subject2", 2, "2020-02-20", - "my_study", "subject3", 1, "2020-03-02", - "my_study", "subject4", 1, "2020-11-02" -) +library(tibble) +library(dplyr) +library(lubridate) -input_ex <- tibble::tribble( +input_ae <- tribble( + ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, + "my_study", "subject1", 1, "2020-01-02", + "my_study", "subject1", 2, "2020-08-31", + "my_study", "subject1", 3, "2020-10-10", + "my_study", "subject2", 1, "2019-05-15", + "my_study", "subject2", 2, "2020-02-20", + "my_study", "subject3", 1, "2020-03-02", + "my_study", "subject4", 1, "2020-11-02" +) %>% + mutate( + AESTDT = ymd(AESTDTC) + ) + +input_ex <- tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, "my_study", "subject1", "2020-01-01", "2020-01-01", 1, 10, "treatment", "my_study", "subject1", "2020-08-29", "2020-08-29", 2, 10, "treatment", @@ -19,10 +26,14 @@ input_ex <- tibble::tribble( "my_study", "subject2", "2020-01-20", "2020-01-20", 2, 0, "placebo", "my_study", "subject3", "2020-03-15", "2020-03-15", 1, 10, "treatment" ) %>% - mutate(EXSTDTC = as.Date(EXSTDTC), EXENDTC = as.Date(EXENDTC)) - + mutate( + EXSTDT = as.Date(EXSTDTC), + EXENDT = as.Date(EXENDTC) + ) -test_that("derive_var_last_dose_amt works as expected", { +# derive_var_last_dose_amt ---- +## Test 1: works as expected ---- +test_that("derive_var_last_dose_amt Test 1: works as expected", { expected_output <- mutate( input_ae, LDOSE = c(10, 10, 10, NA, 0, NA, NA) @@ -33,8 +44,8 @@ test_that("derive_var_last_dose_amt works as expected", { input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, new_var = LDOSE, dose_var = EXDOSE, single_dose_condition = (EXSTDTC == EXENDTC), @@ -44,7 +55,8 @@ test_that("derive_var_last_dose_amt works as expected", { expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) }) -test_that("derive_var_last_dose_amt returns traceability vars", { +## Test 2: returns traceability vars ---- +test_that("derive_var_last_dose_amt Test 2: returns traceability vars", { expected_output <- mutate( input_ae, LDOSEDOM = c("EX", "EX", "EX", NA, "EX", NA, NA), @@ -58,8 +70,8 @@ test_that("derive_var_last_dose_amt returns traceability vars", { input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, new_var = LDOSE, dose_var = EXDOSE, single_dose_condition = (EXSTDTC == EXENDTC), diff --git a/tests/testthat/test-derive_var_last_dose_date.R b/tests/testthat/test-derive_var_last_dose_date.R index e28d228937..4522e87085 100644 --- a/tests/testthat/test-derive_var_last_dose_date.R +++ b/tests/testthat/test-derive_var_last_dose_date.R @@ -1,4 +1,7 @@ -input_ae <- tibble::tribble( +library(tibble) +library(dplyr) +library(lubridate) +input_ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, "my_study", "subject1", 1, "2020-01-02", "my_study", "subject1", 2, "2020-08-31", @@ -7,9 +10,11 @@ input_ae <- tibble::tribble( "my_study", "subject2", 2, "2020-02-20", "my_study", "subject3", 1, "2020-03-02", "my_study", "subject4", 1, "2020-11-02" +) %>% mutate( + AESTDT = ymd(AESTDTC) ) -input_ex <- tibble::tribble( +input_ex <- tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, "my_study", "subject1", "2020-01-01", "2020-01-01", 1, 10, "treatment", "my_study", "subject1", "2020-08-29", "2020-08-29", 2, 10, "treatment", @@ -19,10 +24,11 @@ input_ex <- tibble::tribble( "my_study", "subject2", "2020-01-20", "2020-01-20", 2, 0, "placebo", "my_study", "subject3", "2020-03-15", "2020-03-15", 1, 10, "treatment" ) %>% - mutate(EXSTDTC = as.Date(EXSTDTC), EXENDTC = as.Date(EXENDTC)) + mutate(EXSTDT = as.Date(EXSTDTC), EXENDT = as.Date(EXENDTC)) - -test_that("derive_var_last_dose_date works as expected output_datetime = FALSE", { +# derive_var_last_dose_date ---- +## Test 1: works as expected output_datetime = FALSE ---- +test_that("derive_var_last_dose_date Test 1: works as expected output_datetime = FALSE", { expected_output <- tibble::tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, ~LDOSEDTM, "my_study", "subject1", 1, "2020-01-02", "2020-01-01", @@ -33,15 +39,18 @@ test_that("derive_var_last_dose_date works as expected output_datetime = FALSE", "my_study", "subject3", 1, "2020-03-02", NA_character_, "my_study", "subject4", 1, "2020-11-02", NA_character_ ) %>% - mutate(LDOSEDTM = as.Date(LDOSEDTM)) + mutate( + LDOSEDTM = as.Date(LDOSEDTM), + AESTDT = ymd(AESTDTC) + ) res <- derive_var_last_dose_date( input_ae, input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, new_var = LDOSEDTM, single_dose_condition = (EXSTDTC == EXENDTC), output_datetime = FALSE, @@ -51,8 +60,9 @@ test_that("derive_var_last_dose_date works as expected output_datetime = FALSE", expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) }) -test_that("derive_var_last_dose_date works as expected with output_datetime = TRUE", { - expected_output <- tibble::tribble( +## Test 2: works as expected with output_datetime = TRUE ---- +test_that("derive_var_last_dose_date Test 2: works as expected with output_datetime = TRUE", { + expected_output <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, ~LDOSEDTM, "my_study", "subject1", 1, "2020-01-02", "2020-01-01 00:00:00", "my_study", "subject1", 2, "2020-08-31", "2020-08-29 00:00:00", @@ -62,15 +72,18 @@ test_that("derive_var_last_dose_date works as expected with output_datetime = TR "my_study", "subject3", 1, "2020-03-02", NA_character_, "my_study", "subject4", 1, "2020-11-02", NA_character_ ) %>% - mutate(LDOSEDTM = as.POSIXct(as.character(LDOSEDTM), tz = "UTC")) + mutate( + LDOSEDTM = as.POSIXct(as.character(LDOSEDTM), tz = "UTC"), + AESTDT = ymd(AESTDTC) + ) res <- derive_var_last_dose_date( input_ae, input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, new_var = LDOSEDTM, output_datetime = TRUE, single_dose_condition = (EXSTDTC == EXENDTC), @@ -80,8 +93,9 @@ test_that("derive_var_last_dose_date works as expected with output_datetime = TR expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) }) -test_that("derive_var_last_dose_date returns traceability vars", { - expected_output <- tibble::tribble( +## Test 3: returns traceability vars ---- +test_that("derive_var_last_dose_date Test 3: returns traceability vars", { + expected_output <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, ~LDOSEDTM, "my_study", "subject1", 1, "2020-01-02", "2020-01-01 00:00:00", "my_study", "subject1", 2, "2020-08-31", "2020-08-29 00:00:00", @@ -95,7 +109,8 @@ test_that("derive_var_last_dose_date returns traceability vars", { LDOSEDTM = as.POSIXct(as.character(LDOSEDTM), tz = "UTC"), LDOSEDOM = c("EX", "EX", "EX", NA, "EX", NA, NA), LDOSESEQ = c(1, 2, 3, NA, 2, NA, NA), - LDOSEVAR = c("EXENDTC", "EXENDTC", "EXENDTC", NA, "EXENDTC", NA, NA) + LDOSEVAR = c("EXENDTC", "EXENDTC", "EXENDTC", NA, "EXENDTC", NA, NA), + AESTDT = ymd(AESTDTC) ) res <- derive_var_last_dose_date( @@ -103,12 +118,12 @@ test_that("derive_var_last_dose_date returns traceability vars", { input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, new_var = LDOSEDTM, single_dose_condition = (EXSTDTC == EXENDTC), output_datetime = TRUE, - traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") + traceability_vars = vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXENDTC") ) expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) diff --git a/tests/testthat/test-derive_var_last_dose_grp.R b/tests/testthat/test-derive_var_last_dose_grp.R index 932e42700d..97377da242 100644 --- a/tests/testthat/test-derive_var_last_dose_grp.R +++ b/tests/testthat/test-derive_var_last_dose_grp.R @@ -1,4 +1,7 @@ -input_ae <- tibble::tribble( +library(tibble) +library(dplyr) +library(lubridate) +input_ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, "my_study", "subject1", 1, "2020-01-02", "my_study", "subject1", 2, "2020-08-31", @@ -6,9 +9,12 @@ input_ae <- tibble::tribble( "my_study", "subject2", 2, "2020-02-20", "my_study", "subject3", 1, "2020-03-02", "my_study", "subject4", 1, "2020-11-02" -) +) %>% + mutate( + AESTDT = ymd(AESTDTC) + ) -input_ex <- tibble::tribble( +input_ex <- tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, "my_study", "subject1", "2020-01-01", "2020-01-01", 1, 1, "treatment", "my_study", "subject1", "2020-08-29", "2020-08-29", 2, 3, "treatment", @@ -18,11 +24,12 @@ input_ex <- tibble::tribble( "my_study", "subject3", "2020-01-20", "2020-01-20", 2, 7, "placebo", "my_study", "subject4", "2020-03-15", "2020-03-15", 1, 13, "treatment" ) %>% - mutate(EXSTDTC = as.Date(EXSTDTC), EXENDTC = as.Date(EXENDTC)) - + mutate(EXSTDT = as.Date(EXSTDTC), EXENDT = as.Date(EXENDTC)) -test_that("derive_last_dose_date works as expected", { - expected_output <- tibble::tribble( +# derive_var_last_dose_grp +## Test 1: works as expected ---- +test_that("derive_var_last_dose_grp Test 1: works as expected", { + expected_output <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, ~LDGRP, "my_study", "subject1", 1, "2020-01-02", "G1", "my_study", "subject1", 2, "2020-08-31", "G1", @@ -30,19 +37,21 @@ test_that("derive_last_dose_date works as expected", { "my_study", "subject2", 2, "2020-02-20", "G2", "my_study", "subject3", 1, "2020-03-02", "G2", "my_study", "subject4", 1, "2020-11-02", "G3" - ) - + ) %>% + mutate( + AESTDT = ymd(AESTDTC) + ) res <- derive_var_last_dose_grp(input_ae, input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, + dose_date = EXENDT, new_var = LDGRP, grp_brks = c(1, 5, 10, 15), grp_lbls = c("G1", "G2", "G3"), dose_var = EXDOSE, - analysis_date = AESTDTC, + analysis_date = AESTDT, single_dose_condition = (EXSTDTC == EXENDTC), traceability_vars = NULL ) diff --git a/tests/testthat/test-derive_vars_dt.R b/tests/testthat/test-derive_vars_dt.R deleted file mode 100644 index 58bfba9aa0..0000000000 --- a/tests/testthat/test-derive_vars_dt.R +++ /dev/null @@ -1,273 +0,0 @@ -date <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25:40", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("default: no date imputation, time part set o 00:00:00, add DTF", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, ~ASTDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date(NA), NA_character_, - "2019", as.Date(NA), NA_character_, - "2019---07", as.Date(NA), NA_character_ - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - flag_imputation = "date", - dtc = XXSTDTC - ) - - expect_dfs_equal( - expected_output, - actual_output, - "XXSTDTC" - ) -}) - -test_that("Partial date imputed to the first day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, ~ASTDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date("2019-02-01"), "D", - "2019", as.Date("2019-01-01"), "M", - "2019---07", as.Date("2019-01-01"), "M" - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST" - ) - actual_output1 <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "01-01" - ) - - expect_equal( - expected_output, - actual_output - ) - expect_equal( - expected_output, - actual_output1 - ) -}) - -test_that("Partial date imputed to the last day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~AENDT, ~AENDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date("2019-02-28"), "D", - "2019", as.Date("2019-12-31"), "M", - "2019---07", as.Date("2019-12-31"), "M" - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AEN", - dtc = XXSTDTC, - date_imputation = "LAST" - ) - - expect_equal( - expected_output, - actual_output - ) -}) - - -test_that("Partial date imputed to the LAST day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, ~ASTDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date("2019-02-28"), "D", - "2019", as.Date("2019-12-31"), "M", - "2019---07", as.Date("2019-12-31"), "M" - ) - - actual_output <- derive_vars_dt(date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "LAST" - ) - - expect_equal( - expected_output, - actual_output - ) -}) - - - -test_that("Partial date imputation as MID to the mid day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, ~ASTDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date("2019-02-15"), "D", - "2019", as.Date("2019-06-30"), "M", - "2019---07", as.Date("2019-06-30"), "M" - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "MID" - ) - - expect_dfs_equal( - expected_output, - actual_output, - keys = c("XXSTDTC") - ) -}) - -test_that("Partial date imputation as 6-15 to the mid day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, ~ASTDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date("2019-02-15"), "D", - "2019", as.Date("2019-06-15"), "M", - "2019---07", as.Date("2019-06-15"), "M" - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "06-15" - ) - - expect_dfs_equal( - expected_output, - actual_output, - keys = c("XXSTDTC") - ) -}) - -date <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25:40", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("Partial date imputation as MID and preserve = TRUE to the mid day/month", { # nolint - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, ~ASTDTF, - "2019-07-18T15:25:40", as.Date("2019-07-18"), NA_character_, - "2019-07-18", as.Date("2019-07-18"), NA_character_, - "2019-02", as.Date("2019-02-15"), "D", - "2019", as.Date("2019-06-30"), "M", - "2019---07", as.Date("2019-06-07"), "M" - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "MID", - preserve = TRUE - ) - - expect_dfs_equal( - expected_output, - actual_output, - keys = c("XXSTDTC") - ) -}) - - -test_that("Partial date imputed to the last day/month, no DTF", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, - "2019-07-18T15:25:40", as.Date("2019-07-18"), - "2019-07-18", as.Date("2019-07-18"), - "2019-02", as.Date("2019-02-28"), - "2019", as.Date("2019-12-31"), - "2019---07", as.Date("2019-12-31") - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - flag_imputation = "none", - date_imputation = "LAST" - ) - - expect_dfs_equal( - base = expected_output, - comp = actual_output, - keys = "XXSTDTC" - ) -}) - -test_that("Partial date imputed to the last day/month, no DTF and preserve=TRUE", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, - "2019-07-18T15:25:40", as.Date("2019-07-18"), - "2019-07-18", as.Date("2019-07-18"), - "2019-02", as.Date("2019-02-28"), - "2019", as.Date("2019-12-31"), - "2019---07", as.Date("2019-12-07") - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "LAST", - flag_imputation = "none", - preserve = TRUE - ) - - expect_dfs_equal( - base = expected_output, - comp = actual_output, - keys = "XXSTDTC" - ) -}) - -test_that("Partial date imputed to the first day/month, no DTF and preserve=TRUE", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDT, - "2019-07-18T15:25:40", as.Date("2019-07-18"), - "2019-07-18", as.Date("2019-07-18"), - "2019-02", as.Date("2019-02-01"), - "2019", as.Date("2019-01-01"), - "2019---07", as.Date("2019-01-07") - ) - - actual_output <- derive_vars_dt( - date, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST", - flag_imputation = "none", - preserve = TRUE - ) - - expect_dfs_equal( - base = expected_output, - comp = actual_output, - keys = "XXSTDTC" - ) -}) diff --git a/tests/testthat/test-derive_vars_dtm.R b/tests/testthat/test-derive_vars_dtm.R deleted file mode 100644 index a117b910f8..0000000000 --- a/tests/testthat/test-derive_vars_dtm.R +++ /dev/null @@ -1,397 +0,0 @@ -input <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - - -test_that("default: no date imputation, time part set to 00:00:00, add DTF, TMF", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), "H", - "2019-02", ymd_hms(NA), NA_character_, - "2019", ymd_hms(NA), NA_character_, - "2019---07", ymd_hms(NA), NA_character_ - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AST", - dtc = XXSTDTC - ) - - expect_equal( - expected_output, - actual_output - ) -}) - -test_that("Partial date imputed to the first day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", - "2019", ymd_hms("2019-01-01T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST" - ) - actual_output1 <- derive_vars_dtm( - input, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "01-01" - ) - - expect_equal( - expected_output, - actual_output - ) - expect_equal( - expected_output, - actual_output1 - ) -}) - -test_that("Partial date imputed to the last day/month, Missing time part imputed with 23:59:59", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~AENDTM, ~AENDTF, ~AENTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:59"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:59:59"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T23:59:59"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-28T23:59:59"), "D", "H", - "2019", ymd_hms("2019-12-31T23:59:59"), "M", "H", - "2019---07", ymd_hms("2019-12-31T23:59:59"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AEN", - dtc = XXSTDTC, - date_imputation = "LAST", - time_imputation = "LAST" - ) - - actual_output1 <- derive_vars_dtm( - input, - new_vars_prefix = "AEN", - dtc = XXSTDTC, - date_imputation = "LAST", - time_imputation = "23:59:59" - ) - - expect_equal( - expected_output, - actual_output - ) - expect_equal( - expected_output, - actual_output1 - ) -}) - -test_that("Partial date imputed to the last day/month, Missing time part imputed with 23:59:59, no imputation flag", { # nolint - expected_output <- tibble::tribble( - ~XXSTDTC, ~AENDTM, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:59"), - "2019-07-18T15", ymd_hms("2019-07-18T15:59:59"), - "2019-07-18", ymd_hms("2019-07-18T23:59:59"), - "2019-02", ymd_hms("2019-02-28T23:59:59"), - "2019", ymd_hms("2019-12-31T23:59:59"), - "2019---07", ymd_hms("2019-12-31T23:59:59") - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AEN", - dtc = XXSTDTC, - date_imputation = "LAST", - time_imputation = "LAST", - flag_imputation = "None" - ) - - expect_equal( - expected_output, - actual_output - ) -}) - -test_that("Partial date imputed to the MID day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-15T00:00:00"), "D", "H", - "2019", ymd_hms("2019-06-30T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-06-30T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "MID" - ) - - expect_dfs_equal( - base = expected_output, - comp = actual_output, - keys = c("XXSTDTC") - ) -}) - -test_that("Partial date imputed to the 6-15 day/month", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-15T00:00:00"), "D", "H", - "2019", ymd_hms("2019-06-15T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-06-15T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "06-15" - ) - - expect_equal( - expected_output, - actual_output - ) -}) - -test_that("No re-derivation is done if --DTF variable already exists", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", - "2019", ymd_hms("2019-01-01T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" - ) %>% - select(XXSTDTC, ASTDTF, everything()) - - actual_output <- expect_message( - derive_vars_dtm( - mutate(input, ASTDTF = c(NA, NA, NA, NA, "D", "M", "M")), - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST" - ), - regexp = "^The .* variable is already present in the input dataset and will not be re-derived." - ) - - expect_equal(expected_output, actual_output) -}) - -input_maxed <- input %>% - filter(!str_detect(XXSTDTC, "18")) %>% - mutate(DCUTDT = ymd_hms("2019-02-10T00:00:00")) - -test_that("max_dates parameter works as expected", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-02", ymd_hms("2019-02-10T00:00:00"), "D", "H", - "2019", ymd_hms("2019-02-10T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-02-10T00:00:00"), "M", "H" - ) %>% - mutate(DCUTDT = ymd_hms("2019-02-10T00:00:00")) - - actual_output <- derive_vars_dtm( - input_maxed, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "LAST", - max_dates = vars(DCUTDT) - ) - - expect_dfs_equal(expected_output, actual_output, keys = c("XXSTDTC")) -}) - -input_secs <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("Ignore Seconds Flag is not used when not present in the function call", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", - "2019", ymd_hms("2019-01-01T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input_secs, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST", - time_imputation = "FIRST" - ) - - expect_equal(expected_output, actual_output) -}) - -test_that("Ignore Seconds Flag is not used when set to FALSE in function call", { - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", - "2019", ymd_hms("2019-01-01T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input_secs, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST", - time_imputation = "FIRST", - ignore_seconds_flag = FALSE - ) - - expect_equal(expected_output, actual_output) -}) - - -input_no_s <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - - -test_that("Ignore Seconds Flag remove the Seconds Flag, S, from XXDTF variable when set to TRUE", { # nolint - - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, NA_character_, - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-01T00:00:00"), "D", "H", - "2019", ymd_hms("2019-01-01T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-01-01T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input_no_s, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST", - time_imputation = "FIRST", - ignore_seconds_flag = TRUE - ) - - expect_equal(expected_output, actual_output) -}) - -input_secs <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("Function throws ERROR when Ignore Seconds Flag is invoked and seconds is present in the data ", { # nolint - - - expect_error( - derive_vars_dtm( - input_secs, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "FIRST", - time_imputation = "FIRST", - ignore_seconds_flag = TRUE - ), - regexp = "Seconds detected in data while ignore_seconds_flag is invoked" - ) -}) - -input <- tibble::tribble( - ~XXSTDTC, - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18T15", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("Partial date imputation as MID and preserve = TRUE to the mid day/month", { # nolint - - expected_output <- tibble::tribble( - ~XXSTDTC, ~ASTDTM, ~ASTDTF, ~ASTTMF, - "2019-07-18T15:25:40", ymd_hms("2019-07-18T15:25:40"), NA_character_, NA_character_, - "2019-07-18T15:25", ymd_hms("2019-07-18T15:25:00"), NA_character_, "S", - "2019-07-18T15", ymd_hms("2019-07-18T15:00:00"), NA_character_, "M", - "2019-07-18", ymd_hms("2019-07-18T00:00:00"), NA_character_, "H", - "2019-02", ymd_hms("2019-02-15T00:00:00"), "D", "H", - "2019", ymd_hms("2019-06-30T00:00:00"), "M", "H", - "2019---07", ymd_hms("2019-06-07T00:00:00"), "M", "H" - ) - - actual_output <- derive_vars_dtm( - input, - new_vars_prefix = "AST", - dtc = XXSTDTC, - date_imputation = "MID", - preserve = TRUE - ) - - expect_equal( - expected_output, - actual_output - ) -}) diff --git a/tests/testthat/test-derive_vars_duration.R b/tests/testthat/test-derive_vars_duration.R index c29c499ac6..09cfd45a18 100644 --- a/tests/testthat/test-derive_vars_duration.R +++ b/tests/testthat/test-derive_vars_duration.R @@ -1,15 +1,18 @@ library(tibble) library(lubridate) -test_that("duration and unit variable are added", { +test_that("derive_vars_duration Test 1: Duration and unit variable are added", { input <- tribble( - ~BRTHDT, ~RANDDT, - ymd("1984-09-06"), ymd("2020-02-24"), - ymd("1985-01-01"), NA, - NA, ymd("2021-03-10"), - NA, NA + ~USUBJID, ~BRTHDT, ~RANDDT, + "P01", ymd("1984-09-06"), ymd("2020-02-24"), + "P02", ymd("1985-01-01"), NA, + "P03", NA, ymd("2021-03-10"), + "P04", NA, NA + ) + expected_output <- mutate(input, + AGE = c(35, NA, NA, NA), + AGEU = c("YEARS", NA_character_, NA_character_, NA_character_) ) - expected_output <- mutate(input, AGE = c(35, NA, NA, NA), AGEU = c("YEARS", NA, NA, NA)) actual_output <- derive_vars_duration( input, new_var = AGE, @@ -20,5 +23,57 @@ test_that("duration and unit variable are added", { trunc_out = TRUE ) - expect_equal(actual_output, expected_output) + expect_dfs_equal(actual_output, expected_output, keys = "USUBJID") +}) + +test_that("derive_vars_duration Test 2: Duration and unit variable are added", { + input <- tribble( + ~USUBJID, ~ASTDT, ~AENDT, + "P01", ymd("2021-03-05"), ymd("2021-03-02"), + "P02", ymd("2019-09-18"), ymd("2019-09-18"), + "P03", ymd("1985-01-01"), NA, + "P04", NA, NA + ) + expected_output <- mutate( + input, + ADURN = c(-3, 1, NA, NA), + ADURU = c("DAYS", "DAYS", NA_character_, NA_character_) + ) + actual_output <- derive_vars_duration( + input, + new_var = ADURN, + new_var_unit = ADURU, + start_date = ASTDT, + end_date = AENDT, + out_unit = "days" + ) + + expect_dfs_equal(actual_output, expected_output, keys = "USUBJID") +}) + +test_that("derive_vars_duration Test 3: Duration and unit variable are added", { + input <- tribble( + ~USUBJID, ~ADTM, ~TRTSDTM, + "P01", ymd_hms("2019-08-09T04:30:56"), ymd_hms("2019-08-09T05:00:00"), + "P02", ymd_hms("2019-11-11T10:30:00"), ymd_hms("2019-11-11T11:30:00"), + "P03", ymd_hms("2019-11-11T00:00:00"), ymd_hms("2019-11-11T04:00:00"), + "P04", NA, ymd_hms("2019-11-11T12:34:56"), + ) + expected_output <- mutate( + input, + ADURN = c(30, 60, 240, NA), + ADURU = c("MINUTES", "MINUTES", "MINUTES", NA_character_) + ) + actual_output <- derive_vars_duration( + input, + new_var = ADURN, + new_var_unit = ADURU, + start_date = ADTM, + end_date = TRTSDTM, + in_unit = "minutes", + out_unit = "minutes", + add_one = FALSE + ) + + expect_dfs_equal(actual_output, expected_output, keys = "USUBJID") }) diff --git a/tests/testthat/test-derive_vars_last_dose.R b/tests/testthat/test-derive_vars_last_dose.R index 949c236940..0aae4dc503 100644 --- a/tests/testthat/test-derive_vars_last_dose.R +++ b/tests/testthat/test-derive_vars_last_dose.R @@ -1,4 +1,7 @@ -input_ae <- tibble::tribble( +library(tibble) +library(dplyr) +library(lubridate) +input_ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~AESTDTC, "my_study", "subject1", 1, "2020-01-02", "my_study", "subject1", 2, "2020-08-31", @@ -7,9 +10,12 @@ input_ae <- tibble::tribble( "my_study", "subject2", 2, "2020-02-20", "my_study", "subject3", 1, "2020-03-02", "my_study", "subject4", 1, "2020-11-02" -) +) %>% + mutate( + AESTDT = ymd(AESTDTC) + ) -input_ex <- tibble::tribble( +input_ex <- tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, "my_study", "subject1", "2020-01-01", "2020-01-01", 1, 10, "treatment", "my_study", "subject1", "2020-08-29", "2020-08-29", 2, 10, "treatment", @@ -19,14 +25,15 @@ input_ex <- tibble::tribble( "my_study", "subject2", "2020-01-20", "2020-01-20", 2, 0, "placebo", "my_study", "subject3", "2020-03-15", "2020-03-15", 1, 10, "treatment" ) %>% - mutate(EXSTDTC = as.Date(EXSTDTC), EXENDTC = as.Date(EXENDTC)) - + mutate(EXSTDT = as.Date(EXSTDTC), EXENDT = as.Date(EXENDTC)) +# derive_vars_last_dose ---- +## Test 1: function works as expected ---- test_that("derive_vars_last_dose Test 1: function works as expected", { expected_output <- mutate( input_ae, - EXSTDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), - EXENDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), + EXSTDT = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), + EXENDT = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), EXSEQ = c(1, 2, 3, NA, 2, NA, NA), EXDOSE = c(10, 10, 10, NA, 0, NA, NA), EXTRT = c("treatment", "treatment", "treatment", NA, "placebo", NA, NA) @@ -37,9 +44,9 @@ test_that("derive_vars_last_dose Test 1: function works as expected", { input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDTC, EXSTDTC), - analysis_date = AESTDTC, + dose_date = EXENDT, + new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDT, EXSTDT), + analysis_date = AESTDT, single_dose_condition = (EXSTDTC == EXENDTC), traceability_vars = NULL ) @@ -47,14 +54,18 @@ test_that("derive_vars_last_dose Test 1: function works as expected", { expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) }) - +## Test 2: function checks validity of start and end dose inputs ---- test_that("derive_vars_last_dose Test 2: function checks validity of start and end dose inputs", { - input_ex_wrong <- dplyr::bind_rows( + input_ex_wrong <- bind_rows( input_ex, - tibble::tribble( + tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, - "my_study", "subject4", as.Date("2020-11-05"), as.Date("2020-11-06"), 1, 10, "treatment" - ) + "my_study", "subject4", "2020-11-05", "2020-11-06", 1, 10, "treatment" + ) %>% + mutate( + EXENDT = ymd(EXENDTC), + EXSTDT = ymd(EXSTDTC) + ) ) expect_error( @@ -63,8 +74,8 @@ test_that("derive_vars_last_dose Test 2: function checks validity of start and e input_ex_wrong, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, single_dose_condition = (EXSTDTC == EXENDTC), traceability_vars = NULL ), @@ -72,12 +83,14 @@ test_that("derive_vars_last_dose Test 2: function checks validity of start and e ) }) - +## Test 3: function returns traceability vars ---- test_that("derive_vars_last_dose Test 3: function returns traceability vars", { expected_output <- mutate( input_ae, - EXSTDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), - EXENDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), + EXSTDTC = c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA), + EXENDTC = c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA), + EXENDT = ymd(EXENDTC), + EXSTDT = ymd(EXSTDTC), EXSEQ = c(1, 2, 3, NA, 2, NA, NA), EXDOSE = c(10, 10, 10, NA, 0, NA, NA), EXTRT = c("treatment", "treatment", "treatment", NA, "placebo", NA, NA), @@ -91,29 +104,35 @@ test_that("derive_vars_last_dose Test 3: function returns traceability vars", { input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, single_dose_condition = (EXSTDTC == EXENDTC), - traceability_vars = dplyr::vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXSTDTC") + traceability_vars = vars(LDOSEDOM = "EX", LDOSESEQ = EXSEQ, LDOSEVAR = "EXSTDTC") ) expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) }) - +## Test 4: function errors when multiple doses are on same date ---- test_that("derive_vars_last_dose Test 4: function errors when multiple doses are on same date", { - input_ex_dup <- dplyr::bind_rows( + input_ex_dup <- bind_rows( input_ex, - tibble::tribble( + tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, - "my_study", "subject2", as.Date("2020-01-20"), as.Date("2020-01-20"), 3, 0, "placebo" - ) + "my_study", "subject2", "2020-01-20", "2020-01-20", 3, 0, "placebo" + ) %>% + mutate( + EXSTDT = ymd(EXSTDTC), + EXENDT = ymd(EXENDTC) + ) ) expected_output <- mutate( input_ae, - EXSTDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), - EXENDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), + EXSTDTC = c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA), + EXENDTC = c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA), + EXSTDT = ymd(EXSTDTC), + EXENDT = ymd(EXENDTC), EXSEQ = c(1, 2, 3, NA, 3, NA, NA), EXDOSE = c(10, 10, 10, NA, 0, NA, NA), EXTRT = c("treatment", "treatment", "treatment", NA, "placebo", NA, NA) @@ -125,8 +144,8 @@ test_that("derive_vars_last_dose Test 4: function errors when multiple doses are input_ex_dup, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - analysis_date = AESTDTC, + dose_date = EXENDT, + analysis_date = AESTDT, single_dose_condition = (EXSTDTC == EXENDTC), traceability_vars = NULL ), @@ -134,20 +153,23 @@ test_that("derive_vars_last_dose Test 4: function errors when multiple doses are ) }) - +## Test 5: multiple doses on same date - dose_id supplied ---- test_that("derive_vars_last_dose Test 5: multiple doses on same date - dose_id supplied", { - input_ex_dup <- dplyr::bind_rows( + input_ex_dup <- bind_rows( input_ex, - tibble::tribble( + tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, - "my_study", "subject2", as.Date("2020-01-20"), as.Date("2020-01-20"), 3, 0, "placebo" + "my_study", "subject2", "2020-01-20", "2020-01-20", 3, 0, "placebo" + ) %>% mutate( + EXSTDT = ymd(EXSTDTC), + EXENDT = ymd(EXENDTC) ) ) expected_output <- mutate( input_ae, - EXSTDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), - EXENDTC = as.Date(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), + EXSTDT = ymd(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), + EXENDT = ymd(c("2020-01-01", "2020-08-29", "2020-09-02", NA, "2020-01-20", NA, NA)), EXSEQ = c(1, 2, 3, NA, 3, NA, NA), EXDOSE = c(10, 10, 10, NA, 0, NA, NA), EXTRT = c("treatment", "treatment", "treatment", NA, "placebo", NA, NA) @@ -158,10 +180,10 @@ test_that("derive_vars_last_dose Test 5: multiple doses on same date - dose_id s input_ex_dup, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, + dose_date = EXENDT, dose_id = vars(EXSEQ), - new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXSTDTC, EXENDTC), - analysis_date = AESTDTC, + new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXSTDT, EXENDT), + analysis_date = AESTDT, single_dose_condition = (EXSTDTC == EXENDTC), traceability_vars = NULL ) @@ -169,9 +191,9 @@ test_that("derive_vars_last_dose Test 5: multiple doses on same date - dose_id s expect_dfs_equal(expected_output, res, keys = c("STUDYID", "USUBJID", "AESEQ", "AESTDTC")) }) - -test_that("derive_vars_last_dose Test 6: error is issued if same variable is found in both input datasets ", { # nolint - input_ae <- tibble::tribble( +## Test 6: error is issued if same variable is found in both input datasets ---- +test_that("derive_vars_last_dose Test 6: error is issued if same variable is found in both input datasets", { # nolint + input_ae <- tribble( ~STUDYID, ~USUBJID, ~AESEQ, ~EXSTDTC, "my_study", "subject1", 1, "2020-01-02", "my_study", "subject1", 2, "2020-08-31", @@ -180,9 +202,12 @@ test_that("derive_vars_last_dose Test 6: error is issued if same variable is fou "my_study", "subject2", 2, "2020-02-20", "my_study", "subject3", 1, "2020-03-02", "my_study", "subject4", 1, "2020-11-02" - ) + ) %>% + mutate( + EXSTDT = ymd(EXSTDTC) + ) - input_ex <- tibble::tribble( + input_ex <- tribble( ~STUDYID, ~USUBJID, ~EXSTDTC, ~EXENDTC, ~EXSEQ, ~EXDOSE, ~EXTRT, "my_study", "subject1", "2020-01-01", "2020-01-01", 1, 10, "treatment", "my_study", "subject1", "2020-08-29", "2020-08-29", 2, 10, "treatment", @@ -192,7 +217,10 @@ test_that("derive_vars_last_dose Test 6: error is issued if same variable is fou "my_study", "subject2", "2020-01-20", "2020-01-20", 2, 0, "placebo", "my_study", "subject3", "2020-03-15", "2020-03-15", 1, 10, "treatment" ) %>% - mutate(EXSTDTC = as.Date(EXSTDTC), EXENDTC = as.Date(EXENDTC)) + mutate( + EXSTDT = as.Date(EXSTDTC), + EXENDT = as.Date(EXENDTC) + ) expect_error( derive_vars_last_dose( @@ -200,29 +228,32 @@ test_that("derive_vars_last_dose Test 6: error is issued if same variable is fou input_ex, filter_ex = (EXDOSE > 0) | (EXDOSE == 0 & EXTRT == "placebo"), by_vars = vars(STUDYID, USUBJID), - dose_date = EXENDTC, - new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDTC, EXSTDTC), - analysis_date = EXSTDTC, + dose_date = EXENDT, + new_vars = vars(EXDOSE, EXTRT, EXSEQ, EXENDT, EXSTDT), + analysis_date = EXSTDT, single_dose_condition = (EXSTDTC == EXENDTC), traceability_vars = NULL ), - "Variable(s) `EXSTDTC` found in both datasets, cannot perform join", + "Variable(s) `EXSTDT` found in both datasets, cannot perform join", fixed = TRUE ) }) +## Test 7: no error is raised when setting `dose_date` to a renamed variable ---- test_that("derive_vars_last_dose Test 7: no error is raised when setting `dose_date` to a renamed variable", { # nolint - adae <- tibble::tribble( + adae <- tribble( ~USUBJID, ~AESTDTC, ~AENDTC, ~ASTDT, ~AENDT, ~AEDECOD, "P01", "2022-01-10", "2022-01-12", ymd("2022-01-10"), ymd("2022-01-12"), "Nausea", "P02", "2022-01-31", "2022-01-31", ymd("2022-01-31"), ymd("2022-01-31"), "Vomitting", "P02", "2022-02-02", "2022-02-04", ymd("2022-02-02"), ymd("2022-02-04"), "Vomitting" ) - adex <- tibble::tribble( - ~USUBJID, ~EXTRT, ~EXDOSFRQ, ~EXSTDTC, ~EXENDTC, ~ASTDT, ~AENDT, + adex <- tribble( + ~USUBJID, ~EXTRT, ~EXDOSFRQ, ~EXSTDTC, ~EXENDTC, ~ASTDT, ~AENDT, ~ASTDTM, ~AENDTM, "P01", "Drug A", "QD", "2022-01-09", "2022-01-12", ymd("2022-01-09"), ymd("2022-01-12"), - "P02", "Drug A", "QD", "2022-02-01", "2022-02-04", ymd("2022-02-01"), ymd("2022-02-04") + ymd_hms("2022-01-09 09:30:00"), ymd_hms("2022-01-12 09:30:00"), + "P02", "Drug A", "QD", "2022-02-01", "2022-02-04", ymd("2022-02-01"), ymd("2022-02-04"), + ymd_hms("2022-02-01 10:00:00"), ymd_hms("2022-02-04 10:00:00") ) (adex_single <- create_single_dose_dataset(adex)) @@ -233,7 +264,7 @@ test_that("derive_vars_last_dose Test 7: no error is raised when setting `dose_d adex_single, by_vars = vars(USUBJID), dose_date = EXSTDT, - analysis_date = AESTDTC, + analysis_date = ASTDT, new_vars = vars(EXSTDT = ASTDT) ), NA diff --git a/tests/testthat/test-filter_confirmation.R b/tests/testthat/test-filter_confirmation.R new file mode 100644 index 0000000000..1f66e0b7fe --- /dev/null +++ b/tests/testthat/test-filter_confirmation.R @@ -0,0 +1,211 @@ +library(tibble) +data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "CR", + "1", 4, "SD", + "1", 5, "NE", + "2", 1, "SD", + "2", 2, "PR", + "2", 3, "PD", + "3", 1, "SD", + "4", 1, "PR", + "4", 2, "PD", + "4", 3, "SD", + "4", 4, "SD", + "4", 5, "PR" +) + +# filter_confirmation ---- +## Test 1: filter without first_cond ---- +test_that("filter_confirmation Test 1: filter without first_cond", { + actual <- + filter_confirmation( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVISITN, AVALC), + join_type = "after", + order = vars(AVISITN), + filter = AVALC == "PR" & AVALC.join %in% c("CR", "PR") & + AVISITN < AVISITN.join + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "4", 1, "PR" + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +## Test 2: filter with first_cond ---- +test_that("filter_confirmation Test 2: filter with first_cond", { + actual <- + filter_confirmation( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + first_cond = AVALC == "CR" & + AVALC.join == "CR", + order = vars(AVISITN), + filter = TRUE + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 2, "CR" + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +## Test 3: filter with first_cond and summary function ---- +test_that("filter_confirmation Test 3: filter with first_cond and summary function", { + actual <- + filter_confirmation( + data, + by_vars = vars(USUBJID), + join_vars = vars(AVALC), + join_type = "after", + first_cond = AVALC == "PR" & + AVALC.join %in% c("CR", "PR"), + order = vars(AVISITN), + filter = count_vals(AVALC.join, "SD") <= 1 + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR" + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +## Test 4: join_type = "all" ---- +test_that("filter_confirmation Test 4: join_type = 'all'", { + adae <- tribble( + ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, + "1", 10, "N", 1, + "1", 21, "N", 50, + "1", 23, "Y", 14, + "1", 32, "N", 31, + "1", 42, "N", 20, + "2", 11, "Y", 13, + "2", 23, "N", 2, + "3", 13, "Y", 12, + "4", 14, "N", 32, + "4", 21, "N", 41 + ) + + actual <- filter_confirmation( + adae, + by_vars = vars(USUBJID), + join_vars = vars(ACOVFL, ADY), + join_type = "all", + order = vars(ADY), + filter = ADURN > 30 & ACOVFL.join == "Y" & ADY >= ADY.join - 7 + ) + + expected <- tribble( + ~USUBJID, ~ADY, ~ACOVFL, ~ADURN, + "1", 21, "N", 50, + "1", 32, "N", 31, + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "ADY") + ) +}) + +# min_cond ---- +## Test 1: test it ---- +test_that("min_cond, Test 1: test it", { + data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", + ) + + actual <- group_by(data, USUBJID) %>% mutate( + first_cr_vis = min_cond(var = AVISITN, cond = AVALC == "CR") + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, ~first_cr_vis, + "1", 1, "PR", 2, + "1", 2, "CR", 2, + "1", 3, "NE", 2, + "1", 4, "CR", 2, + "1", 5, "NE", 2, + "2", 1, "CR", 1, + "2", 2, "PR", 1, + "2", 3, "CR", 1, + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) + +# max_cond ---- +## Test 1: test it ---- +test_that("max_cond, Test 1: test it", { + data <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, + "1", 1, "PR", + "1", 2, "CR", + "1", 3, "NE", + "1", 4, "CR", + "1", 5, "NE", + "2", 1, "CR", + "2", 2, "PR", + "2", 3, "CR", + ) + + actual <- group_by(data, USUBJID) %>% mutate( + last_pr_vis = max_cond(var = AVISITN, cond = AVALC == "PR") + ) + + expected <- tribble( + ~USUBJID, ~AVISITN, ~AVALC, ~last_pr_vis, + "1", 1, "PR", 1, + "1", 2, "CR", 1, + "1", 3, "NE", 1, + "1", 4, "CR", 1, + "1", 5, "NE", 1, + "2", 1, "CR", 2, + "2", 2, "PR", 2, + "2", 3, "CR", 2, + ) + + expect_dfs_equal( + base = expected, + compare = actual, + keys = c("USUBJID", "AVISITN") + ) +}) diff --git a/tests/testthat/test-impute_dtc.R b/tests/testthat/test-impute_dtc.R deleted file mode 100644 index c032529ff6..0000000000 --- a/tests/testthat/test-impute_dtc.R +++ /dev/null @@ -1,291 +0,0 @@ -input <- c( - "2019-07-18T15:25:40.243", - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - - -test_that("default: no date imputation, time part set to 00:00:00", { - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:00", - "2019-07-18T00:00:00", - NA_character_, - NA_character_, - NA_character_ - ) - expect_equal(impute_dtc(dtc = input), expected_output) -}) - -test_that("default: no date imputation,Missing time part imputed with 23:59:59 portion", { - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:59", - "2019-07-18T23:59:59", - NA_character_, - NA_character_, - NA_character_ - ) - expect_equal( - impute_dtc( - dtc = input, - time_imputation = "23:59:59" - ), - expected_output - ) - - expect_equal( - impute_dtc( - dtc = input, - time_imputation = "LAST" - ), - expected_output - ) -}) - -test_that("impute to first day/month if date is partial,Missing time part imputed with 00:00:00 portion", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:00", - "2019-07-18T00:00:00", - "2019-02-01T00:00:00", - "2019-01-01T00:00:00", - "2019-01-01T00:00:00" - ) - expect_equal( - impute_dtc( - dtc = input, - date_imputation = "FIRST" - ), - expected_output - ) - - expect_equal( - impute_dtc( - dtc = input, - date_imputation = "01-01" - ), - expected_output - ) -}) - -input <- c( - "2019-07-18T15:25:40.243", - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("impute to last day/month if date is partial,Missing time part imputed with 23:59:59 portion and preserve equals FALSE", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:59", - "2019-07-18T23:59:59", - "2019-02-28T23:59:59", - "2019-12-31T23:59:59", - "2019-12-31T23:59:59" - ) - expect_equal( - impute_dtc( - dtc = input, - date_imputation = "LAST", - time_imputation = "LAST", - preserve = FALSE - ), - expected_output - ) -}) - -input <- c( - "2019-07-18T15:25:40.243", - "2019-07-18T15:25:40", - "2019-07-18T15:25", - "2019-07-18", - "2019-02", - "2019", - "2019---07" -) - -test_that("impute to last day/month if date is partial,Missing time part imputed - with 23:59:59 portion and preserve equals TRUE", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:59", - "2019-07-18T23:59:59", - "2019-02-28T23:59:59", - "2019-12-31T23:59:59", - "2019-12-07T23:59:59" - ) - expect_equal( - imputes <- impute_dtc( - dtc = input, - date_imputation = "LAST", - time_imputation = "LAST", - preserve = TRUE - ), - expected_output - ) -}) - -test_that("impute to last day/month if date is partial,Missing time part imputed - with 23:59:59 portion and preserve equals FALSE", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:59", - "2019-07-18T23:59:59", - "2019-02-28T23:59:59", - "2019-12-31T23:59:59", - "2019-12-31T23:59:59" - ) - expect_equal( - imputes <- impute_dtc( - dtc = input, - date_imputation = "LAST", - time_imputation = "LAST", - preserve = FALSE - ), - expected_output - ) -}) - -test_that("impute to last day/month if date is partial,Missing time part imputed - with 00:00:00 portion and preserve equals FALSE", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:00", - "2019-07-18T00:00:00", - "2019-02-15T00:00:00", - "2019-06-30T00:00:00", - "2019-06-30T00:00:00" - ) - expect_equal( - imputes <- impute_dtc( - dtc = input, - date_imputation = "MID", - time_imputation = "FIRST", - preserve = FALSE - ), - expected_output - ) -}) - - - - -test_that("impute to MID day/month if date is partial,Missing time part imputed - with 00:00:00 portion", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:00", - "2019-07-18T00:00:00", - "2019-02-15T00:00:00", - "2019-06-30T00:00:00", - "2019-06-30T00:00:00" - ) - expect_equal( - impute_dtc( - dtc = input, - date_imputation = "MID" - ), - expected_output - ) -}) - - -test_that("impute to MID day/month if date is partial and preserve argument - works as expected", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:00", - "2019-07-18T00:00:00", - "2019-02-15T00:00:00", - "2019-06-30T00:00:00", - "2019-06-07T00:00:00" - ) - - actual_output <- impute_dtc( - dtc = input, - date_imputation = "MID", - preserve = TRUE - ) - - expect_equal( - actual_output, - expected_output - ) -}) - - -test_that("impute to 01-01 day/month if date is partial and preserve argument - works as expected", { # nolint - expected_output <- c( - "2019-07-18T15:25:40", - "2019-07-18T15:25:40", - "2019-07-18T15:25:00", - "2019-07-18T00:00:00", - "2019-02-01T00:00:00", - "2019-01-01T00:00:00", - "2019-01-07T00:00:00" - ) - - actual_output <- impute_dtc( - dtc = input, - date_imputation = "01-01", - preserve = TRUE - ) - - expect_equal( - actual_output, - expected_output - ) -}) - - - -test_that("min_dates parameter works", { - expect_equal( - impute_dtc(c("2020-12", "2020-11"), - min_dates = list( - c( - lubridate::ymd_hms("2020-12-06T12:12:12"), - NA - ), - c( - lubridate::ymd_hms("2020-11-11T11:11:11"), - lubridate::ymd_hms("2020-11-11T11:11:11") - ) - ), - date_imputation = "first" - ), - c("2020-12-06T12:12:12", "2020-11-11T11:11:11") - ) -}) - -test_that("max_dates parameter works", { - expect_equal( - impute_dtc("2020-12", - max_dates = list( - lubridate::ymd_hms("2020-12-06T12:12:12"), - lubridate::ymd_hms("2020-11-11T11:11:11") - ), - date_imputation = "last" - ), - "2020-12-06T12:12:12" - ) -}) diff --git a/tests/testthat/test-test_helpers.R b/tests/testthat/test-test_helpers.R deleted file mode 100644 index c8b8a193b2..0000000000 --- a/tests/testthat/test-test_helpers.R +++ /dev/null @@ -1,9 +0,0 @@ -test_that("expect_dfs_equal works", { - a <- data.frame(x = 1:3, y = 4:6) - b <- data.frame(x = 1:3, y = 5:7) - - expect_error( - expect_dfs_equal(a, b, keys = "x"), - regexp = "Differences found\\.*" - ) -}) diff --git a/tests/testthat/test-user_helpers.R b/tests/testthat/test-user_helpers.R index 4d01b1cf12..867fc7f410 100644 --- a/tests/testthat/test-user_helpers.R +++ b/tests/testthat/test-user_helpers.R @@ -1,10 +1,23 @@ +# list_all_templates ---- test_that("all templates are listed", { - expect_identical( + expect_equal( unclass(list_all_templates()), - c("ADAE", "ADCM", "ADEG", "ADEX", "ADLB", "ADPP", "ADSL", "ADVS") + c("ADAE", "ADCM", "ADEG", "ADEX", "ADLB", "ADMH", "ADPP", "ADSL", "ADVS"), + check.attributes = FALSE + ) +}) + +test_that("Error Message is returned if package is not installed", { + expect_error( + list_all_templates(package = "non-existing-package"), + regexp = paste0( + "No package called 'non-existing-package' is installed ", + "and hence no templates are available" + ) ) }) +# use_ad_template ---- test_that("package templates can be used", { dir <- tempdir() file <- file.path(dir, "advs.R") @@ -15,6 +28,7 @@ test_that("package templates can be used", { readLines(system.file("templates/ad_advs.R", package = "admiral")), readLines(file) ) + file.remove(file) }) test_that("Error Message is returned if no ADaM template is available", { @@ -34,12 +48,25 @@ test_that("Error Message is returned if ADaM template file already exists", { expect_error( use_ad_template("adsl", save_path = file, open = FALSE) ) + file.remove(file) }) -test_that("`convert_blanks_to_na.list` produces a lists", { - x <- c("", "", "") - expected_output <- lapply(x, convert_blanks_to_na) - actual_output <- convert_blanks_to_na.list(x) +# print.adam_templates ---- +test_that("`adam_templates` objects are printed as intended: no templates", { + templates <- list_all_templates(package = "dplyr") + expected_print_output <- c( + "No ADaM templates available in package 'dplyr'" + ) + expect_identical(capture.output(print(templates)), expected_print_output) +}) - expect_equal(expected_output, actual_output) +test_that("`adam_templates` objects are printed as intended: some templates", { + templates <- c("ADAE", "ADSL") %>% + structure(class = c("adam_templates", "character"), package = "admiral") + expected_print_output <- c( + "Existing ADaM templates in package 'admiral':", + "β€’ ADAE", + "β€’ ADSL" + ) + expect_identical(capture.output(print(templates)), expected_print_output) }) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index fbb6c4c6f1..d639668b79 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -8,13 +8,6 @@ test_that("atomic vectors of length 1", { expect_identical(what_is_it(2.42), "`2.42`") }) -test_that("vectors", { - expect_identical(what_is_it(letters), "a character vector") - expect_identical(what_is_it(1:10), "an integer vector") - expect_identical(what_is_it(c(1.2, 3)), "a double vector") - expect_identical(what_is_it(c(TRUE, FALSE)), "a logical vector") - expect_identical(what_is_it(list(1, letters, TRUE)), "a list") -}) test_that("S3 objects", { expect_identical(what_is_it(mtcars), "a data frame") @@ -109,6 +102,14 @@ test_that("blank strings are turned into `NA` inside data frames", { expect_identical(convert_blanks_to_na(input), expected_output) }) +test_that("`convert_blanks_to_na.list` produces a lists", { + x <- c("", "", "") + expected_output <- lapply(x, convert_blanks_to_na) + actual_output <- convert_blanks_to_na.list(x) + + expect_equal(expected_output, actual_output) +}) + test_that("negate_vars returns list of negated variables", { expect_identical(negate_vars(vars(var1, var2)), rlang::exprs(-var1, -var2)) }) @@ -144,3 +145,33 @@ test_that("`convert_dtm_to_dtc` Error is thrown if dtm is not in correct format" fixed = TRUE ) }) + +test_that("get_constant_vars Test 1: without ignore_vars", { + data <- tibble::tribble( + ~USUBJID, ~AGE, ~AVISIT, + "1", 26, "BASELINE", + "1", 26, "WEEK 1", + "2", 42, "BASELINE", + "2", 42, "WEEK 1" + ) + + expect_equal( + get_constant_vars(data, by_vars = vars(USUBJID)), + vars(USUBJID, AGE) + ) +}) + +test_that("get_constant_vars Test 2: with ignore_vars", { + data <- tibble::tribble( + ~USUBJID, ~AGE, ~WGTBL, ~HGTBL, ~AVISIT, + "1", 26, 61, 172, "BASELINE", + "1", 26, 61, 172, "WEEK 1", + "2", 42, 72, 183, "BASELINE", + "2", 42, 72, 183, "WEEK 1" + ) + + expect_equal( + get_constant_vars(data, by_vars = vars(USUBJID), ignore_vars = vars(WGTBL, HGTBL)), + vars(USUBJID, AGE) + ) +}) diff --git a/tests/testthat/test-warnings.R b/tests/testthat/test-warnings.R deleted file mode 100644 index d548a0c09b..0000000000 --- a/tests/testthat/test-warnings.R +++ /dev/null @@ -1,44 +0,0 @@ -library(admiral.test) - -test_that("A warning is issued when a variable to be derived already exists in the input dataset", { - data(admiral_dm) - - expect_warning( - warn_if_vars_exist(admiral_dm, "AGE"), - "Variable `AGE` already exists in the dataset" - ) - expect_warning( - warn_if_vars_exist(admiral_dm, c("AGE", "AGEU", "ARM")), - "Variables `AGE`, `AGEU` and `ARM` already exist in the dataset" - ) - expect_warning( - warn_if_vars_exist(admiral_dm, c("AAGE", "AGEU", "ARM")), - "Variables `AGEU` and `ARM` already exist in the dataset" - ) - expect_warning( - warn_if_vars_exist(admiral_dm, "AAGE"), - NA - ) -}) - -test_that("A warning is issued when a vector contain unknown datetime format", { - expect_warning( - warn_if_invalid_dtc(dtc = "2021-04-06T-:30:30") - ) -}) - -test_that("A warning is issued when a vector contain an incomplete dtc", { - expect_warning( - warn_if_incomplete_dtc("2021-04-06", n = 19) - ) -}) - -test_that("A warning is issued when two lists are inconsistent", { - expect_warning( - warn_if_inconsistent_list( - base = vars(DTHDOM = "DM", DTHSEQ = DMSEQ, DTHVAR = "text"), - compare = vars(DTHDOM = "DM", DTHSEQ = DMSEQ), - list_name = "Test" - ) - ) -}) diff --git a/vignettes/.gitignore b/vignettes/.gitignore deleted file mode 100644 index 097b241637..0000000000 --- a/vignettes/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.html -*.R diff --git a/vignettes/admiral.Rmd b/vignettes/admiral.Rmd index 9a5c96af27..09690679aa 100644 --- a/vignettes/admiral.Rmd +++ b/vignettes/admiral.Rmd @@ -12,6 +12,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Main Idea @@ -53,33 +55,52 @@ adsl <- dm %>% ## Derive/Impute Numeric Treatment Date/Time and Flag Variables (`TRTSDTM`, `TRTEDTM`, `TRTSTMF`, `TRTETMF`) {#trtdatetimegs} -The function `derive_vars_merged_dtm()` can be used to derive the treatment -start and end date/times using the `EX` domain. This function call returns the original data frame with the column `TRTSDTM`, `TRTEDTM` and flag variables `TRTSTMF`, `TRTETMF` added to the end of the dataframe. Exposure observations with an incomplete date are ignored. We impute time to to be 23:59:59 using `time_imputation = "last"`, which will append 23:59:59 to our `TRTSDTM`, `TRTEDTM` variables. Additionally, we set `flag_imputation = "time"` to get appropriate flag variables `TRTSTMF`, `TRTETMF`. - -Don't be intimidated by the number of arguments! We try to make our arguments self-explanatory, e.g. the `new_vars_prefix` places `TRTS` at the start of the `--DTM` variable and `time_imputation = "last"` appends 23:59:59. However, this is not always possible to make every argument self-explanatory. If you click on the function, `derive_vars_merged_dtm()`, you can bring up the reference documentation and learn more about each argument. +The function `derive_vars_dtm()` can be used to convert the DTC variables from +`EX` to numeric datetime variable and impute missing components. The function +call returns the original data frame with the column `EXSTDTM`, `EXENDTM` and +corresponding time imputation flag variables `EXSTTMF` and `EXENTMF` added to +the end of the dataframe. Exposure observations with an incomplete date are +ignored. We impute missing time to be 23:59:59 using `time_imputation = "last"`. +The required imputation flags are determined automatically by the function. Here +only time imputation flags are derived because time is imputed but date is not +imputed. + +Don't be intimidated by the number of arguments! We try to make our arguments +self-explanatory, e.g. the `new_vars_prefix` places `EXST` at the start of the +`--DTM` variable and `time_imputation = "last"` appends 23:59:59. However, this +is not always possible to make every argument self-explanatory. If you click on +the function, `derive_vars_dtm()`, you can bring up the reference documentation +and learn more about each argument. ```{r, eval=TRUE} # Derive treatment variables -adsl <- adsl %>% - derive_vars_merged_dtm( - dataset_add = ex, - filter_add = nchar(EXSTDTC) >= 10, - new_vars_prefix = "TRTS", +## Impute time of exposure dates (creates numeric datetime and time imputation flag variables) +ex_ext <- ex %>% + derive_vars_dtm( dtc = EXSTDTC, - order = vars(TRTSDTM, EXSEQ), - time_imputation = "last", - flag_imputation = "time", + new_vars_prefix = "EXST" + ) %>% + derive_vars_dtm( + dtc = EXENDTC, + new_vars_prefix = "EXEN", + time_imputation = "last" + ) + +## Derive variables for first/last treatment date and time imputation flags +adsl <- adsl %>% + derive_vars_merged( + dataset_add = ex_ext, + filter_add = !is.na(EXSTDTM), + new_vars = vars(TRTSDTM = EXSTDTM, TRTSTMF = EXSTTMF), + order = vars(EXSTDTM, EXSEQ), mode = "first", by_vars = vars(STUDYID, USUBJID) ) %>% - derive_vars_merged_dtm( - dataset_add = ex, - filter_add = nchar(EXENDTC) >= 10, - new_vars_prefix = "TRTE", - dtc = EXENDTC, - time_imputation = "last", - flag_imputation = "time", - order = vars(TRTEDTM, EXSEQ), + derive_vars_merged( + dataset_add = ex_ext, + filter_add = !is.na(EXENDTM), + new_vars = vars(TRTEDTM = EXENDTM, TRTETMF = EXENTMF), + order = vars(EXENDTM, EXSEQ), mode = "last", by_vars = vars(STUDYID, USUBJID) ) @@ -98,14 +119,16 @@ adsl <- adsl %>% ## Derive Treatment Duration (`TRTDURD`) {#trtdurgs} Now, that `TRTSDT` and `TRTEDT` are derived, the function `derive_var_trtdurd()` -can be used to calculate the Treatment duration (`TRTDURD`). Notice the lack of inputs. The function defaults are set to `TRTSDT` and `TRTEDT`. Clicking on `derive_var_trtdurd()` will bring up the reference documentation where you can see the **Default** arguments. +can be used to calculate the Treatment duration (`TRTDURD`). Notice the lack of inputs. The function defaults are set to `TRTSDT` and `TRTEDT`. Clicking on `derive_var_trtdurd()` will bring up the reference documentation where you can see the **default** arguments. ```{r eval=TRUE} adsl <- adsl %>% derive_var_trtdurd() ``` -Amazing! With one `{dplyr}` function and 3 `{admiral}` functions we successfully created 9 new variables for our `ADSL` dataset. Let's take a look at all our newly derived variables. +Amazing! With one `{dplyr}` function and four `{admiral}` functions we +successfully created nine new variables for our `ADSL` dataset. Let's take a +look at all our newly derived variables. ```{r, eval=TRUE, echo=FALSE} dataset_vignette( @@ -118,7 +141,7 @@ dataset_vignette( ) ``` -**Note:** We only display variables that were derived. A user running this code will have additional adsl variables displayed. You can use the **Choose Columns to Display** button to add more variables into the Table. +**Note:** We only display variables that were derived. A user running this code will have additional adsl variables displayed. You can use the **Choose Columns to Display** button to add more variables into the table. # Derivations @@ -131,8 +154,9 @@ operator. Functions which derive a dedicated variable start with `derive_var_` followed by the variable name, e.g., `derive_var_trtdurd()` derives the `TRTDURD` variable. -Functions which can derive multiple variables start with `derive_vars_` followed by -the variable name, e.g., `derive_vars_merged_dtm` can derive both the `TRTSDT` and `TRTEDT` variables. +Functions which can derive multiple variables start with `derive_vars_` followed +by the variable name, e.g., `derive_vars_dtm()` can derive both the `TRTSDTM` +and `TRTSTMF` variables. Functions which derive a dedicated parameter start with `derive_param_` followed by the parameter name, e.g., `derive_param_bmi()` derives the `BMI` parameter. @@ -156,20 +180,20 @@ be preserved. # Computations -[Computations](../reference/index.html#computations) expect vectors as +[Computations](../reference/index.html#computation-functions-for-vectors) expect vectors as input and return a vector. Usually these computation functions can not be used with `%>%`. These functions can be used in expressions like `convert_dtc_to_dt()` in the derivation of `FINLABDT` in the example below: ```{r, eval=TRUE} -# derive final lab visit date +# Derive final lab visit date ds_final_lab_visit <- ds %>% filter(DSDECOD == "FINAL LAB VISIT") %>% transmute(USUBJID, FINLABDT = convert_dtc_to_dt(DSSTDTC)) -# derive treatment variables +# Derive treatment variables adsl <- dm %>% - # merge on final lab visit date + # Merge on final lab visit date derive_vars_merged( dataset_add = ds_final_lab_visit, by_vars = vars(USUBJID) @@ -285,4 +309,4 @@ channel](https://app.slack.com/client/T028PB489D3/C02M8KN8269). - [Template scripts](https://github.com/pharmaverse/admiral/tree/main/inst/templates) -- [Programming Strategy](programming_strategy.html) +- [Programming Strategy](https://pharmaverse.github.io/admiraldev/main/articles/programming_strategy.html) diff --git a/vignettes/adsl.Rmd b/vignettes/adsl.Rmd index 0010e0bb19..0facb06d21 100644 --- a/vignettes/adsl.Rmd +++ b/vignettes/adsl.Rmd @@ -13,6 +13,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction @@ -33,6 +35,7 @@ otherwise specified.* * [Disposition Dates (e.g. `EOSDT`)](#disposition_date) * [Disposition Status (e.g. `EOSTT`)](#disposition_status) * [Disposition Reason(s) (e.g. `DCSREAS`, `DCSREASP`)](#disposition_reason) + * [Randomization Date (`RANDDT`)](#randomization_date) * [Derive Death Variables](#death) * [Death Date (`DTHDT`)](#death_date) * [Cause of Death (`DTHCAUS`)](#death_cause) @@ -51,7 +54,8 @@ To start, all data frames needed for the creation of `ADSL` should be read into the environment. This will be a company specific process. Some of the data frames needed may be `DM`, `EX`, `DS`, `AE`, and `LB`. -For example purpose, the CDISC Pilot SDTM datasets---which are included in `{admiral.test}`---are used. +For example purpose, the CDISC Pilot SDTM datasets---which are included in +`{admiral.test}`---are used. ```{r, message=FALSE, warning=FALSE} library(admiral) @@ -98,42 +102,54 @@ adsl <- dm %>% ## Derive/Impute Numeric Treatment Date/Time and Duration (`TRTSDTM`, `TRTEDTM`, `TRTDURD`) {#trtdatetime} -The function `derive_vars_merged_dtm()` can be used to derive the treatment -start and end date/times using the `ex` domain. +The function `derive_vars_merged()` can be used to derive the treatment start +and end date/times using the `ex` domain. A pre-processing step for `ex` is +required to convert the variable `EXSTDTC` and `EXSTDTC` to datetime variables +and impute missing date or time components. Conversion and imputation is done by +`derive_vars_dtm()`. Example calls: ```{r eval=TRUE} +# impute start and end time of exposure to first and last respectively, do not impute date +ex_ext <- ex %>% + derive_vars_dtm( + dtc = EXSTDTC, + new_vars_prefix = "EXST" + ) %>% + derive_vars_dtm( + dtc = EXENDTC, + new_vars_prefix = "EXEN", + time_imputation = "last" + ) + adsl <- adsl %>% - derive_vars_merged_dtm( - dataset_add = ex, + derive_vars_merged( + dataset_add = ex_ext, filter_add = (EXDOSE > 0 | (EXDOSE == 0 & - str_detect(EXTRT, "PLACEBO"))) & nchar(EXSTDTC) >= 10, - new_vars_prefix = "TRTS", - dtc = EXSTDTC, - order = vars(TRTSDTM, EXSEQ), + str_detect(EXTRT, "PLACEBO"))) & !is.na(EXSTDTM), + new_vars = vars(TRTSDTM = EXSTDTM, TRTSTMF = EXSTTMF), + order = vars(EXSTDTM, EXSEQ), mode = "first", by_vars = vars(STUDYID, USUBJID) ) %>% - derive_vars_merged_dtm( - dataset_add = ex, + derive_vars_merged( + dataset_add = ex_ext, filter_add = (EXDOSE > 0 | (EXDOSE == 0 & - str_detect(EXTRT, "PLACEBO"))) & nchar(EXENDTC) >= 10, - new_vars_prefix = "TRTE", - dtc = EXENDTC, - time_imputation = "last", - order = vars(TRTEDTM, EXSEQ), + str_detect(EXTRT, "PLACEBO"))) & !is.na(EXENDTM), + new_vars = vars(TRTEDTM = EXENDTM, TRTETMF = EXENTMF), + order = vars(EXENDTM, EXSEQ), mode = "last", by_vars = vars(STUDYID, USUBJID) ) ``` -This call returns the original data frame with the column `TRTSDTM` and -`TRTEDTM` added. Exposure observations with incomplete date and zero doses of -non placebo treatments are ignored. Missing time parts are imputed as first or -last for start and end date respectively. +This call returns the original data frame with the column `TRTSDTM`, `TRTSTMF`, +`TRTEDTM`, and `TRTETMF` added. Exposure observations with incomplete date and +zero doses of non placebo treatments are ignored. Missing time parts are imputed +as first or last for start and end date respectively. The datetime variables returned can be converted to dates using the `derive_vars_dtm_to_dt()` function. @@ -164,20 +180,27 @@ dataset_vignette( ### Disposition Dates (e.g. `EOSDT`) {#disposition_date} -The function `derive_var_disposition_dt()` can be used to derive a disposition date. -The relevant disposition date (`DS.DSSTDTC`) is selected by adjusting the filter parameter. +The functions `derive_vars_dt()` and `derive_vars_merged()` can be used to +derive a disposition date. First the character disposition date (`DS.DSSTDTC`) +is converted to a numeric date (`DSSTDT`) calling `derive_vars_dt()`. Then the +relevant disposition date is selected by adjusting the `filter_add` parameter. To derive the End of Study date (`EOSDT`), a call could be: ```{r eval=TRUE} +# convert character date to numeric date without imputation +ds_ext <- derive_vars_dt( + ds, + dtc = DSSTDTC, + new_vars_prefix = "DSST" +) + adsl <- adsl %>% - derive_vars_merged_dt( - dataset_add = ds, + derive_vars_merged( + dataset_add = ds_ext, by_vars = vars(STUDYID, USUBJID), - new_vars_prefix = "EOS", - dtc = DSSTDTC, - filter_add = DSCAT == "DISPOSITION EVENT" & DSDECOD != "SCREEN FAILURE", - date_imputation = NULL + new_vars = vars(EOSDT = DSSTDT), + filter_add = DSCAT == "DISPOSITION EVENT" & DSDECOD != "SCREEN FAILURE" ) ``` @@ -198,8 +221,6 @@ We would get : dataset_vignette(adsl, display_vars = vars(USUBJID, EOSDT)) ``` - - This call would return the input dataset with the column `EOSDT` added. This function allows the user to impute partial dates as well. If imputation is needed and the date is to be imputed to the first of the month, then set `date_imputation = "FIRST"`. @@ -229,8 +250,9 @@ dataset_vignette(adsl, display_vars = vars(USUBJID, EOSDT, EOSSTT)) This call would return the input dataset with the column `EOSSTT` added. -By default, the function will derive `EOSSTT` as +By default, the function will derive `EOSSTT` as +- `"NOT STARTED"` if `DSDECOD` is `"SCREEN FAILURE"` or `"SCREENING NOT COMPLETED"` - `"COMPLETED"` if `DSDECOD == "COMPLETED"` - `"DISCONTINUED"` if `DSDECOD` is not `"COMPLETED"` or `NA` - `"ONGOING"` otherwise @@ -313,7 +335,7 @@ and `DSTERM` to a suitable `DCSREAS`/`DCSREASP` value. Example function `format_dcsreas()`: ```{r eval=TRUE} -format_dctreas <- function(dsdecod, dsterm = NULL) { +format_dcsreas <- function(dsdecod, dsterm = NULL) { if (is.null(dsterm)) { if_else(dsdecod %notin% c("COMPLETED", "SCREEN FAILURE") & !is.na(dsdecod), dsdecod, NA_character_) } else { @@ -337,6 +359,31 @@ adsl <- adsl %>% ) ``` + +### Randomization Date (`RANDDT`) {#randomization_date} + +The function `derive_vars_merged()` can be used to derive randomization date +variable. To map Randomization Date (`RANDDT`), the call would be: + +```{r eval=TRUE} +adsl <- adsl %>% + derive_vars_merged( + dataset_add = ds_ext, + filter_add = DSDECOD == "RANDOMIZED", + by_vars = vars(STUDYID, USUBJID), + new_vars = vars(RANDDT = DSSTDT) + ) +``` + + +This call would return the input dataset with the column `RANDDT` is added. + +```{r, eval=TRUE, echo=FALSE} +dataset_vignette(adsl, display_vars = vars(USUBJID, RANDDT)) +``` + +[Link](#link_ds) to `DS`. + ## Derive Death Variables {#death} ### Death Date (`DTHDT`) {#death_date} @@ -371,7 +418,7 @@ adsl <- adsl %>% derive_vars_dt( new_vars_prefix = "DTH", dtc = DTHDTC, - date_imputation = "FIRST" + date_imputation = "first" ) ``` @@ -406,7 +453,7 @@ An example call to define the sources would be: src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AESTDTC, + date = AESTDTM, mode = "first", dthcaus = AEDECOD ) @@ -424,7 +471,7 @@ dataset_vignette( src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = "Death in DS" ) @@ -441,8 +488,16 @@ dataset_vignette( Once the sources are defined, the function `derive_var_dthcaus()` can be used to derive `DTHCAUS`: ```{r eval=TRUE} +ae_ext <- derive_vars_dtm( + ae, + dtc = AESTDTC, + new_vars_prefix = "AEST", + highest_imputation = "M", + flag_imputation = "none" +) + adsl <- adsl %>% - derive_var_dthcaus(src_ae, src_ds, source_datasets = list(ae = ae, ds = ds)) + derive_var_dthcaus(src_ae, src_ds, source_datasets = list(ae = ae_ext, ds = ds_ext)) ``` ```{r, eval=TRUE, echo=FALSE} @@ -462,7 +517,7 @@ arguments: src_ae <- dthcaus_source( dataset_name = "ae", filter = AEOUT == "FATAL", - date = AESTDTC, + date = AESTDTM, mode = "first", dthcaus = AEDECOD, traceability_vars = vars(DTHDOM = "AE", DTHSEQ = AESEQ) @@ -471,14 +526,14 @@ src_ae <- dthcaus_source( src_ds <- dthcaus_source( dataset_name = "ds", filter = DSDECOD == "DEATH" & grepl("DEATH DUE TO", DSTERM), - date = DSSTDTC, + date = DSSTDT, mode = "first", dthcaus = DSTERM, traceability_vars = vars(DTHDOM = "DS", DTHSEQ = DSSEQ) ) adsl <- adsl %>% select(-DTHCAUS) %>% # remove it before deriving it again - derive_var_dthcaus(src_ae, src_ds, source_datasets = list(ae = ae, ds = ds)) + derive_var_dthcaus(src_ae, src_ds, source_datasets = list(ae = ae_ext, ds = ds_ext)) ``` ```{r, eval=TRUE, echo=FALSE} @@ -543,22 +598,20 @@ source domain, sequence, variable) An example could be : ```{r eval=TRUE} -ae_src1 <- date_source( +ae_start_date <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "FIRST" + date = AESTDT ) -ae_src2 <- date_source( +ae_end_date <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "LAST" + date = AEENDT ) -lb_src <- date_source( +lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = str_length(LBDTC) >= 10 + date = LBDT, + filter = !is.na(LBDT) ) -adsl_src <- date_source( +trt_end_date <- date_source( dataset_name = "adsl", date = TRTEDT ) @@ -568,11 +621,32 @@ Once the sources are defined, the function `derive_var_extreme_dt()` can be used to derive `LSTALVDT`: ```{r eval=TRUE} +# impute AE start and end date to first +ae_ext <- ae %>% + derive_vars_dt( + dtc = AESTDTC, + new_vars_prefix = "AEST", + highest_imputation = "M" + ) %>% + derive_vars_dt( + dtc = AEENDTC, + new_vars_prefix = "AEEN", + highest_imputation = "M" + ) + +# impute LB date to first +lb_ext <- derive_vars_dt( + lb, + dtc = LBDTC, + new_vars_prefix = "LB", + highest_imputation = "M" +) + adsl <- adsl %>% derive_var_extreme_dt( new_var = LSTALVDT, - ae_src1, ae_src2, lb_src, adsl_src, - source_datasets = list(ae = ae, adsl = adsl, lb = lb), + ae_start_date, ae_end_date, lb_date, trt_end_date, + source_datasets = list(ae = ae_ext, adsl = adsl, lb = lb_ext), mode = "last" ) ``` @@ -589,25 +663,23 @@ Similarly to `dthcaus_source()`, the traceability variables can be added by spec `traceability_vars` argument in `date_source()`. ```{r eval=TRUE} -ae_src1 <- date_source( +ae_start_date <- date_source( dataset_name = "ae", - date = AESTDTC, - date_imputation = "FIRST", + date = AESTDT, traceability_vars = vars(LALVDOM = "AE", LALVSEQ = AESEQ, LALVVAR = "AESTDTC") ) -ae_src2 <- date_source( +ae_end_date <- date_source( dataset_name = "ae", - date = AEENDTC, - date_imputation = "LAST", + date = AEENDT, traceability_vars = vars(LALVDOM = "AE", LALVSEQ = AESEQ, LALVVAR = "AEENDTC") ) -lb_src <- date_source( +lb_date <- date_source( dataset_name = "lb", - date = LBDTC, - filter = str_length(LBDTC) >= 10, + date = LBDT, + filter = !is.na(LBDT), traceability_vars = vars(LALVDOM = "LB", LALVSEQ = LBSEQ, LALVVAR = "LBDTC") ) -adsl_src <- date_source( +trt_end_date <- date_source( dataset_name = "adsl", date = TRTEDTM, traceability_vars = vars(LALVDOM = "ADSL", LALVSEQ = NA_integer_, LALVVAR = "TRTEDTM") @@ -617,8 +689,8 @@ adsl <- adsl %>% select(-LSTALVDT) %>% # created in the previous call derive_var_extreme_dt( new_var = LSTALVDT, - ae_src1, ae_src2, lb_src, adsl_src, - source_datasets = list(ae = ae, adsl = adsl, lb = lb), + ae_start_date, ae_end_date, lb_date, trt_end_date, + source_datasets = list(ae = ae_ext, adsl = adsl, lb = lb_ext), mode = "last" ) ``` @@ -660,7 +732,7 @@ the user defined function(s) would be like: ```{r eval=TRUE} format_agegr2 <- function(var_input) { case_when( - !is.na(var_input) & var_input < 65 ~ "< 65", + var_input < 65 ~ "< 65", var_input >= 65 ~ ">= 65", TRUE ~ NA_character_ ) @@ -723,8 +795,6 @@ The users can add specific code to cover their need for the analysis. The following functions are helpful for many ADSL derivations: - `derive_vars_merged()` - Merge Variables from a Dataset to the Input Dataset - - `derive_vars_merged_dt()` - Merge a (Imputed) Date Variable - - `derive_vars_merged_dtm()` - Merge a (Imputed) Datetime Variable - `derive_var_merged_cat()` - Merge a Categorization Variable - `derive_var_merged_exist_flag()` - Merge an Existence Flag - `derive_var_merged_character()` - Merge a Character Variable @@ -755,4 +825,3 @@ have them associated with an End to End pipeline under the umbrella of the ADaM | Sample Code ---- | -------------- ADSL | [ad_adsl.R](https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adsl.R){target="_blank"} - diff --git a/vignettes/bds_exposure.Rmd b/vignettes/bds_exposure.Rmd index 43b35d2895..eccaa1ef3a 100644 --- a/vignettes/bds_exposure.Rmd +++ b/vignettes/bds_exposure.Rmd @@ -13,6 +13,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction @@ -127,8 +129,8 @@ the user to impute the date as well. Example calls: ```{r eval=TRUE, echo=TRUE} -adex <- derive_vars_dt(adex, new_vars_prefix = "AST", dtc = EXSTDTC, flag_imputation = "none") -adex <- derive_vars_dt(adex, new_vars_prefix = "AEN", dtc = EXENDTC, flag_imputation = "none") +adex <- derive_vars_dt(adex, new_vars_prefix = "AST", dtc = EXSTDTC) +adex <- derive_vars_dt(adex, new_vars_prefix = "AEN", dtc = EXENDTC) ``` ```{r, eval=TRUE, echo=FALSE} @@ -138,7 +140,6 @@ dataset_vignette( ) ``` - The next examples demonstrates the datetime imputation features available in the `derive_vars_dtm()` function, where the time is imputed as "00:00:00": @@ -146,13 +147,14 @@ in the `derive_vars_dtm()` function, where the time is imputed as "00:00:00": adex <- derive_vars_dtm( adex, dtc = EXSTDTC, - date_imputation = "first", + highest_imputation = "M", new_vars_prefix = "AST" ) adex <- derive_vars_dtm( adex, dtc = EXENDTC, + highest_imputation = "M", date_imputation = "last", new_vars_prefix = "AEN" ) @@ -275,7 +277,7 @@ frequency to a corresponding set of records each representing one dose (i.e. ```{r eval=TRUE, echo=TRUE} single_dose <- adex %>% filter(USUBJID == "01-701-1015" & EXSTDY == 1) %>% - create_single_dose_dataset() + create_single_dose_dataset(keep_source_vars = vars(USUBJID, EXDOSE, EXPLDOS, EXDOSFRQ, ASTDT, AENDT)) dataset_vignette( single_dose, diff --git a/vignettes/bds_finding.Rmd b/vignettes/bds_finding.Rmd index 90942dcbfb..f9e71d7825 100644 --- a/vignettes/bds_finding.Rmd +++ b/vignettes/bds_finding.Rmd @@ -13,6 +13,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction @@ -107,7 +109,7 @@ the user to impute the date as well. Example calls: ```{r eval=TRUE} -advs <- derive_vars_dt(advs, new_vars_prefix = "A", dtc = VSDTC, flag_imputation = "none") +advs <- derive_vars_dt(advs, new_vars_prefix = "A", dtc = VSDTC) ``` ```{r, eval=TRUE, echo=FALSE} @@ -140,7 +142,7 @@ advs <- derive_vars_dt( advs, new_vars_prefix = "A", dtc = VSDTC, - date_imputation = "FIRST" + highest_imputation = "M" ) ``` @@ -167,7 +169,7 @@ advs <- derive_vars_dtm( advs, new_vars_prefix = "A", dtc = VSDTC, - date_imputation = "FIRST" + highest_imputation = "M" ) ``` @@ -994,7 +996,7 @@ dataset_vignette( ### Example 3 (Deriving a New `PARAMCD`) -Use function `derive_derived_param()` to create a new `PARAMCD`. Note that only +Use function `derive_param_computed()` to create a new `PARAMCD`. Note that only variables specified in the `by_vars` argument will be populated in the newly created records. @@ -1002,7 +1004,7 @@ Below is an example of creating `Mean Arterial Pressure` (`PARAMCD = MAP2`) with an alternative formula. ```{r eval=TRUE} -advs_ex3 <- derive_derived_param( +advs_ex3 <- derive_param_computed( advs, by_vars = vars(USUBJID, VISIT, ATPT), parameters = c("SYSBP", "DIABP"), diff --git a/vignettes/bds_tte.Rmd b/vignettes/bds_tte.Rmd index 7e46c10929..e0ed7b9346 100644 --- a/vignettes/bds_tte.Rmd +++ b/vignettes/bds_tte.Rmd @@ -13,6 +13,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction @@ -54,17 +56,39 @@ library(lubridate) To start, all datasets needed for the creation of the time-to-event dataset should be read into the environment. This will be a company specific process. -For example purpose, the ADaM datasets---which are included -in `{admiral}`---are used. +For example purpose, the ADSL dataset---which is included +in `{admiral}`---and the SDTM datasets from `{admiral.test}` are used. ```{r} -data("admiral_adsl") data("admiral_ae") -data("admiral_adae") +data("admiral_adsl") -adsl <- admiral_adsl -adae <- admiral_adae ae <- admiral_ae +adsl <- admiral_adsl +``` + +```{r echo=FALSE} +ae <- filter(ae, USUBJID %in% c("01-701-1015", "01-701-1023", "01-703-1086", "01-703-1096", "01-707-1037", "01-716-1024")) +``` + +The following code creates a minimally viable ADAE dataset to be used throughout the following examples. + +```{r} +adae <- ae %>% + left_join(adsl, by = c("STUDYID", "USUBJID")) %>% + derive_vars_dt( + new_vars_prefix = "AST", + dtc = AESTDTC, + highest_imputation = "M" + ) %>% + derive_vars_dt( + new_vars_prefix = "AEN", + dtc = AEENDTC, + highest_imputation = "M", + date_imputation = "last" + ) %>% + mutate(TRTEMFL = if_else(ASTDT >= TRTSDT & + AENDT <= TRTEDT + days(30), "Y", NA_character_)) ``` ## Derive Parameters (`CNSR`, `ADT`, `STARTDT`) {#parameters} @@ -169,7 +193,7 @@ dataset_vignette( ) ``` -Note that in practice for efficacy parameters you might use randomization date as the time to event origin date, but this variable is not in the CDISC Pilot `ADSL` dataset so we used `TRTSDT` for these examples. +Note that in practice for efficacy parameters you might use randomization date as the time to event origin date. ### Add Additional Information for Events and Censoring (`EVNTDESC`, `SRCVAR`, ...) @@ -382,10 +406,10 @@ adtte <- derive_param_tte( dataset_vignette( adtte %>% select( - STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, STARTDTF, ADT, ADTF, CNSR, + STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, ADTF, CNSR, EVNTDESC, SRCDOM, SRCVAR ), - display_vars = vars(USUBJID, PARAMCD, STARTDT, STARTDTF, ADT, ADTF, CNSR) + display_vars = vars(USUBJID, PARAMCD, STARTDT, ADT, ADTF, CNSR) ) ``` @@ -418,7 +442,7 @@ observation_end <- censor_source( # define time to first AE # tt_ae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = ASTDT, set_values_to = vars( EVNTDESC = "ADVERSE EVENT", SRCDOM = "AE", @@ -430,7 +454,7 @@ tt_ae <- event_source( tt_ser_ae <- event_source( dataset_name = "ae", filter = AESER == "Y", - date = AESTDTC, + date = ASTDT, set_values_to = vars( EVNTDESC = "SERIOUS ADVERSE EVENT", SRCDOM = "AE", @@ -442,7 +466,7 @@ tt_ser_ae <- event_source( tt_rel_ae <- event_source( dataset_name = "ae", filter = AEREL %in% c("PROBABLE", "POSSIBLE", "REMOTE"), - date = AESTDTC, + date = ASTDT, set_values_to = vars( EVNTDESC = "RELATED ADVERSE EVENT", SRCDOM = "AE", @@ -468,7 +492,7 @@ adaette <- call_derivation( ) ), dataset_adsl = adsl, - source_datasets = list(adsl = adsl, ae = ae), + source_datasets = list(adsl = adsl, ae = adae), censor_conditions = list(observation_end) ) ``` @@ -521,7 +545,10 @@ ae <- tibble::tribble( "01", "2021-03-04", 2, "Cough", "01", "2021", 3, "Flu" ) %>% - mutate(STUDYID = "AB42") + mutate( + STUDYID = "AB42", + AESTDT = convert_dtc_to_dt(dtc = AESTDTC, highest_imputation = "M") + ) dataset_vignette(ae) ``` @@ -530,7 +557,7 @@ dataset_vignette(ae) # define time to first adverse event event # ttae <- event_source( dataset_name = "ae", - date = AESTDTC, + date = AESTDT, set_values_to = vars( EVNTDESC = "AE", SRCDOM = "AE", diff --git a/vignettes/contribution_model.Rmd b/vignettes/contribution_model.Rmd index 1346dfe1a4..eec0779059 100644 --- a/vignettes/contribution_model.Rmd +++ b/vignettes/contribution_model.Rmd @@ -13,6 +13,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction diff --git a/vignettes/development_process.Rmd b/vignettes/development_process.Rmd deleted file mode 100644 index 01c6ca0c57..0000000000 --- a/vignettes/development_process.Rmd +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: "Development Process" -output: - rmarkdown::html_vignette - -vignette: > - %\VignetteIndexEntry{Development Process} - %\VignetteEngine{knitr::rmarkdown} ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -Once you’ve familiarized yourself with the `{admiral}` [contribution -model](contribution_model.html) and you’re ready to make your first code -contribution, this development process step-by-step guide will help tie all the -other detailed vignettes together to give you the simplest experience of helping -to grow and enhance our codebase. - -1. To start, you will have either created an issue or commented on an existing -issue to notify that you’d like to contribute code. Then one of the `{admiral}` -core team will assign you to the issue. -1. Create a new feature branch from the development branch `devel` following the naming convention and pull the latest changes - as detailed on the [github usage](git_usage.html#working-with-feature-branches-1) guide. -1. Familiarize yourself with the `{admiral}` [programming -strategy](programming_strategy.html), and then make the required code updates. -1. Before making a pull request, check the [Pull Request Review Guidance](pr_review_guidance.html) & the following checklist of common things developers miss: - a. Is all your code formatted according to the [tidyverse](https://style.tidyverse.org/) style guide? - a. Did you create/add appropriate [unit tests](unit_test_guidance.html#writing-unit-tests-in-admiral-)? - a. If you removed/replaced any function and/or function parameters, did you fully follow the [deprecation guidance](programming_strategy.html#deprecation-1)? - a. Did you update the [documentation](programming_strategy.html#function-header-documentation-)? If so, remember to run `devtools::document()` and include the updated `NAMESPACE` and `.Rd` files in `man/` - a. Does your code update have any impact on the [ADaM template](admiral.html#starting-a-script-1) R scripts stored in `inst/templates`? - a. Does your code update have any impact on the vignettes stored in vignettes? - a. Did you update the Changelog `NEWS.md`? - a. Did you build `{admiral}` site `pkgdown::build_site()` and check that all affected examples are displayed correctly and that all new functions occur on the "[Reference](../reference/index.html)" page? -1. Once happy with all the updates, make a [pull request](git_usage.html#pull-request) to merge to the development branch `devel` and link the issue so that it closes after successful merging. -1. Check that there are no merge conflicts. If there are any, fix them before requesting review. See [solving merge conflicts](git_usage.html#solving-merge-conflicts-in-the-terminal-on-rstudio) guidance. -1. Check the results of the automated `R-CMD check` and `lintr` checks and if any issues consult this [guide](pr_review_guidance.html#common-r-cmd-check-issues-1). -1. Assign a reviewer from the `{admiral}` core development team – this could be -anyone you discussed the issue with previously via Slack or GitHub. If unsure, -add a comment that the pull request is ready for review and add the -`@pharmaverse/admiral` tag to it. -1. Once the review is completed, the reviewer will merge the PR and this will then automatically delete the feature branch. - -_Finally, just a note to say from the core developers that we hugely appreciate -you taking the time to contribute to `{admiral}`. Don’t be offended if during -review we send requests back to you, as the expectations are high so that we can -ensure the `{admiral}` codebase remains robust and consistent. The best way to learn -here is always to jump in and get involved, so please don’t be afraid you’ll -make mistakes along the way – we all have and continue to do so, and that’s what -the reviews are for. Also if ever you get stuck don't hesitate to reach out for -support via the [Slack -channel](https://app.slack.com/client/T028PB489D3/C02M8KN8269). **Welcome to our -`{admiral}` community!**_ diff --git a/vignettes/faq.Rmd b/vignettes/faq.Rmd deleted file mode 100644 index 3e64ee4a92..0000000000 --- a/vignettes/faq.Rmd +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: "FAQ" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 6 -vignette: > - %\VignetteIndexEntry{FAQ} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -##### **What is admiral**? - -* Think of `{admiral}` as a toolbox of modular blocks (R functions) to create analysis derivations: - * each block has a stand alone purpose (each function provides a specific functionality) - * Data Scientists can create their own blocks (create own R functions) - -* Constructing ADaM dataset should become like building out of blocks that are based on `{admiral}` modular functions and user created modular functions. - -##### **Why** did we decide to start **admiral**? - - * Data analysis challenges in clinical trials vary depending on scientific goals, therapeutic areas, indications, data sources and data quality. We all face the same challenge so why limit ourselves only to company-level adoption and crowd-sourcing to create ADaM datasets? - * Build ADaMs via collaboration and co-creation - * Early engagement with other like-minded companies moving towards R could lead to our solution being shared open source as a framework for contribution across-industry - * Building ADaMs like a modular building blocks, everyone can contribute and each module has a clear input and output to enable re-usable solutions - * Users can β€œslot in” their own modules to address specific company/TA/Molecule/Study requirements - * TA specific requirements can be open sourced again and transformed into a common ADaM approach for such analysis - * the long-term gain of a consistent way of producing ADaM and a wider community of across-industry developers contributing to grow the codebase to cover the infinite array of possibilities - * Contributors: An option to make a name for yourself in the Pharma open-source community & an avenue to collaborate with other like-minded people across the industry - * Imagine if ADaMs are built in a consistent manner with the same code from openly maintained functions and its impact on the Health Authorities, readable code, QC, talent flow - -##### Why did we use **R as a programming language**? - - * R is not an isolated software product, everyone can contribute (open source principal) - * People from University/Statistical talent pipeline more likely to come through with R skills rather than a proprietary language - * There seems to be a strong data science/analytics R community - * FDA open to accepting R submissions and are heavy users themselves - * Top of the line visualization/graphics - R-Shiny for interactive data displays and also R Markdown offers great report writing functionality - * R is very popular among statisticians so new statistical methods are likely implemented in R before any other language - * There might be equally suited programming languages out there - however at some stage we had to make a decision :) - -##### Why do we use a **certain R version and package versions** for development? - -* The choice of R Version is not set in stone. However, a common development environment is important to establish when working across multiple companies and multiple developers. We currently work in R Version 3.6.3, but that will change as we move forward with `{admiral}`. This need for a common development environment also carries over for our choice of package versions. -* GitHub allows us through the Actions/Workflows to test `{admiral}` under several versions of R as well as several versions of dependent R packages needed for `{admiral}`. Currently we test `{admiral}` against R Version 3.6.3 with a CRAN package snapshot from 2020-02-29, R Version 4.0 with a CRAN package snapshot from 2021-03-31 and the latest R version with the latest snapshots of packages. You can view this workflow on our [GitHub Repository](https://github.com/pharmaverse/admiral/blob/main/.github/workflows/R-CMD-check.yml). -* This common development allows us to easily re-create bugs and provide solutions to each other issues that developers will encounter. -* Reviewers of Pull Requests when running code will know that their environment is identical to the initiator of the Pull Request. This ensures faster review times and higher quality Pull Request reviews. -* We achieve this common development environment by using a **lockfile** created from the [`renv`](https://rstudio.github.io/renv/) package. New developers will encounter a suggested `renv::restore()` in the console to revert or move forward your R version and package versions. - - -##### Admiral offers a **toolbox of functions to facilitate ADaM**. What does that mean? - -* Functions are usually not necessarily specific but parameter driven: - * e.g. the `derive_vars_aage` has a parameterized start and end-date and a unit. - * Depending on the parameters results may vary as does the specification. - * Functions serve as a toolbox so the user can create their ADaM according to the requirements. - * The principles, programming strategy and documentation of `{admiral}` are considered as a framework for users to contribute. - -##### How does a user **know what a function does** exactly? - -* Function details and its purpose, the requirements, parameters, dependencies and examples are documented in the header of each function. -* Complex functions potentially have a vignette on the `{admiral}` homepage to provide more details. -* `{admiral}` does not provide a link to an explicit specification in the define.xml. - -##### Would `{admiral}` **create a whole** ADaM dataset? - - * `{admiral}` is meant as a toolbox to enable Data Scientists to build ADaMs according to their varying analysis needs - * `{admiral}` is not meant as a "click a button, out comes your ADaM" tool - * on the `{admiral}` webpage, example scripts are provided which can be used as a starting point to create an ADaM (see at the end of a vignette) - -##### In **which order** does a user need to execute the functions? - -* Guidance will be provided for ADSL, BDS and OCCDS ADaM structure including template scripts. - -##### Is the `{admiral}` package **validated**? - - * All functions are reviewed and tested (see [What will be provided around **function testing**?]) to ensure that they work as described in the documentation. - * Test cases for each function will be part of the R package. - * Users can add to the tests or provide additional feedback. - * The testing the `{admiral}` team will do for each function does **not replace the QC and validation process at each company**. - * A [GitHub action](https://github.com/marketplace/actions/r-package-validation-report) (using open source packages) exists to generate a validation report for an R package, which would be an option for any company to use. An example report using an earlier version of `{admiral}` exists [here](https://github.com/insightsengineering/thevalidatoR/blob/main/readme_files/report-0.1-admiral.pdf) as an illustration. - -##### What will be provided around **function testing**? - - * Unit tests for reliability of each function - available as part of open source release - * Some integration testing will be done to ensure functions can be called together to create ADaM (e.g. even via the internal testing teams) - * Guidance for testing and documentation expectations of community contribution functions. Then it is for each company to cover the following: - * validation to be able to use the package on company-specific SCE for GxP purposes and associated audit evidence - * strategy of how the use of `{admiral}` fits into company-specific quality assurance process (double programming comparison versus your company-specific legacy ADaM solution could be appropriate until confidence builds) - * see our guidance on [unit testing](unit_test_guidance.html) - -##### Will **admiral provide harmonized define.xml** or submittable specifications for functions? - - * No. The functions are documented via programming headers, the define.xml is the responsibility of the end user. - * Functions are usually generalized and not specific. (see [Admiral offers a **toolbox of functions to facilitate ADaM**. What does that mean?](https://pharmaverse.github.io/admiral/articles/faq.html#admiral-offers-a-toolbox-of-functions-to-facilitate-adam--what-does-that-mean)) - * The users are responsible to make sure they use the functions and their parameters in the right way to ensure alignment with their define.xml - -##### Will `{admiral}` provide ADaM IG **CDISC compliant** datasets? - -* Although `{admiral}` follows CDISC standards it does not claim that the dataset resulting from calling `{admiral}` functions is ADaM compliant. This has to be ensured by the user. - -##### How much of the **ADaM IG is covered by admiral?** - - * ADaM IG is a standard framework without a specific number of datasets or variables, so it cannot be used as a specific baseline to answer that question. - * We will provide guidance for each ADaM dataset structure (ADSL, OCCDS and BDS) that will highlight which functionality `{admiral}` covers. (see [In **which order** does a user need to execute the functions?]) - * The guidance will also highlight the gaps to be filled by the user (e.g. timing, ranges). - * For standard ADaM datasets (ADAE, ADCM, ...) we can provide an estimated coverage based on early adopters Roche/GSK ADaM implementation - -##### Will there be a user/**contribution** guide? - - * Our [programming strategy](programming_strategy.html) serves as a framework - for users how to create their own functions. - * Please see the [contribution model website](contribution_model.html) for a detailed description how to contribute - -##### How has `{admiral}` been **tested externally** to Roche/GSK? - -* During Sept/Oct 2021, a limited release testing was conducted with 18 other companies (and >50 individuals) in order to assess compatibility of the `{admiral}` toolkit with different company standards implementations and to test the usability of the functions, e.g. clarity, reliability, robustness, and flexibility. -* This foundational version of `{admiral}` achieved a 7.9 / 10 average score from all the survey respondents and >75% said they'd advocate using `{admiral}` for ADaM transformations in R. -* Some tester quotes: - * _"Extremely easy to learn and get into, well thought and planned. Plenty of minor functions instead of aiming to create a large "jack of all trades" framework. The toolkit does not attempt to become a large one-button ADaM generator (which is fantastic)."_ - * _"It is a huge advantage for all Pharma companies that we have common functions for common stuff we develop. It will be easier for the authorities when it is the same foundation in the ADaM programs. The development goes faster for every one when we develop across companies, and bug-fixing is faster as many are using same package and will most likely find potential bugs."_ - * _"I am a huge proponent of shared solutions within Pharma. Overall I was VERY impressed with the `{admiral}` project -- both the design, development, documentation, and validation details are available for teams to readily adopt. Prior to participating in the testing, I was a SAS hold-out who wasn't able to see a strong need to shift to R. This project has shown me a reason to consider making the shift."_ diff --git a/vignettes/git_usage.Rmd b/vignettes/git_usage.Rmd deleted file mode 100644 index 37cc9ae664..0000000000 --- a/vignettes/git_usage.Rmd +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: "Guidance for git and GitHub Usage" -output: - rmarkdown::html_vignette: - toc: true - toc_depth: 6 -vignette: > - %\VignetteIndexEntry{Guidance for git and GitHub Usage} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - - -# Introduction - -This article will give you an overview of how the `{admiral}` project is utilizing the version-control software `git` and the website GitHub while working with RStudio. We will go over the primary branches that house the source code for the `{admiral}` project as well as how we use **Feature** branches to address **Issues**. Issues can range from bugs to enhancement that have been identified or requested by developers, users or testers. Finally, we provide the bare minimum of `git` commands needed to get up and running. Please refer to the [Resource](#github_resources) section for more in-depth guidance on using `git` and GitHub. - -# Branches - -- The `main` branch contains the latest **released** version and should not be used for development. You can find the released versions [here](https://GitHub.com/pharmaverse/admiral/releases) -- The `devel` branch contains the latest development version of the package. You will mostly likely be branching off and pulling into the `devel` branch. -- The `gh-pages` branches contains the code used to render this website you are looking at right now! -- The `main`, `devel` and `gh-pages` branches are under protection. If you try and push changes to these branches you will get an error unless you are an administrator. - -- **Feature** branches are where actual development related to a specific issue happens. Feature branches are merged into `devel` once a pull request is merged. Check out the [Pull Request Review Guidance](pr_review_guidance.html). - - -# Working with Feature Branches - -Feature Branches are where most developers will work when addressing Issues. - -## Implementing an Issue - -Each feature branch must be related to an issue. - -**Name:** -The name of the branch must be prefixed with the issue number and be meaningful, e.g. issue #94 "Program function to derive `LSTALVDT`", the Branch name would be: `94_lstalvdt`. - -### Create a New Feature Branch from the Terminal (from `devel`) - -- Checkout the devel branch: `git checkout devel` -- Pull the latest changes from GitHub: `git pull` -- Create a new branch off the devel branch and switch to it: `git checkout -b ` - -### Create a New Feature Branch from GitHub (from `devel`) - -You can also create a feature branch in GitHub. - -- Switch to the `devel` branch -- Type in your new feature branch name -- Click Create branch: `` from `devel` -- Be Sure to Pull down newly created branch into RStudio - -```{r, echo = FALSE} -knitr::include_graphics("github_feature_branch.png", dpi = 144) -``` - - -### Commits from the Terminal in RStudio - -To start the commit process, you will need to tell `git` to move your changes to the staging area. Use `git add ` to move all changes of `` in the staging area to wait for the next commit. You can use `git add .` to move all files you have worked on to the staging area. Next you can commit, which takes a snapshot of your staged changes. When committing, prefix the message with the issue number and add a meaningful message `git commit –m '#94 last alive date implementation'`. - -Lastly, you should push your changes up to GitHub using `git push origin ` - -### Commits from the Git Tab in RStudio - -You can also make use of the Git Tab within RStudio to commit your changes. A benefit of using this Tab is being able to see your changes to the file with red and green highlighting. Just like in the terminal, start the message with the issue number and add a meaningful and succinct sentence. Hit the Commit button and then Push up to GitHub. - -```{r, echo = FALSE} -knitr::include_graphics("github_committ.png", dpi = 144) -``` - - -**NOTE:** Placing the issue number in your commit message allows new developers to quickly find discussion surrounding your issue. When pushed to GitHub the issue number will hyperlink to the issue tracker. A powerful tool for discussion. - -## Pull request - -We recommend a thorough read through of the articles, [Pull Request Review Guidance](pr_review_guidance.html) and the [Programming Strategy](programming_strategy.html) for in-depth discussions on a doing a proper Pull Request. Pull Request authors will benefit from shorter review times by closely following the guidance provided in those two articles. Below we discuss some simple `git` commands in the terminal and on GitHub for doing a Pull Request. We recommend doing the Pull Request in GitHub only and not through the terminal. - -Once all changes are committed, push the updated branch to GitHub: -`git push -u origin ` - -In GitHub, under **Pull requests**, the user will either have a "Compare and pull request" button and/or a "Create Pull Request". The first button will be created for you if GitHub detects recent changes you have made. The branch to merge with must be the `devel` branch (base = `devel`) and the compare branch is the new branch to merge - as shown in the below picture. Please **pay close attention** to the branch you are merging into! - -The issue must be linked to the pull request in the "Development" field of the -Pull Request. - -```{r, echo = FALSE} -knitr::include_graphics("github_linked_issues_dark.png", dpi = 144) -``` - -Once you have completed the Pull Request you will see all committed changes are then available for the reviewer. A reviewer must be specified in the Pull Request. It is recommended to write a brief summary to your reviewers so they can quickly come up to speed on your Pull Request. Pictures are nice too, which are easy to do in GitHub! Use any Screen Capture software and Copy and Paste into your summary. - -```{r, echo = FALSE} -knitr::include_graphics("github_create_pr.png", dpi = 144) -``` - -### Reviewing/Closing an Issue - -- At least one reviewer must approve the Pull Request. Please review the [Pull -Request Review Guidance](pr_review_guidance.html), which provides in depth -guidance on doing a proper Pull Request. -- The reviewer must ensure that the function follows the programming strategy -recommendations. -- Any comment/question/discussion must be addressed and documented in GitHub -before the Pull Request is merged - -Once the review is completed, the reviewer will merge the Pull Request and the -feature branch will automatically be deleted. - -After merging the Pull Request the corresponding issue must be moved to the -"Done" column on the "admiral core board" by the developer. **The issue -must not be closed**. It will be closed automatically once the `devel` branch is -merged into the `main` branch. - - -```{r, echo = FALSE} -knitr::include_graphics("github_done.png", dpi = 144) -``` - -### Solving Merge Conflicts in the Terminal on RStudio - -Merge conflict is a situation where `git` cannot decide which changes to apply since there were multiple updates in the same part of a file. This typically happens when multiple people update the same part of code. Those conflicts always need to be handled manually (as some further code updates may be required): - -``` -git checkout devel -git pull -git checkout -git merge devel -``` - -This provides a list of all files with conflicts In the file with conflicts the conflicting sections are marked with `<<<<<<<`, `=======`, and `>>>>>>>`. The code between these markers must be updated and the markers be removed. Source files need to be updated manually. Generated files like NAMESPACE or the generated documentation files should not be updated manually but recreated after the source files were updated. - -To make the changes available call: - -``` -git add -git commit -m "" -git push -``` - -### Solving Merge Conflicts in GitHub - -For simple merge conflicts, developers can make use of the GitHub interface to solve them. GitHub will show the number of conflicts between the two branches. In the below image, GitHub has found 3 conflicts, but we only display the first one. Just like in the terminal, GitHub will make use of the `<<<<<<<`, `=======`, and `>>>>>>>` to highlight the conflicting sections. You will need to make the decision on whether to keep the code from the base or the feature branch. Once you have decided, go into the code and remove the section you no longer wish to keep. Be sure to remove the `<<<<<<<`, `=======`, and `>>>>>>>` as well! Once you work through the conflicts you will mark as **Resolved and Commit your changes**. It is recommended to pull your branch back down to RStudio to make sure no untoward effects have happen to your branch. - -```{r, echo = FALSE} -knitr::include_graphics("github_conflicts.png", dpi = 144) -``` - - -## Useful `git` Commands - -- merging: `git merge ` - merge my_branch into current branch -- The stashing commands are useful when one wants to go back to clean directory -- `git stash` - stash (store) current changes and restore a clean directory -- `git stash pop` - put back (restore) stashed changes -- `git revert` is also helpful but why? - -**Using code from unmerged branches** - -- Checkout the unmerged branch you want to use: `git checkout ` -- Pull the latest committed changes from the unmerged branch: `git pull` -- Check out your feature branch: `git checkout ` -- Merge the unmerged branch to : `git merge ` - -# Resources on using `git`, GitHub and RStudio {#github_resources} - -* [GitHub and RStudio](https://resources.github.com/whitepapers/github-and-rstudio/) -* [Happy Git and GitHub for the useR](https://happygitwithr.com/) -* [Git and GitHub from the R Package Manual](https://r-pkgs.org/git.html) - - diff --git a/vignettes/github_committ.png b/vignettes/github_committ.png deleted file mode 100644 index abd555dace..0000000000 Binary files a/vignettes/github_committ.png and /dev/null differ diff --git a/vignettes/github_conflicts.png b/vignettes/github_conflicts.png deleted file mode 100644 index 139126d96b..0000000000 Binary files a/vignettes/github_conflicts.png and /dev/null differ diff --git a/vignettes/github_create_pr.png b/vignettes/github_create_pr.png deleted file mode 100644 index de44a1d3e7..0000000000 Binary files a/vignettes/github_create_pr.png and /dev/null differ diff --git a/vignettes/github_done.png b/vignettes/github_done.png deleted file mode 100644 index 7c94e919bf..0000000000 Binary files a/vignettes/github_done.png and /dev/null differ diff --git a/vignettes/github_feature_branch.png b/vignettes/github_feature_branch.png deleted file mode 100644 index 5d71d70059..0000000000 Binary files a/vignettes/github_feature_branch.png and /dev/null differ diff --git a/vignettes/github_linked_issues.png b/vignettes/github_linked_issues.png deleted file mode 100755 index 452011afa9..0000000000 Binary files a/vignettes/github_linked_issues.png and /dev/null differ diff --git a/vignettes/github_linked_issues_dark.png b/vignettes/github_linked_issues_dark.png deleted file mode 100644 index e8ccd90d36..0000000000 Binary files a/vignettes/github_linked_issues_dark.png and /dev/null differ diff --git a/vignettes/github_sprint_done.png b/vignettes/github_sprint_done.png deleted file mode 100755 index 68e7b753cd..0000000000 Binary files a/vignettes/github_sprint_done.png and /dev/null differ diff --git a/vignettes/higher_order.Rmd b/vignettes/higher_order.Rmd new file mode 100644 index 0000000000..740eb500d8 --- /dev/null +++ b/vignettes/higher_order.Rmd @@ -0,0 +1,318 @@ +--- +title: "Higher Order Functions" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Higher Order Functions} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r setup, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) + +library(admiraldev) +``` + +# Introduction + +This vignette explains some of the more advanced options of `{admiral}` related +to higher order functions. A higher order function is a function that takes another +function as input. By introducing these higher order functions, we intend to give +the user greater power over our derivation functions, whilst trying to negate the +need for adding additional `{admiral}` functions or arguments, or the user needing +many separate steps. + +The functions covered here are: + +* `call_derivation()`: Call a single derivation multiple times with some +arguments being fixed across iterations and others varying +* `restrict_derivation()`: Execute a single derivation on a subset of the input dataset +* `slice_derivation()`: The input dataset is split into slices (subsets) and for +each slice a single derivation is called separately. Some or all arguments of the +derivation may vary depending on the slice. + +## Required Packages + +The examples of this vignette require the following packages. + +For example purpose, the ADSL dataset---which is included +in `{admiral}`---and the SDTM datasets from `{admiral.test}` are used. + +```{r, warning=FALSE, message=FALSE} +library(admiral) +library(admiral.test) +library(dplyr) + +data("admiral_adsl") +data("admiral_ae") +data("admiral_vs") +adsl <- admiral_adsl +ae <- convert_blanks_to_na(admiral_ae) +vs <- convert_blanks_to_na(admiral_vs) +``` +```{r echo=FALSE} +adsl <- filter(adsl, USUBJID %in% c("01-701-1111", "01-705-1393")) +ae <- filter(ae, USUBJID %in% c("01-701-1111", "01-705-1393")) +vs <- filter(vs, USUBJID %in% c("01-701-1015")) +``` + +The following code creates a minimally viable ADAE dataset to be used where +needed in the following examples. + +```{r} +adae <- ae %>% + left_join(adsl, by = c("STUDYID", "USUBJID")) %>% + derive_vars_dt( + new_vars_prefix = "AST", + dtc = AESTDTC, + highest_imputation = "M" + ) %>% + mutate(TRTEMFL = if_else(ASTDT >= TRTSDT, "Y", NA_character_)) +``` + +# Call Derivation + +This function exists purely for convenience to save the user repeating numerous +similar derivation function calls. It is best used when multiple derived variables +have very similar specifications with only slight variations. + +As an example, imagine the case where all the parameters in a BDS ADaM required +both a highest value flag and a lowest value flag. + +Here is an example of how to achieve this **without** using `call_derivation()`: + +```{r} +vs_without <- vs %>% + derive_var_extreme_flag( + by_vars = vars(USUBJID, VSTESTCD), + order = vars(VSORRES, VSSEQ), + new_var = AHIFL, + mode = "last" + ) %>% + derive_var_extreme_flag( + by_vars = vars(USUBJID, VSTESTCD), + order = vars(VSORRES, VSSEQ), + new_var = ALOFL, + mode = "first" + ) +``` + +```{r, eval=TRUE, echo=FALSE} +vs_without %>% + arrange(USUBJID, VSTESTCD, VSDY, VSSEQ) %>% + dataset_vignette( + display_vars = vars(USUBJID, VSTESTCD, VSORRES, ALOFL, AHIFL), + filter = VSTESTCD %in% c("TEMP", "WEIGHT") + ) +``` + +Here is an example of how to achieve the same **with** using `call_derivation()`, +where any different arguments are passed using `params()`: + +```{r} +vs_with <- vs %>% + call_derivation( + derivation = derive_var_extreme_flag, + variable_params = list( + params(new_var = AHIFL, mode = "last"), + params(new_var = ALOFL, mode = "first") + ), + by_vars = vars(USUBJID, VSTESTCD), + order = vars(VSORRES, VSSEQ) + ) +``` + +```{r, eval=TRUE, echo=FALSE} +vs_with %>% + arrange(USUBJID, VSTESTCD, VSDY, VSSEQ) %>% + dataset_vignette( + display_vars = vars(USUBJID, VSTESTCD, VSORRES, ALOFL, AHIFL), + filter = VSTESTCD %in% c("TEMP", "WEIGHT") + ) +``` + +In the example, you can see how in these higher order functions, `derivation` +is where the user supplies the name of the derivation function to apply, with +no trailing parentheses required. Then `variable_params` is used to pass a +list of the different arguments needed for each derived variable. + +The advantage of this higher order function would be further highlighted with +examples where more than two variable derivations had similar needs, such as the +below case where multiple time to AE parameters are derived in one call. +Note that this example relies on pre-defined `tte_source` objects, as explained +at [Creating a BDS Time-to-Event ADaM](bds_tte.html). + +```{r} +adaette <- call_derivation( + derivation = derive_param_tte, + variable_params = list( + params( + event_conditions = list(ae_event), + set_values_to = vars(PARAMCD = "TTAE") + ), + params( + event_conditions = list(ae_ser_event), + set_values_to = vars(PARAMCD = "TTSERAE") + ), + params( + event_conditions = list(ae_sev_event), + set_values_to = vars(PARAMCD = "TTSEVAE") + ), + params( + event_conditions = list(ae_wd_event), + set_values_to = vars(PARAMCD = "TTWDAE") + ) + ), + dataset_adsl = adsl, + source_datasets = list(adsl = adsl, adae = adae), + censor_conditions = list(lastalive_censor) +) +``` + +```{r, eval=TRUE, echo=FALSE} +adaette %>% + select(USUBJID, PARAMCD, STARTDT, ADT, CNSR, EVNTDESC, SRCDOM, SRCVAR) %>% + arrange(USUBJID, PARAMCD) %>% + dataset_vignette(display_vars = vars(USUBJID, PARAMCD, STARTDT, ADT, CNSR, EVNTDESC, SRCDOM, SRCVAR)) +``` + +Developing your ADaM scripts this way using `call_derivation()` could give the +following benefits: + +* code becomes more efficient and readable +* maintenance would be eased in case of specification changes +* downstream quality checking would require less effort + +# Restrict Derivation + +The idea behind this function is that sometimes you want to apply a +derivation only for certain records from the input dataset. Introducing +`restrict_derivation()` therefore gives the users the ability to achieve +this across any function, without each function needing to have such an +argument to allow for this. + +An example would be if you wanted to flag the first occurring AE with the +highest severity for each patient, but you only wanted to do this for +records occurring on or after study day 1. + +Here is how you could achieve this using `restrict_derivation()`, +where the function arguments are passed using `params()` and the restriction +criteria is given using `filter`: + +```{r} +ae <- ae %>% + mutate(TEMP_AESEVN = as.integer(factor(AESEV, levels = c("SEVERE", "MODERATE", "MILD")))) %>% + restrict_derivation( + derivation = derive_var_extreme_flag, + args = params( + new_var = AHSEVFL, + by_vars = vars(USUBJID), + order = vars(TEMP_AESEVN, AESTDY, AESEQ), + mode = "first" + ), + filter = AESTDY >= 1 + ) +``` + +```{r, eval=TRUE, echo=FALSE} +ae %>% + arrange(USUBJID, AESTDY, AESEQ, desc(TEMP_AESEVN)) %>% + dataset_vignette( + display_vars = vars(USUBJID, AEDECOD, AESTDY, AESEQ, AESEV, AHSEVFL) + ) +``` + +# Slice Derivation + +This function in a way combines the features of the above two. It allows a single +derivation to be applied with different arguments for different slices (subsets) +of records from the input dataset. You could do this with separate `restrict_derivation()` +calls for each different set of records, but `slice_derivation()` allows +to achieve this in one call. + +An example would be if you wanted to achieve the same derivation as above +for records occurring on or after study day 1, but for pre-treatment AEs +you wanted to flag only the last occurring AE. + +Here is how you could achieve this using `slice_derivation()`, +where the function arguments are passed using `params()` and via the different +slices controlled by `filter`: + +```{r} +ae <- ae %>% + slice_derivation( + derivation = derive_var_extreme_flag, + args = params( + new_var = AHSEV2FL, + by_vars = vars(USUBJID) + ), + derivation_slice( + filter = AESTDY >= 1, + args = params(order = vars(TEMP_AESEVN, AESTDY, AESEQ), mode = "first") + ), + derivation_slice( + filter = TRUE, + args = params(order = vars(AESTDY, AESEQ), mode = "last") + ) + ) +``` + +```{r, eval=TRUE, echo=FALSE} +ae %>% + arrange(USUBJID, AESTDY, AESEQ, desc(TEMP_AESEVN)) %>% + dataset_vignette( + display_vars = vars(USUBJID, AEDECOD, AESTDY, AESEQ, AESEV, AHSEV2FL) + ) +``` + +As you can see in the example, the `derivation_slice` ordering is important. +Here we addressed all the AEs on or after study day 1 first, and then we used +`filter = TRUE` option to catch all remaining records (in this case +pre-treatment AEs). + +The ordering is perhaps shown even more when we look at the below example where +three slices are taken. Remember that observations that match with more than one +slice are only considered for the first matching slice. So in this case we're +creating a flag for each patient for the record with the first severe AE, and +then the first moderate AE, and finally flagging the last occurring AE where +not severe or moderate. + +```{r} +ae <- ae %>% + slice_derivation( + derivation = derive_var_extreme_flag, + args = params( + new_var = AHSEV3FL, + by_vars = vars(USUBJID) + ), + derivation_slice( + filter = AESEV == "SEVERE", + args = params(order = vars(AESTDY, AESEQ), mode = "first") + ), + derivation_slice( + filter = AESEV == "MODERATE", + args = params(order = vars(AESTDY, AESEQ), mode = "first") + ), + derivation_slice( + filter = TRUE, + args = params(order = vars(AESTDY, AESEQ), mode = "last") + ) + ) +``` + +```{r, eval=TRUE, echo=FALSE} +ae %>% + arrange(USUBJID, AESTDY, AESEQ) %>% + dataset_vignette( + display_vars = vars(USUBJID, AEDECOD, AESTDY, AESEQ, AESEV, AHSEV3FL) + ) +``` + +The order is only important when the slices are not mutually exclusive, so +in the above case the moderate AE slice could have been above the severe AE +slice, for example, and there would have been no difference to the result. +However the third slice had to come last to check all remaining (i.e. not severe +or moderate) records only. diff --git a/vignettes/imputation.Rmd b/vignettes/imputation.Rmd index ed0396c223..13a3fabbec 100644 --- a/vignettes/imputation.Rmd +++ b/vignettes/imputation.Rmd @@ -12,6 +12,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction @@ -26,6 +28,12 @@ Relative Day) are numeric variables. They can be derived only if the date or datetime is complete. Therefore `{admiral}` provides imputation functions which fill in missing date or time parts according to certain imputation rules. +In {admiral} we use only two functions `derive_vars_dt()` and +`derive_vars_dtm()` for date and datetime imputations respectively. In all other +functions where dates can be passed as an argument, we expect full dates or +datetimes (unless otherwise specified), so if any possibility of partials then +these functions should be used as a first step to make the required imputation. + ## Required Packages The examples of this vignette require the following packages. @@ -39,12 +47,17 @@ library(dplyr) # Imputation Rules +In {admiral} we don't allow users to pick any single part of the date/time to +impute, we only enable to impute up to a highest level, i.e. you couldn't choose +to say impute months, but not days. + The simplest imputation rule is to set the missing parts to a fixed value. For example ```{r} -impute_dtc( +impute_dtc_dtm( "2019-10", + highest_imputation = "M", date_imputation = "01-01", time_imputation = "00:00:00" ) @@ -53,8 +66,9 @@ impute_dtc( Sometimes this does not work as it would result in invalid dates, e.g., ```{r} -impute_dtc( +impute_dtc_dtm( "2019-02", + highest_imputation = "M", date_imputation = "02-31", time_imputation = "00:00:00" ) @@ -64,8 +78,9 @@ Therefore the keywords `"first"` or `"last"` can be specified to request that missing parts are replaced by the first or last possible value: ```{r} -impute_dtc( +impute_dtc_dtm( "2019-02", + highest_imputation = "M", date_imputation = "last", time_imputation = "00:00:00" ) @@ -73,7 +88,7 @@ impute_dtc( For dates, there is the additional option to use keyword `"mid"` to impute missing day to `15` or missing day and month to `06-30`, but note the -different behaviour below depending on `preserve` parameter for case when month +different behaviour below depending on `preserve` argument for case when month only is missing: ```{r} @@ -82,14 +97,16 @@ dates <- c( "2019", "2019---01" ) -impute_dtc( +impute_dtc_dtm( dates, + highest_imputation = "M", date_imputation = "mid", time_imputation = "00:00:00", preserve = FALSE ) -impute_dtc( +impute_dtc_dtm( dates, + highest_imputation = "M", date_imputation = "mid", time_imputation = "00:00:00", preserve = TRUE @@ -106,23 +123,72 @@ dates <- c( "2019", "2019---01" ) -impute_dtc( +impute_dtc_dtm( dates, + highest_imputation = "M", date_imputation = "06-15", time_imputation = "00:00:00" ) ``` +## Imputation Level + +The imputation level, i.e., which components are imputed if they are missing, is +controlled by the `highest_imputation` argument. All components up to the +specified level are imputed. + +```{r} +dates <- c( + "2019-02-03T12:30:15", + "2019-02-03T12:30", + "2019-02-03", + "2019-02", + "2019" +) + +# Do not impute +impute_dtc_dtm( + dates, + highest_imputation = "n" +) + +# Impute seconds only +impute_dtc_dtm( + dates, + highest_imputation = "s" +) + +# Impute time (hours, minutes, seconds) only +impute_dtc_dtm( + dates, + highest_imputation = "h" +) + +# Impute days and time +impute_dtc_dtm( + dates, + highest_imputation = "D" +) + +# Impute date (months and days) and time +impute_dtc_dtm( + dates, + highest_imputation = "M" +) +``` + +For imputation of years (`highest_imputation = "Y"`) see next section. ## Minimum/Maximum Dates In some scenarios the imputed date should not be before or after certain dates. For example an imputed date after data cut off date or death date is not desirable. The `{admiral}` imputation functions provide the `min_dates` and -`max_dates` parameter to specify those dates. For example: +`max_dates` argument to specify those dates. For example: ```{r} -impute_dtc( +impute_dtc_dtm( "2019-02", + highest_imputation = "M", date_imputation = "last", time_imputation = "last", max_dates = list(ymd("2019-01-14"), ymd("2019-02-25")) @@ -136,6 +202,29 @@ i.e., for "2019-02" the possible dates range from "2019-02-01" to "2019-02-28". Thus "2019-01-14" is ignored. This ensures that the non-missing parts of the dtc date are not changed. +If the `min_dates` or `max_dates` argument is specified, it is also possible to +impute completely missing dates. For `date_imputation = "first"` the `min_dates` +argument must be specified and for `date_imputation = "last"` the `max_dates` +argument. For other imputation rules imputing the year is not possible. + +```{r} +# Impute year to first +impute_dtc_dtm( + c("2019-02", NA), + highest_imputation = "Y", + min_dates = list(ymd("2019-01-14"), ymd("2019-02-25")) +) + +# Impute year to last +impute_dtc_dtm( + c("2019-02", NA), + highest_imputation = "Y", + date_imputation = "last", + time_imputation = "last", + max_dates = list(ymd("2019-01-14"), ymd("2019-02-25")) +) +``` + # Imputation Flags ADaM requires that date or datetime variables for which imputation was used are @@ -146,7 +235,7 @@ is set to `"M"`. The `{admiral}` functions which derive imputed variables are al adding the corresponding imputation flag variables. Note: The `{admiral}` datetime imputation function provides the `ignore_seconds_flag` -parameter which can be set to `TRUE` in cases where seconds were never collected. +argument which can be set to `TRUE` in cases where seconds were never collected. This is due to the following from ADaM IG: For a given SDTM DTC variable, if only hours and minutes are ever collected, and seconds are imputed in `*DTM` as `00`, then it is not necessary to set `*TMF` to `"S"`. @@ -160,8 +249,10 @@ then it is not necessary to set `*TMF` to `"S"`. - `derive_vars_dtm()`: Adds a datetime variable, a date imputation flag variable, and a time imputation flag variable (both optional) based on a --DTC variable and imputation rules. -- `impute_dtc()`: Returns a complete ISO 8601 date or `NA` based on a partial -ISO 8601 date and imputation rules. +- `impute_dtc_dtm()`: Returns a complete ISO 8601 datetime or `NA` based on a +partial ISO 8601 datetime and imputation rules. +- `impute_dtc_dt()`: Returns a complete ISO 8601 date (without time) or `NA` +based on a partial ISO 8601 date(time) and imputation rules. - `convert_dtc_to_dt()`: Returns a date if the input ISO 8601 date is complete. Otherwise, `NA` is returned. - `convert_dtc_to_dtm()`: Returns a datetime if the input ISO 8601 date is complete @@ -192,6 +283,7 @@ ae <- tribble( derive_vars_dtm( dtc = AESTDTC, new_vars_prefix = "AST", + highest_imputation = "M", date_imputation = "first", time_imputation = "first" ) %>% @@ -217,6 +309,7 @@ ae <- tribble( derive_vars_dt( dtc = AESTDTC, new_vars_prefix = "AST", + highest_imputation = "M", date_imputation = "first" ) ``` @@ -226,8 +319,8 @@ dataset_vignette(ae) ## Impute Time without Imputing Date -If the time should be imputed but not the date, the `date_imputation` parameter -should be set to `NULL`. This results in `NA` if the date is partial. As +If the time should be imputed but not the date, the `highest_imputation` argument +should be set to `"h"`. This results in `NA` if the date is partial. As no date is imputed the date imputation flag is not created. ```{r} @@ -241,7 +334,7 @@ ae <- tribble( derive_vars_dtm( dtc = AESTDTC, new_vars_prefix = "AST", - date_imputation = NULL, + highest_imputation = "h", time_imputation = "first" ) ``` @@ -255,7 +348,7 @@ possible dates when filling the missing parts. The result may be a date before treatment start date. This is not desirable because the adverse event would not be considered as treatment emergent and excluded from the adverse event summaries. This can be avoided by specifying the treatment start date variable -(`TRTSDTM`) for the `min_dates` parameter. +(`TRTSDTM`) for the `min_dates` argument. Please note that `TRTSDTM` is used as imputed date only if the non missing date and time parts of `AESTDTC` coincide with those of `TRTSDTM`. Therefore @@ -273,6 +366,7 @@ ae <- tribble( derive_vars_dtm( dtc = AESTDTC, new_vars_prefix = "AST", + highest_imputation = "M", date_imputation = "first", time_imputation = "first", min_dates = vars(TRTSDTM) @@ -286,7 +380,7 @@ dataset_vignette(ae) If a date is imputed as the latest date of all possible dates when filling the missing parts, it should not result in dates after data cut off or death. This -can be achieved by specifying the dates for the `max_dates` parameter. +can be achieved by specifying the dates for the `max_dates` argument. Please note that non missing date parts are not changed. Thus `2019-12-04` is imputed as `2019-12-04 23:59:59` although it is after the data cut off date. It @@ -304,6 +398,7 @@ ae <- tribble( derive_vars_dtm( dtc = AEENDTC, new_vars_prefix = "AEN", + highest_imputation = "M", date_imputation = "last", time_imputation = "last", max_dates = vars(DTHDT, DCUTDT) @@ -328,7 +423,11 @@ mh <- tribble( "2019-06-21", ymd("2019-04-15") ) %>% filter( - convert_dtc_to_dt(MHSTDTC, date_imputation = "first") < TRTSDT + convert_dtc_to_dt( + MHSTDTC, + highest_imputation = "M", + date_imputation = "first" + ) < TRTSDT ) ``` ```{r, echo=FALSE} @@ -352,8 +451,7 @@ vs <- tribble( derivation = derive_vars_dtm, args = params( dtc = VSDTC, - new_vars_prefix = "A", - date_imputation = NULL + new_vars_prefix = "A" ), derivation_slice( filter = VSTPT == "PRE-DOSE", diff --git a/vignettes/lab_grading.Rmd b/vignettes/lab_grading.Rmd new file mode 100644 index 0000000000..9daf542dc5 --- /dev/null +++ b/vignettes/lab_grading.Rmd @@ -0,0 +1,427 @@ +--- +title: "Lab Grading" +output: + rmarkdown::html_vignette: + toc: false +vignette: > + %\VignetteIndexEntry{Lab Grading} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + + +```{r setup, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) + +library(admiraldev) +``` + + +# Introduction + +Within the ADLB ADaM data set there is a concept of lab grading, where there is +a set of criteria for particular lab tests that grade the severity or abnormality of +a lab value. The grades are from 0 to 4, where grade 0 can be viewed generally as a +β€œNORMAL” value. The higher the grade the more severe or more abnormal the lab value is. +There are several sets of lab grading criteria, for the initial implementation of lab +grading we will look at NCI-CTCAEv4. (In future releases `{admiral}` look to implement +further grading criteria, for example NCI-CTCAEv5) + +The NCI-CTCAE version 4 and 5 grading criteria can be found +here: https://ctep.cancer.gov/protocoldevelopment/electronic_applications/ctc.htm . + +The NCI-CTCAEv4 criteria can be found under the heading +[**Common Terminology Criteria for Adverse Events (CTCAE)v4.0**](https://ctep.cancer.gov/protocoldevelopment/electronic_applications/ctc.htm#ctc_40) + + +# Grading metadata + +`{admiral}` will store a metadata data set with required variables and optional variables, the +optional variables are purely for transparency, and will contain detailed information about +the grading criteria. The required variables are those used by `{admiral}` to create the grade. + +## Structure of metadata set + +The metadata data set has the following structure for the required variables: + +Variable | Scope | Type | Example Value +------- | -------- | ------ | -------- +**TERM** | Term describing the criteria applied to a particular lab test.| Character | "Anemia" +**DIRECTION** | The direction of the abnormality of a particular lab test value| Character | "L" or "H". +**SI_UNIT_CHECK** | Unit of lab test, to check against input data if criteria is based on absolute values. | Character | "mmol/L" +**VAR_CHECK** | Comma separated list of variables used in criteria, to check input data that variables exist. | Character | "AVAL, ANRLO" +**GRADE_CRITERIA_CODE** | Variable to hold code that creates grade based on defined criteria. | Character |R code that is a valid case statement within a `mutate` function call + +The metadata data set has the following structure for the optional variables: + +Variable | Scope | Type | Example Value +------- | -------- | ------ | -------- +**SOC** | System Organ Class the lab test belongs to.| Character | "Investigations" +**GRADE_1** | Grade 1 criteria for lab test, normally straight from source document.| Character | ">ULN - 3.0 x ULN". +**GRADE_2** | Grade 2 criteria for lab test, normally straight from source document.| Character | ">3.0 - 5.0 x ULN". +**GRADE_3** | Grade 3 criteria for lab test, normally straight from source document.| Character | ">5.0 - 20.0 x ULN". +**GRADE_4** | Grade 4 criteria for lab test, normally straight from source document.| Character | ">20.0 x ULN". +**DEFINITION** | Definition of abnormality, normally from source document.| Character | "A finding based on laboratory test results that indicate an increase in the level of alanine aminotransferase (ALT or SGPT) in the blood specimen.". +**COMMENT** | Description of any decisions made by `{admiral}` to implement grading criteria, where grading criteria alone was ambiguous. | Character | "Take worst case and assume on anticoagulation". + + +# Creating the lab grade + +## Mapping ADLB VAD to the TERM variable in the `{admiral}` metadata data set + + +```{r message=FALSE} +library(admiral) +library(admiral.test) +library(dplyr) +library(stringr) +library(tibble) + +data("admiral_lb") + +adsl <- admiral_adsl +lb <- admiral_lb + +lb <- convert_blanks_to_na(lb) +``` +```{r echo=FALSE} +lb <- filter(lb, USUBJID %in% c("01-701-1115", "01-705-1186", "01-705-1349", "01-708-1286", "01-707-1037", "01-716-1024")) +``` + +
+Each company needs to map their lab test to a term that describes the criteria being applied. +The list of terms defined in the `{admiral}` metadata to implement NCI-CTCAEv4 is below: +
+ +```{r, eval=TRUE, echo=FALSE} +atoxgr_criteria_ctcv4 %>% + filter(!is.na(TERM)) %>% + dataset_vignette( + display_vars = vars(TERM) + ) +``` +
+Using CDISC data these lab tests can be mapped to the correct terms, firstly create +`PARAMCD`, `PARAM`, `AVAL`, `ANRLO` and `ANRHI`, also some lab grading criteria require `BASE` +and `PCHG`, so these would also need to be created before running `derive_var_atoxgr_dir()` +function. +
+ +```{r, eval=TRUE} +# Look-up tables ---- + +# Assign PARAMCD, PARAM, and PARAMN +param_lookup <- tibble::tribble( + ~LBTESTCD, ~PARAMCD, ~PARAM, ~PARAMN, + "ALB", "ALB", "Albumin (g/L)", 1, + "ALP", "ALKPH", "Alkaline Phosphatase (U/L)", 2, + "ALT", "ALT", "Alanine Aminotransferase (U/L)", 3, + "ANISO", "ANISO", "Anisocytes", 4, + "AST", "AST", "Aspartate Aminotransferase (U/L)", 5, + "BASO", "BASO", "Basophils (10^9/L)", 6, + "BASOLE", "BASOLE", "Basophils/Leukocytes (FRACTION)", 7, + "BILI", "BILI", "Bilirubin (umol/L)", 8, + "BUN", "BUN", "Blood Urea Nitrogen (mmol/L)", 9, + "CA", "CA", "Calcium (mmol/L)", 10, + "CHOL", "CHOLES", "Cholesterol (mmol/L)", 11, + "CK", "CK", "Creatinine Kinase (U/L)", 12, + "CL", "CL", "Chloride (mmol/L)", 13, + "COLOR", "COLOR", "Color", 14, + "CREAT", "CREAT", "Creatinine (umol/L)", 15, + "EOS", "EOS", "Eosinophils (10^9/L)", 16, + "EOSLE", "EOSLE", "Eosinophils/Leukocytes (FRACTION)", 17, + "GGT", "GGT", "Gamma Glutamyl Transferase (U/L)", 18, + "GLUC", "GLUC", "Glucose (mmol/L)", 19, + "HBA1C", "HBA1C", "Hemoglobin A1C (1)", 20, + "HCT", "HCT", "Hematocrit (1)", 21, + "HGB", "HGB", "Hemoglobin (mmol/L)", 22, + "K", "POTAS", "Potassium (mmol/L)", 23, + "KETONES", "KETON", "Ketones", 24, + "LYM", "LYMPH", "Lymphocytes (10^9/L)", 25, + "LYMLE", "LYMPHLE", "Lymphocytes/Leukocytes (FRACTION)", 26, + "MACROCY", "MACROC", "Macrocytes", 27, + "MCH", "MCH", "Ery. Mean Corpuscular Hemoglobin (fmol(Fe))", 28, + "MCHC", "MCHC", "Ery. Mean Corpuscular HGB Concentration (mmol/L)", 29, + "MCV", "MCV", "Ery. Mean Corpuscular Volume (f/L)", 30, + "MICROCY", "MICROC", "Microcytes", 31, + "MONO", "MONO", "Monocytes (10^9/L)", 32, + "MONOLE", "MONOLE", "Monocytes/Leukocytes (FRACTION)", 33, + "PH", "PH", "pH", 34, + "PHOS", "PHOS", "Phosphate (mmol/L)", 35, + "PLAT", "PLAT", "Platelet (10^9/L)", 36, + "POIKILO", "POIKIL", "Poikilocytes", 37, + "POLYCHR", "POLYCH", "Polychromasia", 38, + "PROT", "PROT", "Protein (g/L)", 39, + "RBC", "RBC", "Erythrocytes (TI/L)", 40, + "SODIUM", "SODIUM", "Sodium (mmol/L)", 41, + "SPGRAV", "SPGRAV", "Specific Gravity", 42, + "TSH", "TSH", "Thyrotropin (mU/L)", 43, + "URATE", "URATE", "Urate (umol/L)", 44, + "UROBIL", "UROBIL", "Urobilinogen", 45, + "VITB12", "VITB12", "Vitamin B12 (pmol/L)", 46, + "WBC", "WBC", "Leukocytes (10^9/L)", 47 +) + +adlb <- lb %>% + ## Add PARAMCD PARAM and PARAMN - from LOOK-UP table + derive_vars_merged_lookup( + dataset_add = param_lookup, + new_vars = vars(PARAMCD, PARAM, PARAMN), + by_vars = vars(LBTESTCD) + ) %>% + ## Calculate PARCAT1 AVAL AVALC ANRLO ANRHI + ## Dummy the values for BASE + mutate( + PARCAT1 = LBCAT, + AVAL = LBSTRESN, + AVALC = LBSTRESC, + ANRLO = LBSTNRLO, + ANRHI = LBSTNRHI, + BASE = AVAL - 10 + ) +``` + +Another look-up table is used to add on `ATOXDSCL` and `ATOXDSCH` using +`PARAMCD`. `ATOXDSCL` holds the terms for grading low lab values, and `ATOXDSCH` holds +the terms for grading high lab values. The names of these variables can be user-defined. +`ATOXDSCL` and `ATOXDSCH` are the link from ADLB data to the `{admiral}` metadata that +holds the grading criteria. + +```{r, eval=TRUE} +# Assign ATOXDSCL and ATOXDSCH to hold lab grading terms +# ATOXDSCL and ATOXDSCH hold terms defined by NCI-CTCAEv4. +grade_lookup <- tibble::tribble( + ~PARAMCD, ~ATOXDSCL, ~ATOXDSCH, + "ALB", "Hypoalbuminemia", NA_character_, + "ALKPH", NA_character_, "Alkaline phosphatase increased", + "ALT", NA_character_, "Alanine aminotransferase increased", + "AST", NA_character_, "Aspartate aminotransferase increased", + "BILI", NA_character_, "Blood bilirubin increased", + "CA", "Hypocalcemia", "Hypercalcemia", + "CHOLES", NA_character_, "Cholesterol high", + "CK", NA_character_, "CPK increased", + "CREAT", NA_character_, "Creatinine increased", + "GGT", NA_character_, "GGT increased", + "GLUC", "Hypoglycemia", "Hyperglycemia", + "HGB", "Anemia", "Hemoglobin increased", + "POTAS", "Hypokalemia", "Hyperkalemia", + "LYMPH", "CD4 lymphocytes decreased", NA_character_, + "PHOS", "Hypophosphatemia", NA_character_, + "PLAT", "Platelet count decreased", NA_character_, + "SODIUM", "Hyponatremia", "Hypernatremia", + "WBC", "White blood cell decreased", "Leukocytosis", +) + +adlb <- adlb %>% + derive_vars_merged( + dataset_add = grade_lookup, + by_vars = vars(PARAMCD), + ) +``` + +It is now straightforward to create the grade, for low lab values the grade will +be held in `ATOXGRL` and for high lab values the grade will be held in `ATOXGRH` + +```{r, eval=TRUE} +adlb <- adlb %>% + derive_var_atoxgr_dir( + new_var = ATOXGRL, + tox_description_var = ATOXDSCL, + criteria_direction = "L", + get_unit_expr = extract_unit(PARAM) + ) %>% + derive_var_atoxgr_dir( + new_var = ATOXGRH, + tox_description_var = ATOXDSCH, + criteria_direction = "H", + get_unit_expr = extract_unit(PARAM) + ) +``` + +Note: `{admiral}` does not grade 'Anemia' or 'Hemoglobin Increased' because the metadata is +based on the SI unit of 'g/L', however the CDISC data has SI unit of 'mmol/L'. +Please see `SI_UNIT_CHECK` variable in `{admiral}` metadata `atoxgr_criteria_ctcv4`, the metadata +is in the data folder of `{admiral}`. +
+ +```{r, eval=TRUE, echo=FALSE} + +atoxgr_criteria_ctcv4 %>% + filter(!is.na(SI_UNIT_CHECK)) %>% + dataset_vignette( + display_vars = vars(TERM, SI_UNIT_CHECK), + ) +``` +
+ +`{admiral}` also gives the option to combine `ATOXGRL` and `ATOXGRH` into one variable, +namely `ATOXGR`. Grades held in `ATOXGRL` will be given a negative value in `ATOXGR` +to distinguish between low and high values. + +```{r, eval=TRUE} +adlb <- adlb %>% + derive_var_atoxgr() +``` +
+ +```{r, eval=TRUE, echo=FALSE} +adlb %>% + filter((ATOXGRL == "1") | (ATOXGRH == "1")) %>% + dataset_vignette( + display_vars = vars(ATOXDSCL, ATOXDSCH, ATOXGRL, ATOXGRH, ATOXGR) + ) +``` + + +# NCI-CTCAEV4 implementation + +## Terms graded + +Grading is implemented for those lab tests where a lab value is included in the grading definition, +`{admiral}` does NOT try to read any other data to determine the grade, and only the ADLB VAD is used. +The following CTCAE v4.0 SOC values were identified for grading, these are β€œInvestigations", +β€œMetabolism and nutrition disorders” and β€œBlood and lymphatic system disorders”. + +From these SOC values the following terms criteria is implemented in `{admiral}` + +From SOC = β€œInvestigations" there are 21 CTCAE v4.0 Terms: + + + Activated partial thromboplastin time prolonged + + Alanine aminotransferase increased + + Alkaline phosphatase increased + + Aspartate aminotransferase increased + + Blood bilirubin increased + + CD4 lymphocytes decreased + + Cholesterol high + + CPK increased + + Creatinine increased + + Fibrinogen decreased + + GGT increased + + Haptoglobin decreased + + Hemoglobin increased + + INR increased + + Lipase increased + + Lymphocyte count decreased + + Lymphocyte count increased + + Neutrophil count decreased + + Platelet count decreased + + Serum amylase increased + + White blood cell decreased + +From the SOC = β€œMetabolism and nutrition disorders” there are 14 CTCAE v4.0 Terms: + + + Hypercalcemia + + Hyperglycemia + + Hyperkalemia + + Hypermagnesemia + + Hypernatremia + + Hypertriglyceridemia + + Hyperuricemia + + Hypoalbuminemia + + Hypocalcemia + + Hypoglycemia + + Hypokalemia + + Hypomagnesemia + + Hyponatremia + + Hypophosphatemia + +From the SOC = β€œBlood and lymphatic system disorders” there are 2 CTCAE v4.0 Terms: + + + Anemia + + Leukocytosis + +## Updates made to TERM + +For terms "Hypocalcemia" and "Hypercalcemia" the criteria is provided for Calcium and Ionized Calcium, +therefore `{admiral}` created a row for each in the metadata, this is noted in the COMMENT variable of +the metadata: +
+ +```{r, eval=TRUE, echo=FALSE} +atoxgr_criteria_ctcv4 %>% + filter(str_detect(TERM, "calcemia")) %>% + dataset_vignette( + display_vars = vars(TERM, COMMENT) + ) +``` +
+ +Similarly, there is criteria applicable to Fasting Glucose as well as non-Fasting Glucose for "Hyperglycemia" +so again this was split into 2 rows, and noted in the COMMENT variable. Note "Hypoglycemia" does not require to +be split into 2 rows: + +
+```{r, eval=TRUE, echo=FALSE} +atoxgr_criteria_ctcv4 %>% + filter(str_detect(TERM, "glycemia")) %>% + dataset_vignette( + display_vars = vars(TERM, COMMENT) + ) +``` +
+ +## Assumptions made when grading + +For term "INR Increased" there is the following criteria: + +
+```{r, eval=TRUE, echo=FALSE} + +atoxgr_criteria_ctcv4 %>% + filter(str_detect(TERM, "INR")) %>% + dataset_vignette( + display_vars = vars(TERM, Grade_1) + ) +``` +
+ +`{admiral}` assumed worst case and used both parts of the criteria for grading, so comparing +lab value against ULN and also BASE. The decision made was put in the `COMMENT` field. + +
+```{r, eval=TRUE, echo=FALSE} +atoxgr_criteria_ctcv4 %>% + filter(str_detect(TERM, "INR")) %>% + dataset_vignette( + display_vars = vars(TERM, COMMENT) + ) +``` +
+ +For TERM "Hyperuricemia", the criteria for Grade 1 and Grade 3 is the same with respect +to the lab value, so worse case is assumed as grade 3. The decision made was +put in the `COMMENT` field. + +
+```{r, eval=TRUE, echo=FALSE} +atoxgr_criteria_ctcv4 %>% + filter(str_detect(TERM, "Hypouricemia")) %>% + dataset_vignette( + display_vars = vars(TERM, Grade_1, Grade_3, COMMENT) + ) +``` +
+ +A similar approach was taken for TERM "Hypokalemia" where Grade 1 and Grade 2 criteria +is the same with respect to the lab value, so worse case is assumed as grade 2. +The decision made was put in the `COMMENT` field. + +
+```{r, eval=TRUE, echo=FALSE} +atoxgr_criteria_ctcv4 %>% + filter(str_detect(TERM, "Hypokalemia")) %>% + dataset_vignette( + display_vars = vars(TERM, Grade_1, Grade_2, COMMENT) + ) +``` +
+ +# Conclusion + +In future releases `{admiral}` will implement further grading criteria, with NCI-CTCAE v5 +being the priority. Providing tools for users to easily interact with the metadata to +update criteria, based on their companies needs will also be looked at. Ideally, users should +be able to create their own metadata for company specific grading schemes. diff --git a/vignettes/occds.Rmd b/vignettes/occds.Rmd index 732ba88b4b..be502f3b55 100644 --- a/vignettes/occds.Rmd +++ b/vignettes/occds.Rmd @@ -13,6 +13,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction @@ -60,6 +62,10 @@ data("admiral_adsl") ae <- admiral_ae adsl <- admiral_adsl +``` +```{r echo = FALSE} +ae <- filter(ae, USUBJID %in% c("01-701-1015", "01-701-1023", "01-703-1086", "01-703-1096", "01-707-1037", "01-716-1024")) + ``` At this step, it may be useful to join `ADSL` to your `AE` domain as well. Only the @@ -101,13 +107,13 @@ adae <- adae %>% derive_vars_dtm( dtc = AESTDTC, new_vars_prefix = "AST", - date_imputation = "first", - time_imputation = "first", + highest_imputation = "M", min_dates = vars(TRTSDT) ) %>% derive_vars_dtm( dtc = AEENDTC, new_vars_prefix = "AEN", + highest_imputation = "M", date_imputation = "last", time_imputation = "last", max_dates = vars(DTHDT, EOSDT) @@ -228,12 +234,18 @@ Additionally, this function can also provide traceability variables ```{r eval=TRUE} data(ex_single) +ex_single <- derive_vars_dtm( + ex_single, + dtc = EXSTDTC, + new_vars_prefix = "EXST", + flag_imputation = "none" +) adae <- adae %>% derive_var_last_dose_date( ex_single, filter_ex = (EXDOSE > 0 | (EXDOSE == 0 & grepl("PLACEBO", EXTRT))) & - nchar(EXENDTC) >= 10, - dose_date = EXSTDTC, + !is.na(EXSTDTM), + dose_date = EXSTDTM, analysis_date = ASTDT, single_dose_condition = (EXSTDTC == EXENDTC), new_var = LDOSEDTM, @@ -353,12 +365,15 @@ maximum toxicity grade. ```{r, eval=FALSE} adae <- adae %>% - derive_var_extreme_flag( - by_vars = vars(USUBJID), - order = vars(desc(ATOXGR), ASTDTM, AESEQ), - new_var = AOCCIFL, - filter = TRTEMFL == "Y", - mode = "first" + restrict_derivation( + derivation = derive_var_extreme_flag, + args = params( + by_vars = vars(USUBJID), + order = vars(desc(ATOXGR), ASTDTM, AESEQ), + new_var = AOCCIFL, + mode = "first" + ), + filter = TRTEMFL == "Y" ) ``` @@ -371,12 +386,15 @@ adae <- adae %>% mutate( ASEVN = as.integer(factor(ASEV, levels = c("MILD", "MODERATE", "SEVERE", "DEATH THREATENING"))) ) %>% - derive_var_extreme_flag( - by_vars = vars(USUBJID), - order = vars(desc(ASEVN), ASTDTM, AESEQ), - new_var = AOCCIFL, - filter = TRTEMFL == "Y", - mode = "first" + restrict_derivation( + derivation = derive_var_extreme_flag, + args = params( + by_vars = vars(USUBJID), + order = vars(desc(ASEVN), ASTDTM, AESEQ), + new_var = AOCCIFL, + mode = "first" + ), + filter = TRTEMFL == "Y" ) ``` diff --git a/vignettes/pr_review_actions.png b/vignettes/pr_review_actions.png deleted file mode 100644 index 55dcf8bdcf..0000000000 Binary files a/vignettes/pr_review_actions.png and /dev/null differ diff --git a/vignettes/pr_review_checkbox.png b/vignettes/pr_review_checkbox.png deleted file mode 100644 index 6a147232c3..0000000000 Binary files a/vignettes/pr_review_checkbox.png and /dev/null differ diff --git a/vignettes/pr_review_checklist.png b/vignettes/pr_review_checklist.png deleted file mode 100644 index 64973f90b5..0000000000 Binary files a/vignettes/pr_review_checklist.png and /dev/null differ diff --git a/vignettes/pr_review_guidance.Rmd b/vignettes/pr_review_guidance.Rmd deleted file mode 100644 index 9a3f1b603d..0000000000 --- a/vignettes/pr_review_guidance.Rmd +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: "Pull Request Review Guidance" -output: rmarkdown::html_vignette -vignette: > - %\VignetteIndexEntry{Pull Request Review Guidance} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -# Introduction - -This document is intended to be guidance for creators and reviewers of pull requests (PRs) in the `{admiral}` package. PR authors will benefit from shorter review times by closely following the guidance provided here. - -A pull request into the `devel` branch signifies that an issue that has been "addressed". This issue might be a bug, a feature request or a documentation update. For transparency, we keep the issue open until the `devel` branch is merged into the `main` branch, which usually coincides with a release of `{admiral}` to CRAN. This ensures that repeat issues are not raised and if they are raised are quickly marked as duplicates and closed. - -Closely following the below guidance will ensure that our all our "addressed" issues auto-close once we merge `devel` into `main`. - -# Review Criteria - -For a pull request to be merged into `devel` it needs to pass the automated `R CMD check`, `lintr`, and `task-list-completed` workflows on GitHub at a minimum. The first two checks can be run locally using the `devtools::check()` and `lintr::lint_package()` commands and are recommended to be done before pushing to GitHub. The `task-list-completed` workflow is exclusive to GitHub and will be discussed later. In addition, the PR creator and reviewer should make sure that - -- the [Programming Strategy](programming_strategy.html) and [Development Process](development_process.html) are followed - -- the function is ADaM IG compliant - -- the function does what is intended for (as described in the header and corresponding issue) - -- the function header properly explains the intention of the function, the expected inputs (incl. permitted values of parameters) and the output produced; after reading the documentation the reader should be able to predict the output of the function without having to read the source code - -- the function has an accompanying set of unit tests; for derivations these unit test should have a code coverage of at least 90%; the whole package should have a coverage of >= 80% - -- the implemented derivation is in the scope of `{admiral}`, e.g. does not expect company specific input or hard-code company-specific rules - -- meaningful error or warning messages are issued if the input is invalid - -- documentation is created/updated by running `devtools::document()` - -- functions which are supposed to be exported are listed in the `NAMESPACE` file; this requires an `@export` tag in the function header - -- examples print relevant source variables and newly created variables and/or records in their output - -- the `NEWS.md` file is updated with an entry that explains the new features or changes - -- the author of a function is listed in the `DESCRIPTION` file - -- all files affected by the implemented changes, e.g. vignettes and templates, are updated - - -# So much Red Tape! - -The `{admiral}` development team is aware and sympathetic to the great many checks, processes and -documents needed to work through in order to do a compliant Pull Request. The `task-list-completed` GitHub -workflow was created to help reduce this burden on contributors by providing a standardized checklist that compiles information from the Pull Request Review Guidance, [Programming Strategy](programming_strategy.html) and [Development Process](development_process.html) vignettes. - -The next three sections give a high-level overview of what a contributor faces in opening a PR, and how a contributor interacts with the `task-list-completed` workflow in their PR. - -## Open a Pull Request - -When a contributor opens a PR a lengthy standard text will be inserted into the comment section. Please do not alter any of the automated text. You will need to manually add `Closes #` into the title of the Pull Request. You can use the Edit button in the top right if you forget this step at the start of your Pull Request. Besides that you are free to add in additional textual information, screenshots, etc. at the bottom of the automated text if needed to clarify or contribute to the discussion around your PR. - - -```{r echo=FALSE, out.width='120%'} -knitr::include_graphics("./pr_review_checklist.png") -``` - -## Create a Pull Request - -After you click the green `Create pull request` button the automated text that was inserted will be turned into a checklist in your Pull Request. Each check box has been drawn from the previously mentioned vignettes and presented in the recommended sequence. These check boxes are meant to be a helpful aid in ensuring that you have created a compliant Pull Request. - -```{r echo=FALSE, out.width='120%'} -knitr::include_graphics("./pr_review_checkbox.png") -``` - -## Complete all checks - -The check boxes are linked to the `task-list-completed` workflow. You need to check off each box in acknowledgment that you have done you due diligence in creating a compliant Pull Request. GitHub will refresh the Pull Request and trigger `task-list-completed` workflow that you have completed the task. The PR can not be merged into `devel` until the contributor has checked off each of the check box items. - -```{r echo=FALSE, out.width='120%'} -knitr::include_graphics("./pr_review_actions.png") -``` - -Please don't hesitate to reach out to the `{admiral}` team on [Slack](https://app.slack.com/client/T028PB489D3/C02M8KN8269) or through the [GitHub Issues](https://github.com/pharmaverse/admiral/issues) tracker if you think this checklist needs to be amended or further clarity is needed on a check box item. - - -# Common R CMD Check Issues - -`R CMD check` is a command line tool that checks R packages against a standard set of criteria. For a pull request to pass the check must not issue any notes, warnings or errors. Below is a list of common issues and how to resolve them. - -## Check Fails Only on One Version - -If the `R CMD check` workflow fails only on one or two R versions it can be helpful to reproduce the testing environment locally. - -To reproduce a particular R version environment open the `{admiral}` project in the corresponding R version, comment the line `source("renv/activate.R")` in the `.Rprofile` file, restart the R session and then run the following commands in the R console. - -```r -Sys.setenv(R_REMOTES_NO_ERRORS_FROM_WARNINGS = "true") - -if (!dir.exists(".library")) { - dir.create(".library") -} - -base_recommended_pkgs <- row.names(installed.packages(priority = "high")) -for (pkg in base_recommended_pkgs) { - path <- file.path(.Library, pkg) - cmd <- sprintf("cp -r %s .library", path) - system(cmd) -} -assign(".lib.loc", ".library", envir = environment(.libPaths)) - -r_version <- getRversion() -if (grepl("^3.6", r_version)) { - options(repos = "https://cran.microsoft.com/snapshot/2020-02-29") -} else if (grepl("^4.0", r_version)) { - options(repos = "https://cran.microsoft.com/snapshot/2021-03-31") -} else { - options(repos = "https://cran.rstudio.com") -} - -if (!requireNamespace("remotes", quietly = TRUE)) { - install.packages("remotes") -} -remotes::install_deps(dependencies = TRUE) -remotes::install_github("pharamaverse/admiral.test", ref = "devel") - -rcmdcheck::rcmdcheck() -``` - -This will ensure that the exact package versions we use in the workflow are installed into the hidden folder `.library`. That way your existing R packages are *not* overwritten. - -## Package Dependencies - -``` -> checking package dependencies ... ERROR - Namespace dependency not required: β€˜pkg’ -``` - -Add `pkg` to the `Imports` or `Suggests` field in the `DESCRIPTION` file. In general, dependencies should be listed in the `Imports` field. However, if a package is only used inside vignettes or unit tests it should be listed in `Suggests` because all `{admiral}` functions would work without these "soft" dependencies being installed. - -## Global Variables - -``` -❯ checking R code for possible problems ... NOTE - function_xyz: no visible binding for global variable β€˜some_var’ -``` - -Add `some_var` to the list of "global" variables in `R/globals.R`. - -## Undocumented Function Parameter - -``` -❯ checking Rd \usage sections ... WARNING - Undocumented arguments in documentation object 'function_xyz' - β€˜some_param’ -``` - -Add an `@param some_param` section in the header of `function_xyz()` and run `devtools::document()` afterwards. - -## Outdated Documentation - -``` -❯ checking for code/documentation mismatches ... WARNING - Codoc mismatches from documentation object 'function_xyz': - ... - Argument names in code not in docs: - new_param_name - Argument names in docs not in code: - old_param_name - Mismatches in argument names: - Position: 6 Code: new_param_name Docs: old_param_name -``` - -The name of a parameter has been changed in the function code but not yet in the header. Change `@param old_param_name` to `@param new_param_name` and run `devtools::document()`. diff --git a/vignettes/programming_strategy.Rmd b/vignettes/programming_strategy.Rmd deleted file mode 100644 index a8080b4c77..0000000000 --- a/vignettes/programming_strategy.Rmd +++ /dev/null @@ -1,618 +0,0 @@ ---- -title: "Programming Strategy" -output: - rmarkdown::html_vignette: - toc: true -vignette: > - %\VignetteIndexEntry{Programming Strategy} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -# Introduction - -As `{admiral}` is intended to be contributed by the user community, this -article is meant for developers that want to either expand `{admiral}` functionalities or build on top of `{admiral}`. -In order to keep the framework robust across the whole community, -we have defined a programming strategy that should be followed in such cases. -These contributions could include, for example, company specific derivations of ADaM datasets. - - -# Functional Workflow - -* Overall programming will follow a functional approach. -* We mandate the use of tidyverse (e.g. dplyr) over similar functionality existing in base R -* Each ADaM dataset is built with a set of functions and not with free flow code. -* Each ADaM dataset has a specific programming workflow. -* Each function has a specific purpose that supports the ADaM Dataset programming workflow. It could be an `{admiral}` function or a company specific function. -* Admiral functions can be re-used for company specific functions. -* Each function belongs to a category or multiple categories. -* Each function that is used to derive one or multiple variable(s) is required to be unit tested. -* Functions do have a naming convention. -* Double coding is not used as a QC method (only if absolutely necessary). -* ADaMs are created with readable, submission-ready code. - - -# Categorization of Functions - -The functions are categorized by keywords. The keywords can be specified in the -function header via the `@keywords` field. Each function must use at least one -of the following keywords. Please note that the keywords are handled -case-sensitive. Thus they must be in lower case. - -| Keyword | Description | -|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| -| `general_utility` | A general function which performs a general functionality, like copy variables, check missing, create parameters. | -| `metadata` | A function that provides Metadata functionality. | -| `derivation` | A function that derives one or more ADaM variable(s) | -| `assertion` | Throws an error if a certain condition is not met and `NULL` otherwise | -| `warning` | Returns a warning message based on invalid data input or if existing variables are overwritten. | -| `predicate` | Returns `TRUE` if the argument is a valid input, `FALSE` otherwise. | -| Across ADaM dataset structures keywords: `adam` | A function that is applicable across ADaM datasets. | -| ADaM dataset structure specific keywords: `adls`, `occds`, `bds`, `other` | A function specific to one of the ADaM dataset structures according to CDISC. Use the dataset structure as the keyword. | -| ADaM dataset specific keywords: `adex`, `adlb`, ... | A function specific to an ADaM dataset according to CDISC. Use the name of the corresponding ADaM dataset name as keyword | -| `timing` | Function is related to timing, e.g., imputing dates, converting dates, deriving duration,, ... | -| `computation` | Function which performs a computation that is used by other functions to derive an ADaM variable | -| Therapeutic Area specific functions: `oncology`, `infectious_diseases`, `neuroscience` | Functions that provide a particular algorithm for a specific therapeutic area. | -| Company specific keywords | | - - -# Functions in R - -## Function Design - -Firstly, it is important to explain how we decide on the need for new derivation functions. - -If a derivation rule or algorithm is common and highly similar across different variables/parameters -(e.g. study day or duration) then we would provide a generic function that can be used to satisfy all -the times this may be needed across different ADaMs. Similarly, if we feel that a certain derivation -could be useful beyond a single purpose we also would provide a generic function (e.g. instead of a -last known alive date function, we have an extreme date function where a user could find the last date -from a selection, or for example the first). - -Otherwise, if we feel that a derivation rule is a unique need or sufficiently complex to justify then we -opt for a dedicated function for that specific variable/parameter (e.g. treatment-emergent flag for AEs). - -If certain variables are closely connected (e.g. an imputed date and the corresponding imputation flag) -then a single function would provide both variables. - -If something needed for ADaM could be achieved simply via an existing tidyverse function, then we do not -wrap this into an admiral function, as that would add an unnecessary extra layer for users. - -The following principles are key when designing a new function: - -* _**Modularity**_ - All code follows a modular approach, i.e. the steps must be clearly separated and -have a dedicated purpose. This applies to scripts creating a dataset where each module should create a -single variable or parameter. But also to complex derivations with several steps. Commenting on these -steps is key for readability. - -* _**Avoid Copy and Paste**_ - If the same or very similar code is used multiple times, it should be put -into a separate function. This improves readability and maintainability and makes unit testing easier. -This should not be done for every simple programming step where tidyverse can be used. But rather for -computational functions or data checks. However, also consider not to nest too many functions. - -* _**Checks**_ - Whenever a function fails, a meaningful error message must be provided with a clear -reference to the input which caused the failure. A users should not have to dig into detailed -code if they only want to apply a function. A meaningful error message supports usability. - -* _**Flexibility**_ - Functions should be as flexible as possible as long as it does not reduce the usability. -For example: - - * The source variables or newly created variables and conditions for selecting observations should not be hard-coded. - - * It is useful if a parameter triggers optional steps, e.g. if the filter parameter is specified, the input dataset -is restricted, otherwise this step is skipped. - - * However, parameters should not trigger completely different algorithms. For example `BNRIND` could be derived based -on `BASE` or based on `ANRIND`. It should not be implemented within one function as the algorithms are completely different. -If `BASE` is used, the values are categorized while if `ANRIND` is used, the values are merged from the baseline observation. - -## Input, Output, and Side-effects - -* The behavior of the function is only determined by its input, not by any global object, -i.e. all input like datasets, variable names, options, … must be provided to the function by parameters. -* It is expected that the input datasets are not grouped. If any are grouped, the function must issue an error. -* If a function requires grouping, the function must provide the by_vars parameter. -* The output dataset must be ungrouped. -* The functions should not sort (arrange) the output dataset at the end. -* If the function needs to create temporary variables in an input dataset, these -variables must start with `temp_` and must be removed from the output dataset. -* If the input dataset includes variables starting with `temp_`, an error must be issued. -* The function must not have any side-effects like creating or modifying global objects, printing, writing files, ... - -## Function Names - -* Function names should start with a verb and use snake case, e.g. `derive_var_base()`. - -| Function name prefix | Description | -|----------------------------------------------|-----------------------------------------------------------------------------------------------------| -| `assert_` / `warn_` / `is_` | Functions that check other functions’ inputs | -| `derive_` | Functions that take a dataset as input and return a new dataset with additional rows and/or columns | -| `derive_var_` (e.g. `derive_var_trtdurd`) | Functions which add a single variable | -| `derive_vars_` (e.g. `derive_vars_dt`) | Functions which add multiple variables | -| `derive_param_` (e.g. `derive_param_os`) | Functions which add a single parameter | -| `compute_` / `calculate_` / ... | Functions that take vectors as input and return a vector | - -Please note that the appropriate *var*/*vars* prefix should be used for all cases in which the function creates any variable(s), regardless of the presence of a `new_var` parameter in the function call. - -## Function Parameters - -The default value of optional parameters should be `NULL`. - -There is a recommended parameter order that all contributors are asked to adhere to -(in order to keep consistency across functions): - -1. `dataset` (and any additional datasets denoted by `dataset_*`) -1. `by_vars` -1. `order` -1. `new_var` (and any related `new_var_*` parameters) -1. `filter` (and any additional filters denoted by `filter_*`) -1. all additional arguments: - * Make sure to always mention `start_date` before `end_date` (or related). - -Names of variables inside a dataset should be passed as symbols rather than strings, i.e. `AVAL` rather than `"AVAL"`. -If a parameter accepts one or more variables as input, the variables should be wrapped inside `vars()`. - -For example: - -* `new_var = TEMPBL` -* `by_vars = vars(PARAMCD, AVISIT)` -* `filter = PARAMCD == "TEMP"` -* `order = vars(AVISIT, desc(AESEV))` - -Parameter must not accept expressions for assigning the value of the new -variable. Instead separate parameters need to be provided for defining the -value. For example, if a function derives a variable which may be imputed, the -following is **not acceptable**. - -``` - ... - new_var = vars(mydtm = convert_dtc_to_dtm(impute_dtc(cmstdtc, - date_imputation = "last", - time_imputation = "last"))), - ... -``` - -Separate parameters for the imputation must be provided, e.g.: -``` - ... - new_var = mydtm, - source_var = cmstdtc, - date_imputation = "last", - time_imputation = "last", - ... -``` - -Each function parameter needs to be tested with `assert_` type of function. - -Each expression needs to be tested for the following -(there are many utility functions in `{admiral}` available to the contributor): - -* whether it is an expression (or a list of expressions, depending on the function) -* whether it is a valid expression (i.e. whether it evaluates without error) - -The only exception to this is `derive_var_basetype()` where we allowed the use of `rlang::exprs()`. -The reason is that in this case the user needs to have the flexibility to provide not just symbols but -usually more complicated filtering conditions (that may be based on multiple input parameters). - - -## Common Function Parameters Naming Convention - -The first parameter of `derive_` functions should be the input dataset and it should be named `dataset`. -If more than one input dataset is required, the other input dataset should start with `dataset_`, e.g., `dataset_ex.` - -Parameters for specifying items to add should start with `new_`. -If a variable is added, the second part of the parameter name should be var, if a parameter is added, it should be `param.` -For example: `new_var`, `new_var_unit`, `new_param`. - -Parameters which expect a boolean or boolean vector must start with a verb, e.g., `is_imputed` or `impute_date`. - - -## List of Common Parameters - -| Parameter Name | Description | -|------------------|--------------------------------------------------------------------------------------------------------------------| -| `dataset` | The input dataset. Expects a data.frame or a tibble. | -| `by_vars` | Variables to group by. | -| `order` | List of expressions for sorting a dataset, e.g., `vars(PARAMCD, AVISITN, desc(AVAL))`. | -| `new_var` | Name of a single variable to be added to the dataset. | -| `new_vars` | List of variables to be added to the dataset. | -| `new_var_unit` | Name of the unit variable to be added. It should be the unit of the variable specified for the new_var parameter. | -| `filter` | Expression to filter a dataset, e.g., `PARAMCD == "TEMP"`. | -| `start_date` | The start date of an event/interval. Expects a date object. | -| `end_date` | The end date of an event/interval. Expects a date object. | -| `start_dtc` | (Partial) start date/datetime in ISO 8601 format. | -| `dtc` | (Partial) date/datetime in ISO 8601 format. | -| `date` | Date of an event / interval. Expects a date object. | -| `set_values_to` | List of variable name-value pairs. | - - -## Source Code Formatting - -All source code should be formatted according to the [tidyverse](https://style.tidyverse.org/) style guide. -The [lintr](https://github.com/jimhester/lintr) package will be used to check and enforce this. - - -## Input Checking - -In line with the [fail-fast](https://en.wikipedia.org/wiki/Fail-fast) design principle, -function inputs should be checked for validity -and, if there’s an invalid input, the function should stop immediately with an error. -An exception is the case where a variable to be added by a function already exists in the input dataset: -here only a warning should be displayed and the function should continue executing. - -Inputs should be checked either using `asserthat::assert_that()` or custom assertion functions defined in [`R/assertions.R`](https://github.com/pharmaverse/admiral/blob/main/R/assertions.R). -These custom assertion functions should either return an error in case of an invalid input or return nothing. - -For the most common types of input parameters like a single variable, a list of -variables, a dataset, ... functions for checking are available (see -[assertions](../reference/index.html#section-assertions)). - -Parameters which expect keywords should handle them in a case-insensitive manner, e.g., both -`date_imputation = "FIRST"` and `date_imputation = "first"` should be accepted. -The `assert_character_scalar()` function helps with handling parameters in a -case-insensitive manner. - -A parameter should not be checked in an outer function if the parameter name is the same as in the inner function. -This rule is applicable only if both functions are part of `{admiral}`. - - -## Function Header (Documentation) - -Every function that is exported from the package must have an accompanying header -that should be formatted according to the [roxygen2](https://roxygen2.r-lib.org/) convention. - -In addition to the roxygen2 parameters, `@author` and `@keywords` are also used. - -Author is the owner of the function while the keywords are used to categorize the function. -Please see section "Categorization of functions". - -An example is given below: - -```{r, eval=F} -#' Derive Relative Day Variables -#' -#' Adds relative day variables (`--DY`) to the dataset, e.g., `ASTDY` and -#' `AENDY`. -#' -#' @param dataset Input dataset -#' -#' The columns specified by the `reference_date` and the `source_vars` -#' parameter are expected. -#' -#' @param reference_date The start date column, e.g., date of first treatment -#' -#' A date or date-time object column is expected. -#' -#' Refer to `derive_var_dt()` to impute and derive a date from a date -#' character vector to a date object. -#' -#' @param source_vars A list of datetime or date variables created using -#' `vars()` from which dates are to be extracted. This can either be a list of -#' date(time) variables or named `--DY` variables and corresponding --DT(M) -#' variables e.g. `vars(TRTSDTM, ASTDTM, AENDT)` or `vars(TRTSDT, ASTDTM, -#' AENDT, DEATHDY = DTHDT)`. If the source variable does not end in --DT(M), a -#' name for the resulting `--DY` variable must be provided. -#' -#' @author Teckla Akinyi -#' -#' @details The relative day is derived as number of days from the reference -#' date to the end date. If it is nonnegative, one is added. I.e., the -#' relative day of the reference date is 1. Unless a name is explicitly -#' specified, the name of the resulting relative day variable is generated -#' from the source variable name by replacing DT (or DTM as appropriate) with -#' DY. -#' -#' @return The input dataset with `--DY` corresponding to the `--DTM` or `--DT` -#' source variable(s) added -#' -#' @keywords derivation ADaM timing -#' -#' @export -#' -#' @examples -#' library(lubridate) -#' library(dplyr) -#' -#' datain <- tibble::tribble( -#' ~TRTSDTM, ~ASTDTM, ~AENDT, -#' "2014-01-17T23:59:59", "2014-01-18T13:09:O9", "2014-01-20" -#' ) %>% -#' mutate( -#' TRTSDTM = as_datetime(TRTSDTM), -#' ASTDTM = as_datetime(ASTDTM), -#' AENDT = ymd(AENDT) -#' ) -#' -#' derive_vars_dy( -#' datain, -#' reference_date = TRTSDTM, -#' source_vars = vars(TRTSDTM, ASTDTM, AENDT) -#' ) -``` - - -The following fields are mandatory: - -* `@param`: One entry per function parameter. -The following attributes should be described: expected data type (e.g. `data.frame`, `logical`, `numeric` etc.), default value (if any), permitted values (if applicable), optionality (i.e. is this a required parameter). -If the expected input is a dataset then the required variables should be clearly stated. -* `@details`: A natural-language description of the derivation used inside the function. -* `@author`: The person who wrote the function. In case a function is later on updated by another person the name should be appended to the list of authors. -* `@keyword`: One or more keywords applicable to the function. -* `@return`: A description of the return value of the function. -Any newly added variable(-s) should be mentioned here. -* `@examples`: A fully self-contained example of how to use the function. -Self-contained means that, if this code is executed in a new R session, it will run without errors. -That means any packages need to be loaded with `library()` and any datasets needed either to be created directly inside the example code or loaded using `data()`. -If a dataset is created in the example, it should be done so using the function `tibble::tribble()`. -Make sure to align columns as this ensures quick code readability. - -Copying descriptions should be avoided as it makes the documentation hard to -maintain. For example if the same parameter with the same description is used by -more than one function, the parameter should be described for one function and -the other functions should use `@inheritParams `. - -Please note that if `@inheritParams func_first` is used in the header of the -`func_second()` function, those parameter descriptions of `func_first()` are -included in the documentation of `func_second()` for which - -- the parameter is offered by `func_second()` and -- no `@param` tag for the parameter is included in the header of -`func_second()`. - -The order of the `@param` tags should be the same as in the function definition. -The `@inheritParams` tags should be after the `@param`. This does not affect the -order of the parameter description in the rendered documentation but makes it -easier to maintain the headers. - -Variable names, expressions, functions, and any other code must be enclosed -which backticks. This will render it as code. - -For functions which derive a specific CDISC variable, the title must state the -label of the variable without the variable name. The variable should be stated -in the description. - -# Missing values - -Missing values (`NA`s) need to be explicitly shown. - -Regarding character vectors converted from SAS files: SAS treats missing character values as blank. -Those are imported into R as empty strings (`""`) although in nature they are missing values (`NA`). -All empty strings that originate like this need to be converted to proper R missing values `NA`. - -# File Structuring - -Organizing functions into files is more of an art than a science. -Thus, there are no hard rules but just recommendations. -First and foremost, there are two extremes that should be avoided: -putting each function into its own file and putting all functions into a single file. -Apart from that the following recommendations should be taken into consideration when deciding upon file structuring: - -- If a function is very long (together with its documentation), store it in a separate file -- If some functions are documented together, put them into one file -- If some functions have some sort of commonality or relevance with one another (like `dplyr::bind_rows()` and `dplyr::bind_cols()`), put them into one file -- Store functions together with their helpers and methods -- Have no more than 1000 lines in a single file, unless necessary (exceptions are, for example, classes with methods) - -It is the responsibility of both the author of a new function and reviewer to ensure that these recommendations are put into practice. - - -# R Package Dependencies - -Package dependencies have to be documented in the `DESCRIPTION` file. -If a package is used only in examples and/or unit tests then it should be listed in `Suggests`, otherwise in `Imports`. - -Functions from other packages have to be explicitly imported by using the `@importFrom` tag in the `R/admiral-package.R` file. -To import the `if_else()` and `mutate()` function from `dplyr` the following line would have to be included in that file: -`#' @importFrom dplyr if_else mutate`. - - -# Metadata - -Functions should only perform the derivation logic and not add any kind of metadata, e.g. labels. - - -# Unit Testing - -A function requires a set of unit tests to verify it produces the expected result. -See [Writing Unit Tests in {admiral}](unit_test_guidance.html#writing-unit-tests-in-admiral-) for details. - -# Deprecation - -As `{admiral}` is still evolving, functions or parameters may need to be removed or replaced with more -efficient options from one release to another. In such cases, the relevant function or parameter -must be marked as deprecated. A warning will be issued until the next release and an error will be -generated thereafter. Information about deprecation timelines must be added to the warning/error message. - -## Documentation - -If a function or parameter is removed, the documentation must be updated to indicate the function -or the parameter is now deprecated and which new function/parameter should be used instead. - -The documentation will be updated at: - -+ the description level for a function, - - ```{r, eval=FALSE} -#' Title of the function -#' -#' @description -#' `r lifecycle::badge("deprecated")` -#' -#' This function is *deprecated*, please use `new_fun()` instead. -#' description of the function in plain English - ``` - - The "Examples" section should be removed. - -+ the `@param` level for a parameter. - - ``` - @param old_param *Deprecated*, please use `new_param` instead. - ``` - -## Handling of warning and error - -When a function or parameter is deprecated, the function must be updated to issue an error or a -warning to inform the user. - -There should be a test case added in `tests/testthat/test-deprecation.R` that checks whether this warning/error is issued as appropriate when using the deprecated function or parameter. - -### Function - -If the deprecated function still exists in the package besides the new function, a **warning** must be -issued. If it has been removed, an **error** must be generated. - -``` -### BEGIN DEPRECATION -# Warning if the deprecated function still exists -deprecate_warn("x.y.z", "fun_xxx()", "new_fun_xxx()") - -# Error if the deprecated function does not exist anymore -deprecate_stop("x.y.z", "fun_xxx()", "new_fun_xxx()") -### END DEPRECATION -``` - -For the former case above, please pass any input arguments into a call to the new function so that it still runs and pushes users towards adopting the new function. - -```{r, eval=F} -fun_xxx <- function(dataset, new_var) { - deprecate_warn("x.y.z", "fun_xxx()", "new_fun_xxx()") - new_fun_xxx(dataset, new_var = new_var) -} -``` - -### Parameter - -If a parameter is removed and is not replaced, an **error** must be generated: - -``` -### BEGIN DEPRECATION - if (!missing(old_param)) { - deprecate_stop("x.y.z", "fun_xxx(old_param = )", "fun_xxx(new_param = )") - } -### END DEPRECATION -``` - -If the parameter is renamed or replaced, a **warning** must be issued and the new parameter takes -the value of the old parameter until the next release. -Note: parameters which are not passed as `vars()` argument (e.g. `new_var = VAR1` or `filter = AVAL >10`) -will need to be quoted. - -``` -### BEGIN DEPRECATION - if (!missing(old_param)) { - deprecate_warn("x.y.z", "fun_xxx(old_param = )", "fun_xxx(new_param = )") - # old_param is given using vars() - new_param <- old_param - # old_param is NOT given using vars() - new_param <- enquo(old_param) - } -### END DEPRECATION -``` - -## Unit Testing - -Unit tests for deprecated functions and parameters must be added to -`tests/testthat/test-deprecation.R` to ensure that a warning or error is issued. - -Other unit tests of deprecated functions must be removed. - -# Best Practices and Hints - -Please take the following list as recommendation and try to adhere to its rules if possible. - -* Parameters in function calls should be named except for the first parameter -(e.g. `assert_data_frame(dataset, required_vars = vars(var1, var2), optional = TRUE)`). -* `dplyr::if_else()` should be used when there are only two conditions. -Try to always set the `missing` parameter whenever appropriate. - - -# Readable Code for ADaM - -Each function should be considered as readable code by default. - - -## Basic Rules - -All R code that produces ADaM datasets should be based on readable code for their 1st line code. -Producing Readable Code should not be part or the responsibility of any QC activities or 2nd line programming. - -ADaMs in R will be highly modularized. This means code needs to be commented across the set of functions that produces the final ADaM dataset. - -This guidance is built on the assumption that each ADaM dataset will have one R script that will call a set of functions needed to produce the corresponding ADaM dataset. - - -## Header for the main R-Script - -The main R-script would contain all function calls to create the ADaM dataset. In the header, describe the ADaM dataset that will be produced: - -* Name -* Label -* Input SDTMs and ADaMs -* Short description of its purpose if not obvious by the label (novel endpoints mainly) - - -### Header for functions - -* See Function header - - -### Functions - -* Function calls should have a preceding comment which is a short and meaningful description for which purpose the function is called, like: - * Derive variable X if function name is not descriptive or if it is a customized variable. - * Ideally use plain english to describe what a function is deriving. - * \# derive analysis study day - * \# derive age group <= 18 - * Impute date with missing days. - * Check for missing values. -* A comment can cover multiple function calls that belong to a category or group of variables. -Ideally one keeps it in line with the ADaM IG terminology, like Treatment Variables, Timing Variables, Identifier Variables as much as possible - * \# derive all population indicator variables RANDFL, SAFFL ... -* Functions that create user defined variables, specific to the molecule or study or support a specific endpoint should be called out specifically, like: -the following function calls flag the special Adverse Events or a comment that highlights a molecule specific endoint -* A function that covers a whole algorithm should have a preceding comment that indicates the purpose of the algorithm, like - * \# derive secondary endpoint XYZ - - -### Code - -The code itself should be described in meaningful, plain English, so that a reviewer can understand how the piece of code works, e.g. - -* \# calculates the sum of scores divided by the non missing numbers of scores to calculate the average score - -Code within a function that creates a specific variable should have a starting comment and an ending comment, e.g. - -```{r, eval=F} -# calculate X -# describe how the code works in meaningful plain english -"" -# end of X -``` - -If meaningful, comments can cover multiple variables within a piece of code - -```{r, eval=F} -# creates X, Y, Z -# describe how the code works in meaningful plain english -"" -# end of X, Y, Z -``` - -# R and package versions for development - -* The choice of R Version is not set in stone. However, a common development environment is important to establish when working across multiple companies and multiple developers. We currently work in R Version 3.6.3, but that will change as we move forward with `{admiral}`. This need for a common development environment also carries over for our choice of package versions. -* GitHub allows us through the Actions/Workflows to test `{admiral}` under several versions of R as well as several versions of dependent R packages needed for `{admiral}`. Currently we test `{admiral}` against 3.6.3 with a CRAN package snapshot from 2020-02-29, 4.0 with a CRAN package snapshot from 2021-03-31 and the latest R version with the latest snapshots of packages. You can view this workflow on our [GitHub Repository](https://github.com/pharmaverse/admiral/blob/main/.github/workflows/R-CMD-check.yml) -* This common development allows us to easily re-create bugs and provide solutions to each other issues that developers will encounter. -* Reviewers of Pull Requests when running code will know that their environment is identical to the initiator of the Pull Request. This ensures faster review times and higher quality Pull Request reviews. -* We achieve this common development environment by using a **lockfile** created from the [`renv`](https://rstudio.github.io/renv/) package. New developers will encounter a suggested `renv::restore()` in the console to revert or move forward your R version and package versions. diff --git a/vignettes/queries_dataset.Rmd b/vignettes/queries_dataset.Rmd index 37b63ed2b3..f1af94400a 100644 --- a/vignettes/queries_dataset.Rmd +++ b/vignettes/queries_dataset.Rmd @@ -14,6 +14,8 @@ knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) + +library(admiraldev) ``` # Introduction diff --git a/vignettes/unit_test_guidance.Rmd b/vignettes/unit_test_guidance.Rmd deleted file mode 100644 index 455e0d0058..0000000000 --- a/vignettes/unit_test_guidance.Rmd +++ /dev/null @@ -1,203 +0,0 @@ ---- -title: "Unit Test Guidance" -output: - rmarkdown::html_vignette: - toc: true -vignette: > - %\VignetteIndexEntry{Unit Test Guidance} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - - -# Why Write Unit Tests? - -## Unit Tests Become a Safety Net for Developers - -A comprehensive suite of unit tests can act as a safety net for developers. By -frequently running the tests, they can assure their recent modifications to the -code haven’t broken anything. In other words, unit tests help prevent regressions. - -## Unit Tests Can Contribute to Higher Code Quality - -Since unit tests act as a safety net, developers become more confident when -changing the code. They can refactor the code without fear of breaking things, -driving the general quality of the code base up. - -## Unit Tests Can Contribute to Better Application Architecture - -If you can add unit tests easily to a code base, that’s usually a good sign -regarding the quality of the app’s architecture. So, the drive to write testable -code can be an incentive for better architecture. - -## Detects Code Smells in your Codebase - -If ease of adding unit tests to a code base is a good sign, the opposite is also -true. Having a hard time creating unit tests for a given piece of code might be -a sign of code smells in the codeβ€”e.g. functions that are too complex. - -# Writing Good Unit Tests - -## Tests Should Be Fast - -If they’re slow, developers won’t run them as often as they should. That defeats -the whole purpose of having a suite of unit tests in the first place, which is -to boost the developers’ confidence to make changes to the code. The tests can’t -work as the safety net if they’re not run often. - -## Tests Should Be Simple - -There are several techniques we can apply to have a high degree of confidence in -the correctness of our tests. One of those is to keep your tests with low -cyclomatic complexity. Cyclomatic complexity is a code metric that indicates the -number of possible execution paths a given method can follow. A piece of code -with lower complexity is easier to understand and maintain, which means developers -are less likely to introduce bugs when working on it. We can measure the -cyclomatic complexity of your tests (using, for instance, a linter tool) and do -your best to keep it low. - -## Test Shouldn’t Duplicate Implementation Logic - -If the same person wrote both the test and the implementation, it’s possible they -made the same errors in both places. Since the tests mirror the implementation, -they might still pass, and the implementation could be wrong, but the tests might -fool you into thinking otherwise. Resist the urge to make your tests fancy, keep -them simple, and your testing suite will be better for it. - -## Tests Should Be Readable - -This best practice overlaps a little bit with the one about keeping your tests -simple. If tests are hard to read, developers are more likely to misunderstand -them and introduce bugs. Test cases could be used as a form of documentation, so -they obviously need to be readable. - -## Running Unit Tests Part of the Build Process - -Automate the whole process of running the unit tests and taking some action when -they fail. Your build process should execute your unit tests and mark the build -as broken when the tests fail. - -# Writing Unit Tests in {admiral} - -## Plan your Unit Tests - -Start by considering the derivation rule you are testing and the possible -arguments/flexibilities of your function code. Then plan which scenarios you will -test. These can either involve generating different input test cases or feeding -them into different calls of your function. - -## Test coverage - -Unit tests should cover the functionality of the function. -If another function `g()` is called within a function `f()`, the unit tests of `f()` should not test the functionality of `g()`. -This should be tested by the unit tests of `g()`, i.e. unit tests should be added at the lowest level. - -## Tests Should be Robust to Cover Realistic Data Scenarios - -For generating input test cases, it can be helpful to consider regular cases -(expected common data scenarios), boundary cases (where data points are close or -equal), and special cases (uncommon but valid data scenarios, e.g. missing or -special characters). Although you will never cover every single eventuality of -possible input data (no reliability testing method ever gives 100% certainty), -you do need to give confidence that the code is robust enough to work across most -data scenarios. - -## Testing Should Cover Possible Arguments - -For the different calls of your function, consider how the user might apply your -function and test a variety of possible calls, whilst still remembering the tips -above that tests should be fast and simple. -This is only needed in cases where the complexity and level of flexibility of -your function justifies it, e.g. see the test script: https://github.com/pharmaverse/admiral/blob/main/tests/testthat/test-derive_var_extreme_flag.R. - -## Exported Functions - -Don’t forget to add a unit test for each exported function. - -## Set up the Test Script - -Within {admiral} folder https://github.com/pharmaverse/admiral/tree/main/tests/testthat, -add a script with the naming convention β€œtest-\.R”., -the unit test script can be created from the console also, as follows: - -``` -usethis::use_test("") -``` -the testing framework used is testthat and has the following format : - -``` -test_that(" Test 1: ", { - input <- tibble::tribble( - ~inputvar1, ~inputvar2, ... - - ... - ) - - expected_output <- mutate(input, outputvar = c()) - - expect_dfs_equal((input), expected_output) - -}) -``` - -For example, if you are testing a function called my_new_func that is contained -in script all_funcs.R then from console use: - -``` -usethis::use_test("all_funcs") -``` - -Open the newly created file "test-all_funcs.R" and use the following format: - -``` -test_that("my_new_func Test 1: ", { - input <- tibble::tribble( - ~inputvar1, ~inputvar2, ... - - ... - ) - - expected_output <- mutate(input, outputvar = c()) - - expect_dfs_equal((input), expected_output) -}) -``` -**Note**: When comparing datasets in {admiral} we use function `expect_dfs_equal()`. - -The input and expected output for the unit tests must follow the following rules: - -* Input and output should be as simple as possible. -* Values should be hard-coded whenever possible. -* If values need to be derived, only unit tested functions can be used. - -If a dataset needs to be created for testing purpose, it should be done so using the function `tibble::tribble()`. -Make sure to align columns as well. This ensures quick code readability. - -Ensure you give a meaningful explanation of the test in the testthat call, as -these will be compiled in the package validation report. Having the name of the -function and test ID included in title will also help with traceability. - -Once you have tested your unit test program, you can run all unit tests from -the console, as follows. - -``` -devtools::test() -``` - -## Automation of Unit Tests - -When a user actions a pull request in {admiral} GitHub repo, the unit tests are -automatically run and pull request will be denied if any unit tests fail. - -## Flow Chart - -```{r echo=FALSE, out.width='120%'} -knitr::include_graphics("./unit_test_guidance.png") -``` diff --git a/vignettes/unit_test_guidance.png b/vignettes/unit_test_guidance.png deleted file mode 100755 index 0e0eb8f408..0000000000 Binary files a/vignettes/unit_test_guidance.png and /dev/null differ diff --git a/vignettes/writing_vignettes.Rmd b/vignettes/writing_vignettes.Rmd deleted file mode 100644 index 0574b49246..0000000000 --- a/vignettes/writing_vignettes.Rmd +++ /dev/null @@ -1,234 +0,0 @@ ---- -title: "Writing Vignettes" -output: rmarkdown::html_vignette -vignette: > - %\VignetteIndexEntry{Writing Vignettes} - %\VignetteEngine{knitr::rmarkdown} ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -# Introduction - -This guidance ensures consistency across the vignettes in the `{admiral}` package in terms of content, structure and code execution. -As a general rule, the vignette workflow defined in [r-pkgs.org](https://r-pkgs.org/vignettes.html) is followed. - - -# Metadata - -Each vignette in `{admiral}` should start with the following metadata. - -``` ---- -title: "" -output: rmarkdown::html_vignette: -vignette: > - %\VignetteIndexEntry{<Title>} - %\VignetteEngine{knitr::rmarkdown} ---- -``` - -The `<Title>` of the vignette should be meaningful. - -# Markdown - -## Default Options - -If any chunks are used within the vignette, the following options should be set after the metadata -to ensure that the chunks are rendered consistently across all vignettes. - - `r ''````{r setup, include=FALSE} - knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" - ) - ``` - - -## Format Sections - -### Table of Contents - -Headings must be title case and start from Heading 1: - -``` -# Heading 1 -This is the description of my first section. - -## Heading 1.1 -This is the description of my first sub-section within my first section. - -## Heading 1.2 -This is the description of my second sub-section within my first section. - -``` - -The first section gives a brief introduction of the vignette. The last sub-section -of the introduction should describe the packages required to run the `{admiral}` functions. -The `{admiral}` package should be described first. - -``` -# Introduction - -This is the introduction of my vignette. - -## Required Packages -``` - - `r ''````{r, warning=FALSE, message=FALSE} - library(admiral) - # <all packages used in the vignette> - ``` - -The `warning=FALSE` and `message=FALSE` options are there to prevent the usual messages: - -<span style="color: red;font-family: textmate, monospace; font-size = 10pt;" >Attaching package: 'xxxx'</br>The following objects are masked from 'package:yyyyy'</br> fun1, fun2</span> - -### Conventions - -#### General Conventions - -+ Any new vignette must be added to the `_pkgdown.yml` file in the relevant section. - -+ Any variable name, dataset name, function, argument name must be quoted with backticks: e.g. - - -``` - The `date` parameter of the `derive_my_var()` function expects a date variable, e.g., `ADT`. -``` - -+ Functions must also end with `()`. - -+ Variables and datasets name are expected to be uppercase. - -+ All the codes must be described, executed and the output result displayed once the code is executed. -Use: - - - `r ''````{r} - #<code> - ``` - -+ Any output created must clearly show what the function has derived. -It should at least show the variables/records used as input to the function and the derived -variables/records. If a dataset must be displayed, it will be formatted using the `dataset_vignette()` -function so that it is displayed consistently across the vignettes.E.g. - - - + Description and execution of the code used to derive the variable/record - - - -```{r, include=FALSE} -library(lubridate) -library(dplyr) -library(admiral.test) -library(DT) -library(admiral) - -data(admiral_vs) -``` - -```{r, eval=FALSE, echo=TRUE} -vs1 <- admiral_vs %>% - derive_vars_dt( - new_vars_prefix = "A", - dtc = VSDTC, - date_imputation = "FIRST" - ) -``` - - - + Output dataset formatted using `dataset_vignette()`... - - -```{r, echo=FALSE} -vs1 <- admiral_vs %>% - derive_vars_dt( - new_vars_prefix = "A", - dtc = VSDTC, - date_imputation = "FIRST" - ) - -dataset_vignette( - vs1, - display_vars = vars(USUBJID, VSTESTCD, VISIT, VSDTC, ADT), - filter = VSTESTCD == "WEIGHT" -) -``` - - -Note: The call to get the formatted dataset would be: - - - ```{r, eval=FALSE, echo=TRUE} -dataset_vignette( - vs1, - display_vars = vars(USUBJID, VSTESTCD, VISIT, VSDTC, ADT), - filter = VSTESTCD == "WEIGHT" -) - ``` - -Displaying many big datasets on a vignette, may require a long time to load -the page and may cause messages from the browser that the page is not -responsive. In this case the number of displayed observations should be -restricted either by restricting the source datasets at the beginning of the -vignette or in the call of `dataset_vignette()` if only some calls are -affected. - -#### Conventions for ADaM Workflow - -For vignettes describing an ADaM workflow, - -+ the second section will summarize the programming workflow. The first sub-section within this -workflow will always describe the data read to demonstrate the use of the `{admiral}` functions, - -+ Each sub-section within the programming workflow should be tagged (e.g. [Step1] (#step)), so that -the user can go on the relevant section from the programming workflow (in addition to the Table of -contents). Don’t use a tag with a number but use a meaningful name (e.g. do not use `(#link1)`, -but use `(#this_action)`) - -+ the last section should link to a template script. - - -``` -# Programming Workflow - -* [Read in Data](#readdata) -* [Derive/Impute End and Start Analysis Date/time and Relative Day](#datetime) -* ... -* [Assign `ASEQ`](#aseq) - -## Read in Data {#readdata} -## Derive/Impute End and Start Analysis Date/time and Relative Day {#datetime} -## ... -## Assign `ASEQ` {#aseq} - -# Another Section - -# Example Script -ADaM | Sample Code ----- | -------------- -ADxx | [ad_adxx.R](https://github.com/pharmaverse/admiral/blob/main/inst/templates/ad_adxx.R){target="_blank"} - -``` - -+ `ADSL` variables - - All `ADSL` variables which are required for any derivation are merged to the SDTM dataset before the - first derivation. - These `ADSL` variables have to be added to the by-variables for derivations which add observations. - This ensures that the `ADSL` variables are populated `for` the new observations. - A `adsl_vars` variable should be created at the beginning of the script and added to the `by_vars` - parameter for derivations which add observations. - - `ADSL` variables which should be in the final dataset but are not required for any derivation are - merged to the dataset after the last derivation. - - -