├── .Rbuildignore ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.Rmd ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── cran-release.yml │ ├── feature_request.md │ └── release.yml └── workflows │ ├── check.yaml │ ├── cla.yaml │ ├── docs.yaml │ ├── post-release.yaml │ ├── release.yaml │ └── scheduled.yaml ├── .gitignore ├── .gitlab-ci.yml ├── .lintr ├── .pre-commit-config.yaml ├── .revdeprefs.yaml ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── listing_export.R ├── paginate_listing.R ├── rlistings-package.R ├── rlistings.R └── rlistings_methods.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── inst ├── WORDLIST └── cheatsheet │ ├── rlistings_cheatsheet_03-24.pdf │ ├── rlistings_cheatsheet_03-24.pptx │ └── rlistings_cheatsheet_03-24_thumbs.png ├── man ├── figures │ ├── lifecycle-archived.svg │ ├── lifecycle-defunct.svg │ ├── lifecycle-deprecated.svg │ ├── lifecycle-experimental.svg │ ├── lifecycle-maturing.svg │ ├── lifecycle-questioning.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── logo.svg ├── listing_methods.Rd ├── listings.Rd ├── make_row_df-listing_df-method.Rd ├── matrix_form-listing_df-method.Rd ├── paginate.Rd ├── reexports.Rd ├── rlistings-package.Rd ├── split_into_pages_by_var.Rd └── vec_nlines.Rd ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── revdep └── .gitignore ├── rlistings.Rproj ├── tests ├── testthat.R └── testthat │ ├── _snaps │ ├── export.md │ ├── matrix_form.md │ ├── paginate_listing.md │ └── print.md │ ├── setup-options.R │ ├── setup.R │ ├── test-export.R │ ├── test-listings.R │ ├── test-matrix_form.R │ ├── test-paginate_listing.R │ └── test-print.R └── vignettes ├── col_formatting.Rmd ├── large_list.Rmd ├── pagination.Rmd ├── ref_footnotes.Rmd ├── rlisting_time.png └── rlistings.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^renv$ 2 | ^renv\.lock$ 3 | CODE_OF_CONDUCT.md 4 | SECURITY.md 5 | design/* 6 | ^.*\.Rproj$ 7 | ^\.Rproj\.user$ 8 | ^_pkgdown\.yml$ 9 | ^docs$ 10 | ^\.github$ 11 | ^\.git$ 12 | README.* 13 | TODO\.md 14 | ^\.lintr$ 15 | ^\.pre-commit-config\.yaml$ 16 | ^staged_dependencies\.yaml$ 17 | ^LICENSE\.md$ 18 | ^\.gitlab-ci\.yml$ 19 | ^man-roxygen$ 20 | pkgdown 21 | ^pkgdown$ 22 | ^.revdeprefs\.yaml$ 23 | ^revdep$ 24 | ^\.covrignore$ 25 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @edelarua @Melkiades @ayogasekaram 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.Rmd: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at support@github.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | 🙏 Thank you for taking the time to contribute! 4 | 5 | Your input is deeply valued, whether an issue, a pull request, or even feedback, regardless of size, content or scope. 6 | 7 | ## Table of contents 8 | 9 | [👶 Getting started](#getting-started) 10 | 11 | [📔 Code of Conduct](#code-of-conduct) 12 | 13 | [🗃 License](#license) 14 | 15 | [📜 Issues](#issues) 16 | 17 | [🚩 Pull requests](#pull-requests) 18 | 19 | [💻 Coding guidelines](#coding-guidelines) 20 | 21 | [🏆 Recognition model](#recognition-model) 22 | 23 | [❓ Questions](#questions) 24 | 25 | ## Getting started 26 | 27 | Please refer the project [documentation][docs] for a brief introduction. Please also see other [articles][articles] within the project documentation for additional information. 28 | 29 | ## Code of Conduct 30 | 31 | A [Code of Conduct](CODE_OF_CONDUCT.md) governs this project. Participants and contributors are expected to follow the rules outlined therein. 32 | 33 | ## License 34 | 35 | All your contributions will be covered by this project's [license][license]. 36 | 37 | ## Issues 38 | 39 | We use GitHub to track issues, feature requests, and bugs. Before submitting a new issue, please check if the issue has already been reported. If the issue already exists, please upvote the existing issue 👍. 40 | 41 | For new feature requests, please elaborate on the context and the benefit the feature will have for users, developers, or other relevant personas. 42 | 43 | ## Pull requests 44 | 45 | ### GitHub Flow 46 | 47 | This repository uses the [GitHub Flow](https://docs.github.com/en/get-started/quickstart/github-flow) model for collaboration. To submit a pull request: 48 | 49 | 1. Create a branch 50 | 51 | Please see the [branch naming convention](#branch-naming-convention) below. If you don't have write access to this repository, please fork it. 52 | 53 | 2. Make changes 54 | 55 | Make sure your code 56 | * passes all checks imposed by GitHub Actions 57 | * is well documented 58 | * is well tested with unit tests sufficiently covering the changes introduced 59 | 60 | 3. Create a pull request (PR) 61 | 62 | In the pull request description, please link the relevant issue (if any), provide a detailed description of the change, and include any assumptions. 63 | 64 | 4. Address review comments, if any 65 | 66 | 5. Post approval 67 | 68 | Merge your PR if you have write access. Otherwise, the reviewer will merge the PR on your behalf. 69 | 70 | 6. Pat yourself on the back 71 | 72 | Congratulations! 🎉 73 | You are now an official contributor to this project! We are grateful for your contribution. 74 | 75 | ### Branch naming convention 76 | 77 | Suppose your changes are related to a current issue in the current project; please name your branch as follows: `_`. Please use underscore (`_`) as a delimiter for word separation. For example, `420_fix_ui_bug` would be a suitable branch name if your change is resolving and UI-related bug reported in issue number `420` in the current project. 78 | 79 | If your change affects multiple repositories, please name your branches as follows: `__`. For example, `69_awesomeproject_fix_spelling_error` would reference issue `69` reported in project `awesomeproject` and aims to resolve one or more spelling errors in multiple (likely related) repositories. 80 | 81 | ### `monorepo` and `staged.dependencies` 82 | 83 | Sometimes you might need to change upstream dependent package(s) to be able to submit a meaningful change. We are using [`staged.dependencies`](https://github.com/openpharma/staged.dependencies) functionality to simulate a `monorepo` behavior. The dependency configuration is already specified in this project's `staged_dependencies.yaml` file. You need to name the feature branches appropriately. _This is the only exception from the branch naming convention described above_. 84 | 85 | Please refer to the [staged.dependencies package documentation](https://openpharma.github.io/staged.dependencies/) for more details. 86 | 87 | ## Coding guidelines 88 | 89 | This repository follows some unified processes and standards adopted by its maintainers to ensure software development is carried out consistently within teams and cohesively across other repositories. 90 | 91 | ### Style guide 92 | 93 | This repository follows the standard [`tidyverse` style guide](https://style.tidyverse.org/) and uses [`lintr`](https://github.com/r-lib/lintr) for lint checks. Customized lint configurations are available in this repository's `.lintr` file. 94 | 95 | ### Dependency management 96 | 97 | Lightweight is the right weight. This repository follows [tinyverse](https://www.tinyverse.org/) recommedations of limiting dependencies to minimum. 98 | 99 | ### Dependency version management 100 | 101 | If the code is not compatible with all (!) historical versions of a given dependenct package, it is required to specify minimal version in the `DESCRIPTION` file. In particular: if the development version requires (imports) the development version of another package - it is required to put `abc (>= 1.2.3.9000)`. 102 | 103 | ### Recommended development environment & tools 104 | 105 | #### R & package versions 106 | 107 | We continuously test our packages against the newest R version along with the most recent dependencies from CRAN and BioConductor. We recommend that your working environment is also set up in the same way. You can find the details about the R version and packages used in the `R CMD check` GitHub Action execution log - there is a step that prints out the R `sessionInfo()`. 108 | 109 | If you discover bugs on older R versions or with an older set of dependencies, please create the relevant bug reports. 110 | 111 | #### `pre-commit` 112 | 113 | We highly recommend that you use the [`pre-commit`](https://pre-commit.com/) tool combined with [`R hooks for pre-commit`](https://github.com/lorenzwalthert/precommit) to execute some of the checks before committing and pushing your changes. 114 | 115 | Pre-commit hooks are already available in this repository's `.pre-commit-config.yaml` file. 116 | 117 | ## Recognition model 118 | 119 | As mentioned previously, all contributions are deeply valued and appreciated. While all contribution data is available as part of the [repository insights][insights], to recognize a _significant_ contribution and hence add the contributor to the package authors list, the following rules are enforced: 120 | 121 | * Minimum 5% of lines of code authored* (determined by `git blame` query) OR 122 | * Being at the top 5 contributors in terms of number of commits OR lines added OR lines removed* 123 | 124 | *Excluding auto-generated code, including but not limited to `roxygen` comments or `renv.lock` files. 125 | 126 | The package maintainer also reserves the right to adjust the criteria to recognize contributions. 127 | 128 | ## Questions 129 | 130 | If you have further questions regarding the contribution guidelines, please contact the package/repository maintainer. 131 | 132 | 133 | [docs]: https://insightsengineering.github.io/rlistings/index.html 134 | [articles]: https://insightsengineering.github.io/rlistings/main/articles/index.html 135 | [license]: https://insightsengineering.github.io/rlistings/main/LICENSE-text.html 136 | [insights]: https://github.com/insightsengineering/rlistings/pulse 137 | -------------------------------------------------------------------------------- /.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.2] 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/cran-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🎉 CRAN Release 3 | description: Template for release to CRAN 4 | title: "[CRAN 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 | - [ ] PR 1 23 | 24 | #### Issues 25 | - [ ] Issue 1 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: pre-release 30 | attributes: 31 | label: Pre-release 32 | description: Pre-requisites that must be fulfilled before initiating the release process. 33 | placeholder: Add your list of pre-requisites here. 34 | value: | 35 | - [ ] Make sure you adhere to CRAN submission policy: 36 | * https://cran.r-project.org/web/packages/submission_checklist.html 37 | * https://cran.r-project.org/web/packages/policies.html. 38 | - [ ] Make sure that high priority bugs (label "priority" + "bug") have been resolved before going into the release. 39 | - [ ] Review old/hanging PRs before going into the release (Optional). 40 | - [ ] Revisit R-package's lifecycle badges (Optional). 41 | - [ ] Make sure that all upstream dependencies of this package that need to be submitted to CRAN were accepted before going into release activities. 42 | - [ ] Make sure integration tests are green 2-3 days before the release. Look carefully through logs (check for warnings and notes). 43 | - [ ] 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 | - [ ] Create a new release candidate branch 53 | `git checkout -b release-candidate-vX.Y.Z` 54 | - [ ] Update NEWS.md file: make sure it reflects a holistic summary of what has changed in the package. 55 | - [ ] Remove the additional fields (`Remotes`) from the DESCRIPTION file where applicable. 56 | - [ ] Make sure that the minimum dependency versions are updated in the DESCRIPTION file for the package and its reverse dependencies (Optional). 57 | - [ ] Increase versioned dependency on {package name} to >=X.Y.Z (Optional). 58 | - [ ] Commit your changes and create the PR on GitHub (add "[skip vbump]" in the PR title). Add all updates, commit, and push changes: 59 | ```r 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 | 68 | 69 | #### Test the release 70 | - [ ] 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). 71 | - [ ] Monitor integration tests, if integration fails, create priority issues on the board. 72 | - [ ] Execute UAT tests (Optional). 73 | 74 | #### CRAN submission 75 | - [ ] 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). 76 | ```r 77 | # Create rc tag for submission for internal validation 78 | git tag vX.Y.Z-rc 79 | git push origin vX.Y.Z-rc 80 | ``` 81 | - [ ] Build the package locally using the command:`R CMD build .` which will generate a .tar.gz file necessary for the CRAN submission. 82 | - [ ] Submit the package to https://win-builder.r-project.org/upload.aspx for testing, for more details please see "Building and checking R source packages for Windows": https://win-builder.r-project.org/. 83 | - [ ] Once tested, send the package that was built in the previous steps to CRAN via this form: https://cran.r-project.org/submit.html. 84 | - [ ] Address CRAN feedback, tag the package vX.Y.Z-rc(n+1) and repeat the submission to CRAN whenever necessary. 85 | - [ ] Get the package accepted and published on CRAN. 86 | 87 | #### Tag the release 88 | - [ ] 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. 89 | 90 | ##### Make sure of the following before continuing 91 | - [ ] CI checks are passing in GH before releasing the package. 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 | - [ ] Ensure that CRAN checks are passing for the package. 111 | - [ ] Make sure that the package is published to internal repositories. 112 | - [ ] Make sure internal documentation is up to date. 113 | - [ ] Review and update installation instructions for the package wherever needed (Optional). 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/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.yml: -------------------------------------------------------------------------------- 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 | - [ ] PR 1 23 | 24 | #### Issues 25 | - [ ] Issue 1 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: pre-release 30 | attributes: 31 | label: Pre-release 32 | description: Pre-requisites that must be fulfilled before initiating the release process. 33 | placeholder: Add your list of pre-requisites here. 34 | value: | 35 | - [ ] Make sure that high priority bugs (label "priority" + "bug") have been resolved before going into the release. 36 | - [ ] Review old/hanging PRs before going into the release. 37 | - [ ] Revisit R-package's lifecycle badges (Optional). 38 | - [ ] 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). 39 | - [ ] Check Validation Pipeline dry-run results for the package. 40 | - [ ] Make sure all relevant integration tests are green 2-3 days before the release. Look carefully through logs (check for warnings and notes). 41 | - [ ] Inform about the soft code freeze, decide what gets merged in before starting release activities. 42 | - type: textarea 43 | id: release 44 | attributes: 45 | label: Release 46 | description: The steps to be taken in order to create a release. 47 | placeholder: Steps to create a release. 48 | value: | 49 | #### Prepare the release 50 | - [ ] Create a new release candidate branch 51 | `git checkout -b release-candidate-vX.Y.Z` 52 | - [ ] Update NEWS.md file: make sure it reflects a holistic summary of what has changed in the package, check README. 53 | - [ ] Remove the additional fields (`Remotes`) from the DESCRIPTION file where applicable. 54 | - [ ] Make sure that the minimum dependency versions are updated in the DESCRIPTION file for the package. 55 | - [ ] Increase versioned dependency on {package name} to >=X.Y.Z. 56 | - [ ] Commit your changes and create the PR on GitHub (add "[skip vbump]" in the PR title). Add all updates, commit, and push changes: 57 | ```r 58 | # Make the necessary modifications to your files 59 | # Stage the changes 60 | git add 61 | # Commit the changes 62 | git commit -m "[skip vbump] " 63 | git push origin release-candidate-vX.Y.Z 64 | ``` 65 | 66 | 67 | #### Test the release 68 | - [ ] 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). 69 | - [ ] Monitor integration tests, if integration fails, create priority issues on the board. 70 | - [ ] Execute UAT tests (Optional). 71 | 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 | ```r 79 | # Create rc tag for submission for internal validation 80 | git tag vX.Y.Z-rc 81 | git push origin vX.Y.Z-rc 82 | ``` 83 | - [ ] Submit the package for internal validation. 84 | - [ ] 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. 85 | - [ ] Get the package validated. 86 | 87 | #### Tag the release 88 | - [ ] 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. 89 | 90 | ##### Make sure of the following before continuing with the release: 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/workflows/check.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Check 🛠 3 | 4 | on: 5 | pull_request: 6 | types: 7 | - opened 8 | - synchronize 9 | - reopened 10 | - ready_for_review 11 | branches: 12 | - main 13 | push: 14 | branches: 15 | - main 16 | workflow_dispatch: 17 | 18 | jobs: 19 | audit: 20 | name: Audit Dependencies 🕵️‍♂️ 21 | uses: insightsengineering/r.pkg.template/.github/workflows/audit.yaml@main 22 | r-cmd: 23 | name: R CMD Check 🧬 24 | uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main 25 | secrets: 26 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 27 | with: 28 | deps-installation-method: setup-r-dependencies 29 | enforce-note-blocklist: true 30 | note-blocklist: | 31 | checking dependencies in R code .* NOTE 32 | checking R code for possible problems .* NOTE 33 | checking examples .* NOTE 34 | checking Rd line widths .* NOTE 35 | checking S3 generic/method consistency .* NOTE 36 | checking Rd .usage sections .* NOTE 37 | checking for unstated dependencies in vignettes .* NOTE 38 | checking top-level files .* NOTE 39 | unit-test-report-brand: >- 40 | https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/thumbs/rlistings.png 41 | r-cmd-non-cran: 42 | name: R CMD Check (non-CRAN) 🧬 43 | uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main 44 | secrets: 45 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 46 | with: 47 | deps-installation-method: setup-r-dependencies 48 | additional-env-vars: | 49 | NOT_CRAN=true 50 | enforce-note-blocklist: true 51 | concurrency-group: non-cran 52 | unit-test-report-directory: unit-test-report-non-cran 53 | note-blocklist: | 54 | checking dependencies in R code .* NOTE 55 | checking R code for possible problems .* NOTE 56 | checking examples .* NOTE 57 | checking Rd line widths .* NOTE 58 | checking S3 generic/method consistency .* NOTE 59 | checking Rd .usage sections .* NOTE 60 | checking for unstated dependencies in vignettes .* NOTE 61 | checking top-level files .* NOTE 62 | coverage: 63 | name: Coverage 📔 64 | uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main 65 | secrets: 66 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 67 | with: 68 | deps-installation-method: setup-r-dependencies 69 | additional-env-vars: | 70 | NOT_CRAN=true 71 | linter: 72 | if: github.event_name != 'push' 73 | name: SuperLinter 🦸‍♀️ 74 | uses: insightsengineering/r.pkg.template/.github/workflows/linter.yaml@main 75 | roxygen: 76 | name: Roxygen 🅾 77 | uses: insightsengineering/r.pkg.template/.github/workflows/roxygen.yaml@main 78 | secrets: 79 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 80 | with: 81 | deps-installation-method: setup-r-dependencies 82 | auto-update: true 83 | gitleaks: 84 | name: gitleaks 💧 85 | uses: insightsengineering/r.pkg.template/.github/workflows/gitleaks.yaml@main 86 | spelling: 87 | name: Spell Check 🆎 88 | uses: insightsengineering/r.pkg.template/.github/workflows/spelling.yaml@main 89 | secrets: 90 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 91 | links: 92 | if: github.event_name != 'push' 93 | name: Check URLs 🌐 94 | uses: insightsengineering/r.pkg.template/.github/workflows/links.yaml@main 95 | vbump: 96 | name: Version Bump 🤜🤛 97 | if: github.event_name == 'push' 98 | uses: insightsengineering/r.pkg.template/.github/workflows/version-bump.yaml@main 99 | secrets: 100 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 101 | version: 102 | name: Version Check 🏁 103 | uses: insightsengineering/r.pkg.template/.github/workflows/version.yaml@main 104 | licenses: 105 | name: License Check 🃏 106 | uses: insightsengineering/r.pkg.template/.github/workflows/licenses.yaml@main 107 | grammar: 108 | if: github.event_name != 'push' 109 | name: Grammar Check 🔤 110 | uses: insightsengineering/r.pkg.template/.github/workflows/grammar.yaml@main 111 | -------------------------------------------------------------------------------- /.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 | paths: 9 | - "inst/templates/**" 10 | - "_pkgdown.*" 11 | - DESCRIPTION 12 | - "**.md" 13 | - "**.Rmd" 14 | - "man/**" 15 | - "LICENSE.*" 16 | - NAMESPACE 17 | pull_request: 18 | types: 19 | - opened 20 | - synchronize 21 | - reopened 22 | - ready_for_review 23 | branches: 24 | - main 25 | paths: 26 | - "inst/templates/**" 27 | - "_pkgdown.*" 28 | - DESCRIPTION 29 | - "**.md" 30 | - "**.Rmd" 31 | - "man/**" 32 | - "LICENSE.*" 33 | - NAMESPACE 34 | workflow_dispatch: 35 | 36 | jobs: 37 | docs: 38 | name: Pkgdown Docs 📚 39 | uses: insightsengineering/r.pkg.template/.github/workflows/pkgdown.yaml@main 40 | secrets: 41 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 42 | with: 43 | default-landing-page: latest-tag 44 | additional-unit-test-report-directories: unit-test-report-non-cran 45 | deps-installation-method: setup-r-dependencies 46 | -------------------------------------------------------------------------------- /.github/workflows/post-release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Post release ✨ 3 | 4 | on: 5 | release: 6 | types: ["released"] 7 | 8 | jobs: 9 | vbump: 10 | name: Version Bump 🤜🤛 11 | uses: insightsengineering/r.pkg.template/.github/workflows/version-bump.yaml@main 12 | secrets: 13 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 14 | with: 15 | vbump-after-release: true 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 🎈 3 | 4 | on: 5 | push: 6 | tags: 7 | - "v*" 8 | workflow_dispatch: 9 | 10 | jobs: 11 | docs: 12 | name: Pkgdown Docs 📚 13 | needs: release 14 | uses: insightsengineering/r.pkg.template/.github/workflows/pkgdown.yaml@main 15 | secrets: 16 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 17 | validation: 18 | name: R Package Validation report 📃 19 | needs: release 20 | uses: insightsengineering/r.pkg.template/.github/workflows/validation.yaml@main 21 | secrets: 22 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 23 | release: 24 | name: Create release 🎉 25 | uses: insightsengineering/r.pkg.template/.github/workflows/release.yaml@main 26 | secrets: 27 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 28 | build: 29 | name: Build package and reports 🎁 30 | needs: [release, docs] 31 | uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main 32 | secrets: 33 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 34 | with: 35 | enforce-note-blocklist: true 36 | note-blocklist: | 37 | checking dependencies in R code .* NOTE 38 | checking R code for possible problems .* NOTE 39 | checking examples .* NOTE 40 | checking Rd line widths .* NOTE 41 | checking top-level files .* NOTE 42 | unit-test-report-brand: >- 43 | https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/thumbs/rlistings.png 44 | coverage: 45 | name: Coverage 📔 46 | needs: [release, docs] 47 | uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main 48 | secrets: 49 | REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} 50 | with: 51 | additional-env-vars: | 52 | NOT_CRAN=true 53 | wasm: 54 | name: Build WASM packages 🧑‍🏭 55 | needs: release 56 | uses: insightsengineering/r.pkg.template/.github/workflows/wasm.yaml@main 57 | -------------------------------------------------------------------------------- /.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: "shajoezhu,Melkiades,edelarua,ayogasekaram" 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 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | /doc/ 6 | /Meta/ 7 | .DS_Store 8 | docs 9 | README.html 10 | pkgdown 11 | tests/testthat/_snaps/**/*.new.md 12 | tests/testthat/_snaps/**/*.new.svg 13 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | include: 4 | - project: 'nest/automation/gitlab-shared-library' 5 | ref: main 6 | file: R/R_NEST_min.gitlab-ci.yml -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: linters_with_defaults( 2 | line_length_linter = line_length_linter(120), 3 | object_usage_linter = NULL, 4 | object_name_linter = NULL, 5 | commented_code_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.9009 6 | hooks: 7 | # - id: style-files 8 | # args: [--style_pkg=styler, --style_fun=tidyverse_style] 9 | - id: roxygenize 10 | name: Regenerate package documentation 11 | additional_dependencies: 12 | - insightsengineering/formatters 13 | - methods 14 | - tibble 15 | - checkmate 16 | - grDevices 17 | - grid 18 | - stats 19 | - utils 20 | # codemeta must be above use-tidy-description when both are used 21 | # - id: codemeta-description-updated 22 | - id: use-tidy-description 23 | - id: spell-check 24 | exclude: > 25 | (?x)^( 26 | .*\.[rR]| 27 | .*\.feather| 28 | .*\.jpeg| 29 | .*\.pdf| 30 | .*\.png| 31 | .*\.py| 32 | .*\.RData| 33 | .*\.rds| 34 | .*\.Rds| 35 | .*\.Rproj| 36 | .*\.sh| 37 | (.*/|)\.gitignore| 38 | (.*/|)\.covrignore| 39 | (.*/|)\.gitlab-ci\.y[a]?ml| 40 | (.*/|)\.lintr| 41 | (.*/|)\.pre-commit-.*| 42 | (.*/|)\.Rbuildignore| 43 | (.*/|)\.Renviron| 44 | (.*/|)\.Rprofile| 45 | (.*/|)\.travis\.y[a]?ml| 46 | (.*/|)appveyor\.y[a]?ml| 47 | (.*/|)CODEOWNERS| 48 | (.*/|)DESCRIPTION| 49 | (.*/|)LICENSE| 50 | (.*/|)NAMESPACE| 51 | (.*/|)staged_dependencies\.y[a]?ml| 52 | (.*/|)renv/settings\.dcf| 53 | (.*/|)renv\.lock| 54 | (.*/|)WORDLIST| 55 | \.github/workflows/.*| 56 | data/.*| 57 | )$ 58 | # - id: lintr 59 | - id: readme-rmd-rendered 60 | - id: parsable-R 61 | - id: no-browser-statement 62 | - id: no-debug-statement 63 | - id: deps-in-desc 64 | - repo: https://github.com/pre-commit/pre-commit-hooks 65 | rev: v5.0.0 66 | hooks: 67 | - id: check-added-large-files 68 | args: ['--maxkb=200'] 69 | - id: file-contents-sorter 70 | files: '^\.Rbuildignore$' 71 | - id: end-of-file-fixer 72 | exclude: '\.Rd' 73 | - repo: https://github.com/pre-commit-ci/pre-commit-ci-config 74 | rev: v1.6.1 75 | hooks: 76 | # Only reuiqred when https://pre-commit.ci is used for config validation 77 | - id: check-pre-commit-ci-config 78 | - repo: local 79 | hooks: 80 | - id: forbid-to-commit 81 | name: Don't commit common R artifacts 82 | entry: Cannot commit .Rhistory, .RData, .Rds or .rds. 83 | language: fail 84 | files: '\.(Rhistory|RData|Rds|rds)$' 85 | # `exclude: ` to allow committing specific files 86 | 87 | ci: 88 | autoupdate_schedule: monthly 89 | -------------------------------------------------------------------------------- /.revdeprefs.yaml: -------------------------------------------------------------------------------- 1 | - insightsengineering/scda.test 2 | - insightsengineering/chevron 3 | - insightsengineering/autoslider.core 4 | - insightsengineering/teal.modules.clinical 5 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rlistings 2 | Title: Clinical Trial Style Data Readout Listings 3 | Version: 0.2.11.9002 4 | Date: 2025-05-28 5 | Authors@R: c( 6 | person("Gabriel", "Becker", , "gabembecker@gmail.com", role = "aut", 7 | comment = "original creator of the package"), 8 | person("Adrian", "Waddell", , "adrian.waddell@gene.com", role = "aut"), 9 | person("Joe", "Zhu", , "joe.zhu@roche.com", role = c("aut", "cre"), 10 | comment = c(ORCID = "0000-0001-7566-2787")), 11 | person("Davide", "Garolini", , "davide.garolini@roche.com", role = "aut", 12 | comment = c(ORCID = "0000-0002-1445-1369")), 13 | person("Emily", "de la Rua", , "emily.de_la_rua@contractors.roche.com", role = "aut", 14 | comment = c(ORCID = "0009-0000-8738-5561")), 15 | person("Abinaya", "Yogasekaram", , "abinaya.yogasekaram@contractors.roche.com", role = "ctb", 16 | comment = c(ORCID = "0009-0005-2083-1105")), 17 | person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")) 18 | ) 19 | Description: Listings are often part of the submission of clinical trial 20 | data in regulatory settings. We provide a framework for the specific 21 | formatting features often used when displaying large datasets in that 22 | context. 23 | License: Apache License 2.0 24 | URL: https://insightsengineering.github.io/rlistings/, 25 | https://github.com/insightsengineering/rlistings/ 26 | BugReports: https://github.com/insightsengineering/rlistings/issues 27 | Depends: 28 | formatters (>= 0.5.11), 29 | methods, 30 | tibble (>= 2.0.0) 31 | Imports: 32 | checkmate (>= 2.1.0), 33 | grDevices, 34 | grid, 35 | stats, 36 | utils 37 | Suggests: 38 | dplyr (>= 1.0.2), 39 | knitr (>= 1.42), 40 | lifecycle (>= 0.2.0), 41 | rmarkdown (>= 2.23), 42 | stringi (>= 1.6), 43 | testthat (>= 3.1.5), 44 | withr (>= 2.0.0) 45 | VignetteBuilder: 46 | knitr, 47 | rmarkdown 48 | Config/Needs/verdepcheck: insightsengineering/formatters, 49 | tidyverse/tibble, mllg/checkmate, tidyverse/dplyr, yihui/knitr, 50 | r-lib/lifecycle, rstudio/rmarkdown, gagolews/stringi, r-lib/testthat, 51 | r-lib/withr 52 | Config/Needs/website: insightsengineering/nesttemplate 53 | Config/testthat/edition: 3 54 | Encoding: UTF-8 55 | Language: en-US 56 | Roxygen: list(markdown = TRUE) 57 | RoxygenNote: 7.3.2 58 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,listing_df) 4 | export("align_colnames<-") 5 | export("listing_dispcols<-") 6 | export("spanning_col_label_df<-") 7 | export(add_listing_col) 8 | export(add_listing_dispcol) 9 | export(align_colnames) 10 | export(as_keycol) 11 | export(as_listing) 12 | export(export_as_txt) 13 | export(get_keycols) 14 | export(is_keycol) 15 | export(listing_dispcols) 16 | export(paginate_listing) 17 | export(spanning_col_label_df) 18 | export(split_into_pages_by_var) 19 | exportMethods("[") 20 | exportMethods("main_footer<-") 21 | exportMethods("main_title<-") 22 | exportMethods("prov_footer<-") 23 | exportMethods("subtitles<-") 24 | exportMethods(main_footer) 25 | exportMethods(main_title) 26 | exportMethods(make_row_df) 27 | exportMethods(matrix_form) 28 | exportMethods(num_rep_cols) 29 | exportMethods(prov_footer) 30 | exportMethods(subtitles) 31 | exportMethods(toString) 32 | import(formatters) 33 | import(methods) 34 | import(tibble) 35 | importFrom(formatters,export_as_txt) 36 | importFrom(grDevices,dev.off) 37 | importFrom(grDevices,pdf) 38 | importFrom(grid,convertHeight) 39 | importFrom(grid,convertWidth) 40 | importFrom(grid,get.gpar) 41 | importFrom(grid,gpar) 42 | importFrom(grid,grid.draw) 43 | importFrom(grid,grid.newpage) 44 | importFrom(grid,plotViewport) 45 | importFrom(grid,pushViewport) 46 | importFrom(grid,textGrob) 47 | importFrom(grid,unit) 48 | importFrom(stats,na.omit) 49 | importFrom(utils,head) 50 | importFrom(utils,tail) 51 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ## rlistings 0.2.11.9002 2 | * Added parameter `align_colnames` to `as_listings()`, along with post-processing functions `align_colnames()` and `align_colnames()<-`. This flag allows to align colnames as the column content is aligned. 3 | * `as_listing` now accepts a `spanning_col_labels` argument which can declare decorative spanning labels to appear above the individual column labels when a listing_df is rendered. #263 by @gmbecker 4 | 5 | ## rlistings 0.2.11 6 | * Added parameter `sort_cols` to `as_listing` to specify columns to sort the listing by. Previously listings were always sorted by key columns. 7 | * Addition of separators between values in `as_listings(add_trailing_sep = )` with determined values `as_listings(trailing_sep = )`. 8 | * Added a vignette with tips for exporting large listings. 9 | 10 | ## rlistings 0.2.10 11 | * Added an error message for listings with variables of `difftime` class. 12 | * Added message when the listing object has zero row. 13 | 14 | ## rlistings 0.2.9 15 | * Added `truetype` font support based on new `formatters` api, by @gmbecker. 16 | * Fixed tests so that paginations based on different fonts and page sizes can be compared, by @gmbecker. 17 | * `paginate_listing` now accepts `col_gap` argument and passes it down correctly to pagination machinery in `formatters`, by @gmbecker. 18 | 19 | ## rlistings 0.2.8 20 | * Added relevant tests for pagination when key columns need to be repeated in each page and when they are all empty. 21 | * Added relevant tests for new line characters' handling in footnotes and titles. 22 | * Added a cheatsheet. 23 | * Added function `split_into_pages_by_var` to split a listing into a list of listings according to values of a given 24 | variable. This enables page splits by variable when paginating. 25 | * Removed defunct function `pag_listing_indices`. 26 | * Changed title of "Getting Started with rlistings" vignette to "Getting Started". 27 | * Refactored `paginate_listing` to use directly `paginate_to_mpfs` function from `formatters` package. 28 | 29 | ## rlistings 0.2.7 30 | * Applied `styler` and resolved package lint. Changed default indentation from 4 spaces to 2. 31 | * Fixed bug in `add_listing_col` when both a function and a format are specified. 32 | * Added a vignette on referential footnotes workaround. 33 | * Added a vignette on formatting columns. 34 | * Added a vignette on pagination. 35 | 36 | ## rlistings 0.2.6 37 | * Fixed bug in pagination preventing key column values to appear in paginated listings when `export_as_txt` was used. 38 | * Added tests to cover for `export_as_txt` outputs. 39 | * Integrated support for newline characters. 40 | 41 | ## rlistings 0.2.5 42 | * Fixed bug in `as_listing` preventing custom formatting from being applied to key columns. 43 | * Updated `matrix_form` to allow `NA` values in key columns. 44 | * Updated `as_listing` to trim any rows containing only NA values and print an informative message. 45 | 46 | ## rlistings 0.2.4 47 | * Added `num_rep_cols` method for listings. Resolves error with key column repetition during pagination . 48 | * Fixed a bug when exporting a degenerative list, which is a data frame of a single row and a single column. 49 | * Specified minimal version of package dependencies. 50 | 51 | ## rlistings 0.2.3 52 | * Added new arguments `default_formatting` and `col_formatting` to `as_listing` to specify column format configurations. 53 | * Added new argument `unique_rows` to `as_listing` to remove duplicate rows from listing. 54 | * Default alignment is now `left` across all types. Reinstate `NA` as default. 55 | * Introduced `testthat` edition 3. 56 | 57 | ## rlistings 0.2.2 58 | * Moved `export_as_txt` to `formatters`. Added to reexports. 59 | 60 | ## rlistings 0.2.1 61 | 62 | ### Enhancements 63 | * Extend page-size machinery in pagination by allowing the page specification (`page_type`, `pg_width`, 64 | `pg_height`, `font_family`, `font_size`) to be transformed into `lpp` (lines per page) and `cpp` (characters per page). 65 | * New function `export_as_txt` to support output saved in plain text. 66 | * `cols` argument renamed to `disp_cols` in the function `as_listing`. 67 | * New argument `non_disp_cols` in the function `as_listing`. 68 | * `disp_cols` argument now defaults to all columns not included in `key_cols`. 69 | * Columns named in `key_cols` no longer need to also be listed in `disp_cols`. 70 | * Pagination is now calculated based on formatted cells values (including wrapping) rather than raw cell contents. 71 | * Key columns are now guaranteed to be the leftmost columns (both stored and displayed) in `listing_df` objects. 72 | * Added tests for `paginate_listing`. 73 | * Added development cycle with `lifecycle` support, and experimental badges. 74 | * Added initial installments for `checkmate` assertion support. 75 | * Added a main package page with all the relevant imports and descriptions (`rlistings-package`). 76 | * Added "Get Started" vignette and updated README. 77 | * Added `markdown` support to all functions. 78 | 79 | ### Bug fixes 80 | * `matrix_form(lsting, TRUE)` no longer throws an error. 81 | 82 | ## rlistings 0.1.1 83 | * Add title, subtitle, and (main and prov) footer support. 84 | * Now depends on `dplyr` instead of `magrittr` to hopefully avoid `var_labels` droppage issues. 85 | * `paginate_listing` now supports pagination in both directions. 86 | 87 | ## rlistings 0.1.0 88 | * Initial experimental rlistings API. Everything subject to change. 89 | -------------------------------------------------------------------------------- /R/listing_export.R: -------------------------------------------------------------------------------- 1 | #' @importFrom formatters export_as_txt 2 | #' 3 | #' @examples 4 | #' dat <- ex_adae 5 | #' 6 | #' lsting <- as_listing(dat[1:25, ], key_cols = c("USUBJID", "AESOC")) %>% 7 | #' add_listing_col("AETOXGR") %>% 8 | #' add_listing_col("BMRKR1", format = "xx.x") %>% 9 | #' add_listing_col("AESER / AREL", fun = function(df) paste(df$AESER, df$AREL, sep = " / ")) 10 | #' main_title(lsting) <- "this is some title" 11 | #' main_footer(lsting) <- "this is some footer" 12 | #' 13 | #' cat(export_as_txt(lsting, file = NULL, paginate = TRUE)) 14 | #' 15 | #' @export 16 | formatters::export_as_txt 17 | -------------------------------------------------------------------------------- /R/paginate_listing.R: -------------------------------------------------------------------------------- 1 | #' Paginate listings 2 | #' 3 | #' @description `r lifecycle::badge("experimental")` 4 | #' 5 | #' Pagination of a listing. This can be vertical for long listings with many 6 | #' rows and/or horizontal if there are many columns. This function is a wrapper of 7 | #' [formatters::paginate_to_mpfs()] and it is mainly meant for exploration and testing. 8 | #' 9 | #' @inheritParams formatters::pag_indices_inner 10 | #' @inheritParams formatters::vert_pag_indices 11 | #' @inheritParams formatters::page_lcpp 12 | #' @inheritParams formatters::toString 13 | #' @param lsting (`listing_df` or `list`)\cr the listing or list of listings to paginate. 14 | #' @param lpp (`numeric(1)` or `NULL`)\cr number of rows/lines (excluding titles and footers) 15 | #' to include per page. Standard is `70` while `NULL` disables vertical pagination. 16 | #' @param cpp (`numeric(1)` or `NULL`)\cr width (in characters) of the pages for horizontal 17 | #' pagination. `NULL` (the default) indicates no horizontal pagination should be done. 18 | #' @param print_pages (`flag`)\cr whether the paginated listing should be printed to the console 19 | #' (`cat(toString(x))`). 20 | #' 21 | #' @return A list of `listing_df` objects where each list element corresponds to a separate page. 22 | #' 23 | #' @examples 24 | #' dat <- ex_adae 25 | #' lsting <- as_listing(dat[1:25, ], disp_cols = c("USUBJID", "AESOC", "RACE", "AETOXGR", "BMRKR1")) 26 | #' mat <- matrix_form(lsting) 27 | #' cat(toString(mat)) 28 | #' 29 | #' paginate_listing(lsting, lpp = 10) 30 | #' 31 | #' paginate_listing(lsting, cpp = 100, lpp = 40) 32 | #' 33 | #' paginate_listing(lsting, cpp = 80, lpp = 40, verbose = TRUE) 34 | #' 35 | #' @export 36 | #' @rdname paginate 37 | paginate_listing <- function(lsting, 38 | page_type = "letter", 39 | font_family = "Courier", 40 | font_size = 8, 41 | lineheight = 1, 42 | landscape = FALSE, 43 | pg_width = NULL, 44 | pg_height = NULL, 45 | margins = c(top = .5, bottom = .5, left = .75, right = .75), 46 | lpp = NA_integer_, 47 | cpp = NA_integer_, 48 | colwidths = NULL, 49 | tf_wrap = !is.null(max_width), 50 | rep_cols = NULL, 51 | max_width = NULL, 52 | col_gap = 3, 53 | fontspec = font_spec(font_family, font_size, lineheight), 54 | verbose = FALSE, 55 | print_pages = TRUE) { 56 | checkmate::assert_multi_class(lsting, c("listing_df", "list")) 57 | checkmate::assert_numeric(colwidths, lower = 0, len = length(listing_dispcols(lsting)), null.ok = TRUE) 58 | checkmate::assert_flag(tf_wrap) 59 | checkmate::assert_count(max_width, null.ok = TRUE) 60 | checkmate::assert_flag(verbose) 61 | checkmate::assert_flag(print_pages) 62 | 63 | pages <- paginate_to_mpfs(lsting, 64 | page_type = page_type, 65 | fontspec = fontspec, 66 | landscape = landscape, 67 | pg_width = pg_width, 68 | pg_height = pg_height, 69 | margins = margins, 70 | lpp = lpp, 71 | cpp = cpp, 72 | colwidths = colwidths, 73 | tf_wrap = tf_wrap, 74 | max_width = max_width, 75 | rep_cols = rep_cols, 76 | col_gap = col_gap, 77 | verbose = verbose 78 | ) 79 | 80 | if (print_pages) { 81 | nothing <- lapply(seq_along(pages), function(pagi) { 82 | cat("--- Page", paste0(pagi, "/", length(pages)), "---\n") 83 | # It is NULL because paginate_mpfs takes care of it 84 | cat(toString(pages[[pagi]], widths = NULL, tf_wrap = tf_wrap, max_width = max_width, col_gap = col_gap)) 85 | cat("\n") 86 | }) 87 | } 88 | invisible(pages) 89 | } 90 | -------------------------------------------------------------------------------- /R/rlistings-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | #' @import formatters 5 | #' @import tibble 6 | #' @import methods 7 | #' @importFrom utils head tail 8 | #' @importFrom grid textGrob grid.newpage pushViewport plotViewport unit grid.draw 9 | #' @importFrom grid convertHeight convertWidth get.gpar gpar 10 | #' @importFrom grDevices dev.off pdf 11 | #' @importFrom stats na.omit 12 | NULL 13 | -------------------------------------------------------------------------------- /R/rlistings_methods.R: -------------------------------------------------------------------------------- 1 | ## XXX this historically has been 1, but it actually should be 1.2!!!!! 2 | dflt_courier <- font_spec("Courier", 9, 1) 3 | 4 | #' Methods for `listing_df` objects 5 | #' 6 | #' See core documentation in [formatters::formatters-package] for descriptions of these functions. 7 | #' 8 | #' @inheritParams formatters::toString 9 | #' @param x (`listing_df`)\cr the listing. 10 | #' @param ... additional parameters passed to [formatters::toString()]. 11 | #' 12 | #' @method print listing_df 13 | #' 14 | #' @export 15 | #' @name listing_methods 16 | print.listing_df <- function(x, 17 | widths = NULL, 18 | tf_wrap = FALSE, 19 | max_width = NULL, 20 | fontspec = NULL, 21 | col_gap = 3L, 22 | round_type = c("iec", "sas"), 23 | ...) { 24 | tryCatch( 25 | { 26 | cat( 27 | toString( 28 | matrix_form(x, fontspec = fontspec, col_gap = col_gap), 29 | widths = widths, 30 | tf_wrap = tf_wrap, 31 | max_width = max_width, 32 | fontspec = fontspec, 33 | col_gap = col_gap, 34 | round_type = round_type, 35 | ... 36 | ) 37 | ) 38 | }, 39 | error = function(e) { 40 | if (nrow(x) == 0) { 41 | print("No observation in the listing object.") 42 | } else { 43 | stop(e) 44 | } 45 | } 46 | ) 47 | invisible(x) 48 | } 49 | 50 | #' @exportMethod toString 51 | #' @name listing_methods 52 | #' @aliases toString,listing_df-method 53 | setMethod("toString", "listing_df", function(x, 54 | widths = NULL, 55 | fontspec = NULL, 56 | col_gap = 3L, 57 | round_type = c("iec", "sas"), 58 | ...) { 59 | toString( 60 | matrix_form(x, fontspec = fontspec, col_gap = col_gap, round_type = round_type), 61 | fontspec = fontspec, 62 | col_gap = col_gap, 63 | widths = widths, 64 | round_type = round_type, 65 | ... 66 | ) 67 | }) 68 | 69 | ## because rle in base is too much of a stickler for being atomic 70 | basic_run_lens <- function(x) { 71 | n <- length(x) 72 | if (n == 0) { 73 | return(integer()) 74 | } 75 | 76 | y <- x[-1L] != x[-n] 77 | i <- c(which(y), n) 78 | diff(c(0L, i)) 79 | } 80 | 81 | #' @param df (`listing_df`)\cr the listing. 82 | #' @param colnm (`string`)\cr column name. 83 | #' @param colvec (`vector`)\cr column values based on `colnm`. 84 | #' 85 | #' @rdname vec_nlines 86 | #' @keywords internal 87 | format_colvector <- function(df, colnm, colvec = df[[colnm]], round_type = c("iec", "sas")) { 88 | if (missing(colvec) && !(colnm %in% names(df))) { 89 | stop("column ", colnm, " not found") 90 | } 91 | na_str <- obj_na_str(colvec) 92 | if (is.null(na_str) || all(is.na(na_str))) { 93 | na_str <- rep("-", max(1L, length(na_str))) 94 | } 95 | 96 | strvec <- vapply(colvec, format_value, "", format = obj_format(colvec), na_str = na_str, round_type = round_type) 97 | strvec 98 | } 99 | 100 | #' Utilities for formatting a listing column 101 | #' 102 | #' For `vec_nlines`, calculate the number of lines each element of a column vector will 103 | #' take to render. For `format_colvector`, 104 | #' 105 | #' @param vec (`vector`)\cr a column vector to be rendered into ASCII. 106 | #' @param max_width (`numeric(1)` or `NULL`)\cr the width to render the column with. 107 | #' @return (`numeric`)\cr a vector of the number of lines element-wise that will be 108 | #' needed to render the elements of `vec` to width `max_width`. 109 | #' 110 | #' @keywords internal 111 | setGeneric("vec_nlines", function(vec, max_width = NULL, fontspec = dflt_courier, round_type = c("iec", "sas")) { 112 | standardGeneric("vec_nlines") 113 | }) 114 | 115 | #' @param vec (`vector`)\cr a vector. 116 | #' 117 | #' @rdname vec_nlines 118 | #' @keywords internal 119 | setMethod("vec_nlines", "ANY", function(vec, max_width = NULL, fontspec = dflt_courier, round_type = c("iec", "sas")) { 120 | round_type <- match.arg(round_type) 121 | if (is.null(max_width)) { 122 | max_width <- floor(0.9 * getOption("width")) # default of base::strwrap 123 | # NB: flooring as it is used as <= (also in base::strwrap) 124 | } 125 | # in formatters for characters 126 | unlist(lapply(format_colvector(colvec = vec, round_type = round_type), nlines, 127 | max_width = max_width, fontspec = fontspec 128 | )) 129 | }) 130 | 131 | ## setMethod("vec_nlines", "character", function(vec, max_width = NULL) { 132 | ## strvec <- wrap_txt(format_colvector(colvec = vec), width = max_width, collapse = "\n") 133 | ## mtchs <- gregexpr("\n", strvec, fixed = TRUE) 134 | ## 1L + vapply(mtchs, function(vi) sum(vi > 0), 1L) 135 | ## }) 136 | 137 | ## setMethod("vec_nlines", "factor", function(vec, max_width = NULL) { 138 | ## lvl_nlines <- vec_nlines(levels(vec), max_width = max_width) 139 | ## ret <- lvl_nlines[vec] 140 | ## ret[is.na(ret)] <- format_value(NA_character 141 | ## }) 142 | 143 | 144 | 145 | #' Make pagination data frame for a listing 146 | #' 147 | #' @inheritParams formatters::make_row_df 148 | #' @param tt (`listing_df`)\cr the listing to be rendered. 149 | #' @param visible_only (`flag`)\cr ignored, as listings do not have 150 | #' non-visible structural elements. 151 | #' 152 | #' @return a `data.frame` with pagination information. 153 | #' 154 | #' @seealso [formatters::make_row_df()] 155 | #' 156 | #' @examples 157 | #' lsting <- as_listing(mtcars) 158 | #' mf <- matrix_form(lsting) 159 | #' 160 | #' @export 161 | setMethod( 162 | "make_row_df", "listing_df", 163 | function(tt, colwidths = NULL, visible_only = TRUE, 164 | rownum = 0, 165 | indent = 0L, 166 | path = character(), 167 | incontent = FALSE, 168 | repr_ext = 0L, 169 | repr_inds = integer(), 170 | sibpos = NA_integer_, 171 | nsibs = NA_integer_, 172 | fontspec = dflt_courier, 173 | round_type = c("iec", "sas")) { 174 | ## assume sortedness by keycols 175 | keycols <- get_keycols(tt) 176 | dispcols <- listing_dispcols(tt) 177 | abs_rownumber <- seq_along(tt[[1]]) 178 | if (length(keycols) >= 1) { 179 | runlens <- basic_run_lens(tt[[tail(keycols, 1)]]) 180 | } else { 181 | runlens <- rep(1, NROW(tt)) 182 | } 183 | sibpos <- unlist(lapply(runlens, seq_len)) 184 | nsibs <- rep(runlens, times = runlens) 185 | extents <- rep(1L, nrow(tt)) 186 | if (length(colwidths) > 0 && length(colwidths) != length(dispcols)) { 187 | stop( 188 | "Non-null colwidths vector must be the same length as the number of display columns.\n", 189 | "Got: ", length(colwidths), "(", length(dispcols), " disp cols)." 190 | ) 191 | } 192 | if (length(colwidths) > 0) { 193 | names(colwidths) <- dispcols 194 | } 195 | ## extents is a row-wise vector of extents, for each col, we update 196 | ## if that column has any rows wider than the previously recorded extent. 197 | for (col in dispcols) { 198 | ## duplicated from matrix_form method, refactor! 199 | col_ext <- vec_nlines(tt[[col]], max_width = colwidths[col], fontspec = fontspec, round_type = round_type) 200 | extents <- ifelse(col_ext > extents, col_ext, extents) 201 | } 202 | ret <- data.frame( 203 | label = "", name = "", 204 | abs_rownumber = abs_rownumber, 205 | path = I(as.list(rep(NA_character_, NROW(tt)))), 206 | pos_in_siblings = sibpos, 207 | n_siblings = nsibs, 208 | self_extent = extents, 209 | par_extent = 0L, 210 | reprint_inds = I(replicate(NROW(tt), list(integer()))), 211 | node_class = "listing_df", 212 | indent = 0L, 213 | nrowrefs = 0L, ## XXX this doesn't support footnotes 214 | ncellrefs = 0L, ## XXX this doesn't support footnotes 215 | nreflines = 0L, ## XXX this doesn't support footnotes 216 | force_page = FALSE, 217 | page_title = NA_character_, 218 | trailing_sep = NA_character_ 219 | ) 220 | stopifnot(identical( 221 | names(ret), 222 | names(pagdfrow( 223 | nm = "", lab = "", rnum = 1L, pth = NA_character_, extent = 1L, 224 | rclass = "" 225 | )) 226 | )) 227 | ret 228 | } 229 | ) 230 | 231 | ## tt$sibpos <- unlist(lapply( 232 | ## ## don't support pathing for now 233 | ## tt$path <- I(lapply(1:NROW(tt), 234 | ## function(i) { 235 | ## retpath <- character(2*length(keycols)) 236 | ## for(j in seq_along(keycols)) { 237 | ## retpath[2*j - 1] <- keycols[j] 238 | ## retpath[2*j] <- tt[i, keycols[j], drop = TRUE] 239 | ## } 240 | ## retpath 241 | ## })) 242 | ## spl <- split(tt, tt[keycols]) 243 | ## spl <- spl[vapply(spl, function(y) NROW(y) > 0, NA)] 244 | ## dfs <- lapply(spl, function(df) { 245 | ## df <- df[order(df$abs_rownumber),] 246 | ## ndf <- NROW(df) 247 | ## lapply(1:ndf, function(i) { 248 | ## rw <- df[i,] 249 | ## stopifnot(nrow(rw) == 1) 250 | ## pagdfrow(nm = "", 251 | ## lab = "", 252 | ## rnum = rw$abs_rownumber, 253 | ## pth = NA_character_, 254 | ## sibpos = i, 255 | ## nsibs = ndf, 256 | ## extent = 1L, 257 | ## rclass = "listing_df", 258 | ## repind = integer()) 259 | ## }) 260 | ## }) 261 | ## ret <- do.call(rbind, unlist(dfs, recursive = FALSE)) 262 | ## ret <- ret[order(ret$abs_rownumber),] 263 | ## ret 264 | ## }) 265 | 266 | #' @inheritParams base::Extract 267 | #' @param x (`listing_df`)\cr the listing. 268 | #' @param i (`any`)\cr object passed to base `[` methods. 269 | #' @param j (`any`)\cr object passed to base `[` methods. 270 | #' 271 | #' @export 272 | #' @aliases [,listing_df-method 273 | #' @rdname listing_methods 274 | setMethod( 275 | "[", "listing_df", 276 | function(x, i, j, drop = FALSE) { 277 | xattr <- attributes(x) 278 | xattr$names <- xattr$names[j] 279 | res <- NextMethod() 280 | if (!drop) { 281 | attributes(res) <- xattr 282 | } 283 | res 284 | } 285 | ) 286 | 287 | #' @rdname listing_methods 288 | #' @param obj (`listing_df`)\cr the listing. 289 | #' 290 | #' @return 291 | #' * Accessor methods return the value of the aspect of `obj`. 292 | #' * Setter methods return `obj` with the relevant element of the listing updated. 293 | #' 294 | #' @examples 295 | #' lsting <- as_listing(mtcars) 296 | #' main_title(lsting) <- "Hi there" 297 | #' 298 | #' main_title(lsting) 299 | #' 300 | #' @export 301 | setMethod( 302 | "main_title", "listing_df", 303 | function(obj) attr(obj, "main_title") %||% character() 304 | ) 305 | 306 | #' @rdname listing_methods 307 | #' @export 308 | setMethod( 309 | "subtitles", "listing_df", 310 | function(obj) attr(obj, "subtitles") %||% character() 311 | ) 312 | 313 | #' @rdname listing_methods 314 | #' @export 315 | setMethod( 316 | "main_footer", "listing_df", 317 | function(obj) attr(obj, "main_footer") %||% character() 318 | ) 319 | 320 | #' @rdname listing_methods 321 | #' @export 322 | setMethod( 323 | "prov_footer", "listing_df", 324 | function(obj) attr(obj, "prov_footer") %||% character() 325 | ) 326 | 327 | .chk_value <- function(val, fname, len_one = FALSE, null_ok = TRUE) { 328 | if (null_ok && is.null(val)) { 329 | return(TRUE) 330 | } 331 | if (!is.character(val)) { 332 | stop("value for ", fname, " must be a character, got ", 333 | "object of class: ", paste(class(val), collapse = ","), 334 | call. = FALSE 335 | ) 336 | } 337 | if (len_one && length(val) > 1) { 338 | stop( 339 | "value for ", fname, " must be length <= 1, got ", 340 | "vector of length ", length(val) 341 | ) 342 | } 343 | TRUE 344 | } 345 | 346 | #' @rdname listing_methods 347 | #' @export 348 | setMethod( 349 | "main_title<-", "listing_df", 350 | function(obj, value) { 351 | ## length 1 restriction is to match rtables behavior 352 | ## which currently enforces this (though incompletely) 353 | .chk_value(value, "main_title", len_one = TRUE) 354 | attr(obj, "main_title") <- value 355 | obj 356 | } 357 | ) 358 | 359 | #' @rdname listing_methods 360 | #' @export 361 | setMethod( 362 | "subtitles<-", "listing_df", 363 | function(obj, value) { 364 | .chk_value(value, "subtitles") 365 | attr(obj, "subtitles") <- value 366 | obj 367 | } 368 | ) 369 | 370 | #' @rdname listing_methods 371 | #' @export 372 | setMethod( 373 | "main_footer<-", "listing_df", 374 | function(obj, value) { 375 | .chk_value(value, "main_footer") 376 | attr(obj, "main_footer") <- value 377 | obj 378 | } 379 | ) 380 | 381 | #' @rdname listing_methods 382 | #' @export 383 | setMethod( 384 | "prov_footer<-", "listing_df", 385 | function(obj, value) { 386 | .chk_value(value, "prov_footer") 387 | attr(obj, "prov_footer") <- value 388 | obj 389 | } 390 | ) 391 | 392 | #' @rdname listing_methods 393 | #' @export 394 | setMethod( 395 | "num_rep_cols", "listing_df", 396 | function(obj) { 397 | length(get_keycols(obj)) 398 | } 399 | ) 400 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | editor_options: 4 | chunk_output_type: console 5 | markdown: 6 | wrap: 72 7 | --- 8 | 9 | 10 | 11 | ```{r, echo = FALSE} 12 | knitr::opts_chunk$set( 13 | collapse = TRUE, 14 | comment = "#>", 15 | fig.path = "man/figures/README-" 16 | ) 17 | ``` 18 | 19 | # rlistings 20 | 21 | 22 | 23 | [![Check 🛠](https://github.com/insightsengineering/rlistings/actions/workflows/check.yaml/badge.svg)](https://insightsengineering.github.io/rlistings/main/unit-test-report/) 24 | [![Docs 📚](https://github.com/insightsengineering/rlistings/actions/workflows/docs.yaml/badge.svg)](https://insightsengineering.github.io/rlistings/) 25 | [![Code Coverage 📔](https://raw.githubusercontent.com/insightsengineering/rlistings/_xml_coverage_reports/data/main/badge.svg)](https://insightsengineering.github.io/rlistings/main/coverage-report/) 26 | 27 | ![GitHub forks](https://img.shields.io/github/forks/insightsengineering/rlistings?style=social) 28 | ![GitHub Repo stars](https://img.shields.io/github/stars/insightsengineering/rlistings?style=social) 29 | 30 | ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/insightsengineering/rlistings) 31 | ![GitHub contributors](https://img.shields.io/github/contributors/insightsengineering/rlistings) 32 | ![GitHub last commit](https://img.shields.io/github/last-commit/insightsengineering/rlistings) 33 | ![GitHub pull requests](https://img.shields.io/github/issues-pr/insightsengineering/rlistings) 34 | ![GitHub repo size](https://img.shields.io/github/repo-size/insightsengineering/rlistings) 35 | ![GitHub language count](https://img.shields.io/github/languages/count/insightsengineering/rlistings) 36 | [![WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip) 37 | [![Current Version](https://img.shields.io/github/r-package/v/insightsengineering/rlistings/main?color=purple\&label=package%20version)](https://github.com/insightsengineering/rlistings/tree/main) 38 | [![Open Issues](https://img.shields.io/github/issues-raw/insightsengineering/rlistings?color=red\&label=open%20issues)](https://github.com/insightsengineering/rlistings/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) 39 | 40 | 41 | ## Listings with R 42 | 43 | The `rlistings` R package is a package that was designed to create and 44 | display listings with R. The focus of this package is to provide 45 | functionality for value formatting and ASCII rendering infrastructure 46 | for tables and listings. Many of the functions contained in `rlistings` 47 | depend on the 48 | [`formatters`](https://insightsengineering.github.io/formatters/) 49 | package, which provides a framework for ASCII rendering and is available 50 | on CRAN. 51 | 52 | `rlistings` development is driven by the need to create regulatory ready 53 | listings for health authority review. Some of the key requirements for 54 | this undertaking are listed below: 55 | 56 | - flexible formatting (pagesize, column widths, alignment, labels, 57 | etc.) 58 | - multiple output formats (csv, out, txt) 59 | - repeated key columns 60 | - flexible pagination in both horizontal and vertical directions 61 | - titles and footnotes 62 | 63 | `rlistings` currently covers some of these requirements, and remains 64 | under active development. 65 | 66 | ## Installation 67 | 68 | `rlistings` is available on CRAN and you can install the latest released 69 | version with: 70 | 71 | ``` r 72 | install.packages("rlistings") 73 | ``` 74 | 75 | or you can install the latest development version directly from GitHub 76 | with: 77 | 78 | ``` r 79 | # install.packages("pak") 80 | pak::pak("insightsengineering/rlistings") 81 | ``` 82 | 83 | Packaged releases (both those on CRAN and those between official CRAN 84 | releases) can be found in the [releases 85 | list](https://github.com/insightsengineering/rlistings/releases). 86 | 87 | To understand how to use this package, please refer to the [Introduction to `rlistings`](https://insightsengineering.github.io/rlistings/latest-tag/articles/rlistings.html) article, which provides multiple examples of code implementation. 88 | 89 | ## Cheatsheet 90 | 91 | 92 | 93 | ## Usage 94 | 95 | The following example shows a simple listing and its printed output. 96 | 97 | ```{r} 98 | library(rlistings) 99 | 100 | # Reducing the data 101 | mtcars_ex <- mtcars %>% dplyr::mutate("car" = rownames(mtcars)) 102 | 103 | as_listing(mtcars_ex, 104 | key_cols = c("gear", "carb"), 105 | disp_cols = c("gear", "carb", "qsec", "car") 106 | ) %>% head() 107 | ``` 108 | 109 | ## Acknowledgment 110 | 111 | This package is a result of a joint effort by many developers and 112 | stakeholders. We would like to thank everyone who contributed so far! 113 | 114 | ## Stargazers and Forkers 115 | 116 | ### Stargazers over time 117 | 118 | [![Stargazers over 119 | time](https://starchart.cc/insightsengineering/rlistings.svg)](https://starchart.cc/insightsengineering/rlistings) 120 | 121 | ### Stargazers 122 | 123 | [![Stargazers repo roster for 124 | @insightsengineering/rlistings](https://reporoster.com/stars/insightsengineering/rlistings)](https://github.com/insightsengineering/rlistings/stargazers) 125 | 126 | [![Forkers repo roster for 127 | @insightsengineering/rlistings](https://reporoster.com/forks/insightsengineering/rlistings)](https://github.com/insightsengineering/rlistings/network/members) 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # rlistings 5 | 6 | 7 | 8 | [![Check 9 | 🛠](https://github.com/insightsengineering/rlistings/actions/workflows/check.yaml/badge.svg)](https://insightsengineering.github.io/rlistings/main/unit-test-report/) 10 | [![Docs 11 | 📚](https://github.com/insightsengineering/rlistings/actions/workflows/docs.yaml/badge.svg)](https://insightsengineering.github.io/rlistings/) 12 | [![Code Coverage 13 | 📔](https://raw.githubusercontent.com/insightsengineering/rlistings/_xml_coverage_reports/data/main/badge.svg)](https://insightsengineering.github.io/rlistings/main/coverage-report/) 14 | 15 | ![GitHub 16 | forks](https://img.shields.io/github/forks/insightsengineering/rlistings?style=social) 17 | ![GitHub Repo 18 | stars](https://img.shields.io/github/stars/insightsengineering/rlistings?style=social) 19 | 20 | ![GitHub commit 21 | activity](https://img.shields.io/github/commit-activity/m/insightsengineering/rlistings) 22 | ![GitHub 23 | contributors](https://img.shields.io/github/contributors/insightsengineering/rlistings) 24 | ![GitHub last 25 | commit](https://img.shields.io/github/last-commit/insightsengineering/rlistings) 26 | ![GitHub pull 27 | requests](https://img.shields.io/github/issues-pr/insightsengineering/rlistings) 28 | ![GitHub repo 29 | size](https://img.shields.io/github/repo-size/insightsengineering/rlistings) 30 | ![GitHub language 31 | count](https://img.shields.io/github/languages/count/insightsengineering/rlistings) 32 | [![WIP – Initial development is in progress, but there has not yet been 33 | a stable, usable release suitable for the 34 | public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip) 35 | [![Current 36 | Version](https://img.shields.io/github/r-package/v/insightsengineering/rlistings/main?color=purple&label=package%20version)](https://github.com/insightsengineering/rlistings/tree/main) 37 | [![Open 38 | Issues](https://img.shields.io/github/issues-raw/insightsengineering/rlistings?color=red&label=open%20issues)](https://github.com/insightsengineering/rlistings/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) 39 | 40 | 41 | ## Listings with R 42 | 43 | The `rlistings` R package is a package that was designed to create and 44 | display listings with R. The focus of this package is to provide 45 | functionality for value formatting and ASCII rendering infrastructure 46 | for tables and listings. Many of the functions contained in `rlistings` 47 | depend on the 48 | [`formatters`](https://insightsengineering.github.io/formatters/) 49 | package, which provides a framework for ASCII rendering and is available 50 | on CRAN. 51 | 52 | `rlistings` development is driven by the need to create regulatory ready 53 | listings for health authority review. Some of the key requirements for 54 | this undertaking are listed below: 55 | 56 | - flexible formatting (pagesize, column widths, alignment, labels, etc.) 57 | - multiple output formats (csv, out, txt) 58 | - repeated key columns 59 | - flexible pagination in both horizontal and vertical directions 60 | - titles and footnotes 61 | 62 | `rlistings` currently covers some of these requirements, and remains 63 | under active development. 64 | 65 | ## Installation 66 | 67 | `rlistings` is available on CRAN and you can install the latest released 68 | version with: 69 | 70 | ``` r 71 | install.packages("rlistings") 72 | ``` 73 | 74 | or you can install the latest development version directly from GitHub 75 | with: 76 | 77 | ``` r 78 | # install.packages("pak") 79 | pak::pak("insightsengineering/rlistings") 80 | ``` 81 | 82 | Packaged releases (both those on CRAN and those between official CRAN 83 | releases) can be found in the [releases 84 | list](https://github.com/insightsengineering/rlistings/releases). 85 | 86 | To understand how to use this package, please refer to the [Introduction 87 | to 88 | `rlistings`](https://insightsengineering.github.io/rlistings/latest-tag/articles/rlistings.html) 89 | article, which provides multiple examples of code implementation. 90 | 91 | ## Cheatsheet 92 | 93 | 94 | 95 | ## Usage 96 | 97 | The following example shows a simple listing and its printed output. 98 | 99 | ``` r 100 | library(rlistings) 101 | #> Loading required package: formatters 102 | #> 103 | #> Attaching package: 'formatters' 104 | #> The following object is masked from 'package:base': 105 | #> 106 | #> %||% 107 | #> Loading required package: tibble 108 | 109 | # Reducing the data 110 | mtcars_ex <- mtcars %>% dplyr::mutate("car" = rownames(mtcars)) 111 | 112 | as_listing(mtcars_ex, 113 | key_cols = c("gear", "carb"), 114 | disp_cols = c("gear", "carb", "qsec", "car") 115 | ) %>% head() 116 | #> gear carb qsec car 117 | #> ——————————————————————————————————————— 118 | #> 3 1 19.44 Hornet 4 Drive 119 | #> 20.22 Valiant 120 | #> 20.01 Toyota Corona 121 | #> 2 17.02 Hornet Sportabout 122 | #> 16.87 Dodge Challenger 123 | #> 17.3 AMC Javelin 124 | ``` 125 | 126 | ## Acknowledgment 127 | 128 | This package is a result of a joint effort by many developers and 129 | stakeholders. We would like to thank everyone who contributed so far! 130 | 131 | ## Stargazers and Forkers 132 | 133 | ### Stargazers over time 134 | 135 | [![Stargazers over 136 | time](https://starchart.cc/insightsengineering/rlistings.svg)](https://starchart.cc/insightsengineering/rlistings) 137 | 138 | ### Stargazers 139 | 140 | [![Stargazers repo roster for 141 | @insightsengineering/rlistings](https://reporoster.com/stars/insightsengineering/rlistings)](https://github.com/insightsengineering/rlistings/stargazers) 142 | 143 | [![Forkers repo roster for 144 | @insightsengineering/rlistings](https://reporoster.com/forks/insightsengineering/rlistings)](https://github.com/insightsengineering/rlistings/network/members) 145 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | --- 2 | url: https://insightsengineering.github.io/rlistings 3 | 4 | template: 5 | package: nesttemplate 6 | 7 | navbar: 8 | structure: 9 | left: [intro, reference, articles, tutorials, news, reports] 10 | right: [search, github] 11 | components: 12 | reports: 13 | text: Reports 14 | menu: 15 | - text: Coverage report 16 | href: coverage-report/ 17 | - text: Unit test report 18 | href: unit-test-report/ 19 | - text: Non-CRAN unit test report 20 | href: unit-test-report-non-cran/ 21 | github: 22 | icon: fa-github 23 | href: https://github.com/insightsengineering/rlistings 24 | 25 | articles: 26 | - title: Articles 27 | navbar: ~ 28 | contents: 29 | - col_formatting 30 | - large_list 31 | - pagination 32 | - ref_footnotes 33 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | ADAE 2 | Biomarker 3 | Cheatsheet 4 | Forkers 5 | Hoffmann 6 | IEC 7 | ORCID 8 | README 9 | Rua 10 | WIP 11 | api 12 | cheatsheet 13 | csv 14 | customizations 15 | de 16 | df 17 | droppage 18 | formatter 19 | funder 20 | ie 21 | iec 22 | pagesize 23 | paginations 24 | pathing 25 | peforms 26 | pre 27 | sas 28 | -------------------------------------------------------------------------------- /inst/cheatsheet/rlistings_cheatsheet_03-24.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/inst/cheatsheet/rlistings_cheatsheet_03-24.pdf -------------------------------------------------------------------------------- /inst/cheatsheet/rlistings_cheatsheet_03-24.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/inst/cheatsheet/rlistings_cheatsheet_03-24.pptx -------------------------------------------------------------------------------- /inst/cheatsheet/rlistings_cheatsheet_03-24_thumbs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/inst/cheatsheet/rlistings_cheatsheet_03-24_thumbs.png -------------------------------------------------------------------------------- /man/figures/lifecycle-archived.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclearchivedarchived -------------------------------------------------------------------------------- /man/figures/lifecycle-defunct.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledefunctdefunct -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycledeprecateddeprecated -------------------------------------------------------------------------------- /man/figures/lifecycle-experimental.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecycleexperimentalexperimental -------------------------------------------------------------------------------- /man/figures/lifecycle-maturing.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclematuringmaturing -------------------------------------------------------------------------------- /man/figures/lifecycle-questioning.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclequestioningquestioning -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclestablestable -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | lifecyclelifecyclesupersededsuperseded -------------------------------------------------------------------------------- /man/listing_methods.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings_methods.R 3 | \name{listing_methods} 4 | \alias{listing_methods} 5 | \alias{print.listing_df} 6 | \alias{toString,listing_df-method} 7 | \alias{[,listing_df-method} 8 | \alias{main_title,listing_df-method} 9 | \alias{subtitles,listing_df-method} 10 | \alias{main_footer,listing_df-method} 11 | \alias{prov_footer,listing_df-method} 12 | \alias{main_title<-,listing_df-method} 13 | \alias{subtitles<-,listing_df-method} 14 | \alias{main_footer<-,listing_df-method} 15 | \alias{prov_footer<-,listing_df-method} 16 | \alias{num_rep_cols,listing_df-method} 17 | \title{Methods for \code{listing_df} objects} 18 | \usage{ 19 | \method{print}{listing_df}( 20 | x, 21 | widths = NULL, 22 | tf_wrap = FALSE, 23 | max_width = NULL, 24 | fontspec = NULL, 25 | col_gap = 3L, 26 | round_type = c("iec", "sas"), 27 | ... 28 | ) 29 | 30 | \S4method{toString}{listing_df}( 31 | x, 32 | widths = NULL, 33 | fontspec = NULL, 34 | col_gap = 3L, 35 | round_type = c("iec", "sas"), 36 | ... 37 | ) 38 | 39 | \S4method{[}{listing_df}(x, i, j, drop = FALSE) 40 | 41 | \S4method{main_title}{listing_df}(obj) 42 | 43 | \S4method{subtitles}{listing_df}(obj) 44 | 45 | \S4method{main_footer}{listing_df}(obj) 46 | 47 | \S4method{prov_footer}{listing_df}(obj) 48 | 49 | \S4method{main_title}{listing_df}(obj) <- value 50 | 51 | \S4method{subtitles}{listing_df}(obj) <- value 52 | 53 | \S4method{main_footer}{listing_df}(obj) <- value 54 | 55 | \S4method{prov_footer}{listing_df}(obj) <- value 56 | 57 | \S4method{num_rep_cols}{listing_df}(obj) 58 | } 59 | \arguments{ 60 | \item{x}{(\code{listing_df})\cr the listing.} 61 | 62 | \item{widths}{(\code{numeric} or \code{NULL})\cr Proposed widths for the columns of \code{x}. The expected 63 | length of this numeric vector can be retrieved with \code{ncol(x) + 1} as the column of row names 64 | must also be considered.} 65 | 66 | \item{tf_wrap}{(\code{flag})\cr whether the text for title, subtitles, and footnotes should be wrapped.} 67 | 68 | \item{max_width}{(\code{integer(1)}, \code{string} or \code{NULL})\cr width that title and footer (including 69 | footnotes) materials should be word-wrapped to. If \code{NULL}, it is set to the current print width of the 70 | session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is 71 | used. Parameter is ignored if \code{tf_wrap = FALSE}.} 72 | 73 | \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for 74 | calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} 75 | 76 | \item{col_gap}{(\code{numeric(1)})\cr space (in characters) between columns.} 77 | 78 | \item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, 79 | the default, peforms rounding compliant with IEC 60559 (see details), while 80 | sas performs nearest-value rounding consistent with rounding within SAS.} 81 | 82 | \item{...}{additional parameters passed to \code{\link[formatters:tostring]{formatters::toString()}}.} 83 | 84 | \item{i}{(\code{any})\cr object passed to base \code{[} methods.} 85 | 86 | \item{j}{(\code{any})\cr object passed to base \code{[} methods.} 87 | 88 | \item{drop}{relevant for matrices and arrays. If \code{TRUE} the result is 89 | coerced to the lowest possible dimension (see the examples). This 90 | only works for extracting elements, not for the replacement. See 91 | \code{\link[base]{drop}} for further details. 92 | } 93 | 94 | \item{obj}{(\code{listing_df})\cr the listing.} 95 | 96 | \item{value}{typically an array-like \R object of a similar class as 97 | \code{x}.} 98 | } 99 | \value{ 100 | \itemize{ 101 | \item Accessor methods return the value of the aspect of \code{obj}. 102 | \item Setter methods return \code{obj} with the relevant element of the listing updated. 103 | } 104 | } 105 | \description{ 106 | See core documentation in \link[formatters:formatters-package]{formatters::formatters-package} for descriptions of these functions. 107 | } 108 | \examples{ 109 | lsting <- as_listing(mtcars) 110 | main_title(lsting) <- "Hi there" 111 | 112 | main_title(lsting) 113 | 114 | } 115 | -------------------------------------------------------------------------------- /man/listings.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings.R 3 | \name{as_listing} 4 | \alias{as_listing} 5 | \alias{spanning_col_label_df} 6 | \alias{spanning_col_label_df<-} 7 | \alias{as_keycol} 8 | \alias{is_keycol} 9 | \alias{get_keycols} 10 | \alias{listing_dispcols} 11 | \alias{add_listing_dispcol} 12 | \alias{listing_dispcols<-} 13 | \alias{align_colnames} 14 | \alias{align_colnames<-} 15 | \alias{add_listing_col} 16 | \title{Create a listing from a \code{data.frame} or \code{tibble}} 17 | \usage{ 18 | as_listing( 19 | df, 20 | key_cols = names(df)[1], 21 | disp_cols = NULL, 22 | non_disp_cols = NULL, 23 | sort_cols = key_cols, 24 | unique_rows = FALSE, 25 | default_formatting = list(all = fmt_config()), 26 | col_formatting = NULL, 27 | align_colnames = FALSE, 28 | add_trailing_sep = NULL, 29 | trailing_sep = " ", 30 | main_title = NULL, 31 | subtitles = NULL, 32 | main_footer = NULL, 33 | prov_footer = NULL, 34 | split_into_pages_by_var = NULL, 35 | spanning_col_labels = no_spans_df 36 | ) 37 | 38 | spanning_col_label_df(df) 39 | 40 | spanning_col_label_df(df) <- value 41 | 42 | as_keycol(vec) 43 | 44 | is_keycol(vec) 45 | 46 | get_keycols(df) 47 | 48 | listing_dispcols(df) 49 | 50 | add_listing_dispcol(df, new) 51 | 52 | listing_dispcols(df) <- value 53 | 54 | align_colnames(df) 55 | 56 | align_colnames(df) <- value 57 | 58 | add_listing_col( 59 | df, 60 | name, 61 | fun = NULL, 62 | format = NULL, 63 | na_str = "NA", 64 | align = "left" 65 | ) 66 | } 67 | \arguments{ 68 | \item{df}{(\code{data.frame} or \code{listing_df})\cr the \code{data.frame} to be converted to a listing or 69 | \code{listing_df} to be modified.} 70 | 71 | \item{key_cols}{(\code{character})\cr vector of names of columns which should be treated as \emph{key columns} 72 | when rendering the listing. Key columns allow you to group repeat occurrences.} 73 | 74 | \item{disp_cols}{(\code{character} or \code{NULL})\cr vector of names of non-key columns which should be 75 | displayed when the listing is rendered. Defaults to all columns of \code{df} not named in \code{key_cols} or 76 | \code{non_disp_cols}.} 77 | 78 | \item{non_disp_cols}{(\code{character} or \code{NULL})\cr vector of names of non-key columns to be excluded as display 79 | columns. All other non-key columns are treated as display columns. Ignored if \code{disp_cols} is non-\code{NULL}.} 80 | 81 | \item{sort_cols}{(\code{character} or \code{NULL})\cr vector of names of columns (in order) which should be used to sort the 82 | listing. Defaults to \code{key_cols}. If \code{NULL}, no sorting will be performed.} 83 | 84 | \item{unique_rows}{(\code{flag})\cr whether only unique rows should be included in the listing. Defaults to \code{FALSE}.} 85 | 86 | \item{default_formatting}{(\code{list})\cr a named list of default column format configurations to apply when rendering 87 | the listing. Each name-value pair consists of a name corresponding to a data class (or "numeric" for all 88 | unspecified numeric classes) and a value of type \code{fmt_config} with the format configuration that should be 89 | implemented for columns of that class. If named element "all" is included in the list, this configuration will be 90 | used for all data classes not specified. Objects of type \code{fmt_config} can take 3 arguments: \code{format}, \code{na_str}, 91 | and \code{align}.} 92 | 93 | \item{col_formatting}{(\code{list})\cr a named list of custom column formatting configurations to apply to specific 94 | columns when rendering the listing. Each name-value pair consists of a name corresponding to a column name and a 95 | value of type \code{fmt_config} with the formatting configuration that should be implemented for that column. Objects 96 | of type \code{fmt_config} can take 3 arguments: \code{format}, \code{na_str}, and \code{align}. Defaults to \code{NULL}.} 97 | 98 | \item{align_colnames}{(\code{flag})\cr whether the column titles should have the same alignment as their columns. All 99 | titles default to \code{"center"} alignment if \code{FALSE} (default). This can be changed with \code{align_colnames()}.} 100 | 101 | \item{add_trailing_sep}{(\code{character} or \code{numeric} or \code{NULL})\cr If it is assigned to one or more column names, 102 | a trailing separator will be added between groups with identical values for that column. Numeric option allows 103 | the user to specify in which rows it can be added. Defaults to \code{NULL}.} 104 | 105 | \item{trailing_sep}{(\code{character(1)})\cr The separator to be added between groups. The character will be repeated to 106 | fill the row.} 107 | 108 | \item{main_title}{(\code{string} or \code{NULL})\cr the main title for the listing, or \code{NULL} (the default).} 109 | 110 | \item{subtitles}{(\code{character} or \code{NULL})\cr a vector of subtitles for the listing, or \code{NULL} (the default).} 111 | 112 | \item{main_footer}{(\code{character} or \code{NULL})\cr a vector of main footer lines for the listing, or \code{NULL} (the default).} 113 | 114 | \item{prov_footer}{(\code{character} or \code{NULL})\cr a vector of provenance footer lines for the listing, or \code{NULL} 115 | (the default). Each string element is placed on a new line.} 116 | 117 | \item{split_into_pages_by_var}{(\code{character} or \code{NULL})\cr the name of a variable for on the listing should be split 118 | into pages, with each page corresponding to one unique value/level of the variable. See 119 | \code{\link[=split_into_pages_by_var]{split_into_pages_by_var()}} for more details.} 120 | 121 | \item{spanning_col_labels}{(\code{data.frame})\cr A data.frame with the columns 122 | \code{span_level}, \code{label}, \code{start}, and \code{span} defining 0 or more levels of 123 | addition spanning (ie grouping) of columns. Defaults to no additional spanning labels.} 124 | 125 | \item{value}{(\code{string})\cr new value.} 126 | 127 | \item{vec}{(\code{string})\cr name of a column vector from a \code{listing_df} object to be annotated as a key column.} 128 | 129 | \item{new}{(\code{character})\cr vector of names of columns to be added to 130 | the set of display columns.} 131 | 132 | \item{name}{(\code{string})\cr name of the existing or new column to be 133 | displayed when the listing is rendered.} 134 | 135 | \item{fun}{(\code{function} or \code{NULL})\cr a function which accepts \code{df} and 136 | returns the vector for a new column, which is added to \code{df} as 137 | \code{name}, or \code{NULL} if marking an existing column as a listing column.} 138 | 139 | \item{format}{(\code{string} or \code{function})\cr a format label (string) or formatter function.} 140 | 141 | \item{na_str}{(\code{string})\cr string that should be displayed in place of missing values.} 142 | 143 | \item{align}{(\code{string})\cr alignment values should be rendered with.} 144 | } 145 | \value{ 146 | A \code{listing_df} object, sorted by its key columns. 147 | 148 | \code{df} with \code{name} created (if necessary) and marked for 149 | display during rendering. 150 | } 151 | \description{ 152 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} 153 | 154 | Create listings displaying \code{key_cols} and \code{disp_cols} to produce a compact and 155 | elegant representation of the input \code{data.frame} or \code{tibble}. 156 | } 157 | \details{ 158 | At its core, a \code{listing_df} object is a \code{tbl_df} object with a customized 159 | print method and support for the formatting and pagination machinery provided by 160 | the \code{formatters} package. 161 | 162 | \code{listing_df} objects have two 'special' types of columns: key columns and display columns. 163 | 164 | Key columns act as indexes, which means a number of things in practice. 165 | 166 | All key columns are also display columns. 167 | 168 | \code{listing_df} objects are always sorted by their set of key columns at creation time. 169 | Any \code{listing_df} object which is not sorted by its full set of key columns (e.g., 170 | one whose rows have been reordered explicitly during creation) is invalid and the behavior 171 | when rendering or paginating that object is undefined. 172 | 173 | Each value of a key column is printed only once per page and per unique combination of 174 | values for all higher-priority (i.e., to the left of it) key columns. Locations 175 | where a repeated value would have been printed within a key column for the same 176 | higher-priority-key combination on the same page are rendered as empty space. 177 | Note, determination of which elements to display within a key column at rendering is 178 | based on the underlying value; any non-default formatting applied to the column 179 | has no effect on this behavior. 180 | 181 | Display columns are columns which should be rendered, but are not key columns. By 182 | default this is all non-key columns in the incoming data, but in need not be. 183 | Columns in the underlying data which are neither key nor display columns remain 184 | within the object available for computations but \emph{are not rendered during 185 | printing or export of the listing}. 186 | 187 | Spanning column labels are displayed centered above the individual labels 188 | of the columns they span across. \code{span_level} 1 is placed directly above 189 | the column labels, with higher "span_levels` displayed above it in ascending 190 | order. 191 | 192 | IF spanning column labels are present, a single spanning label cannot span 193 | across both key and non-key displayed columns simultaneously due to key 194 | columns' repetition after page breaks during horizontal pagination. Attempting 195 | to set a spanning column label which does so will result in an error. 196 | } 197 | \note{ 198 | Unlike in the \code{rtables} sister package, spanning labels here are purely 199 | decorative and do not reflect any structure among the columns modeled by 200 | \code{rlistings}. Thus, we cannot, e.g., use pathing to select columns under 201 | a certain spanning column label, or restrict horizontal pagination to 202 | leave 'groups' of columns implied by a spanning label intact. 203 | } 204 | \examples{ 205 | dat <- ex_adae 206 | 207 | # This example demonstrates the listing with key_cols (values are grouped by USUBJID) and 208 | # multiple lines in prov_footer 209 | lsting <- as_listing(dat[1:25, ], 210 | key_cols = c("USUBJID", "AESOC"), 211 | main_title = "Example Title for Listing", 212 | subtitles = "This is the subtitle for this Adverse Events Table", 213 | main_footer = "Main footer for the listing", 214 | prov_footer = c( 215 | "You can even add a subfooter", "Second element is place on a new line", 216 | "Third string" 217 | ) 218 | ) \%>\% 219 | add_listing_col("AETOXGR") \%>\% 220 | add_listing_col("BMRKR1", format = "xx.x") \%>\% 221 | add_listing_col("AESER / AREL", fun = function(df) paste(df$AESER, df$AREL, sep = " / ")) 222 | 223 | mat <- matrix_form(lsting) 224 | 225 | cat(toString(mat)) 226 | 227 | # This example demonstrates the listing table without key_cols 228 | # and specifying the cols with disp_cols. 229 | dat <- ex_adae 230 | lsting <- as_listing(dat[1:25, ], 231 | disp_cols = c("USUBJID", "AESOC", "RACE", "AETOXGR", "BMRKR1") 232 | ) 233 | 234 | mat <- matrix_form(lsting) 235 | 236 | cat(toString(mat)) 237 | 238 | # This example demonstrates a listing with format configurations specified 239 | # via the default_formatting and col_formatting arguments 240 | dat <- ex_adae 241 | dat$AENDY[3:6] <- NA 242 | lsting <- as_listing(dat[1:25, ], 243 | key_cols = c("USUBJID", "AESOC"), 244 | disp_cols = c("STUDYID", "SEX", "ASEQ", "RANDDT", "ASTDY", "AENDY"), 245 | default_formatting = list( 246 | all = fmt_config(align = "left"), 247 | numeric = fmt_config( 248 | format = "xx.xx", 249 | na_str = "", 250 | align = "right" 251 | ) 252 | ) 253 | ) \%>\% 254 | add_listing_col("BMRKR1", format = "xx.x", align = "center") 255 | 256 | mat <- matrix_form(lsting) 257 | 258 | cat(toString(mat)) 259 | 260 | } 261 | -------------------------------------------------------------------------------- /man/make_row_df-listing_df-method.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings_methods.R 3 | \name{make_row_df,listing_df-method} 4 | \alias{make_row_df,listing_df-method} 5 | \title{Make pagination data frame for a listing} 6 | \usage{ 7 | \S4method{make_row_df}{listing_df}( 8 | tt, 9 | colwidths = NULL, 10 | visible_only = TRUE, 11 | rownum = 0, 12 | indent = 0L, 13 | path = character(), 14 | incontent = FALSE, 15 | repr_ext = 0L, 16 | repr_inds = integer(), 17 | sibpos = NA_integer_, 18 | nsibs = NA_integer_, 19 | fontspec = dflt_courier, 20 | round_type = c("iec", "sas") 21 | ) 22 | } 23 | \arguments{ 24 | \item{tt}{(\code{listing_df})\cr the listing to be rendered.} 25 | 26 | \item{colwidths}{(\code{numeric})\cr internal detail, do not set manually.} 27 | 28 | \item{visible_only}{(\code{flag})\cr ignored, as listings do not have 29 | non-visible structural elements.} 30 | 31 | \item{rownum}{(\code{numeric(1)})\cr internal detail, do not set manually.} 32 | 33 | \item{indent}{(\code{integer(1)})\cr internal detail, do not set manually.} 34 | 35 | \item{path}{(\code{character})\cr path to the (sub)table represented by \code{tt}. Defaults to \code{character()}.} 36 | 37 | \item{incontent}{(\code{flag})\cr internal detail, do not set manually.} 38 | 39 | \item{repr_ext}{(\code{integer(1)})\cr internal detail, do not set manually.} 40 | 41 | \item{repr_inds}{(\code{integer})\cr internal detail, do not set manually.} 42 | 43 | \item{sibpos}{(\code{integer(1)})\cr internal detail, do not set manually.} 44 | 45 | \item{nsibs}{(\code{integer(1)})\cr internal detail, do not set manually.} 46 | 47 | \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for 48 | calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} 49 | 50 | \item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, 51 | the default, peforms rounding compliant with IEC 60559 (see details), while 52 | sas performs nearest-value rounding consistent with rounding within SAS.} 53 | } 54 | \value{ 55 | a \code{data.frame} with pagination information. 56 | } 57 | \description{ 58 | Make pagination data frame for a listing 59 | } 60 | \examples{ 61 | lsting <- as_listing(mtcars) 62 | mf <- matrix_form(lsting) 63 | 64 | } 65 | \seealso{ 66 | \code{\link[formatters:make_row_df]{formatters::make_row_df()}} 67 | } 68 | -------------------------------------------------------------------------------- /man/matrix_form-listing_df-method.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings.R 3 | \name{matrix_form,listing_df-method} 4 | \alias{matrix_form,listing_df-method} 5 | \title{Transform \code{rtable} to a list of matrices which can be used for outputting} 6 | \usage{ 7 | \S4method{matrix_form}{listing_df}( 8 | obj, 9 | indent_rownames = FALSE, 10 | expand_newlines = TRUE, 11 | fontspec = font_spec, 12 | col_gap = 3L, 13 | round_type = c("iec", "sas") 14 | ) 15 | } 16 | \arguments{ 17 | \item{obj}{(\code{ANY})\cr object to be transformed into a ready-to-render form (a \code{\link[formatters]{MatrixPrintForm}} object).} 18 | 19 | \item{indent_rownames}{(\code{flag})\cr silently ignored, as listings do not have row names 20 | nor indenting structure.} 21 | 22 | \item{expand_newlines}{(\code{flag})\cr this should always be \code{TRUE} for listings. We keep it 23 | for debugging reasons.} 24 | 25 | \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for 26 | calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} 27 | 28 | \item{col_gap}{(\code{numeric(1)})\cr the gap to be assumed between columns, in number of spaces with 29 | font specified by \code{fontspec}.} 30 | 31 | \item{round_type}{(\code{"iec"} or \code{"sas"})\cr the type of rounding to perform. iec, 32 | the default, peforms rounding compliant with IEC 60559 (see details), while 33 | sas performs nearest-value rounding consistent with rounding within SAS.} 34 | } 35 | \value{ 36 | a \link[formatters:MatrixPrintForm]{formatters::MatrixPrintForm} object. 37 | } 38 | \description{ 39 | Although \code{rtable}s are represented as a tree data structure when outputting the table to ASCII or HTML, 40 | it is useful to map the \code{rtable} to an in-between state with the formatted cells in a matrix form. 41 | } 42 | \examples{ 43 | lsting <- as_listing(mtcars) 44 | mf <- matrix_form(lsting) 45 | 46 | } 47 | \seealso{ 48 | \code{\link[formatters:matrix_form]{formatters::matrix_form()}} 49 | } 50 | -------------------------------------------------------------------------------- /man/paginate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/paginate_listing.R 3 | \name{paginate_listing} 4 | \alias{paginate_listing} 5 | \title{Paginate listings} 6 | \usage{ 7 | paginate_listing( 8 | lsting, 9 | page_type = "letter", 10 | font_family = "Courier", 11 | font_size = 8, 12 | lineheight = 1, 13 | landscape = FALSE, 14 | pg_width = NULL, 15 | pg_height = NULL, 16 | margins = c(top = 0.5, bottom = 0.5, left = 0.75, right = 0.75), 17 | lpp = NA_integer_, 18 | cpp = NA_integer_, 19 | colwidths = NULL, 20 | tf_wrap = !is.null(max_width), 21 | rep_cols = NULL, 22 | max_width = NULL, 23 | col_gap = 3, 24 | fontspec = font_spec(font_family, font_size, lineheight), 25 | verbose = FALSE, 26 | print_pages = TRUE 27 | ) 28 | } 29 | \arguments{ 30 | \item{lsting}{(\code{listing_df} or \code{list})\cr the listing or list of listings to paginate.} 31 | 32 | \item{page_type}{(\code{string})\cr name of a page type. See \code{\link[formatters]{page_types}}. Ignored 33 | when \code{pg_width} and \code{pg_height} are set directly.} 34 | 35 | \item{font_family}{(\code{string})\cr name of a font family. An error will be thrown 36 | if the family named is not monospaced. Defaults to \code{"Courier"}.} 37 | 38 | \item{font_size}{(\code{numeric(1)})\cr font size. Defaults to \code{12}.} 39 | 40 | \item{lineheight}{(\code{numeric(1)})\cr line height. Defaults to \code{1}.} 41 | 42 | \item{landscape}{(\code{flag})\cr whether the dimensions of \code{page_type} should be 43 | inverted for landscape orientation. Defaults to \code{FALSE}, ignored when \code{pg_width} and 44 | \code{pg_height} are set directly.} 45 | 46 | \item{pg_width}{(\code{numeric(1)})\cr page width in inches.} 47 | 48 | \item{pg_height}{(\code{numeric(1)})\cr page height in inches.} 49 | 50 | \item{margins}{(\code{numeric(4)})\cr named numeric vector containing \code{"bottom"}, \code{"left"}, 51 | \code{"top"}, and \code{"right"} margins in inches. Defaults to \code{.5} inches for both vertical 52 | margins and \code{.75} for both horizontal margins.} 53 | 54 | \item{lpp}{(\code{numeric(1)} or \code{NULL})\cr number of rows/lines (excluding titles and footers) 55 | to include per page. Standard is \code{70} while \code{NULL} disables vertical pagination.} 56 | 57 | \item{cpp}{(\code{numeric(1)} or \code{NULL})\cr width (in characters) of the pages for horizontal 58 | pagination. \code{NULL} (the default) indicates no horizontal pagination should be done.} 59 | 60 | \item{colwidths}{(\code{numeric})\cr vector of column widths (in characters) for use in vertical pagination.} 61 | 62 | \item{tf_wrap}{(\code{flag})\cr whether the text for title, subtitles, and footnotes should be wrapped.} 63 | 64 | \item{rep_cols}{(\code{numeric(1)})\cr number of \emph{columns} (not including row labels) to be repeated on every page. 65 | Defaults to 0.} 66 | 67 | \item{max_width}{(\code{integer(1)}, \code{string} or \code{NULL})\cr width that title and footer (including 68 | footnotes) materials should be word-wrapped to. If \code{NULL}, it is set to the current print width of the 69 | session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is 70 | used. Parameter is ignored if \code{tf_wrap = FALSE}.} 71 | 72 | \item{col_gap}{(\code{numeric(1)})\cr width of gap between columns, in same units as extent in \code{pagdf} (spaces 73 | under a particular font specification).} 74 | 75 | \item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for 76 | calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} 77 | 78 | \item{verbose}{(\code{flag})\cr whether additional informative messages about the search for 79 | pagination breaks should be shown. Defaults to \code{FALSE}.} 80 | 81 | \item{print_pages}{(\code{flag})\cr whether the paginated listing should be printed to the console 82 | (\code{cat(toString(x))}).} 83 | } 84 | \value{ 85 | A list of \code{listing_df} objects where each list element corresponds to a separate page. 86 | } 87 | \description{ 88 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} 89 | 90 | Pagination of a listing. This can be vertical for long listings with many 91 | rows and/or horizontal if there are many columns. This function is a wrapper of 92 | \code{\link[formatters:paginate_indices]{formatters::paginate_to_mpfs()}} and it is mainly meant for exploration and testing. 93 | } 94 | \examples{ 95 | dat <- ex_adae 96 | lsting <- as_listing(dat[1:25, ], disp_cols = c("USUBJID", "AESOC", "RACE", "AETOXGR", "BMRKR1")) 97 | mat <- matrix_form(lsting) 98 | cat(toString(mat)) 99 | 100 | paginate_listing(lsting, lpp = 10) 101 | 102 | paginate_listing(lsting, cpp = 100, lpp = 40) 103 | 104 | paginate_listing(lsting, cpp = 80, lpp = 40, verbose = TRUE) 105 | 106 | } 107 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/listing_export.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{export_as_txt} 7 | \title{Objects exported from other packages} 8 | \examples{ 9 | dat <- ex_adae 10 | 11 | lsting <- as_listing(dat[1:25, ], key_cols = c("USUBJID", "AESOC")) \%>\% 12 | add_listing_col("AETOXGR") \%>\% 13 | add_listing_col("BMRKR1", format = "xx.x") \%>\% 14 | add_listing_col("AESER / AREL", fun = function(df) paste(df$AESER, df$AREL, sep = " / ")) 15 | main_title(lsting) <- "this is some title" 16 | main_footer(lsting) <- "this is some footer" 17 | 18 | cat(export_as_txt(lsting, file = NULL, paginate = TRUE)) 19 | 20 | } 21 | \keyword{internal} 22 | \description{ 23 | These objects are imported from other packages. Follow the links 24 | below to see their documentation. 25 | 26 | \describe{ 27 | \item{formatters}{\code{\link[formatters]{export_as_txt}}} 28 | }} 29 | 30 | -------------------------------------------------------------------------------- /man/rlistings-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings-package.R 3 | \docType{package} 4 | \name{rlistings-package} 5 | \alias{rlistings} 6 | \alias{rlistings-package} 7 | \title{rlistings: Clinical Trial Style Data Readout Listings} 8 | \description{ 9 | Listings are often part of the submission of clinical trial data in regulatory settings. We provide a framework for the specific formatting features often used when displaying large datasets in that context. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://insightsengineering.github.io/rlistings/} 15 | \item \url{https://github.com/insightsengineering/rlistings/} 16 | \item Report bugs at \url{https://github.com/insightsengineering/rlistings/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Joe Zhu \email{joe.zhu@roche.com} (\href{https://orcid.org/0000-0001-7566-2787}{ORCID}) 22 | 23 | Authors: 24 | \itemize{ 25 | \item Gabriel Becker \email{gabembecker@gmail.com} (original creator of the package) 26 | \item Adrian Waddell \email{adrian.waddell@gene.com} 27 | \item Davide Garolini \email{davide.garolini@roche.com} (\href{https://orcid.org/0000-0002-1445-1369}{ORCID}) 28 | \item Emily de la Rua \email{emily.de_la_rua@contractors.roche.com} (\href{https://orcid.org/0009-0000-8738-5561}{ORCID}) 29 | } 30 | 31 | Other contributors: 32 | \itemize{ 33 | \item Abinaya Yogasekaram \email{abinaya.yogasekaram@contractors.roche.com} (\href{https://orcid.org/0009-0005-2083-1105}{ORCID}) [contributor] 34 | \item F. Hoffmann-La Roche AG [copyright holder, funder] 35 | } 36 | 37 | } 38 | \keyword{internal} 39 | -------------------------------------------------------------------------------- /man/split_into_pages_by_var.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings.R 3 | \name{split_into_pages_by_var} 4 | \alias{split_into_pages_by_var} 5 | \title{Split Listing by Values of a Variable} 6 | \usage{ 7 | split_into_pages_by_var(lsting, var, page_prefix = var) 8 | } 9 | \arguments{ 10 | \item{lsting}{(\code{listing_df})\cr the listing to split.} 11 | 12 | \item{var}{(\code{string})\cr name of the variable to split on. If the column is a factor, 13 | the resulting list follows the order of the levels.} 14 | 15 | \item{page_prefix}{(\code{string})\cr prefix to be appended with the split value (\code{var} level), 16 | at the end of the subtitles, corresponding to each resulting list element (listing).} 17 | } 18 | \value{ 19 | A list of \code{lsting_df} objects each corresponding to a unique value of \code{var}. 20 | } 21 | \description{ 22 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} 23 | 24 | Split is performed based on unique values of the given parameter present in the listing. 25 | Each listing can only be split by variable once. If this function is applied prior to 26 | pagination, parameter values will be separated by page. 27 | } 28 | \note{ 29 | This function should only be used after the complete listing has been created. The 30 | listing cannot be modified further after applying this function. 31 | } 32 | \examples{ 33 | dat <- ex_adae[1:20, ] 34 | 35 | lsting <- as_listing( 36 | dat, 37 | key_cols = c("USUBJID", "AGE"), 38 | disp_cols = "SEX", 39 | main_title = "title", 40 | main_footer = "footer" 41 | ) \%>\% 42 | add_listing_col("BMRKR1", format = "xx.x") \%>\% 43 | split_into_pages_by_var("SEX") 44 | 45 | lsting 46 | 47 | } 48 | -------------------------------------------------------------------------------- /man/vec_nlines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rlistings_methods.R 3 | \name{format_colvector} 4 | \alias{format_colvector} 5 | \alias{vec_nlines} 6 | \alias{vec_nlines,ANY-method} 7 | \title{Utilities for formatting a listing column} 8 | \usage{ 9 | format_colvector(df, colnm, colvec = df[[colnm]], round_type = c("iec", "sas")) 10 | 11 | vec_nlines( 12 | vec, 13 | max_width = NULL, 14 | fontspec = dflt_courier, 15 | round_type = c("iec", "sas") 16 | ) 17 | 18 | \S4method{vec_nlines}{ANY}( 19 | vec, 20 | max_width = NULL, 21 | fontspec = dflt_courier, 22 | round_type = c("iec", "sas") 23 | ) 24 | } 25 | \arguments{ 26 | \item{df}{(\code{listing_df})\cr the listing.} 27 | 28 | \item{colnm}{(\code{string})\cr column name.} 29 | 30 | \item{colvec}{(\code{vector})\cr column values based on \code{colnm}.} 31 | 32 | \item{vec}{(\code{vector})\cr a vector.} 33 | 34 | \item{max_width}{(\code{numeric(1)} or \code{NULL})\cr the width to render the column with.} 35 | } 36 | \value{ 37 | (\code{numeric})\cr a vector of the number of lines element-wise that will be 38 | needed to render the elements of \code{vec} to width \code{max_width}. 39 | } 40 | \description{ 41 | For \code{vec_nlines}, calculate the number of lines each element of a column vector will 42 | take to render. For \code{format_colvector}, 43 | } 44 | \keyword{internal} 45 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | cloud.noindex 6 | data.sqlite 7 | *.html 8 | -------------------------------------------------------------------------------- /rlistings.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | ProjectId: ed7ec588-ab60-4c79-92bd-87d98dec874d 3 | 4 | RestoreWorkspace: Default 5 | SaveWorkspace: Default 6 | AlwaysSaveHistory: Default 7 | 8 | EnableCodeIndexing: Yes 9 | UseSpacesForTab: Yes 10 | NumSpacesForTab: 2 11 | Encoding: UTF-8 12 | 13 | RnwWeave: Sweave 14 | LaTeX: pdfLaTeX 15 | 16 | AutoAppendNewline: Yes 17 | StripTrailingWhitespace: Yes 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(rlistings) 2 | library(testthat) 3 | 4 | testthat::test_check("rlistings", reporter = "check") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/matrix_form.md: -------------------------------------------------------------------------------- 1 | # align_colnames can change alignment for column titles 2 | 3 | Code 4 | cat(toString(out[[2]])) 5 | Output 6 | Unique Subject Identifier A 7 | ——————————————————————————————————— 8 | AB12345-BRA-1-id-134 AB12345 9 | 10 | --- 11 | 12 | Code 13 | cat(toString(out[[2]])) 14 | Output 15 | Unique Subject Identifier A 16 | ——————————————————————————————————— 17 | AB12345-BRA-1-id-134 AB12345 18 | 19 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/print.md: -------------------------------------------------------------------------------- 1 | # Listing print correctly 2 | 3 | Code 4 | res 5 | Output 6 | [1] " Unique Description " 7 | [2] " Subject Of " 8 | [3] " Identifier Planned Arm Continous Level Biomarker 1" 9 | [4] "--------------------------------------------------------------------" 10 | [5] "AB12345-CHN-1-id-307 B: Placebo 4.57499101339464 " 11 | [6] "AB12345-CHN-11-id-220 B: Placebo 10.2627340069523 " 12 | [7] "AB12345-CHN-15-id-201 C: Combination 6.9067988141075 " 13 | [8] "AB12345-CHN-15-id-262 C: Combination 4.05546277230382 " 14 | [9] "AB12345-CHN-3-id-128 A: Drug X 14.424933692778 " 15 | [10] "AB12345-CHN-7-id-267 B: Placebo 6.2067627167943 " 16 | [11] "AB12345-NGA-11-id-173 C: Combination 4.99722573047567 " 17 | [12] "AB12345-RUS-3-id-378 C: Combination 2.80323956920649 " 18 | [13] "AB12345-USA-1-id-261 B: Placebo 2.85516419937308 " 19 | [14] " AB12345-USA-1-id-45 A: Drug X 0.463560441314472 " 20 | 21 | # Listing print correctly with different widths 22 | 23 | Code 24 | cat(toString(matrix_form(lsting), widths = c(7, 8, 9), hsep = "-")) 25 | Output 26 | 27 | Descript 28 | Unique ion Continous 29 | Subject Of Level 30 | Identif Planned Biomarker 31 | ier Arm 1 32 | ------------------------------ 33 | AB12345 B: 4.5749910 34 | - Placebo 1339464 35 | CHN-1- 36 | id-307 37 | AB12345 B: 10.262734 38 | - Placebo 0069523 39 | CHN-11- 40 | id-220 41 | AB12345 C: Combi 6.9067988 42 | - nation 141075 43 | CHN-15- 44 | id-201 45 | AB12345 C: Combi 4.0554627 46 | - nation 7230382 47 | CHN-15- 48 | id-262 49 | AB12345 A: Drug 14.424933 50 | - X 692778 51 | CHN-3- 52 | id-128 53 | AB12345 B: 6.2067627 54 | - Placebo 167943 55 | CHN-7- 56 | id-267 57 | AB12345 C: Combi 4.9972257 58 | - nation 3047567 59 | NGA-11- 60 | id-173 61 | AB12345 C: Combi 2.8032395 62 | - nation 6920649 63 | RUS-3- 64 | id-378 65 | AB12345 B: 2.8551641 66 | - Placebo 9937308 67 | USA-1- 68 | id-261 69 | AB12345 A: Drug 0.4635604 70 | - X 41314472 71 | USA-1- 72 | id-45 73 | 74 | # as_listing produces correct output when default_formatting is specified 75 | 76 | Code 77 | res 78 | Output 79 | [1] " Unique Description " 80 | [2] " Subject Of " 81 | [3] " Identifier Planned Arm Continous Level Biomarker 1" 82 | [4] "--------------------------------------------------------------------" 83 | [5] "AB12345-CHN-1-id-307 B: Placebo 4.57" 84 | [6] "AB12345-CHN-11-id-220 B: Placebo " 85 | [7] "AB12345-CHN-15-id-201 C: Combination " 86 | [8] "AB12345-CHN-15-id-262 C: Combination 4.06" 87 | [9] "AB12345-CHN-3-id-128 A: Drug X 14.42" 88 | [10] "AB12345-CHN-7-id-267 B: Placebo " 89 | [11] "AB12345-NGA-11-id-173 C: Combination 5.00" 90 | [12] "AB12345-RUS-3-id-378 C: Combination " 91 | [13] "AB12345-USA-1-id-261 B: Placebo 2.86" 92 | [14] "AB12345-USA-1-id-45 A: Drug X 0.46" 93 | 94 | # as_listing produces correct output when col_formatting is specified 95 | 96 | Code 97 | res 98 | Output 99 | [1] " Unique Description " 100 | [2] " Subject Of " 101 | [3] " Identifier Planned Arm Continous Level Biomarker 1" 102 | [4] "-----------------------------------------------------------------" 103 | [5] " AB12345-CHN-1-id-307 ARM #: 2 4.57499101339464 " 104 | [6] "AB12345-CHN-11-id-220 - 10.2627340069523 " 105 | [7] "AB12345-CHN-15-id-201 - 6.9067988141075 " 106 | [8] "AB12345-CHN-15-id-262 ARM #: 3 4.05546277230382 " 107 | [9] " AB12345-CHN-3-id-128 ARM #: 1 14.424933692778 " 108 | [10] " AB12345-CHN-7-id-267 - 6.2067627167943 " 109 | [11] "AB12345-NGA-11-id-173 ARM #: 3 4.99722573047567 " 110 | [12] " AB12345-RUS-3-id-378 - 2.80323956920649 " 111 | [13] " AB12345-USA-1-id-261 ARM #: 2 2.85516419937308 " 112 | [14] " AB12345-USA-1-id-45 ARM #: 1 0.463560441314472 " 113 | 114 | --- 115 | 116 | Code 117 | cat(toString(matrix_form(lsting), hsep = "-")) 118 | Output 119 | Unique 120 | Subject 121 | Identifier Continous Level Biomarker 1 122 | --------------------------------------------------- 123 | AB12345-CHN-1-id-307 5 124 | AB12345-CHN-11-id-220 10 125 | AB12345-CHN-15-id-201 7 126 | AB12345-CHN-15-id-262 4 127 | AB12345-CHN-3-id-128 14 128 | AB12345-CHN-7-id-267 6 129 | AB12345-NGA-11-id-173 5 130 | AB12345-RUS-3-id-378 3 131 | AB12345-USA-1-id-261 3 132 | AB12345-USA-1-id-45 0 133 | 134 | # listings support newline characters 135 | 136 | Code 137 | res 138 | Output 139 | [1] "main_title: argh" 140 | [2] "asr" 141 | [3] "subtitle: argh" 142 | [4] "asr" 143 | [5] "sada" 144 | [6] "" 145 | [7] "------------------------------------------------------" 146 | [8] " " 147 | [9] " " 148 | [10] " Unique Description a " 149 | [11] " Subject Of " 150 | [12] " Identifier Planned Arm n " 151 | [13] "------------------------------------------------------" 152 | [14] "AB12345-CHN-11-id-220 - 10.2627340069523" 153 | [15] " asd " 154 | [16] "AB12345-CHN-15-id-262 ARM #: 3 4.05546277230382" 155 | [17] " AB12345-RUS-3-id-378 - 2.80323956920649" 156 | [18] " asd " 157 | [19] " aaatrial ARM #: 1 14.424933692778 " 158 | [20] " trial " 159 | [21] "------------------------------------------------------" 160 | [22] "" 161 | [23] "main_footer: argh" 162 | [24] "asr" 163 | [25] "sada" 164 | [26] "" 165 | [27] "prov_footer: argh" 166 | [28] "asr" 167 | [29] "sada" 168 | 169 | # listings supports horizontal separators 170 | 171 | Code 172 | as_listing(df = data.frame(one_col = c("aa", "aa", "b")), key_cols = "one_col", 173 | add_trailing_sep = "one_col", trailing_sep = "+") 174 | Output 175 | one_col 176 | ——————— 177 | aa 178 | 179 | +++++++ 180 | b 181 | 182 | # spanning column label machinery works 183 | 184 | Code 185 | cat(txtvec, sep = "\n") 186 | Output 187 | key columns lame columns 188 | Description of Planned Arm Race Unique Subject Identifier Continous Level Biomarker 1 Categorical Level Biomarker 2 Body System or Organ Class Dictionary-Derived Term 189 | ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 190 | A: Drug X WHITE AB12345-BRA-1-id-134 6.46299057842479 LOW cl B.2 dcd B.2.1.2.1 191 | 6.46299057842479 LOW cl D.1 dcd D.1.1.4.2 192 | 6.46299057842479 LOW cl A.1 dcd A.1.1.1.2 193 | 6.46299057842479 LOW cl A.1 dcd A.1.1.1.2 194 | C: Combination WHITE AB12345-BRA-1-id-141 7.51607612428241 HIGH cl B.2 dcd B.2.1.2.1 195 | 7.51607612428241 HIGH cl D.2 dcd D.2.1.5.3 196 | 7.51607612428241 HIGH cl A.1 dcd A.1.1.1.1 197 | 7.51607612428241 HIGH cl A.1 dcd A.1.1.1.2 198 | 7.51607612428241 HIGH cl A.1 dcd A.1.1.1.1 199 | 7.51607612428241 HIGH cl D.1 dcd D.1.1.1.1 200 | 201 | --- 202 | 203 | Code 204 | cat(txt_silly, sep = "\n") 205 | Output 206 | whatever man 207 | AE info 208 | biomarkers 209 | key columns lame columns 210 | Description of Planned Arm Race Unique Subject Identifier Continous Level Biomarker 1 Categorical Level Biomarker 2 Body System or Organ Class Dictionary-Derived Term 211 | ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 212 | A: Drug X WHITE AB12345-BRA-1-id-134 6.46299057842479 LOW cl B.2 dcd B.2.1.2.1 213 | 6.46299057842479 LOW cl D.1 dcd D.1.1.4.2 214 | 6.46299057842479 LOW cl A.1 dcd A.1.1.1.2 215 | 6.46299057842479 LOW cl A.1 dcd A.1.1.1.2 216 | C: Combination WHITE AB12345-BRA-1-id-141 7.51607612428241 HIGH cl B.2 dcd B.2.1.2.1 217 | 7.51607612428241 HIGH cl D.2 dcd D.2.1.5.3 218 | 7.51607612428241 HIGH cl A.1 dcd A.1.1.1.1 219 | 7.51607612428241 HIGH cl A.1 dcd A.1.1.1.2 220 | 7.51607612428241 HIGH cl A.1 dcd A.1.1.1.1 221 | 7.51607612428241 HIGH cl D.1 dcd D.1.1.1.1 222 | 223 | -------------------------------------------------------------------------------- /tests/testthat/setup-options.R: -------------------------------------------------------------------------------- 1 | # `opts_partial_match_old` is left for exclusions due to partial matching in dependent packages (i.e. not fixable here) 2 | # it might happen that it is not used right now, but it is left for possible future use 3 | # use with: `withr::with_options(opts_partial_match_old, { ... })` inside the test 4 | opts_partial_match_old <- list( 5 | warnPartialMatchDollar = getOption("warnPartialMatchDollar"), 6 | warnPartialMatchArgs = getOption("warnPartialMatchArgs"), 7 | warnPartialMatchAttr = getOption("warnPartialMatchAttr") 8 | ) 9 | opts_partial_match_new <- list( 10 | warnPartialMatchDollar = TRUE, 11 | warnPartialMatchArgs = TRUE, 12 | warnPartialMatchAttr = TRUE 13 | ) 14 | 15 | if (isFALSE(getFromNamespace("on_cran", "testthat")()) && requireNamespace("withr", quietly = TRUE)) { 16 | withr::local_options( 17 | opts_partial_match_new, 18 | .local_envir = testthat::teardown_env() 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /tests/testthat/setup.R: -------------------------------------------------------------------------------- 1 | anl <- ex_adsl 2 | anl <- anl[1:10, c("USUBJID", "ARM", "BMRKR1")] 3 | anl <- var_relabel(anl, 4 | USUBJID = "Unique\nSubject\nIdentifier", 5 | ARM = "Description\nOf\nPlanned Arm" 6 | ) 7 | 8 | # Helper function used in pagination tests 9 | h_lsting_adae <- function(disp_cols = c("USUBJID", "AESOC", "RACE", "AETOXGR", "BMRKR1")) { 10 | as_listing(ex_adae[1:25, ], disp_cols = disp_cols) 11 | } 12 | 13 | # Helper function to print output from paginate_listings 14 | fast_print <- function(x) { 15 | nothing <- lapply(seq_along(x), function(pg_num) { 16 | cat("Page", pg_num, "\n") 17 | cat(toString(x[[pg_num]])) 18 | }) 19 | } 20 | 21 | 22 | compare_paginations <- function(paglst1, paglst2) { 23 | if (length(paglst1) != length(paglst2)) { 24 | indres <- FALSE 25 | } else { 26 | indres <- vapply( 27 | seq_along(paglst1), 28 | function(i) compare_mpfs_no_fontspec(paglst1[[i]], paglst2[[i]]), 29 | TRUE 30 | ) 31 | } 32 | expect_true(all(indres), "paginations are not equivalent as expected") 33 | } 34 | 35 | compare_mpfs_no_fontspec <- function(mpf1, mpf2) { 36 | mf_fontspec(mpf1) <- NULL 37 | mf_fontspec(mpf2) <- NULL 38 | identical(mpf1, mpf2) 39 | } 40 | -------------------------------------------------------------------------------- /tests/testthat/test-export.R: -------------------------------------------------------------------------------- 1 | testthat::test_that("Listing print correctly", { 2 | lsting <- as_listing(anl, key_cols = c("USUBJID")) 3 | main_title(lsting) <- "this is some title" 4 | main_footer(lsting) <- "this is some footer" 5 | testthat::expect_silent({ 6 | export_as_txt(lsting, file = NULL) 7 | }) 8 | }) 9 | 10 | testthat::test_that("key columns repeat with export_as_txt", { 11 | skip_if_not_installed("dplyr") 12 | require("dplyr", quietly = TRUE) 13 | # pre-processing and ordering 14 | tmp_data <- ex_adae %>% 15 | dplyr::slice(1:30) %>% 16 | dplyr::distinct(USUBJID, AGE, BMRKR1, .keep_all = TRUE) 17 | 18 | lsting <- as_listing(tmp_data, 19 | key_cols = c("USUBJID", "AGE"), 20 | disp_cols = character() 21 | ) %>% 22 | add_listing_col("BMRKR1", format = "xx.x") 23 | 24 | listing_exp <- suppressMessages(export_as_txt(lsting, lpp = 4, verbose = TRUE, page_break = "\n")) 25 | 26 | testthat::expect_snapshot(cat(listing_exp)) 27 | }) 28 | 29 | testthat::test_that("key columns repeat with pagination with export_as_txt", { 30 | # pre-processing and ordering 31 | tbl <- as_listing( 32 | iris, 33 | key_cols = c("Species"), 34 | disp_cols = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width") 35 | ) 36 | 37 | listing <- export_as_txt(head(tbl), 38 | cpp = 50, paginate = TRUE, 39 | page_break = "\n" 40 | ) 41 | 42 | testthat::expect_snapshot(cat(listing)) 43 | }) 44 | 45 | testthat::test_that("Listing print correctly, with paginate", { 46 | dat <- ex_adae 47 | lsting <- as_listing(dat[1:25, 1:8], key_cols = c("USUBJID", "AGE", "SEX")) 48 | page_out <- export_as_txt(lsting, file = NULL, paginate = TRUE, rep_cols = length(get_keycols(lsting))) 49 | 50 | expect_identical(length(gregexpr(c("Unique Subject Identifier"), page_out)[[1]]), 2L) 51 | expect_identical(length(gregexpr(c("Age"), page_out)[[1]]), 2L) 52 | expect_identical(length(gregexpr(c("Sex"), page_out)[[1]]), 2L) 53 | }) 54 | 55 | testthat::test_that("export_as_txt works and repeats the correct lines in pagination", { 56 | dat <- ex_adae 57 | lsting <- as_listing(dat[1:25, c(seq(1, 3), 40)], 58 | key_cols = c("USUBJID", "AESOC"), 59 | main_title = "Example Title for Listing", 60 | subtitles = "This is the subtitle for this Adverse Events Table", 61 | main_footer = "Main footer for the listing", 62 | prov_footer = c( 63 | "You can even add a subfooter", "Second element is place on a new line", 64 | "Third string" 65 | ) 66 | ) 67 | # There are differences in pagination that should be taken into account (ref footnotes and rinfo) 68 | local_pagination <- paginate_listing(lsting, lpp = 33, cpp = 550, print_pages = FALSE)[[2]] 69 | testthat::expect_equal( 70 | matrix_form(local_pagination, TRUE, TRUE)$strings, 71 | paginate_to_mpfs(lsting, lpp = 33, cpp = 550)[[2]]$strings 72 | ) 73 | 74 | pages_listings <- export_as_txt(lsting, file = NULL, paginate = TRUE, lpp = 33, cpp = 550) 75 | testthat::expect_snapshot(cat(pages_listings)) 76 | }) 77 | 78 | testthat::test_that("export_as_txt works with split_into_pages_by_var", { 79 | tmp_data <- ex_adae[1:50, ] 80 | 81 | lsting <- as_listing( 82 | tmp_data, 83 | key_cols = c("USUBJID", "AGE"), 84 | disp_cols = "SEX", 85 | main_title = "title", 86 | main_footer = "foot" 87 | ) %>% 88 | add_listing_col("BMRKR1", format = "xx.x") %>% 89 | split_into_pages_by_var("SEX", page_prefix = "Patient Subset - Sex") 90 | 91 | pages_listings <- export_as_txt(lsting, file = NULL, paginate = TRUE, lpp = 30, cpp = 65) 92 | testthat::expect_snapshot(cat(pages_listings)) 93 | }) 94 | 95 | testthat::test_that("export_as_txt works with empty listings", { 96 | df <- data.frame( 97 | x = with_label("Null Report: No observations met the reporting criteria for inclusion in this output.", "") 98 | ) 99 | lsting <- as_listing(df) 100 | 101 | res <- export_as_txt(lsting) 102 | testthat::expect_snapshot(cat(res)) 103 | }) 104 | 105 | testthat::test_that("export_as_txt works with listings with all key cols", { 106 | lsting <- as_listing( 107 | df = ex_adcm[1:50, ], 108 | key_cols = c("BMRKR2", "CMCLAS", "CMDECOD"), 109 | disp_cols = c("BMRKR2", "CMCLAS", "CMDECOD"), 110 | unique_rows = TRUE 111 | ) 112 | 113 | res <- export_as_txt(lsting) 114 | testthat::expect_snapshot(cat(res)) 115 | }) 116 | -------------------------------------------------------------------------------- /tests/testthat/test-listings.R: -------------------------------------------------------------------------------- 1 | testthat::test_that("Column labels are the same", { 2 | ## listings var labels don't get mucked up by topleft machinery #262 3 | lsting <- as_listing(anl, key_cols = c("USUBJID")) %>% 4 | add_listing_col("ARM") 5 | 6 | testthat::expect_identical(var_labels(anl), var_labels(lsting)) 7 | 8 | matform <- matrix_form(lsting) 9 | 10 | testthat::expect_identical( 11 | matform$strings[1:3, 1, drop = TRUE], 12 | c("Unique", "Subject", "Identifier") 13 | ) 14 | testthat::expect_identical( 15 | matform$strings[1:3, 2, drop = TRUE], 16 | c("Description", "Of", "Planned Arm") 17 | ) 18 | }) 19 | 20 | testthat::test_that("listings work well with different formats and attributes", { 21 | # (1) Error with NA values in numeric column when apply format 22 | anl_tmp <- anl 23 | var_labels(anl_tmp) <- var_labels(ex_adsl)[c("USUBJID", "ARM", "BMRKR1")] 24 | anl_tmp$BMRKR1[1:3] <- NA 25 | 26 | lsting <- as_listing(anl_tmp, key_cols = c("ARM", "USUBJID")) %>% 27 | add_listing_col("ARM") %>% 28 | add_listing_col("USUBJID") %>% 29 | add_listing_col("BMRKR1", format = "xx.xx") 30 | 31 | main_title(lsting) <- "main title" 32 | subtitles(lsting) <- c("sub", "titles") 33 | main_footer(lsting) <- "main footer" 34 | prov_footer(lsting) <- "provenance" 35 | 36 | mat <- matrix_form(lsting) 37 | testthat::expect_identical( 38 | unname(mat$strings[2, 3, drop = TRUE]), 39 | "NA" 40 | ) 41 | 42 | ## this tests that the format is applied correctly 43 | testthat::expect_identical( 44 | unname(mat$strings[3, 3, drop = TRUE]), 45 | format_value(lsting$BMRKR1[2], "xx.xx") 46 | ) 47 | 48 | testthat::expect_identical(main_title(lsting), "main title") 49 | testthat::expect_identical(main_title(lsting), main_title(mat)) 50 | 51 | testthat::expect_identical(subtitles(lsting), c("sub", "titles")) 52 | testthat::expect_identical(subtitles(lsting), subtitles(mat)) 53 | 54 | testthat::expect_identical(main_footer(lsting), "main footer") 55 | testthat::expect_identical(main_footer(lsting), main_footer(mat)) 56 | 57 | testthat::expect_identical(prov_footer(lsting), "provenance") 58 | testthat::expect_identical(prov_footer(lsting), prov_footer(mat)) 59 | 60 | testthat::expect_error( 61 | { 62 | main_title(lsting) <- 1L 63 | }, 64 | "value for main_title .*class: integer$" 65 | ) 66 | 67 | testthat::expect_error( 68 | { 69 | main_title(lsting) <- c("lol", "silly") 70 | }, 71 | "value for main_title .*got vector of length 2" 72 | ) 73 | 74 | testthat::expect_error( 75 | { 76 | subtitles(lsting) <- 1L 77 | }, 78 | "value for subtitles .*class: integer$" 79 | ) 80 | 81 | testthat::expect_error( 82 | { 83 | main_footer(lsting) <- 1L 84 | }, 85 | "value for main_footer .*class: integer$" 86 | ) 87 | 88 | testthat::expect_error( 89 | { 90 | prov_footer(lsting) <- 1L 91 | }, 92 | "value for prov_footer .*class: integer$" 93 | ) 94 | 95 | main_title(lsting) <- NULL 96 | testthat::expect_identical(main_title(lsting), character()) 97 | }) 98 | 99 | testthat::test_that("Content of listings supports newlines", { 100 | ## newline support #16 101 | 102 | mydf <- data.frame(col1 = c(1, 1, 2), col2 = c("hi\nthere", "what", "who")) 103 | var_labels(mydf) <- c("column\n1", "column 2") 104 | mylst <- as_listing(mydf, names(mydf), key_cols = "col1") 105 | 106 | mpf <- matrix_form(mylst) 107 | 108 | testthat::expect_identical( 109 | mpf$strings[1:2, 1, drop = TRUE], 110 | c("column", "1") 111 | ) 112 | 113 | rdf <- make_row_df(mylst) 114 | 115 | testthat::expect_identical( 116 | rdf$self_extent, 117 | c(2L, 1L, 1L) 118 | ) 119 | }) 120 | 121 | testthat::test_that("regression test for keycols being lost due to `head()`", { 122 | ## causing #41 123 | rlst <- as_listing(mtcars, 124 | key_cols = c("gear", "carb"), 125 | disp_cols = "qsec" 126 | ) 127 | 128 | testthat::expect_identical( 129 | dim(head(rlst, 5)), 130 | c(5L, ncol(mtcars)) 131 | ) 132 | }) 133 | 134 | testthat::test_that("column inclusion and ordering stuff", { 135 | ## key columns must be left-most k columns (#36) 136 | lsting <- as_listing( 137 | df = ex_adae, 138 | disp_cols = c("USUBJID", "AGE", "SEX", "RACE", "ARM", "AEDECOD", "AESEV", "AETOXGR"), 139 | key_cols = c("ARM", "USUBJID", "AETOXGR") 140 | ) 141 | 142 | mpfh <- matrix_form(head(lsting)) 143 | exp <- mpfh$strings[1:4, 1:4] 144 | dimnames(exp) <- NULL 145 | testthat::expect_equal( 146 | exp, 147 | matrix( 148 | c( 149 | "Description of Planned Arm", 150 | "Unique Subject Identifier", 151 | "Analysis Toxicity Grade", "Age", 152 | "A: Drug X", 153 | "AB12345-BRA-1-id-134", 154 | "2", "47", 155 | "", "", 156 | "", "47", 157 | "", "", 158 | "3", "47" 159 | ), 160 | byrow = TRUE, nrow = 4 161 | ) 162 | ) 163 | ## key cols don't need to be repeated in disp_cols 164 | lsting2 <- as_listing( 165 | df = ex_adae, 166 | disp_cols = c("AGE", "SEX", "RACE", "AEDECOD", "AESEV"), 167 | key_cols = c("ARM", "USUBJID", "AETOXGR") 168 | ) 169 | 170 | testthat::expect_true(identical(lsting, lsting2)) 171 | 172 | ## display cols default to everything thats not a key col 173 | lsting3 <- as_listing(df = ex_adae, key_cols = c("USUBJID", "ARM")) 174 | testthat::expect_identical( 175 | listing_dispcols(lsting3), 176 | c("USUBJID", "ARM", setdiff(names(ex_adae), c("USUBJID", "ARM"))) 177 | ) 178 | 179 | ## non_disp_cols causes columns to be excluded 180 | lsting4 <- as_listing( 181 | df = ex_adae, key_cols = c("USUBJID", "ARM"), 182 | non_disp_cols = c("RACE", "AESEV") 183 | ) 184 | testthat::expect_identical( 185 | listing_dispcols(lsting4), 186 | c("USUBJID", "ARM", setdiff( 187 | names(ex_adae), 188 | c("USUBJID", "ARM", "RACE", "AESEV") 189 | )) 190 | ) 191 | testthat::expect_error( 192 | as_listing( 193 | df = ex_adae, key_cols = c("USUBJID", "ARM"), 194 | non_disp_cols = c("ARM", "RACE", "AESEV") 195 | ), 196 | "Key column also listed in non_disp_cols" 197 | ) 198 | testthat::expect_error( 199 | as_listing( 200 | df = ex_adae, key_cols = c("USUBJID", "ARM"), 201 | disp_cols = c("AEDECOD", "AETOXGR"), 202 | non_disp_cols = c("RACE", "AESEV") 203 | ), 204 | "Got non-null values for both disp_cols and non_disp_cols" 205 | ) 206 | 207 | ## no-keycols is supported #73 208 | lsting3 <- as_listing( 209 | df = ex_adae[1:30, 1:5], 210 | key_cols = NULL 211 | ) 212 | testthat::expect_silent({ 213 | str <- toString(lsting3) 214 | }) 215 | }) 216 | 217 | testthat::test_that("unique_rows removes duplicate rows from listing", { 218 | # only key_col 219 | lsting <- as_listing( 220 | ex_adsl, 221 | key_cols = "SEX", 222 | disp_cols = character(0), 223 | unique_rows = TRUE 224 | ) 225 | result_strings <- matrix_form(lsting)$strings 226 | expected_strings <- matrix( 227 | c("Sex", "F", "M", "U", "UNDIFFERENTIATED"), 228 | dimnames = list(c(), "SEX") 229 | ) 230 | expect_equal(expected_strings, result_strings) 231 | 232 | # key_col and disp_col 233 | lsting <- as_listing( 234 | ex_adsl, 235 | key_cols = "ARMCD", 236 | disp_cols = "SEX", 237 | unique_rows = TRUE 238 | ) 239 | result_strings <- matrix_form(lsting)$strings 240 | expected_strings <- matrix( 241 | c( 242 | "Planned Arm Code", "ARM A", "", "", "", "ARM B", "", "", "ARM C", "", "", "", 243 | "Sex", "M", "F", "UNDIFFERENTIATED", "U", "F", "M", "U", "M", "F", "U", "UNDIFFERENTIATED" 244 | ), 245 | ncol = 2, 246 | dimnames = list(c(), c("ARMCD", "SEX")) 247 | ) 248 | expect_equal(expected_strings, result_strings) 249 | }) 250 | 251 | testthat::test_that("as_listing custom format works in key cols", { 252 | lsting <- as_listing( 253 | ex_adsl[1:10, ], 254 | key_cols = c("AGE", "BMRKR1"), 255 | disp_cols = c("SEX", "ARM"), 256 | default_formatting = list(all = fmt_config(), numeric = fmt_config(format = "xx.xx")) 257 | ) 258 | 259 | testthat::expect_identical(matrix_form(lsting)$strings[2, 1:2], c(AGE = "24.00", BMRKR1 = "4.57")) 260 | testthat::expect_identical(matrix_form(lsting)$strings[3, 1:2], c(AGE = "", BMRKR1 = "5.00")) 261 | }) 262 | 263 | testthat::test_that("as_listing works with NA values in key cols", { 264 | mtcars$gear[1:5] <- NA 265 | mtcars$carb[6:10] <- NA 266 | 267 | lsting <- as_listing( 268 | mtcars, 269 | key_cols = c("gear", "carb"), 270 | disp_cols = "qsec" 271 | ) 272 | 273 | testthat::expect_identical( 274 | matrix_form(lsting)$strings[29:33, ], 275 | matrix( 276 | c("NA", "1", "18.61", "", "", "19.44", "", "2", "17.02", "", "4", "16.46", "", "", "17.02"), 277 | ncol = 3, 278 | byrow = TRUE, 279 | dimnames = list(c(), c("gear", "carb", "qsec")) 280 | ) 281 | ) 282 | 283 | lsting <- as_listing( 284 | mtcars, 285 | key_cols = c("gear", "carb"), 286 | disp_cols = "qsec", 287 | default_formatting = list(all = fmt_config(), numeric = fmt_config(na_str = "")) 288 | ) 289 | 290 | testthat::expect_identical(matrix_form(lsting)$strings[29, 1], c(gear = "")) 291 | testthat::expect_identical(matrix_form(lsting)$strings[13, 2], c(carb = "")) 292 | 293 | mtcars[33, ] <- mtcars[32, ] 294 | mtcars[33, c(7, 10:11)] <- NA 295 | testthat::expect_warning(lsting <- as_listing( 296 | mtcars, 297 | key_cols = c("gear", "carb"), 298 | disp_cols = "qsec" 299 | ), "rows that only contain NA") 300 | }) 301 | 302 | testthat::test_that("as_listing(sort_cols) works", { 303 | # default behavior (sort by key_cols) 304 | lsting <- as_listing( 305 | mtcars, 306 | key_cols = c("gear", "carb"), 307 | disp_cols = "qsec" 308 | ) 309 | testthat::expect_true(!is.unsorted(lsting$gear)) 310 | testthat::expect_identical( 311 | lsting$carb, 312 | c(1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 2, 2, 4, 6, 8), 313 | ignore_attr = TRUE 314 | ) 315 | testthat::expect_true(is.unsorted(lsting$qsec)) 316 | 317 | # works with only one key column 318 | lsting <- as_listing( 319 | mtcars, 320 | key_cols = c("gear", "carb"), 321 | disp_cols = "qsec", 322 | sort_cols = "carb" 323 | ) 324 | testthat::expect_identical( 325 | lsting$gear, 326 | c(4, 3, 3, 4, 4, 3, 4, 3, 4, 4, 4, 3, 3, 3, 5, 5, 4, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 3, 3, 5, 5, 5), 327 | ignore_attr = TRUE 328 | ) 329 | testthat::expect_true(!is.unsorted(lsting$carb)) 330 | testthat::expect_true(is.unsorted(lsting$qsec)) 331 | 332 | # works with columns not displayed in listing 333 | lsting <- as_listing( 334 | mtcars, 335 | key_cols = c("gear", "carb"), 336 | disp_cols = "qsec", 337 | sort_cols = c("am", "vs") 338 | ) 339 | testthat::expect_true(is.unsorted(lsting$gear)) 340 | testthat::expect_true(is.unsorted(lsting$carb)) 341 | testthat::expect_true(is.unsorted(lsting$qsec)) 342 | testthat::expect_true(!is.unsorted(lsting$am)) 343 | testthat::expect_identical( 344 | lsting$vs, 345 | c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), 346 | ignore_attr = TRUE 347 | ) 348 | 349 | # works with no sorting 350 | lsting <- as_listing( 351 | mtcars, 352 | key_cols = c("gear", "carb"), 353 | disp_cols = "qsec", 354 | sort_cols = NULL 355 | ) 356 | testthat::expect_identical(lsting$gear, mtcars$gear, ignore_attr = TRUE) 357 | testthat::expect_true(is.unsorted(lsting$carb)) 358 | testthat::expect_true(is.unsorted(lsting$qsec)) 359 | 360 | # error if sort column given is not in df 361 | testthat::expect_error( 362 | lsting <- as_listing( 363 | mtcars, 364 | key_cols = c("gear", "carb"), 365 | disp_cols = "qsec", 366 | sort_cols = "test" 367 | ) 368 | ) 369 | }) 370 | 371 | testthat::test_that("add_listing_col works with a function when a format is applied", { 372 | lsting <- as_listing( 373 | mtcars[1:5, ], 374 | key_cols = c("gear", "carb"), 375 | disp_cols = "qsec" 376 | ) %>% 377 | add_listing_col( 378 | "kpg", 379 | function(df) df$mpg * 1.60934, 380 | format = "xx.xx" 381 | ) 382 | 383 | testthat::expect_identical( 384 | matrix_form(lsting)$strings[, 4], 385 | c("kpg", "34.44", "30.09", "36.69", "33.80", "33.80") 386 | ) 387 | }) 388 | 389 | testthat::test_that("split_into_pages_by_var works as expected", { 390 | tmp_data <- ex_adae[1:100, ] 391 | 392 | lsting <- as_listing( 393 | tmp_data, 394 | key_cols = c("USUBJID", "AGE"), 395 | disp_cols = "SEX", 396 | main_title = "title", 397 | main_footer = "foot" 398 | ) %>% 399 | split_into_pages_by_var("SEX", page_prefix = "Patient Subset - Sex") 400 | 401 | testthat::expect_equal(length(lsting), length(unique(tmp_data[["SEX"]]))) 402 | testthat::expect_equal(subtitles(lsting[[1]]), "Patient Subset - Sex: F") 403 | 404 | lsting <- as_listing( 405 | tmp_data, 406 | key_cols = c("USUBJID", "AGE"), 407 | disp_cols = "SEX", 408 | main_title = "title", 409 | main_footer = "foot" 410 | ) %>% 411 | split_into_pages_by_var("SEX") 412 | lsting_id <- as_listing( 413 | tmp_data, 414 | key_cols = c("USUBJID", "AGE"), 415 | disp_cols = "SEX", 416 | main_title = "title", 417 | main_footer = "foot", 418 | split_into_pages_by_var = "SEX" 419 | ) 420 | testthat::expect_identical(lsting, lsting_id) 421 | }) 422 | 423 | testthat::test_that("appropriate error message returned for 'difftime' class", { 424 | tmp_data <- ex_adae[1:100, ] 425 | class(tmp_data$study_duration_secs) <- "difftime" 426 | 427 | testthat::expect_error(as_listing( 428 | tmp_data, 429 | key_cols = c("USUBJID", "AGE"), 430 | disp_cols = "study_duration_secs", 431 | main_title = "title", 432 | main_footer = "foot" 433 | ) %>% 434 | split_into_pages_by_var("SEX", page_prefix = "Patient Subset - Sex")) 435 | }) 436 | -------------------------------------------------------------------------------- /tests/testthat/test-matrix_form.R: -------------------------------------------------------------------------------- 1 | testthat::test_that("matrix_form keeps relevant information and structure about the listing", { 2 | skip_if_not_installed("dplyr") 3 | require("dplyr", quietly = TRUE) 4 | 5 | my_iris <- iris %>% 6 | slice(c(16, 3)) %>% 7 | mutate("fake_rownames" = c("mean", "mean")) 8 | 9 | lsting <- as_listing(my_iris, 10 | key_cols = c("fake_rownames", "Petal.Width"), 11 | disp_cols = c("Petal.Length") 12 | ) 13 | mat <- matrix_form(lsting) ## to match basic_listing_mfb 14 | 15 | # IMPORTANT: the following is coming directly from spoof matrix form for rlistings coming from {formatters} 16 | mat_rebuilt <- basic_listing_mf(my_iris[c("fake_rownames", "Petal.Width", "Petal.Length")], 17 | keycols = c("fake_rownames", "Petal.Width"), add_decoration = FALSE, fontspec = NULL 18 | ) 19 | 20 | testthat::expect_equal(names(mat_rebuilt), names(mat)) 21 | 22 | mat_rebuilt$row_info$pos_in_siblings <- mat$row_info$pos_in_siblings # not relevant in listings 23 | mat_rebuilt$row_info$n_siblings <- mat$row_info$n_siblings # not relevant in listings 24 | testthat::expect_equal(mf_rinfo(mat_rebuilt), mf_rinfo(mat)) 25 | 26 | mat_rebuilt["page_titles"] <- list(NULL) 27 | 28 | testthat::expect_equal(mat, mat_rebuilt) 29 | 30 | testthat::expect_equal(toString(mat), toString(mat_rebuilt)) 31 | 32 | 33 | # The same but with rownames 34 | lmf <- basic_listing_mf(mtcars) 35 | testthat::expect_equal(ncol(lmf), length(lmf$col_widths)) 36 | testthat::expect_equal(ncol(lmf), ncol(lmf$strings)) 37 | testthat::expect_false(mf_has_rlabels(lmf)) 38 | 39 | rlmf <- as_listing(mtcars) %>% matrix_form() # rownames are always ignored!!! 40 | testthat::expect_equal(ncol(rlmf), length(rlmf$col_widths)) 41 | testthat::expect_equal(ncol(rlmf), ncol(rlmf$strings)) 42 | testthat::expect_false(mf_has_rlabels(rlmf)) 43 | }) 44 | 45 | test_that("matrix_form detects { or } in labels and sends meaningful error message", { 46 | dat <- ex_adae[1:10, ] 47 | dat$AENDY[3:6] <- "something {haha} something" 48 | lsting <- as_listing( 49 | dat, 50 | key_cols = c("USUBJID"), 51 | disp_cols = c("STUDYID", "AENDY") 52 | ) 53 | expect_error( 54 | matrix_form(lsting), 55 | "Labels cannot contain" 56 | ) 57 | 58 | # Workaround for ref_fnotes works 59 | levels(dat$ARM)[1] <- "A: Drug X(1)" 60 | 61 | # Generate listing 62 | lsting <- as_listing( 63 | df = dat, 64 | key_cols = c("ARM"), 65 | disp_cols = c("BMRKR1"), 66 | main_footer = "(1) adasdasd" 67 | ) 68 | 69 | expect_true(grepl(toString(lsting), pattern = "\\(1\\) adasdasd")) 70 | expect_true(grepl(toString(lsting), pattern = "A: Drug X\\(1\\)")) 71 | }) 72 | 73 | test_that("align_colnames can change alignment for column titles", { 74 | dat <- ex_adae[1:3, ] 75 | attr(dat$STUDYID, "label") <- "A" # For clearer alignment 76 | 77 | lsting <- as_listing(dat, 78 | key_cols = "USUBJID", 79 | disp_cols = "STUDYID", 80 | col_formatting = c("STUDYID" = fmt_config(align = "left")), 81 | align_colnames = TRUE 82 | ) 83 | 84 | # same alignment 85 | mat <- matrix_form(lsting) 86 | expect_equal(mat$aligns[, 2], rep("left", 4)) 87 | 88 | # post-processing changes 89 | align_colnames(lsting) <- FALSE 90 | mat <- matrix_form(lsting) 91 | expect_equal(mat$aligns[1, 2], "center") 92 | 93 | # pagination 94 | align_colnames(lsting) <- FALSE 95 | out <- paginate_listing(lsting, lpp = 4, print_pages = FALSE) 96 | expect_snapshot(cat(toString(out[[2]]))) 97 | 98 | # pagination 99 | align_colnames(lsting) <- TRUE 100 | out <- paginate_listing(lsting, lpp = 4, print_pages = FALSE) 101 | expect_snapshot(cat(toString(out[[2]]))) 102 | }) 103 | -------------------------------------------------------------------------------- /tests/testthat/test-print.R: -------------------------------------------------------------------------------- 1 | testthat::test_that("Listing print correctly", { 2 | lsting <- as_listing(anl, key_cols = c("USUBJID")) %>% 3 | add_listing_col("ARM") 4 | 5 | res <- strsplit(toString(matrix_form(lsting), hsep = "-"), "\\n")[[1]] 6 | ## regression 7 | printout <- capture.output(print(lsting, hsep = "-")) 8 | testthat::expect_false(any(grepl("iec", printout, fixed = TRUE))) 9 | testthat::expect_identical(res, printout) 10 | 11 | testthat::expect_snapshot(res) 12 | }) 13 | 14 | testthat::test_that("Listing print correctly with different widths", { 15 | lsting <- as_listing(anl, key_cols = c("USUBJID")) %>% 16 | add_listing_col("ARM") 17 | 18 | res <- strsplit(toString(matrix_form(lsting), widths = c(7, 8, 9), hsep = "-"), "\\n")[[1]] 19 | res2 <- strsplit(toString(lsting, widths = c(7, 8, 9), hsep = "-"), "\\n")[[1]] 20 | 21 | testthat::expect_identical(res, res2) 22 | 23 | testthat::expect_snapshot(cat(toString(matrix_form(lsting), widths = c(7, 8, 9), hsep = "-"))) 24 | }) 25 | 26 | testthat::test_that("as_listing produces correct output when default_formatting is specified", { 27 | anl$BMRKR1[3:6] <- NA 28 | lsting <- as_listing( 29 | anl, 30 | key_cols = "USUBJID", 31 | default_formatting = list( 32 | all = fmt_config(align = "left"), 33 | numeric = fmt_config(format = "xx.xx", na_str = "", align = "right") 34 | ) 35 | ) 36 | 37 | res <- strsplit(toString(matrix_form(lsting), hsep = "-"), "\\n")[[1]] 38 | 39 | testthat::expect_snapshot(res) 40 | 41 | testthat::expect_error( 42 | { 43 | as_listing( 44 | anl, 45 | key_cols = "USUBJID", 46 | default_formatting = list(all = list(align = "left")) 47 | ) 48 | }, 49 | "All format configurations supplied in `default_formatting` must be of type `fmt_config`." 50 | ) 51 | 52 | testthat::expect_error( 53 | res <- as_listing( 54 | anl, 55 | key_cols = "USUBJID", 56 | default_formatting = list(numeric = fmt_config(align = "left")) 57 | ), 58 | regexp = paste0( 59 | "Format configurations must be supplied for all listing columns. ", 60 | "To cover all remaining columns please add an ", 61 | "'all' configuration to `default_formatting`." 62 | ) 63 | ) 64 | }) 65 | 66 | testthat::test_that("as_listing produces correct output when col_formatting is specified", { 67 | anl$ARM[3:6] <- NA 68 | lsting <- as_listing( 69 | anl, 70 | key_cols = "USUBJID", 71 | col_formatting = list( 72 | USUBJID = fmt_config(align = "right"), 73 | ARM = fmt_config(format = sprintf_format("ARM #: %s"), na_str = "-", align = "left") 74 | ) 75 | ) 76 | 77 | res <- strsplit(toString(matrix_form(lsting), hsep = "-"), "\\n")[[1]] 78 | testthat::expect_snapshot(res) 79 | 80 | # Mixed behavior 81 | # Note: all is rightfully masked by the more specific numeric assignment 82 | lsting <- as_listing( 83 | anl, 84 | key_cols = "USUBJID", disp_cols = "BMRKR1", 85 | default_formatting = list( 86 | numeric = fmt_config(align = "right"), 87 | all = fmt_config(na_str = "default na") 88 | ), 89 | col_formatting = list( 90 | BMRKR1 = fmt_config(na_str = "bmrkr1 special", format = "xx.") # This has precedence 91 | ) 92 | ) 93 | 94 | testthat::expect_snapshot(cat(toString(matrix_form(lsting), hsep = "-"))) 95 | 96 | testthat::expect_error( 97 | { 98 | as_listing( 99 | anl, 100 | key_cols = "USUBJID", 101 | col_formatting = list(all = list(align = "left")) 102 | ) 103 | }, 104 | "All format configurations supplied in `col_formatting` must be of type `fmt_config`." 105 | ) 106 | 107 | # Other error 108 | testthat::expect_error( 109 | { 110 | as_listing( 111 | anl, 112 | key_cols = "USUBJID", 113 | col_formatting = list(USUBJID = list(numeric = fmt_config(align = "left"))) 114 | ) 115 | }, 116 | "All format configurations supplied in `col_formatting` must be of type `fmt_config`." 117 | ) 118 | }) 119 | 120 | testthat::test_that("listings support newline characters", { 121 | anl$ARM[3:6] <- NA 122 | anl$USUBJID[1] <- "aaatrial\ntrial\n" # last \n is trimmed 123 | vl <- var_labels(anl) 124 | vl[3] <- "\n\na\n\nn\n" 125 | var_labels(anl) <- vl 126 | anl_tmp <- anl[1:4, ] 127 | lsting <- as_listing( 128 | anl_tmp, 129 | key_cols = "USUBJID", 130 | col_formatting = list( 131 | USUBJID = fmt_config(align = "right"), 132 | ARM = fmt_config(format = sprintf_format("ARM #: %s"), na_str = "-\nasd\n", align = "left") 133 | ) 134 | ) 135 | main_footer(lsting) <- c("main_footer: argh\nasr", "sada\n") 136 | prov_footer(lsting) <- c("prov_footer: argh\nasr", "sada\n") 137 | main_title(lsting) <- "main_title: argh\nasr" 138 | subtitles(lsting) <- c("subtitle: argh\nasr", "sada\n") 139 | 140 | res <- strsplit(toString(matrix_form(lsting), hsep = "-"), "\\n")[[1]] 141 | testthat::expect_snapshot(res) 142 | 143 | res_txt <- strsplit(export_as_txt(lsting, hsep = "-"), "\\n")[[1]] 144 | testthat::expect_identical(res, res_txt) 145 | }) 146 | 147 | testthat::test_that("listings supports wrapping", { 148 | lsting <- as_listing( 149 | anl, 150 | key_cols = "USUBJID", 151 | col_formatting = list( 152 | USUBJID = fmt_config(align = "right"), 153 | ARM = fmt_config(format = sprintf_format("ARM #: %s"), na_str = "-\nasd\n", align = "left") 154 | ) 155 | ) 156 | 157 | cw <- c(5, 8, 2) 158 | ts_wrap <- strsplit(toString(lsting, widths = cw), "\n")[[1]] 159 | testthat::expect_equal(length(ts_wrap), 98) 160 | eat_wrap <- strsplit(export_as_txt(lsting, colwidths = cw), "\n")[[1]] 161 | testthat::expect_equal(length(eat_wrap), 98 + 2 + 15) # 2 is the page separator, 15 is header 162 | testthat::expect_equal(eat_wrap[-seq(116 - 9 - 17, 115 - 9)], ts_wrap) # 9 is in second page 163 | 164 | # Fix C stack inf rec loop 165 | testthat::expect_silent(toString(lsting, widths = c(10, 10, 1))) 166 | }) 167 | 168 | 169 | testthat::test_that("sas rounding support", { 170 | df <- data.frame(id = 1:3 + 0.845, value = 0.845) 171 | lsting <- as_listing(df, key_cols = "id", default_formatting = list(all = fmt_config("xx.xx"))) 172 | txt1 <- export_as_txt(lsting) 173 | txtlns1 <- strsplit(txt1, "\n", fixed = TRUE)[[1]] 174 | expect_true(all(grepl(".*84.*84 $", txtlns1[3:5]))) 175 | expect_false(any(grepl("85", txtlns1))) 176 | txt2 <- export_as_txt(lsting, round_type = "sas") 177 | txtlns2 <- strsplit(txt2, "\n", fixed = TRUE)[[1]] 178 | expect_true(all(grepl(".*85.*85 $", txtlns2[3:5]))) 179 | expect_false(any(grepl("84", txtlns2))) 180 | expect_identical( 181 | export_as_txt(lsting, round_type = "sas"), 182 | toString(lsting, round_type = "sas") 183 | ) 184 | }) 185 | 186 | testthat::test_that("listings supports horizontal separators", { 187 | result <- as_listing( 188 | df = ex_adae, 189 | disp_cols = c("ARM"), 190 | key_cols = c("USUBJID", "AETOXGR"), 191 | add_trailing_sep = c("ARM", "AETOXGR"), # columns 192 | trailing_sep = "k" 193 | ) 194 | result <- head(result, 15) 195 | 196 | expect_equal( 197 | sum( 198 | sapply( 199 | strsplit(toString(result), "\n")[[1]], 200 | function(x) { 201 | x == paste0(rep(substr(x, 1, 1), nchar(x)), collapse = "") 202 | }, 203 | USE.NAMES = FALSE 204 | ) 205 | ), 206 | 9 207 | ) 208 | 209 | # numeric values 210 | result <- as_listing( 211 | df = ex_adae, 212 | disp_cols = c("ARM"), 213 | key_cols = c("USUBJID", "AETOXGR"), 214 | add_trailing_sep = c(1, 2), 215 | trailing_sep = "k" 216 | ) 217 | result <- head(result, 15) 218 | 219 | expect_equal( 220 | sum( 221 | sapply( 222 | strsplit(toString(result), "\n")[[1]], 223 | function(x) { 224 | x == paste0(rep(substr(x, 1, 1), nchar(x)), collapse = "") 225 | }, 226 | USE.NAMES = FALSE 227 | ) 228 | ), 229 | 2 + 1 # there is the bar too 230 | ) 231 | 232 | 233 | # Some errors 234 | expect_error( 235 | result <- as_listing( 236 | df = ex_adae, 237 | add_trailing_sep = c(-1, 2), 238 | trailing_sep = "k" 239 | ), 240 | "The row indices specified in `add_trailing_sep` are not valid" 241 | ) 242 | 243 | expect_error( 244 | result <- as_listing( 245 | df = ex_adae, 246 | add_trailing_sep = c(1, 2), 247 | trailing_sep = "more values" 248 | ), 249 | "All elements must have exactly 1 characters" 250 | ) 251 | 252 | expect_error( 253 | result <- as_listing( 254 | df = ex_adae, 255 | add_trailing_sep = "not present" 256 | ), 257 | "does not exist in the dataframe" 258 | ) 259 | 260 | # snapshot 261 | expect_snapshot( 262 | as_listing( 263 | df = data.frame(one_col = c("aa", "aa", "b")), 264 | key_cols = "one_col", 265 | add_trailing_sep = "one_col", trailing_sep = "+" 266 | ) 267 | ) 268 | }) 269 | 270 | test_that("spanning column label machinery works", { 271 | lsting <- as_listing( 272 | df = head(ex_adae, 10), 273 | key_cols = c("ARM", "RACE", "USUBJID"), 274 | disp_cols = c("BMRKR1", "BMRKR2", "AEBODSYS", "AEDECOD"), 275 | spanning_col_labels = data.frame(span_level = 1, 276 | label = c("key columns", "lame columns"), 277 | start = c(1, 4), 278 | span = c(3, 4)) 279 | ) 280 | txtvec <- capture.output(print(lsting)) 281 | expect_true(grepl("^[[:space:]]+key columns[[:space:]]+lame columns[[:space:]]+$", txtvec[1])) 282 | testthat::expect_snapshot(cat(txtvec, sep = "\n")) 283 | 284 | expect_error( 285 | as_listing( 286 | df = head(ex_adae, 10), 287 | key_cols = c("ARM", "RACE", "USUBJID"), 288 | disp_cols = c("BMRKR1", "BMRKR2", "AEBODSYS", "AEDECOD"), 289 | spanning_col_labels = data.frame(span_level = 1, 290 | label = c("key columns", "lame columns"), 291 | start = c(1, 4), 292 | span = c(5, 3)) 293 | ) 294 | ) 295 | 296 | 297 | ## ok when some cols aren't covered by a spanning label 298 | 299 | lsting2 <- as_listing( 300 | df = head(ex_adae, 10), 301 | key_cols = c("ARM", "RACE", "USUBJID"), 302 | disp_cols = c("BMRKR1", "BMRKR2", "AEBODSYS", "AEDECOD"), 303 | spanning_col_labels = data.frame(span_level = 1, 304 | label = c("key columns", 305 | "lame columns"), 306 | start = c(2, 6), 307 | span = c(2, 2)) 308 | ) 309 | txtvec2 <- capture.output(print(lsting2)) 310 | expect_true(grepl("^[[:space:]]+key columns[[:space:]]+lame columns[[:space:]]+$", txtvec2[1])) 311 | ## not the same though... 312 | expect_false(txtvec[1] == txtvec2[1]) 313 | 314 | ## pagination behaves ok 315 | 316 | txt3 <- export_as_txt(lsting, cpp = 120) 317 | ## 3 pages (only 2 of them have the page break tho... 318 | ## and all of them have the lame columns header spanning over their non key dispcols 319 | expect_equal( 320 | length(grep("^(\\\\s\\\\n){0,1}[[:space:]]+key columns[[:space:]]+lame columns[[:space:]]+$", 321 | strsplit(txt3, "\n")[[1]])), 322 | 3 323 | ) 324 | 325 | 326 | ## multi-label badboys, the concept of this for a listing gets sillier and sillier 327 | ## the more rows of extra labels you add, but in for a penny in for a pound. 328 | ## might as well do it right if we're gonna do it 329 | 330 | ## we can even have overlaps (on different span levels) 331 | 332 | lsting_silly <- as_listing( 333 | df = head(ex_adae, 10), 334 | key_cols = c("ARM", "RACE", "USUBJID"), 335 | disp_cols = c("BMRKR1", "BMRKR2", "AEBODSYS", "AEDECOD"), 336 | spanning_col_labels = rbind( 337 | data.frame(span_level = 1, label = c("key columns", "lame columns"), start = c(1, 4), span = c(3, 4)), 338 | data.frame(span_level = 2, label = "biomarkers", start = 4, span = 2), 339 | data.frame(span_level = 3, label = "AE info", start = 6, span = 2), 340 | data.frame(span_level = 4, label = "whatever man", start = 5, span = 3) 341 | ) 342 | ) 343 | 344 | txt_silly <- capture.output(print(lsting_silly)) 345 | testthat::expect_snapshot(cat(txt_silly, sep = "\n")) 346 | }) 347 | -------------------------------------------------------------------------------- /vignettes/col_formatting.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Column Formatting" 3 | author: "Emily de la Rua" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Column Formatting} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | suggested_dependent_pkgs <- c("dplyr") 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>", 17 | eval = all(vapply( 18 | suggested_dependent_pkgs, 19 | requireNamespace, 20 | logical(1), 21 | quietly = TRUE 22 | )) 23 | ) 24 | ``` 25 | 26 | --------- 27 | 28 | ## Introduction 29 | 30 | This vignette demonstrates how content in columns of a `listing_df` object can be customized using format configurations with the `rlistings` R package. 31 | 32 | The following topics will be covered: 33 | 34 | - Adjusting default column formatting settings in `as_listing` 35 | - Applying custom formatting to specific columns in `as_listing` 36 | - Applying custom formatting settings when adding a new column to a listing via `add_listing_col` 37 | 38 | To learn more about how listings are constructed using the `rlistings` package, see the [Getting Started vignette](rlistings.html). 39 | 40 | --------- 41 | 42 | ## Default Formatting in `as_listing` 43 | 44 | When creating a listing with the `rlistings` package, you may want to customize how content is rendered within one or more of your listing columns. In this section we will demonstrate how default formatting can be set within the `as_listing` function via the `default_formatting` parameter. 45 | 46 | The `default_formatting` argument to `as_listing` accepts a named list of format configurations to apply within your listing. Format configurations are supplied as `fmt_config` objects which contain 3 elements to control formatting: 47 | 48 | 1. `format`: A format label (string) or format function to apply when rendering values (see all valid options with `?formatters::list_valid_format_labels()`). Defaults to `NULL`. 49 | 2. `na_str`: A string that should be displayed in place of missing values. Defaults to `"NA"`. 50 | 3. `align`: Alignment to use when rendering the listing column. Defaults to `"center"`. Other options include `"left"`, `"right"`, `"decimal"`, `"dec_right"`, and `"dec_left"`. 51 | 52 | The `default_formatting` argument can use the same format configuration for all columns in a listing (as is the default), but also allows the user to set different format configurations for each data class present in your listing. The list supplied to `default_formatting` must contain a named element corresponding to every data class present in your listing, or include the `all` element with a configuration that will be applied to any data classes that are _not_ explicitly covered. 53 | 54 | To demonstrate, we will create a basic listing below and customize formatting using the `default_formatting` parameter. 55 | 56 | We begin by loading in the `rlistings` package. 57 | 58 | ```{r, message=FALSE} 59 | library(rlistings) 60 | require(dplyr) 61 | ``` 62 | 63 | For this example, we will use the dummy ADAE dataset provided within the `formatters` package as our data frame, which consists of 48 columns of adverse event patient data, and one or more rows per patient. For the purpose of this example, we will subset the data and only use the first 15 records of the dataset. We will create some `NA` values in the data to showcase how `NA` values can be formatted, and sort the data by what will be our key columns. 64 | 65 | ```{r} 66 | adae <- ex_adae[1:15, ] 67 | 68 | set.seed(1) 69 | adae <- as.data.frame(lapply(adae, function(x) replace(x, sample(length(x), 0.1 * length(x)), NA))) 70 | 71 | adae <- adae %>% dplyr::arrange(USUBJID, AGE, TRTSDTM) 72 | ``` 73 | 74 | Now we will create a basic listing. 75 | 76 | ```{r} 77 | lsting_1 <- as_listing( 78 | df = adae, 79 | key_cols = c("USUBJID", "AGE", "TRTSDTM"), 80 | disp_cols = c("BMRKR1", "ASEQ", "AESEV"), 81 | ) 82 | 83 | lsting_1 84 | ``` 85 | 86 | Notice that all of the data in the table above is displayed as is, with no rounding or formatting applied. All columns are centered and all missing values are displayed as `"NA"`. 87 | 88 | Suppose we want to left align all of the columns in the listing and replace missing values with the string `""`. This can be done by setting the `all` element in the list supplied to `default_formatting`, as shown in the following example. 89 | 90 | ```{r} 91 | default_fmt <- list( 92 | all = fmt_config(na_str = "", align = "left") 93 | ) 94 | 95 | lsting_2 <- as_listing( 96 | df = adae, 97 | key_cols = c("USUBJID", "AGE", "TRTSDTM"), 98 | disp_cols = c("BMRKR1", "ASEQ", "AESEV"), 99 | default_formatting = default_fmt 100 | ) 101 | 102 | lsting_2 103 | ``` 104 | 105 | Now consider that we would like to display our numeric columns with two decimal places and then align these columns on the decimal point. This can be done by adding a `"numeric"` element to the `default_formatting` list as follows: 106 | 107 | ```{r} 108 | default_fmt <- list( 109 | all = fmt_config(na_str = "", align = "left"), 110 | numeric = fmt_config(format = "xx.xx", na_str = "", align = "decimal") 111 | ) 112 | 113 | lsting_3 <- as_listing( 114 | df = adae, 115 | key_cols = c("USUBJID", "AGE", "TRTSDTM"), 116 | disp_cols = c("BMRKR1", "ASEQ", "AESEV"), 117 | default_formatting = default_fmt 118 | ) 119 | 120 | lsting_3 121 | ``` 122 | 123 | Along with the format strings listed by `formatters::list_valid_format_labels`, we can also specify a format _function_ to allow for more customized formats in our listing. In the following example, we will define and apply a custom format function to format date (`POSIXt` class) columns in our listing. 124 | 125 | ```{r} 126 | # Custom format function - takes date format as input 127 | date_fmt <- function(fmt) { 128 | function(x, ...) do.call(format, list(x = x, fmt)) 129 | } 130 | 131 | default_fmt <- list( 132 | all = fmt_config(na_str = "", align = "left"), 133 | numeric = fmt_config(format = "xx.xx", na_str = "", align = "decimal"), 134 | POSIXt = fmt_config(format = date_fmt("%B %d, %Y @ %I:%M %p %Z"), na_str = "") 135 | ) 136 | 137 | lsting_4 <- as_listing( 138 | df = adae, 139 | key_cols = c("USUBJID", "AGE", "TRTSDTM"), 140 | disp_cols = c("BMRKR1", "ASEQ", "AESEV"), 141 | default_formatting = default_fmt 142 | ) 143 | 144 | lsting_4 145 | ``` 146 | 147 | In the output above, the `all` format configuration, which originally applied to all columns in the listing, now only applies to the two character/factor variables (`USUBJID` and `AESEV`). This is because all other data classes in the listing have been covered by other elements in the list provided to `default_formatting`. When format configurations are supplied to a listing, any other applicable configuration take precedence over the `all` format configuration. 148 | 149 | ## Column-Wise Formatting in `as_listing` 150 | 151 | In this section, we will demonstrate how custom formatting can be applied on a column-by-column basis rather than to all columns of a specified data class or an entire listing at once. 152 | 153 | Take, for example, `lsting_4` created in the previous section. 154 | 155 | ```{r} 156 | lsting_4 157 | ``` 158 | 159 | This listing applies the same format configuration to all numeric columns. But in some cases, this may not produce the result we want. In the above listing, the "Age" and "Analysis Sequence Number" columns contain only integer values, so we would like to _not_ render these columns with two decimal places and instead only apply the current numeric format configuration to the "Continuous Level Biomarker 1" column. To do so, we make use of the `col_formatting` argument to `as_listing`. Like `default_formatting`, this argument takes a named list of format configurations (`fmt_config` objects) as input, but unlike `default_formatting` the names of the list elements correspond to column names. The `col_formatting` argument can be used in combination with the `default_formatting` argument or on its own, and for any number of columns present in your listing, depending on your requirements. 160 | 161 | See the following example which demonstrates how `col_formatting` can be used with the `BMRKR1` column. We will use the `"xx"` format and right alignment for the two remaining numeric columns. 162 | 163 | ```{r} 164 | default_fmt <- list( 165 | all = fmt_config(na_str = "", align = "left"), 166 | numeric = fmt_config(format = "xx", na_str = "", align = "right"), 167 | POSIXt = fmt_config(format = date_fmt("%B %d, %Y @ %I:%M %p %Z"), na_str = "") 168 | ) 169 | 170 | col_fmt <- list( 171 | BMRKR1 = fmt_config(format = "xx.xx", na_str = "", align = "decimal") 172 | ) 173 | 174 | lsting_5 <- as_listing( 175 | df = adae, 176 | key_cols = c("USUBJID", "AGE", "TRTSDTM"), 177 | disp_cols = c("BMRKR1", "ASEQ", "AESEV"), 178 | default_formatting = default_fmt, 179 | col_formatting = col_fmt 180 | ) 181 | 182 | lsting_5 183 | ``` 184 | 185 | Now all of the columns present in our listing are formatted according to our specifications. Note that format configurations supplied to `col_formatting` for individual columns take precedence over any format configurations from `default_formatting`. 186 | 187 | ## Adding Formatted Columns to a Listing via `add_listing_col` 188 | 189 | In some cases, you may want to add a new column with its own formatting settings to a pre-existing listing. In this section, we will demonstrate how this can be accomplished using the `add_listing_col`. Columns added after a listing has already been created with `as_listing` will not inherit format configurations previously applied, so formatting for the new column must be specified _within_ the `add_listing_col` function. Instead of creating a `fmt_config` object, the `format`, `na_str`, and `align` specifications are supplied directly to `add_listing_col` using its the parameters by the same names. If these parameters are not specified, default values of `NULL`, `"NA"`, and `"left"` will be used as `format`, `na_str`, and `align`, respectively. The `add_listing_col` can be used in sequence as many times as needed to add new columns to a listing. 190 | 191 | In this example, we will add a column to `lsting_5` created in the previous section. This new column will calculates the length of the analysis (in days) by subtracting "Analysis Start Relative Day" (`ASTDY`) from "Analysis End Relative Day" (`AENDY`). This can be done as follows: 192 | 193 | ```{r} 194 | lsting_6 <- lsting_5 %>% 195 | add_listing_col( 196 | name = "Length of\nAnalysis", 197 | fun = function(df) df$AENDY - df$ASTDY, 198 | format = "xx.x", 199 | na_str = "NE", 200 | align = "center" 201 | ) 202 | 203 | lsting_6 204 | ``` 205 | 206 | --------- 207 | 208 | ## Summary 209 | 210 | In this vignette, you have learned how column formatting can be configured using the `default_formatting` and `col_formatting` arguments to `as_listing` and the `add_listing_col` function to customize how listings are rendered. 211 | 212 | **For more information please explore the [rlistings website](https://insightsengineering.github.io/rlistings/main/).** 213 | -------------------------------------------------------------------------------- /vignettes/large_list.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tips when exporting large listing" 3 | author: "Joe Zhu" 4 | date: "`r Sys.Date()`" 5 | output: 6 | rmarkdown::html_vignette 7 | vignette: > 8 | %\VignetteIndexEntry{Tips when exporting large listing} 9 | %\VignetteEngine{knitr::rmarkdown} 10 | %\VignetteEncoding{UTF-8} 11 | --- 12 | 13 | ```{r, include = FALSE} 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>" 17 | ) 18 | ``` 19 | 20 | ```{css, echo=FALSE} 21 | pre { 22 | max-height: 800px !important; 23 | } 24 | ``` 25 | 26 | ## Introduction 27 | 28 | For submission, sometimes listings are required. Sometimes large listings can include thousands of patients and tens of thousands of rows. Exporting such large listing files is time-consuming and memory-heavy, and it is not obvious if the job is still ongoing or is already dead. The time cost is mostly due to paginating the listing object into multiple pages. 29 | 30 | Consider the following example: 31 | 32 | ```{r, eval = FALSE} 33 | library(rlistings) 34 | 35 | iris2 <- do.call(rbind, rep(list(iris), 40)) 36 | rlist <- as_listing(iris2, key_cols = "Species", 37 | disp_cols = c("Sepal.Length", "Sepal.Width", "Petal.Width", "Petal.Length")) 38 | 39 | bench::mark( 40 | a = paginate_to_mpfs(rlist[1:1000, ]), 41 | b = paginate_to_mpfs(rlist[1:2000, ]), 42 | c = paginate_to_mpfs(rlist[1:3000, ]), 43 | d = paginate_to_mpfs(rlist[1:4000, ]), 44 | e = paginate_to_mpfs(rlist[1:5000, ]), 45 | f = paginate_to_mpfs(rlist[1:6000, ]), 46 | check = FALSE, 47 | max_iterations = 1 48 | ) 49 | ``` 50 | 51 | It gives the following benchmark results: 52 | 53 | ``` 54 | expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time 55 | 56 | 1 a 6.13s 6.13s 0.163 2.17GB 0.816 1 5 6.13s 57 | 2 b 13.35s 13.35s 0.0749 8.63GB 1.42 1 19 13.35s 58 | 3 c 22.48s 22.48s 0.0445 19.38GB 1.74 1 39 22.48s 59 | 4 d 36.73s 36.73s 0.0272 34.41GB 1.80 1 66 36.73s 60 | 5 e 44.88s 44.88s 0.0223 53.72GB 1.63 1 73 44.88s 61 | 6 f 1.01m 1.01m 0.0166 77.32GB 1.72 1 104 1.01m 62 | # ℹ 4 more variables: result , memory , time , gc 63 | ``` 64 | 65 | It is obvious that the time consumption grows linearly as the rows of data increase. 66 | 67 | ![](rlisting_time.png) 68 | 69 | To decrease the runtime and prevent memory issues, we would recommend splitting the listing object by a grouping variable and exporting the output separately. Consider the following demonstration: 70 | 71 | ```{r, eval = FALSE} 72 | iris3 <- cbind(iris2, gp = rep(c(1, 2, 3, 4, 5, 6), 1000)) 73 | rlist3 <- as_listing(iris3, key_cols = "Species", 74 | disp_cols = c("Sepal.Length", "Sepal.Width", "Petal.Width", "Petal.Length")) 75 | 76 | start.time <- Sys.time() 77 | rlist3 %>% split(rlist3$gp) %>% lapply(., paginate_to_mpfs) 78 | end.time <- Sys.time() 79 | 80 | time.taken <- end.time - start.time 81 | time.taken 82 | 83 | # > Time difference of 36.06119 secs 84 | ``` 85 | 86 | In principle, you could consider processing this work with multi-threading to further reduce the runtime. 87 | 88 | ```{r, eval = FALSE} 89 | library(parallel) 90 | 91 | start.time <- Sys.time() 92 | rlist3 %>% split(rlist3$gp) %>% mclapply(., paginate_to_mpfs) 93 | end.time <- Sys.time() 94 | 95 | time.taken <- end.time - start.time 96 | time.taken 97 | 98 | #> Time difference of 18.20406 secs 99 | ``` 100 | 101 | -------------------------------------------------------------------------------- /vignettes/pagination.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pagination" 3 | author: "Emily de la Rua" 4 | date: "`r Sys.Date()`" 5 | output: 6 | rmarkdown::html_vignette 7 | vignette: > 8 | %\VignetteIndexEntry{Pagination} 9 | %\VignetteEngine{knitr::rmarkdown} 10 | %\VignetteEncoding{UTF-8} 11 | --- 12 | 13 | ```{r, include = FALSE} 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>" 17 | ) 18 | ``` 19 | 20 | ```{css, echo=FALSE} 21 | pre { 22 | max-height: 800px !important; 23 | } 24 | ``` 25 | 26 | ## Introduction 27 | 28 | This vignette shows how pagination can be applied to `listing_df` objects using the `rlistings` R package. 29 | 30 | Specifically, the following topics will be covered: 31 | 32 | - Basics of pagination 33 | - Pagination with exporters 34 | - Pagination by parameter 35 | 36 | To learn more about how listings are constructed using the `rlistings` package, see the [Getting Started vignette](rlistings.html). 37 | 38 | --------- 39 | 40 | ## Basics of Pagination 41 | 42 | In many cases, listings have more rows or more columns than can fit on a single page. When this occurs, we may choose to use pagination to improve the readability of the listing and/or to fit in onto a standard-sized page for exporting or printing. When pagination is applied, all listing annotations (titles & footers) are printed on each of the returned pages. Note that currently only mono-spaced fonts are supported for pagination as characters are used to measure text width. 43 | 44 | Vertical pagination occurs when a listing is especially long and has too many _rows_ to fit on one page, while horizontal pagination is applied when a listing is especially wide and has too many _columns_ to print on a single page. When horizontal pagination is applied to a listing, any key columns that have been specified for the listing will be repeated as the leftmost columns on each page. If a value from a key column, for example a subject ID, is present in rows that extend over several pages, that key column value will be printed at the top of each page. 45 | 46 | The `paginate_listing` function paginates a listing object and returns a list of listing objects with each list element corresponding to a separate page. This function should be used if you want to paginate a listing to view within your R console or you would like to process the list of paginated output further before exporting. 47 | 48 | In the R code below, we will give a basic example of how to create a listing with `rlistings` from a pre-processed data frame and paginate the listing using `paginate_listing`. 49 | 50 | We first load in the `rlistings` package. 51 | 52 | ```{r, message=FALSE} 53 | library(rlistings) 54 | ``` 55 | 56 | For the purpose of this example we will use the dummy ADAE dataset provided within the `formatters` package as our data frame, which consists of 48 columns of adverse event patient data, and one or more rows per patient. For the purpose of this example, we will subset the data and only use the first 100 records of the dataset. 57 | 58 | ```{r} 59 | adae <- ex_adae[1:100, ] 60 | ``` 61 | 62 | Now we will create a basic listing. 63 | 64 | ```{r} 65 | lsting <- as_listing( 66 | df = adae, 67 | key_cols = c("USUBJID", "ARM"), 68 | disp_cols = c("AETOXGR", "AEDECOD", "AESEV"), 69 | main_title = "Title", 70 | main_footer = "Footer" 71 | ) 72 | 73 | head(lsting, 20) 74 | ``` 75 | 76 | In the listing output above, notice that there are two key columns that we expect to be repeated on each page after pagination. 77 | 78 | Next, we see how the pagination works with the default settings via the `paginate_listing` function that is a wrapper of `paginate_to_mpfs()` that is the core pagination function and lives in `formatters`. 79 | Default pagination applies the following settings, which are adjustable via the parameter specified in parentheses: 80 | 81 | - Page type: `"letter"` (`page_type`) - other options: `"a4"`, `"legal"` 82 | - Font family: `"Courier"` (`font_family`) - other options: `"mono"`, `"NimbusMon"`, `"Japan1"`, `"Japan1HeiMin"`, `"Japan1GothicBBB"`, `"Japan1Ryumin"`, `"Korea1"`, `"Korea1deb"`, `"CNS1"`, `"GB1"` 83 | - Font size: `8` (`font_size`) 84 | - Line height: `1` (`line_height`) 85 | - Landscape: `FALSE` - portrait orientation (`landscape`) 86 | - Margins: `0.5`, `0.75` - inches for top/bottom and left/right margins, respectively (`margins`) 87 | 88 | These parameters, as well as some additional arguments not listed here (see `?paginate_listing` for all options), can be set to fine-tune your pagination output. 89 | 90 | ```{r} 91 | paginate_listing(lsting) 92 | ``` 93 | 94 | We can see from the above output that applying pagination separated our listing into 4 pages (list elements). Each page includes the two key columns as well as however many display columns fit horizontally on the page. Pages 1 and 3 contain only the key columns and the "Analysis Toxicity Grade" column, while the overflowing columns from these pages are present on pages 2 and 4, respectively. Additionally, vertical pagination is applied for this listing, with 20 rows that do not fit vertically on pages 1 and 2 overflowing onto pages 3 and 4, respectively. We can also see that the last subject included on pages 1 and 2, with subject ID "AB12345-BRA-12-id-59", has additional rows that overflow onto pages 3 and 4, and the key column values for this subject are repeated in the first line of these two pages. 95 | 96 | ### Alternative Methods to Specify Page Size 97 | 98 | In addition to specifying `page_type`, there are three alternative methods that can be used to specify page size when paginating a listing: 99 | 100 | 1. `pg_width` and `pg_height` 101 | 2. `lpp` and `cpp` 102 | 3. `colwidths` 103 | 104 | If method 1 or 2 is implemented, the `page_type` argument will be ignored. 105 | 106 | #### 1. `pg_width` and `pg_height` 107 | 108 | As an alternative to specifying page type, the user can instead supply page width (`pg_width`) and 109 | page height (`pg_height`) values in inches to define the page size. 110 | 111 | #### 2. `lpp` and `cpp` 112 | 113 | For more control users can instead set the `lpp` (lines per page) and `cpp` (characters per page) parameters to set an exact number of rows in the vertical dimension that should be included per page, and characters per line that should be included per page in the horizontal dimension, respectively. If `NULL` is supplied to either of these parameters, pagination in the associated dimension will not be applied. 114 | 115 | Considerations when using `lpp` and `cpp`: 116 | 117 | - The `lpp` value must include lines for titles and footers, which are included on every page. 118 | - If a value is supplied which does not allow for valid pagination, an error will occur. One example where an error would occur is if your titles & footer information (including separator lines) spans 10 rows but you specify `lpp` as 5. 119 | 120 | See the following example which uses `lpp` and `cpp` instead of `page_type` to specify page size: 121 | 122 | ```{r} 123 | paginate_listing(lsting, lpp = 50, cpp = NULL) 124 | ``` 125 | 126 | Here we set `lpp` to 50 which shortens the vertical length of each page to a maximum of 50 lines (8 rows of header/footer information + 42 rows of data). By setting `cpp` to `NULL` we disable pagination in the horizontal direction so that all columns fit horizontally across each page. 127 | 128 | #### 3. `colwidths` 129 | 130 | When applying horizontal pagination, the `colwidths` parameter can be set via a numeric vector with widths to use for each column. The length of this vector must be equal to the number of columns in the listing, with each element corresponding to the column of the listing at the same index. 131 | 132 | Considerations when using `colwidths`: 133 | 134 | - If the supplied column width is smaller than the widest text in the column (or its label), then the width of that column defaults to the number of characters of the widest text in that column. 135 | - This argument only affects horizontal pagination (page width) and should be used in combination with an argument that applies vertical pagination (page height). 136 | 137 | ## Pagination with Exporters 138 | 139 | ### `export_as_txt` 140 | 141 | As with `paginate_listing`, `export_as_txt` can also be used to paginate listing objects. Instead of returning a list of listings by page, the `export_as_txt` function will, if no file is specified, return a concatenated string value of all of the page content resulting after pagination. If the `file` parameter is specified, the `export_as_txt` function will instead write the result to the supplied `.txt` file. 142 | 143 | The `export_as_txt` function contains all of the arguments available in `paginate_listing`, which work the same way, plus some additional arguments that are useful for listing pagination: 144 | 145 | - `file`: The path to write a text file to, with the paginated listing rendered as ASCII text. 146 | - `hsep`: Character to repeat to create separator line between header/footer and body. 147 | - `page_break`: Page break symbol. Defaults to `"\\s\\n"`. 148 | 149 | Note that if the `paginate` argument is set to `FALSE`, no pagination will occur. 150 | 151 | See an example using `export_as_txt` below. We use the `cat` function to make the output more easily readable in the console: 152 | 153 | ```{r} 154 | cat(export_as_txt(lsting)) 155 | ``` 156 | 157 | Notice the page break symbol (`\s\n`) is repeated where page breaks occur (i.e. prior to the title on each new page). 158 | 159 | ### `export_as_rtf` 160 | 161 | The `export_as_rtf` function can be used similarly to `export_as_txt` to paginate and export listings except this function will write output to a supplied `.rtf` file containing the listing output. If no file is supplied, the `RTF` formatted output will be printed to the console. See `?export_as_rtf` for more details on this function. 162 | 163 | ## Pagination by Parameter 164 | 165 | In addition to paginating by page size as described in the previous sections of this vignette, a user may also want to paginate their listing such that each page corresponds to a different value of a given parameter. For example, you may require that each treatment arm is printed on a separate page. This can currently be done with `rlistings` using the `split_listing_by_var` function, which can be applied to your pre-existing listing as follows: 166 | 167 | ```{r} 168 | lsting_by_arm <- lsting %>% 169 | split_into_pages_by_var("ARM", page_prefix = "Treatment Arm") 170 | 171 | lsting_by_arm 172 | ``` 173 | 174 | As with `paginate_listing`, this creates a list of listings where each list element corresponds to a new page, but with each page corresponding to only one value of the given parameter. Note that the `page_prefix` argument can be specified to modify the text printed on each page to describe the current parameter value. 175 | 176 | #### Combining Pagination by Parameter with Regular Pagination 177 | 178 | To then apply regular pagination to the listing, you can apply `paginate_listing` to your list of listings by parameter. Any arguments supplied to this function will be applied to each list element. 179 | 180 | #### Combining Pagination by Parameter with `export_as_txt` 181 | 182 | Similarly, for pagination via `export_as_txt` after paginating by parameter, you can apply `export_as_txt` to your list of listings by parameter. Any arguments supplied to `export_as_txt` will be applied to each list element and the list will then be concatenated into the correct text format. 183 | 184 | For example: 185 | 186 | ```{r} 187 | cat(export_as_txt(lsting_by_arm)) 188 | ``` 189 | 190 | Again, we use the `cat` function to make the text output more easily readable in the console. 191 | 192 | --------- 193 | 194 | ## Summary 195 | 196 | In this vignette, you have learned how to use the `rlistings` package to paginate listings. You have seen examples demonstrating how custom pagination can be configured, as well as examples of pagination applied using exporter functions. You have also seen learned how pagination can be applied with pages separated by value of a given parameter. 197 | 198 | **For more information on listings pagination please see `?paginate_listing`.** 199 | -------------------------------------------------------------------------------- /vignettes/ref_footnotes.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Referential Footnotes" 3 | author: "Emily de la Rua" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Referential Footnotes} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | suggested_dependent_pkgs <- c("dplyr") 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>", 17 | eval = all(vapply( 18 | suggested_dependent_pkgs, 19 | requireNamespace, 20 | logical(1), 21 | quietly = TRUE 22 | )) 23 | ) 24 | ``` 25 | 26 | --------- 27 | 28 | ## Introduction 29 | 30 | There is currently no formal method for adding referential footnotes to a listing object. This vignette demonstrates how referential footnotes can be added to a `listing_df` object via a workaround applied during pre-processing. 31 | 32 | To learn more about how listings are constructed using the `rlistings` package, see the [Getting Started vignette](rlistings.html). 33 | 34 | --------- 35 | 36 | ## Referential Footnotes Workaround 37 | 38 | When creating a listing with the `rlistings` package, you may want to add referential footnotes, similar to how referential footnotes can be added to `rtable` objects. Since there is no formal method in `rlistings` for applying referential footnotes to a `listing_df` object, we will demonstrate how a workaround can be applied to add a set of pseudo-referential footnotes to your listing. 39 | 40 | To demonstrate, we will create a basic listing below. 41 | 42 | We begin by loading in the `rlistings` package. 43 | 44 | ```{r, message=FALSE} 45 | library(rlistings) 46 | library(dplyr) 47 | ``` 48 | 49 | For this example, we will use the dummy ADAE dataset provided within the `formatters` package as our data frame, which consists of 48 columns of adverse event patient data, and one or more rows per patient. For the purpose of this example, we will subset the data and only use the first 30 records of the dataset. 50 | 51 | ```{r} 52 | adae <- ex_adae[1:30, ] 53 | ``` 54 | 55 | Now we will create a basic listing. 56 | 57 | ```{r} 58 | lsting <- as_listing( 59 | df = adae, 60 | key_cols = c("ARM", "USUBJID", "ASEQ", "ASTDY"), 61 | disp_cols = c("BMRKR1", "AESEV"), 62 | ) 63 | 64 | lsting 65 | ``` 66 | 67 | For this example, we will add 4 referential footnotes. 68 | 69 | 1. In the `ARM` column, for all records with `ARM = "A: Drug X"` and `ASEQ` equal to 1 or 2. 70 | 2. In the `ASTDY` column, for imputed dates, where imputed dates are indicated by the `ASTDTF` variable. 71 | 3. In the `AESEV` column, for all records with `AETOXGR` equal to 5. 72 | 3. In the `USUBJID` column header. 73 | 74 | Footnote text can be supplied either as a vector, where each element is a new footnote, or as a single string with footnotes separated by the new line (`\n`) character. For example, see the following list of referential footnotes: 75 | 76 | ```{r} 77 | ref_fns <- "* ASEQ 1 or 2\n** Analysis start date is imputed\n*** Records with ATOXGR = 5\n**** ID column" 78 | ``` 79 | 80 | We start with our first footnote in the `ARM` column for records with arm "A: Drug X" which also have analysis sequence number 1 or 2. Referential footnotes can be added to any variable by converting the variable to a factor, editing the variable values, and adding a corresponding footnote below the listing. We will add our first referential footnote in the `ARM` column. To ensure that levels are correctly ordered, be sure to specify the new level order when mutating your variable. 81 | 82 | ```{r} 83 | # Save variable labels for your data to add back in after mutating dataset 84 | df_lbls <- var_labels(adae) 85 | 86 | # Mutate variable where referential footnotes are to be added according to your conditions 87 | # Specify order of levels with new referential footnotes added 88 | adae <- adae %>% dplyr::mutate( 89 | ARM = factor( 90 | ifelse(ARM == "A: Drug X" & ASEQ %in% 1:2, paste0(ARM, " (1)"), as.character(ARM)), 91 | levels = c(sapply(levels(adae$ARM), paste0, c("", "(1)"))) 92 | ) 93 | ) 94 | 95 | # Add data variable labels back in 96 | var_labels(adae) <- df_lbls 97 | 98 | # Generate listing 99 | lsting <- as_listing( 100 | df = adae, 101 | key_cols = c("ARM", "USUBJID", "ASEQ", "ASTDY"), 102 | disp_cols = c("BMRKR1", "AESEV"), 103 | main_footer = "(1) ASEQ 1 or 2" 104 | ) 105 | 106 | lsting 107 | ``` 108 | 109 | Additional referential footnotes can be added to the data be repeating the above steps. 110 | 111 | For example, we can add the second referential footnote to the `ASTDY` column for imputed analysis start days. We use dummy variable `ASTDTF` to indicate imputed analysis start dates. When adding referential footnotes to numeric variables, the variables must be converted to factors. 112 | 113 | ```{r} 114 | set.seed(1) 115 | 116 | # Save variable labels for your data to add back in after mutating dataset 117 | df_lbls <- var_labels(adae) 118 | 119 | # Mutate variable where referential footnotes are to be added according to your conditions 120 | # Specify order of levels with new referential footnotes added 121 | adae <- adae %>% 122 | dplyr::mutate(ASTDTF = sample(c("Y", NA), nrow(.), replace = TRUE, prob = c(0.25, 0.75))) %>% 123 | dplyr::mutate(ASTDY = factor( 124 | ifelse(!is.na(ASTDTF), paste0(as.character(ASTDY), "**"), as.character(ASTDY)), 125 | levels = c(sapply(sort(unique(adae$ASTDY)), paste0, c("", "**"))) 126 | )) %>% 127 | dplyr::select(-ASTDTF) 128 | 129 | # Add data variable labels back in 130 | var_labels(adae) <- df_lbls 131 | 132 | # Generate listing 133 | lsting <- as_listing( 134 | df = adae, 135 | key_cols = c("ARM", "USUBJID", "ASEQ", "ASTDY"), 136 | disp_cols = c("BMRKR1", "AESEV"), 137 | ) 138 | 139 | lsting 140 | ``` 141 | 142 | Next we can add our third referential footnote to the `AESEV` column for records with analysis toxicity grade 5. 143 | 144 | ```{r} 145 | # Save variable labels for your data to add back in after mutating dataset 146 | df_lbls <- var_labels(adae) 147 | 148 | # Mutate variable where referential footnotes are to be added according to your conditions 149 | # Specify order of levels with new referential footnotes added 150 | adae <- adae %>% dplyr::mutate( 151 | AESEV = factor( 152 | ifelse(AETOXGR == 5, paste0(AESEV, "***"), as.character(AESEV)), 153 | levels = c(sapply(levels(adae$AESEV), paste0, c("", "***"))) 154 | ) 155 | ) 156 | 157 | # Add data variable labels back in 158 | var_labels(adae) <- df_lbls 159 | 160 | # Generate listing 161 | lsting <- as_listing( 162 | df = adae, 163 | key_cols = c("ARM", "USUBJID", "ASEQ", "ASTDY"), 164 | disp_cols = c("BMRKR1", "AESEV"), 165 | ) 166 | 167 | lsting 168 | ``` 169 | 170 | Referential footnotes can also be added to column header labels. Suppose we want to add our fourth referential footnote to the `USUBJID` column label. We can do so by editing the `USUBJID` variable label and adding the footnote text as follows: 171 | 172 | ```{r} 173 | # Modify data variable label 174 | adae <- adae %>% var_relabel( 175 | USUBJID = paste0(var_labels(adae)[["USUBJID"]], "****") 176 | ) 177 | 178 | # Generate listing 179 | lsting <- as_listing( 180 | df = adae, 181 | key_cols = c("ARM", "USUBJID", "ASTDY", "ASEQ"), 182 | disp_cols = c("BMRKR1", "AESEV") 183 | ) 184 | 185 | lsting 186 | ``` 187 | 188 | Finally, we add in the referential footnote text below the listing as follows. 189 | 190 | ```{r} 191 | # Generate listing 192 | lsting <- as_listing( 193 | df = adae, 194 | key_cols = c("ARM", "USUBJID", "ASTDY", "ASEQ"), 195 | disp_cols = c("BMRKR1", "AESEV") 196 | ) 197 | 198 | main_footer(lsting) <- c(main_footer(lsting), ref_fns) 199 | 200 | lsting 201 | ``` 202 | 203 | Now our listing is complete, with all four referential footnotes denoted within the listing and described in the footnotes section below. 204 | 205 | --------- 206 | 207 | ## Summary 208 | 209 | In this vignette, you have learned how a pre-processing workaround can be applied to add referential footnotes to listings. 210 | 211 | **For more information please explore the [rlistings website](https://insightsengineering.github.io/rlistings/main/).** 212 | -------------------------------------------------------------------------------- /vignettes/rlisting_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insightsengineering/rlistings/045926f2357fd91f0d0622213d4c89642ca8707f/vignettes/rlisting_time.png -------------------------------------------------------------------------------- /vignettes/rlistings.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Getting Started" 3 | author: "Emily de la Rua and Gabriel Becker" 4 | date: "2022-10-26" 5 | output: 6 | rmarkdown::html_vignette 7 | vignette: > 8 | %\VignetteIndexEntry{Getting Started} 9 | %\VignetteEngine{knitr::rmarkdown} 10 | %\VignetteEncoding{UTF-8} 11 | editor_options: 12 | markdown: 13 | wrap: 72 14 | --- 15 | 16 | ```{r, include = FALSE} 17 | knitr::opts_chunk$set( 18 | collapse = TRUE, 19 | comment = "#>" 20 | ) 21 | ``` 22 | 23 | ## Introduction 24 | 25 | This vignette shows the general purpose and basic functionality of the `rlistings` R package. 26 | 27 | The `rlistings` R package contains value formatting and ASCII rendering infrastructure for tables and listings useful for clinical trials and other statistical analysis. 28 | The core functionality is built on top of the [`formatters` package](https://insightsengineering.github.io/formatters/latest-tag/). 29 | 30 | Some of the key features currently available to customize listings created using the `rlistings` package include: 31 | 32 | - Key columns 33 | - Titles and footnotes 34 | 35 | For information on listing column formatting see the [Column Formatting vignette](col_formatting.html). 36 | To learn about listing pagination see the [Pagination vignette](pagination.html). 37 | 38 | The index of all available `rlistings` functions can be found on the [rlistings website functions reference](https://insightsengineering.github.io/rlistings/main/reference/index.html). 39 | 40 | The `rlistings` package is intended for use in creating simple one-dimensional listings. For construction of more complex tables see the [`rtables` package](https://insightsengineering.github.io/rtables/). 41 | 42 | --------- 43 | 44 | ## Building a Listing 45 | 46 | With the basic framework provided in this package, a `data.frame` 47 | object can be easily converted into a listing using the `as_listing` 48 | function with several optional customizations available. 49 | 50 | A listing, at its core, is a set of observation-level data which is to 51 | be rendered with particular formatting but without any sort of 52 | aggregation or further analysis. In practice, this translates to to a 53 | classed `data.frame` (or `tbl_df`) object with a specialized print 54 | method. This means that, unlike tables created with `rlistings`' 55 | sibling package `rtables`, a listing object is fundamentally the 56 | incoming `data.frame` with a few annotations attached to it. 57 | 58 | 59 | In the R code below we will give a basic example of how to create an 60 | `rlistings` listing from a pre-processed data frame. 61 | 62 | We first load in the `rlistings` package. 63 | 64 | ```{r} 65 | library(rlistings) 66 | ``` 67 | 68 | For the purpose of this example we will use the dummy ADAE dataset 69 | provided within the `formatters` package as our data frame, which 70 | consists of 48 columns of adverse event patient data, and one or more 71 | rows per patient. 72 | 73 | ```{r} 74 | adae <- ex_adae 75 | ``` 76 | 77 | Now we will create our listing. 78 | 79 | The `df` parameter sets our `data.frame` object. The `disp_cols` 80 | argument takes a vector of names of any columns taken from the data 81 | frame that should be included in the listing. Column headers are set 82 | by the `label` attribute of each given variable. If there is no label 83 | associated with a given variable then the variable name will be taken 84 | as a header instead. For this example we will choose 8 arbitrary 85 | columns to display - 5 specific to the patient and 3 relating to the 86 | adverse event. 87 | 88 | Since the dataset consists of 1934 rows in total, we will use the 89 | `head` function to print only the first 15 rows of the listing. 90 | 91 | ```{r} 92 | lsting <- as_listing( 93 | df = adae, 94 | disp_cols = c("USUBJID", "AETOXGR", "ARM", "AGE", "SEX", "RACE", "AEDECOD", "AESEV"), 95 | ) 96 | 97 | head(lsting, 15) 98 | ``` 99 | 100 | In the listing output above you can see that there are several rows 101 | associated with each patient, resulting in many instances of repeated 102 | values over several columns. This can cleaned up by setting key 103 | columns with the `key_cols` argument. 104 | 105 | We can also declare the set of (non-key) display columns by compliment, 106 | via the `non_disp_col` argument. If specifies this argument accepts 107 | names of columns which will non be displayed. All other non-key columns 108 | are then displayed. 109 | 110 | 111 | ```{r} 112 | lsting <- as_listing( 113 | df = adae, 114 | non_disp_cols = tail(names(adae), 8) 115 | ) 116 | head(lsting, 15) 117 | ``` 118 | 119 | ## Key Columns 120 | 121 | Key columns act as contextual identifiers for observations. Their core 122 | behavioral feature is that sequentially repeated values are not 123 | displayed when they do not add information. 124 | 125 | In practice, this means that each value of a key column is printed 126 | only once per unique combination of values for all higher-priority 127 | (i.e., to the left of it) key columns (per page). Locations where a 128 | repeated value would have been printed within a key column for the 129 | same higher-priority-key combination on the same page are rendered as 130 | empty space. Note, determination of which elements to display within 131 | a key column at rendering is based on the underlying value; any 132 | non-default formatting applied to the column has no effect on this 133 | behavior. 134 | 135 | The `key_cols` argument takes a vector of column names identifying the 136 | key columns for the listing. A listing is always sorted by its key 137 | columns (with order defining the sort precedence). Below we specify 138 | trial arm and patient ID as key columns to improve readability. 139 | 140 | ```{r} 141 | lsting <- as_listing( 142 | df = adae, 143 | disp_cols = c("ARM", "AGE", "SEX", "RACE", "AEDECOD", "AESEV"), 144 | key_cols = c("USUBJID", "AETOXGR") 145 | ) 146 | 147 | head(lsting, 15) 148 | ``` 149 | 150 | ## Titles and Footers 151 | 152 | Additionally, an `rlistings` listing can be annotated with two types 153 | of header information (main title and subtitles) and two types of 154 | footer information (main footers and provenance footers). A single 155 | title can be set using the `main_title` argument, while one or more 156 | subtitles, main footers, and provenance footers can be set by the 157 | `subtitles`, `main_footer` and `prov_footer` arguments 158 | respectively. These are demonstrated in the following updated listing. 159 | 160 | ```{r} 161 | lsting <- as_listing( 162 | df = adae, 163 | disp_cols = c("ARM", "AGE", "SEX", "RACE", "AEDECOD", "AESEV"), 164 | key_cols = c("USUBJID", "AETOXGR"), 165 | main_title = "Main Title", 166 | subtitles = c("Subtitle A", "Subtitle B"), 167 | main_footer = c("Main Footer A", "Main Footer B", "Main Footer C"), 168 | prov_footer = c("Provenance Footer A", "Provenance Footer B") 169 | ) 170 | 171 | head(lsting, 15) 172 | ``` 173 | 174 | --------- 175 | 176 | ## Summary 177 | 178 | In this vignette you have learned how to implement the basic listing framework provided by the `rlistings` package to build a simple listing. You have also seen examples demonstrating how the optional parameters of the `as_listing` function can be set to customize and annotate your listings. 179 | 180 | **For more information please explore the [rlistings website](https://insightsengineering.github.io/rlistings/main/).** 181 | --------------------------------------------------------------------------------