├── .Rbuildignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── release.yaml ├── pull_request_template.md └── workflows │ ├── check.yaml │ ├── cla.yaml │ ├── docs.yaml │ ├── release.yaml │ └── scheduled.yaml ├── .gitignore ├── .lintr ├── .pre-commit-config.yaml ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── assertions.R ├── corPFSOS.R ├── empSignificant.R ├── estimateParams.R ├── eventTracking.R ├── getClinicalTrials.R ├── getSimulatedData.R ├── getWaitTimeSum.R ├── hazardFunctions.R ├── package.R ├── piecewiseDistribution.R ├── piecewiseHazards.R ├── survivalFunctions.R └── transitionParameters.R ├── README.md ├── README.rmd ├── _pkgdown.yaml ├── design ├── create_hex_logo.R ├── design_getSimulatedData.Rmd ├── design_simulatedTrial.Rmd └── design_test-getSimulatedData.Rmd ├── inst └── WORDLIST ├── man ├── ExpHazOS.Rd ├── ExpQuantOS.Rd ├── ExpSurvOS.Rd ├── ExpSurvPFS.Rd ├── PCWInversionMethod.Rd ├── PFSOSInteg.Rd ├── PWCsurvOS.Rd ├── PWCsurvPFS.Rd ├── PwcOSInt.Rd ├── WeibOSInteg.Rd ├── WeibSurvOS.Rd ├── WeibSurvPFS.Rd ├── addStaggeredEntry.Rd ├── assert_intervals.Rd ├── assert_positive_number.Rd ├── avgHRExpOS.Rd ├── avgHRIntegExpOS.Rd ├── censoringByNumberEvents.Rd ├── corPFSOS.Rd ├── corTrans.Rd ├── empSignificant.Rd ├── estimateParams.Rd ├── exponential_transition.Rd ├── expvalOSInteg.Rd ├── expvalPFSInteg.Rd ├── figures │ └── logo.svg ├── getCensoredData.Rd ├── getClinicalTrials.Rd ├── getDatasetWideFormat.Rd ├── getEventsAll.Rd ├── getInit.Rd ├── getNumberEvents.Rd ├── getOneClinicalTrial.Rd ├── getOneToTwoRows.Rd ├── getPCWDistr.Rd ├── getPWCHazard.Rd ├── getResults.Rd ├── getSimulatedData.Rd ├── getSumPCW.Rd ├── getTarget.Rd ├── getTimePoint.Rd ├── getWaitTimeSum.Rd ├── haz.Rd ├── integrateVector.Rd ├── logRankTest.Rd ├── log_p11.Rd ├── negLogLik.Rd ├── p11Integ.Rd ├── passedLogRank.Rd ├── piecewise_exponential.Rd ├── prepareData.Rd ├── pwA.Rd ├── runTrial.Rd ├── simIDM-package.Rd ├── singleExpQuantOS.Rd ├── survOS.Rd ├── survPFS.Rd ├── survPFSOS.Rd ├── survTrans.Rd ├── trackEventsPerTrial.Rd └── weibull_transition.Rd ├── revdep └── .gitignore ├── simIDM.Rproj ├── tests ├── testthat.R └── testthat │ ├── _snaps │ └── estimateParams.md │ ├── test-addStaggeredEntry.R │ ├── test-assertions.R │ ├── test-corPFSOS.R │ ├── test-empSignificant.R │ ├── test-estimateParams.R │ ├── test-eventTracking.R │ ├── test-getClinicalTrials.R │ ├── test-getDatsetWideFormat.R │ ├── test-getSimulatedData.R │ ├── test-getSimulatedDataDistib.R │ ├── test-hazardFunctions.R │ ├── test-piecewiseDistrb.R │ ├── test-piecewiseHazards.R │ ├── test-survivalFunctions.R │ ├── test-transitionParameters.R │ └── test-waitTimeSum.R └── vignettes ├── MSM.png ├── correlation.Rmd ├── pwc_survival.Rmd ├── quickstart.Rmd ├── references.bib ├── scenario.png └── trialplanning.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^_pkgdown\.yml$ 4 | ^vignettes/hello\.Rmd$ 5 | ^docs$ 6 | ^\.github$ 7 | README.* 8 | ^\.lintr$ 9 | ^staged_dependencies\.yaml$ 10 | coverage.* 11 | ^\.pre-commit-config\.yaml$ 12 | ^codemeta\.json$ 13 | _pkgdown.yaml 14 | ^design$ 15 | ^pkgdown$ 16 | ^.revdeprefs\.yaml$ 17 | ^revdep$ 18 | ^\.covrignore$ 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @danielinteractive @niessl @numbersman77 2 | .github/workflows/* @insightsengineering/idr 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 19 | 20 | 21 | **Summary** 22 | 30 | 31 | Your brief description of the problem 32 | 33 | ```r 34 | 35 | # your reproducible example here 36 | 37 | ``` 38 | 39 | **R session info** 40 | 43 | 44 | ```sh 45 | 46 | # R -e "utils::sessionInfo()" output goes here 47 | 48 | ``` 49 | 50 | **OS / Environment** 51 | 54 | 55 | - OS: [e.g. Windows 10, Ubuntu 20.04, Centos 8] 56 | - Docker Image [e.g. rocker/verse:4.1.0] 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Summary** 11 | 30 | 31 | As a [persona], I [want to], [so that]. 32 | 33 | 34 | **Additional Information** 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Release 3 | description: Template for package release 4 | title: "[Release]: " 5 | labels: ["release"] 6 | assignees: 7 | - KlaudiaBB 8 | - cicdguy 9 | body: 10 | - type: markdown 11 | attributes: 12 | value: | 13 | ⚠️ Please do not link or mention any internal references in this issue. This includes internal URLs, intellectual property and references. 14 | - type: textarea 15 | id: blocked-by 16 | attributes: 17 | label: Blocked by 18 | description: Any PRs or issues that this release is blocked by. 19 | placeholder: Add a list of blocking PRs or issues here. 20 | value: | 21 | ### PRs 22 | 23 | - [ ] PR 1 24 | 25 | ### Issues 26 | 27 | - [ ] Issue 1 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: pre-release 32 | attributes: 33 | label: Pre-release 34 | description: Pre-requisites that must be fulfilled before initiating the release process. 35 | placeholder: Add your list of pre-requisites here. 36 | value: | 37 | - [ ] Make sure that high priority bugs (label "priority" + "bug") have been resolved before going into the release. 38 | - [ ] Review old/hanging PRs before going into the release. 39 | - [ ] Revisit R-package's lifecycle badges (Optional). 40 | - [ ] Release Manager: Discuss package dependencies, create a plan to sequentially close release activities and submit groups of packages for internal validation (Applicable only for regulatory release). 41 | - [ ] Check Validation Pipeline dry-run results for the package. 42 | - [ ] Make sure all relevant integration tests are green 2-3 days before the release. Look carefully through logs (check for warnings and notes). 43 | - [ ] Inform about the soft code freeze, decide what gets merged in before starting release activities. 44 | - type: textarea 45 | id: release 46 | attributes: 47 | label: Release 48 | description: The steps to be taken in order to create a release. 49 | placeholder: Steps to create a release. 50 | value: | 51 | ### Prepare the release 52 | 53 | - [ ] Create a new release candidate branch 54 | `git checkout -b release-candidate-vX.Y.Z` 55 | - [ ] Update NEWS.md file: make sure it reflects a holistic summary of what has changed in the package, check README. 56 | - [ ] Remove the additional fields (`Remotes`) from the DESCRIPTION file where applicable. 57 | - [ ] Make sure that the minimum dependency versions are updated in the DESCRIPTION file for the package. 58 | - [ ] Increase versioned dependency on {package name} to >=X.Y.Z. 59 | - [ ] Commit your changes and create the PR on GitHub (add "[skip vbump]" in the PR title). Add all updates, commit, and push changes: 60 | `# Make the necessary modifications to your files 61 | # Stage the changes 62 | git add 63 | # Commit the changes 64 | git commit -m "[skip vbump] " 65 | git push origin release-candidate-vX.Y.Z` 66 | 67 | ### Test the release 68 | 69 | - [ ] Execute the manual tests on Shiny apps that are deployed on various hosting providers (Posit connect and shinyapps.io) - track the results in GitHub issue (Applicable only for frameworks that use Shiny). 70 | - [ ] Monitor integration tests, if integration fails, create priority issues on the board. 71 | - [ ] Execute UAT tests (Optional). 72 | 73 | ### Validation loop 74 | 75 | Note: This section is applicable only for regulatory packages. 76 | 77 | - [ ] Tag the update(s) as a release candidate vX.Y.Z-rc (e.g. v0.5.3-rc1) on the release candidate branch (release-candidate-vX.Y.Z). 78 | `# Create rc tag for submission for internal validation 79 | git tag vX.Y.Z-rc 80 | git push origin vX.Y.Z-rc` 81 | - [ ] Submit the package for internal validation. 82 | - [ ] Address any feedback (internal validation/user testing), retag the package as a release candidate vX.Y.Z-rc(n+1). Repeat the submission for internal validation if necessary. 83 | - [ ] Get the package validated. 84 | 85 | ### Tag the release 86 | 87 | - [ ] If the additional fields were removed, add them back in a separate PR, and then merge the PR back to main (add "[skip vbump]" in the PR title). If nothing was removed just merge the PR you created in the "Prepare the release" section to `main`. Note the commit hash of the merged commit. **Note:** additional commits might be added to the `main` branch by a bot or an automation - we do **NOT** want to tag this commit. 88 | 89 | #### Make sure of the following before continuing with the release: 90 | 91 | - [ ] CI checks are passing in GH. 92 | - [ ] Shiny apps are deployable and there are no errors/warnings (Applicable only for frameworks that use Shiny). 93 | 94 | - [ ] Create a git tag with the final version set to vX.Y.Z on the main branch. In order to do this: 95 | 1. Checkout the commit hash. 96 | `git checkout ` 97 | 2. Tag the hash with the release version (vX.Y.Z). 98 | `git tag vX.Y.Z` 99 | 3. Push the tag to make the final release. 100 | `git push origin vX.Y.Z` 101 | - [ ] Update downstream package dependencies to (>=X.Y.Z) in {package name}. 102 | Note: Once the release tag is created, the package is automatically published to internal repositories. 103 | - type: textarea 104 | id: post-release 105 | attributes: 106 | label: Post-release 107 | description: The list of activities to be completed after the release. 108 | placeholder: The steps that must be taken after the release. 109 | value: | 110 | - [ ] Make sure that the package is published to internal repositories (Validated and/or Non-Validated repository). 111 | - [ ] Review and update installation instructions for the package if needed. 112 | - [ ] Make sure internal documentation/documentation catalogs are up to date. 113 | - [ ] Notify the IDR team to start post-release/clean-up activities. 114 | - [ ] Announce the release on ________. 115 | - type: textarea 116 | id: decision-tree 117 | attributes: 118 | label: Decision tree 119 | description: Any decision tree(s) that would aid release management 120 | placeholder: Any decision tree(s) that would aid release management. 121 | value: | 122 | Click [here](https://github.com/insightsengineering/.github/blob/main/.github/ISSUE_TEMPLATE/RELEASE_DECISION_TREE.md) to see the release decision tree. 123 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request 2 | 3 | 4 | 5 | Fixes #nnn 6 | -------------------------------------------------------------------------------- /.github/workflows/check.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Check 🛠 3 | 4 | on: 5 | pull_request: 6 | types: 7 | - opened 8 | - synchronize 9 | - ready_for_review 10 | branches: 11 | - main 12 | push: 13 | branches: 14 | - main 15 | workflow_dispatch: 16 | 17 | jobs: 18 | audit: 19 | name: Audit Dependencies 🕵️‍♂️ 20 | uses: insightsengineering/r.pkg.template/.github/workflows/audit.yaml@main 21 | r-cmd: 22 | name: R CMD Check 🧬 23 | uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main 24 | secrets: 25 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 26 | with: 27 | additional-r-cmd-check-params: --as-cran 28 | coverage: 29 | name: Coverage 📔 30 | uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main 31 | secrets: 32 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 33 | with: 34 | additional-env-vars: | 35 | NOT_CRAN=true 36 | linter: 37 | if: github.event_name == 'pull_request' 38 | name: SuperLinter 🦸‍♀️ 39 | uses: insightsengineering/r.pkg.template/.github/workflows/linter.yaml@main 40 | roxygen: 41 | name: Roxygen 🅾 42 | uses: insightsengineering/r.pkg.template/.github/workflows/roxygen.yaml@main 43 | secrets: 44 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 45 | with: 46 | auto-update: true 47 | gitleaks: 48 | name: gitleaks 💧 49 | uses: insightsengineering/r.pkg.template/.github/workflows/gitleaks.yaml@main 50 | spelling: 51 | if: github.event_name == 'pull_request' 52 | name: Spell Check 🆎 53 | uses: insightsengineering/r.pkg.template/.github/workflows/spelling.yaml@main 54 | links: 55 | if: github.event_name == 'pull_request' 56 | name: Check URLs 🌐 57 | uses: insightsengineering/r.pkg.template/.github/workflows/links.yaml@main 58 | vbump: 59 | name: Version Bump 🤜🤛 60 | if: github.event_name == 'push' 61 | uses: insightsengineering/r.pkg.template/.github/workflows/version-bump.yaml@main 62 | secrets: 63 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 64 | version: 65 | name: Version Check 🏁 66 | uses: insightsengineering/r.pkg.template/.github/workflows/version.yaml@main 67 | licenses: 68 | name: License Check 🃏 69 | uses: insightsengineering/r.pkg.template/.github/workflows/licenses.yaml@main 70 | style: 71 | if: github.event_name == 'pull_request' 72 | name: Style Check 👗 73 | uses: insightsengineering/r.pkg.template/.github/workflows/style.yaml@main 74 | with: 75 | auto-update: true 76 | secrets: 77 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 78 | -------------------------------------------------------------------------------- /.github/workflows/cla.yaml: -------------------------------------------------------------------------------- 1 | name: CLA 🔏 2 | 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | # For PRs that originate from forks 8 | pull_request_target: 9 | types: 10 | - opened 11 | - closed 12 | - synchronize 13 | 14 | jobs: 15 | CLA: 16 | name: CLA 📝 17 | uses: insightsengineering/.github/.github/workflows/cla.yaml@main 18 | secrets: inherit 19 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Docs 📚 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: 10 | - main 11 | workflow_dispatch: 12 | 13 | jobs: 14 | docs: 15 | name: Pkgdown Docs 📚 16 | uses: insightsengineering/r.pkg.template/.github/workflows/pkgdown.yaml@main 17 | with: 18 | refs-order: c("latest-tag", "main") 19 | default-landing-page: main 20 | secrets: 21 | REPO_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 🎈 3 | 4 | on: 5 | push: 6 | tags: 7 | - "v*" 8 | workflow_dispatch: 9 | 10 | jobs: 11 | release: 12 | name: Create release 🎉 13 | uses: insightsengineering/r.pkg.template/.github/workflows/release.yaml@main 14 | permissions: 15 | contents: write 16 | build: 17 | name: Build package and reports 🎁 18 | needs: [release, docs] 19 | uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main 20 | secrets: 21 | REPO_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | with: 23 | additional-r-cmd-check-params: --as-cran 24 | coverage: 25 | name: Coverage 📔 26 | needs: [release, docs] 27 | uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main 28 | secrets: 29 | REPO_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | with: 31 | additional-env-vars: | 32 | NOT_CRAN=true 33 | docs: 34 | name: Pkgdown Docs 📚 35 | needs: release 36 | uses: insightsengineering/r.pkg.template/.github/workflows/pkgdown.yaml@main 37 | with: 38 | refs-order: c("latest-tag", "main") 39 | default-landing-page: latest-tag 40 | secrets: 41 | REPO_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | wasm: 43 | name: Build WASM packages 🧑‍🏭 44 | needs: release 45 | uses: insightsengineering/r.pkg.template/.github/workflows/wasm.yaml@main 46 | -------------------------------------------------------------------------------- /.github/workflows/scheduled.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Scheduled 🕰️ 3 | 4 | on: 5 | schedule: 6 | - cron: '45 3 * * 0' 7 | workflow_dispatch: 8 | inputs: 9 | chosen-workflow: 10 | description: | 11 | Select which workflow you'd like to run 12 | required: true 13 | type: choice 14 | default: rhub 15 | options: 16 | - rhub 17 | - dependency-test 18 | - branch-cleanup 19 | - revdepcheck 20 | 21 | jobs: 22 | dependency-test: 23 | if: > 24 | github.event_name == 'schedule' || ( 25 | github.event_name == 'workflow_dispatch' && 26 | inputs.chosen-workflow == 'dependency-test' 27 | ) 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | test-strategy: ["min_cohort", "min_isolated", "release", "max"] 32 | uses: insightsengineering/r.pkg.template/.github/workflows/verdepcheck.yaml@main 33 | name: Dependency Test - ${{ matrix.test-strategy }} 🔢 34 | secrets: 35 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 36 | GCHAT_WEBHOOK: ${{ secrets.GCHAT_WEBHOOK }} 37 | with: 38 | strategy: ${{ matrix.test-strategy }} 39 | additional-env-vars: | 40 | PKG_SYSREQS_DRY_RUN=true 41 | branch-cleanup: 42 | if: > 43 | github.event_name == 'schedule' || ( 44 | github.event_name == 'workflow_dispatch' && 45 | inputs.chosen-workflow == 'branch-cleanup' 46 | ) 47 | name: Branch Cleanup 🧹 48 | uses: insightsengineering/r.pkg.template/.github/workflows/branch-cleanup.yaml@main 49 | secrets: 50 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 51 | cran-status: 52 | name: CRAN Status Monitor 📺 53 | uses: insightsengineering/r.pkg.template/.github/workflows/cran-status.yaml@main 54 | with: 55 | issue-assignees: "niessl,danielinteractive" 56 | revdepcheck: 57 | if: > 58 | github.event_name == 'schedule' || ( 59 | github.event_name == 'workflow_dispatch' && 60 | inputs.chosen-workflow == 'revdepcheck' 61 | ) 62 | name: revdepcheck ↩️ 63 | uses: insightsengineering/r.pkg.template/.github/workflows/revdepcheck.yaml@main 64 | rhub: 65 | if: > 66 | github.event_name == 'schedule' || ( 67 | github.event_name == 'workflow_dispatch' && 68 | inputs.chosen-workflow == 'rhub' 69 | ) 70 | name: R-hub 🌐 71 | uses: insightsengineering/r.pkg.template/.github/workflows/rhub.yaml@main 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .httr-oauth 3 | .project 4 | .RData 5 | .Rhistory 6 | .Rproj.user 7 | .Ruserdata 8 | .settings/** 9 | *.html 10 | *.Rcheck 11 | *.rprof 12 | *.sas.txt 13 | *~ 14 | /.project 15 | devel/* 16 | doc 17 | docs 18 | inst/outputs/* 19 | logs 20 | Meta 21 | packrat/lib*/ 22 | temp 23 | temp_w 24 | templates/ 25 | tmp.* 26 | vignettes/*.html 27 | vignettes/*.md 28 | vignettes/*.R 29 | coverage.* 30 | tests/testthat/_snaps/**/*.new.md 31 | tests/testthat/_snaps/**/*.new.svg 32 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: linters_with_defaults( 2 | line_length_linter = line_length_linter(120), 3 | cyclocomp_linter = NULL, 4 | object_usage_linter = NULL, 5 | object_name_linter = NULL 6 | ) 7 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # All available hooks: https://pre-commit.com/hooks.html 2 | # R specific hooks: https://github.com/lorenzwalthert/precommit 3 | repos: 4 | - repo: https://github.com/lorenzwalthert/precommit 5 | rev: v0.4.3.9003 6 | hooks: 7 | - id: style-files 8 | args: [--style_pkg=styler, --style_fun=tidyverse_style] 9 | - id: roxygenize 10 | additional_dependencies: 11 | - checkmate 12 | - knitr 13 | - testthat 14 | - utils 15 | - mvna 16 | - git2r 17 | - id: use-tidy-description 18 | - id: spell-check 19 | exclude: > 20 | (?x)^( 21 | data/.*| 22 | design/.*| 23 | (.*/|)\.Rprofile| 24 | (.*/|)\.Renviron| 25 | (.*/|)\.gitignore| 26 | (.*/|)NAMESPACE| 27 | (.*/|)DESCRIPTION| 28 | (.*/|)WORDLIST| 29 | (.*/|)LICENSE| 30 | (.*/|)\.Rbuildignore| 31 | (.*/|)\.lintr| 32 | (.*/|)_pkgdown.y[a]?ml| 33 | (.*/|)\.covrignore| 34 | (.*/|)staged_dependencies.y[a]?ml| 35 | (.*/|)\.pre-commit-.*| 36 | \.github/.*| 37 | .*\.[rR]| 38 | .*\.Rproj| 39 | .*\.py| 40 | .*\.png| 41 | .*\.feather| 42 | .*\.rds| 43 | .*\.Rds| 44 | .*\.sh| 45 | .*\.RData 46 | )$ 47 | - id: lintr 48 | additional_dependencies: 49 | - checkmate 50 | - knitr 51 | - testthat 52 | - utils 53 | - mvna 54 | - id: readme-rmd-rendered 55 | - id: parsable-R 56 | - id: no-browser-statement 57 | - id: deps-in-desc 58 | - repo: https://github.com/pre-commit/mirrors-prettier 59 | rev: v4.0.0-alpha.8 60 | hooks: 61 | - id: prettier 62 | - repo: https://github.com/pre-commit/pre-commit-hooks 63 | rev: v5.0.0 64 | hooks: 65 | - id: check-added-large-files 66 | args: ["--maxkb=200"] 67 | - id: end-of-file-fixer 68 | exclude: > 69 | (?x)^( 70 | .*\.Rd| 71 | tests/testthat/_snaps/.* 72 | )$ 73 | - id: trailing-whitespace 74 | exclude: > 75 | (?x)^( 76 | .*\.Rd| 77 | tests/testthat/_snaps/.* 78 | )$ 79 | - id: check-yaml 80 | - id: no-commit-to-branch 81 | - id: mixed-line-ending 82 | args: ["--fix=lf"] 83 | - repo: local 84 | hooks: 85 | - id: forbid-to-commit 86 | name: Don't commit common R artifacts 87 | entry: Cannot commit .Rhistory, .RData, .Rds or .rds. 88 | language: fail 89 | files: '\.Rhistory|\.RData|\.Rds|\.rds$' 90 | # `exclude: ` to allow committing specific files. 91 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: simIDM 3 | Title: Simulating Oncology Trials using an Illness-Death Model 4 | Version: 0.1.0.9008 5 | Authors@R: c( 6 | person("Alexandra", "Erdmann", , "alexandra.erdmann@uni-ulm.de", role = c("aut", "cre")), 7 | person("Kaspar", "Rufibach", , "kaspar.rufibach@roche.com", role = "aut"), 8 | person("Holger", "Löwe", , "hbj.loewe@gmail.com", role = "aut"), 9 | person("Daniel", "Sabanés Bové", , "daniel.sabanes_bove@roche.com", role = "aut"), 10 | person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")), 11 | person("University of Ulm", role = c("cph", "fnd")) 12 | ) 13 | Description: Based on the illness-death model a large number of clinical 14 | trials with oncology endpoints progression-free survival (PFS) and 15 | overall survival (OS) can be simulated, see Meller, Beyersmann and 16 | Rufibach (2019) . The simulation set-up allows 17 | for random and event-driven censoring, an arbitrary number of 18 | treatment arms, staggered study entry and drop-out. Exponentially, 19 | Weibull and piecewise exponentially distributed survival times can be 20 | generated. The correlation between PFS and OS can be calculated. 21 | License: Apache License 2.0 22 | URL: https://github.com/insightsengineering/simIDM/ 23 | BugReports: https://github.com/insightsengineering/simIDM/issues 24 | Depends: 25 | R (>= 3.6) 26 | Imports: 27 | checkmate, 28 | furrr, 29 | future, 30 | mstate, 31 | parallelly, 32 | stats, 33 | survival 34 | Suggests: 35 | coxphw, 36 | knitr, 37 | mvna, 38 | prodlim, 39 | rmarkdown, 40 | rpact, 41 | testthat (>= 3.0.0) 42 | VignetteBuilder: 43 | knitr 44 | Config/testthat/edition: 3 45 | Encoding: UTF-8 46 | Language: en-US 47 | LazyData: true 48 | Roxygen: list(markdown = TRUE) 49 | RoxygenNote: 7.3.2 50 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(getInit,ExponentialTransition) 4 | S3method(getInit,WeibullTransition) 5 | S3method(getResults,ExponentialTransition) 6 | S3method(getResults,WeibullTransition) 7 | S3method(getTarget,ExponentialTransition) 8 | S3method(getTarget,WeibullTransition) 9 | S3method(haz,ExponentialTransition) 10 | S3method(haz,PWCTransition) 11 | S3method(haz,WeibullTransition) 12 | S3method(survOS,ExponentialTransition) 13 | S3method(survOS,PWCTransition) 14 | S3method(survOS,WeibullTransition) 15 | S3method(survPFS,ExponentialTransition) 16 | S3method(survPFS,PWCTransition) 17 | S3method(survPFS,WeibullTransition) 18 | S3method(survTrans,ExponentialTransition) 19 | S3method(survTrans,WeibullTransition) 20 | export(ExpHazOS) 21 | export(ExpQuantOS) 22 | export(ExpSurvOS) 23 | export(ExpSurvPFS) 24 | export(PCWInversionMethod) 25 | export(PWCsurvOS) 26 | export(PWCsurvPFS) 27 | export(WeibSurvOS) 28 | export(WeibSurvPFS) 29 | export(addStaggeredEntry) 30 | export(assert_intervals) 31 | export(assert_positive_number) 32 | export(avgHRExpOS) 33 | export(avgHRIntegExpOS) 34 | export(censoringByNumberEvents) 35 | export(corPFSOS) 36 | export(corTrans) 37 | export(empSignificant) 38 | export(estimateParams) 39 | export(exponential_transition) 40 | export(expvalOSInteg) 41 | export(expvalPFSInteg) 42 | export(getCensoredData) 43 | export(getClinicalTrials) 44 | export(getDatasetWideFormat) 45 | export(getEventsAll) 46 | export(getInit) 47 | export(getNumberEvents) 48 | export(getOneClinicalTrial) 49 | export(getOneToTwoRows) 50 | export(getPCWDistr) 51 | export(getPWCHazard) 52 | export(getResults) 53 | export(getSimulatedData) 54 | export(getSumPCW) 55 | export(getTarget) 56 | export(getTimePoint) 57 | export(getWaitTimeSum) 58 | export(haz) 59 | export(logRankTest) 60 | export(log_p11) 61 | export(negLogLik) 62 | export(passedLogRank) 63 | export(piecewise_exponential) 64 | export(prepareData) 65 | export(pwA) 66 | export(survOS) 67 | export(survPFS) 68 | export(survPFSOS) 69 | export(survTrans) 70 | export(trackEventsPerTrial) 71 | export(weibull_transition) 72 | import(checkmate) 73 | importFrom(stats,acf) 74 | importFrom(survival,survdiff) 75 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # simIDM 0.1.0.9008 2 | 3 | ### New Features 4 | 5 | - `estimateParams` estimates parameters of exponential and Weibull transition hazards using maximum likelihood for a given data set after using `prepareData`. 6 | - `corTrans` calculates the correlation between PFS and OS for a given set of transition hazards, and `corPFSOS` estimates this correlation for a given data set, optionally with a bootstrap based confidence interval. 7 | 8 | ### Bug Fixes 9 | 10 | - `ExpSurvOS` now returns 0 instead of NaN for large values of t. 11 | - `WeibSurvOS` now does not return an error for large values of t. 12 | - `PWCSurvOS` now does not return an error for large values of t. It also no longer returns values larger than 1. It is significantly faster, based on a closed form calculation instead of numerical integration. 13 | - `getSimulatedData` now also works when there are no transitions from progression to death, similarly for `getOneClinicalTrial` (which now warns if there are no such transitions at all). 14 | - `corPFSOS` now undoes the `future` plan upon function exit. 15 | 16 | ### Miscellaneous 17 | 18 | - `PwcOSInt`, `integrateVector`, `WeibOSInteg` are no longer exported, and only used internally. 19 | - Renamed piecewise constant hazards function to `getPWCHazard` (previously `getPCWHazard`). 20 | 21 | # simIDM 0.0.5 22 | 23 | - First CRAN version of the package. 24 | - The package simulates illness-death models with constant, Weibull or piecewise constant transition hazards. 25 | 26 | ### New Features 27 | 28 | - Exponentially, Weibull and piecewise exponentially distributed survival times. 29 | - Random censoring and event-driven censoring after a pre-specified number of PFS or OS events. 30 | - Arbitrary number of treatment arms and flexible randomization ratio. 31 | - Staggered study entry. 32 | - Derivation of PFS and OS survival functions from transition hazards. 33 | -------------------------------------------------------------------------------- /R/assertions.R: -------------------------------------------------------------------------------- 1 | #' Assertion for Positive Number 2 | #' 3 | #' @param x what to check. 4 | #' @param zero_ok (`flag`)\cr whether `x` can be zero or not. 5 | #' 6 | #' @return Raises an error if `x` is not a single positive (or non-negative) number. 7 | #' @export 8 | #' 9 | #' @examples 10 | #' assert_positive_number(3.2) 11 | #' assert_positive_number(0, zero_ok = TRUE) 12 | assert_positive_number <- function(x, 13 | zero_ok = FALSE) { 14 | assert_number(x) 15 | assert_flag(zero_ok) 16 | if (zero_ok) { 17 | assert_true(x >= 0) 18 | } else { 19 | assert_true(x > 0) 20 | } 21 | } 22 | 23 | #' Assertion for vector describing intervals 24 | #' 25 | #' We define an intervals vector to always start with 0, and contain 26 | #' unique ordered time points. 27 | #' 28 | #' @param x what to check. 29 | #' @param y (`count`)\cr required length of `y`. 30 | #' 31 | #' @return Raises an error if `x` is not an intervals vector starting with 0. 32 | #' @export 33 | #' 34 | #' @examples 35 | #' assert_intervals(c(0, 5, 7), 3) 36 | assert_intervals <- function(x, y) { 37 | assert_numeric( 38 | x, 39 | lower = 0, 40 | any.missing = FALSE, 41 | all.missing = FALSE, 42 | len = y, 43 | unique = TRUE, 44 | sorted = TRUE 45 | ) 46 | assert_true(x[1] == 0) 47 | } 48 | -------------------------------------------------------------------------------- /R/empSignificant.R: -------------------------------------------------------------------------------- 1 | #' Log-Rank Test for a Single Trial 2 | #' 3 | #' This function evaluates the significance of either PFS or OS endpoints in a trial, 4 | #' based on a pre-specified critical value. 5 | #' 6 | #' @param data (`data.frame`)\cr data frame containing entry and exit times of an 7 | #' illness-death model. See [getSimulatedData()] for details. 8 | #' @param typeEvent (`string`)\cr endpoint to be evaluated, possible values are `PFS` and `OS`. 9 | #' @param critical (positive `number`)\cr critical value of the log-rank test. 10 | #' 11 | #' @return Logical value indicating log-rank test significance. 12 | #' @export 13 | #' 14 | #' @examples 15 | #' transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 16 | #' transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 17 | #' simTrial <- getClinicalTrials( 18 | #' nRep = 1, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 19 | #' transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 20 | #' accrual = list(param = "intensity", value = 7) 21 | #' )[[1]] 22 | #' logRankTest(data = simTrial, typeEvent = "OS", critical = 3.4) 23 | logRankTest <- function(data, typeEvent = c("PFS", "OS"), critical) { 24 | # Must be have the format of one row per patient (`datType` must be 1rowPatient` 25 | # in getClinicalTrials()), i.e. have 10 (if censored) or 11 columns. 26 | assert_data_frame(data, min.cols = 10, max.cols = 11) 27 | typeEvent <- match.arg(typeEvent) 28 | assert_positive_number(critical) 29 | 30 | if (typeEvent == "OS") { 31 | time <- data$OStime 32 | event <- data$OSevent 33 | } else if (typeEvent == "PFS") { 34 | time <- data$PFStime 35 | event <- data$PFSevent 36 | } 37 | 38 | logRank <- survival::survdiff(survival::Surv(time, event) ~ trt, data) 39 | sqrt(logRank$chisq) > critical 40 | } 41 | 42 | 43 | #' Helper function to conduct log-rank tests for either PFS or OS 44 | #' 45 | #' This function evaluates the significance of either PFS or OS endpoints for each trial 46 | #' in a list of trials, based on a pre-specified critical value. 47 | #' 48 | #' @param simTrials (`list`)\cr simulated trial data sets, see [getClinicalTrials()]. 49 | #' @param typeEvent (`string`)\cr endpoint to be evaluated, possible values are `PFS` and `OS`. 50 | #' @param eventNum (`integer`)\cr number of events required to trigger analysis. 51 | #' @param critical (positive `number`)\cr critical value of the log-rank test. 52 | #' 53 | #' @return Logical vector indicating log-rank test significance for each trial. 54 | #' @export 55 | #' 56 | #' @examples 57 | #' transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 58 | #' transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 59 | #' simTrials <- getClinicalTrials( 60 | #' nRep = 50, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 61 | #' transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 62 | #' accrual = list(param = "intensity", value = 7) 63 | #' ) 64 | #' passedLogRank(simTrials = simTrials, typeEvent = "PFS", eventNum = 300, critical = 2.4) 65 | #' @keywords internal 66 | passedLogRank <- function(simTrials, typeEvent, eventNum, critical) { 67 | assert_list(simTrials, null.ok = FALSE) 68 | assert_positive_number(critical) 69 | assert_count(eventNum, positive = TRUE) 70 | 71 | # Censor simulated trials at time-point of OS/PFS analysis. 72 | trialsAna <- lapply( 73 | X = simTrials, 74 | FUN = censoringByNumberEvents, 75 | eventNum = eventNum, 76 | typeEvent = typeEvent 77 | ) 78 | # Compute log-rank test for all trials for OS/PFS. 79 | unlist(lapply( 80 | X = trialsAna, 81 | FUN = logRankTest, 82 | typeEvent = typeEvent, 83 | critical = critical 84 | )) 85 | } 86 | 87 | #' Empirical Significance for a List of Simulated Trials 88 | #' 89 | #' This function computes four types of empirical significance — PFS, OS, at-least (significant 90 | #' in at least one of PFS/OS), and joint (significant in both PFS and OS) — using 91 | #' the log-rank test. Empirical significance is calculated as the proportion of significant 92 | #' results in simulated trials, each ending when a set number of PFS/OS events occur. 93 | #' Critical values for PFS and OS test significance must be specified. If trials simulate equal 94 | #' transition hazards across groups (H0), empirical significance estimates type I error; 95 | #' if they simulate differing transition hazards (H1), it estimates power. 96 | #' 97 | #' @param simTrials (`list`)\cr simulated trial data sets, see [getClinicalTrials()]. 98 | #' @param criticalPFS (positive `number`)\cr critical value of the log-rank test for PFS. 99 | #' @param criticalOS (positive `number`)\cr critical value of the log-rank test for OS. 100 | #' @param eventNumPFS (`integer`)\cr number of PFS events required to trigger PFS analysis. 101 | #' @param eventNumOS (`integer`)\cr number of OS events required to trigger OS analysis. 102 | #' 103 | #' @return This returns values of four measures of empirical significance. 104 | #' @export 105 | #' 106 | #' @examples 107 | #' transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 108 | #' transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 109 | #' simTrials <- getClinicalTrials( 110 | #' nRep = 50, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 111 | #' transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 112 | #' accrual = list(param = "intensity", value = 7) 113 | #' ) 114 | #' empSignificant( 115 | #' simTrials = simTrials, criticalPFS = 2.4, criticalOS = 2.2, 116 | #' eventNumPFS = 300, eventNumOS = 500 117 | #' ) 118 | empSignificant <- function(simTrials, criticalPFS, criticalOS, eventNumPFS, eventNumOS) { 119 | assert_list(simTrials, null.ok = FALSE) 120 | assert_positive_number(criticalPFS) 121 | assert_positive_number(criticalOS) 122 | assert_count(eventNumPFS, positive = TRUE) 123 | assert_count(eventNumOS, positive = TRUE) 124 | 125 | nRep <- length(simTrials) 126 | simTrials <- lapply( 127 | X = simTrials, 128 | FUN = function(x) if (ncol(x) == 9) getDatasetWideFormat(x) else x 129 | ) 130 | 131 | # Which trials passed the log-rank test for PFS/OS? 132 | passedLogRankPFS <- passedLogRank( 133 | simTrials = simTrials, 134 | typeEvent = "PFS", 135 | eventNum = eventNumPFS, 136 | critical = criticalPFS 137 | ) 138 | passedLogRankOS <- passedLogRank( 139 | simTrials = simTrials, 140 | typeEvent = "OS", 141 | eventNum = eventNumOS, 142 | critical = criticalOS 143 | ) 144 | 145 | # Empirical significance is the fraction of trials with significant log-rank test: 146 | significantPFS <- sum(passedLogRankPFS) / nRep 147 | significantOS <- sum(passedLogRankOS) / nRep 148 | 149 | # Derived measures of significance: 150 | sumPassed <- passedLogRankPFS + passedLogRankOS 151 | # At least one of PFS/OS has significant log-rank test. 152 | significantAtLeastOne <- sum(sumPassed >= 1) / nRep 153 | # Both PFS/OS have significant log-rank tests. 154 | significantBoth <- sum(sumPassed == 2) / nRep 155 | 156 | list( 157 | "significantPFS" = significantPFS, 158 | "significantOS" = significantOS, 159 | "significantAtLeastOne" = significantAtLeastOne, 160 | "significantBoth" = significantBoth 161 | ) 162 | } 163 | -------------------------------------------------------------------------------- /R/getWaitTimeSum.R: -------------------------------------------------------------------------------- 1 | #' Event Times Distributed as Sum of Weibull 2 | #' 3 | #' This returns event times with a distribution resulting from the sum of two Weibull distributed random variables 4 | #' using the inversion method. 5 | #' 6 | #' @param U (`numeric`)\cr uniformly distributed random variables. 7 | #' @param haz1 (positive `number`)\cr first summand (constant hazard). 8 | #' @param haz2 (positive `number`)\cr second summand (constant hazard). 9 | #' @param p1 (positive `number`)\cr rate parameter of Weibull distribution for `haz1`. 10 | #' @param p2 (positive `number`)\cr rate parameter of Weibull distribution for `haz2`. 11 | #' @param entry (`numeric`)\cr the entry times in the current state. 12 | #' 13 | #' @return This returns a vector with event times. 14 | #' @export 15 | #' 16 | #' @examples 17 | #' getWaitTimeSum(U = c(0.4, 0.5), haz1 = 0.8, haz2 = 1, p1 = 1.1, p2 = 1.5, entry = c(0, 0)) 18 | getWaitTimeSum <- function(U, haz1, haz2, p1, p2, entry) { 19 | assert_numeric(U, lower = 0, upper = 1, any.missing = FALSE, all.missing = FALSE) 20 | assert_positive_number(haz1, zero_ok = TRUE) 21 | assert_positive_number(haz2, zero_ok = TRUE) 22 | assert_positive_number(p1) 23 | assert_positive_number(p2) 24 | assert_numeric(entry, lower = 0, len = length(U), any.missing = FALSE, all.missing = FALSE) 25 | 26 | N <- length(U) 27 | # Exponential distributed survival times. 28 | if (p1 == 1 && p2 == 1) { 29 | return(-log(1 - U) / (haz1 + haz2)) 30 | } 31 | # Weibull distributed survival times. 32 | temp <- function(x, y, t0) { 33 | return(haz1 * (x + t0)^p1 - haz1 * t0^p1 - haz2 * t0^p2 + haz2 * (x + t0)^p2 + y) 34 | } 35 | stime <- NULL 36 | i <- 1 37 | while (length(stime) < N) { 38 | u <- U[i] 39 | t0 <- entry[i] 40 | if (temp(0, log(1 - u), t0) * temp(10000, log(1 - u), t0) < 0) { 41 | res <- stats::uniroot(temp, c(0, 10000), tol = 10^-16, y = log(1 - u), t0 = t0) 42 | stime[i] <- res$root 43 | i <- i + 1 44 | if (res$root == 0) { 45 | warning("uniroot: accuracy (tol=10^-16) is not enough.") 46 | } 47 | } else { 48 | warning("uniroot: Values at interval endpoints (interval=c(0,10000)) are not of opposite signs. \n") 49 | } 50 | } 51 | stime 52 | } 53 | -------------------------------------------------------------------------------- /R/hazardFunctions.R: -------------------------------------------------------------------------------- 1 | #' OS Hazard Function from Constant Transition Hazards 2 | #' 3 | #' @param t (`numeric`)\cr study time-points. 4 | #' @param h01 (positive `number`)\cr transition hazard for 0 to 1 transition. 5 | #' @param h02 (positive `number`)\cr transition hazard for 0 to 2 transition. 6 | #' @param h12 (positive `number`)\cr transition hazard for 1 to 2 transition. 7 | #' 8 | #' @return This returns the value of the OS hazard function at time t. 9 | #' @export 10 | #' 11 | #' @examples 12 | #' ExpHazOS(c(1:5), 0.2, 1.1, 0.8) 13 | ExpHazOS <- function(t, h01, h02, h12) { 14 | assert_numeric(t, lower = 0, any.missing = FALSE) 15 | assert_positive_number(h01) 16 | assert_positive_number(h02) 17 | assert_positive_number(h12) 18 | 19 | h012 <- h12 - h01 - h02 20 | ((h12 - h02) * (h01 + h02) - h01 * h12 * exp(-h012 * t)) / ((h12 - h02) - h01 * exp(-h012 * t)) 21 | } 22 | 23 | #' Helper Function for `avgHRExpOS()` 24 | #' 25 | #' It is an integrand of the form OS hazard function with intensities h01, h02, h12 26 | #' at time point t multiplied with a weighted product of the two OS Survival functions 27 | #' at t (one for intensities h0 and one for h1). 28 | #' 29 | #' @param x (`numeric`)\cr variable of integration. 30 | #' @param h01 (positive `number`)\cr transition hazard for 0 to 1 transition. 31 | #' @param h02 (positive `number`)\cr transition hazard for 0 to 2 transition. 32 | #' @param h12 (positive `number`)\cr transition hazard for 1 to 2 transition. 33 | #' @param h0 (`list`)\cr transition parameters for the first treatment group. 34 | #' @param h1 (`list`)\cr transition parameters for the second treatment group. 35 | #' @param alpha (`number`)\cr weight parameter, see [avgHRExpOS()]. 36 | #' @return This returns the value of the integrand used to calculate 37 | #' the average hazard ratio for constant transition hazards, see [avgHRExpOS()]. 38 | #' @export 39 | #' 40 | #' @examples 41 | #' h0 <- list(h01 = 0.18, h02 = 0.06, h12 = 0.17) 42 | #' h1 <- list(h01 = 0.23, h02 = 0.07, h12 = 0.19) 43 | #' avgHRIntegExpOS(x = 5, h01 = 0.2, h02 = 0.5, h12 = 0.7, h0 = h0, h1 = h1, alpha = 0.5) 44 | avgHRIntegExpOS <- function(x, h01, h02, h12, h0, h1, alpha) { 45 | assert_positive_number(alpha) 46 | 47 | weightedSurv <- (ExpSurvOS(t = x, h01 = h0$h01, h02 = h0$h02, h12 = h0$h12) * 48 | ExpSurvOS(t = x, h01 = h1$h01, h02 = h1$h02, h12 = h1$h12))^alpha 49 | ExpHazOS(x, h01, h02, h12) * weightedSurv 50 | } 51 | 52 | #' Average OS Hazard Ratio from Constant Transition Hazards 53 | #' 54 | #' @param transitionByArm (`list`)\cr transition parameters for each treatment group. 55 | #' See [exponential_transition()], [piecewise_exponential()] and [weibull_transition()] for details. 56 | #' @param alpha (`number`)\cr assigns weights to time points, where values higher than 0.5 assign greater weight 57 | #' to earlier times and values lower than 0.5 assign greater weight to later times. 58 | #' @param upper (`number`)\cr upper (time) limit of integration. 59 | #' 60 | #' @return This returns the value of the average hazard ratio. 61 | #' @export 62 | #' 63 | #' @examples 64 | #' transitionTrt <- exponential_transition(h01 = 0.18, h02 = 0.06, h12 = 0.17) 65 | #' transitionCtl <- exponential_transition(h01 = 0.23, h02 = 0.07, h12 = 0.19) 66 | #' transitionList <- list(transitionCtl, transitionTrt) 67 | #' avgHRExpOS(transitionByArm = transitionList, alpha = 0.5, upper = 100) 68 | avgHRExpOS <- function(transitionByArm, alpha = 0.5, upper = Inf) { 69 | assert_list(transitionByArm, len = 2) 70 | assert_class(transitionByArm[[1]], "TransitionParameters") 71 | assert_class(transitionByArm[[2]], "TransitionParameters") 72 | assert_positive_number(alpha) 73 | assert_positive_number(upper) 74 | 75 | h0 <- transitionByArm[[1]]$hazard 76 | h1 <- transitionByArm[[2]]$hazard 77 | 78 | num <- stats::integrate(avgHRIntegExpOS, 79 | lower = 0, upper = upper, h01 = h1$h01, h02 = h1$h02, h12 = h1$h12, 80 | h0 = h0, h1 = h1, alpha = alpha 81 | )$value 82 | denom <- stats::integrate(avgHRIntegExpOS, 83 | lower = 0, upper = upper, h01 = h0$h01, h02 = h0$h02, h12 = h0$h12, 84 | h0 = h0, h1 = h1, alpha = alpha 85 | )$value 86 | num / denom 87 | } 88 | -------------------------------------------------------------------------------- /R/package.R: -------------------------------------------------------------------------------- 1 | #' `simIDM` Package 2 | #' 3 | #' `simIDM` simulates a survival multistate model that jointly models PFS and OS. 4 | #' 5 | "_PACKAGE" 6 | 7 | #' @import checkmate 8 | #' @importFrom stats acf 9 | #' @importFrom survival survdiff 10 | NULL 11 | -------------------------------------------------------------------------------- /R/piecewiseDistribution.R: -------------------------------------------------------------------------------- 1 | #' Piecewise Exponentially Distributed Event Times 2 | #' 3 | #' This returns event times with a distribution resulting from piece-wise constant hazards 4 | #' using the inversion method. 5 | #' 6 | #' @param U (`numeric`)\cr uniformly distributed random variables. 7 | #' @param haz (`numeric`)\cr piecewise constant hazard. 8 | #' @param pw (`numeric`)\cr time intervals for the piecewise constant hazard. 9 | #' @param t_0 (`numeric`)\cr the starting times. 10 | #' 11 | #' @return This returns a vector with event times. 12 | #' @export 13 | #' 14 | #' @examples 15 | #' getPCWDistr(U = runif(3), haz = c(1.1, 0.5, 0.4), pw = c(0, 7, 10), t_0 = c(0, 1, 4.2)) 16 | getPCWDistr <- function(U, haz, pw, t_0) { 17 | assert_numeric(U, lower = 0, upper = 1, any.missing = FALSE, all.missing = FALSE) 18 | assert_numeric(haz, lower = 0, any.missing = FALSE, all.missing = FALSE) 19 | assert_intervals(pw, length(haz)) 20 | assert_numeric(t_0, lower = 0, len = length(U), any.missing = FALSE, all.missing = FALSE) 21 | 22 | N <- length(U) 23 | t1 <- rep(NA, N) # Initialize event times. 24 | n2 <- length(haz) 25 | for (kk in seq_len(N)) { 26 | # Shift if t_0 !=0. 27 | cuts_temp <- c(t_0[kk], pw[pw > t_0[kk]]) 28 | cuts_temp <- cuts_temp - t_0[kk] 29 | # Number of cut-points after shift. 30 | n <- length(cuts_temp) 31 | # Number of hazards to remove for shift (t_0 > times). 32 | remov <- n2 - n 33 | haz_temp <- haz[(remov + 1):n2] 34 | LogU <- log(1 - U[kk]) 35 | 36 | t1[kk] <- if (n != 1) { 37 | PCWInversionMethod(haz = haz_temp, pw = cuts_temp, LogU = LogU) 38 | } else if (n == 1) { # I.e. exponential distribution. 39 | -LogU / haz_temp 40 | } 41 | } 42 | t1 43 | } 44 | 45 | #' Single Piecewise Exponentially Distributed Event Time 46 | #' 47 | #' This returns an event time with a distribution resulting from piece-wise constant hazards 48 | #' using the inversion method. 49 | #' 50 | #' @param pw (`numeric`)\cr time intervals for the piecewise constant hazard. 51 | #' @param haz (`numeric`)\cr piecewise constant hazard. 52 | #' @param LogU (`numeric`)\cr transformed uniformly distributed random variables (log(1-U)). 53 | #' 54 | #' @return This returns one single event time. 55 | #' @export 56 | #' 57 | #' @examples 58 | #' PCWInversionMethod(haz = c(1.1, 0.5, 0.4), pw = c(0, 7, 10), LogU = log(1 - runif(1))) 59 | PCWInversionMethod <- function(haz, pw, LogU) { 60 | assert_numeric(haz, lower = 0, any.missing = FALSE, all.missing = FALSE) 61 | assert_intervals(pw, length(haz)) 62 | assert_number(LogU) 63 | 64 | n <- length(pw) 65 | t1 <- 0 66 | # Determine sum of alpha*time-interval for all i. 67 | dt <- pw[2:n] - pw[1:(n - 1)] 68 | 69 | # Helping matrix. 70 | tempMatrix <- matrix(0, nrow = n, ncol = n - 1) 71 | tempMatrix[lower.tri(tempMatrix)] <- 1 72 | 73 | sumA <- -as.vector(tempMatrix %*% (haz[1:(n - 1)] * dt)) 74 | # Find the appropriate time interval. 75 | rev_index <- findInterval(LogU, rev(sumA), rightmost.closed = FALSE, left.open = TRUE, all.inside = FALSE) 76 | # Use reverse index. 77 | index <- n - rev_index 78 | # Calculate event time. 79 | pw[index] + (sumA[index] - LogU) / haz[index] 80 | } 81 | -------------------------------------------------------------------------------- /R/piecewiseHazards.R: -------------------------------------------------------------------------------- 1 | #' Piecewise Constant Hazard Values 2 | #' 3 | #' This returns piecewise constant hazard values at specified time points. 4 | #' 5 | #' @param haz (`numeric`)\cr piecewise constant input hazard. 6 | #' @param pw (`numeric`)\cr time intervals for the piecewise constant hazard. 7 | #' @param x (`numeric`)\cr time-points. 8 | #' 9 | #' @return Hazard values at input time-points. 10 | #' @export 11 | #' 12 | #' @examples 13 | #' getPWCHazard(c(1, 1.2, 1.4), c(0, 2, 3), c(1, 4, 6)) 14 | getPWCHazard <- function(haz, pw, x) { # nolint 15 | sapply(x, function(jj) { 16 | y <- NULL 17 | # Find interval and corresponding hazard value for time x[jj]. 18 | for (ii in seq_along(haz)) { 19 | if (jj >= pw[ii]) { 20 | y <- haz[ii] 21 | } 22 | } 23 | y 24 | }) 25 | } 26 | 27 | #' Sum of Two Piecewise Constant Hazards 28 | #' 29 | #' This returns the sum of two piecewise constant hazards per interval. 30 | #' 31 | #' @param haz1 (`numeric`)\cr first summand (piecewise constant hazard). 32 | #' @param haz2 (`numeric`)\cr second summand (piecewise constant hazard). 33 | #' @param pw1 (`numeric`)\cr time intervals of first summand. 34 | #' @param pw2 (`numeric`)\cr time intervals of second summand. 35 | #' 36 | #' @return List with elements `hazards` and `intervals` for the sum of two piecewise constant hazards. 37 | #' @export 38 | #' 39 | #' @examples 40 | #' getSumPCW(c(1.2, 0.3, 0.6), c(1.2, 0.7, 1), c(0, 8, 9), c(0, 1, 4)) 41 | getSumPCW <- function(haz1, haz2, pw1, pw2) { 42 | # Get all cutpoints for the intervals. 43 | cuts_sum <- unique(sort(c(pw1, pw2))) 44 | haz_sum <- NULL 45 | # Get sum of hazards for all intervals. 46 | for (i in seq_along(cuts_sum)) { 47 | haz_sum[i] <- getPWCHazard(haz1, pw1, cuts_sum[i]) + 48 | getPWCHazard(haz2, pw2, cuts_sum[i]) 49 | } 50 | list( 51 | hazards = haz_sum, 52 | intervals = cuts_sum 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /R/transitionParameters.R: -------------------------------------------------------------------------------- 1 | #' Transition Hazards for Exponential Event Times 2 | #' 3 | #' This creates a list with class `TransitionParameters` containing 4 | #' hazards, time intervals and Weibull rates for exponential event times 5 | #' in an illness-death model. 6 | #' 7 | #' @param h01 (positive `number`)\cr transition hazard for 0 to 1 transition. 8 | #' @param h02 (positive `number`)\cr transition hazard for 0 to 2 transition. 9 | #' @param h12 (positive `number`)\cr transition hazard for 1 to 2 transition. 10 | #' 11 | #' @return List with elements `hazards`, `intervals`, `weibull_rates` and `family` 12 | #' (exponential). 13 | #' @export 14 | #' 15 | #' @examples 16 | #' exponential_transition(1, 1.6, 0.3) 17 | exponential_transition <- function(h01 = 1, h02 = 1, h12 = 1) { 18 | assert_positive_number(h01, zero_ok = TRUE) 19 | assert_positive_number(h02, zero_ok = TRUE) 20 | assert_positive_number(h12, zero_ok = TRUE) 21 | structure( 22 | list( 23 | hazards = list(h01 = h01, h02 = h02, h12 = h12), 24 | intervals = list(pw01 = 0, pw02 = 0, pw12 = 0), 25 | weibull_rates = list(p01 = 1, p02 = 1, p12 = 1), 26 | family = "exponential" 27 | ), 28 | class = c("ExponentialTransition", "TransitionParameters") 29 | ) 30 | } 31 | 32 | #' Transition Hazards for Piecewise Exponential Event Times 33 | #' 34 | #' This creates a list with class `TransitionParameters` containing 35 | #' hazards, time intervals and Weibull rates for piecewise exponential event times 36 | #' in an illness-death model. 37 | #' 38 | #' @param h01 (`numeric vector`)\cr constant transition hazards for 0 to 1 transition 39 | #' @param h02 (`numeric vector`)\cr constant transition hazards for 0 to 2 transition 40 | #' @param h12 (`numeric vector`)\cr constant transition hazards for 1 to 2 transition 41 | #' @param pw01 (`numeric vector`)\cr time intervals for the piecewise constant hazards `h01` 42 | #' @param pw02 (`numeric vector`)\cr time intervals for the piecewise constant hazards `h02` 43 | #' @param pw12 (`numeric vector`)\cr time intervals for the piecewise constant hazards `h12` 44 | #' 45 | #' @return List with elements `hazards`, `intervals`, `weibull_rates` and `family` 46 | #' (piecewise exponential). 47 | #' @export 48 | #' 49 | #' @examples 50 | #' piecewise_exponential( 51 | #' h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 52 | #' pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 53 | #' ) 54 | piecewise_exponential <- function(h01, h02, h12, pw01, pw02, pw12) { 55 | assert_numeric(h01, lower = 0, any.missing = FALSE, all.missing = FALSE) 56 | assert_numeric(h02, lower = 0, any.missing = FALSE, all.missing = FALSE) 57 | assert_numeric(h12, lower = 0, any.missing = FALSE, all.missing = FALSE) 58 | 59 | assert_intervals(pw01, length(h01)) 60 | assert_intervals(pw02, length(h02)) 61 | assert_intervals(pw12, length(h12)) 62 | 63 | structure( 64 | list( 65 | hazards = list(h01 = h01, h02 = h02, h12 = h12), 66 | intervals = list(pw01 = pw01, pw02 = pw02, pw12 = pw12), 67 | weibull_rates = list(p01 = 1, p02 = 1, p12 = 1), 68 | family = "piecewise exponential" 69 | ), 70 | class = c("PWCTransition", "TransitionParameters") 71 | ) 72 | } 73 | 74 | #' Transition Hazards for Weibull Distributed Event Times 75 | #' 76 | #' This creates a list with class `TransitionParameters` containing 77 | #' hazards, time intervals and Weibull rates for Weibull distributed event times 78 | #' in an illness-death model. 79 | #' 80 | #' @param h01 (positive `number`)\cr transition hazard for 0 to 1 transition 81 | #' @param h02 (positive `number`)\cr transition hazard for 0 to 2 transition 82 | #' @param h12 (positive `number`)\cr transition hazard for 1 to 2 transition 83 | #' @param p01 (positive `number`)\cr rate parameter of Weibull distribution for `h01` 84 | #' @param p02 (positive `number`)\cr rate parameter of Weibull distribution for `h02` 85 | #' @param p12 (positive `number`)\cr rate parameter of Weibull distribution for `h12` 86 | #' 87 | #' @return List with elements `hazards`, `intervals`, `weibull_rates` and `family` 88 | #' (Weibull). 89 | #' @export 90 | #' 91 | #' @examples 92 | #' weibull_transition(h01 = 1, h02 = 1.3, h12 = 0.5, p01 = 1.2, p02 = 1.3, p12 = 0.5) 93 | weibull_transition <- function(h01 = 1, h02 = 1, h12 = 1, p01 = 1, p02 = 1, p12 = 1) { 94 | assert_positive_number(h01, zero_ok = TRUE) 95 | assert_positive_number(h02, zero_ok = TRUE) 96 | assert_positive_number(h12, zero_ok = TRUE) 97 | assert_positive_number(p01) 98 | assert_positive_number(p02) 99 | assert_positive_number(p12) 100 | structure( 101 | list( 102 | hazards = list(h01 = h01, h02 = h02, h12 = h12), 103 | weibull_rates = list(p01 = p01, p02 = p02, p12 = p12), 104 | intervals = list(pw01 = 0, pw02 = 0, pw12 = 0), 105 | family = "Weibull" 106 | ), 107 | class = c("WeibullTransition", "TransitionParameters") 108 | ) 109 | } 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # simIDM 7 | 8 | 9 | 10 | [![Project Status: Active – The project has reached a stable, usable 11 | state and is being actively 12 | developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 13 | [![CRAN 14 | status](https://www.r-pkg.org/badges/version-last-release/simIDM)](https://www.r-pkg.org/badges/version-last-release/simIDM) 15 | [![CRAN monthly 16 | downloads](https://cranlogs.r-pkg.org/badges/simIDM)](https://cranlogs.r-pkg.org/badges/simIDM) 17 | [![CRAN total 18 | downloads](https://cranlogs.r-pkg.org/badges/grand-total/simIDM)](https://cranlogs.r-pkg.org/badges/grand-total/simIDM) 19 | [![Code 20 | Coverage](https://raw.githubusercontent.com/insightsengineering/simIDM/_xml_coverage_reports/data/main/badge.svg)](https://raw.githubusercontent.com/insightsengineering/simIDM/_xml_coverage_reports/data/main/coverage.xml) 21 | 22 | 23 | Survival multistate models are a powerful and flexible tool for modeling 24 | and analyzing complex time-to-event data. The three-state illness-death 25 | model can be used to jointly model the oncology endpoints 26 | progression-free survival (PFS) and overall survival (OS). Jointly 27 | modeling the endpoints PFS and OS with the illness-death model has the 28 | major advantage of both adequately accounting for the correlation of the 29 | two endpoints and eliminating the need of the strong assumption of 30 | proportional hazards. This package provides the tools to simulate a 31 | large number of clinical trials with endpoints OS and PFS based on the 32 | illness-death model, which can be used for trail planning, for example. 33 | The simulation set-up allows random and event-driven censoring, an 34 | arbitrary number of treatment arms, staggered study entry and drop-out. 35 | Exponentially, Weibull and piecewise exponentially distributed survival 36 | times can be generated. In addition, the correlation between PFS and OS 37 | can be calculated based on the simulation scenario, or estimated from a 38 | given data set. 39 | 40 | **Scope:** 41 | 42 | - Simulation of the illness-death model with constant, Weibull or 43 | piecewise constant transition hazards. 44 | - Conversion of the transition times to PFS and OS survival times. 45 | - Correlation between PFS and OS survival times can be calculated. 46 | 47 | **Main Features:** 48 | 49 | - Exponentially, Weibull and piecewise exponentially distributed 50 | survival times. 51 | - Random censoring and event-driven censoring after a pre-specified 52 | number of PFS or OS events. 53 | - Arbitrary number of treatment arms and flexible randomization ratio. 54 | - Staggered study entry. 55 | - Derivation of PFS and OS survival functions from transition hazards. 56 | - Correlation between PFS and OS can be estimated from a given data set. 57 | 58 | ## Installation 59 | 60 | ### Release 61 | 62 | You can install the current release version from *CRAN* with: 63 | 64 | ``` r 65 | install.packages("simIDM") 66 | ``` 67 | 68 | ### Development 69 | 70 | You can install the current development version from *GitHub* with: 71 | 72 | ``` r 73 | if (!require("remotes")) { 74 | install.packages("remotes") 75 | } 76 | remotes::install_github("insightsengineering/simIDM") 77 | ``` 78 | 79 | ## Getting Started 80 | 81 | See also the [quick 82 | start](https://insightsengineering.github.io/simIDM/main/articles/quickstart.html) 83 | vignette or get started by trying out this example: 84 | 85 | ``` r 86 | library(simIDM) 87 | transitionGroup1 <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 88 | transitionGroup2 <- exponential_transition(h01 = 1, h02 = 1.3, h12 = 1.7) 89 | 90 | simStudies <- getClinicalTrials( 91 | nRep = 100, nPat = c(50, 50), seed = 1234, datType = "1rowPatient", 92 | transitionByArm = list(transitionGroup1, transitionGroup2), dropout = list(rate = 0.1, time = 12), 93 | accrual = list(param = "intensity", value = 12) 94 | ) 95 | ``` 96 | 97 | We get as output a list with `nRep` elements, each containing a data set 98 | of a single simulated trial. 99 | 100 | ``` r 101 | head(simStudies[[1]]) 102 | #> id trt PFStime CensoredPFS PFSevent OStime CensoredOS OSevent 103 | #> 1 1 1 0.08087899 0 1 2.0330026 0 1 104 | #> 2 2 1 0.84758881 0 1 0.8475888 0 1 105 | #> 3 3 1 0.18276912 0 1 0.1968048 0 1 106 | #> 4 4 1 0.13789870 0 1 1.2899802 0 1 107 | #> 5 5 1 0.06458797 0 1 0.6901351 0 1 108 | #> 6 6 1 0.83894555 0 1 1.0709457 0 1 109 | #> recruitTime OStimeCal PFStimeCal 110 | #> 1 0.8516769 2.884679 0.9325558 111 | #> 2 4.1068045 4.954393 4.9543933 112 | #> 3 2.3596282 2.556433 2.5423973 113 | #> 4 1.1682298 2.458210 1.3061285 114 | #> 5 0.7710655 1.461201 0.8356535 115 | #> 6 3.1585892 4.229535 3.9975347 116 | ``` 117 | 118 | ## Citing `simIDM` 119 | 120 | To cite `simIDM` please see 121 | [here](https://insightsengineering.github.io/simIDM/main/authors.html#citation). 122 | -------------------------------------------------------------------------------- /README.rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | 8 | # simIDM 9 | 10 | ```{r, echo = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>", 14 | fig.path = "README-" 15 | ) 16 | ``` 17 | 18 | 19 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 20 | [![CRAN status](https://www.r-pkg.org/badges/version-last-release/simIDM)](https://www.r-pkg.org/badges/version-last-release/simIDM) 21 | [![CRAN monthly downloads](https://cranlogs.r-pkg.org/badges/simIDM)](https://cranlogs.r-pkg.org/badges/simIDM) 22 | [![CRAN total downloads](https://cranlogs.r-pkg.org/badges/grand-total/simIDM)](https://cranlogs.r-pkg.org/badges/grand-total/simIDM) 23 | [![Code Coverage](https://raw.githubusercontent.com/insightsengineering/simIDM/_xml_coverage_reports/data/main/badge.svg)](https://raw.githubusercontent.com/insightsengineering/simIDM/_xml_coverage_reports/data/main/coverage.xml) 24 | 25 | \ 26 | 27 | Survival multistate models are a powerful and flexible tool for modeling and analyzing complex time-to-event data. 28 | The three-state illness-death model can be used to jointly model the oncology endpoints 29 | progression-free survival (PFS) and overall survival (OS). Jointly modeling the endpoints PFS and OS with the illness-death 30 | model has the major 31 | advantage of both adequately accounting for the correlation of the two endpoints and eliminating the need of the strong 32 | assumption of proportional hazards. This package provides the tools to simulate a large number of clinical trials with 33 | endpoints OS and PFS based on the illness-death model, which can be used for trail planning, for example. The simulation 34 | set-up allows random and event-driven censoring, an arbitrary number of treatment arms, staggered study entry and drop-out. 35 | Exponentially, Weibull and piecewise exponentially distributed survival times can be generated. 36 | In addition, the correlation between PFS and OS can be calculated based on the simulation scenario, or estimated from a given data set. 37 | 38 | **Scope:** 39 | 40 | * Simulation of the illness-death model with constant, Weibull or piecewise constant transition hazards. 41 | * Conversion of the transition times to PFS and OS survival times. 42 | * Correlation between PFS and OS survival times can be calculated. 43 | 44 | **Main Features:** 45 | 46 | * Exponentially, Weibull and piecewise exponentially distributed survival times. 47 | * Random censoring and event-driven censoring after a pre-specified number of PFS or OS events. 48 | * Arbitrary number of treatment arms and flexible randomization ratio. 49 | * Staggered study entry. 50 | * Derivation of PFS and OS survival functions from transition hazards. 51 | * Correlation between PFS and OS can be estimated from a given data set. 52 | 53 | ## Installation 54 | 55 | ### Release 56 | 57 | You can install the current release version from *CRAN* with: 58 | 59 | ```{r cran-installation, eval = FALSE} 60 | install.packages("simIDM") 61 | ``` 62 | 63 | ### Development 64 | 65 | You can install the current development version from *GitHub* with: 66 | 67 | ```{r gh-installation, eval = FALSE} 68 | if (!require("remotes")) { 69 | install.packages("remotes") 70 | } 71 | remotes::install_github("insightsengineering/simIDM") 72 | ``` 73 | 74 | ## Getting Started 75 | 76 | See also the [quick start](https://insightsengineering.github.io/simIDM/main/articles/quickstart.html) vignette or get started by trying out this example: 77 | 78 | ```{r getting-started, results = 'hide'} 79 | library(simIDM) 80 | transitionGroup1 <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 81 | transitionGroup2 <- exponential_transition(h01 = 1, h02 = 1.3, h12 = 1.7) 82 | 83 | simStudies <- getClinicalTrials( 84 | nRep = 100, nPat = c(50, 50), seed = 1234, datType = "1rowPatient", 85 | transitionByArm = list(transitionGroup1, transitionGroup2), dropout = list(rate = 0.1, time = 12), 86 | accrual = list(param = "intensity", value = 12) 87 | ) 88 | ``` 89 | 90 | We get as output a list with `nRep` elements, each containing a data set of a single simulated trial. 91 | 92 | ```{r print} 93 | head(simStudies[[1]]) 94 | ``` 95 | 96 | 97 | ## Citing `simIDM` 98 | 99 | To cite `simIDM` please see [here](https://insightsengineering.github.io/simIDM/main/authors.html#citation). 100 | -------------------------------------------------------------------------------- /_pkgdown.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | url: https://insightsengineering.github.io/simIDM 3 | 4 | template: 5 | bootstrap: 5 6 | params: 7 | ganalytics: UA-125641273-1 8 | 9 | navbar: 10 | structure: 11 | left: [intro, reference, articles, tutorials, news, reports] 12 | right: [search, github] 13 | components: 14 | reports: 15 | text: Reports 16 | menu: 17 | - text: Coverage report 18 | href: coverage-report/ 19 | - text: Unit test report 20 | href: unit-test-report/ 21 | github: 22 | icon: fa-github 23 | href: https://github.com/insightsengineering/simIDM 24 | 25 | articles: 26 | - title: Introduction 27 | navbar: Introduction 28 | contents: 29 | - quickstart 30 | - trialplanning 31 | - correlation 32 | - title: Details 33 | navbar: Details 34 | contents: 35 | - pwc_survival 36 | reference: 37 | - title: Package 38 | contents: 39 | - simIDM-package 40 | - title: Assertions 41 | contents: 42 | - assert_positive_number 43 | - assert_intervals 44 | - title: Transition parameters 45 | contents: 46 | - exponential_transition 47 | - piecewise_exponential 48 | - weibull_transition 49 | - title: Helper functions for piecewise or Weibull Hazards 50 | contents: 51 | - getPWCHazard 52 | - getSumPCW 53 | - getPCWDistr 54 | - PCWInversionMethod 55 | - getWaitTimeSum 56 | - title: Simulation of a single data set for a single treatment arm 57 | contents: 58 | - getSimulatedData 59 | - getOneToTwoRows 60 | - addStaggeredEntry 61 | - title: Simulation of a large number of clinical trials 62 | contents: 63 | - getOneClinicalTrial 64 | - getDatasetWideFormat 65 | - getClinicalTrials 66 | - title: Event tracking 67 | contents: 68 | - getTimePoint 69 | - getCensoredData 70 | - censoringByNumberEvents 71 | - getEventsAll 72 | - getNumberEvents 73 | - trackEventsPerTrial 74 | - title: Survival functions 75 | contents: 76 | - ExpSurvPFS 77 | - ExpSurvOS 78 | - ExpQuantOS 79 | - WeibSurvPFS 80 | - WeibSurvOS 81 | - pwA 82 | - PWCsurvPFS 83 | - PWCsurvOS 84 | - title: Hazard functions 85 | contents: 86 | - ExpHazOS 87 | - avgHRIntegExpOS 88 | - avgHRExpOS 89 | - title: Empirical significance 90 | contents: 91 | - logRankTest 92 | - empSignificant 93 | - title: Parameter Estimation 94 | contents: 95 | - estimateParams 96 | - getInit 97 | - getResults 98 | - getTarget 99 | - prepareData 100 | - title: Likelihood 101 | contents: 102 | - haz 103 | - survTrans 104 | - negLogLik 105 | - title: PFS-OS Correlation 106 | contents: 107 | - corPFSOS 108 | - corTrans 109 | - expvalPFSInteg 110 | - expvalOSInteg 111 | - log_p11 112 | - survPFS 113 | - survPFSOS 114 | - survOS 115 | -------------------------------------------------------------------------------- /design/create_hex_logo.R: -------------------------------------------------------------------------------- 1 | pkg_color <- "red" 2 | fill_color <- "lightgrey" 3 | out_path <- file.path(getwd(), "man/figures/logo-large") 4 | out_file <- paste(out_path, "png", sep = ".") 5 | 6 | idm_plot <- function() { 7 | prodlim::plotIllnessDeathModel( 8 | style = 1, 9 | box1.label = "0", 10 | box2.label = "1", 11 | box3.label = "2", 12 | box1.col = "green", 13 | box2.col = "yellow", 14 | box3.col = "red", 15 | cex = 2 16 | ) 17 | } 18 | idm_plot() 19 | 20 | tmp <- tempdir() 21 | idm_fig <- file.path(tmp, "idm_plot.png") 22 | png(filename = idm_fig, res = 100, width = 500, height = 500, bg = "transparent") 23 | idm_plot() 24 | dev.off() 25 | 26 | plot(magick::image_read(idm_fig)) 27 | 28 | make_hexplot <- function() { 29 | require(hexSticker) 30 | require(showtext) 31 | 32 | hexSticker::sticker( 33 | subplot = idm_fig, 34 | package = "simIDM", 35 | p_size = 140, 36 | p_color = pkg_color, 37 | p_y = 1.5, 38 | s_y = 0.8, 39 | s_x = 0.9, 40 | s_width = 0.48, 41 | s_height = 0.48, 42 | h_fill = fill_color, 43 | h_color = pkg_color, 44 | h_size = 2, 45 | url = "github.com/insightsengineering/simIDM", 46 | u_size = 24, 47 | u_color = pkg_color, 48 | filename = out_file, 49 | p_family = "mono", 50 | # We need this white around sticker, otherwise the top and bottom of the 51 | # hexagon are not nice (too flat). 52 | white_around_sticker = TRUE, 53 | dpi = 2000 54 | ) 55 | # But therefore we now need to postprocess the image by transforming white 56 | # to transparent background. 57 | out_img <- magick::image_read(out_file) 58 | out_img <- magick::image_transparent(out_img, color = "white") 59 | magick::image_write(out_img, path = out_file) 60 | } 61 | 62 | make_hexplot() 63 | usethis::use_logo(out_file) 64 | -------------------------------------------------------------------------------- /design/design_simulatedTrial.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "getClinicalTrials" 3 | author: "Alexandra Erdmann" 4 | date: "1/26/2022" 5 | output: html_document 6 | --- 7 | 8 | ```{r setup, include=FALSE} 9 | knitr::opts_chunk$set(echo = TRUE) 10 | ``` 11 | 12 | ### Objective 13 | 14 | Simulate a large number of oncology clinical trials. 15 | Studies with an arbitrary number of arms are possible. 16 | 17 | 18 | ### Prototype getClinicalTrials 19 | 20 | ```{r,eval = FALSE} 21 | #' ... parameters for [getOneClinicalTrial()] - does not work!!! 22 | getClinicalTrials <- function(nRep, ..., seed = 1234, datType = "1rowTransition") { 23 | set.seed(seed) 24 | # getOneClinicalTrial generates one single clinical trial with multiple arms. Generate nRep simulated trials: 25 | simulatedTrials <- lapply( 26 | seq_len(nRep), 27 | FUN = function(x, ...) getOneClinicalTrial(...), 28 | ... 29 | ) 30 | # final data set format: one row per patient or one row per transition 31 | if (datType == "1rowPatient") { 32 | simulatedTrials <- lapply(simulatedTrials, getDatasetWideFormat) 33 | } 34 | return(simulatedTrials) 35 | } 36 | ``` 37 | 38 | 39 | ```{r} 40 | #' ... parameters for [getOneClinicalTrial()] 41 | getClinicalTrials <- function(nRep, seed = 1234, nPat, transitionByArm, dropout, accrual, datType = "1rowTransition") { 42 | set.seed(seed) 43 | # getOneClinicalTrial generates one single clinical trial with multiple arms. Generate nRep simulated trials: 44 | simulatedTrials <- replicate( 45 | nRep, 46 | getOneClinicalTrial(nPat, transitionByArm, dropout, accrual), 47 | simplify = FALSE 48 | ) 49 | # final data set format: one row per patient or one row per transition 50 | if (datType == "1rowPatient") { 51 | simulatedTrials <- lapply(simulatedTrials, getDatasetWideFormat) 52 | } 53 | return(simulatedTrials) 54 | } 55 | ``` 56 | 57 | ###Helper function: get one clinical trial 58 | 59 | ```{r prototype} 60 | getOneClinicalTrial <- function(nPat, transitionByArm, 61 | dropout = list(rate = 0, time = 12), 62 | accrual = list(param = "time", value = 0)) { 63 | assert_list(transitionByArm) 64 | nPat <- as.integer(nPat) 65 | nArm <- length(transitionByArm) 66 | 67 | assert_integer(nPat, 68 | lower = 1, 69 | any.missing = FALSE, 70 | all.missing = FALSE, 71 | len = nArm, 72 | ) 73 | # each loop simulates one trial arm 74 | # starting values for loop 75 | simdata <- NULL 76 | previousPts <- 0 77 | for (i in 1:nArm) { 78 | group <- getSimulatedData(nPat[i], transitionByArm[[i]], dropout, accrual) 79 | group$trt <- i 80 | group$id <- group$id + previousPts 81 | simdata <- rbind(simdata, group) 82 | previousPts <- previousPts + nPat[i] 83 | } 84 | return(simdata) 85 | } 86 | ``` 87 | 88 | 89 | ###helper function: get wide data format 90 | 91 | ```{r} 92 | #### get dataset one row per patient 93 | 94 | getDatasetWideFormat <- function(data) { 95 | ## recruitment time -> entryAct if from==0 96 | recruitTime <- subset(data[, c("id", "entryAct")], data$from == 0) 97 | names(recruitTime)[names(recruitTime) == "entryAct"] <- "recruitTime" 98 | 99 | ## time of to==2 or censoring time 100 | OStime <- subset(data[, c("id", "exit")], data$to == 2 | data$to == "cens") 101 | names(OStime)[names(OStime) == "exit"] <- "OStime" 102 | ## time of to==2 or to==1 or censored; 103 | PFStime <- subset(data[, c("id", "exit")], (data$to == 2 & data$from == 0) | (data$to == 1) | (data$to == "cens" & data$from == 0)) 104 | names(PFStime)[names(PFStime) == "exit"] <- "PFStime" 105 | 106 | ## censored 1=yes, 0=no 107 | censoredIdsPFS <- data$id[data$to == "cens" & data$from == 0] 108 | censoredIdsOS <- data$id[data$to == "cens"] 109 | id <- unique(data$id) 110 | CensoredOS <- cbind(id = id, CensoredOS = as.integer(id %in% censoredIdsOS)) 111 | CensoredPFS <- cbind(id = id, CensoredPFS = as.integer(id %in% censoredIdsPFS)) 112 | 113 | ### merge 114 | newdata <- unique(data[, c("id", "trt")]) 115 | newdata <- merge(x = newdata, y = PFStime, by = "id") 116 | newdata <- merge(x = newdata, y = CensoredPFS, by = "id") 117 | # PFSevent observed 118 | newdata$PFSevent <- abs(1 - newdata$CensoredPFS) 119 | newdata <- merge(x = newdata, y = OStime, by = "id") 120 | newdata <- merge(x = newdata, y = CensoredOS, by = "id") 121 | ## OS event observed 122 | newdata$OSevent <- abs(1 - newdata$CensoredOS) 123 | newdata <- merge(x = newdata, y = recruitTime, by = "id") 124 | 125 | ### calendar times 126 | newdata$OStimeCal <- newdata$OStime + newdata$recruitTime 127 | newdata$PFStimeCal <- newdata$PFStime + newdata$recruitTime 128 | 129 | return(newdata) 130 | } 131 | ``` 132 | 133 | 134 | ###Try it out 135 | ```{r} 136 | transition1 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 0.5, p01 = 1.2, p02 = 1.3, p12 = 0.5) 137 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1, p01 = 1.2, p02 = 1.3, p12 = 0.5) 138 | transition3 <- weibull_transition(h01 = 1, h02 = 1.8, h12 = 0.8, p01 = 1.2, p02 = 1.3, p12 = 1) 139 | 140 | transitionList <- list(transition1, transition2) 141 | transitionList2 <- list(transition1, transition2, transition3) 142 | 143 | testData <- getClinicalTrials( 144 | nRep = 10, nPat = c(20, 20), seed = 1234, datType = "1rowTransition", 145 | transitionByArm = transitionList, 146 | dropout = list(rate = 0.5, time = 12), accrual = list(param = "intensity", value = 7) 147 | ) 148 | 149 | testData2 <- getClinicalTrials( 150 | nRep = 10, nPat = c(20, 20, 20), 151 | transitionByArm = transitionList2, 152 | dropout = list(rate = 0.5, time = 12), accrual = list(param = "intensity", value = 7), 153 | seed = 1234, datType = "1rowPatient" 154 | ) 155 | 156 | test1 <- getOneClinicalTrial(c(20, 20), transitionList, 157 | dropout = list(rate = 0.5, time = 12), 158 | accrual = list(param = "time", value = 0) 159 | ) 160 | ``` 161 | 162 | -------------------------------------------------------------------------------- /design/design_test-getSimulatedData.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "test for getSimulatedData" 3 | author: "Alexandra Erdmann" 4 | date: "4/8/2022" 5 | output: html_document 6 | --- 7 | 8 | ```{r setup, include=FALSE} 9 | knitr::opts_chunk$set(echo = TRUE) 10 | ``` 11 | 12 | 13 | ## Objective 14 | 15 | Create a test that is able to check whether the transition hazards of the MSM lead to the specified distribution of 16 | survival times. 17 | Idea: Compare true the Nelson-Aalen estimator (can be derived from specified transitions) with the mean Nelson-Aalen over 1000 simulations. Or: empirical distribution of survivla times (without censoring)? 18 | Use `skip_on_ci()` as the long running test should be skiped on GitHub. 19 | 20 | 21 | 22 | ##Compare TRUE Nelson-Aalen with simulated 23 | 24 | ```{r} 25 | library(mvna) 26 | 27 | nRep <- 1000 28 | N <- 200 29 | 30 | tra <- matrix(ncol = 3, nrow = 3, FALSE) 31 | tra[1, 2:3] <- TRUE 32 | tra[2, c(3)] <- TRUE 33 | 34 | 35 | ## averaged Nelson-Aalen 36 | estimateNA <- function(simTrials, tr.choice, times) { 37 | EstimatedNA <- lapply(seq_along(simTrials), function(j) { 38 | na <- NULL 39 | na <- mvna(simTrials[[j]], c("0", "1", "2"), tra, "cens") 40 | na_predict <- predict(na, times, 41 | tr.choice = c(tr.choice), 42 | level = 0.95, var.type = c("aalen"), ci.fun = c("log") 43 | )[[tr.choice]][, "na"] 44 | return(na_predict) 45 | }) 46 | EstimatedNAMean <- rowMeans(matrix(unlist(EstimatedNA), ncol = length(EstimatedNA))) 47 | return(EstimatedNAMean) 48 | } 49 | 50 | 51 | 52 | ### constant transition hazards 53 | startTime <- Sys.time() 54 | simTrials1 <- replicate(nRep, getSimulatedData(N, 55 | transition = exponential_transition(h01 = 0.3, h02 = 0.4, h12 = 0.4), 56 | dropout = list(rate = 0, time = 12), 57 | accrual = list(param = "time", value = 0) 58 | ), simplify = FALSE) 59 | 60 | times <- seq(0, 20, 0.1) 61 | Const12 <- estimateNA(simTrials1, "1 2", times) 62 | Const01 <- estimateNA(simTrials1, "0 1", times) 63 | 64 | # 1->2 transition 65 | ## true alpha 66 | trueAlpha <- 0.4 67 | trueNA <- trueAlpha * times 68 | #### plot 69 | plot(times, Const12, type = "l") 70 | lines(times, trueNA, col = "red") 71 | 72 | 73 | # 0->1 transition 74 | ## true alpha 75 | trueAlpha <- 0.3 76 | trueNA <- trueAlpha * times 77 | #### plot 78 | plot(times, Const01, type = "l") 79 | lines(times, trueNA, col = "red") 80 | endTime <- Sys.time() 81 | 82 | # prints recorded time 83 | print(endTime - startTime) 84 | ``` 85 | 86 | 87 | Add for Weibull and piecewise transition hazards. True Nelson-Aalen estimators: 88 | 89 | ```{r} 90 | # Weibull 91 | trueNA <- (trueAlpha * times)^p 92 | 93 | ### PW constant 94 | truePW <- function(alpha, pw, times) { 95 | if (times <= pw[2]) { 96 | return(alpha[1] * (times - pw[1])) 97 | } else if (times <= pw[3]) { 98 | return(alpha[1] * (pw[2] - pw[1]) + alpha[2] * (times - pw[2])) 99 | } else { 100 | return(alpha[1] * (pw[2] - pw[1]) + alpha[2] * (pw[3] - pw[2]) + alpha[3] * (times - pw[3])) 101 | } 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | Beyersmann 2 | Bonferroni 3 | Bové 4 | Sabanés 5 | CensoredOS 6 | CensoredPFS 7 | Cov 8 | Hoffmann 9 | IDM 10 | Meller 11 | Multistate 12 | NaN 13 | OSevent 14 | OStime 15 | OStimeCal 16 | PFS 17 | PFSevent 18 | PFStime 19 | PFStimeCal 20 | Rpact 21 | Sabanes 22 | Schoenfeld 23 | Ulm 24 | Unif 25 | calender 26 | cdot 27 | censAct 28 | doi 29 | dotsb 30 | dotsc 31 | du 32 | entryAct 33 | exitAct 34 | frac 35 | funder 36 | geq 37 | ik 38 | infty 39 | integrand 40 | lapply 41 | mathbb 42 | multistate 43 | neq 44 | pre 45 | recruitTime 46 | rightarrow 47 | summand 48 | th 49 | trt 50 | -------------------------------------------------------------------------------- /man/ExpHazOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hazardFunctions.R 3 | \name{ExpHazOS} 4 | \alias{ExpHazOS} 5 | \title{OS Hazard Function from Constant Transition Hazards} 6 | \usage{ 7 | ExpHazOS(t, h01, h02, h12) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 13 | 14 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 15 | 16 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition.} 17 | } 18 | \value{ 19 | This returns the value of the OS hazard function at time t. 20 | } 21 | \description{ 22 | OS Hazard Function from Constant Transition Hazards 23 | } 24 | \examples{ 25 | ExpHazOS(c(1:5), 0.2, 1.1, 0.8) 26 | } 27 | -------------------------------------------------------------------------------- /man/ExpQuantOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{ExpQuantOS} 4 | \alias{ExpQuantOS} 5 | \title{Quantile function for OS survival function induced by an illness-death model} 6 | \usage{ 7 | ExpQuantOS(q = 1/2, h01, h02, h12) 8 | } 9 | \arguments{ 10 | \item{q}{(\code{numeric})\cr quantile(s) at which to compute event time (q = 1 / 2 corresponds to median).} 11 | 12 | \item{h01}{(\verb{numeric vector})\cr constant transition hazards for 0 to 1 transition.} 13 | 14 | \item{h02}{(\verb{numeric vector})\cr constant transition hazards for 0 to 2 transition.} 15 | 16 | \item{h12}{(\verb{numeric vector})\cr constant transition hazards for 1 to 2 transition.} 17 | } 18 | \value{ 19 | This returns the time(s) t such that the OS survival function at t equals q. 20 | } 21 | \description{ 22 | Quantile function for OS survival function induced by an illness-death model 23 | } 24 | \examples{ 25 | ExpQuantOS(1 / 2, 0.2, 0.5, 2.1) 26 | } 27 | -------------------------------------------------------------------------------- /man/ExpSurvOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{ExpSurvOS} 4 | \alias{ExpSurvOS} 5 | \title{OS Survival Function from Constant Transition Hazards} 6 | \usage{ 7 | ExpSurvOS(t, h01, h02, h12) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 13 | 14 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 15 | 16 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition.} 17 | } 18 | \value{ 19 | This returns the value of OS survival function at time t. 20 | } 21 | \description{ 22 | OS Survival Function from Constant Transition Hazards 23 | } 24 | \examples{ 25 | ExpSurvOS(c(1:5), 0.2, 0.4, 0.1) 26 | } 27 | -------------------------------------------------------------------------------- /man/ExpSurvPFS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{ExpSurvPFS} 4 | \alias{ExpSurvPFS} 5 | \title{PFS Survival Function from Constant Transition Hazards} 6 | \usage{ 7 | ExpSurvPFS(t, h01, h02) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 13 | 14 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 15 | } 16 | \value{ 17 | This returns the value of PFS survival function at time t. 18 | } 19 | \description{ 20 | PFS Survival Function from Constant Transition Hazards 21 | } 22 | \examples{ 23 | ExpSurvPFS(c(1:5), 0.2, 0.4) 24 | } 25 | -------------------------------------------------------------------------------- /man/PCWInversionMethod.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/piecewiseDistribution.R 3 | \name{PCWInversionMethod} 4 | \alias{PCWInversionMethod} 5 | \title{Single Piecewise Exponentially Distributed Event Time} 6 | \usage{ 7 | PCWInversionMethod(haz, pw, LogU) 8 | } 9 | \arguments{ 10 | \item{haz}{(\code{numeric})\cr piecewise constant hazard.} 11 | 12 | \item{pw}{(\code{numeric})\cr time intervals for the piecewise constant hazard.} 13 | 14 | \item{LogU}{(\code{numeric})\cr transformed uniformly distributed random variables (log(1-U)).} 15 | } 16 | \value{ 17 | This returns one single event time. 18 | } 19 | \description{ 20 | This returns an event time with a distribution resulting from piece-wise constant hazards 21 | using the inversion method. 22 | } 23 | \examples{ 24 | PCWInversionMethod(haz = c(1.1, 0.5, 0.4), pw = c(0, 7, 10), LogU = log(1 - runif(1))) 25 | } 26 | -------------------------------------------------------------------------------- /man/PFSOSInteg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{PFSOSInteg} 4 | \alias{PFSOSInteg} 5 | \title{Helper Function for \code{survPFSOS()}} 6 | \usage{ 7 | PFSOSInteg(u, t, transition) 8 | } 9 | \arguments{ 10 | \item{u}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{t}{(\code{numeric})\cr time at which the value of the PFS*OS survival function is to be computed.} 13 | 14 | \item{transition}{(\code{TransitionParameters})\cr 15 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 16 | } 17 | \value{ 18 | Numeric result of the integrand used to calculate the PFS*OS survival function. 19 | } 20 | \description{ 21 | Helper Function for \code{survPFSOS()} 22 | } 23 | \note{ 24 | Not all vectors \code{u} and \code{t} work here due to assertions in \code{\link[=log_p11]{log_p11()}}. 25 | } 26 | \keyword{internal} 27 | -------------------------------------------------------------------------------- /man/PWCsurvOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{PWCsurvOS} 4 | \alias{PWCsurvOS} 5 | \title{OS Survival Function from Piecewise Constant Hazards} 6 | \usage{ 7 | PWCsurvOS(t, h01, h02, h12, pw01, pw02, pw12) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(\verb{numeric vector})\cr constant transition hazards for 0 to 1 transition.} 13 | 14 | \item{h02}{(\verb{numeric vector})\cr constant transition hazards for 0 to 2 transition.} 15 | 16 | \item{h12}{(\verb{numeric vector})\cr constant transition hazards for 1 to 2 transition.} 17 | 18 | \item{pw01}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h01}.} 19 | 20 | \item{pw02}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h02}.} 21 | 22 | \item{pw12}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h12}.} 23 | } 24 | \value{ 25 | This returns the value of OS survival function at time t. 26 | } 27 | \description{ 28 | OS Survival Function from Piecewise Constant Hazards 29 | } 30 | \examples{ 31 | PWCsurvOS(1:5, c(0.3, 0.5), c(0.5, 0.8), c(0.7, 1), c(0, 4), c(0, 8), c(0, 3)) 32 | } 33 | -------------------------------------------------------------------------------- /man/PWCsurvPFS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{PWCsurvPFS} 4 | \alias{PWCsurvPFS} 5 | \title{PFS Survival Function from Piecewise Constant Hazards} 6 | \usage{ 7 | PWCsurvPFS(t, h01, h02, pw01, pw02) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(\verb{numeric vector})\cr constant transition hazards for 0 to 1 transition.} 13 | 14 | \item{h02}{(\verb{numeric vector})\cr constant transition hazards for 0 to 2 transition.} 15 | 16 | \item{pw01}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h01}.} 17 | 18 | \item{pw02}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h02}.} 19 | } 20 | \value{ 21 | This returns the value of PFS survival function at time t. 22 | } 23 | \description{ 24 | PFS Survival Function from Piecewise Constant Hazards 25 | } 26 | \examples{ 27 | PWCsurvPFS(1:5, c(0.3, 0.5), c(0.5, 0.8), c(0, 4), c(0, 8)) 28 | } 29 | -------------------------------------------------------------------------------- /man/PwcOSInt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{PwcOSInt} 4 | \alias{PwcOSInt} 5 | \title{Helper Function of \code{PWCsurvOS()}} 6 | \usage{ 7 | PwcOSInt(x, t, h01, h02, h12, pw01, pw02, pw12) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{t}{(\code{numeric})\cr study time-points.} 13 | 14 | \item{h01}{(\verb{numeric vector})\cr constant transition hazards for 0 to 1 transition.} 15 | 16 | \item{h02}{(\verb{numeric vector})\cr constant transition hazards for 0 to 2 transition.} 17 | 18 | \item{h12}{(\verb{numeric vector})\cr constant transition hazards for 1 to 2 transition.} 19 | 20 | \item{pw01}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h01}.} 21 | 22 | \item{pw02}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h02}.} 23 | 24 | \item{pw12}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h12}.} 25 | } 26 | \value{ 27 | Numeric results of the integrand used to calculate 28 | the OS survival function for piecewise constant transition hazards, see \code{PWCsurvOS}. 29 | } 30 | \description{ 31 | Helper Function of \code{PWCsurvOS()} 32 | } 33 | \keyword{internal} 34 | -------------------------------------------------------------------------------- /man/WeibOSInteg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{WeibOSInteg} 4 | \alias{WeibOSInteg} 5 | \title{Helper Function for \code{WeibSurvOS()}} 6 | \usage{ 7 | WeibOSInteg(x, t, h01, h02, h12, p01, p02, p12) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{t}{(\code{numeric})\cr study time-points.} 13 | 14 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 15 | 16 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 17 | 18 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition.} 19 | 20 | \item{p01}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h01}.} 21 | 22 | \item{p02}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h02}.} 23 | 24 | \item{p12}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h12}.} 25 | } 26 | \value{ 27 | Numeric results of the integrand used to calculate 28 | the OS survival function for Weibull transition hazards, see \code{WeibSurvOS()}. 29 | } 30 | \description{ 31 | Helper Function for \code{WeibSurvOS()} 32 | } 33 | \keyword{internal} 34 | -------------------------------------------------------------------------------- /man/WeibSurvOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{WeibSurvOS} 4 | \alias{WeibSurvOS} 5 | \title{OS Survival Function from Weibull Transition Hazards} 6 | \usage{ 7 | WeibSurvOS(t, h01, h02, h12, p01, p02, p12) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 13 | 14 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 15 | 16 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition.} 17 | 18 | \item{p01}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h01}.} 19 | 20 | \item{p02}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h02}.} 21 | 22 | \item{p12}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h12}.} 23 | } 24 | \value{ 25 | This returns the value of OS survival function at time t. 26 | } 27 | \description{ 28 | OS Survival Function from Weibull Transition Hazards 29 | } 30 | \examples{ 31 | WeibSurvOS(c(1:5), 0.2, 0.5, 2.1, 1.2, 0.9, 1) 32 | } 33 | -------------------------------------------------------------------------------- /man/WeibSurvPFS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{WeibSurvPFS} 4 | \alias{WeibSurvPFS} 5 | \title{PFS Survival Function from Weibull Transition Hazards} 6 | \usage{ 7 | WeibSurvPFS(t, h01, h02, p01, p02) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 13 | 14 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 15 | 16 | \item{p01}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h01}.} 17 | 18 | \item{p02}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h02}.} 19 | } 20 | \value{ 21 | This returns the value of PFS survival function at time t. 22 | } 23 | \description{ 24 | PFS Survival Function from Weibull Transition Hazards 25 | } 26 | \examples{ 27 | WeibSurvPFS(c(1:5), 0.2, 0.5, 1.2, 0.9) 28 | } 29 | -------------------------------------------------------------------------------- /man/addStaggeredEntry.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getSimulatedData.R 3 | \name{addStaggeredEntry} 4 | \alias{addStaggeredEntry} 5 | \title{Staggered Study Entry} 6 | \usage{ 7 | addStaggeredEntry(simData, N, accrualParam, accrualValue) 8 | } 9 | \arguments{ 10 | \item{simData}{(\code{data.frame})\cr simulated data frame containing entry and exit times 11 | at individual study time scale. See \code{\link[=getSimulatedData]{getSimulatedData()}} for details.} 12 | 13 | \item{N}{(\code{int})\cr number of patients.} 14 | 15 | \item{accrualParam}{(\code{string})\cr possible values are 'time' or 'intensity'.} 16 | 17 | \item{accrualValue}{(\code{number})\cr specifies the accrual intensity. For \code{accrualParam} equal time, 18 | it describes the length of the accrual period. For \code{accrualParam} equal intensity, it describes 19 | the number of patients recruited per time unit. If \code{accrualValue} is equal to 0, 20 | all patients start at calendar time 0 21 | in the initial state.} 22 | } 23 | \value{ 24 | This returns a data set containing a single simulated study containing accrual times, 25 | i.e. staggered study entry. 26 | This is a helper function of \code{\link[=getSimulatedData]{getSimulatedData()}}. 27 | } 28 | \description{ 29 | This function adds staggered study entry times to a simulated data set with illness-death model structure. 30 | } 31 | \examples{ 32 | simData <- data.frame( 33 | id = c(1, 1, 2, 3), from = c(0, 1, 0, 0), to = c(1, 2, "cens", 2), 34 | entry = c(0, 3, 0, 0), 35 | exit = c(3, 5.3, 5.6, 7.2), censTime = c(6.8, 6.8, 5.6, 9.4) 36 | ) 37 | addStaggeredEntry(simData, 3, accrualParam = "time", accrualValue = 5) 38 | } 39 | -------------------------------------------------------------------------------- /man/assert_intervals.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/assertions.R 3 | \name{assert_intervals} 4 | \alias{assert_intervals} 5 | \title{Assertion for vector describing intervals} 6 | \usage{ 7 | assert_intervals(x, y) 8 | } 9 | \arguments{ 10 | \item{x}{what to check.} 11 | 12 | \item{y}{(\code{count})\cr required length of \code{y}.} 13 | } 14 | \value{ 15 | Raises an error if \code{x} is not an intervals vector starting with 0. 16 | } 17 | \description{ 18 | We define an intervals vector to always start with 0, and contain 19 | unique ordered time points. 20 | } 21 | \examples{ 22 | assert_intervals(c(0, 5, 7), 3) 23 | } 24 | -------------------------------------------------------------------------------- /man/assert_positive_number.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/assertions.R 3 | \name{assert_positive_number} 4 | \alias{assert_positive_number} 5 | \title{Assertion for Positive Number} 6 | \usage{ 7 | assert_positive_number(x, zero_ok = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{what to check.} 11 | 12 | \item{zero_ok}{(\code{flag})\cr whether \code{x} can be zero or not.} 13 | } 14 | \value{ 15 | Raises an error if \code{x} is not a single positive (or non-negative) number. 16 | } 17 | \description{ 18 | Assertion for Positive Number 19 | } 20 | \examples{ 21 | assert_positive_number(3.2) 22 | assert_positive_number(0, zero_ok = TRUE) 23 | } 24 | -------------------------------------------------------------------------------- /man/avgHRExpOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hazardFunctions.R 3 | \name{avgHRExpOS} 4 | \alias{avgHRExpOS} 5 | \title{Average OS Hazard Ratio from Constant Transition Hazards} 6 | \usage{ 7 | avgHRExpOS(transitionByArm, alpha = 0.5, upper = Inf) 8 | } 9 | \arguments{ 10 | \item{transitionByArm}{(\code{list})\cr transition parameters for each treatment group. 11 | See \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=piecewise_exponential]{piecewise_exponential()}} and \code{\link[=weibull_transition]{weibull_transition()}} for details.} 12 | 13 | \item{alpha}{(\code{number})\cr assigns weights to time points, where values higher than 0.5 assign greater weight 14 | to earlier times and values lower than 0.5 assign greater weight to later times.} 15 | 16 | \item{upper}{(\code{number})\cr upper (time) limit of integration.} 17 | } 18 | \value{ 19 | This returns the value of the average hazard ratio. 20 | } 21 | \description{ 22 | Average OS Hazard Ratio from Constant Transition Hazards 23 | } 24 | \examples{ 25 | transitionTrt <- exponential_transition(h01 = 0.18, h02 = 0.06, h12 = 0.17) 26 | transitionCtl <- exponential_transition(h01 = 0.23, h02 = 0.07, h12 = 0.19) 27 | transitionList <- list(transitionCtl, transitionTrt) 28 | avgHRExpOS(transitionByArm = transitionList, alpha = 0.5, upper = 100) 29 | } 30 | -------------------------------------------------------------------------------- /man/avgHRIntegExpOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hazardFunctions.R 3 | \name{avgHRIntegExpOS} 4 | \alias{avgHRIntegExpOS} 5 | \title{Helper Function for \code{avgHRExpOS()}} 6 | \usage{ 7 | avgHRIntegExpOS(x, h01, h02, h12, h0, h1, alpha) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 13 | 14 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 15 | 16 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition.} 17 | 18 | \item{h0}{(\code{list})\cr transition parameters for the first treatment group.} 19 | 20 | \item{h1}{(\code{list})\cr transition parameters for the second treatment group.} 21 | 22 | \item{alpha}{(\code{number})\cr weight parameter, see \code{\link[=avgHRExpOS]{avgHRExpOS()}}.} 23 | } 24 | \value{ 25 | This returns the value of the integrand used to calculate 26 | the average hazard ratio for constant transition hazards, see \code{\link[=avgHRExpOS]{avgHRExpOS()}}. 27 | } 28 | \description{ 29 | It is an integrand of the form OS hazard function with intensities h01, h02, h12 30 | at time point t multiplied with a weighted product of the two OS Survival functions 31 | at t (one for intensities h0 and one for h1). 32 | } 33 | \examples{ 34 | h0 <- list(h01 = 0.18, h02 = 0.06, h12 = 0.17) 35 | h1 <- list(h01 = 0.23, h02 = 0.07, h12 = 0.19) 36 | avgHRIntegExpOS(x = 5, h01 = 0.2, h02 = 0.5, h12 = 0.7, h0 = h0, h1 = h1, alpha = 0.5) 37 | } 38 | -------------------------------------------------------------------------------- /man/censoringByNumberEvents.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eventTracking.R 3 | \name{censoringByNumberEvents} 4 | \alias{censoringByNumberEvents} 5 | \title{Event-driven censoring.} 6 | \usage{ 7 | censoringByNumberEvents(data, eventNum, typeEvent) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr illness-death data set in \verb{1rowPatient} format.} 11 | 12 | \item{eventNum}{(\code{int})\cr number of events.} 13 | 14 | \item{typeEvent}{(\code{string})\cr type of event. Possible values are \code{PFS} and \code{OS}.} 15 | } 16 | \value{ 17 | This function returns a data set that is censored after \code{eventNum} of 18 | \code{typeEvent}-events occurred. 19 | } 20 | \description{ 21 | This function censors a study after a pre-specified number of events occurred. 22 | } 23 | \examples{ 24 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 25 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 26 | 27 | simStudy <- getOneClinicalTrial( 28 | nPat = c(20, 20), transitionByArm = list(transition1, transition2), 29 | dropout = list(rate = 0.3, time = 10), 30 | accrual = list(param = "time", value = 7) 31 | ) 32 | simStudyWide <- getDatasetWideFormat(simStudy) 33 | censoringByNumberEvents(data = simStudyWide, eventNum = 20, typeEvent = "PFS") 34 | } 35 | -------------------------------------------------------------------------------- /man/corPFSOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{corPFSOS} 4 | \alias{corPFSOS} 5 | \title{Correlation of PFS and OS event times for data from the IDM} 6 | \usage{ 7 | corPFSOS( 8 | data, 9 | transition, 10 | bootstrap = TRUE, 11 | bootstrap_n = 100, 12 | conf_level = 0.95 13 | ) 14 | } 15 | \arguments{ 16 | \item{data}{(\code{data.frame})\cr in the format produced by \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}}.} 17 | 18 | \item{transition}{(\code{TransitionParameters} object)\cr specifying the assumed distribution of transition hazards. 19 | Initial parameters for optimization can be specified here. 20 | See \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 21 | 22 | \item{bootstrap}{(\code{flag})\cr if \code{TRUE} computes confidence interval via bootstrap.} 23 | 24 | \item{bootstrap_n}{(\code{count})\cr number of bootstrap samples.} 25 | 26 | \item{conf_level}{(\code{proportion})\cr confidence level for the confidence interval.} 27 | } 28 | \value{ 29 | The correlation of PFS and OS. 30 | } 31 | \description{ 32 | Correlation of PFS and OS event times for data from the IDM 33 | } 34 | \examples{ 35 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 36 | data <- getClinicalTrials( 37 | nRep = 1, nPat = c(100), seed = 1234, datType = "1rowTransition", 38 | transitionByArm = list(transition), dropout = list(rate = 0.5, time = 12), 39 | accrual = list(param = "intensity", value = 7) 40 | )[[1]] 41 | corPFSOS(data, transition = exponential_transition(), bootstrap = FALSE) 42 | \dontrun{ 43 | corPFSOS(data, transition = exponential_transition(), bootstrap = TRUE) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /man/corTrans.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{corTrans} 4 | \alias{corTrans} 5 | \title{Correlation of PFS and OS event times for Different Transition Models} 6 | \usage{ 7 | corTrans(transition) 8 | } 9 | \arguments{ 10 | \item{transition}{(\code{TransitionParameters})\cr 11 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 12 | } 13 | \value{ 14 | The correlation of PFS and OS. 15 | } 16 | \description{ 17 | Correlation of PFS and OS event times for Different Transition Models 18 | } 19 | \examples{ 20 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 21 | corTrans(transition) 22 | } 23 | -------------------------------------------------------------------------------- /man/empSignificant.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/empSignificant.R 3 | \name{empSignificant} 4 | \alias{empSignificant} 5 | \title{Empirical Significance for a List of Simulated Trials} 6 | \usage{ 7 | empSignificant(simTrials, criticalPFS, criticalOS, eventNumPFS, eventNumOS) 8 | } 9 | \arguments{ 10 | \item{simTrials}{(\code{list})\cr simulated trial data sets, see \code{\link[=getClinicalTrials]{getClinicalTrials()}}.} 11 | 12 | \item{criticalPFS}{(positive \code{number})\cr critical value of the log-rank test for PFS.} 13 | 14 | \item{criticalOS}{(positive \code{number})\cr critical value of the log-rank test for OS.} 15 | 16 | \item{eventNumPFS}{(\code{integer})\cr number of PFS events required to trigger PFS analysis.} 17 | 18 | \item{eventNumOS}{(\code{integer})\cr number of OS events required to trigger OS analysis.} 19 | } 20 | \value{ 21 | This returns values of four measures of empirical significance. 22 | } 23 | \description{ 24 | This function computes four types of empirical significance — PFS, OS, at-least (significant 25 | in at least one of PFS/OS), and joint (significant in both PFS and OS) — using 26 | the log-rank test. Empirical significance is calculated as the proportion of significant 27 | results in simulated trials, each ending when a set number of PFS/OS events occur. 28 | Critical values for PFS and OS test significance must be specified. If trials simulate equal 29 | transition hazards across groups (H0), empirical significance estimates type I error; 30 | if they simulate differing transition hazards (H1), it estimates power. 31 | } 32 | \examples{ 33 | transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 34 | transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 35 | simTrials <- getClinicalTrials( 36 | nRep = 50, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 37 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 38 | accrual = list(param = "intensity", value = 7) 39 | ) 40 | empSignificant( 41 | simTrials = simTrials, criticalPFS = 2.4, criticalOS = 2.2, 42 | eventNumPFS = 300, eventNumOS = 500 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /man/estimateParams.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{estimateParams} 4 | \alias{estimateParams} 5 | \title{Estimate Parameters of the Multistate Model Using Clinical Trial Data} 6 | \usage{ 7 | estimateParams(data, transition) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr in the format produced by \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}}.} 11 | 12 | \item{transition}{(\code{TransitionParameters} object)\cr specifying the assumed distribution of transition hazards. 13 | Initial parameters for optimization can be specified here. 14 | See \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 15 | } 16 | \value{ 17 | Returns a \code{TransitionParameters} object with the estimated parameters. 18 | } 19 | \description{ 20 | Estimate Parameters of the Multistate Model Using Clinical Trial Data 21 | } 22 | \details{ 23 | This function estimates parameters for transition models using clinical trial data. 24 | The \code{transition} object can be initialized with starting values for parameter estimation. 25 | It uses \code{\link[stats:optim]{stats::optim()}} to optimize the parameters. 26 | } 27 | \examples{ 28 | transition <- exponential_transition(h01 = 2, h02 = 1.4, h12 = 1.6) 29 | simData <- getOneClinicalTrial( 30 | nPat = c(30), transitionByArm = list(transition), 31 | dropout = list(rate = 0.3, time = 12), 32 | accrual = list(param = "time", value = 1) 33 | ) 34 | # Initialize transition with desired starting values for optimization: 35 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 36 | estimate <- estimateParams(simData, transition) 37 | } 38 | -------------------------------------------------------------------------------- /man/exponential_transition.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/transitionParameters.R 3 | \name{exponential_transition} 4 | \alias{exponential_transition} 5 | \title{Transition Hazards for Exponential Event Times} 6 | \usage{ 7 | exponential_transition(h01 = 1, h02 = 1, h12 = 1) 8 | } 9 | \arguments{ 10 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition.} 11 | 12 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition.} 13 | 14 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition.} 15 | } 16 | \value{ 17 | List with elements \code{hazards}, \code{intervals}, \code{weibull_rates} and \code{family} 18 | (exponential). 19 | } 20 | \description{ 21 | This creates a list with class \code{TransitionParameters} containing 22 | hazards, time intervals and Weibull rates for exponential event times 23 | in an illness-death model. 24 | } 25 | \examples{ 26 | exponential_transition(1, 1.6, 0.3) 27 | } 28 | -------------------------------------------------------------------------------- /man/expvalOSInteg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{expvalOSInteg} 4 | \alias{expvalOSInteg} 5 | \title{Helper Function for Computing E(OS^2)} 6 | \usage{ 7 | expvalOSInteg(x, transition) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{transition}{(\code{TransitionParameters})\cr 13 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 14 | } 15 | \value{ 16 | Numeric results of the integrand used to calculate E(OS^2). 17 | } 18 | \description{ 19 | Helper Function for Computing E(OS^2) 20 | } 21 | \examples{ 22 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 23 | expvalOSInteg(0.4, transition) 24 | } 25 | -------------------------------------------------------------------------------- /man/expvalPFSInteg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{expvalPFSInteg} 4 | \alias{expvalPFSInteg} 5 | \title{Helper Function for Computing E(PFS^2)} 6 | \usage{ 7 | expvalPFSInteg(x, transition) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{transition}{(\code{TransitionParameters})\cr 13 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 14 | } 15 | \value{ 16 | Numeric results of the integrand used to calculate E(PFS^2). 17 | } 18 | \description{ 19 | Helper Function for Computing E(PFS^2) 20 | } 21 | \examples{ 22 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 23 | expvalPFSInteg(0.4, transition) 24 | } 25 | -------------------------------------------------------------------------------- /man/getCensoredData.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eventTracking.R 3 | \name{getCensoredData} 4 | \alias{getCensoredData} 5 | \title{Helper function for \code{censoringByNumberEvents}} 6 | \usage{ 7 | getCensoredData(time, event, data) 8 | } 9 | \arguments{ 10 | \item{time}{(\code{numeric}) \cr event times.} 11 | 12 | \item{event}{(\code{numeric})\cr event indicator.} 13 | 14 | \item{data}{(\code{data.frame})\cr data frame including patient id \code{id}, recruiting time \code{recruitTime} 15 | and individual censoring time \code{censTimeInd}.} 16 | } 17 | \value{ 18 | This function returns a data frame with columns: 19 | event time, censoring indicator, event indicator and event time 20 | in calendar time. 21 | } 22 | \description{ 23 | Helper function for \code{censoringByNumberEvents} 24 | } 25 | \examples{ 26 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 27 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 28 | 29 | simStudy <- getOneClinicalTrial( 30 | nPat = c(20, 20), transitionByArm = list(transition1, transition2), 31 | dropout = list(rate = 0.3, time = 10), 32 | accrual = list(param = "time", value = 7) 33 | ) 34 | simStudyWide <- getDatasetWideFormat(simStudy) 35 | simStudyWide$censTimeInd <- 5 - simStudyWide$recruitTime 36 | NotRecruited <- simStudyWide$id[simStudyWide$censTimeInd < 0] 37 | censoredData <- simStudyWide[!(simStudyWide$id \%in\% NotRecruited), ] 38 | getCensoredData(time = censoredData$OStime, event = censoredData$OSevent, data = censoredData) 39 | } 40 | -------------------------------------------------------------------------------- /man/getClinicalTrials.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getClinicalTrials.R 3 | \name{getClinicalTrials} 4 | \alias{getClinicalTrials} 5 | \title{Simulation of a Large Number of Oncology Clinical Trials} 6 | \usage{ 7 | getClinicalTrials(nRep, ..., seed = 1234, datType = "1rowTransition") 8 | } 9 | \arguments{ 10 | \item{nRep}{(\code{int})\cr number of simulated trials.} 11 | 12 | \item{...}{parameters transferred to \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}}, see \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}} for details.} 13 | 14 | \item{seed}{(\code{int})\cr random seed used for this simulation.} 15 | 16 | \item{datType}{(\code{string})\cr possible values are \verb{1rowTransition} and \verb{1rowPatient}.} 17 | } 18 | \value{ 19 | This function returns a list with \code{nRep} simulated data sets in the format specified by \code{datType}. 20 | See \code{\link[=getDatasetWideFormat]{getDatasetWideFormat()}} \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}} for details. 21 | } 22 | \description{ 23 | Simulation of a Large Number of Oncology Clinical Trials 24 | } 25 | \examples{ 26 | transition1 <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 27 | transition2 <- exponential_transition(h01 = 1, h02 = 1.3, h12 = 1.7) 28 | getClinicalTrials( 29 | nRep = 10, nPat = c(20, 20), seed = 1234, datType = "1rowTransition", 30 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 31 | accrual = list(param = "intensity", value = 7) 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /man/getDatasetWideFormat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getClinicalTrials.R 3 | \name{getDatasetWideFormat} 4 | \alias{getDatasetWideFormat} 5 | \title{Conversion of a Data Set from One Row per Transition to One Row per Patient} 6 | \usage{ 7 | getDatasetWideFormat(data) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr data frame containing entry and exit times of an illness-death model. 11 | See \code{\link[=getSimulatedData]{getSimulatedData()}} for details.} 12 | } 13 | \value{ 14 | This function returns a data set with one row per patient and endpoints PFS and OS. 15 | } 16 | \description{ 17 | Conversion of a Data Set from One Row per Transition to One Row per Patient 18 | } 19 | \details{ 20 | The output data set contains the following columns: 21 | \itemize{ 22 | \item id (\code{integer}): patient id. 23 | \item trt \code{integer}): treatment id. 24 | \item PFStime (\code{numeric}): event time of PFS event. 25 | \item CensoredPFS (\code{logical}): censoring indicator for PFS event. 26 | \item PFSevent (\code{logical}): event indicator for PFS event. 27 | \item OStime (\code{numeric}): event time of OS event. 28 | \item CensoredOS (\code{logical}): censoring indicator for OS event. 29 | \item OSevent (\code{logical}): event indicator for OS event. 30 | \item recruitTime (\code{numeric}): time of recruitment. 31 | \item OStimeCal (\code{numeric}): OS event time at calendar time scale. 32 | \item PFStimeCal (\code{numeric}): PFS event time at calendar time scale. 33 | } 34 | } 35 | \examples{ 36 | transition1 <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 37 | transition2 <- exponential_transition(h01 = 1, h02 = 1.3, h12 = 1.7) 38 | transition3 <- exponential_transition(h01 = 1.1, h02 = 1, h12 = 1.5) 39 | simData <- getOneClinicalTrial( 40 | nPat = c(30, 20, 30), transitionByArm = list(transition1, transition2, transition3), 41 | dropout = list(rate = 0, time = 12), 42 | accrual = list(param = "time", value = 0) 43 | ) 44 | getDatasetWideFormat(simData) 45 | } 46 | -------------------------------------------------------------------------------- /man/getEventsAll.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eventTracking.R 3 | \name{getEventsAll} 4 | \alias{getEventsAll} 5 | \title{Number of recruited/censored/ongoing Patients.} 6 | \usage{ 7 | getEventsAll(data, t) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr illness-death data set in \verb{1rowPatient} format.} 11 | 12 | \item{t}{(\code{numeric})\cr study time-point.} 13 | } 14 | \value{ 15 | This function returns number of recruited patients, 16 | number of censored and number of patients under observations. 17 | } 18 | \description{ 19 | Number of recruited/censored/ongoing Patients. 20 | } 21 | \examples{ 22 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 23 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 24 | 25 | simStudy <- getOneClinicalTrial( 26 | nPat = c(20, 20), transitionByArm = list(transition1, transition2), 27 | dropout = list(rate = 0.6, time = 10), 28 | accrual = list(param = "time", value = 0) 29 | ) 30 | simStudyWide <- getDatasetWideFormat(simStudy) 31 | getEventsAll(data = simStudyWide, t = 1.5) 32 | } 33 | -------------------------------------------------------------------------------- /man/getInit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{getInit} 4 | \alias{getInit} 5 | \alias{getInit.ExponentialTransition} 6 | \alias{getInit.WeibullTransition} 7 | \title{Retrieve Initial Parameter Vectors for Likelihood Maximization} 8 | \usage{ 9 | getInit(transition) 10 | 11 | \method{getInit}{ExponentialTransition}(transition) 12 | 13 | \method{getInit}{WeibullTransition}(transition) 14 | } 15 | \arguments{ 16 | \item{transition}{(\code{ExponentialTransition} or \code{WeibullTransition})\cr containing the initial parameters. 17 | See \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 18 | } 19 | \value{ 20 | The numeric vector of initial parameters for likelihood maximization. 21 | } 22 | \description{ 23 | Retrieve Initial Parameter Vectors for Likelihood Maximization 24 | } 25 | \section{Methods (by class)}{ 26 | \itemize{ 27 | \item \code{getInit(ExponentialTransition)}: for the Exponential Transition Model 28 | 29 | \item \code{getInit(WeibullTransition)}: for the Weibull Transition Model 30 | 31 | }} 32 | \examples{ 33 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 34 | getInit(transition) 35 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 36 | getInit(transition) 37 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 38 | getInit(transition) 39 | } 40 | -------------------------------------------------------------------------------- /man/getNumberEvents.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eventTracking.R 3 | \name{getNumberEvents} 4 | \alias{getNumberEvents} 5 | \title{Helper Function for \code{trackEventsPerTrial}} 6 | \usage{ 7 | getNumberEvents(event, time, t) 8 | } 9 | \arguments{ 10 | \item{event}{(\code{numeric})\cr event indicator.} 11 | 12 | \item{time}{(\code{numeric}) \cr event times.} 13 | 14 | \item{t}{(\code{numeric})\cr study time-point.} 15 | } 16 | \value{ 17 | This function returns the number of events occurred until time t. 18 | } 19 | \description{ 20 | Helper Function for \code{trackEventsPerTrial} 21 | } 22 | \examples{ 23 | event <- c(0, 1, 1, 1, 0) 24 | time <- c(3, 3.4, 5, 6, 5.5) 25 | getNumberEvents(event = event, time = time, t = 5) 26 | } 27 | -------------------------------------------------------------------------------- /man/getOneClinicalTrial.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getClinicalTrials.R 3 | \name{getOneClinicalTrial} 4 | \alias{getOneClinicalTrial} 5 | \title{Simulation of a Single Oncology Clinical Trial} 6 | \usage{ 7 | getOneClinicalTrial( 8 | nPat, 9 | transitionByArm, 10 | dropout = list(rate = 0, time = 12), 11 | accrual = list(param = "time", value = 0) 12 | ) 13 | } 14 | \arguments{ 15 | \item{nPat}{(\code{integer})\cr numbers of patients per treatment arm.} 16 | 17 | \item{transitionByArm}{(\code{list}) \cr transition parameters for each treatment group. 18 | See \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=piecewise_exponential]{piecewise_exponential()}} and \code{\link[=weibull_transition]{weibull_transition()}} for details.} 19 | 20 | \item{dropout}{dropout (\code{list})\cr specifies drop-out probability. See \code{\link[=getSimulatedData]{getSimulatedData()}} for details. 21 | Can be specified either as one list that should be applied to all treatment groups or a separate list 22 | for each treatment group.} 23 | 24 | \item{accrual}{accrual (\code{list})\cr specifies accrual intensity. See \code{\link[=addStaggeredEntry]{addStaggeredEntry()}} for details. 25 | Can be specified either as one list that should be applied to all treatment groups or a separate list 26 | for each treatment group.} 27 | } 28 | \value{ 29 | This returns a data frame with one simulated clinical trial and multiple treatment arms. 30 | See \code{\link[=getSimulatedData]{getSimulatedData()}} for the explanation of the columns. The column \code{trt} contains the treatment indicator. 31 | This is a helper function of \code{\link[=getClinicalTrials]{getClinicalTrials()}}. 32 | } 33 | \description{ 34 | This function creates a data set with a single simulated oncology clinical trial with one row per transition 35 | based on an illness-death model. Studies with an arbitrary number of treatment arms are possible. 36 | } 37 | \examples{ 38 | transition1 <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 39 | transition2 <- exponential_transition(h01 = 1, h02 = 1.3, h12 = 1.7) 40 | transition3 <- exponential_transition(h01 = 1.1, h02 = 1, h12 = 1.5) 41 | getOneClinicalTrial( 42 | nPat = c(30, 20, 30), transitionByArm = list(transition1, transition2, transition3), 43 | dropout = list(rate = 0, time = 12), 44 | accrual = list(param = "time", value = 0) 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /man/getOneToTwoRows.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getSimulatedData.R 3 | \name{getOneToTwoRows} 4 | \alias{getOneToTwoRows} 5 | \title{Transitions from the Intermediate State to the Absorbing State} 6 | \usage{ 7 | getOneToTwoRows(simDataOne, transition) 8 | } 9 | \arguments{ 10 | \item{simDataOne}{(\code{data.frame})\cr a data frame containing all patients with transitions 11 | into the intermediate state. See \code{\link[=getSimulatedData]{getSimulatedData()}} for details.} 12 | 13 | \item{transition}{(\code{TransitionParameters})\cr transition parameters comprising 14 | \code{hazards}, corresponding \code{intervals} and \code{weibull_rates}, see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=piecewise_exponential]{piecewise_exponential()}} 15 | and \code{\link[=weibull_transition]{weibull_transition()}} for details.} 16 | } 17 | \value{ 18 | This returns a data frame with one row per patient for the second transition, 19 | i.e. the transition out of the intermediate 20 | state. This is a helper function of \code{\link[=getSimulatedData]{getSimulatedData()}}. 21 | } 22 | \description{ 23 | This function creates transition entry and exit times from the intermediate state to the absorbing state 24 | for an existing data frame containing the exit times out of the initial state. 25 | } 26 | \examples{ 27 | simDataOne <- data.frame( 28 | id = c(1:3), to = c(1, 1, 1), from = c(0, 0, 0), entry = c(0, 0, 0), 29 | exit = c(3, 5.6, 7.2), censTime = c(6.8, 5.9, 9.4) 30 | ) 31 | transition <- exponential_transition(1, 1.6, 0.3) 32 | getOneToTwoRows(simDataOne, transition) 33 | } 34 | -------------------------------------------------------------------------------- /man/getPCWDistr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/piecewiseDistribution.R 3 | \name{getPCWDistr} 4 | \alias{getPCWDistr} 5 | \title{Piecewise Exponentially Distributed Event Times} 6 | \usage{ 7 | getPCWDistr(U, haz, pw, t_0) 8 | } 9 | \arguments{ 10 | \item{U}{(\code{numeric})\cr uniformly distributed random variables.} 11 | 12 | \item{haz}{(\code{numeric})\cr piecewise constant hazard.} 13 | 14 | \item{pw}{(\code{numeric})\cr time intervals for the piecewise constant hazard.} 15 | 16 | \item{t_0}{(\code{numeric})\cr the starting times.} 17 | } 18 | \value{ 19 | This returns a vector with event times. 20 | } 21 | \description{ 22 | This returns event times with a distribution resulting from piece-wise constant hazards 23 | using the inversion method. 24 | } 25 | \examples{ 26 | getPCWDistr(U = runif(3), haz = c(1.1, 0.5, 0.4), pw = c(0, 7, 10), t_0 = c(0, 1, 4.2)) 27 | } 28 | -------------------------------------------------------------------------------- /man/getPWCHazard.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/piecewiseHazards.R 3 | \name{getPWCHazard} 4 | \alias{getPWCHazard} 5 | \title{Piecewise Constant Hazard Values} 6 | \usage{ 7 | getPWCHazard(haz, pw, x) 8 | } 9 | \arguments{ 10 | \item{haz}{(\code{numeric})\cr piecewise constant input hazard.} 11 | 12 | \item{pw}{(\code{numeric})\cr time intervals for the piecewise constant hazard.} 13 | 14 | \item{x}{(\code{numeric})\cr time-points.} 15 | } 16 | \value{ 17 | Hazard values at input time-points. 18 | } 19 | \description{ 20 | This returns piecewise constant hazard values at specified time points. 21 | } 22 | \examples{ 23 | getPWCHazard(c(1, 1.2, 1.4), c(0, 2, 3), c(1, 4, 6)) 24 | } 25 | -------------------------------------------------------------------------------- /man/getResults.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{getResults} 4 | \alias{getResults} 5 | \alias{getResults.ExponentialTransition} 6 | \alias{getResults.WeibullTransition} 7 | \title{Format Results of Parameter Estimation for Different Transition Models} 8 | \usage{ 9 | getResults(transition, res) 10 | 11 | \method{getResults}{ExponentialTransition}(transition, res) 12 | 13 | \method{getResults}{WeibullTransition}(transition, res) 14 | } 15 | \arguments{ 16 | \item{transition}{(\code{TransitionParameters})\cr 17 | see \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 18 | 19 | \item{res}{(\code{numeric} vector)\cr vector of parameter estimates from the likelihood maximization procedure.} 20 | } 21 | \value{ 22 | Returns a \code{TransitionParameters} object with parameter estimates. 23 | } 24 | \description{ 25 | Format Results of Parameter Estimation for Different Transition Models 26 | } 27 | \section{Methods (by class)}{ 28 | \itemize{ 29 | \item \code{getResults(ExponentialTransition)}: for the Exponential Transition Model 30 | 31 | \item \code{getResults(WeibullTransition)}: for the Weibull Transition Model 32 | 33 | }} 34 | \examples{ 35 | results <- c(1.2, 1.5, 1.6) 36 | getResults(exponential_transition(), results) 37 | results <- c(1.2, 1.5, 1.6) 38 | getResults(exponential_transition(), results) 39 | results <- c(1.2, 1.5, 1.6, 2, 2.5, 1) 40 | getResults(weibull_transition(), results) 41 | } 42 | -------------------------------------------------------------------------------- /man/getSimulatedData.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getSimulatedData.R 3 | \name{getSimulatedData} 4 | \alias{getSimulatedData} 5 | \title{Simulate Data Set from an Illness-Death Model} 6 | \usage{ 7 | getSimulatedData( 8 | N, 9 | transition = exponential_transition(h01 = 1, h02 = 1, h12 = 1), 10 | dropout = list(rate = 0, time = 12), 11 | accrual = list(param = "time", value = 0) 12 | ) 13 | } 14 | \arguments{ 15 | \item{N}{(\code{int})\cr number of patients.} 16 | 17 | \item{transition}{(\code{TransitionParameters})\cr transition parameters comprising 18 | \code{hazards}, corresponding \code{intervals} and \code{weibull_rates}, see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=piecewise_exponential]{piecewise_exponential()}} 19 | and \code{\link[=weibull_transition]{weibull_transition()}} for details.} 20 | 21 | \item{dropout}{(\code{list})\cr specifies drop-out probability. 22 | Random censoring times are generated using exponential distribution. \code{dropout$rate} specifies 23 | the drop-out probability per \code{dropout$time} time units. 24 | If \code{dropout$rate} is equal to 0, then no censoring is applied.} 25 | 26 | \item{accrual}{(\code{list})\cr specifies accrual intensity. See \code{\link[=addStaggeredEntry]{addStaggeredEntry()}} for details.} 27 | } 28 | \value{ 29 | This returns a data frame with one row per transition per individual. 30 | } 31 | \description{ 32 | This function creates a single simulated data set for a single treatment arm. It simulates data 33 | from an illness-death model with one row per transition and subject. 34 | } 35 | \details{ 36 | The output data set contains the following columns: 37 | \itemize{ 38 | \item id (\code{integer}): patient id. 39 | \item from (\code{numeric}): starting state of the transition. 40 | \item to (\code{character}): final state of the transition. 41 | \item entry (\code{numeric}): entry time of the transition on the individual time scale. 42 | \item exit (\code{numeric}): exit time of the transition on the individual time scale. 43 | \item entryAct (\code{numeric}): entry time of the transition on study time scale. 44 | \item exitAct (\code{numeric}): exit time of the transition on study time scale. 45 | \item censAct (\code{numeric}): censoring time of the individual on study time scale. 46 | } 47 | } 48 | \examples{ 49 | getSimulatedData( 50 | N = 10, 51 | transition = exponential_transition(h01 = 1, h02 = 1.5, h12 = 1), 52 | dropout = list(rate = 0.3, time = 1), 53 | accrual = list(param = "time", value = 5) 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /man/getSumPCW.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/piecewiseHazards.R 3 | \name{getSumPCW} 4 | \alias{getSumPCW} 5 | \title{Sum of Two Piecewise Constant Hazards} 6 | \usage{ 7 | getSumPCW(haz1, haz2, pw1, pw2) 8 | } 9 | \arguments{ 10 | \item{haz1}{(\code{numeric})\cr first summand (piecewise constant hazard).} 11 | 12 | \item{haz2}{(\code{numeric})\cr second summand (piecewise constant hazard).} 13 | 14 | \item{pw1}{(\code{numeric})\cr time intervals of first summand.} 15 | 16 | \item{pw2}{(\code{numeric})\cr time intervals of second summand.} 17 | } 18 | \value{ 19 | List with elements \code{hazards} and \code{intervals} for the sum of two piecewise constant hazards. 20 | } 21 | \description{ 22 | This returns the sum of two piecewise constant hazards per interval. 23 | } 24 | \examples{ 25 | getSumPCW(c(1.2, 0.3, 0.6), c(1.2, 0.7, 1), c(0, 8, 9), c(0, 1, 4)) 26 | } 27 | -------------------------------------------------------------------------------- /man/getTarget.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{getTarget} 4 | \alias{getTarget} 5 | \alias{getTarget.ExponentialTransition} 6 | \alias{getTarget.WeibullTransition} 7 | \title{Generate the Target Function for Optimization} 8 | \usage{ 9 | getTarget(transition) 10 | 11 | \method{getTarget}{ExponentialTransition}(transition) 12 | 13 | \method{getTarget}{WeibullTransition}(transition) 14 | } 15 | \arguments{ 16 | \item{transition}{(\code{TransitionParameters})\cr 17 | specifying the distribution family. See \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 18 | } 19 | \value{ 20 | Function that calculates the negative log-likelihood for the given parameters. 21 | } 22 | \description{ 23 | Generate the Target Function for Optimization 24 | } 25 | \details{ 26 | This function creates a target function for optimization, computing the negative log-likelihood for given 27 | parameters, data, and transition model type. 28 | } 29 | \section{Methods (by class)}{ 30 | \itemize{ 31 | \item \code{getTarget(ExponentialTransition)}: for the Exponential Transition Model 32 | 33 | \item \code{getTarget(WeibullTransition)}: for the Weibull Transition Model 34 | 35 | }} 36 | \examples{ 37 | transition <- exponential_transition(2, 1.3, 0.8) 38 | simData <- getOneClinicalTrial( 39 | nPat = c(30), transitionByArm = list(transition), 40 | dropout = list(rate = 0.8, time = 12), 41 | accrual = list(param = "time", value = 1) 42 | ) 43 | params <- c(1.2, 1.5, 1.6) # For ExponentialTransition 44 | data <- prepareData(simData) 45 | transition <- exponential_transition() 46 | fun <- getTarget(transition) 47 | fun(params, data) 48 | transition <- exponential_transition(2, 1.3, 0.8) 49 | simData <- getOneClinicalTrial( 50 | nPat = c(30), transitionByArm = list(transition), 51 | dropout = list(rate = 0.8, time = 12), 52 | accrual = list(param = "time", value = 1) 53 | ) 54 | params <- c(1.2, 1.5, 1.6) 55 | data <- prepareData(simData) 56 | transition <- exponential_transition() 57 | target <- getTarget(transition) 58 | target(params, data) 59 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 60 | simData <- getOneClinicalTrial( 61 | nPat = c(30), transitionByArm = list(transition), 62 | dropout = list(rate = 0.8, time = 12), 63 | accrual = list(param = "time", value = 1) 64 | ) 65 | params <- c(1.2, 1.5, 1.6, 0.8, 1.3, 1.1) 66 | data <- prepareData(simData) 67 | transition <- weibull_transition() 68 | target <- getTarget(transition) 69 | target(params, data) 70 | } 71 | -------------------------------------------------------------------------------- /man/getTimePoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eventTracking.R 3 | \name{getTimePoint} 4 | \alias{getTimePoint} 5 | \title{Time-point by which a specified number of events occurred.} 6 | \usage{ 7 | getTimePoint(data, eventNum, typeEvent, byArm = FALSE) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr illness-death data set in \verb{1rowPatient} format.} 11 | 12 | \item{eventNum}{(\code{int})\cr number of events.} 13 | 14 | \item{typeEvent}{(\code{string})\cr type of event. Possible values are \code{PFS} and \code{OS}.} 15 | 16 | \item{byArm}{(\code{logical})\cr if \code{TRUE} time-point per treatment arm, else joint evaluation 17 | of treatment arms.} 18 | } 19 | \value{ 20 | This returns the time-point by which \code{eventNum} of \code{typeEvent}-events occurred. 21 | } 22 | \description{ 23 | This returns the study time-point by which a specified number of events (PFS or OS) occurred. 24 | } 25 | \examples{ 26 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 27 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 28 | 29 | simStudy <- getOneClinicalTrial( 30 | nPat = c(20, 20), transitionByArm = list(transition1, transition2), 31 | dropout = list(rate = 0.3, time = 10), 32 | accrual = list(param = "time", value = 0) 33 | ) 34 | simStudyWide <- getDatasetWideFormat(simStudy) 35 | getTimePoint(simStudyWide, eventNum = 10, typeEvent = "OS", byArm = FALSE) 36 | } 37 | -------------------------------------------------------------------------------- /man/getWaitTimeSum.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getWaitTimeSum.R 3 | \name{getWaitTimeSum} 4 | \alias{getWaitTimeSum} 5 | \title{Event Times Distributed as Sum of Weibull} 6 | \usage{ 7 | getWaitTimeSum(U, haz1, haz2, p1, p2, entry) 8 | } 9 | \arguments{ 10 | \item{U}{(\code{numeric})\cr uniformly distributed random variables.} 11 | 12 | \item{haz1}{(positive \code{number})\cr first summand (constant hazard).} 13 | 14 | \item{haz2}{(positive \code{number})\cr second summand (constant hazard).} 15 | 16 | \item{p1}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{haz1}.} 17 | 18 | \item{p2}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{haz2}.} 19 | 20 | \item{entry}{(\code{numeric})\cr the entry times in the current state.} 21 | } 22 | \value{ 23 | This returns a vector with event times. 24 | } 25 | \description{ 26 | This returns event times with a distribution resulting from the sum of two Weibull distributed random variables 27 | using the inversion method. 28 | } 29 | \examples{ 30 | getWaitTimeSum(U = c(0.4, 0.5), haz1 = 0.8, haz2 = 1, p1 = 1.1, p2 = 1.5, entry = c(0, 0)) 31 | } 32 | -------------------------------------------------------------------------------- /man/haz.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{haz} 4 | \alias{haz} 5 | \alias{haz.ExponentialTransition} 6 | \alias{haz.WeibullTransition} 7 | \alias{haz.PWCTransition} 8 | \title{Hazard Function for Different Transition Models} 9 | \usage{ 10 | haz(transition, t, trans) 11 | 12 | \method{haz}{ExponentialTransition}(transition, t, trans) 13 | 14 | \method{haz}{WeibullTransition}(transition, t, trans) 15 | 16 | \method{haz}{PWCTransition}(transition, t, trans) 17 | } 18 | \arguments{ 19 | \item{transition}{(\code{ExponentialTransition} or \code{WeibullTransition})\cr 20 | see \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 21 | 22 | \item{t}{(\code{numeric})\cr time at which hazard is to be computed.} 23 | 24 | \item{trans}{(\code{integer})\cr index specifying the transition type.} 25 | } 26 | \value{ 27 | The hazard rate for the specified transition and time. 28 | } 29 | \description{ 30 | Hazard Function for Different Transition Models 31 | } 32 | \details{ 33 | The transition types are: 34 | \itemize{ 35 | \item \code{1}: Transition from state 0 (stable) to 1 (progression). 36 | \item \code{2}: Transition from state 0 (stable) to 2 (death). 37 | \item \code{3}: Transition from state 1 (progression) to 2 (death). 38 | } 39 | } 40 | \section{Methods (by class)}{ 41 | \itemize{ 42 | \item \code{haz(ExponentialTransition)}: for an exponential transition model. 43 | 44 | \item \code{haz(WeibullTransition)}: for the Weibull transition model. 45 | 46 | \item \code{haz(PWCTransition)}: for the piecewise constant transition model. 47 | 48 | }} 49 | \examples{ 50 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 51 | haz(transition, 0.4, 2) 52 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 53 | haz(transition, 0.4, 2) 54 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 55 | haz(transition, 0.4, 2) 56 | transition <- piecewise_exponential( 57 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 58 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 59 | ) 60 | haz(transition, 6, 2) 61 | } 62 | -------------------------------------------------------------------------------- /man/integrateVector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{integrateVector} 4 | \alias{integrateVector} 5 | \title{Helper for Efficient Integration} 6 | \usage{ 7 | integrateVector(integrand, upper, ...) 8 | } 9 | \arguments{ 10 | \item{integrand}{(\code{function})\cr to be integrated.} 11 | 12 | \item{upper}{(\code{numeric})\cr upper limits of integration.} 13 | 14 | \item{...}{additional arguments to be passed to \code{integrand}.} 15 | } 16 | \value{ 17 | This function returns for each upper limit the estimates of the integral. 18 | } 19 | \description{ 20 | Helper for Efficient Integration 21 | } 22 | \keyword{internal} 23 | -------------------------------------------------------------------------------- /man/logRankTest.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/empSignificant.R 3 | \name{logRankTest} 4 | \alias{logRankTest} 5 | \title{Log-Rank Test for a Single Trial} 6 | \usage{ 7 | logRankTest(data, typeEvent = c("PFS", "OS"), critical) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr data frame containing entry and exit times of an 11 | illness-death model. See \code{\link[=getSimulatedData]{getSimulatedData()}} for details.} 12 | 13 | \item{typeEvent}{(\code{string})\cr endpoint to be evaluated, possible values are \code{PFS} and \code{OS}.} 14 | 15 | \item{critical}{(positive \code{number})\cr critical value of the log-rank test.} 16 | } 17 | \value{ 18 | Logical value indicating log-rank test significance. 19 | } 20 | \description{ 21 | This function evaluates the significance of either PFS or OS endpoints in a trial, 22 | based on a pre-specified critical value. 23 | } 24 | \examples{ 25 | transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 26 | transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 27 | simTrial <- getClinicalTrials( 28 | nRep = 1, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 29 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 30 | accrual = list(param = "intensity", value = 7) 31 | )[[1]] 32 | logRankTest(data = simTrial, typeEvent = "OS", critical = 3.4) 33 | } 34 | -------------------------------------------------------------------------------- /man/log_p11.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{log_p11} 4 | \alias{log_p11} 5 | \title{Probability of Remaining in Progression Between Two Time Points for Different Transition Models} 6 | \usage{ 7 | log_p11(transition, s, t) 8 | } 9 | \arguments{ 10 | \item{transition}{(\code{TransitionParameters})\cr 11 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 12 | 13 | \item{s}{(\code{numeric})\cr lower time points.} 14 | 15 | \item{t}{(\code{numeric})\cr higher time points.} 16 | } 17 | \value{ 18 | This returns the natural logarithm of the probability of remaining in progression (state 1) 19 | between two time points, conditional on being in state 1 at the lower time point. 20 | } 21 | \description{ 22 | Probability of Remaining in Progression Between Two Time Points for Different Transition Models 23 | } 24 | \examples{ 25 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 26 | log_p11(transition, 1, 3) 27 | } 28 | -------------------------------------------------------------------------------- /man/negLogLik.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{negLogLik} 4 | \alias{negLogLik} 5 | \title{Compute the Negative Log-Likelihood for a Given Data Set and Transition Model} 6 | \usage{ 7 | negLogLik(transition, data) 8 | } 9 | \arguments{ 10 | \item{transition}{(\code{ExponentialTransition} or \code{WeibullTransition})\cr 11 | see \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 12 | 13 | \item{data}{(\code{data.frame})\cr in the format created by \code{\link[=prepareData]{prepareData()}}.} 14 | } 15 | \value{ 16 | The value of the negative log-likelihood. 17 | } 18 | \description{ 19 | Compute the Negative Log-Likelihood for a Given Data Set and Transition Model 20 | } 21 | \details{ 22 | Calculates the negative log-likelihood for a given data set and transition model. It uses the hazard 23 | and survival functions specific to the transition model. 24 | } 25 | \examples{ 26 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 27 | simData <- getOneClinicalTrial( 28 | nPat = c(30), transitionByArm = list(transition), 29 | dropout = list(rate = 0.8, time = 12), 30 | accrual = list(param = "time", value = 1) 31 | ) 32 | negLogLik(transition, prepareData(simData)) 33 | } 34 | -------------------------------------------------------------------------------- /man/p11Integ.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{p11Integ} 4 | \alias{p11Integ} 5 | \title{Helper Function for \code{log_p11()}} 6 | \usage{ 7 | p11Integ(x, transition) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{numeric})\cr variable of integration.} 11 | 12 | \item{transition}{(\code{TransitionParameters})\cr 13 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 14 | } 15 | \value{ 16 | Hazard rate at the specified time for the transition from progression to death. 17 | } 18 | \description{ 19 | Helper Function for \code{log_p11()} 20 | } 21 | \keyword{internal} 22 | -------------------------------------------------------------------------------- /man/passedLogRank.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/empSignificant.R 3 | \name{passedLogRank} 4 | \alias{passedLogRank} 5 | \title{Helper function to conduct log-rank tests for either PFS or OS} 6 | \usage{ 7 | passedLogRank(simTrials, typeEvent, eventNum, critical) 8 | } 9 | \arguments{ 10 | \item{simTrials}{(\code{list})\cr simulated trial data sets, see \code{\link[=getClinicalTrials]{getClinicalTrials()}}.} 11 | 12 | \item{typeEvent}{(\code{string})\cr endpoint to be evaluated, possible values are \code{PFS} and \code{OS}.} 13 | 14 | \item{eventNum}{(\code{integer})\cr number of events required to trigger analysis.} 15 | 16 | \item{critical}{(positive \code{number})\cr critical value of the log-rank test.} 17 | } 18 | \value{ 19 | Logical vector indicating log-rank test significance for each trial. 20 | } 21 | \description{ 22 | This function evaluates the significance of either PFS or OS endpoints for each trial 23 | in a list of trials, based on a pre-specified critical value. 24 | } 25 | \examples{ 26 | transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 27 | transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 28 | simTrials <- getClinicalTrials( 29 | nRep = 50, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 30 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 31 | accrual = list(param = "intensity", value = 7) 32 | ) 33 | passedLogRank(simTrials = simTrials, typeEvent = "PFS", eventNum = 300, critical = 2.4) 34 | } 35 | \keyword{internal} 36 | -------------------------------------------------------------------------------- /man/piecewise_exponential.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/transitionParameters.R 3 | \name{piecewise_exponential} 4 | \alias{piecewise_exponential} 5 | \title{Transition Hazards for Piecewise Exponential Event Times} 6 | \usage{ 7 | piecewise_exponential(h01, h02, h12, pw01, pw02, pw12) 8 | } 9 | \arguments{ 10 | \item{h01}{(\verb{numeric vector})\cr constant transition hazards for 0 to 1 transition} 11 | 12 | \item{h02}{(\verb{numeric vector})\cr constant transition hazards for 0 to 2 transition} 13 | 14 | \item{h12}{(\verb{numeric vector})\cr constant transition hazards for 1 to 2 transition} 15 | 16 | \item{pw01}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h01}} 17 | 18 | \item{pw02}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h02}} 19 | 20 | \item{pw12}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards \code{h12}} 21 | } 22 | \value{ 23 | List with elements \code{hazards}, \code{intervals}, \code{weibull_rates} and \code{family} 24 | (piecewise exponential). 25 | } 26 | \description{ 27 | This creates a list with class \code{TransitionParameters} containing 28 | hazards, time intervals and Weibull rates for piecewise exponential event times 29 | in an illness-death model. 30 | } 31 | \examples{ 32 | piecewise_exponential( 33 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 34 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /man/prepareData.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{prepareData} 4 | \alias{prepareData} 5 | \title{Preparation of a Data Set to Compute Log-likelihood} 6 | \usage{ 7 | prepareData(data) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr containing entry and exit times of an illness-death model. 11 | See \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}} for details.} 12 | } 13 | \value{ 14 | This function returns a data set with one row per patient and transition, when the patient is at risk. 15 | } 16 | \description{ 17 | Preparation of a Data Set to Compute Log-likelihood 18 | } 19 | \details{ 20 | The output data set contains the following columns: 21 | \itemize{ 22 | \item id (\code{integer}): patient id. 23 | \item from (\code{integer}): start event state. 24 | \item to (\code{integer}): end event state. 25 | \item trans (\code{integer}): transition (1, 2 or 3) identifier 26 | \itemize{ 27 | \item \code{1}: Transition from state 0 (stable) to 1 (progression). 28 | \item \code{2}: Transition from state 0 (stable) to 2 (death). 29 | \item \code{3}: Transition from state 1 (progression) to 2 (death). 30 | } 31 | \item entry (\code{numeric}): time at which the patient begins to be at risk for the transition. 32 | \item exit (\code{numeric}): time at which the patient ends to be at risk for the transition. 33 | \item status (\code{logical}): event indicator for the transition. 34 | } 35 | } 36 | \examples{ 37 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 38 | simData <- getOneClinicalTrial( 39 | nPat = c(30), transitionByArm = list(transition), 40 | dropout = list(rate = 0.8, time = 12), 41 | accrual = list(param = "time", value = 1) 42 | ) 43 | prepareData(simData) 44 | } 45 | -------------------------------------------------------------------------------- /man/pwA.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{pwA} 4 | \alias{pwA} 5 | \title{Cumulative Hazard for Piecewise Constant Hazards} 6 | \usage{ 7 | pwA(t, haz, pw) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr study time-points.} 11 | 12 | \item{haz}{(\verb{numeric vector})\cr constant transition hazards.} 13 | 14 | \item{pw}{(\verb{numeric vector})\cr time intervals for the piecewise constant hazards.} 15 | } 16 | \value{ 17 | This returns the value of cumulative hazard at time t. 18 | } 19 | \description{ 20 | Cumulative Hazard for Piecewise Constant Hazards 21 | } 22 | \examples{ 23 | pwA(1:5, c(0.5, 0.9), c(0, 4)) 24 | } 25 | -------------------------------------------------------------------------------- /man/runTrial.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getClinicalTrials.R 3 | \name{runTrial} 4 | \alias{runTrial} 5 | \title{Helper Function for Adding Progress Bar to Trial Simulation} 6 | \usage{ 7 | runTrial(x, pb, ...) 8 | } 9 | \arguments{ 10 | \item{x}{(\code{int})\cr iteration index within lapply.} 11 | 12 | \item{...}{parameters transferred to \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}}, see \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}} for details.} 13 | } 14 | \value{ 15 | This returns the same as \code{\link[=getOneClinicalTrial]{getOneClinicalTrial()}}, but updates the progress bar. 16 | } 17 | \description{ 18 | Helper Function for Adding Progress Bar to Trial Simulation 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/simIDM-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \docType{package} 4 | \name{simIDM-package} 5 | \alias{simIDM} 6 | \alias{simIDM-package} 7 | \title{\code{simIDM} Package} 8 | \description{ 9 | \code{simIDM} simulates a survival multistate model that jointly models PFS and OS. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://github.com/insightsengineering/simIDM/} 15 | \item Report bugs at \url{https://github.com/insightsengineering/simIDM/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Alexandra Erdmann \email{alexandra.erdmann@uni-ulm.de} 21 | 22 | Authors: 23 | \itemize{ 24 | \item Kaspar Rufibach \email{kaspar.rufibach@roche.com} 25 | \item Holger Löwe \email{hbj.loewe@gmail.com} 26 | \item Daniel Sabanés Bové \email{daniel.sabanes_bove@roche.com} 27 | } 28 | 29 | Other contributors: 30 | \itemize{ 31 | \item F. Hoffmann-La Roche AG [copyright holder, funder] 32 | \item University of Ulm [copyright holder, funder] 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /man/singleExpQuantOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/survivalFunctions.R 3 | \name{singleExpQuantOS} 4 | \alias{singleExpQuantOS} 5 | \title{Helper Function for Single Quantile for OS Survival Function} 6 | \usage{ 7 | singleExpQuantOS(q, h01, h02, h12) 8 | } 9 | \arguments{ 10 | \item{q}{(\code{number})\cr single quantile at which to compute event time.} 11 | 12 | \item{h01}{(\verb{numeric vector})\cr constant transition hazards for 0 to 1 transition.} 13 | 14 | \item{h02}{(\verb{numeric vector})\cr constant transition hazards for 0 to 2 transition.} 15 | 16 | \item{h12}{(\verb{numeric vector})\cr constant transition hazards for 1 to 2 transition.} 17 | } 18 | \value{ 19 | Single time t such that the OS survival function at t equals q. 20 | } 21 | \description{ 22 | Helper Function for Single Quantile for OS Survival Function 23 | } 24 | \keyword{internal} 25 | -------------------------------------------------------------------------------- /man/survOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{survOS} 4 | \alias{survOS} 5 | \alias{survOS.ExponentialTransition} 6 | \alias{survOS.WeibullTransition} 7 | \alias{survOS.PWCTransition} 8 | \title{OS Survival Function for Different Transition Models} 9 | \usage{ 10 | survOS(transition, t) 11 | 12 | \method{survOS}{ExponentialTransition}(transition, t) 13 | 14 | \method{survOS}{WeibullTransition}(transition, t) 15 | 16 | \method{survOS}{PWCTransition}(transition, t) 17 | } 18 | \arguments{ 19 | \item{transition}{(\code{TransitionParameters})\cr 20 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 21 | 22 | \item{t}{(\code{numeric})\cr time at which the value of the OS survival function is to be computed.} 23 | } 24 | \value{ 25 | The value of the survival function for the specified transition and time. 26 | } 27 | \description{ 28 | OS Survival Function for Different Transition Models 29 | } 30 | \section{Methods (by class)}{ 31 | \itemize{ 32 | \item \code{survOS(ExponentialTransition)}: Survival Function for an exponential transition model. 33 | 34 | \item \code{survOS(WeibullTransition)}: Survival Function for a Weibull transition model. 35 | 36 | \item \code{survOS(PWCTransition)}: Survival Function for a piecewise constant transition model. 37 | 38 | }} 39 | \examples{ 40 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 41 | survOS(transition, 0.4) 42 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 43 | survOS(transition, 0.4) 44 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 45 | survOS(transition, 0.4) 46 | transition <- piecewise_exponential( 47 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 48 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 49 | ) 50 | survOS(transition, 0.4) 51 | } 52 | -------------------------------------------------------------------------------- /man/survPFS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{survPFS} 4 | \alias{survPFS} 5 | \alias{survPFS.ExponentialTransition} 6 | \alias{survPFS.WeibullTransition} 7 | \alias{survPFS.PWCTransition} 8 | \title{PFS Survival Function for Different Transition Models} 9 | \usage{ 10 | survPFS(transition, t) 11 | 12 | \method{survPFS}{ExponentialTransition}(transition, t) 13 | 14 | \method{survPFS}{WeibullTransition}(transition, t) 15 | 16 | \method{survPFS}{PWCTransition}(transition, t) 17 | } 18 | \arguments{ 19 | \item{transition}{(\code{TransitionParameters})\cr 20 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 21 | 22 | \item{t}{(\code{numeric})\cr time at which the value of the PFS survival function is to be computed.} 23 | } 24 | \value{ 25 | The value of the survival function for the specified transition and time. 26 | } 27 | \description{ 28 | PFS Survival Function for Different Transition Models 29 | } 30 | \section{Methods (by class)}{ 31 | \itemize{ 32 | \item \code{survPFS(ExponentialTransition)}: Survival Function for an exponential transition model. 33 | 34 | \item \code{survPFS(WeibullTransition)}: Survival Function for a Weibull transition model. 35 | 36 | \item \code{survPFS(PWCTransition)}: Survival Function for a piecewise constant transition model. 37 | 38 | }} 39 | \examples{ 40 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 41 | survPFS(transition, 0.4) 42 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 43 | survPFS(transition, 0.4) 44 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 45 | survPFS(transition, 0.4) 46 | transition <- piecewise_exponential( 47 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 48 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 49 | ) 50 | survPFS(transition, 0.4) 51 | } 52 | -------------------------------------------------------------------------------- /man/survPFSOS.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/corPFSOS.R 3 | \name{survPFSOS} 4 | \alias{survPFSOS} 5 | \title{Survival Function of the Product PFS*OS for Different Transition Models} 6 | \usage{ 7 | survPFSOS(t, transition) 8 | } 9 | \arguments{ 10 | \item{t}{(\code{numeric})\cr time at which the value of the PFS*OS survival function is to be computed.} 11 | 12 | \item{transition}{(\code{TransitionParameters})\cr 13 | see \code{\link[=exponential_transition]{exponential_transition()}}, \code{\link[=weibull_transition]{weibull_transition()}} or \code{\link[=piecewise_exponential]{piecewise_exponential()}} for details.} 14 | } 15 | \value{ 16 | This returns the value of PFS*OS survival function at time t. 17 | } 18 | \description{ 19 | Survival Function of the Product PFS*OS for Different Transition Models 20 | } 21 | \examples{ 22 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 23 | survPFSOS(0.4, transition) 24 | } 25 | -------------------------------------------------------------------------------- /man/survTrans.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/estimateParams.R 3 | \name{survTrans} 4 | \alias{survTrans} 5 | \alias{survTrans.ExponentialTransition} 6 | \alias{survTrans.WeibullTransition} 7 | \title{Survival Function for Different Transition Models} 8 | \usage{ 9 | survTrans(transition, t, trans) 10 | 11 | \method{survTrans}{ExponentialTransition}(transition, t, trans) 12 | 13 | \method{survTrans}{WeibullTransition}(transition, t, trans) 14 | } 15 | \arguments{ 16 | \item{transition}{(\code{ExponentialTransition} or \code{WeibullTransition})\cr 17 | see \code{\link[=exponential_transition]{exponential_transition()}} or \code{\link[=weibull_transition]{weibull_transition()}} for details.} 18 | 19 | \item{t}{(\code{numeric})\cr time at which survival probability is to be computed.} 20 | 21 | \item{trans}{(\code{integer})\cr index specifying the transition type.} 22 | } 23 | \value{ 24 | The survival probability for the specified transition and time. 25 | } 26 | \description{ 27 | Survival Function for Different Transition Models 28 | } 29 | \section{Methods (by class)}{ 30 | \itemize{ 31 | \item \code{survTrans(ExponentialTransition)}: for the Exponential Transition Model 32 | 33 | \item \code{survTrans(WeibullTransition)}: for the Weibull Transition Model 34 | 35 | }} 36 | \examples{ 37 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 38 | survTrans(transition, 0.4, 2) 39 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 40 | survTrans(transition, 0.4, 2) 41 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 42 | survTrans(transition, 0.4, 2) 43 | } 44 | -------------------------------------------------------------------------------- /man/trackEventsPerTrial.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/eventTracking.R 3 | \name{trackEventsPerTrial} 4 | \alias{trackEventsPerTrial} 5 | \title{Event tracking in an oncology trial.} 6 | \usage{ 7 | trackEventsPerTrial(data, timeP, byArm = FALSE) 8 | } 9 | \arguments{ 10 | \item{data}{(\code{data.frame})\cr illness-death data set in \verb{1rowPatient} format.} 11 | 12 | \item{timeP}{(\code{numeric})\cr vector of study time-points.} 13 | 14 | \item{byArm}{(\code{logical})\cr if \code{TRUE} time-point per treatment arm, else joint evaluation of treatment arms.} 15 | } 16 | \value{ 17 | This function returns a data frame including number of PFS events, number of OS events, 18 | number of recruited patients, number of censored patients and number of ongoing patients at \code{timeP}. 19 | } 20 | \description{ 21 | Event tracking in an oncology trial. 22 | } 23 | \examples{ 24 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 25 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 26 | 27 | simStudy <- getOneClinicalTrial( 28 | nPat = c(20, 20), transitionByArm = list(transition1, transition2), 29 | dropout = list(rate = 0.3, time = 10), 30 | accrual = list(param = "time", value = 0) 31 | ) 32 | simStudyWide <- getDatasetWideFormat(simStudy) 33 | trackEventsPerTrial(data = simStudyWide, timeP = 1.5, byArm = FALSE) 34 | } 35 | -------------------------------------------------------------------------------- /man/weibull_transition.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/transitionParameters.R 3 | \name{weibull_transition} 4 | \alias{weibull_transition} 5 | \title{Transition Hazards for Weibull Distributed Event Times} 6 | \usage{ 7 | weibull_transition(h01 = 1, h02 = 1, h12 = 1, p01 = 1, p02 = 1, p12 = 1) 8 | } 9 | \arguments{ 10 | \item{h01}{(positive \code{number})\cr transition hazard for 0 to 1 transition} 11 | 12 | \item{h02}{(positive \code{number})\cr transition hazard for 0 to 2 transition} 13 | 14 | \item{h12}{(positive \code{number})\cr transition hazard for 1 to 2 transition} 15 | 16 | \item{p01}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h01}} 17 | 18 | \item{p02}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h02}} 19 | 20 | \item{p12}{(positive \code{number})\cr rate parameter of Weibull distribution for \code{h12}} 21 | } 22 | \value{ 23 | List with elements \code{hazards}, \code{intervals}, \code{weibull_rates} and \code{family} 24 | (Weibull). 25 | } 26 | \description{ 27 | This creates a list with class \code{TransitionParameters} containing 28 | hazards, time intervals and Weibull rates for Weibull distributed event times 29 | in an illness-death model. 30 | } 31 | \examples{ 32 | weibull_transition(h01 = 1, h02 = 1.3, h12 = 0.5, p01 = 1.2, p02 = 1.3, p12 = 0.5) 33 | } 34 | -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | cloud.noindex 6 | data.sqlite 7 | *.html 8 | -------------------------------------------------------------------------------- /simIDM.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageCheckArgs: --as-cran 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | pkg_name <- "simIDM" 2 | library(pkg_name, character.only = TRUE) 3 | testthat::test_check(pkg_name) 4 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/estimateParams.md: -------------------------------------------------------------------------------- 1 | # prepareData works as expected 2 | 3 | Code 4 | actual 5 | Output 6 | id from to trans entry exit status 7 | 1 1 1 2 1 0.0 0.20 0 8 | 2 1 1 3 2 0.0 0.20 0 9 | 3 2 1 2 1 0.0 0.70 1 10 | 4 2 1 3 2 0.0 0.70 0 11 | 5 2 2 3 3 0.7 0.80 0 12 | 6 3 1 2 1 0.0 0.40 0 13 | 7 3 1 3 2 0.0 0.40 1 14 | 8 4 1 2 1 0.0 0.10 1 15 | 9 4 1 3 2 0.0 0.10 0 16 | 10 4 2 3 3 0.1 0.25 1 17 | 18 | -------------------------------------------------------------------------------- /tests/testthat/test-addStaggeredEntry.R: -------------------------------------------------------------------------------- 1 | # addStaggeredEntry ---- 2 | test_that("addStaggeredEntry works as expected if no staggered study entry is present", { 3 | SimData <- data.frame( 4 | id = c(1, 1, 2, 3), from = c(0, 1, 0, 0), to = c(1, 2, "cens", 2), entry = c(0, 3, 0, 0), 5 | exit = c(3, 5.3, 5.6, 7.2), censTime = c(6.8, 6.8, 5.6, 9.4) 6 | ) 7 | actual <- addStaggeredEntry(SimData, N = 3, accrualParam = "intensity", accrualValue = 0) 8 | expected <- cbind(SimData, entryAct = SimData$entry, exitAct = SimData$exit, censAct = SimData$censTime) 9 | expected[, c("censTime")] <- list(NULL) 10 | expect_equal(actual, expected) 11 | 12 | 13 | actual2 <- addStaggeredEntry(SimData, N = 3, accrualParam = "time", accrualValue = 0) 14 | expected2 <- cbind(SimData, entryAct = SimData$entry, exitAct = SimData$exit, censAct = SimData$censTime) 15 | expected2[, c("censTime")] <- list(NULL) 16 | expect_equal(actual2, expected2) 17 | }) 18 | 19 | 20 | test_that("addStaggeredEntry works as expected if staggered study entry is specified by accrual parameter time", { 21 | SimData <- data.frame( 22 | id = c(1, 1, 2, 3), from = c(0, 1, 0, 0), to = c(1, 2, "cens", 2), entry = c(0, 3, 0, 0), 23 | exit = c(3, 5.3, 5.6, 7.2), censTime = c(6.8, 6.8, 5.6, 9.4) 24 | ) 25 | actual <- addStaggeredEntry(SimData, N = 3, accrualParam = "time", accrualValue = 6) 26 | 27 | # accrual times have to be between 0 and 6. 28 | expect_true(all((actual$entryAct - actual$entry) <= 6)) 29 | expect_true(all((actual$entryAct - actual$entry) >= 0)) 30 | expect_true(all(actual$entryAct > actual$entry)) 31 | 32 | # the actual exit time is the individual exit time + the actual entry time at study scale. 33 | expect_equal(actual$exitAct - (actual$entryAct - actual$entry), actual$exit) 34 | }) 35 | 36 | 37 | 38 | test_that("addStaggeredEntry works as expected if staggered study entry is specified by accrual parameter intensity", { 39 | SimData <- data.frame( 40 | id = c(1, 1, 2, 3), from = c(0, 1, 0, 0), to = c(1, 2, "cens", 2), entry = c(0, 3, 0, 0), 41 | exit = c(3, 5.3, 5.6, 7.2), censTime = c(6.8, 6.8, 5.6, 9.4) 42 | ) 43 | actual <- addStaggeredEntry(SimData, N = 3, accrualParam = "intensity", accrualValue = 8) 44 | 45 | # Accrual times have to be between 0 and N/accrualValue. 46 | expValue <- 3 / 8 47 | expect_true(all((actual$entryAct - actual$entry) <= expValue)) 48 | expect_true(all((actual$entryAct - actual$entry) >= 0)) 49 | expect_true(all(actual$entryAct > actual$entry)) 50 | 51 | # The actual exit time is the individual exit time + the actual entry time at study scale. 52 | expect_equal(actual$exitAct - (actual$entryAct - actual$entry), actual$exit) 53 | }) 54 | -------------------------------------------------------------------------------- /tests/testthat/test-assertions.R: -------------------------------------------------------------------------------- 1 | # assert_positive_number ---- 2 | 3 | test_that("assert_positive_number works as expected", { 4 | expect_silent(assert_positive_number(1.4)) 5 | expect_silent(assert_positive_number(0, zero_ok = TRUE)) 6 | expect_error(assert_positive_number(0)) 7 | expect_error(assert_positive_number(-1)) 8 | expect_error(assert_positive_number("bla")) 9 | }) 10 | 11 | # assert_intervals ---- 12 | 13 | test_that("assert_intervals works as expected", { 14 | expect_silent(assert_intervals(c(0, 1, 3, 4), 4)) 15 | expect_silent(assert_intervals(c(0, 5.5, 7.7), 3)) 16 | expect_error(assert_intervals(c(0, 5.5, 7.7), 2)) 17 | expect_error(assert_intervals(c(1, 5.5, 7.7), 3)) 18 | expect_error(assert_intervals(c(0, 7, 7), 3)) 19 | expect_error(assert_intervals(c(0, 7, 2), 3)) 20 | }) 21 | -------------------------------------------------------------------------------- /tests/testthat/test-empSignificant.R: -------------------------------------------------------------------------------- 1 | # logRankTest ---- 2 | 3 | test_that("logRankTest works as expected", { 4 | transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 5 | transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 6 | simTrial <- getClinicalTrials( 7 | nRep = 1, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 8 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 9 | accrual = list(param = "intensity", value = 7) 10 | )[[1]] 11 | actual <- logRankTest(data = simTrial, typeEvent = "OS", critical = 3.4) 12 | expect_equal(actual, TRUE) 13 | 14 | actual2 <- logRankTest(data = simTrial, typeEvent = "PFS", critical = 6) 15 | expect_equal(actual2, FALSE) 16 | }) 17 | 18 | # passedLogRank ---- 19 | 20 | test_that("passedLogRank works as expected", { 21 | transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 22 | transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 23 | simTrials <- getClinicalTrials( 24 | nRep = 3, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 25 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 26 | accrual = list(param = "intensity", value = 7) 27 | ) 28 | actual <- passedLogRank(simTrials = simTrials, typeEvent = "PFS", eventNum = 300, critical = 2.4) 29 | expect_equal(actual, c(TRUE, TRUE, FALSE)) 30 | 31 | actual2 <- passedLogRank(simTrials = simTrials, typeEvent = "OS", eventNum = 300, critical = 2.4) 32 | expect_equal(actual2, c(FALSE, FALSE, FALSE)) 33 | }) 34 | 35 | # empSignificant ---- 36 | 37 | test_that("empSignificant works as expected", { 38 | transition1 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 39 | transition2 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 40 | simTrials <- getClinicalTrials( 41 | nRep = 50, nPat = c(800, 800), seed = 1234, datType = "1rowPatient", 42 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0.5, time = 12), 43 | accrual = list(param = "intensity", value = 7) 44 | ) 45 | actual <- empSignificant( 46 | simTrials = simTrials, criticalPFS = 2.4, criticalOS = 2.2, 47 | eventNumPFS = 300, eventNumOS = 500 48 | ) 49 | expect_equal(actual, list( 50 | "significantPFS" = 0.74, 51 | "significantOS" = 0.52, 52 | "significantAtLeastOne" = 0.78, 53 | "significantBoth" = 0.48 54 | )) 55 | }) 56 | -------------------------------------------------------------------------------- /tests/testthat/test-estimateParams.R: -------------------------------------------------------------------------------- 1 | # prepareData ---- 2 | 3 | test_that("prepareData works as expected", { 4 | # Create simIDM data for the 4 possible different transition scenarios. 5 | colnames <- c( 6 | "id", "trt", "PFStime", "CensoredPFS", "PFSevent", "OStime", 7 | "CensoredOS", "OSevent", "recruitTime", "OStimeCal", "PFStimeCal" 8 | ) 9 | patCens1 <- c(1, 1, 0.2, 1, 0, 0.2, 1, 0, 0.1, 0.3, 0.3) 10 | patCens2 <- c(2, 1, 0.7, 0, 1, 0.8, 1, 0, 1.2, 2, 1.9) 11 | pat13 <- c(3, 1, 0.4, 0, 1, 0.4, 0, 1, 0.4, 0.8, 0.8) 12 | pat123 <- c(4, 1, 0.1, 0, 1, 0.25, 0, 1, 0, 0.25, 0.1) 13 | df <- setNames(data.frame(rbind(patCens1, patCens2, pat13, pat123)), nm = colnames) 14 | actual <- prepareData(df) 15 | expect_snapshot(actual) 16 | }) 17 | 18 | # negLogLik ---- 19 | 20 | test_that("negLogLik works as expected for Exponential", { 21 | transition <- exponential_transition(2, 1.3, 0.8) 22 | data <- prepareData(getClinicalTrials( 23 | nRep = 1, nPat = 50, seed = 1234, datType = "1rowPatient", 24 | transitionByArm = list(transition), dropout = list(rate = 0.3, time = 12), 25 | accrual = list(param = "intensity", value = 7) 26 | )[[1]]) 27 | actual1 <- negLogLik(transition, data) 28 | expect_equal(actual1, 58.65772) 29 | }) 30 | 31 | test_that("negLogLik works as expected for Weibull", { 32 | transition <- weibull_transition(h01 = 0.2, h02 = 0.5, h12 = 1.6, p01 = 1, p02 = 2.5, p12 = 3) 33 | data <- prepareData(getClinicalTrials( 34 | nRep = 1, nPat = 50, seed = 1234, datType = "1rowPatient", 35 | transitionByArm = list(transition), dropout = list(rate = 0.3, time = 12), 36 | accrual = list(param = "intensity", value = 7) 37 | )[[1]]) 38 | actual2 <- negLogLik(transition, data) 39 | expect_equal(actual2, 54.644006) 40 | }) 41 | 42 | # haz ---- 43 | 44 | test_that("haz works as expected for Exponential", { 45 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 46 | 47 | actual1 <- haz(transition, 0.4, 2) 48 | expect_equal(actual1, 1.5) 49 | 50 | actual2 <- haz(transition, c(0.4, 3), c(2, 2)) 51 | expect_equal(actual2, c(1.5, 1.5)) 52 | }) 53 | 54 | test_that("haz works as expected for Weibull", { 55 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 56 | 57 | actual1 <- haz(transition, 0.4, 2) 58 | expect_equal(actual1, 0.9486833) 59 | 60 | actual2 <- haz(transition, c(0.4, 0.8), c(2, 1)) 61 | expect_equal(actual2, c(0.9486833, 1.92)) 62 | }) 63 | 64 | # survTrans---- 65 | 66 | test_that("survTrans works as expected for Exponential", { 67 | transition <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 68 | actual <- survTrans(transition, 0.4, 2) 69 | expect_equal(actual, 0.54881164) 70 | }) 71 | 72 | test_that("survTrans works as expected for Weibull", { 73 | transition <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 2, p02 = 2.5, p12 = 3) 74 | actual <- survTrans(transition, 0.1, 3) 75 | expect_equal(actual, 0.99840128) 76 | }) 77 | 78 | # getInit ---- 79 | 80 | test_that("getInit works as expected for Exponential", { 81 | transition <- exponential_transition(h01 = 2.2, h02 = 0.5, h12 = 1.3) 82 | actual <- getInit(transition) 83 | expect_equal(actual, c(2.2, 0.5, 1.3)) 84 | }) 85 | 86 | test_that("getInit works as expected for Weibull", { 87 | transition <- weibull_transition(h01 = 0.2, h02 = 0.5, h12 = 1.6, p01 = 1, p02 = 2.5, p12 = 3) 88 | actual <- getInit(transition) 89 | expect_equal(actual, c(0.2, 0.5, 1.6, 1, 2.5, 3)) 90 | }) 91 | 92 | # getTarget ---- 93 | 94 | test_that("getTarget works as expected for Exponential", { 95 | transition <- exponential_transition(2, 1.3, 0.8) 96 | data <- prepareData(getClinicalTrials( 97 | nRep = 1, nPat = 50, seed = 1234, datType = "1rowPatient", 98 | transitionByArm = list(transition), dropout = list(rate = 0.3, time = 12), 99 | accrual = list(param = "intensity", value = 7) 100 | )[[1]]) 101 | params <- c(1.2, 1.5, 1.6) 102 | target <- getTarget(transition) 103 | actual <- target(params, data) 104 | expect_equal(actual, 84.68301) 105 | }) 106 | 107 | test_that("getTarget works as expected for Weibull", { 108 | transition <- weibull_transition(h01 = 0.2, h02 = 0.5, h12 = 1.6, p01 = 1, p02 = 2.5, p12 = 3) 109 | data <- prepareData(getClinicalTrials( 110 | nRep = 1, nPat = 50, seed = 1234, datType = "1rowPatient", 111 | transitionByArm = list(transition), dropout = list(rate = 0.3, time = 12), 112 | accrual = list(param = "intensity", value = 7) 113 | )[[1]]) 114 | params <- c(1.2, 1.5, 1.6, 2, 1, 2) 115 | target <- getTarget(transition) 116 | actual <- target(params, data) 117 | expect_equal(actual, 103.6357444) 118 | }) 119 | 120 | # getResults ---- 121 | 122 | test_that("getResults works as expected for Exponential", { 123 | results <- c(1.2, 1.5, 1.6) 124 | actual <- getResults(exponential_transition(), results) 125 | expect_identical(actual$hazards, list(h01 = 1.2, h02 = 1.5, h12 = 1.6)) 126 | }) 127 | 128 | test_that("getResults works as expected for weibull", { 129 | results <- c(1.2, 1.5, 1.6, 2, 1, 0.5) 130 | actual <- getResults(weibull_transition(), results) 131 | expect_identical(actual$hazards, list(h01 = 1.2, h02 = 1.5, h12 = 1.6)) 132 | expect_identical(actual$weibull_rates, list(p01 = 2, p02 = 1, p12 = 0.5)) 133 | }) 134 | 135 | # estimateParams ---- 136 | 137 | test_that("estimateParams estimates the true parameters correctly for Exponential", { 138 | transition <- exponential_transition(2, 1.3, 0.8) 139 | data <- getClinicalTrials( 140 | nRep = 1, nPat = 100000, seed = 123, datType = "1rowPatient", 141 | transitionByArm = list(transition), dropout = list(rate = 0.3, time = 1), 142 | accrual = list(param = "intensity", value = 500) 143 | )[[1]] 144 | actual <- estimateParams(data, transition) 145 | expect_equal(actual$hazards, list(h01 = 2, h02 = 1.3, h12 = 0.8), tolerance = 1e-2) 146 | expect_identical(actual$weibull_rates, list(p01 = 1, p02 = 1, p12 = 1)) 147 | expect_identical(class(actual), c("ExponentialTransition", "TransitionParameters")) 148 | }) 149 | 150 | test_that("estimateParams estimates the true parameters correctly for Weibull", { 151 | transition <- weibull_transition(h01 = 0.4, h02 = 0.9, h12 = 1.6, p01 = 1, p02 = 0.5, p12 = 1.9) 152 | data <- getClinicalTrials( 153 | nRep = 1, nPat = 100000, seed = 123, datType = "1rowPatient", 154 | transitionByArm = list(transition), dropout = list(rate = 0.3, time = 1), 155 | accrual = list(param = "intensity", value = 500) 156 | )[[1]] 157 | actual <- estimateParams(data, transition) 158 | expect_equal(actual$hazards, list(h01 = 0.4, h02 = 0.9, h12 = 1.6), tolerance = 1e-2) 159 | expect_equal(actual$weibull_rates, list(p01 = 1, p02 = 0.5, p12 = 1.9), tolerance = 1e-2) 160 | expect_identical(class(actual), c("WeibullTransition", "TransitionParameters")) 161 | }) 162 | -------------------------------------------------------------------------------- /tests/testthat/test-eventTracking.R: -------------------------------------------------------------------------------- 1 | # getTimePoint ---- 2 | 3 | test_that("getTimePoint works as expected", { 4 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 5 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 6 | 7 | simStudy <- getOneClinicalTrial( 8 | nPat = c(30, 30), transitionByArm = list(transition1, transition2), 9 | dropout = list(rate = 0.5, time = 12), 10 | accrual = list(param = "time", value = 0) 11 | ) 12 | simStudyWide <- getDatasetWideFormat(simStudy) 13 | 14 | actual <- getTimePoint(data = simStudyWide, eventNum = 10, typeEvent = "OS") 15 | actual2 <- getTimePoint(data = simStudyWide, eventNum = 6, typeEvent = "PFS") 16 | expect_equal(length(simStudyWide$id[simStudyWide$OSevent == 1 & simStudyWide$OStime <= actual]), 10) 17 | expect_equal(length(simStudyWide$id[simStudyWide$PFSevent == 1 & simStudyWide$PFStime <= actual2]), 6) 18 | }) 19 | 20 | 21 | test_that("getTimePoint works as expected by Arm", { 22 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 23 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 24 | 25 | simStudy <- getOneClinicalTrial( 26 | nPat = c(30, 30), transitionByArm = list(transition1, transition2), 27 | dropout = list(rate = 0.5, time = 12), 28 | accrual = list(param = "time", value = 5) 29 | ) 30 | simStudyWide <- getDatasetWideFormat(simStudy) 31 | 32 | actual <- getTimePoint(data = simStudyWide, eventNum = 5, typeEvent = "OS", byArm = TRUE) 33 | actual2 <- getTimePoint(data = simStudyWide, eventNum = 7, typeEvent = "PFS", byArm = TRUE) 34 | expect_equal(length(simStudyWide$id[simStudyWide$OSevent == 1 & simStudyWide$OStimeCal <= actual[1] & 35 | simStudyWide$trt == 1]), 5) 36 | expect_equal(length(simStudyWide$id[simStudyWide$OSevent == 1 & simStudyWide$OStimeCal <= actual[2] & 37 | simStudyWide$trt == 2]), 5) 38 | expect_equal(length(simStudyWide$id[simStudyWide$PFSevent == 1 & simStudyWide$PFStimeCal <= actual2[1] & 39 | simStudyWide$trt == 1]), 7) 40 | expect_equal(length(simStudyWide$id[simStudyWide$PFSevent == 1 & simStudyWide$PFStimeCal <= actual2[2] & 41 | simStudyWide$trt == 2]), 7) 42 | }) 43 | 44 | 45 | # censoringByNumberEvents---- 46 | 47 | test_that("censoringByNumberEvents works as expected", { 48 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 49 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 50 | 51 | simStudy <- getOneClinicalTrial( 52 | nPat = c(30, 30), transitionByArm = list(transition1, transition2), 53 | dropout = list(rate = 0.5, time = 12), 54 | accrual = list(param = "time", value = 7) 55 | ) 56 | simStudyWide <- getDatasetWideFormat(simStudy) 57 | 58 | actual <- censoringByNumberEvents(data = simStudyWide, eventNum = 12, typeEvent = "OS") 59 | actual2 <- censoringByNumberEvents(data = simStudyWide, eventNum = 16, typeEvent = "PFS") 60 | expect_equal(length(actual$id[actual$OSevent == 1]), 12) 61 | expect_equal(length(actual2$id[actual2$PFSevent == 1]), 16) 62 | }) 63 | 64 | # trackEventsPerTrial---- 65 | 66 | test_that("trackEventsPerTrial works as expected by arm", { 67 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 68 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 69 | 70 | simStudy <- getOneClinicalTrial( 71 | nPat = c(30, 30), transitionByArm = list(transition1, transition2), 72 | dropout = list(rate = 0.5, time = 2), 73 | accrual = list(param = "time", value = 5) 74 | ) 75 | simStudyWide <- getDatasetWideFormat(simStudy) 76 | 77 | actual <- trackEventsPerTrial(data = simStudyWide, timeP = 0.3, byArm = TRUE) 78 | expect_equal(actual[["1"]]["Recruited", ] - (actual[["1"]]["OS", ] + actual[["1"]]["Censored", ] 79 | + actual[["1"]]["Ongoing", ]), 0) 80 | expect_true(actual[["1"]]["PFS", ] >= actual[["1"]]["OS", ]) 81 | 82 | expect_equal(actual[["2"]]["Recruited", ] - (actual[["2"]]["OS", ] + actual[["2"]]["Censored", ] 83 | + actual[["2"]]["Ongoing", ]), 0) 84 | expect_true(actual[["2"]]["PFS", ] >= actual[["2"]]["OS", ]) 85 | }) 86 | 87 | 88 | # getCensoredData---- 89 | 90 | test_that("getCensoredData works as expected", { 91 | time <- c(1, 1.2, 1.4, 1.5) 92 | event <- c(1, 0, 1, 1) 93 | data <- data.frame(id = 1:4, censTimeInd = c(1.4, 1.3, 1.5, 0.8), recruitTime = c(1, 1, 2, 3)) 94 | 95 | actual <- getCensoredData(time = time, event = event, data = data) 96 | 97 | expected <- data.frame( 98 | time = c(1, 1.2, 1.4, 0.8), Censored = c(0, 1, 0, 1), 99 | event = c(1, 0, 1, 0), timeCal = c(2, 2.2, 3.4, 3.8) 100 | ) 101 | expect_equal(actual, expected) 102 | }) 103 | 104 | # getEventsAll---- 105 | test_that("getEventsAll works as expected", { 106 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 107 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 108 | set.seed(12345) 109 | simStudy <- getOneClinicalTrial( 110 | nPat = c(5, 5), transitionByArm = list(transition1, transition2), 111 | dropout = list(rate = 0.5, time = 2), 112 | accrual = list(param = "time", value = 5) 113 | ) 114 | simStudyWide <- getDatasetWideFormat(simStudy) 115 | 116 | actual <- getEventsAll(data = simStudyWide, t = 3.5) 117 | expect_equal(actual, c(Recruited = 7, Censored = 3, UnderObs = 1)) 118 | }) 119 | 120 | 121 | 122 | # getNumberEvents---- 123 | test_that("getNumberEvents works as expected", { 124 | event <- c(1, 0, 1, 1, 1, 1) 125 | time <- c(1.2, 1.5, 7, 2.4, 3.5, 1.8) 126 | actual <- getNumberEvents(event = event, time = time, t = 3) 127 | expect_equal(actual, 3) 128 | }) 129 | -------------------------------------------------------------------------------- /tests/testthat/test-getDatsetWideFormat.R: -------------------------------------------------------------------------------- 1 | # getDatasetWideFormat ---- 2 | 3 | test_that("getDatasetWideFormat works as expected", { 4 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 0.8, p02 = 0.9, p12 = 1) 5 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.1, p02 = 0.9, p12 = 1.1) 6 | 7 | simStudy <- getOneClinicalTrial( 8 | nPat = c(30, 30), transitionByArm = list(transition1, transition2), 9 | dropout = list(rate = 0.5, time = 12), 10 | accrual = list(param = "time", value = 0) 11 | ) 12 | 13 | actual <- getDatasetWideFormat(simStudy) 14 | expect_true(length(actual$id[actual$trt == 1]) == 30) 15 | expect_true(length(actual$id[actual$trt == 2]) == 30) 16 | 17 | expect_true(all(actual$PFSevent + actual$CensoredPFS == 1)) 18 | expect_true(all(actual$OSevent + actual$CensoredOS == 1)) 19 | 20 | expect_true(all(actual$OStime <= actual$OStimeCal)) 21 | expect_true(all(actual$PFStime <= actual$PFStimeCal)) 22 | 23 | expect_named(actual, c( 24 | "id", "trt", "PFStime", "CensoredPFS", "PFSevent", "OStime", "CensoredOS", 25 | "OSevent", "recruitTime", "OStimeCal", "PFStimeCal" 26 | )) 27 | }) 28 | -------------------------------------------------------------------------------- /tests/testthat/test-getSimulatedData.R: -------------------------------------------------------------------------------- 1 | # getOneToTwoRows ---- 2 | 3 | test_that("getOneToTwoRows works as expected", { 4 | simDataOne <- data.frame( 5 | id = c(1:3), to = c(1, 1, 1), from = c(0, 0, 0), entry = c(0, 0, 0), 6 | exit = c(0.43, 0.15, 0.01), censTime = c(0.9, 5.9, 0.5) 7 | ) 8 | 9 | transition <- exponential_transition(1, 1.6, 0.3) 10 | actual <- getOneToTwoRows(simDataOne, transition) 11 | 12 | expect_true(length(actual$id) == 3) 13 | expect_true(all(actual$from == 1)) 14 | expect_true(all(actual$to %in% c("cens", 2))) 15 | expect_true(all(actual$exit > actual$entry)) 16 | expect_true(all(actual$entry == simDataOne$exit)) 17 | actualCens <- actual[actual$to == "cens", ] 18 | expect_true(all(actualCens$censTime == actualCens$exit)) 19 | }) 20 | 21 | # getSimulatedData ---- 22 | 23 | test_that("getSimulatedData works as expected if no random censoring is present", { 24 | actual <- getSimulatedData(10, 25 | transition = exponential_transition(h01 = 1, h02 = 1.5, h12 = 1), 26 | dropout = list(rate = 0, time = 1), 27 | accrual = list(param = "time", value = 5) 28 | ) 29 | 30 | # at least one row per patient and not more than two per patient. 31 | expect_true(length(actual$id) >= 10 & length(actual$id) <= 20) 32 | expect_named(actual, c("id", "from", "to", "entry", "exit", "entryAct", "exitAct", "censAct")) 33 | 34 | expect_true(all(actual$exit > actual$entry)) 35 | # no censoring present? 36 | expect_true(all(actual$to %in% c(1, 2))) 37 | # illness-death model 38 | expect_true(all(actual$from %in% c(0, 1))) 39 | }) 40 | 41 | 42 | test_that("getSimulatedData works as expected if random censoring is present", { 43 | actual <- getSimulatedData(10, 44 | transition = exponential_transition(h01 = 1, h02 = 1.5, h12 = 1), 45 | dropout = list(rate = 0.5, time = 1), 46 | accrual = list(param = "time", value = 5) 47 | ) 48 | 49 | # at least one row per patient and not more than two per patient. 50 | expect_true(length(actual$id) >= 10 & length(actual$id) <= 20) 51 | expect_named(actual, c("id", "from", "to", "entry", "exit", "entryAct", "exitAct", "censAct")) 52 | 53 | expect_true(all(actual$exit > actual$entry)) 54 | # no censoring present? 55 | expect_true(all(actual$to %in% c(1, 2, "cens"))) 56 | # illness-death model 57 | expect_true(all(actual$from %in% c(0, 1))) 58 | }) 59 | 60 | test_that("getSimulatedData creates expected data set for exponential transitions", { 61 | set.seed(1243) 62 | actual <- getSimulatedData(4, 63 | transition = exponential_transition(h01 = 1, h02 = 1.5, h12 = 1), 64 | dropout = list(rate = 0.5, time = 1), 65 | accrual = list(param = "time", value = 5) 66 | ) 67 | row1 <- data.frame( 68 | id = 1, from = 0, to = "2", entry = 0.0000000, exit = 0.10917836889122016, 69 | entryAct = 3.6695390287786722, exitAct = 3.7787173976698925, censAct = 5.2614395397317368, 70 | stringsAsFactors = FALSE 71 | ) 72 | expect_equal(actual[1, ], row1) 73 | }) 74 | 75 | 76 | test_that("getSimulatedData creates expected data set for Weibull transitions", { 77 | set.seed(1243) 78 | actual <- getSimulatedData(4, 79 | transition = weibull_transition( 80 | h01 = 1, h02 = 1.5, h12 = 1, 81 | p01 = 1.1, p02 = 0.8, p12 = 1.2 82 | ), 83 | dropout = list(rate = 0.5, time = 1), 84 | accrual = list(param = "time", value = 5) 85 | ) 86 | row1 <- data.frame( 87 | id = 1, from = 0, to = "2", entry = 0.0000000, exit = 0.0842096040823, 88 | entryAct = 3.66953902878, exitAct = 3.75374863286, censAct = 5.26143953973, 89 | stringsAsFactors = FALSE 90 | ) 91 | expect_equal(actual[1, ], row1) 92 | }) 93 | 94 | test_that("getSimulatedData works also without progression to death transitions", { 95 | set.seed(31) 96 | result <- expect_silent(getSimulatedData( 97 | N = 1L, 98 | transition = piecewise_exponential( 99 | pw01 = c(0, 2), 100 | pw02 = c(0, 2), 101 | pw12 = c(0, 2), 102 | h01 = rep(0.04781994, 2), 103 | h02 = rep(0.03882345, 2), 104 | h12 = rep(0.31313900, 2) 105 | ), 106 | dropout = list(rate = 0, time = 1), 107 | accrual = list(param = "intensity", value = 5) 108 | )) 109 | expect_data_frame(result, nrows = 1L) 110 | expect_false(any(result$to == 1)) 111 | expect_false(any(result$from == 1)) 112 | }) 113 | -------------------------------------------------------------------------------- /tests/testthat/test-getSimulatedDataDistib.R: -------------------------------------------------------------------------------- 1 | # helper function for tests. 2 | library(mvna) 3 | getEstimatedNA <- function(actual, times) { 4 | # transition matrix 5 | tra <- matrix(ncol = 3, nrow = 3, FALSE) 6 | tra[1, 2:3] <- TRUE 7 | tra[2, c(3)] <- TRUE 8 | 9 | # Nelson-Aalen estimator. 10 | EstimatedNA <- lapply(seq_along(actual), function(j) { 11 | na <- NULL 12 | na <- mvna(actual[[j]][actual[[j]]$trt == 1, ], c("0", "1", "2"), tra, "cens") 13 | na_predict <- predict(na, times, 14 | tr.choice = c("0 1"), 15 | level = 0.95, var.type = c("aalen"), ci.fun = c("log") 16 | )[["0 1"]][, "na"] 17 | return(na_predict) 18 | }) 19 | EstimatedNAMean <- rowMeans(matrix(unlist(EstimatedNA), ncol = length(EstimatedNA))) 20 | return(EstimatedNAMean) 21 | } 22 | 23 | 24 | 25 | # getSimulatedData --- ---- 26 | test_that("getSimulatedData generates distributions as expected - Exponential", { 27 | transition1 <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 28 | transition2 <- exponential_transition(h01 = 1, h02 = 1.3, h12 = 1.7) 29 | actual <- getClinicalTrials( 30 | nRep = 1000, nPat = c(200, 200), seed = 1234, datType = "1rowTransition", 31 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0, time = 12), 32 | accrual = list(param = "intensity", value = 7) 33 | ) 34 | # compare true Nelson-Aalen estimator with simulated one. 35 | times <- seq(0, 3, 0.01) 36 | EstimatedNAMean <- getEstimatedNA(actual, times) 37 | 38 | # true NA - trt 1 0 -> 1 transition. 39 | 40 | trueNA <- transition1$hazards$h01 * times 41 | 42 | if (interactive()) { 43 | plot(times, EstimatedNAMean, type = "l") 44 | lines(times, trueNA, col = "red") 45 | } 46 | tol1 <- 0.01 47 | expect_true(all(abs(EstimatedNAMean[1:130] - trueNA[1:130]) <= tol1)) 48 | }) 49 | 50 | 51 | 52 | 53 | # getSimulatedData --- ---- 54 | test_that("getSimulatedData generates distributions as expected - Weibull", { 55 | transition1 <- weibull_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6, p01 = 1.2, p02 = 1, p12 = 0.3) 56 | transition2 <- weibull_transition(h01 = 1, h02 = 1.3, h12 = 1.7, p01 = 1.2, p02 = 1, p12 = 0.3) 57 | actual <- getClinicalTrials( 58 | nRep = 1000, nPat = c(200, 200), seed = 1234, datType = "1rowTransition", 59 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0, time = 12), 60 | accrual = list(param = "intensity", value = 7) 61 | ) 62 | # compare true Nelson-Aalen estimator with simulated one. 63 | times <- seq(0, 3, 0.01) 64 | EstimatedNAMean <- getEstimatedNA(actual, times) 65 | 66 | # true NA - trt 1 0 -> 1 transition. 67 | 68 | trueNA <- transition1$hazards$h01 * times^transition1$weibull_rates$p01 69 | 70 | if (interactive()) { 71 | plot(times, EstimatedNAMean, type = "l") 72 | lines(times, trueNA, col = "red") 73 | } 74 | tol1 <- 0.01 75 | expect_true(all(abs(EstimatedNAMean[1:100] - trueNA[1:100]) <= tol1)) 76 | }) 77 | 78 | 79 | 80 | 81 | 82 | # getSimulatedData --- ---- 83 | test_that("getSimulatedData generates distributions as expected - PW", { 84 | transition1 <- piecewise_exponential( 85 | h01 = c(1, 0.8, 1.3), h02 = c(1.6, 1.5, 1), h12 = c(1, 1.6, 1), 86 | pw01 = c(0, 0.5, 1), pw02 = c(0, 3, 8), pw12 = c(0, 2, 4) 87 | ) 88 | transition2 <- piecewise_exponential( 89 | h01 = c(1, 0.9, 1.5), h02 = c(1.7, 1.8, 1), h12 = c(1.2, 1.6, 1), 90 | pw01 = c(0, 1, 7), pw02 = c(0, 3, 8), pw12 = c(0, 2, 4) 91 | ) 92 | actual <- getClinicalTrials( 93 | nRep = 1000, nPat = c(200, 200), seed = 1234, datType = "1rowTransition", 94 | transitionByArm = list(transition1, transition2), dropout = list(rate = 0, time = 12), 95 | accrual = list(param = "intensity", value = 7) 96 | ) 97 | # compare true Nelson-Aalen estimator with simulated one. 98 | times <- seq(0, 3, 0.01) 99 | EstimatedNAMean <- getEstimatedNA(actual, times) 100 | 101 | # true NA - trt 1 0 -> 1 transition. 102 | pw01TRT1 <- transition1$intervals$pw01 103 | h01TRT1 <- transition1$hazards$h01 104 | trueNA <- truePWC <- sapply(times, function(t) { 105 | if (t <= pw01TRT1[2]) { 106 | return(h01TRT1[1] * (t - pw01TRT1[1])) 107 | } else if (t <= pw01TRT1[3]) { 108 | return(h01TRT1[1] * (pw01TRT1[2] - pw01TRT1[1]) + h01TRT1[2] * (t - pw01TRT1[2])) 109 | } else { 110 | return(h01TRT1[1] * (pw01TRT1[2] - pw01TRT1[1]) + h01TRT1[2] * 111 | (pw01TRT1[3] - pw01TRT1[2]) + h01TRT1[3] * (t - pw01TRT1[3])) 112 | } 113 | }) 114 | 115 | 116 | if (interactive()) { 117 | plot(times, EstimatedNAMean, type = "l") 118 | lines(times, trueNA, col = "red") 119 | } 120 | tol1 <- 0.01 121 | expect_true(all(abs(EstimatedNAMean[1:100] - trueNA[1:100]) <= tol1)) 122 | }) 123 | -------------------------------------------------------------------------------- /tests/testthat/test-hazardFunctions.R: -------------------------------------------------------------------------------- 1 | # ExpHazOS ---- 2 | 3 | test_that("ExpHazOS works as expected", { 4 | actual <- ExpHazOS(1, 0.2, 1.1, 0.8) 5 | expect_equal(actual, 1.038192, tolerance = 1e-3) 6 | 7 | actual2 <- ExpHazOS(2, 0.4, 1.4, 0.1) 8 | expect_equal(actual2, 0.266345, tolerance = 1e-3) 9 | }) 10 | 11 | test_that("ExpHazOS works also with vector of times t", { 12 | result <- ExpHazOS(t = c(1:3), 0.2, 1.1, 0.8) 13 | expected <- c(1.0381919, 0.9777975, 0.9253826) 14 | expect_equal(result, expected, tolerance = 1e-3) 15 | }) 16 | 17 | # avgHRIntegExpOS ---- 18 | 19 | test_that("avgHRIntegExpOS works as expected", { 20 | h0 <- list(h01 = 0.18, h02 = 0.06, h12 = 0.17) 21 | h1 <- list(h01 = 0.23, h02 = 0.07, h12 = 0.19) 22 | actual <- avgHRIntegExpOS(x = 5, h01 = 0.2, h02 = 0.5, h12 = 0.7, h0 = h0, h1 = h1, alpha = 0.5) 23 | expect_equal(actual, 0.297362, tolerance = 1e-3) 24 | 25 | actual2 <- avgHRIntegExpOS(x = 1, h01 = 0.3, h02 = 0.4, h12 = 0.5, h0 = h0, h1 = h1, alpha = 0.8) 26 | expect_equal(actual2, 0.3764565, tolerance = 1e-3) 27 | }) 28 | 29 | # avgHRExpOS ---- 30 | 31 | test_that("avgHRExpOS works as expected", { 32 | transition1 <- exponential_transition(h01 = 0.1, h02 = 0.4, h12 = 0.3) 33 | transition2 <- exponential_transition(h01 = 0.06, h02 = 0.3, h12 = 0.3) 34 | transitionList1 <- list(transition1, transition2) 35 | transitionList2 <- list(transition1, transition1) 36 | 37 | actual <- avgHRExpOS(transitionByArm = transitionList1, alpha = 0.5, upper = 100) 38 | expect_equal(actual, 0.8038991, tolerance = 1e-3) 39 | 40 | actual2 <- avgHRExpOS(transitionByArm = transitionList2, alpha = 0.5, upper = 100) 41 | expect_equal(actual2, 1, tolerance = 1e-3) 42 | }) 43 | -------------------------------------------------------------------------------- /tests/testthat/test-piecewiseDistrb.R: -------------------------------------------------------------------------------- 1 | # getPCWdistr ---- 2 | 3 | test_that("getPCWDistr works as expected for constant hazards after time shift", { 4 | U <- 0.65 5 | actual <- getPCWDistr(U, c(1.1, 0.8), c(0, 5), 6) 6 | expected <- -log(1 - U) / 0.8 7 | expect_equal(actual, expected) 8 | }) 9 | 10 | test_that("getPCWDistr works as expected for pwc hazards without time shift", { 11 | U <- 0.91 12 | actual <- getPCWDistr(U = U, haz = c(1.1, 0.5, 0.4), pw = c(0, 0.6, 5), t_0 = 0) 13 | # 1-survival function of the actual time-points. 14 | expected <- 1 - exp(-(1.1 * 0.6) - (actual - 0.6) * 0.5) 15 | expect_equal(U, expected) 16 | }) 17 | 18 | 19 | test_that("getPCWDistr works as expected for piecewise constant hazards with time shift", { 20 | U <- c(0.45, 0.8) 21 | actual <- getPCWDistr(U = U, haz = c(1.1, 0.5, 0.4), pw = c(0, 0.6, 5), t_0 = c(4.2, 4.2)) 22 | # 1-survival function of the actual time-points. 23 | expected <- 1 - exp(-(0.8 * 0.5) - (actual + 4.2 - 5) * 0.4) 24 | expect_equal(U, expected) 25 | }) 26 | 27 | # PCWInversionMethod ---- 28 | 29 | test_that("PCWInversionMethod works as expected", { 30 | U <- 0.4 31 | logU <- log(1 - U) 32 | actual <- PCWInversionMethod(haz = c(1.1, 0.5, 0.4), pw = c(0, 0.6, 5), LogU = logU) 33 | # 1-survival function of the actual time-point. 34 | expected <- 1 - exp(-(actual * 1.1)) 35 | expect_equal(U, expected) 36 | }) 37 | -------------------------------------------------------------------------------- /tests/testthat/test-piecewiseHazards.R: -------------------------------------------------------------------------------- 1 | # getPWCHazard ---- 2 | 3 | test_that("getPWCHazard works as expected", { 4 | actual <- getPWCHazard(c(0.8, 1.1, 1), c(0, 5, 8), c(1, 5, 7, 9)) 5 | expect_identical(actual, c(0.8, 1.1, 1.1, 1)) 6 | }) 7 | 8 | 9 | # getSumPCW ---- 10 | test_that("getSumPCW works as expected", { 11 | actual <- getSumPCW(c(0.8, 1.1, 1), c(0.8, 1.1, 1, 0.4), c(0, 5, 8), c(0, 3, 7, 9)) 12 | expect_equal(actual, list(hazards = c(1.6, 1.9, 2.2, 2.1, 2, 1.4), intervals = c(0, 3, 5, 7, 8, 9))) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/testthat/test-survivalFunctions.R: -------------------------------------------------------------------------------- 1 | # ExpSurvPFS ---- 2 | 3 | test_that("ExpSurvPFS works as expected", { 4 | actual <- ExpSurvPFS(2, 0.3, 1.8) 5 | expect_equal(actual, 0.01499558, tolerance = 1e-3) 6 | 7 | actual2 <- ExpSurvPFS(0, 0.3, 1.8) 8 | expect_equal(actual2, 1) 9 | }) 10 | 11 | test_that("ExpSurvPFS works also with vector of times t", { 12 | result <- ExpSurvPFS(t = c(1, 0.5, 3), 0.3, 1.8) 13 | expected <- c(0.1225, 0.3499, 0.0018) 14 | expect_equal(result, expected, tolerance = 1e-3) 15 | }) 16 | 17 | # ExpSurvOS ---- 18 | 19 | test_that("ExpSurvOS works as expected", { 20 | actual <- ExpSurvOS(1, 0.7, 0.5, 0.8) 21 | expect_equal(round(actual, 5), 0.56043) 22 | 23 | actual2 <- ExpSurvOS(0, 0.7, 0.5, 0.8) 24 | expect_equal(actual2, 1) 25 | 26 | actual3 <- ExpSurvOS(1000, 1.2, 1.5, 1.6) 27 | expect_equal(actual3, 0) 28 | }) 29 | 30 | test_that("ExpSurvOS works also with vector of times t", { 31 | result <- ExpSurvOS(t = c(1, 0.5, 3), 0.7, 0.5, 0.8) 32 | expected <- c(0.5604, 0.7615, 0.1383) 33 | expect_equal(result, expected, tolerance = 1e-3) 34 | }) 35 | 36 | # WeibSurvPFS ---- 37 | 38 | test_that("WeibSurvPFS works as expected", { 39 | actual <- WeibSurvPFS(2:8, 0.3, 1.8, 1, 1) 40 | expect_equal(actual, ExpSurvPFS(2:8, 0.3, 1.8)) 41 | 42 | actual2 <- WeibSurvPFS(0, 0.3, 1.8, 1.2, 0.8) 43 | expect_equal(actual2, 1) 44 | }) 45 | 46 | # integrateVector ---- 47 | 48 | test_that("integrateVector works as expected", { 49 | integrand <- function(x) x^2 50 | upper <- c(1, 0.4, 1) 51 | 52 | actual <- integrateVector(integrand, upper = upper) 53 | expected <- c( 54 | integrate(integrand, 0, 1)$value, 55 | integrate(integrand, 0, 0.4)$value, 56 | integrate(integrand, 0, 1)$value 57 | ) 58 | expect_equal(actual, expected) 59 | }) 60 | 61 | # WeibOSInteg ---- 62 | 63 | test_that("WeibOSInteg works as expected with scalar x", { 64 | result <- expect_silent(WeibOSInteg(4, 2:6, 0.2, 0.5, 2.1, 1.2, 0.9, 1)) 65 | expected <- c(5.368515, 0.657409, 0.080504, 0.009858, 0.001207) 66 | expect_equal(result, expected, tolerance = 1e-4) 67 | }) 68 | 69 | test_that("WeibOSInteg works as expected with vector x", { 70 | result <- expect_silent(WeibOSInteg(1:5, 2:6, 0.2, 0.5, 2.1, 1.2, 0.9, 1)) 71 | expected <- c(0.06081, 0.034948, 0.018842, 0.009858, 0.005061) 72 | expect_equal(result, expected, tolerance = 1e-4) 73 | }) 74 | 75 | test_that("WeibOSInteg works as expected with scalar t", { 76 | result <- expect_silent(WeibOSInteg(2:6, 4, 0.2, 0.5, 2.1, 1.2, 0.9, 1)) 77 | expected <- c(0.00428, 0.018842, 0.080504, 0.337499, 1.395583) 78 | expect_equal(result, expected, tolerance = 1e-4) 79 | }) 80 | 81 | # WeibSurvOS ---- 82 | 83 | test_that("WeibSurvOS works as expected", { 84 | actual <- WeibSurvOS(1:5, 0.7, 0.5, 0.8, 1, 1, 1) 85 | expect_equal(actual, ExpSurvOS(1:5, 0.7, 0.5, 0.8)) 86 | 87 | actual2 <- WeibSurvOS(0, 0.7, 0.5, 0.8, 1.2, 1, 0.9) 88 | expect_equal(actual2, 1) 89 | 90 | actual3 <- WeibSurvOS(1000, 1, 1.1, 1.2, 1.3, 0.8, 1.4) 91 | expect_equal(actual3, 0) 92 | }) 93 | 94 | # pwA ---- 95 | 96 | test_that("pwA works as expected", { 97 | actual <- pwA(c(2, 4), c(0.7, 0.9), c(0, 3)) 98 | expect_equal(actual, c(0.7 * 2, 0.7 * 3 + 0.9)) 99 | }) 100 | 101 | # PWCsurvPFS ---- 102 | 103 | test_that("PWCsurvPFS works as expected", { 104 | actual <- PWCsurvPFS(1:3, c(0.7, 0.9), c(0.5, 1), c(0, 3), c(0, 7)) 105 | expect_equal(actual, ExpSurvPFS(1:3, 0.7, 0.5), tolerance = 1e-3) 106 | 107 | actual2 <- PWCsurvPFS(0, c(0.7, 0.9), c(0.5, 0.2), c(0, 9), c(0, 7)) 108 | expect_equal(actual2, 1) 109 | 110 | actual3 <- PWCsurvPFS(2, c(0.7, 0.9), c(0.5, 0.2), c(0, 1), c(0, 0.8)) 111 | expect_equal(actual3, 0.1064585, tolerance = 1e-3) 112 | }) 113 | 114 | # PWCsurvOS ---- 115 | 116 | test_that("PWCsurvOS works as expected", { 117 | actual <- PWCsurvOS(c(0, 1, 2, 1.4), c(0.7, 0.9), c(0.5, 1), c(0.9, 1.2), c(0, 4), c(0, 3), c(0, 7)) 118 | expect_equal(actual, ExpSurvOS(c(0, 1, 2, 1.4), 0.7, 0.5, 0.9)) 119 | 120 | actual2 <- PWCsurvOS(100, 0.06, 1, 8, 0, 0, 0) 121 | expect_equal(actual2, 0, tolerance = 1e-10) 122 | 123 | actual3 <- PWCsurvOS(2000, c(0.7, 0.9), c(0.5, 1), c(0.9, 14), c(0, 4), c(0, 3), c(0, 7)) 124 | expect_equal(actual3, 0, tolerance = 1e-10) 125 | }) 126 | 127 | test_that("PWCsurvOS does not return values larger than 1", { 128 | result <- PWCsurvOS(3.13, c(0, 1, 1), c(0, 0.5, 1), c(0, 1, 1), c(0, 3, 8), c(0, 6, 7), c(0, 8, 9)) 129 | expect_lte(result, 1) 130 | }) 131 | 132 | test_that("PWCsurvOS is monotonically decreasing and not larger than 1", { 133 | b1 <- 3 134 | b2 <- 6 135 | t_values <- seq(2.9, 6.2, length.out = 100) 136 | assert_true(all(diff(t_values) > 0)) 137 | result <- PWCsurvOS(t_values, c(0, 1, 1), c(0, 0.5, 1), c(0, 1, 1), c(0, b1, 8), c(0, b2, 7), c(0, 8, 9)) 138 | expect_true(all(diff(result) <= 0)) 139 | expect_true(all(result <= 1)) 140 | expect_true(all(result >= 0)) 141 | }) 142 | 143 | test_that("PWCsurvOS gives equal results as the numerical integration", { 144 | t <- c(0.1, 1, 5.2, 3, 10.5, 2, 7.3, 1.5, 5.2, 60.5, 0.3, 20.6, 0.01) 145 | h01 <- c(1, 0.2, 3.2) 146 | pw01 <- c(0, 2.5, 15.3) 147 | h02 <- 4 148 | pw02 <- 0 149 | h12 <- c(5.1, 4.2, 3.1, 7.3, 50.5) 150 | pw12 <- c(0, 7.3, 20, 50.2, 70.5) 151 | 152 | result <- PWCsurvOS(t, h01, h02, h12, pw01, pw02, pw12) 153 | expected <- PWCsurvPFS(t, h01, h02, pw01, pw02) + 154 | sapply(t, function(t) { 155 | integrateVector(PwcOSInt, 156 | upper = t, 157 | t = t, 158 | h01 = h01, 159 | h02 = h02, 160 | h12 = h12, 161 | pw01 = pw01, 162 | pw02 = pw02, 163 | pw12 = pw12 164 | ) 165 | }) 166 | expect_equal(result, expected, tolerance = 1e-6) 167 | }) 168 | 169 | # PwcOSInt ---- 170 | 171 | test_that("PwcOSInt works as expected", { 172 | actual <- PwcOSInt(1, 1.1, c(0.3, 0.5), c(0.5, 0.8), c(0.7, 1), c(0, 4), c(0, 8), c(0, 3)) 173 | expect_equal(actual, 0.12568546, tolerance = 1e-6) 174 | 175 | actual2 <- PwcOSInt(4, 4, c(0.3, 0.5), c(0.5, 0.8), c(0.7, 100), c(0, 2), c(0, 3), c(0, 3)) 176 | expect_equal(actual2, 0.010120956, tolerance = 1e-6) 177 | 178 | actual3 <- PwcOSInt(700, 700, c(0.3, 0.5), c(0.5, 0.8), c(0.7, 100), c(0, 2), c(0, 3), c(0, 3)) 179 | expect_equal(actual3, 0, tolerance = 1e-6) 180 | }) 181 | 182 | # singleExpQuantOS ---- 183 | 184 | test_that("singleExpQuantOS works as expected", { 185 | actual <- singleExpQuantOS(1 / 2, 0.2, 0.5, 2.1) 186 | expect_equal(actual, 1.144539, tolerance = 1e-5) 187 | }) 188 | 189 | # ExpQuantOS ---- 190 | 191 | test_that("ExpQuantOS works as expected", { 192 | actual <- ExpQuantOS(1 / 2, 0.2, 0.5, 2.1) 193 | expect_equal(actual, 1.144539, tolerance = 1e-5) 194 | }) 195 | 196 | test_that("ExpQuantOS works also with vector of quantiles q", { 197 | actual <- ExpQuantOS(q = c(0.1, 0.3, 0.7), 0.2, 0.5, 2.1) 198 | expected <- c(3.4788, 1.8981, 0.6237) 199 | expect_equal(actual, expected, tolerance = 1e-5) 200 | }) 201 | -------------------------------------------------------------------------------- /tests/testthat/test-transitionParameters.R: -------------------------------------------------------------------------------- 1 | # exponential_transition ---- 2 | 3 | test_that("exponential_transition works as expected", { 4 | actual <- exponential_transition(1, 1.2, 0.8) 5 | 6 | expect_identical(actual$hazards, list(h01 = 1, h02 = 1.2, h12 = 0.8)) 7 | expect_identical(actual$family, "exponential") 8 | expect_identical(actual$intervals, list(pw01 = 0, pw02 = 0, pw12 = 0)) 9 | expect_identical(actual$weibull_rates, list(p01 = 1, p02 = 1, p12 = 1)) 10 | 11 | expect_error(exponential_transition(-1, 1.2, 0.8)) 12 | expect_error(exponential_transition(1, 1.2, 0.8, 6)) 13 | }) 14 | 15 | # piecewise_exponential ---- 16 | 17 | test_that("piecewise_exponential works as expected", { 18 | actual <- piecewise_exponential( 19 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 20 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 21 | ) 22 | 23 | expect_identical(actual$hazards, list(h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1))) 24 | expect_identical(actual$family, "piecewise exponential") 25 | expect_identical(actual$intervals, list(pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9))) 26 | expect_identical(actual$weibull_rates, list(p01 = 1, p02 = 1, p12 = 1)) 27 | 28 | # first element piecewise has to be 0 29 | expect_error(piecewise_exponential( 30 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 31 | pw01 = c(4, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 32 | )) 33 | ## hazards must be positive numbers 34 | expect_error(piecewise_exponential( 35 | h01 = c(-1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 36 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 7), pw12 = c(0, 8, 9) 37 | )) 38 | # intervals must be increasing 39 | expect_error(piecewise_exponential( 40 | h01 = c(1, 1, 1), h02 = c(1.5, 0.5, 1), h12 = c(1, 1, 1), 41 | pw01 = c(0, 3, 8), pw02 = c(0, 6, 3), pw12 = c(0, 8, 9) 42 | )) 43 | }) 44 | 45 | # weibull_transition ---- 46 | 47 | test_that("weibull_transition works as expected", { 48 | actual <- weibull_transition( 49 | h01 = 1, h02 = 1.3, h12 = 0.5, 50 | p01 = 1.2, p02 = 1.3, p12 = 0.5 51 | ) 52 | 53 | 54 | expect_identical(actual$hazards, list(h01 = 1, h02 = 1.3, h12 = 0.5)) 55 | expect_identical(actual$family, "Weibull") 56 | expect_identical(actual$intervals, list(pw01 = 0, pw02 = 0, pw12 = 0)) 57 | expect_identical(actual$weibull_rates, list(p01 = 1.2, p02 = 1.3, p12 = 0.5)) 58 | 59 | expect_error(weibull_transition( 60 | h01 = 1, h02 = -1.3, h12 = 0.5, 61 | p01 = 1.2, p02 = 1.3, p12 = 0.5 62 | )) 63 | expect_error(weibull_transition( 64 | h01 = 1, h02 = 1.3, h12 = 0.5, 65 | p01 = 1.2, p02 = 1.3, p12 = -1 66 | )) 67 | }) 68 | -------------------------------------------------------------------------------- /tests/testthat/test-waitTimeSum.R: -------------------------------------------------------------------------------- 1 | # getWaitTimeSum ---- 2 | test_that("getWaitTimeSum works as expected for sum of constant hazards", { 3 | U <- 0.65 4 | actual <- getWaitTimeSum(U, haz1 = 0.8, haz2 = 1.2, p1 = 1, p2 = 1, entry = 0) 5 | expected <- -log(1 - U) / (0.8 + 1.2) 6 | expect_equal(actual, expected) 7 | }) 8 | 9 | 10 | test_that("getWaitTimeSum works as expected for sum of hazards (entry = 0)", { 11 | U <- c(0.23, 0.86) 12 | actual <- getWaitTimeSum(U, haz1 = 0.8, haz2 = 1.2, p1 = 1.5, p2 = 0.7, entry = c(0, 0)) 13 | expected <- c(0.8 * actual[1]^1.5 + 1.2 * actual[1]^0.7, 0.8 * actual[2]^1.5 + 1.2 * actual[2]^0.7) 14 | expect_equal(-log(1 - U), expected) 15 | }) 16 | 17 | test_that("getWaitTimeSum works as expected for sum of hazards (entry != 0)", { 18 | U <- 0.4 19 | actual <- getWaitTimeSum(U, haz1 = 0.8, haz2 = 1.2, p1 = 1.5, p2 = 0.7, entry = 5) 20 | expected <- 0.8 * (actual + 5)^1.5 - 0.8 * 5^1.5 - 1.2 * 5^0.7 + 1.2 * (actual + 5)^0.7 21 | expect_equal(-log(1 - U), expected) 22 | }) 23 | -------------------------------------------------------------------------------- /vignettes/MSM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/simIDM/f3c27cfd5893a27b76a7cf7ef77d12752bbe3d3f/vignettes/MSM.png -------------------------------------------------------------------------------- /vignettes/correlation.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Correlation between PFS and OS' 3 | author: "Holger Löwe" 4 | output: rmarkdown::html_vignette 5 | bibliography: references.bib 6 | vignette: | 7 | %\VignetteIndexEntry{Correlation between PFS and OS} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r setup, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | Here we describe how the correlation between progression-free survival (PFS) and overall survival (OS) is computed. 20 | 21 | ## Introduction 22 | 23 | The illness-death model used in `simIDM` is designed to jointly model PFS and OS as endpoints in an oncology clinical trial. Within each treatment arm, the model is specified through the transition hazards $\lambda_{01}(t)$, $\lambda_{02}(t)$ and $\lambda_{12}(t)$. This approach allows us to consider the joint distribution of the two endpoints and to derive the correlation between PFS and OS directly from the assumed transition hazards. 24 | 25 | ## Cor(PFS, OS): Statistical Background 26 | 27 | @meller2019joint derive a closed formula for $Cor(PFS,OS)$. The general formula for the correlation is 28 | \[ 29 | Cor(PFS, OS) = \frac{Cov(PFS, OS)}{\sqrt{Var(PFS) \, Var(OS)}}. 30 | \] 31 | The expected values of PFS and OS can be derived via the survival functions for PFS and OS, respectively: 32 | \[ 33 | \mathbb{E}(PFS) = \int_{0}^{\infty} S_{PFS}(u) \,du 34 | \] 35 | and 36 | \[ 37 | \mathbb{E}(OS) = \int_{0}^{\infty} S_{OS}(u) \,du. 38 | \] 39 | The variance of PFS and OS are computed as follows: 40 | \[ 41 | Var(PFS) = \mathbb{E}(PFS^2) - \mathbb{E}(PFS)^2 42 | \] 43 | where 44 | \[ 45 | \mathbb{E}(PFS^2) = 2 \cdot\int_{0}^{\infty} u \cdot S_{PFS}(u) \,du 46 | \] 47 | and in a similar way for $Var(OS)$. 48 | $Cov(PFS,OS)$ is derived using 49 | \[ 50 | Cov(PFS,OS) = \mathbb{E}(PFS \cdot OS) - \mathbb{E}(PFS) \cdot \mathbb{E}(OS) 51 | \] 52 | together with the general formula for deriving expected survival times, and where 53 | \[ 54 | P(PFS \cdot OS > t) = P(PFS > \sqrt{t}) \, + \int_{\left(0, \sqrt{t} \right]} P_{11}(u,t/u;u) \cdot P(PFS>u-) \cdot \lambda_{01}(u) \, du. 55 | \] 56 | This requires the transition probability $P_{11}(s,t;t_{1})$, which has the form of a standard survival function: 57 | \[ 58 | P_{11}(s,t;t_{1}) = exp \left( -\int_{s}^{t} \lambda_{12}(u;t_{1}) \, du\right). 59 | \] 60 | 61 | We can compute the correlation based on assumptions or estimate it from data. Both work by simply plugging in assumed or estimated survival functions. 62 | 63 | ## Example: Computing Cor(PFS, OS) directly 64 | 65 | We consider three alternative scenarios to model transition hazards within a treatment arm: 66 | 67 | * constant (exponential distribution) 68 | * Weibull 69 | * piecewise constant 70 | 71 | ```{r} 72 | library(simIDM) 73 | 74 | # constant hazards: 75 | transitionExp <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 76 | 77 | # Weibull hazards: 78 | transitionWeib <- weibull_transition(h01 = 1, h02 = 1.2, h12 = 1.3, p01 = 1.1, p02 = 0.8, p12 = 1.2) 79 | 80 | # piecewise constant hazards: 81 | transitionPwc <- piecewise_exponential( 82 | h01 = c(1, 1.3), h02 = c(0.8, 1.5), h12 = c(1, 1), 83 | pw01 = c(0, 3), pw02 = c(0, 1), pw12 = c(0, 8) 84 | ) 85 | ``` 86 | 87 | Now, we can compute the PFS-OS correlation with [`corTrans()`](https://insightsengineering.github.io/simIDM/main/reference/corTrans.html): 88 | 89 | ```{r} 90 | # constant hazards: 91 | corTrans(transitionExp) 92 | 93 | # Weibull hazards: 94 | corTrans(transitionWeib) 95 | 96 | # piecewise constant hazards: 97 | corTrans(transitionPwc) 98 | ``` 99 | 100 | ## Estimating Cor(PFS, OS) from data 101 | 102 | In case we are given trial data and want to estimate the PFS-OS correlation from the data, the following approach can be adopted: 103 | 104 | 1. Specify the assumed distribution family. 105 | 2. Estimate the transition hazards under this assumption from the data. 106 | 3. Compute the correlation of PFS and OS. 107 | 108 | We can estimate the parameters via maximum likelihood (ML), using the log-likelihood based on the counting process notation of @andersen1993, see also @meller2019joint: 109 | \[ 110 | L(\theta) = \sum_{i=1}^{n} \sum_{k=1}^{3} \left( \log \left[ \lambda_{k}(t_{ik})^{d_{ik}} \frac{S_{k}(t_{ik})}{S_{k}(t_{0ik})} \right] \times \mathbb{I}(i \in Y_{ik}) \right), 111 | \] 112 | where the sum is over all $n$ individuals. $k \in \{ 1, 2, 3 \}$ is a simplified notation for the transitions 0 $\rightarrow$ 1, 0 $\rightarrow$ 2 and 1 $\rightarrow$ 2, $\lambda_{k}$ is the corresponding transition hazard and $S_{k}$ the survival function. $d_{ik}$ is an indicator function, taking the value 1 if the $i$-th individual made the $k$-th transition and $t_{0ik}$ and $t_{ik}$ are the time the $i$-th individual enters and exits, respectively, the root state of the $k$-th transition. The indicator function at the end of the formula is equal to 1 if the $i$-th individual is at risk for the transition $k$ and 0 otherwise. 113 | 114 | ## Example: Estimating Cor(PFS, OS) from data 115 | 116 | Currently, this package supports parameter estimation for assuming either constant 117 | or Weibull transition hazards. The [`estimateParams()`](https://insightsengineering.github.io/simIDM/main/reference/estimateParams.html) function expects a `data` argument of the same format as used throughout the package, and a `transition` argument of class `TransitionParameters`, specifying the assumed distribution and desired starting values for ML estimation. 118 | To demonstrate this, we simulate data using constant transition hazards: 119 | 120 | ```{r} 121 | transitionExp <- exponential_transition(h01 = 1.2, h02 = 1.5, h12 = 1.6) 122 | simData <- getOneClinicalTrial( 123 | nPat = c(500), transitionByArm = list(transitionExp), 124 | dropout = list(rate = 0.8, time = 12), 125 | accrual = list(param = "time", value = 1) 126 | ) 127 | ``` 128 | 129 | We can estimate the parameters as follows: 130 | 131 | ```{r} 132 | # Create TransitionParameters object with starting values for ML estimation: 133 | transition <- exponential_transition(h01 = 1, h02 = 1, h12 = 1) 134 | # Estimate parameters: 135 | est <- estimateParams(data = simData, transition = transition) 136 | # Get estimated transition hazards: 137 | est$hazards 138 | ``` 139 | 140 | Then, in a final step, we pass `est` to [`corTrans()`](https://insightsengineering.github.io/simIDM/main/reference/corTrans.html) to compute the PFS-OS correlation. 141 | 142 | Alternatively, one can combine these steps efficiently via [`corPFSOS()`](https://insightsengineering.github.io/simIDM/main/reference/corTrans.html), which has an additional `bootstrap` argument to quantify the uncertainty 143 | of the correlation estimate: 144 | 145 | ```{r} 146 | corPFSOS(data = simData, transition = transition, bootstrap = TRUE, conf_level = 0.95) 147 | ``` 148 | 149 | ## References 150 | -------------------------------------------------------------------------------- /vignettes/pwc_survival.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Piecewise Constant Hazards Calculations" 3 | author: "Daniel Sabanés Bové" 4 | output: rmarkdown::html_vignette 5 | format: 6 | html: 7 | html-math-method: mathjax 8 | include-in-header: 9 | - text: | 10 | 17 | bibliography: references.bib 18 | vignette: > 19 | %\VignetteIndexEntry{Piecewise Constant Hazards Calculations} 20 | %\VignetteEngine{knitr::rmarkdown} 21 | %\VignetteEncoding{UTF-8} 22 | --- 23 | 24 | ```{r setup, include = FALSE} 25 | knitr::opts_chunk$set( 26 | collapse = TRUE, 27 | comment = "#>" 28 | ) 29 | ``` 30 | 31 | ## Introduction 32 | 33 | In this vignette we derive the explicit formula for the overall survival under 34 | piecewise constant hazards. This is implemented in the function `PWCsurvOS()`. 35 | 36 | So we are in the situation where the hazards for the transitions are piecewise constant, 37 | i.e. all three hazard functions $\lambda_{01}(t)$ (stable to progression), $\lambda_{02}(t)$ 38 | (stable to death) and $\lambda_{12}(t)$ (progression to death) are step functions. 39 | Say the start points of the constant hazard pieces for $\lambda_{01}(t)$ are $0 \equiv t_{01}^{(1)} < \dotsb < t_{01}^{(k_{01})}$, $k_{01} \geq 1$, with corresponding constant positive hazards $h_{01}^{(1)}, \dotsc, h_{01}^{(k_{01})}$. Obviously we use here the smallest set of pieces, i.e. neighboring hazards are required 40 | to be different, $h_{01}^{(j)} \neq h_{01}^{(j+1)}$. 41 | This holds analogously for the hazard functions of the other two state transitions. 42 | 43 | We denote the cumulative hazards similarly as $\Lambda_{01}(t)$, $\Lambda_{02}(t)$ and 44 | $\Lambda_{12}(t)$. Note that these are piecewise linear, with the slope changes occurring at the times of hazard changes. 45 | 46 | ## Overall survival calculation 47 | 48 | Now we want to calculate the overall survival (OS) survival function 49 | induced by the piecewise constant hazard model. We start from 50 | 51 | $$ 52 | S_{\text{OS}}(t) 53 | = S_{\text{PFS}}(t) + \int_0^t S_{\text{PFS}}(u) \lambda_{01}(u)\exp(\Lambda_{12}(u) - \Lambda_{12}(t))\, du 54 | $$ 55 | where $S_{\text{OS}}(t)$ is the survival function for OS, and $S_{\text{PFS}}(t)$ is the 56 | survival function for PFS with the closed form 57 | 58 | $$ 59 | S_{\text{PFS}}(t) = \exp(- \Lambda_{01}(t) - \Lambda_{02}(t)). 60 | $$ 61 | Hence we can rewrite the integral from above as 62 | 63 | $$ 64 | \exp(- \Lambda_{12}(t)) \int_0^t \exp(\Lambda_{12}(u) - \Lambda_{01}(u) - \Lambda_{02}(u))\lambda_{01}(u)\, du 65 | $$ 66 | So overall we now have 67 | 68 | $$ 69 | S_{\text{OS}}(t) 70 | = S_{\text{PFS}}(t) + \exp(- \Lambda_{12}(t)) \cal{I}(t) 71 | $$ 72 | and we can rewrite the integral 73 | $$ 74 | \cal{I}(t) := \int_0^t \exp(\Lambda_{12}(u) - \Lambda_{01}(u) - \Lambda_{02}(u))\lambda_{01}(u)\, du 75 | $$ 76 | in terms of the unique starting time points $0 \equiv t_{(1)} < \dotsb < t_{(k)}$, chosen such that 77 | the set $\{t_{(1)}, \dotsc, t_{(k)}\}$ is the smallest super set of all state specific transition starting points $\{t_{01}^{(1)}, \dotsc, t_{01}^{(k_{01})}\}$, $\{t_{02}^{(1)}, \dotsc, t_{02}^{(k_{02})}\}$ and $\{t_{12}^{(1)}, \dotsc, t_{12}^{(k_{12})}\}$, as 78 | 79 | \begin{align} 80 | \cal{I}(t) = 81 | &\int_{t_{(1)}}^{t_{(2)}} \exp(a_{(1)} + b_{(1)}(u - t_{(1)}))h_{01(1)}\,du \\ 82 | &+ \dotsb + \\ 83 | &\int_{t_{(l)}}^{t} \exp(a_{(l)} + b_{(l)}(u - t_{(l)}))h_{01(l)}\,du, 84 | \end{align} 85 | where: 86 | 87 | - $t_{(l)}$ is the start of the last integral part and defined as the maximum starting point that is smaller than $t$ 88 | - $h_{01(j)} = \lambda_{01}(t_{(j)})$, $j=1, \dotsc, k$ are the hazard values for the stable to progression transition within the unique time intervals 89 | - $a_{(j)} = \Lambda_{12}(t_{(j)}) - \Lambda_{01}(t_{(j)}) - \Lambda_{02}(t_{(j)})$, $j=1, \dotsc, k$ are the intercepts 90 | - $b_{(j)} = h_{12(j)} - h_{01(j)} - h_{02(j)}$, $j=1, \dotsc, k$ are the slopes, again using the hazard values within the unique time intervals for the specific transitions 91 | 92 | Note that this is essentially just because of 93 | $$ 94 | \Lambda(t) = \Lambda(s) + h (t-s) 95 | $$ 96 | when there is a constant hazard $\lambda(t) \equiv h$ and two time points $s