├── .Rbuildignore ├── .Rinstignore ├── .covrignore ├── .github ├── .gitignore ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── feature_template.md │ ├── issue_template.md │ └── question_template.md ├── SUPPORT.md ├── pull_request_template.md └── workflows │ ├── check-standard.yaml │ ├── document.yaml │ ├── pkgdown.yaml │ ├── render-readme.yaml │ ├── repo-sync.yaml │ ├── rhub.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── Makefile ├── NAMESPACE ├── NEWS.md ├── R ├── 0-declare-global-variables.R ├── Col.Meta-doc.R ├── SeqId.R ├── SomaDataIO-package.R ├── adat-helpers.R ├── adat2eSet.R ├── addAttributes.R ├── addClass.R ├── calcOutlierMap.R ├── calc_eLOD.R ├── cleanNames.R ├── convertColMeta.R ├── data.R ├── diffAdats.R ├── dplyr-reexports.R ├── dplyr-verbs.R ├── genRowNames.R ├── getAnalyteInfo.R ├── getAnalytes.R ├── getMeta.R ├── getOutlierIds.R ├── getSomaScanLiftCCC.R ├── getTargetNames.R ├── groupGenerics.R ├── is-intact-attr.R ├── is-seqFormat.R ├── lift-adat.R ├── loadAdatsAsList.R ├── matchSeqIds.R ├── merge-clin.R ├── params.R ├── parseCheck.R ├── parseHeader.R ├── pivotExpressionSet.R ├── plotMap.R ├── preProcessAdat.R ├── prepHeaderMeta.R ├── read-adat.R ├── read-annotations.R ├── rownames.R ├── s3-merge-soma-adat.R ├── s3-print-soma-adat.R ├── s3-soma-adat.R ├── s3-summary-soma-adat.R ├── s3-transform.R ├── scaleAnalytes.R ├── syncColMeta.R ├── sysdata.rda ├── tidyr-reexports.R ├── tidyr-verbs.R ├── utils-lift.R ├── utils-pipe.R ├── utils-read-adat.R ├── utils-release.R ├── utils.R ├── write-adat.R ├── z-deprecated.R └── zzz.R ├── README.Rmd ├── README.md ├── SECURITY.md ├── SomaDataIO.Rproj ├── _pkgdown.yml ├── cran-comments.md ├── data-raw └── SomaScanObjects.R ├── data ├── data_objects.rda └── example_data.rda ├── inst ├── WORDLIST ├── check-pkg-versions.R ├── cli │ └── merge │ │ ├── merge_clin.R │ │ ├── meta.csv │ │ └── meta2.csv └── extdata │ └── example_data10.adat ├── man ├── Col.Meta.Rd ├── SeqId.Rd ├── SomaDataIO-deprecated.Rd ├── SomaDataIO-package.Rd ├── SomaScanObjects.Rd ├── adat-helpers.Rd ├── adat2eSet.Rd ├── addAttributes.Rd ├── addClass.Rd ├── calcOutlierMap.Rd ├── calc_eLOD.Rd ├── cleanNames.Rd ├── diffAdats.Rd ├── figures │ ├── lifecycle-deprecated.svg │ ├── lifecycle-soft-deprecated.svg │ ├── lifecycle-stable.svg │ ├── lifecycle-superseded.svg │ └── logo.png ├── getAnalyteInfo.Rd ├── getAnalytes.Rd ├── getOutlierIds.Rd ├── groupGenerics.Rd ├── is_intact_attr.Rd ├── is_seqFormat.Rd ├── lift_adat.Rd ├── loadAdatsAsList.Rd ├── merge_clin.Rd ├── params.Rd ├── parseHeader.Rd ├── pipe.Rd ├── pivotExpressionSet.Rd ├── plot.Map.Rd ├── preProcessAdat.Rd ├── read_adat.Rd ├── read_annotations.Rd ├── reexports.Rd ├── rownames.Rd ├── soma_adat.Rd ├── transform.Rd └── write_adat.Rd ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── tests ├── spelling.R ├── testthat.R └── testthat │ ├── _snaps │ ├── S3-median.md │ ├── S3-merge.md │ ├── S3-print.md │ ├── S3-summary.md │ ├── diffAdats.md │ ├── getTargetNames.md │ ├── groupGenerics.md │ ├── is-intact-attr.md │ ├── loadAdatsAsList.md │ ├── parseCheck.md │ ├── parseHeader.md │ ├── preProcessAdat.md │ ├── preProcessAdat │ │ ├── preProcessAdat_qc_plot_Age.png │ │ └── preProcessAdat_qc_plot_Sex.png │ ├── read-adat.md │ ├── scaleAnalytes.md │ ├── utils-read-adat.md │ └── utils-release.md │ ├── helper.R │ ├── test-S3-extract.R │ ├── test-S3-median.R │ ├── test-S3-merge.R │ ├── test-S3-print.R │ ├── test-S3-summary.R │ ├── test-S3-transform.R │ ├── test-SeqId.R │ ├── test-adat2eSet.R │ ├── test-addAttributes.R │ ├── test-addClass.R │ ├── test-calc_eLOD.R │ ├── test-checkADAT.R │ ├── test-diffAdats.R │ ├── test-dplyr-verbs.R │ ├── test-genRownames.R │ ├── test-getAdatVersion.R │ ├── test-getAnalyteInfo.R │ ├── test-getAnalytes.R │ ├── test-getMeta.R │ ├── test-getOutlierIds.R │ ├── test-getSeqIdMatches.R │ ├── test-getSomaScanLiftCCC.R │ ├── test-getSomaScanVersion.R │ ├── test-getTargetNames.R │ ├── test-groupGenerics.R │ ├── test-is-apt.R │ ├── test-is-intact-attr.R │ ├── test-is-seqFormat.R │ ├── test-lift-adat.R │ ├── test-loadAdatsAsList.R │ ├── test-locateSeqId.R │ ├── test-matchSeqIds.R │ ├── test-merge-clin.R │ ├── test-objects.R │ ├── test-parseCheck.R │ ├── test-parseHeader.R │ ├── test-pivotExpressionSet.R │ ├── test-preProcessAdat.R │ ├── test-prepHeaderMeta.R │ ├── test-read-adat.R │ ├── test-read-annotations.R │ ├── test-rownames.R │ ├── test-scaleAnalytes.R │ ├── test-syncColMeta.R │ ├── test-tidyr-verbs.R │ ├── test-utils-read-adat.R │ ├── test-utils-release.R │ ├── test-with-interactive.R │ ├── test-write-adat.R │ └── testdata │ ├── empty.adat │ ├── single_sample.adat │ └── test-anno.xlsx └── vignettes ├── .gitignore ├── SomaDataIO.Rmd └── articles ├── cli-merge-tool.Rmd ├── lifting-and-bridging.Rmd ├── pre-processing.Rmd ├── stat-binary-classification.Rmd ├── stat-linear-regression.Rmd ├── stat-three-group-analysis-anova.Rmd ├── stat-two-group-comparison.Rmd ├── tips-loading-and-wrangling.Rmd ├── tips-safely-map-values.Rmd ├── tips-safely-rename-df.Rmd └── tips-train-test-setup.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^data-raw$ 4 | ^revdep$ 5 | ^notes$ 6 | ^pkgdown$ 7 | ^README\.Rmd$ 8 | ^LICENSE\.md$ 9 | ^SECURITY\.md$ 10 | ^_pkgdown\.yml$ 11 | ^\.covrignore$ 12 | ^\.github$ 13 | ^Makefile$ 14 | ^cran-comments\.md$ 15 | ^sync-somareadr\.sh$ 16 | ^retro-update-pkgdown\.sh$ 17 | ^CRAN-SUBMISSION$ 18 | ^vignettes/articles$ 19 | -------------------------------------------------------------------------------- /.Rinstignore: -------------------------------------------------------------------------------- 1 | WORDLIST 2 | check-pkg-versions.R 3 | -------------------------------------------------------------------------------- /.covrignore: -------------------------------------------------------------------------------- 1 | R/z-deprecated.R 2 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to SomaDataIO 2 | 3 | We currently do not accept individual contributions 4 | to `SomaDataIO` via the traditional PR fork-and-clone pathway. 5 | Please submit an issue under one of our prescripted 6 | [issue-types](https://github.com/SomaLogic/SomaDataIO/issues/new/choose). 7 | 8 | 9 | ## Support 10 | For information on how to obtain support for `SomaDataIO`, please see 11 | [SUPPORT.md](https://github.com/SomaLogic/SomaDataIO/blob/HEAD/.github/SUPPORT.md). 12 | 13 | 14 | ## as Always, a Reprex 15 | Once again, if you’ve found a bug, please file an issue 16 | using the link above that illustrates the :bug: with a minimal 17 | [**repr**oducible **ex**ample](http://reprex.tidyverse.org/). 18 | For more information about `reprex`, 19 | please start [here](https://www.tidyverse.org/help/#reprex). 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ✨ Feature request 3 | about: Make a case for a new feature in SomaDataIO 4 | labels: feature 5 | --- 6 | 7 | ## Checks 8 | - [ ] Please search previous issues before creating a new one, 9 | to ensure yours is not a duplicate. You can 10 | [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/), 11 | `is:pr`, `is:closed`, etc., as needed, e.g. you'd simply remove `is:open` to 12 | search _all_ issues in the repo, open or closed. 13 | 14 | - [ ] The proposed feature fits in the scope of SomaScan 15 | data I/O or low level data `soma_adat` manipulation 16 | 17 | 18 | When you are ready to file the feature :sparkles: request, 19 | please delete everything above this line:\ 20 | <-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> 21 | 22 | 23 | ## Feature 24 | 1. Please briefly describe the proposed new feature. 25 | 1. Please highlight how it solves the problem. 26 | 27 | 28 | ## R code 29 | ```r 30 | # insert reprex here ... 31 | ``` 32 | 33 | --- 34 | 35 | Thanks for contributing :partying_face:! 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug 3 | about: Describe the bug you've encountered 4 | labels: bug 5 | --- 6 | 7 | ## Checks 8 | - [ ] Please search previous issues before creating a new one, 9 | to ensure yours is not a duplicate. You can 10 | [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/), 11 | `is:pr`, `is:closed`, etc., as needed, e.g. you'd simply remove `is:open` to 12 | search _all_ issues in the repo, open or closed. 13 | 14 | - [ ] Please include a minimal REProducible EXample (AKA a `reprex`) 15 | 16 | 17 | ## Reprex 18 | If you've never heard of a [reprex](http://reprex.tidyverse.org/), 19 | please start by reading 20 | [**repr**oducible **ex**ample](https://www.tidyverse.org/help/#reprex). 21 | You might need to install these packages: 22 | 23 | ```r 24 | install.packages(c("reprex", "sessioninfo"), repos = "http://cran.r-project.org") 25 | ``` 26 | 27 | 28 | When you are ready to file the bug :bug: report, 29 | please delete everything above this line:\ 30 | <-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> 31 | 32 | 33 | ## The Problem 34 | Please briefly describe the problem you have encountered ... 35 | 36 | If you have a question, please don't use this forum, 37 | please open a "Question" issue 38 | [here](https://github.com/SomaLogic/SomaDataIO/issues/new/choose) 39 | 40 | 41 | ## Output 42 | ```r 43 | # insert reprex here ... 44 | ``` 45 | 46 | ## Steps 47 | Please describe any steps you have already taken 48 | to diagnose the issue. 49 | 50 | - bullet 1 51 | - bullet 2 52 | 53 | --- 54 | 55 | Thanks for reporting :partying_face:! 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ❓ Question 3 | about: Ask a question about SomaDataIO 4 | labels: question 5 | --- 6 | 7 | - You are using `SomaDataIO` :partying_face: ... but have hit a road block :weary: 8 | - Please search previous issues before creating a new one, 9 | to ensure yours is not a duplicate. You can 10 | [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/), 11 | `is:pr`, `is:closed`, etc., as needed, e.g. you'd simply remove `is:open` to 12 | search _all_ issues in the repo, open or closed. 13 | - For additonal support please please see 14 | [SUPPORT.md](https://github.com/SomaLogic/SomaDataIO/blob/HEAD/.github/SUPPORT.md). 15 | 16 | 17 | When you are ready to file your :question: question, 18 | please delete everything above this line:\ 19 | <-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> 20 | 21 | 22 | ## The Problem 23 | Please briefly describe the problem you have encountered ... 24 | 25 | - bullet 1 26 | - bullet 2 27 | 28 | 29 | ## R code 30 | Please describe your question with R code (if possible): 31 | ```r 32 | # insert code here ... 33 | ``` 34 | 35 | --- 36 | 37 | Thank you for using `SomaDataIO` :pray:! 38 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting help with SomaDataIO 2 | 3 | Thanks for using SomaDataIO! 4 | 5 | Before filing an issue, there are a few places to explore 6 | and pieces to put together to make the process as smooth as possible. 7 | 8 | ## Make a reprex 9 | 10 | If we can’t reproduce the bug, we can’t fix it! 11 | Start by making a minimal **repr**oducible **ex**ample using the [reprex](https://reprex.tidyverse.org/) package. 12 | If you haven't heard of or used `reprex` before, you're in for a treat! 13 | Seriously, `reprex` will make all of your R-question-asking endeavors easier 14 | (which is an excellent ROI for the five to ten minutes it'll take you to learn what it's all about). 15 | For additional `reprex` pointers, check out the [Get help!](https://www.tidyverse.org/help/). 16 | 17 | ## Where to ask? 18 | 19 | Armed with your `reprex`, the next step is to figure out [where to ask](https://www.tidyverse.org/help/#where-to-ask). 20 | 21 | * If it's a `R` related question: start with [community.rstudio.com](https://community.rstudio.com/) 22 | and/or [StackOverflow](https://stackoverflow.com). There are more people there to answer questions. 23 | 24 | * If it's a bug with [SomaDataIO](https://github.com/SomaLogic/SomaDataIO/): you're 25 | in the right place; please [file an issue](https://github.com/SomaLogic/SomaDataIO/issues/new). 26 | 27 | * If you're not sure: let the community help you figure it out! 28 | If your problem _is_ a bug or a feature request, you can easily return 29 | [here](https://github.com/SomaLogic/SomaDataIO/issues/) and report it. 30 | 31 | * For _non_-coding related feedback or inquiries please reach out to . 32 | 33 | Before opening a new issue, be sure to 34 | [search issues and pull requests](https://github.com/SomaLogic/SomaDataIO/issues/) 35 | to make sure the bug hasn't been reported and/or already fixed in the development version. 36 | By default, the search will be pre-populated with `is:issue is:open`. 37 | You can [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/), 38 | `is:pr`, `is:closed`, etc., as needed, e.g. you'd simply remove `is:open` to 39 | search _all_ issues in the repo, open or closed. 40 | 41 | ## What happens next? 42 | 43 | We aim to respond to questions or simple bugs within 24 - 48h, particularly 44 | questions accompanied by a `reprex` can typically be addressed quickly. 45 | More complex bugs or feature requests can take longer, as they require a 46 | deeper discussion regarding the software development lifecycle of `SomaDataIO`. 47 | As mentioned above, you may always reach out to our Global Scientific Engagement 48 | team at or for more general inquiries at . 49 | 50 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Overview of Pull Request 2 | 3 | - General description of this pull request ... 4 | 5 | ## Main Changes 6 | - bullet 1 7 | - bullet 2 8 | 9 | Fixes # 10 | 11 | 12 | --- 13 | 14 | 15 | ## Internal Use Only 16 | ### Change Type 17 | 18 | Please check the relevant box(es): 19 | 20 | - [ ] New feature (no API change; @stufield) 21 | - [ ] Bug fix (no API change -> fixes an issue; @stufield) 22 | - [ ] Breaking change (fix or feature with API change; @stufield) 23 | - [ ] Documentation update (@SLbmangum, @kmurugesan14) 24 | - [ ] Package maintenance (structural, non-code, non-breaking changes; @stufield) 25 | - [ ] README change (@SLbmangum, @kmurugesan14, @nmcnabbSL) 26 | - [ ] LICENSE changes (@SLbmangum) 27 | 28 | 29 | ### Reviewer by Department 30 | 31 | | Department | Reviewer | Change Type | 32 | |:-------------- |:------------- | -------------------- | 33 | | Bioinformatics | @stufield | Code, bugs, features | 34 | | Info. Sec. | @trhein | LICENSE | 35 | | Legal | @SLbmangum | LICENSE | 36 | | Product | @kmurugesan14 | Documentation | 37 | | Regulatory | @nmcnabbSL | Documentation | 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/check-standard.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | paths: 9 | - 'DESCRIPTION' 10 | - 'NAMESPACE' 11 | - 'R/**' 12 | - 'tests/testthat/**' 13 | pull_request: 14 | branches: 15 | - main 16 | - master 17 | paths: 18 | - '.Rbuildignore' 19 | - '.Rinstignore' 20 | - 'DESCRIPTION' 21 | - 'NAMESPACE' 22 | - 'R/**' 23 | - 'tests/testthat/**' 24 | - 'vignettes/**' 25 | workflow_dispatch: 26 | 27 | name: R-CMD-check 28 | 29 | jobs: 30 | R-CMD-check: 31 | runs-on: ${{ matrix.config.os }} 32 | 33 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 34 | 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | config: 39 | - {os: macos-latest, r: 'release'} 40 | - {os: windows-latest, r: 'release'} 41 | #- {os: ubuntu-latest, r: 'release'} # was taking too long to build 42 | 43 | env: 44 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 45 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 46 | R_KEEP_PKG_SOURCE: yes 47 | TZ: 'America/Denver' 48 | 49 | steps: 50 | - uses: actions/checkout@v3 51 | 52 | - uses: r-lib/actions/setup-pandoc@v2 53 | 54 | - uses: r-lib/actions/setup-r@v2 55 | with: 56 | r-version: ${{ matrix.config.r }} 57 | 58 | - uses: r-lib/actions/setup-r-dependencies@v2 59 | with: 60 | dependencies: '"all"' 61 | cache: false 62 | extra-packages: | 63 | any::rcmdcheck 64 | any::testthat 65 | any::knitr 66 | any::remotes 67 | any::rmarkdown 68 | needs: check 69 | 70 | - uses: r-lib/actions/check-r-package@v2 71 | with: 72 | args: 'c("--no-manual", "--as-cran", "--no-build-vignettes", "--timings")' 73 | build_args: 'c("--no-manual", "--no-resave-data")' 74 | upload-snapshots: false 75 | upload-results: false 76 | error-on: '"warning"' 77 | check-dir: '"check"' 78 | -------------------------------------------------------------------------------- /.github/workflows/document.yaml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | 3 | name: Document 4 | 5 | jobs: 6 | document: 7 | runs-on: ubuntu-latest 8 | 9 | env: 10 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 11 | BRANCH_NAME: ${{ github.head_ref || github.ref_name }} 12 | 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | clean: true 19 | 20 | - name: Setup R 21 | uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - name: Install dependencies 26 | uses: r-lib/actions/setup-r-dependencies@v2 27 | with: 28 | extra-packages: | 29 | any::roxygen2 30 | needs: roxygen2 31 | 32 | - name: Document 33 | run: roxygen2::roxygenise() 34 | shell: Rscript {0} 35 | 36 | - name: Commit and push changes 37 | run: | 38 | git config --local user.name "$GITHUB_ACTOR" 39 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 40 | git add man/\* NAMESPACE DESCRIPTION 41 | git commit -m "Update documentation" || echo "No changes to commit" 42 | git pull --ff-only 43 | git push origin 44 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | - master 6 | paths: 7 | - 'DESCRIPTION' 8 | - '_pkgdown.yml' 9 | - 'NEWS.md' 10 | - 'README.md' 11 | - 'vignettes/**' 12 | workflow_dispatch: 13 | 14 | name: pkgdown 15 | 16 | jobs: 17 | pkgdown: 18 | runs-on: macOS-14 # macOS important Bootstrap/bslib themes 19 | 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | permissions: 24 | contents: write 25 | 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: | 38 | any::pkgdown 39 | local::. 40 | needs: website 41 | 42 | - name: Install package 43 | run: | 44 | R CMD INSTALL --use-vanilla . 45 | 46 | - name: Deploy to GitHub pages 🚀 47 | run: | 48 | git config --local user.name "$GITHUB_ACTOR" 49 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 50 | Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 51 | -------------------------------------------------------------------------------- /.github/workflows/render-readme.yaml: -------------------------------------------------------------------------------- 1 | on: workflow_dispatch 2 | 3 | name: Render README 4 | 5 | jobs: 6 | render: 7 | name: Render README 8 | runs-on: macOS-12 9 | 10 | env: 11 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 12 | 13 | steps: 14 | - name: Checkout repo 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - uses: r-lib/actions/setup-pandoc@v2 20 | 21 | - uses: r-lib/actions/setup-r@v2 22 | 23 | - name: Install rmarkdown, remotes, and the local package 24 | run: | 25 | install.packages('remotes') 26 | remotes::install_cran('rmarkdown') 27 | remotes::install_local('.') 28 | shell: Rscript {0} 29 | 30 | - name: Render README 31 | run: rmarkdown::render("README.Rmd") 32 | shell: Rscript {0} 33 | 34 | - name: Commit results 35 | run: | 36 | git config --local user.name "$GITHUB_ACTOR" 37 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 38 | git commit README.md -m 'Re-build README.Rmd' || echo "No changes to commit" 39 | git push origin || echo "No changes to commit" 40 | -------------------------------------------------------------------------------- /.github/workflows/repo-sync.yaml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to sync the public repository -> SomaDataIO-internal 2 | # This GHA is seldom used but it should be run occasionally 3 | # to ensure the internal repo is up to date with the public side 4 | # This GHA must be manually triggered from the GUI inside 5 | # git@github.com:SomaLogic/SomaDataIO-internal.git repository 6 | # Author: Stu Field 7 | 8 | on: workflow_dispatch 9 | 10 | name: Public -> Internal 11 | 12 | jobs: 13 | repo-sync: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: repo-sync 22 | env: 23 | ssh_private_key: ${{ secrets.SSH_PRIVATE_SOMADATAIO_KEY }} 24 | run: | 25 | date > generated.txt 26 | mkdir ~/.ssh 27 | echo "$ssh_private_key" > ~/.ssh/id_rsa 28 | chmod 600 ~/.ssh/id_rsa 29 | git \ 30 | -c user.name="$GITHUB_ACTOR" \ 31 | -c user.email="$GITHUB_ACTOR"@users.noreply.github.com \ 32 | pull git@github.com:SomaLogic/SomaDataIO.git main 33 | git push origin main 34 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'R/**' 9 | - 'tests/testthat/**' 10 | - '.covrignore' 11 | - '.github/workflows/test-coverage.yaml' 12 | pull_request: 13 | branches: 14 | - main 15 | paths: 16 | - 'R/**' 17 | - 'tests/testthat/**' 18 | - '.covrignore' 19 | - '.github/workflows/test-coverage.yaml' 20 | workflow_dispatch: 21 | 22 | name: test-coverage 23 | 24 | jobs: 25 | test-coverage: 26 | runs-on: ubuntu-latest 27 | 28 | env: 29 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | 34 | - uses: r-lib/actions/setup-r@v2 35 | with: 36 | use-public-rspm: true 37 | 38 | - uses: r-lib/actions/setup-r-dependencies@v2 39 | with: 40 | extra-packages: any::covr 41 | needs: coverage 42 | 43 | - name: Test coverage & upload Codecov 🚀 44 | run: | 45 | covr::codecov( 46 | quiet = FALSE, 47 | clean = FALSE, 48 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 49 | ) 50 | shell: Rscript {0} 51 | 52 | #- name: Upload results to Codecov 🚀 53 | # uses: codecov/codecov-action@v3.1.2 54 | 55 | - name: Show 'testthat' output 56 | if: always() 57 | run: | 58 | ## -------------------------------------------------------------------- 59 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 60 | shell: bash 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore all RStudio project related files 2 | \.DS_Store 3 | .Rproj.user 4 | ^r\\.Rproj$ 5 | \.Rhistory 6 | \.RData 7 | sync-somareadr\.sh 8 | inst/doc 9 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: SomaDataIO 3 | Title: Input/Output 'SomaScan' Data 4 | Version: 6.3.0.9000 5 | Authors@R: c( 6 | person(given = "Stu", 7 | family = "Field", 8 | role = "aut", 9 | email = "stu.g.field@gmail.com", 10 | comment = c(ORCID = "0000-0002-1024-5859")), 11 | person(given = "Caleb", 12 | family = "Scheidel", 13 | role = "cre", 14 | email = "calebjscheidel@gmail.com"), 15 | person(given = "Standard BioTools, Inc.", role = c("cph", "fnd")) 16 | ) 17 | Description: Load and export 'SomaScan' data via the 18 | 'Standard BioTools, Inc.' structured text file 19 | called an ADAT ('*.adat'). For file format see 20 | . 21 | The package also exports auxiliary functions for 22 | manipulating, wrangling, and extracting relevant 23 | information from an ADAT object once in memory. 24 | License: MIT + file LICENSE 25 | URL: https://somalogic.github.io/SomaDataIO/, https://somalogic.com 26 | BugReports: https://github.com/SomaLogic/SomaDataIO/issues 27 | Depends: 28 | R (>= 4.1.0) 29 | Imports: 30 | cli, 31 | dplyr (>= 1.0.6), 32 | ggplot2, 33 | lifecycle (>= 1.0.0), 34 | magrittr (>= 2.0.1), 35 | methods, 36 | readxl (>= 1.3.1), 37 | tibble (>= 3.1.2), 38 | tidyr (>= 1.1.3) 39 | Suggests: 40 | Biobase, 41 | knitr, 42 | purrr, 43 | recipes, 44 | rlang, 45 | rmarkdown, 46 | spelling, 47 | testthat (>= 3.0.0), 48 | usethis (>= 2.0.1), 49 | withr 50 | VignetteBuilder: 51 | knitr 52 | Copyright: Standard BioTools, Inc. 2025 53 | Encoding: UTF-8 54 | Language: en-US 55 | LazyData: true 56 | LazyDataCompression: xz 57 | LazyLoad: true 58 | Config/testthat/edition: 3 59 | Config/Needs/website: tidyverse/tidytemplate 60 | Roxygen: list(markdown = TRUE) 61 | RoxygenNote: 7.3.2 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2025 2 | COPYRIGHT HOLDER: Standard BioTools, Inc. 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright © 2025 Standard BioTools, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # h/t to @jimhester and @yihui for this parse block: 2 | # https://github.com/yihui/knitr/blob/dc5ead7bcfc0ebd2789fe99c527c7d91afb3de4a/Makefile#L1-L4 3 | # Note the portability change as suggested in the manual: 4 | # https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Writing-portable-packages 5 | PKGNAME := $(shell sed -n "s/Package: *\([^ ]*\)/\1/p" DESCRIPTION) 6 | PKGVERS := $(shell sed -n "s/Version: *\([^ ]*\)/\1/p" DESCRIPTION) 7 | PKGSRC := $(shell basename `pwd`) 8 | RM = rm -rf 9 | MV = mv -f 10 | RCMD = R --vanilla CMD 11 | RSCRIPT = Rscript --vanilla 12 | 13 | 14 | all: check clean 15 | update: objects 16 | roxygen: docs 17 | 18 | docs: 19 | @ $(RSCRIPT) -e "roxygen2::roxygenise(roclets = c('collate', 'namespace', 'rd'))" 20 | 21 | readme: 22 | @ echo "Rendering README.Rmd" 23 | @ $(RSCRIPT) \ 24 | -e "Sys.setenv(RSTUDIO_PANDOC='/Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools')" \ 25 | -e "options(cli.width = 80L)" \ 26 | -e "rmarkdown::render('README.Rmd', quiet = TRUE)" 27 | @ $(RM) README.html 28 | 29 | test: 30 | @ $(RSCRIPT) \ 31 | -e "Sys.setenv(TZ = 'America/Denver')" \ 32 | -e "path <- Sys.getenv('R_LIBS_DEV')" \ 33 | -e "path <- normalizePath(path, winslash = '/', mustWork = TRUE)" \ 34 | -e ".libPaths(c(path, .libPaths()))" \ 35 | -e "rm(path)" \ 36 | -e "message('Dev mode: ON')" \ 37 | -e "devtools::test(reporter = 'summary', stop_on_failure = TRUE)" 38 | 39 | test_file: 40 | @ Rscript \ 41 | -e "Sys.setenv(TZ = 'America/Denver', NOT_CRAN = 'true')" \ 42 | -e "path <- Sys.getenv('R_LIBS_DEV')" \ 43 | -e "path <- normalizePath(path, winslash = '/', mustWork = TRUE)" \ 44 | -e ".libPaths(c(path, .libPaths()))" \ 45 | -e "rm(path)" \ 46 | -e "message('Dev mode: ON')" \ 47 | -e "devtools::load_all()" \ 48 | -e "testthat::test_file('$(FILE)', reporter = 'summary', stop_on_failure = TRUE)" 49 | 50 | accept_snapshots: 51 | @ Rscript -e "testthat::snapshot_accept()" 52 | 53 | build: docs 54 | @ cd ..;\ 55 | $(RCMD) build --resave-data $(PKGSRC) 56 | 57 | check: build 58 | @ cd ..;\ 59 | $(RCMD) check --no-manual $(PKGNAME)_$(PKGVERS).tar.gz 60 | 61 | # create SomaDataIO package objects and re-save as 'data/*.rda' 62 | # requires an internet connection to pull from the SomaLogic-Data repository 63 | objects: 64 | @ echo "Creating package objects ..." 65 | @ wget https://raw.githubusercontent.com/SomaLogic/SomaLogic-Data/main/example_data.adat 66 | @ $(RSCRIPT) data-raw/SomaScanObjects.R 67 | @ echo "Saving objects to 'data/*.rda' ..." 68 | @ $(RM) example_data.adat 69 | 70 | check_versions: 71 | @ $(RSCRIPT) inst/check-pkg-versions.R 72 | 73 | install: 74 | @ R CMD INSTALL --use-vanilla --preclean --resave-data . 75 | 76 | clean: 77 | @ cd ..;\ 78 | $(RM) $(PKGNAME)_$(PKGVERS).tar.gz $(PKGNAME).Rcheck 79 | -------------------------------------------------------------------------------- /R/0-declare-global-variables.R: -------------------------------------------------------------------------------- 1 | # --------------------------- # 2 | # Declaring Global Variables: 3 | # This is mostly for passing R CMD checks 4 | # global variables that come from other dependant 5 | # packages, or objects in the 'data/' directory 6 | # Reference: https://github.com/tidyverse/magrittr/issues/29 7 | # ---------------------------------------------------------- # 8 | if ( getRversion() >= "2.15.1" ) 9 | utils::globalVariables( 10 | c(".", 11 | "AptName", 12 | "array_id", 13 | "blank_col", 14 | "ColCheck", 15 | "Dilution", 16 | "eLOD", 17 | "feature", 18 | "Group", 19 | "GroupCount", 20 | "Normalization Scale Factor", 21 | "Organism", 22 | "prefix", 23 | "Response", 24 | "RowCheck", 25 | "rn", 26 | "SampleId", 27 | "SampleType", 28 | "SeqId", 29 | "seqid", 30 | "SubjectCount", 31 | "SubjectId", 32 | "TargetFullName", 33 | "Type", 34 | "value", 35 | "Value" 36 | ) 37 | ) 38 | -------------------------------------------------------------------------------- /R/SomaDataIO-package.R: -------------------------------------------------------------------------------- 1 | 2 | #' @importFrom utils head tail 3 | #' @keywords internal package 4 | "_PACKAGE" 5 | 6 | 7 | #' @name SomaDataIO-package 8 | #' 9 | #' @details Load an ADAT file into the global workspace with a call 10 | #' to [read_adat()]. This function parses the main data 11 | #' table into a `data.frame` object and assigns the remaining data from 12 | #' the file as object `attributes`, i.e. call `attributes(adat)`. 13 | #' Other functions in the package are designed to make extracting, 14 | #' manipulating, and wrangling data in the newly created [SomaDataIO::soma_adat] 15 | #' object more convenient. 16 | #' 17 | #' Those familiar with micro-array data analysis and associated packages, e.g. 18 | #' \pkg{Biobase}, will notice that the feature data (proteins) are arranged as 19 | #' columns and the samples (arrays) are the rows. This is the 20 | #' transpose of typical micro-array data. This conflict can be easily solved 21 | #' using the transpose function, [t()], which is part of the `base R`. 22 | #' In addition, those familiar with the standard `ExpressionSet` object, 23 | #' available from `Bioconductor`, might find the functions [adat2eSet()] and 24 | #' [pivotExpressionSet()] particularly useful. 25 | #' 26 | #' @examples 27 | #' # a listing of all pkg functions 28 | #' library(help = SomaDataIO) 29 | #' 30 | #' # the `soma_adat` class 31 | #' class(example_data) 32 | #' is.soma_adat(example_data) 33 | #' 34 | #' # Annotations Lookup Table 35 | #' anno_tbl <- getAnalyteInfo(example_data) 36 | #' anno_tbl 37 | #' 38 | #' # Find all analytes starting with "MMP" in `anno_tbl` 39 | #' dplyr::filter(anno_tbl, grepl("^MMP", Target)) 40 | NULL 41 | -------------------------------------------------------------------------------- /R/addAttributes.R: -------------------------------------------------------------------------------- 1 | #' Add Attributes to `soma_adat` Objects 2 | #' 3 | #' Adds a set of attributes, typically "Header.Meta" and "Col.Meta", 4 | #' to a `data.frame`, `tibble`, `soma_adat` or similar tabular object. 5 | #' Existing attributes `data` are _not_ over-written. 6 | #' Typically untouched are: 7 | #' \itemize{ 8 | #' \item `names` 9 | #' \item `class` 10 | #' \item `row.names` 11 | #' } 12 | #' 13 | #' @param data The _receiving_ `data.frame` object for new attributes. 14 | #' @param new.atts A _named_ `list` object containing new attributes 15 | #' to add to the existing ones. 16 | #' @return A data frame object corresponding to `data` but with the 17 | #' attributes of `new.atts` grafted on to it. 18 | #' Existing attribute names are _not_ over-written. 19 | #' @author Stu Field 20 | #' @seealso [attr()], [setdiff()] 21 | #' @export 22 | addAttributes <- function(data, new.atts) { 23 | stopifnot( 24 | "`data` must be a data frame, tibble, or similar." = inherits(data, "data.frame"), 25 | "`new.atts` must be a *named* list." = inherits(new.atts, "list"), 26 | "`new.atts` must be a *named* list." = !is.null(names(new.atts)) 27 | ) 28 | attrs <- setdiff(names(new.atts), names(attributes(data))) 29 | if ( length(attrs) > 0L ) { 30 | for ( i in attrs ) { 31 | attr(data, i) <- new.atts[[i]] 32 | } 33 | } 34 | data 35 | } 36 | -------------------------------------------------------------------------------- /R/addClass.R: -------------------------------------------------------------------------------- 1 | #' Add a Class to an Object 2 | #' 3 | #' Utility to add (prepend) a class(es) to existing objects. 4 | #' 5 | #' @param x The object to receive new class(es). 6 | #' @param class Character. The name of additional class(es). 7 | #' @return An object with new classes. 8 | #' @author Stu Field 9 | #' @seealso [class()], [typeof()], [structure()] 10 | #' @examples 11 | #' class(iris) 12 | #' 13 | #' addClass(iris, "new") |> class() 14 | #' 15 | #' addClass(iris, c("A", "B")) |> class() # 2 classes 16 | #' 17 | #' addClass(iris, c("A", "data.frame")) |> class() # no duplicates 18 | #' 19 | #' addClass(iris, c("data.frame", "A")) |> class() # re-orders if exists 20 | #' @export 21 | addClass <- function(x, class) { 22 | if ( is.null(class) ) { 23 | warning("Passing `class = NULL` leaves class(x) unchanged.", call. = FALSE) 24 | } 25 | if ( any(is.na(class)) ) { 26 | stop("The `class` param cannot contain `NA`: ", .value(class), call. = FALSE) 27 | } 28 | new_class <- union(class, class(x)) 29 | structure(x, class = new_class) 30 | } 31 | -------------------------------------------------------------------------------- /R/cleanNames.R: -------------------------------------------------------------------------------- 1 | #' Clean Up Character String 2 | #' 3 | #' Often the names, particularly within `soma_adat` objects, 4 | #' are messy due to varying inputs, this function attempts to remedy this by 5 | #' removing the following: 6 | #' \itemize{ 7 | #' \item trailing/leading/internal whitespace 8 | #' \item non-alphanumeric strings (except underscores) 9 | #' \item duplicated internal dots (`..`), (`...`), etc. 10 | #' \item SomaScan normalization scale factor format 11 | #' } 12 | #' 13 | #' @param x Character. String to clean up. 14 | #' @return A cleaned up character string. 15 | #' @seealso [trimws()], [gsub()], [sub()] 16 | #' @author Stu Field 17 | #' @examples 18 | #' cleanNames(" sdkfj...sdlkfj.sdfii4994### ") 19 | #' 20 | #' cleanNames("Hyb..Scale") 21 | #' @export 22 | cleanNames <- function(x) { 23 | y <- squish(x) # zap leading/trailing/internal whitespace 24 | y <- gsub("[^A-Za-z0-9_]", ".", y) # zap non-alphanum (keep '_') 25 | y <- gsub("\\.+", ".", y) # zap multiple dots 26 | y <- gsub("^\\.|\\.$", "", y) # zap leading/trailing dots 27 | y <- sub("^Hyb[.]Scale", "HybControlNormScale", y) 28 | sub("^Med[.]Scale", "NormScale", y) 29 | } 30 | 31 | squish <- function(x) { 32 | # zap leading/trailing whitespace & extra internal whitespace 33 | gsub("[[:space:]]+", " ", trimws(x)) 34 | } 35 | -------------------------------------------------------------------------------- /R/convertColMeta.R: -------------------------------------------------------------------------------- 1 | #' Create Annotations Table 2 | #' 3 | #' Uses the column meta data (Col.Meta; feature data that appears above 4 | #' the protein measurements in the adat file) 5 | #' and compiles them into a grouped tibble lookup table 6 | #' for simple manipulation and indexing. 7 | #' 8 | #' @param x The "Col.Meta" element from an adat attributes. 9 | #' @return A `tibble` object with columns corresponding 10 | #' to the column meta data entries in the adat. 11 | #' @author Stu Field 12 | #' @importFrom tibble as_tibble 13 | #' @noRd 14 | convertColMeta <- function(x) { 15 | # conversion fails if un-equal length columns 16 | tbl <- setNames(as_tibble(x), cleanNames(names(x))) 17 | 18 | if ( !is.null(tbl$Dilution) ) { 19 | tbl$Dilution2 <- as.numeric(gsub("%$|Mix ", "", tbl$Dilution)) / 100 20 | } 21 | 22 | convert_lgl <- function(.x) { 23 | w <- tryCatch(as.numeric(.x), warning = function(w) w) 24 | is_warn <- inherits(w, "simpleWarning") 25 | # NA warning tripped? 26 | na_warn <- is_warn && identical(w$message, "NAs introduced by coercion") 27 | num_ok <- !na_warn 28 | num_ok && !inherits(.x, c("factor", "integer")) # don't touch factors/integers 29 | } 30 | idx <- which(vapply(tbl, convert_lgl, NA)) 31 | for ( i in idx ) tbl[[i]] <- as.numeric(tbl[[i]]) 32 | tbl$Dilution <- as.character(tbl$Dilution) # keep character 33 | tbl$SeqId <- getSeqId(tbl$SeqId, TRUE) # rm versions; safety 34 | tbl 35 | } 36 | -------------------------------------------------------------------------------- /R/dplyr-reexports.R: -------------------------------------------------------------------------------- 1 | 2 | #' @importFrom dplyr count 3 | #' @export 4 | dplyr::count 5 | 6 | #' @importFrom dplyr rename 7 | #' @export 8 | dplyr::rename 9 | 10 | #' @importFrom dplyr slice_sample 11 | #' @export 12 | dplyr::slice_sample 13 | 14 | #' @importFrom dplyr slice 15 | #' @export 16 | dplyr::slice 17 | 18 | #' @importFrom dplyr sample_frac 19 | #' @export 20 | dplyr::sample_frac 21 | 22 | #' @importFrom dplyr sample_n 23 | #' @export 24 | dplyr::sample_n 25 | 26 | #' @importFrom dplyr filter 27 | #' @export 28 | dplyr::filter 29 | 30 | #' @importFrom dplyr mutate 31 | #' @export 32 | dplyr::mutate 33 | 34 | #' @importFrom dplyr arrange 35 | #' @export 36 | dplyr::arrange 37 | 38 | #' @importFrom dplyr group_by 39 | #' @export 40 | dplyr::group_by 41 | 42 | #' @importFrom dplyr ungroup 43 | #' @export 44 | dplyr::ungroup 45 | 46 | #' @importFrom dplyr left_join 47 | #' @export 48 | dplyr::left_join 49 | 50 | #' @importFrom dplyr anti_join 51 | #' @export 52 | dplyr::anti_join 53 | 54 | #' @importFrom dplyr full_join 55 | #' @export 56 | dplyr::full_join 57 | 58 | #' @importFrom dplyr inner_join 59 | #' @export 60 | dplyr::inner_join 61 | 62 | #' @importFrom dplyr semi_join 63 | #' @export 64 | dplyr::semi_join 65 | 66 | #' @importFrom dplyr right_join 67 | #' @export 68 | dplyr::right_join 69 | -------------------------------------------------------------------------------- /R/genRowNames.R: -------------------------------------------------------------------------------- 1 | #' Generates row names for a `soma_adat` object 2 | #' 3 | #' @param adat A data frame representing an ADAT. 4 | #' @return A vector of row names derived from `SlideId_Subarray`. 5 | #' @noRd 6 | genRowNames <- function(adat) { 7 | 8 | checkDups <- function(x) any(duplicated(x)) # internal 9 | 10 | if ( all(c("Subarray", "SlideId") %in% names(adat)) ) { 11 | 12 | adat_rn <- paste0(adat$SlideId, "_", adat$Subarray) 13 | 14 | # Added for datasets with same slide_id sub-scanned with different software 15 | # nocov start 16 | if ( checkDups(adat_rn) ) { 17 | if ( "PlateId" %in% names(adat) ) { 18 | adat_rn <- paste0(adat$PlateId, "_", adat_rn) 19 | } else if ( "DatasetId" %in% names(adat) ) { 20 | adat_rn <- paste0(adat$DatasetId, "_", adat_rn) 21 | } 22 | if ( checkDups(adat_rn) ) { 23 | warning( 24 | "Found duplicate row names, i.e. `SlideId_Subarray` non-unique. ", 25 | "They will be numbered sequentially.", call. = FALSE 26 | ) 27 | adat_rn <- seq_len(nrow(adat)) 28 | } 29 | } 30 | # nocov end 31 | } else { 32 | warning( 33 | "No SlideId_Subarray found in ADAT. ", 34 | "Rows numbered sequentially.", call. = FALSE 35 | ) 36 | adat_rn <- as.character(seq_len(nrow(adat))) 37 | } 38 | adat_rn 39 | } 40 | -------------------------------------------------------------------------------- /R/getAnalyteInfo.R: -------------------------------------------------------------------------------- 1 | #' Get Analyte Annotation Information 2 | #' 3 | #' Uses the `Col.Meta` attribute (analyte annotation data that appears above 4 | #' the protein measurements in the `*.adat` text file) of a `soma_adat` object, 5 | #' adds the `AptName` column key, conducts a few sanity checks, and 6 | #' generates a "lookup table" of analyte data that can be used for simple 7 | #' manipulation and indexing of analyte annotation information. 8 | #' Most importantly, the analyte column names of the `soma_adat` 9 | #' (e.g. `seq.XXXX.XX`) become the `AptName` column of the lookup table and 10 | #' represents the key index between the table and `soma_adat` from which it comes. 11 | #' 12 | #' @inheritParams params 13 | #' @return A `tibble` object with columns corresponding 14 | #' to the column meta data entries in the `soma_adat`. One row per analyte. 15 | #' @author Stu Field 16 | #' @seealso [getAnalytes()], [is_intact_attr()], [read_adat()] 17 | #' @examples 18 | #' # Get Aptamer table 19 | #' anno_tbl <- getAnalyteInfo(example_data) 20 | #' anno_tbl 21 | #' 22 | #' # Use `dplyr::group_by()` 23 | #' dplyr::tally(dplyr::group_by(anno_tbl, Dilution)) # print summary by dilution 24 | #' 25 | #' # Columns containing "Target" 26 | #' anno_tbl |> 27 | #' dplyr::select(dplyr::contains("Target")) 28 | #' 29 | #' # Rows of "Target" starting with MMP 30 | #' anno_tbl |> 31 | #' dplyr::filter(grepl("^MMP", Target)) 32 | #' @importFrom tibble tibble 33 | #' @export 34 | getAnalyteInfo <- function(adat) { 35 | 36 | colmeta <- attr(adat, "Col.Meta") 37 | stopifnot( 38 | "`Col.Meta` is absent from ADAT." = !is.null(colmeta), 39 | "`Col.Meta` must be a `tbl_df`." = inherits(colmeta, "tbl_df") 40 | ) 41 | colmeta <- dplyr::ungroup(colmeta) # for safety (previously a 'grouped_df') 42 | # AptName is the key index that links AnalyteInfo -> ADAT 43 | tbl <- tibble(AptName = getAnalytes(adat), SeqId = getSeqId(AptName, TRUE)) 44 | 45 | if ( nrow(tbl) != nrow(colmeta) ) { 46 | warning( 47 | "Features inconsistent between `AptName` vs `SeqId` in `getAnalyteInfo()`.\n", 48 | "Merging annotations based on analyte features of `soma_adat`.", call. = FALSE 49 | ) 50 | } 51 | dplyr::left_join(tbl, colmeta, by = "SeqId") 52 | } 53 | -------------------------------------------------------------------------------- /R/getMeta.R: -------------------------------------------------------------------------------- 1 | #' Get Meta 2 | #' 3 | #' [getMeta()] returns the inverse, a character vector of string 4 | #' names of *non*-analyte feature columns/variables, which typically 5 | #' correspond to the clinical ("meta") data variables. 6 | #' S3 methods exist for these classes: 7 | #' ```{r method-classes2, echo = FALSE} 8 | #' options(width = 80) 9 | #' methods("getMeta") 10 | #' ``` 11 | #' 12 | #' @rdname getAnalytes 13 | #' @return [getMeta()]: a character vector of ADAT clinical ("meta") data names. 14 | #' @return For both, if `n = TRUE`, an integer corresponding to the 15 | #' __length__ of the character vector. 16 | #' @examples 17 | #' 18 | #' # clinical variables 19 | #' mvec <- getMeta(example_data) 20 | #' head(mvec, 10) 21 | #' getMeta(example_data, n = TRUE) 22 | #' 23 | #' # test 'data.frame' and 'character' S3 methods are identical 24 | #' identical(getMeta(example_data), getMeta(names(example_data))) # TRUE 25 | #' @export 26 | getMeta <- function(x, n = FALSE) UseMethod("getMeta") 27 | 28 | #' @noRd 29 | #' @export 30 | getMeta.default <- function(x, n) { 31 | stop( 32 | "Couldn't find a S3 method for this class object: ", .value(class(x)), 33 | call. = FALSE 34 | ) 35 | } 36 | 37 | #' @noRd 38 | #' @export 39 | getMeta.data.frame <- function(x, n = FALSE) { 40 | getMeta(names(x), n = n) 41 | } 42 | 43 | #' @noRd 44 | #' @export 45 | getMeta.soma_adat <- getMeta.data.frame 46 | 47 | #' @noRd 48 | #' @export 49 | getMeta.list <- getMeta.data.frame 50 | 51 | #' @noRd 52 | #' @export 53 | getMeta.matrix <- function(x, n = FALSE) { 54 | getMeta(colnames(x), n = n) 55 | } 56 | 57 | #' @noRd 58 | #' @export 59 | getMeta.character <- function(x, n = FALSE) { 60 | lgl <- !is.apt(x) 61 | if ( n ) { 62 | sum(lgl) 63 | } else { 64 | x[lgl] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /R/getOutlierIds.R: -------------------------------------------------------------------------------- 1 | #' Get Flagged Ids From MAD Outlier Map 2 | #' 3 | #' Return the IDs of flagged samples for objects of the `outlier_map` class. 4 | #' Samples are flagged based on the percent analytes (RFU columns) for a given 5 | #' sample that were identified as outliers using the median absolute 6 | #' deviation (MAD). 7 | #' 8 | #' @family Calc Map 9 | #' @inheritParams plot.Map 10 | #' @param x An object of class: 11 | #' * `outlier_map` - from [calcOutlierMap()] 12 | #' @param data Optional. The data originally used to create the map `x`. If 13 | #' omitted, a single column data frame is returned. 14 | #' @param include Optional. Character vector of column name(s) in `data` to 15 | #' include in the resulting data frame. Ignored if `data = NULL`. 16 | #' @return A `data.frame` of the indices (`idx`) of flagged samples, along 17 | #' with any additional variables as specified by `include`. 18 | #' @author Caleb Scheidel 19 | #' @examples 20 | #' # flagged outliers 21 | #' # create a single sample outlier (12) 22 | #' out_adat <- example_data 23 | #' apts <- getAnalytes(out_adat) 24 | #' out_adat[12, apts] <- out_adat[12, apts] * 10 25 | #' 26 | #' om <- calcOutlierMap(out_adat) 27 | #' getOutlierIds(om, out_adat, flags = 0.05, include = c("Sex", "Subarray")) 28 | #' @export 29 | getOutlierIds <- function(x, flags = 0.05, data = NULL, include = NULL) { 30 | 31 | if ( !inherits(x, "outlier_map") ) { 32 | stop("Input `x` object must be class `outlier_map`!", 33 | call. = FALSE) 34 | } 35 | 36 | # ensure that flags value is between 0 & 1 37 | if ( flags < 0 || flags > 1 ) { 38 | stop("`flags =` argument must be between 0 and 1!", call. = FALSE) 39 | } 40 | 41 | flagged <- which(rowSums(x$matrix) >= ncol(x$matrix) * flags) |> unname() 42 | df_idx <- data.frame(idx = flagged) # default 1-col df 43 | 44 | if ( !length(flagged) ) { 45 | .todo("No observations were flagged at this flagging proportion: {.val {flags}}") 46 | } 47 | 48 | if ( is.null(data) ) { 49 | df_idx 50 | } else { 51 | stopifnot( 52 | "The `data` argument must be a `data.frame` object." = is.data.frame(data), 53 | "All `include` must be in `data`." = all(include %in% names(data)) 54 | ) 55 | df <- as.data.frame(data) # strip soma_adat class 56 | cbind( 57 | df_idx, 58 | rm_rn(df[flagged, include, drop = FALSE]) # ensure no rn 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /R/getSomaScanLiftCCC.R: -------------------------------------------------------------------------------- 1 | #' @rdname adat-helpers 2 | #' 3 | #' @description 4 | #' [getSomaScanLiftCCC()] accesses the lifting Concordance Correlation 5 | #' Coefficients between various SomaScan versions. For more about 6 | #' CCC metrics see [lift_adat()]. 7 | #' 8 | #' @inheritParams params 9 | #' @references Lin, Lawrence I-Kuei. 1989. A Concordance Correlation 10 | #' Coefficient to Evaluate Reproducibility. __Biometrics__. 45:255-268. 11 | #' @return 12 | #' \item{[getSomaScanLiftCCC()]}{Returns a tibble of either the 13 | #' `serum` or `plasma` CCC between various versions of the SomaScan assay.} 14 | #' @examples 15 | #' 16 | #' # plasma (default) 17 | #' getSomaScanLiftCCC() 18 | #' 19 | #' # serum 20 | #' getSomaScanLiftCCC("serum") 21 | #' @export 22 | getSomaScanLiftCCC <- function(matrix = c("plasma", "serum")) { 23 | matrix <- match.arg(matrix) 24 | dplyr::select(lift_master, SeqId, 25 | dplyr::starts_with(matrix) & dplyr::ends_with("ccc")) 26 | } 27 | -------------------------------------------------------------------------------- /R/getTargetNames.R: -------------------------------------------------------------------------------- 1 | #' Get Target Names 2 | #' 3 | #' @describeIn getAnalyteInfo 4 | #' creates a lookup table (or dictionary) as a named list object of `AptNames` 5 | #' and Target names in key-value pairs. 6 | #' This is a convenient tool to quickly access a `TargetName` given 7 | #' the `AptName` in which the key-value pairs map the `seq.XXXX.XX` 8 | #' to its corresponding `TargetName` in `tbl`. 9 | #' This structure which provides a convenient auto-completion mechanism at 10 | #' the command line or for generating plot titles. 11 | #' 12 | #' @param tbl A `tibble` object containing analyte target annotation 13 | #' information. This is usually the result of a call to [getAnalyteInfo()]. 14 | #' @examples 15 | #' 16 | #' # Target names 17 | #' tg <- getTargetNames(anno_tbl) 18 | #' 19 | #' # how to use for plotting 20 | #' feats <- sample(anno_tbl$AptName, 6) 21 | #' op <- par(mfrow = c(2, 3)) 22 | #' sapply(feats, function(.x) plot(1:10, main = tg[[.x]])) 23 | #' par(op) 24 | #' @export 25 | getTargetNames <- function(tbl) { 26 | stopifnot( 27 | "`tbl` must contain Target info." = 28 | sum(c("TargetFullName", "Target") %in% names(tbl)) > 0, 29 | "`tbl` must contain an `AptName` column." = "AptName" %in% names(tbl) 30 | ) 31 | targets <- tbl$TargetFullName %||% tbl$Target 32 | structure(as.list(targets), names = tbl$AptName, class = "target_map") 33 | } 34 | 35 | 36 | #' @noRd 37 | #' @export 38 | print.target_map <- function(x, ...) { 39 | writeLines(cli_rule("AptName-Target Lookup Map", line = 2, line_col = "magenta")) 40 | print(tibble::enframe(unlist(x), name = "AptName", value = "Target")) 41 | writeLines(cli_rule(line = 2, line_col = "green")) 42 | invisible(x) 43 | } 44 | -------------------------------------------------------------------------------- /R/is-seqFormat.R: -------------------------------------------------------------------------------- 1 | #' Test `AptName` Format 2 | #' 3 | #' Test whether an object is in the new `seq.XXXX.XX` format. 4 | #' 5 | #' @param x The object to be tested. 6 | #' @return A logical indicating whether `x` contains `AptNames` consistent 7 | #' with the new format, beginning with a `seq.` prefix. 8 | #' @examples 9 | #' # character S3 method 10 | #' is_seqFormat(names(example_data)) # no; meta data not ^seq. 11 | #' is_seqFormat(tail(names(example_data), -20L)) # yes 12 | #' 13 | #' # soma_adat S3 method 14 | #' is_seqFormat(example_data) 15 | #' @author Stu Field, Eduardo Tabacman 16 | #' @export 17 | is_seqFormat <- function(x) UseMethod("is_seqFormat") 18 | 19 | #' Default method 20 | #' @noRd 21 | #' @export 22 | is_seqFormat.default <- function(x) { 23 | stop( 24 | "Couldn't find a S3 method for this class object: ", .value(class(x)), 25 | call. = FALSE 26 | ) 27 | } 28 | 29 | #' S3 soma_adat method 30 | #' @noRd 31 | #' @export 32 | is_seqFormat.soma_adat <- function(x) { 33 | is_seqFormat(getAnalytes(x)) 34 | } 35 | 36 | #' S3 data.frame method 37 | #' @noRd 38 | #' @export 39 | is_seqFormat.data.frame <- is_seqFormat.soma_adat 40 | 41 | #' S3 character method 42 | #' @noRd 43 | #' @export 44 | is_seqFormat.character <- function(x) { 45 | ifelse(length(x) == 0L, FALSE, all(grepl("^seq\\.", x))) 46 | } 47 | -------------------------------------------------------------------------------- /R/params.R: -------------------------------------------------------------------------------- 1 | #' Common Parameters in \pkg{SomaDataIO} 2 | #' 3 | #' The parameters below are commonly used throughout 4 | #' the \pkg{SomaDataIO} package. 5 | #' 6 | #' @name params 7 | #' 8 | #' @param adat A `soma_adat` object (with intact attributes), 9 | #' typically created using [read_adat()]. 10 | #' 11 | #' @param x A `soma_adat` object (with intact attributes), 12 | #' typically created using [read_adat()]. 13 | #' 14 | #' @param matrix Character. A string of (usually) either 15 | #' `"serum"` or `"plasma"`. 16 | #' 17 | #' @return A `soma_adat` class object. 18 | NULL 19 | -------------------------------------------------------------------------------- /R/pivotExpressionSet.R: -------------------------------------------------------------------------------- 1 | #' Convert to Long Format 2 | #' 3 | #' Utility to convert an `ExpressionSet` class object 4 | #' from the "wide" data format to the "long" format via [tidyr::pivot_longer()]. 5 | #' The \pkg{Biobase} package is required for this function. 6 | #' 7 | #' @family eSet 8 | #' @param eSet An `ExpressionSet` class object, created using [adat2eSet()]. 9 | #' @return A `tibble` consisting of the long format 10 | #' conversion of an `ExpressionSet` object. 11 | #' @author Stu Field 12 | #' @examplesIf rlang::is_installed("Biobase") 13 | #' # subset into a reduced mini-ADAT object 14 | #' # 10 samples (rows) 15 | #' # 5 clinical variables and 3 features (cols) 16 | #' sub_adat <- example_data[1:10, c(1:5, 35:37)] 17 | #' ex_set <- adat2eSet(sub_adat) 18 | #' 19 | #' # convert ExpressionSet object to long format 20 | #' adat_long <- pivotExpressionSet(ex_set) 21 | #' @importFrom tibble as_tibble 22 | #' @importFrom tidyr pivot_longer 23 | #' @importFrom dplyr left_join select 24 | #' @export 25 | pivotExpressionSet <- function(eSet) { 26 | 27 | if ( !requireNamespace("Biobase", quietly = TRUE) ) { 28 | # nocov start 29 | stop( 30 | "The `Biobase` package is required to use this function.\n", 31 | "See ?adat2eSet for installation instructions.", call. = FALSE 32 | ) 33 | # nocov end 34 | } 35 | 36 | # samples (rows) x features (cols); move rn -> 1st column 37 | f_data <- Biobase::fData(eSet) |> rn2col("feature") 38 | p_data <- Biobase::pData(eSet) |> rn2col("array_id") 39 | 40 | data_long <- Biobase::exprs(eSet) |> 41 | t() |> data.frame() |> rn2col("array_id") |> 42 | tidyr::pivot_longer(cols = -array_id, names_to = "feature") 43 | 44 | data_long |> 45 | dplyr::left_join(f_data, by = "feature") |> # merge feature data 46 | dplyr::left_join(p_data, by = "array_id") |> # merge clinical data 47 | dplyr::arrange(array_id) |> # order by sample/array 48 | dplyr::select(array_id, feature, dplyr::everything()) |> # re-order 49 | dplyr::select(-value, dplyr::everything()) |> # move 'value' to end 50 | tibble::as_tibble() # convert to tibble 51 | } 52 | -------------------------------------------------------------------------------- /R/s3-merge-soma-adat.R: -------------------------------------------------------------------------------- 1 | 2 | #' @importFrom lifecycle deprecate_stop 3 | #' @export 4 | merge.soma_adat <- function(x, y, by = intersect(names(x), names(y)), 5 | by.x = by, by.y = by, all = FALSE, 6 | all.x = all, all.y = all, sort = TRUE, 7 | suffixes = c(".x", ".y"), nu.dups = TRUE, 8 | incomparables = NULL, ...) { 9 | # redirect to use `dplyr` alternatives 10 | call <- match.call() 11 | call[[1L]] <- str2lang("dplyr::left_join") 12 | deprecate_stop( 13 | "6.0.0", I("Using `merge()` on a `soma_adat`"), 14 | I("any of the `dplyr::*_join()` alternatives"), 15 | details = paste0("Perhaps ", .code(deparse(call)), "?") 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /R/s3-summary-soma-adat.R: -------------------------------------------------------------------------------- 1 | #' S3 Summary 2 | #' 3 | #' The S3 [summary()] method returns the following for each column of the ADAT 4 | #' object containing SOMAmer data (clinical meta data is *excluded*): 5 | #' \itemize{ 6 | #' \item Target (if available) 7 | #' \item Minimum value 8 | #' \item 1st Quantile 9 | #' \item Median 10 | #' \item Mean 11 | #' \item 3rd Quantile 12 | #' \item Maximum value 13 | #' \item Standard deviation 14 | #' \item Median absolute deviation ([mad()]) 15 | #' \item Interquartile range ([IQR()]) 16 | #' } 17 | #' 18 | #' @rdname soma_adat 19 | #' @order 3 20 | #' @param tbl An annotations table. If `NULL` (default), 21 | #' annotation information is extracted from the object itself (if possible). 22 | #' Alternatively, the result of a call to [getAnalyteInfo()], from 23 | #' which Target names can be extracted. 24 | #' @param digits Integer. Used for number formatting with [signif()]. 25 | #' @examples 26 | #' # S3 summary method 27 | #' # MMP analytes (4) 28 | #' mmps <- c("seq.2579.17", "seq.2788.55", "seq.2789.26", "seq.4925.54") 29 | #' mmp_adat <- example_data[, c("Sex", mmps)] 30 | #' summary(mmp_adat) 31 | #' 32 | #' # Summarize by group 33 | #' mmp_adat |> 34 | #' split(mmp_adat$Sex) |> 35 | #' lapply(summary) 36 | #' 37 | #' # Alternatively pass annotations with Target info 38 | #' anno <- getAnalyteInfo(mmp_adat) 39 | #' summary(mmp_adat, tbl = anno) 40 | #' @importFrom stats IQR mad sd setNames 41 | #' @importFrom dplyr select all_of 42 | #' @export 43 | summary.soma_adat <- function(object, tbl = NULL, 44 | digits = max(3L, getOption("digits") - 3L), ...) { 45 | 46 | if ( is.null(tbl) ) { 47 | tbl <- getAnalyteInfo(object) 48 | } 49 | 50 | nm <- getAnalytes(object) 51 | labs <- c("Target", "Min", "1Q", "Median", "Mean", "3Q", 52 | "Max", "sd", "MAD", "IQR") |> .pad(6) 53 | 54 | vals <- dplyr::select(object, all_of(nm)) |> 55 | lapply(function(.x) { 56 | vec <- .x[!is.na(.x)] # rm NaN/NA; outside b/c summary() 57 | format(c(unname(summary(vec)), sd(vec), mad(vec), IQR(vec)), 58 | digits = digits) 59 | }) 60 | 61 | # lookup table 62 | look <- as.list(tbl$Target) |> setNames(tbl$AptName) 63 | tgts <- setNames(names(vals), names(vals)) |> 64 | lapply(function(.x) ifelse(is.null(look[[.x]]), "", look[[.x]])) # if NULL -> "" 65 | 66 | setNames(nm, nm) |> 67 | lapply(function(.col) { 68 | paste(labs, ":", .pad(c(tgts[[.col]], vals[[.col]]), width = 10)) 69 | }) |> 70 | data.frame() |> 71 | addClass("adat_summary") 72 | } 73 | 74 | #' @noRd 75 | #' @importFrom utils capture.output 76 | #' @export 77 | print.adat_summary <- function(x, ...) { 78 | z <- as.matrix(x) 79 | rownames(z) <- rep.int("", nrow(z)) 80 | out <- capture.output(print.default(z, quote = FALSE)) 81 | lapply(out, function(.x) { 82 | if ( grepl("seq", .x) ) { 83 | cat(cr_red(.x), "\n") 84 | } else if ( grepl("Target", .x) ) { 85 | cat(cr_blue(.x), "\n") 86 | } else { 87 | cat(.x, "\n") 88 | } 89 | }) 90 | invisible(x) 91 | } 92 | -------------------------------------------------------------------------------- /R/s3-transform.R: -------------------------------------------------------------------------------- 1 | #' Scale Transform `soma_adat` Columns/Rows 2 | #' 3 | #' Scale the *i-th* row or column of a `soma_adat` object by the *i-th* 4 | #' element of a vector. Designed to facilitate linear transformations 5 | #' of _only_ the analyte/RFU entries by scaling the data matrix. 6 | #' If scaling the analytes/RFU (columns), `v` _must_ have 7 | #' `getAnalytes(adat, n = TRUE)` elements. 8 | #' If scaling the samples (rows), `v` _must_ 9 | #' have `nrow(_data)` elements. 10 | #' 11 | #' Performs the following operations (quickly): 12 | #' 13 | #' Columns: 14 | #' \deqn{ 15 | #' M_{nxp} = A_{nxp} * diag(v)_{pxp} 16 | #' } 17 | #' 18 | #' Rows: 19 | #' \deqn{ 20 | #' M_{nxp} = diag(v)_{nxn} * A_{nxp} 21 | #' } 22 | #' 23 | #' @name transform 24 | #' @param _data A `soma_adat` object. 25 | #' @param v A numeric vector of the appropriate length corresponding to `dim`. 26 | #' @param dim Integer. The dimension to apply elements of `v` to. 27 | #' `1` = rows; `2` = columns (default). 28 | #' @param ... Currently not used but required by the S3 generic. 29 | #' @return A modified value of `_data` with either the rows or columns 30 | #' linearly transformed by `v`. 31 | #' @note This method in intentionally naive, and assumes the user has 32 | #' ordered `v` to match the columns/rows of `_data` appropriately. 33 | #' This must be done upstream. 34 | #' @seealso [apply()], [sweep()] 35 | #' @examples 36 | #' # simplified example of underlying operations 37 | #' M <- matrix(1:12, ncol = 4) 38 | #' M 39 | #' 40 | #' v <- 1:4 41 | #' M %*% diag(v) # transform columns 42 | #' 43 | #' v <- 1:3 44 | #' diag(v) %*% M # transform rows 45 | #' 46 | #' # dummy ADAT example: 47 | #' v <- c(2, 0.5) # double seq1; half seq2 48 | #' adat <- data.frame(sample = paste0("sample_", 1:3), 49 | #' seq.1234.56 = c(1, 2, 3), 50 | #' seq.9999.88 = c(4, 5, 6) * 10) 51 | #' adat 52 | #' 53 | #' # `soma_adat` to invoke S3 method dispatch 54 | #' class(adat) <- c("soma_adat", "data.frame") 55 | #' trans <- transform(adat, v) 56 | #' data.frame(trans) 57 | #' @export 58 | transform.soma_adat <- function(`_data`, v, dim = 2L, ...) { 59 | stopifnot(dim %in% 1:2L) 60 | x <- `_data` 61 | .apts <- getAnalytes(x) 62 | if ( dim == 2L ) { 63 | stopifnot(length(v) == length(.apts)) # check cols 64 | x[, .apts] <- t( t(x[, .apts, drop = FALSE]) * v ) 65 | } else { 66 | stopifnot(length(v) == nrow(x)) # check rows 67 | x[, .apts] <- as.matrix(x[, .apts, drop = FALSE]) * v 68 | } 69 | x 70 | } 71 | -------------------------------------------------------------------------------- /R/scaleAnalytes.R: -------------------------------------------------------------------------------- 1 | #' Scale/transform Analyte RFUs 2 | #' 3 | #' Scale analytes by a scalar reference value with `SeqId` names to 4 | #' match with the analytes contained in `.data`. Columns without a 5 | #' corresponding reference value are not modified (with a warning). 6 | #' 7 | #' @param .data A `soma_adat` class object ... _must_ be a `soma_adat` for 8 | #' downstream methods to properly dispatch. 9 | #' @param scale_vec A vector of scalars, named by `SeqId`, see [getSeqId()]. 10 | #' @author Stu Field 11 | #' @examples 12 | #' apts <- withr::with_seed(101, sample(getAnalytes(example_data), 3L)) 13 | #' adat <- head(example_data, 3L) |> dplyr::select(SampleId, all_of(apts)) 14 | #' ref <- c("3072-4" = 10.0, "18184-28" = 0.1, "4430-44" = 1.0) 15 | #' new <- scaleAnalytes(adat, ref) 16 | #' new 17 | #' @importFrom tibble enframe deframe 18 | #' @noRd 19 | scaleAnalytes <- function(.data, scale_vec) { 20 | 21 | .code <- function(x) { 22 | paste0("\033[90m", encodeString(x, quote = "`"), "\033[39m") 23 | } 24 | 25 | if ( !is.soma_adat(.data) ) { 26 | obj <- class(.data)[1L] 27 | call <- match.call() 28 | new <- paste0("addClass(", deparse(call[[2L]]), ", \"soma_adat\")") 29 | call[[2L]] <- str2lang(new) 30 | msg <- sprintf( 31 | "`scaleAnalytes()` must be called on a %s object, not a %s.\n", 32 | .value("soma_adat"), .value(obj) 33 | ) 34 | msg2 <- sprintf("Perhaps: %s?", .code(deparse(call))) 35 | stop(msg, msg2, call. = FALSE) 36 | } 37 | 38 | apts <- getAnalytes(.data) 39 | stbl <- enframe(scale_vec, "SeqId") 40 | matches <- getSeqIdMatches(apts, stbl$SeqId) # order matters; apts 1st 41 | missing <- setdiff(apts, matches$apts) 42 | extra <- setdiff(stbl$SeqId, matches$`stbl$SeqId`) 43 | if ( length(missing) > 0L ) { 44 | warning( 45 | "Missing scalar value for (", length(missing), ") analytes. ", 46 | "They will not be transformed.\n", 47 | "Please check the reference or its named SeqIds.", 48 | call. = FALSE 49 | ) 50 | } 51 | if ( length(extra) > 0L ) { 52 | warning( 53 | "There are extra scaling values (", length(extra), ") in the reference.\n", 54 | "They will be ignored.", call. = FALSE 55 | ) 56 | stbl <- dplyr::filter(stbl, SeqId %in% matches$`stbl$SeqId`) 57 | } 58 | 59 | svec <- deframe(stbl) # return to named vector 60 | svec <- svec[matches$`stbl$SeqId`] # order reference to the adat 61 | 62 | stopifnot(all(names(svec) %in% getSeqId(apts))) 63 | 64 | # apply svec scalars by column 65 | new <- transform(.data[, matches$apts, drop = FALSE], unname(svec)) 66 | .data[, matches$apts] <- data.frame(new, row.names = NULL) 67 | .data 68 | } 69 | -------------------------------------------------------------------------------- /R/syncColMeta.R: -------------------------------------------------------------------------------- 1 | #' Synchronize Col.Meta Attribute 2 | #' 3 | #' Uses `SeqId` matching to synchronize the attributes of 4 | #' a `soma_adat` object that has extra `Col.Meta` in its attributes. 5 | #' 6 | #' @param data A `soma_adat` data object to update attributes. 7 | #' @return An identical object to `data` with `Col.Meta` trimmed 8 | #' to match those in its columns. 9 | #' @author Stu Field 10 | #' @noRd 11 | syncColMeta <- function(data) { 12 | col_meta <- attr(data, "Col.Meta") 13 | # no trim leading whitespace if 'else' below is to perform pattern match 14 | ft <- trimws(getAnalytes(data), which = "right") 15 | # if all features have `seq.` format; no need to match (speed) 16 | if ( all(grepl("^seq[.]", ft)) ) { 17 | new_seq <- gsub("^seq[.]", "", ft) 18 | } else { 19 | df <- locateSeqId(ft) 20 | new_seq <- substr(df$x, df$start, df$stop) 21 | } 22 | new_seq <- sub("\\.", "-", new_seq) 23 | k <- match(new_seq, col_meta$SeqId) 24 | # Update the attributes -> Col.Meta information 25 | structure(data, Col.Meta = col_meta[k, ]) 26 | } 27 | -------------------------------------------------------------------------------- /R/sysdata.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/R/sysdata.rda -------------------------------------------------------------------------------- /R/tidyr-reexports.R: -------------------------------------------------------------------------------- 1 | 2 | #' @importFrom tidyr unite 3 | #' @export 4 | tidyr::unite 5 | 6 | #' @importFrom tidyr separate 7 | #' @export 8 | tidyr::separate 9 | -------------------------------------------------------------------------------- /R/tidyr-verbs.R: -------------------------------------------------------------------------------- 1 | 2 | #' These are `tidyr` verb methods for `soma_adat` class 3 | #' @noRd 4 | NULL 5 | 6 | #' @export 7 | separate.soma_adat <- function(data, col, into, sep = "[^[:alnum:]]+", 8 | remove = TRUE, convert = FALSE, extra = "warn", 9 | fill = "warn", ...) { 10 | atts <- attributes(data) 11 | data <- rn2col(data, ".separate_rn") 12 | # must do it this way b/c NextMethod() doesn't play nice with lazyeval 13 | col2 <- tryCatch(eval(col), error = function(e) NULL) %||% deparse(substitute(col)) 14 | stopifnot( 15 | "`col` must be a `character(1)` or a symbol." = is.character(col2), 16 | "`col` must have length = 1." = length(col2) == 1L, 17 | "`col` must be a variable name in `data`." = col2 %in% names(data) 18 | ) 19 | data <- data.frame(data) 20 | .data <- tidyr::separate(data, col2, into, sep, remove, convert, extra, fill, ...) 21 | col2rn(.data, ".separate_rn") |> 22 | addAttributes(atts) |> 23 | addClass("soma_adat") 24 | } 25 | 26 | #' @export 27 | unite.soma_adat <- function(data, col, ..., sep = "_", remove = TRUE, na.rm = FALSE) { 28 | atts <- attributes(data) 29 | data <- rn2col(data, ".unite_rn") 30 | data <- data.frame(data) 31 | .data <- tidyr::unite(data, !!col, ..., sep = sep, remove = remove, na.rm = na.rm) 32 | col2rn(.data, ".unite_rn") |> 33 | addAttributes(atts) |> 34 | addClass("soma_adat") 35 | } 36 | -------------------------------------------------------------------------------- /R/utils-lift.R: -------------------------------------------------------------------------------- 1 | 2 | # map external commercial names to 3 | # internal SomaScan version names 4 | # ---------------------------------- 5 | map_ver2k <- c( 6 | V3 = "1.1k", 7 | v3 = "1.1k", 8 | v3.0 = "1.1k", 9 | V3.2 = "1.3k", 10 | v3.2 = "1.3k", 11 | V4 = "5k", 12 | v4 = "5k", 13 | v4.0 = "5k", 14 | V4.1 = "7k", 15 | v4.1 = "7k", 16 | V5 = "11k", 17 | v5 = "11k", 18 | v5.0 = "11k" 19 | ) 20 | 21 | map_k2ver <- c( 22 | "1.1k" = "v3.0", 23 | "1.3k" = "v3.2", 24 | "5k" = "v4.0", 25 | "7k" = "v4.1", 26 | "11k" = "v5.0" 27 | ) 28 | 29 | # matrx: either serum or plasma 30 | # bridge: direction of the bridge 31 | .get_lift_ref <- function(matrx = c("plasma", "serum"), bridge) { 32 | matrx <- match.arg(matrx) 33 | df <- dplyr::select(lift_master, SeqId, paste0(matrx, "_", bridge)) 34 | df[is.na(df)] <- 1.0 # 1.0 scale factor for NAs 35 | setNames(df[[2L]], df$SeqId) 36 | } 37 | 38 | 39 | # Checks ---- 40 | # check that SomaScan data has been ANML normalized 41 | # x = Header attributes 42 | .check_anml <- function(x) { 43 | steps <- x$ProcessSteps 44 | if ( is.null(steps) | !grepl("ANML", steps, ignore.case = TRUE) ) { 45 | stop("ANML normalized SOMAscan data is required for lifting.", 46 | call. = FALSE) 47 | } 48 | invisible(NULL) 49 | } 50 | 51 | #' @param x the 'from' space. 52 | #' @param y the bridge variable, e.g. '5k_to_7k'. 53 | #' @return The 'to' space, from the 'y' param. 54 | #' @noRd 55 | .check_direction <- function(x, y) { 56 | x <- tolower(x) 57 | from <- gsub("(.*)_to_(.*)$", "\\1", y) 58 | to <- gsub("(.*)_to_(.*)$", "\\2", y) 59 | 60 | if ( isFALSE(x == from) ) { 61 | stop( 62 | "You have indicated a bridge from ", .value(from), 63 | " space, however your RFU data appears to be in ", 64 | .value(x), " space.", call. = FALSE 65 | ) 66 | } 67 | if ( isTRUE(x == to) ) { 68 | stop( 69 | "You have indicated a bridge to ", .value(to), 70 | " space, however your RFU data already appears to be in ", 71 | .value(x), " space.", call. = FALSE 72 | ) 73 | } 74 | 75 | invisible(to) 76 | } 77 | -------------------------------------------------------------------------------- /R/utils-pipe.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | #' @param lhs A value or the magrittr placeholder. 12 | #' @param rhs A function call using the magrittr semantics. 13 | #' @return The result of calling `rhs(lhs)`. 14 | NULL 15 | -------------------------------------------------------------------------------- /R/z-deprecated.R: -------------------------------------------------------------------------------- 1 | #' Deprecated function(s) of the \pkg{SomaDataIO} package 2 | #' 3 | #' @description 4 | #' These functions have either been 5 | #' `r lifecycle::badge("superseded")` or 6 | #' `r lifecycle::badge("deprecated")` 7 | #' in the current version of \pkg{SomaDataIO} package. 8 | #' They may eventually be completely removed, so 9 | #' please re-code your scripts accordingly based on the 10 | #' suggestions below: 11 | #' 12 | #' \tabular{lcr}{ 13 | #' **Function** \tab \tab **Now Use** \cr 14 | #' [getSomamers()] \tab `r lifecycle::badge("superseded")` \tab [getAnalytes()] \cr 15 | #' [getSomamerData()] \tab `r lifecycle::badge("superseded")` \tab [getAnalyteInfo()] \cr 16 | #' } 17 | #' 18 | #' @details 19 | #' Some badges you may see in \pkg{SomaDataIO}: 20 | #' 21 | #' `r lifecycle::badge("superseded")` 22 | #' 23 | #' `r lifecycle::badge("deprecated")` 24 | #' 25 | #' `r lifecycle::badge("soft-deprecated")` 26 | #' 27 | #' `r lifecycle::badge("stable")` 28 | #' 29 | #' @name SomaDataIO-deprecated 30 | #' @aliases getSomamers getSomamerData 31 | #' @importFrom lifecycle deprecate_warn deprecate_stop 32 | NULL 33 | 34 | 35 | #' @describeIn getAnalyteInfo 36 | #' `r lifecycle::badge("superseded")`. Please now use [getAnalyteInfo()]. 37 | #' @export 38 | getFeatureData <- function(adat) { 39 | deprecate_warn("5.1.0", "SomaDataIO::getFeatureData()", "getAnalyteInfo()") 40 | getAnalyteInfo(adat) 41 | } 42 | 43 | #' @describeIn getAnalytes 44 | #' `r lifecycle::badge("superseded")`. Please now use [getAnalytes()]. 45 | #' @export 46 | getFeatures <- function(x, n = FALSE, rm.controls = FALSE) { 47 | deprecate_warn("5.1.0", "SomaDataIO::getFeatures()", "getAnalytes()") 48 | getAnalytes(x = x, n = n, rm.controls = rm.controls) 49 | } 50 | 51 | #' @describeIn pivotExpressionSet 52 | #' `r lifecycle::badge("superseded")`. Please now use [pivotExpressionSet()]. 53 | #' @export 54 | meltExpressionSet <- function(eSet) { 55 | deprecate_stop("5.0.0", "SomaDataIO::meltExpressionSet()", "pivotExpressionSet()") 56 | } 57 | 58 | #' @noRd 59 | #' @export 60 | getSomamers <- function(x) { 61 | deprecate_stop("5.0.0", "SomaDataIO::getSomamers()", "getAnalytes()") 62 | } 63 | 64 | #' @noRd 65 | #' @export 66 | getSomamerData <- function(x) { 67 | deprecate_stop("5.0.0", "SomaDataIO::getSomamerData()", "getAnalyteInfo()") 68 | } 69 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | 2 | .dummy <- function() { } # nolint: brace_linter. 3 | 4 | .onLoad <- function(libname, pkgname) { 5 | # this is to make the active binding switch between 6 | # UTF-8 and ASCII symbol encodings 7 | # nocov start 8 | `%enc%` <- function(utf, ascii) { 9 | if ( getOption("cli.unicode", TRUE) && l10n_info()$`UTF-8` ) { 10 | utf 11 | } else { 12 | ascii 13 | } 14 | } 15 | pkgenv <- environment(.dummy) 16 | makeActiveBinding("symb_tick", function() "\u2713" %enc% "v", pkgenv) 17 | makeActiveBinding("symb_cross", function() "\u2716" %enc% "x", pkgenv) 18 | makeActiveBinding("symb_warn", function() "\u26A0" %enc% "!", pkgenv) 19 | makeActiveBinding("symb_point", function() "\u276F" %enc% ">", pkgenv) 20 | makeActiveBinding("symb_info", function() "\u2139" %enc% "i", pkgenv) 21 | # nocov end 22 | } 23 | 24 | .onAttach <- function(libname, pkgname) { 25 | # Startup Message 26 | packageStartupMessage( 27 | cli_rule(right = "Legal", line = 2, col = "magenta"), 28 | "\n", 29 | create_legal(), 30 | "\n", 31 | cli_rule(line = 2, col = "magenta") 32 | ) 33 | } 34 | 35 | create_legal <- function() { 36 | x <- strwrap( 37 | paste( 38 | "SomaDataIO\u2122 39 | 40 | Copyright \u00A9 2025 Standard BioTools, Inc. 41 | 42 | The `SomaDataIO` package is licensed under the MIT license 43 | (`LICENSE.md`) and is intended solely for research use 44 | only (\"RUO\") purposes. The code contained herein may *not* 45 | be used for diagnostic, clinical, therapeutic, or other 46 | commercial purposes. Further, \"SomaDataIO\" and \"SomaLogic\" 47 | are trademarks owned by Standard BioTools, Inc. No license 48 | is hereby granted to these trademarks other than for purposes 49 | of identifying the origin or source of the Software. The above 50 | copyright notice and this permission notice shall be included 51 | in all copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, 54 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 55 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 56 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 57 | CLAIM, DAMAGES, WHETHER DIRECT OR INDIRECT, OR OTHER LIABILITY, 58 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 59 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 60 | DEALINGS IN THE SOFTWARE." 61 | ), 62 | indent = 0, prefix = " ", initial = " " 63 | ) 64 | paste(x, collapse = "\n") 65 | } 66 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | - create an issue [here](https://github.com/SomaLogic/SomaDataIO/issues/) 6 | - send an e-mail to techsupport@somalogic.com to report a vulnerability 7 | -------------------------------------------------------------------------------- /SomaDataIO.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageCheckArgs: --no-tests --no-build-vignettes 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | 2 | # Submission 6.3.0 3 | 4 | - This is a submission to an existing CRAN package. 5 | 6 | - It contains new functionality, a new vignette article and 7 | improved documentation. 8 | 9 | 10 | ## Reverse Dependencies 11 | 12 | - There are no reverse dependencies. 13 | 14 | - One package, `SomaScan.db`, has a reverse *Suggests*. 15 | 16 | - It is an associated `BioConductor` package which we also maintain. 17 | 18 | 19 | ## R CMD check results 20 | ``` 21 | 0 errors | 0 warnings | 2 notes 22 | ``` 23 | 24 | ### Notes 25 | 26 | The following `notes` were displayed during 27 | `devtools::check(remote = TRUE, manual = TRUE)`: 28 | 29 | ``` 30 | ❯ checking CRAN incoming feasibility ... [7s/28s] NOTE 31 | Maintainer: ‘Caleb Scheidel ’ 32 | 33 | ❯ checking installed package size ... NOTE 34 | installed size is 6.0Mb 35 | sub-directories of 1Mb or more: 36 | data 3.8Mb 37 | extdata 1.1Mb 38 | ``` 39 | 40 | ## Package Size 41 | 42 | - Package tarball from `R CMD build` is 4.2Mb 43 | 44 | 45 | ## Example Timings from Win-builder 46 | 47 | ``` 48 | name user system elapsed 49 | Col.Meta 0.34 0.00 0.34 50 | SeqId 0.03 0.02 0.05 51 | SomaDataIO-package 0.12 0.00 0.12 52 | SomaScanObjects 0.36 0.00 0.36 53 | adat-helpers 0.06 0.00 0.08 54 | adat2eSet 0.58 0.06 0.64 55 | addClass 0 0 0 56 | calcOutlierMap 4.49 0.29 4.78 57 | calc_eLOD 0.03 0.00 0.03 58 | cleanNames 0 0 0 59 | diffAdats 0.47 0.05 0.51 60 | getAnalyteInfo 0.17 0.00 0.17 61 | getAnalytes 0.01 0.00 0.01 62 | getOutlierIds 2.03 0.09 2.14 63 | groupGenerics 0.49 0.00 0.49 64 | is_intact_attr 0.01 0.00 0.02 65 | is_seqFormat 0.02 0.00 0.02 66 | lift_adat 0.44 0.00 0.44 67 | loadAdatsAsList 1.67 0.02 1.69 68 | merge_clin 0.12 0.01 0.14 69 | parseHeader 0.14 0.02 0.15 70 | pivotExpressionSet 0.11 0.00 0.11 71 | plot.Map 1.78 0.06 1.84 72 | preProcessAdat 1.88 0.03 1.91 73 | read_adat 1.44 0.03 1.47 74 | read_annotations 0 0 0 75 | rownames 0.01 0.00 0.01 76 | soma_adat 0.29 0.00 0.29 77 | transform 0 0 0 78 | write_adat 0.40 0.01 0.42 79 | ``` 80 | 81 | -------------------------------------------------------------------------------- /data-raw/SomaScanObjects.R: -------------------------------------------------------------------------------- 1 | devtools::load_all(".") 2 | 3 | # originals 4 | data <- example_data 5 | x <- ex_analytes 6 | y <- ex_anno_tbl 7 | z <- ex_target_names 8 | zz <- ex_clin_data 9 | 10 | # 'new' 11 | example_data <- read_adat("example_data.adat") # download via wget 12 | ex_analytes <- getAnalytes(example_data) 13 | ex_anno_tbl <- getAnalyteInfo(example_data) 14 | ex_target_names <- getTargetNames(ex_anno_tbl) 15 | 16 | withr::with_seed(123, { 17 | ex_clin_data <- example_data |> 18 | dplyr::filter(SampleType == "Sample") |> 19 | dplyr::mutate( 20 | smoking_status = sample(c("Current", "Past", "Never"), 21 | size = 170, replace = TRUE), 22 | alcohol_use = sample(c("Yes", "No"), 23 | size = 170, replace = TRUE) 24 | ) |> 25 | select(SampleId, smoking_status, alcohol_use) |> 26 | as_tibble() 27 | }) 28 | 29 | 30 | # 'save only if necessary' 31 | if ( !isTRUE(all.equal(data, example_data)) ) { 32 | save(example_data, file = "data/example_data.rda", compress = "xz") 33 | } 34 | 35 | # 'save only if necessary' 36 | if ( !all(isTRUE(all.equal(x, ex_analytes)), 37 | isTRUE(all.equal(y, ex_anno_tbl)), 38 | isTRUE(all.equal(z, ex_target_names)), 39 | isTRUE(all.equal(zz, ex_clin_data))) ) { 40 | save(ex_analytes, ex_anno_tbl, ex_target_names, ex_clin_data, file = "data/data_objects.rda", compress = "xz") 41 | } 42 | -------------------------------------------------------------------------------- /data/data_objects.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/data/data_objects.rda -------------------------------------------------------------------------------- /data/example_data.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/data/example_data.rda -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | ADAT 2 | ADATs 3 | ANML 4 | Agilent 5 | Analyte 6 | Analytes 7 | AptName 8 | AssayNotes 9 | Barcode 10 | BioTools 11 | Biobase 12 | Biometrics 13 | CCC 14 | CLI 15 | CMD 16 | CSF 17 | CalQcRatio 18 | CalReference 19 | Calc 20 | Codecov 21 | ColCheck 22 | Covance 23 | Dimapasok 24 | EDTA 25 | EID 26 | EOL 27 | EXID 28 | Entrez 29 | EntrezGeneID 30 | EntrezGeneSymbol 31 | ExpressionSet 32 | ExtIdentifier 33 | FC 34 | Hiser 35 | HybControlNormScale 36 | Kuei 37 | LF 38 | Lifecycle 39 | MERCHANTABILITY 40 | MacOS 41 | MADs 42 | NHS 43 | NormScale 44 | ORCID 45 | PII 46 | PPT 47 | PercentDilution 48 | PlateId 49 | PlatePosition 50 | PlateScale 51 | Pre 52 | QcReference 53 | README 54 | RFU 55 | RFUs 56 | RUO 57 | ReferenceRFU 58 | Reproducibility 59 | RowCheck 60 | SELEX 61 | SG 62 | SL 63 | SLnnnnnn 64 | SOMAmer 65 | SampleDescription 66 | SampleGroup 67 | SampleId 68 | SampleMatrix 69 | SampleNotes 70 | SampleType 71 | ScannerID 72 | SeqId 73 | SeqIds 74 | SeqidVersion 75 | Setdiff 76 | SiteId 77 | SlideId 78 | SomaId 79 | SomaLogic 80 | SomaScan 81 | SomaScan’ 82 | SsfExtId 83 | Subarray 84 | Tabacman 85 | TargetFullName 86 | TimePoint 87 | TubeUniqueID 88 | Un 89 | UniProt 90 | YAML 91 | adat 92 | aliquot 93 | analyte 94 | analytes 95 | barcode 96 | checksums 97 | cli 98 | dplyr 99 | eLOD 100 | eSet 101 | frac 102 | funder 103 | ggplot 104 | gridlines 105 | homogenate 106 | intra 107 | leftrightarrow 108 | lifecycle 109 | lysate 110 | lysis 111 | magrittr 112 | md 113 | medNormRef 114 | nd 115 | normals 116 | pkgdown 117 | plex 118 | pre 119 | proteomic 120 | readxl 121 | rightarrow 122 | rowname 123 | rowwise 124 | rsample 125 | subarray 126 | tada 127 | th 128 | tibble 129 | tidyr 130 | untransformed 131 | vectorized 132 | -------------------------------------------------------------------------------- /inst/check-pkg-versions.R: -------------------------------------------------------------------------------- 1 | # ------------------ 2 | # check versions of: 3 | # 1) package required version ('version' -> DESCRIPTION) 4 | # 2) current installed version ('installed_version' -> system) 5 | # 3) latest available version ('latest_available' -> CRAN/BioC) 6 | # 7 | # * 'needs_update' if 'installed_version' < 'version' 8 | # ------------------ 9 | # Usage: 10 | # Rscript --vanilla inst/check-pkg-versions.R 11 | # ------------------ 12 | # Author: 13 | # Stu Field 14 | # ------------------ 15 | 16 | get_deps <- function(path = ".") { 17 | dcf <- read.dcf(file = file.path(path, "DESCRIPTION")) 18 | if ( nrow(dcf) < 1L) { 19 | stop("DESCRIPTION file of package is corrupt.", call. = FALSE) 20 | } 21 | desc <- as.list(dcf[1L, ])[c("Depends", "Imports", "Suggests")] 22 | lapply(desc, parse_deps) |> 23 | dplyr::bind_rows(.id = "type") 24 | } 25 | 26 | parse_deps <- function(deps) { 27 | deps <- trimws(strsplit(deps, ",")[[1L]]) 28 | deps <- lapply(strsplit(deps, "\\("), trimws) 29 | deps <- lapply(deps, sub, pattern = "\\)$", replacement = "") 30 | res <- data.frame(stringsAsFactors = FALSE, 31 | package = vapply(deps, "[", "", 1L), 32 | version = vapply(deps, "[", "", 2L) 33 | ) 34 | res$version <- gsub("\\s+", " ", res$version) 35 | res$version[is.na(res$version)] <- "*" 36 | res 37 | } 38 | 39 | tbl <- get_deps(".") 40 | 41 | tbl$installed_version <- sapply(tbl$package, function(.x) { 42 | if (.x == "R") 43 | paste(R.Version()[c("major", "minor")], collapse = ".") 44 | else 45 | utils::packageDescription(.x, fields = "Version") 46 | }, USE.NAMES = FALSE) 47 | tbl$installed_version <- package_version(tbl$installed_version) 48 | 49 | ver <- gsub("^[^0-9]+", "", tbl$version) 50 | ver[ver == ""] <- "0.0.0.0000" # set to '0' version if no specified version 51 | # if installed version less than DESCRIPTION (required) version -> must update 52 | tbl$needs_update <- tbl$installed_version < package_version(ver) 53 | 54 | latest_r <- readLines("https://cran.rstudio.com/bin/windows/base/release.html") 55 | latest_r <- grep("R-[0-9.]+.+-win\\.exe", latest_r, value = TRUE) 56 | latest_r <- strsplit(latest_r, "-")[[1L]] 57 | latest_r <- grep("[0-9][.][0-9][.][0-9]", latest_r, value = TRUE) 58 | 59 | cran <- utils::available.packages(repos = "https://cloud.r-project.org/") 60 | bioc <- utils::available.packages(repos = BiocManager::repositories(version = remotes::bioc_version())[1L]) 61 | 62 | cran_ver <- sapply(tbl$package, function(.x) { 63 | if (.x %in% rownames(cran)) { 64 | cran[.x, "Version"] 65 | } else if (.x %in% c("R", "methods")) { 66 | latest_r 67 | } else if (.x == "Biobase") { 68 | bioc["Biobase", "Version"] 69 | } 70 | }, USE.NAMES = FALSE) 71 | tbl$latest_available <- package_version(cran_ver) 72 | tbl <- tibble::as_tibble(tbl) 73 | tbl 74 | -------------------------------------------------------------------------------- /inst/cli/merge/merge_clin.R: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------- 2 | # Merge clinical variables into SomaScan data (ADAT) 3 | # 4 | # Command Line Interface (CLI) to merge/join clinical variables 5 | # in a *.csv file format into SomaScan data as an existing *.adat file 6 | # based on a common index key and write the resulting 7 | # object to a new ADAT file (*.adat). 8 | # 9 | # The index key can be 1 of 2 forms: 10 | # 1) a common index key to __both__ *.adat and *.csv 11 | # 2) an expression of the form `key1=key2` indicating the index key 12 | # to use for the *.adat (`key1`) and *.csv (`key2`) respectively 13 | # 14 | # Copyright: 15 | # Copyright (c) 2025 Standard BioTools, Inc. 16 | # Author: 17 | # Stu Field 18 | # 19 | # CLI Usage (4 args): 20 | # Rscript --vanilla merge_clin.R 21 | # 22 | # Examples: 23 | # Rscript --vanilla merge_clin.R example_data10.adat meta.csv SampleId foo.adat 24 | # Rscript --vanilla merge_clin.R example_data10.adat meta2.csv SampleId=ClinKey foo.adat 25 | # -------------------------------------------------- 26 | args <- commandArgs(trailingOnly = TRUE) 27 | stopifnot("`merge_clin.R` should be called with *4* arguments." = length(args) == 4L) 28 | key <- args[3L] 29 | if ( grepl("^\\S+=\\S+$", key) ) { 30 | key <- sub("=", "==", key) 31 | } 32 | key <- str2lang(key) 33 | join_key <- dplyr::join_by(!! key) 34 | path_x <- normalizePath(args[1L], mustWork = TRUE) 35 | path_y <- normalizePath(args[2L], mustWork = TRUE) 36 | adat <- SomaDataIO::merge_clin( 37 | x = SomaDataIO::read_adat(path_x), 38 | clin_data = path_y, 39 | by = join_key, 40 | by_class = setNames("character", join_key$y) 41 | ) 42 | SomaDataIO::write_adat(adat, file = args[4L]) 43 | -------------------------------------------------------------------------------- /inst/cli/merge/meta.csv: -------------------------------------------------------------------------------- 1 | SampleId,group,newvar 2 | 1,a,-0.757960 3 | 3,b,-0.363479 4 | 5,a,1.010235 5 | 7,b,1.342776 6 | 9,a,-3.010827 7 | -------------------------------------------------------------------------------- /inst/cli/merge/meta2.csv: -------------------------------------------------------------------------------- 1 | ClinKey,group,newvar 2 | 1,a,-0.757960 3 | 3,b,-0.363479 4 | 5,a,1.010235 5 | 7,b,1.342776 6 | 9,a,-3.010827 7 | -------------------------------------------------------------------------------- /man/SomaDataIO-deprecated.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/z-deprecated.R 3 | \name{SomaDataIO-deprecated} 4 | \alias{SomaDataIO-deprecated} 5 | \alias{getSomamers} 6 | \alias{getSomamerData} 7 | \title{Deprecated function(s) of the \pkg{SomaDataIO} package} 8 | \description{ 9 | These functions have either been 10 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} or 11 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 12 | in the current version of \pkg{SomaDataIO} package. 13 | They may eventually be completely removed, so 14 | please re-code your scripts accordingly based on the 15 | suggestions below: 16 | 17 | \tabular{lcr}{ 18 | \strong{Function} \tab \tab \strong{Now Use} \cr 19 | \code{\link[=getSomamers]{getSomamers()}} \tab \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} \tab \code{\link[=getAnalytes]{getAnalytes()}} \cr 20 | \code{\link[=getSomamerData]{getSomamerData()}} \tab \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} \tab \code{\link[=getAnalyteInfo]{getAnalyteInfo()}} \cr 21 | } 22 | } 23 | \details{ 24 | Some badges you may see in \pkg{SomaDataIO}: 25 | 26 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}} 27 | 28 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} 29 | 30 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}} 31 | 32 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#stable}{\figure{lifecycle-stable.svg}{options: alt='[Stable]'}}}{\strong{[Stable]}} 33 | } 34 | -------------------------------------------------------------------------------- /man/SomaDataIO-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/SomaDataIO-package.R 3 | \docType{package} 4 | \name{SomaDataIO-package} 5 | \alias{SomaDataIO} 6 | \alias{SomaDataIO-package} 7 | \title{SomaDataIO: Input/Output 'SomaScan' Data} 8 | \description{ 9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} 10 | 11 | Load and export 'SomaScan' data via the 'Standard BioTools, Inc.' structured text file called an ADAT ('*.adat'). For file format see \url{https://github.com/SomaLogic/SomaLogic-Data/blob/main/README.md}. The package also exports auxiliary functions for manipulating, wrangling, and extracting relevant information from an ADAT object once in memory. 12 | } 13 | \details{ 14 | Load an ADAT file into the global workspace with a call 15 | to \code{\link[=read_adat]{read_adat()}}. This function parses the main data 16 | table into a \code{data.frame} object and assigns the remaining data from 17 | the file as object \code{attributes}, i.e. call \code{attributes(adat)}. 18 | Other functions in the package are designed to make extracting, 19 | manipulating, and wrangling data in the newly created \link{soma_adat} 20 | object more convenient. 21 | 22 | Those familiar with micro-array data analysis and associated packages, e.g. 23 | \pkg{Biobase}, will notice that the feature data (proteins) are arranged as 24 | columns and the samples (arrays) are the rows. This is the 25 | transpose of typical micro-array data. This conflict can be easily solved 26 | using the transpose function, \code{\link[=t]{t()}}, which is part of the \verb{base R}. 27 | In addition, those familiar with the standard \code{ExpressionSet} object, 28 | available from \code{Bioconductor}, might find the functions \code{\link[=adat2eSet]{adat2eSet()}} and 29 | \code{\link[=pivotExpressionSet]{pivotExpressionSet()}} particularly useful. 30 | } 31 | \examples{ 32 | # a listing of all pkg functions 33 | library(help = SomaDataIO) 34 | 35 | # the `soma_adat` class 36 | class(example_data) 37 | is.soma_adat(example_data) 38 | 39 | # Annotations Lookup Table 40 | anno_tbl <- getAnalyteInfo(example_data) 41 | anno_tbl 42 | 43 | # Find all analytes starting with "MMP" in `anno_tbl` 44 | dplyr::filter(anno_tbl, grepl("^MMP", Target)) 45 | } 46 | \seealso{ 47 | Useful links: 48 | \itemize{ 49 | \item \url{https://somalogic.github.io/SomaDataIO/} 50 | \item \url{https://somalogic.com} 51 | \item Report bugs at \url{https://github.com/SomaLogic/SomaDataIO/issues} 52 | } 53 | 54 | } 55 | \author{ 56 | \strong{Maintainer}: Caleb Scheidel \email{calebjscheidel@gmail.com} 57 | 58 | Authors: 59 | \itemize{ 60 | \item Stu Field \email{stu.g.field@gmail.com} (\href{https://orcid.org/0000-0002-1024-5859}{ORCID}) 61 | } 62 | 63 | Other contributors: 64 | \itemize{ 65 | \item Standard BioTools, Inc. [copyright holder, funder] 66 | } 67 | 68 | } 69 | \keyword{internal} 70 | \keyword{package} 71 | -------------------------------------------------------------------------------- /man/adat2eSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/adat2eSet.R 3 | \name{adat2eSet} 4 | \alias{adat2eSet} 5 | \title{Convert ADAT to ExpressionSet Object} 6 | \usage{ 7 | adat2eSet(adat) 8 | } 9 | \arguments{ 10 | \item{adat}{A \code{soma_adat} class object as read into the R 11 | environment using \code{\link[=read_adat]{read_adat()}}.} 12 | } 13 | \value{ 14 | A Bioconductor object of class \code{ExpressionSet}. 15 | } 16 | \description{ 17 | Utility to convert a SomaLogic \code{soma_adat} object to an 18 | \code{ExpressionSet} object via the \pkg{Biobase} package 19 | from \strong{Bioconductor}: 20 | \url{https://www.bioconductor.org/packages/release/bioc/html/Biobase.html}. 21 | } 22 | \details{ 23 | The \pkg{Biobase} package is required and must be installed from 24 | \strong{Bioconductor} via the following at the R console: 25 | 26 | \if{html}{\out{
}}\preformatted{if (!requireNamespace("BiocManager", quietly = TRUE)) \{ 27 | install.packages("BiocManager") 28 | \} 29 | BiocManager::install("Biobase", version = remotes::bioc_version()) 30 | }\if{html}{\out{
}} 31 | } 32 | \examples{ 33 | \dontshow{if (rlang::is_installed("Biobase")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 34 | eSet <- adat2eSet(example_data) 35 | class(eSet) 36 | eSet 37 | 38 | ft <- Biobase::exprs(eSet) 39 | head(ft[, 1:10L], 10L) 40 | \dontshow{\}) # examplesIf} 41 | } 42 | \references{ 43 | \url{https://bioconductor.org/install/} 44 | } 45 | \seealso{ 46 | Other eSet: 47 | \code{\link{pivotExpressionSet}()} 48 | } 49 | \author{ 50 | Stu Field 51 | } 52 | \concept{eSet} 53 | -------------------------------------------------------------------------------- /man/addAttributes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/addAttributes.R 3 | \name{addAttributes} 4 | \alias{addAttributes} 5 | \title{Add Attributes to \code{soma_adat} Objects} 6 | \usage{ 7 | addAttributes(data, new.atts) 8 | } 9 | \arguments{ 10 | \item{data}{The \emph{receiving} \code{data.frame} object for new attributes.} 11 | 12 | \item{new.atts}{A \emph{named} \code{list} object containing new attributes 13 | to add to the existing ones.} 14 | } 15 | \value{ 16 | A data frame object corresponding to \code{data} but with the 17 | attributes of \code{new.atts} grafted on to it. 18 | Existing attribute names are \emph{not} over-written. 19 | } 20 | \description{ 21 | Adds a set of attributes, typically "Header.Meta" and "Col.Meta", 22 | to a \code{data.frame}, \code{tibble}, \code{soma_adat} or similar tabular object. 23 | Existing attributes \code{data} are \emph{not} over-written. 24 | Typically untouched are: 25 | \itemize{ 26 | \item \code{names} 27 | \item \code{class} 28 | \item \code{row.names} 29 | } 30 | } 31 | \seealso{ 32 | \code{\link[=attr]{attr()}}, \code{\link[=setdiff]{setdiff()}} 33 | } 34 | \author{ 35 | Stu Field 36 | } 37 | -------------------------------------------------------------------------------- /man/addClass.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/addClass.R 3 | \name{addClass} 4 | \alias{addClass} 5 | \title{Add a Class to an Object} 6 | \usage{ 7 | addClass(x, class) 8 | } 9 | \arguments{ 10 | \item{x}{The object to receive new class(es).} 11 | 12 | \item{class}{Character. The name of additional class(es).} 13 | } 14 | \value{ 15 | An object with new classes. 16 | } 17 | \description{ 18 | Utility to add (prepend) a class(es) to existing objects. 19 | } 20 | \examples{ 21 | class(iris) 22 | 23 | addClass(iris, "new") |> class() 24 | 25 | addClass(iris, c("A", "B")) |> class() # 2 classes 26 | 27 | addClass(iris, c("A", "data.frame")) |> class() # no duplicates 28 | 29 | addClass(iris, c("data.frame", "A")) |> class() # re-orders if exists 30 | } 31 | \seealso{ 32 | \code{\link[=class]{class()}}, \code{\link[=typeof]{typeof()}}, \code{\link[=structure]{structure()}} 33 | } 34 | \author{ 35 | Stu Field 36 | } 37 | -------------------------------------------------------------------------------- /man/calc_eLOD.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/calc_eLOD.R 3 | \name{calc_eLOD} 4 | \alias{calc_eLOD} 5 | \title{Calculate Estimated Limit of Detection (eLOD)} 6 | \usage{ 7 | calc_eLOD(data) 8 | } 9 | \arguments{ 10 | \item{data}{A \code{soma_adat}, \code{data.frame}, or \code{tibble} object including 11 | SeqId columns (\code{seq.xxxxx.xx}) containing RFU values.} 12 | } 13 | \value{ 14 | A \code{tibble} object with 2 columns: SeqId and eLOD. 15 | } 16 | \description{ 17 | Calculate the estimated limit of detection (eLOD) for SOMAmer reagent 18 | analytes in the provided input data. The input data should be filtered to 19 | include only buffer samples desired for eLOD calculation. 20 | } 21 | \details{ 22 | eLOD is calculated using the following steps: 23 | \enumerate{ 24 | \item For each SOMAmer, the median and adjusted median absolute 25 | deviation (\eqn{MAD_{Adjusted}}) are calculated, where 26 | \deqn{MAD_{Adjusted} = 1.4826 * MAD} 27 | The 1.4826 is a set constant used to adjust the MAD to be reflective of 28 | the standard deviation of the normal distribution. 29 | \item For each SOMAmer, calculate \deqn{eLOD = median + 3.3 * MAD_{Adjusted}} 30 | } 31 | 32 | Note: The eLOD is useful for non-core matrices, including cell lysate 33 | and CSF, but should be used carefully for evaluating background signal in 34 | plasma and serum. 35 | } 36 | \examples{ 37 | # filter data frame using vector of SampleId controls 38 | df <- withr::with_seed(101, { 39 | data.frame( 40 | SampleType = rep(c("Sample", "Buffer"), each = 10), 41 | SampleId = paste0("Sample_", 1:20), 42 | seq.20.1.100 = runif(20, 1, 100), 43 | seq.21.1.100 = runif(20, 1, 100), 44 | seq.22.2.100 = runif(20, 1, 100) 45 | ) 46 | }) 47 | sample_ids <- paste0("Sample_", 11:20) 48 | selected_samples <- df |> filter(SampleId \%in\% sample_ids) 49 | 50 | selected_elod <- calc_eLOD(selected_samples) 51 | head(selected_elod) 52 | \dontrun{ 53 | # filter `soma_adat` object to buffer samples 54 | buffer_samples <- example_data |> filter(SampleType == "Buffer") 55 | 56 | # calculate eLOD 57 | buffer_elod <- calc_eLOD(buffer_samples) 58 | head(buffer_elod) 59 | 60 | # use eLOD to calculate signal to noise ratio of samples 61 | samples_median <- example_data |> dplyr::filter(SampleType == "Sample") |> 62 | dplyr::summarise(across(starts_with("seq"), median, .names = "median_{col}")) |> 63 | tidyr::pivot_longer(starts_with("median_"), names_to = "SeqId", 64 | values_to = "median_signal") |> 65 | dplyr::mutate(SeqId = gsub("median_seq", "seq", SeqId)) 66 | 67 | # analytes with signal to noise > 2 68 | ratios <- samples_median |> 69 | dplyr::mutate(signal_to_noise = median_signal / buffer_elod$eLOD) |> 70 | dplyr::filter(signal_to_noise > 2) |> 71 | dplyr::arrange(desc(signal_to_noise)) 72 | 73 | head(ratios) 74 | } 75 | } 76 | \author{ 77 | Caleb Scheidel, Christopher Dimapasok 78 | } 79 | -------------------------------------------------------------------------------- /man/cleanNames.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cleanNames.R 3 | \name{cleanNames} 4 | \alias{cleanNames} 5 | \title{Clean Up Character String} 6 | \usage{ 7 | cleanNames(x) 8 | } 9 | \arguments{ 10 | \item{x}{Character. String to clean up.} 11 | } 12 | \value{ 13 | A cleaned up character string. 14 | } 15 | \description{ 16 | Often the names, particularly within \code{soma_adat} objects, 17 | are messy due to varying inputs, this function attempts to remedy this by 18 | removing the following: 19 | \itemize{ 20 | \item trailing/leading/internal whitespace 21 | \item non-alphanumeric strings (except underscores) 22 | \item duplicated internal dots (\code{..}), (\code{...}), etc. 23 | \item SomaScan normalization scale factor format 24 | } 25 | } 26 | \examples{ 27 | cleanNames(" sdkfj...sdlkfj.sdfii4994### ") 28 | 29 | cleanNames("Hyb..Scale") 30 | } 31 | \seealso{ 32 | \code{\link[=trimws]{trimws()}}, \code{\link[=gsub]{gsub()}}, \code{\link[=sub]{sub()}} 33 | } 34 | \author{ 35 | Stu Field 36 | } 37 | -------------------------------------------------------------------------------- /man/diffAdats.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diffAdats.R 3 | \name{diffAdats} 4 | \alias{diffAdats} 5 | \title{Diff Two ADAT Objects} 6 | \usage{ 7 | diffAdats(adat1, adat2, tolerance = 1e-06) 8 | } 9 | \arguments{ 10 | \item{adat1, adat2}{Two \code{soma_adat} objects to compare.} 11 | 12 | \item{tolerance}{Numeric \verb{> 0}. Differences smaller than tolerance are 13 | not triggered. See \code{\link[=all.equal]{all.equal()}}.} 14 | } 15 | \value{ 16 | \code{NULL}, invisibly. Called for side effects. 17 | } 18 | \description{ 19 | Diff tool for the differences between two \code{soma_adat} objects. 20 | When diffs of the table \emph{values} are interrogated, \strong{only} 21 | the intersect of the column meta data or feature data is considered 22 | } 23 | \note{ 24 | Only diffs of the column name \emph{intersect} are reported. 25 | } 26 | \examples{ 27 | # subset `example_data` for speed 28 | # all SeqIds from 2000 -> 2999 29 | seqs <- grep("^seq\\\\.2[0-9]{3}", names(example_data), value = TRUE) 30 | ex_data_small <- head(example_data[, c(getMeta(example_data), seqs)], 10L) 31 | dim(ex_data_small) 32 | 33 | # no diff to itself 34 | diffAdats(ex_data_small, ex_data_small) 35 | 36 | # remove random column 37 | rm <- withr::with_seed(123, sample(1:ncol(ex_data_small), 1)) 38 | diffAdats(ex_data_small, ex_data_small[, -rm]) 39 | 40 | # randomly shuffle Subarray 41 | diffAdats(ex_data_small, dplyr::mutate(ex_data_small, Subarray = sample(Subarray))) 42 | 43 | # modify 2 RFUs randomly 44 | new <- ex_data_small 45 | new[5L, c(rm, rm + 1L)] <- 999 46 | diffAdats(ex_data_small, new) 47 | } 48 | \author{ 49 | Stu Field 50 | } 51 | -------------------------------------------------------------------------------- /man/figures/lifecycle-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-soft-deprecated.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: soft-deprecated 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | soft-deprecated 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/lifecycle-stable.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: stable 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | lifecycle 21 | 22 | 25 | 26 | stable 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/figures/lifecycle-superseded.svg: -------------------------------------------------------------------------------- 1 | 2 | lifecycle: superseded 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | lifecycle 18 | 19 | superseded 20 | 21 | 22 | -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/man/figures/logo.png -------------------------------------------------------------------------------- /man/getAnalyteInfo.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getAnalyteInfo.R, R/getTargetNames.R, 3 | % R/z-deprecated.R 4 | \name{getAnalyteInfo} 5 | \alias{getAnalyteInfo} 6 | \alias{getTargetNames} 7 | \alias{getFeatureData} 8 | \title{Get Analyte Annotation Information} 9 | \usage{ 10 | getAnalyteInfo(adat) 11 | 12 | getTargetNames(tbl) 13 | 14 | getFeatureData(adat) 15 | } 16 | \arguments{ 17 | \item{adat}{A \code{soma_adat} object (with intact attributes), 18 | typically created using \code{\link[=read_adat]{read_adat()}}.} 19 | 20 | \item{tbl}{A \code{tibble} object containing analyte target annotation 21 | information. This is usually the result of a call to \code{\link[=getAnalyteInfo]{getAnalyteInfo()}}.} 22 | } 23 | \value{ 24 | A \code{tibble} object with columns corresponding 25 | to the column meta data entries in the \code{soma_adat}. One row per analyte. 26 | } 27 | \description{ 28 | Uses the \code{Col.Meta} attribute (analyte annotation data that appears above 29 | the protein measurements in the \verb{*.adat} text file) of a \code{soma_adat} object, 30 | adds the \code{AptName} column key, conducts a few sanity checks, and 31 | generates a "lookup table" of analyte data that can be used for simple 32 | manipulation and indexing of analyte annotation information. 33 | Most importantly, the analyte column names of the \code{soma_adat} 34 | (e.g. \code{seq.XXXX.XX}) become the \code{AptName} column of the lookup table and 35 | represents the key index between the table and \code{soma_adat} from which it comes. 36 | } 37 | \section{Functions}{ 38 | \itemize{ 39 | \item \code{getTargetNames()}: creates a lookup table (or dictionary) as a named list object of \code{AptNames} 40 | and Target names in key-value pairs. 41 | This is a convenient tool to quickly access a \code{TargetName} given 42 | the \code{AptName} in which the key-value pairs map the \code{seq.XXXX.XX} 43 | to its corresponding \code{TargetName} in \code{tbl}. 44 | This structure which provides a convenient auto-completion mechanism at 45 | the command line or for generating plot titles. 46 | 47 | \item \code{getFeatureData()}: \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}}. Please now use \code{\link[=getAnalyteInfo]{getAnalyteInfo()}}. 48 | 49 | }} 50 | \examples{ 51 | # Get Aptamer table 52 | anno_tbl <- getAnalyteInfo(example_data) 53 | anno_tbl 54 | 55 | # Use `dplyr::group_by()` 56 | dplyr::tally(dplyr::group_by(anno_tbl, Dilution)) # print summary by dilution 57 | 58 | # Columns containing "Target" 59 | anno_tbl |> 60 | dplyr::select(dplyr::contains("Target")) 61 | 62 | # Rows of "Target" starting with MMP 63 | anno_tbl |> 64 | dplyr::filter(grepl("^MMP", Target)) 65 | 66 | # Target names 67 | tg <- getTargetNames(anno_tbl) 68 | 69 | # how to use for plotting 70 | feats <- sample(anno_tbl$AptName, 6) 71 | op <- par(mfrow = c(2, 3)) 72 | sapply(feats, function(.x) plot(1:10, main = tg[[.x]])) 73 | par(op) 74 | } 75 | \seealso{ 76 | \code{\link[=getAnalytes]{getAnalytes()}}, \code{\link[=is_intact_attr]{is_intact_attr()}}, \code{\link[=read_adat]{read_adat()}} 77 | } 78 | \author{ 79 | Stu Field 80 | } 81 | -------------------------------------------------------------------------------- /man/getOutlierIds.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getOutlierIds.R 3 | \name{getOutlierIds} 4 | \alias{getOutlierIds} 5 | \title{Get Flagged Ids From MAD Outlier Map} 6 | \usage{ 7 | getOutlierIds(x, flags = 0.05, data = NULL, include = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{An object of class: 11 | \itemize{ 12 | \item \code{outlier_map} - from \code{\link[=calcOutlierMap]{calcOutlierMap()}} 13 | }} 14 | 15 | \item{flags}{Numeric in \verb{[0, 1]}. 16 | For an \code{"outlier_map"}, the proportion of the analytes (columns) 17 | for a given sample that must be outliers for a flag to be placed at the right-axis, 18 | right-axis, thus flagging that sample. 19 | If \code{NULL} (default), \code{0.05} (5\%) is selected.} 20 | 21 | \item{data}{Optional. The data originally used to create the map \code{x}. If 22 | omitted, a single column data frame is returned.} 23 | 24 | \item{include}{Optional. Character vector of column name(s) in \code{data} to 25 | include in the resulting data frame. Ignored if \code{data = NULL}.} 26 | } 27 | \value{ 28 | A \code{data.frame} of the indices (\code{idx}) of flagged samples, along 29 | with any additional variables as specified by \code{include}. 30 | } 31 | \description{ 32 | Return the IDs of flagged samples for objects of the \code{outlier_map} class. 33 | Samples are flagged based on the percent analytes (RFU columns) for a given 34 | sample that were identified as outliers using the median absolute 35 | deviation (MAD). 36 | } 37 | \examples{ 38 | # flagged outliers 39 | # create a single sample outlier (12) 40 | out_adat <- example_data 41 | apts <- getAnalytes(out_adat) 42 | out_adat[12, apts] <- out_adat[12, apts] * 10 43 | 44 | om <- calcOutlierMap(out_adat) 45 | getOutlierIds(om, out_adat, flags = 0.05, include = c("Sex", "Subarray")) 46 | } 47 | \seealso{ 48 | Other Calc Map: 49 | \code{\link{calcOutlierMap}()}, 50 | \code{\link{plot.Map}()} 51 | } 52 | \author{ 53 | Caleb Scheidel 54 | } 55 | \concept{Calc Map} 56 | -------------------------------------------------------------------------------- /man/is_intact_attr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/is-intact-attr.R 3 | \name{is_intact_attr} 4 | \alias{is_intact_attr} 5 | \alias{is.intact.attributes} 6 | \title{Are Attributes Intact?} 7 | \usage{ 8 | is_intact_attr(adat, verbose = interactive()) 9 | 10 | is.intact.attributes(adat, verbose = interactive()) 11 | } 12 | \arguments{ 13 | \item{adat}{A \code{soma_adat} object to query.} 14 | 15 | \item{verbose}{Logical. Should diagnostic information about failures 16 | be printed to the console? If the default, see \code{\link[=interactive]{interactive()}}, is invoked, 17 | only messages via direct calls are triggered. This prohibits messages 18 | generated deep in the call stack from bubbling up to the user.} 19 | } 20 | \value{ 21 | Logical. \code{TRUE} if all checks pass, otherwise \code{FALSE}. 22 | } 23 | \description{ 24 | This function runs a series of checks to determine 25 | if a \code{soma_adat} object has a complete 26 | set of attributes. If not, this indicates that the object has 27 | been modified since the initial \code{\link[=read_adat]{read_adat()}} call. 28 | Checks for the presence of both "Header.Meta" and "Col.Meta" in the 29 | attribute names. These entries are added during the 30 | \code{\link[=read_adat]{read_adat()}} call. Specifically, within these sections 31 | it also checks for the presence of the following entries: 32 | \describe{ 33 | \item{"Header.Meta" section:}{"HEADER", "COL_DATA", and "ROW_DATA"} 34 | \item{"Col.Meta" section:}{"SeqId", "Target", "Units", and "Dilution"} 35 | } 36 | If any of the above they are altered or missing, \code{FALSE} is returned. 37 | 38 | \code{\link[=is.intact.attributes]{is.intact.attributes()}} is \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}}. 39 | It remains for backward compatibility and may be removed in the future. 40 | You are encouraged to shift your code to \code{\link[=is_intact_attr]{is_intact_attr()}}. 41 | } 42 | \examples{ 43 | # checking attributes 44 | my_adat <- example_data 45 | is_intact_attr(my_adat) # TRUE 46 | is_intact_attr(my_adat[, -303L]) # doesn't break atts; TRUE 47 | attributes(my_adat)$Col.Meta$Target <- NULL # break attributes 48 | is_intact_attr(my_adat) # FALSE (Target missing) 49 | } 50 | \seealso{ 51 | \code{\link[=attributes]{attributes()}} 52 | } 53 | -------------------------------------------------------------------------------- /man/is_seqFormat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/is-seqFormat.R 3 | \name{is_seqFormat} 4 | \alias{is_seqFormat} 5 | \title{Test \code{AptName} Format} 6 | \usage{ 7 | is_seqFormat(x) 8 | } 9 | \arguments{ 10 | \item{x}{The object to be tested.} 11 | } 12 | \value{ 13 | A logical indicating whether \code{x} contains \code{AptNames} consistent 14 | with the new format, beginning with a \code{seq.} prefix. 15 | } 16 | \description{ 17 | Test whether an object is in the new \code{seq.XXXX.XX} format. 18 | } 19 | \examples{ 20 | # character S3 method 21 | is_seqFormat(names(example_data)) # no; meta data not ^seq. 22 | is_seqFormat(tail(names(example_data), -20L)) # yes 23 | 24 | # soma_adat S3 method 25 | is_seqFormat(example_data) 26 | } 27 | \author{ 28 | Stu Field, Eduardo Tabacman 29 | } 30 | -------------------------------------------------------------------------------- /man/loadAdatsAsList.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/loadAdatsAsList.R 3 | \name{loadAdatsAsList} 4 | \alias{loadAdatsAsList} 5 | \alias{collapseAdats} 6 | \title{Load ADAT files as a list} 7 | \usage{ 8 | loadAdatsAsList(files, collapse = FALSE, verbose = interactive(), ...) 9 | 10 | collapseAdats(x) 11 | } 12 | \arguments{ 13 | \item{files}{A character string of files to load.} 14 | 15 | \item{collapse}{Logical. Should the resulting list of ADATs be 16 | collapsed into a single ADAT object?} 17 | 18 | \item{verbose}{Logical. Should the function call be run in \emph{verbose} mode.} 19 | 20 | \item{...}{Additional arguments passed to \code{\link[=read_adat]{read_adat()}}.} 21 | 22 | \item{x}{A list of \code{soma_adat} class objects returned from 23 | \code{\link[=loadAdatsAsList]{loadAdatsAsList()}}.} 24 | } 25 | \value{ 26 | A list of ADATs named by \code{files}, each a \code{soma_adat} object 27 | corresponding to an individual file in \code{files}. For \code{\link[=collapseAdats]{collapseAdats()}}, 28 | a single, collapsed \code{soma_adat} object. 29 | } 30 | \description{ 31 | Load a series of ADATs and return a list of \code{soma_adat} 32 | objects, one for each ADAT file. 33 | \code{\link[=collapseAdats]{collapseAdats()}} concatenates a list of ADATs from \code{\link[=loadAdatsAsList]{loadAdatsAsList()}}, 34 | while maintaining the relevant attribute entries (mainly the \code{HEADER} 35 | element). This makes writing out the final object possible without the 36 | loss of \code{HEADER} information. 37 | } 38 | \details{ 39 | \describe{ 40 | \item{\strong{Note 1}:}{The default behavior is to "vertically bind" 41 | (\code{\link[=rbind]{rbind()}}) on the \emph{intersect} of the column variables, with 42 | unique columns silently dropped.} 43 | \item{\strong{Note 2}:}{If "vertically binding" on the column \emph{union} is 44 | desired, use \code{\link[dplyr:bind_rows]{dplyr::bind_rows()}}, however this results in \code{NAs} in 45 | non-intersecting columns. For many files with little variable 46 | intersection, a sparse RFU-matrix will result 47 | (and will likely break ADAT attributes): 48 | 49 | \if{html}{\out{
}}\preformatted{adats <- loadAdatsAsList(files) 50 | union_adat <- dplyr::bind_rows(adats, .id = "SourceFile") 51 | }\if{html}{\out{
}} 52 | 53 | } 54 | } 55 | } 56 | \examples{ 57 | # only 1 file in directory 58 | dir(system.file("extdata", package = "SomaDataIO")) 59 | 60 | files <- system.file("extdata", package = "SomaDataIO") |> 61 | dir(pattern = "[.]adat$", full.names = TRUE) |> rev() 62 | 63 | adats <- loadAdatsAsList(files) 64 | class(adats) 65 | 66 | # collapse into 1 ADAT 67 | collapsed <- collapseAdats(adats) 68 | class(collapsed) 69 | 70 | # Alternatively use `collapse = TRUE` 71 | \donttest{ 72 | loadAdatsAsList(files, collapse = TRUE) 73 | } 74 | } 75 | \seealso{ 76 | \code{\link[=read_adat]{read_adat()}} 77 | 78 | Other IO: 79 | \code{\link{parseHeader}()}, 80 | \code{\link{read_adat}()}, 81 | \code{\link{soma_adat}}, 82 | \code{\link{write_adat}()} 83 | } 84 | \author{ 85 | Stu Field 86 | } 87 | \concept{IO} 88 | -------------------------------------------------------------------------------- /man/params.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/params.R 3 | \name{params} 4 | \alias{params} 5 | \title{Common Parameters in \pkg{SomaDataIO}} 6 | \arguments{ 7 | \item{adat}{A \code{soma_adat} object (with intact attributes), 8 | typically created using \code{\link[=read_adat]{read_adat()}}.} 9 | 10 | \item{x}{A \code{soma_adat} object (with intact attributes), 11 | typically created using \code{\link[=read_adat]{read_adat()}}.} 12 | 13 | \item{matrix}{Character. A string of (usually) either 14 | \code{"serum"} or \code{"plasma"}.} 15 | } 16 | \value{ 17 | A \code{soma_adat} class object. 18 | } 19 | \description{ 20 | The parameters below are commonly used throughout 21 | the \pkg{SomaDataIO} package. 22 | } 23 | -------------------------------------------------------------------------------- /man/parseHeader.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/parseHeader.R 3 | \name{parseHeader} 4 | \alias{parseHeader} 5 | \title{SomaLogic ADAT parser} 6 | \usage{ 7 | parseHeader(file) 8 | } 9 | \arguments{ 10 | \item{file}{Character. The elaborated path and file name of the 11 | \verb{*.adat} file to be loaded into an R workspace environment.} 12 | } 13 | \value{ 14 | A list of relevant file information required by \code{\link[=read_adat]{read_adat()}} 15 | in order to complete loading the ADAT file, including: 16 | \item{Header.Meta}{list of notes and other information about the adat} 17 | \item{Col.Meta}{list of vectors that contain the column meta 18 | data about individual analytes, includes information about the target 19 | name and calibration and QC ratios} 20 | \item{file_specs}{list of values of the file parsing specifications} 21 | \item{row_meta}{character vector of the clinical variables; assay 22 | information that is included in the adat output along with the RFU data} 23 | } 24 | \description{ 25 | Parses the header section of an ADAT file. 26 | } 27 | \examples{ 28 | f <- system.file("extdata", "example_data10.adat", 29 | package = "SomaDataIO", mustWork = TRUE) 30 | header <- parseHeader(f) 31 | names(header) 32 | 33 | header$Header.Meta 34 | 35 | header$file_specs 36 | 37 | header$row_meta 38 | 39 | head(as.data.frame(header$Col.Meta)) 40 | } 41 | \seealso{ 42 | Other IO: 43 | \code{\link{loadAdatsAsList}()}, 44 | \code{\link{read_adat}()}, 45 | \code{\link{soma_adat}}, 46 | \code{\link{write_adat}()} 47 | } 48 | \author{ 49 | Stu Field 50 | } 51 | \concept{IO} 52 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils-pipe.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \arguments{ 10 | \item{lhs}{A value or the magrittr placeholder.} 11 | 12 | \item{rhs}{A function call using the magrittr semantics.} 13 | } 14 | \value{ 15 | The result of calling \code{rhs(lhs)}. 16 | } 17 | \description{ 18 | See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/pivotExpressionSet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/pivotExpressionSet.R, R/z-deprecated.R 3 | \name{pivotExpressionSet} 4 | \alias{pivotExpressionSet} 5 | \alias{meltExpressionSet} 6 | \title{Convert to Long Format} 7 | \usage{ 8 | pivotExpressionSet(eSet) 9 | 10 | meltExpressionSet(eSet) 11 | } 12 | \arguments{ 13 | \item{eSet}{An \code{ExpressionSet} class object, created using \code{\link[=adat2eSet]{adat2eSet()}}.} 14 | } 15 | \value{ 16 | A \code{tibble} consisting of the long format 17 | conversion of an \code{ExpressionSet} object. 18 | } 19 | \description{ 20 | Utility to convert an \code{ExpressionSet} class object 21 | from the "wide" data format to the "long" format via \code{\link[tidyr:pivot_longer]{tidyr::pivot_longer()}}. 22 | The \pkg{Biobase} package is required for this function. 23 | } 24 | \section{Functions}{ 25 | \itemize{ 26 | \item \code{meltExpressionSet()}: \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}}. Please now use \code{\link[=pivotExpressionSet]{pivotExpressionSet()}}. 27 | 28 | }} 29 | \examples{ 30 | \dontshow{if (rlang::is_installed("Biobase")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} 31 | # subset into a reduced mini-ADAT object 32 | # 10 samples (rows) 33 | # 5 clinical variables and 3 features (cols) 34 | sub_adat <- example_data[1:10, c(1:5, 35:37)] 35 | ex_set <- adat2eSet(sub_adat) 36 | 37 | # convert ExpressionSet object to long format 38 | adat_long <- pivotExpressionSet(ex_set) 39 | \dontshow{\}) # examplesIf} 40 | } 41 | \seealso{ 42 | Other eSet: 43 | \code{\link{adat2eSet}()} 44 | } 45 | \author{ 46 | Stu Field 47 | } 48 | \concept{eSet} 49 | -------------------------------------------------------------------------------- /man/read_adat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/read-adat.R 3 | \name{read_adat} 4 | \alias{read_adat} 5 | \alias{read.adat} 6 | \alias{is.soma_adat} 7 | \title{Read (Load) SomaLogic ADATs} 8 | \usage{ 9 | read_adat(file, debug = FALSE, verbose = getOption("verbose"), ...) 10 | 11 | read.adat(file, debug = FALSE, verbose = getOption("verbose"), ...) 12 | 13 | is.soma_adat(x) 14 | } 15 | \arguments{ 16 | \item{file}{Character. The elaborated path and file name of the \verb{*.adat} 17 | file to be loaded into an R workspace.} 18 | 19 | \item{debug}{Logical. Used for debugging and development of an ADAT that 20 | fails to load, particularly out-of-spec, poorly modified, or legacy ADATs.} 21 | 22 | \item{verbose}{Logical. Should the function call be run in \emph{verbose} 23 | mode, printing relevant diagnostic call information to the console.} 24 | 25 | \item{...}{Additional arguments passed ultimately to 26 | \code{\link[=read.delim]{read.delim()}}, or additional arguments passed to either 27 | other S3 print or summary methods as required by those generics.} 28 | 29 | \item{x}{An \code{R} object to test.} 30 | } 31 | \value{ 32 | A \code{data.frame}-like object of class \code{soma_adat} 33 | consisting of SomaLogic RFU (feature) data and clinical meta data as 34 | columns, and samples as rows. Row names are labeled with the unique ID 35 | "SlideId_Subarray" concatenation. The sections of the ADAT header (e.g., 36 | "Header.Meta", "Col.Meta", ...) are stored as attributes (e.g. 37 | \code{attributes(x)$Header.Meta}). 38 | 39 | Logical. Whether \code{x} inherits from class \code{soma_adat}. 40 | } 41 | \description{ 42 | The parse and load a \verb{*.adat} file as a \code{data.frame}-like object into 43 | an R workspace environment. The class of the returned object is 44 | a \code{soma_adat} object. 45 | 46 | \code{\link[=read.adat]{read.adat()}} is \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#superseded}{\figure{lifecycle-superseded.svg}{options: alt='[Superseded]'}}}{\strong{[Superseded]}}. 47 | For backward compatibility it will likely never go away completely, 48 | but you are strongly encouraged to shift your code to use \code{\link[=read_adat]{read_adat()}}. 49 | 50 | \code{\link[=is.soma_adat]{is.soma_adat()}} checks whether an object is of class \code{soma_adat}. 51 | See \code{\link[=inherits]{inherits()}}. 52 | } 53 | \examples{ 54 | # path to *.adat file 55 | # replace with your file path 56 | adat_path <- system.file("extdata", "example_data10.adat", 57 | package = "SomaDataIO", mustWork = TRUE) 58 | adat_path 59 | 60 | my_adat <- read_adat(adat_path) 61 | 62 | is.soma_adat(my_adat) 63 | } 64 | \seealso{ 65 | \code{\link[=read.delim]{read.delim()}} 66 | 67 | Other IO: 68 | \code{\link{loadAdatsAsList}()}, 69 | \code{\link{parseHeader}()}, 70 | \code{\link{soma_adat}}, 71 | \code{\link{write_adat}()} 72 | } 73 | \author{ 74 | Stu Field 75 | } 76 | \concept{IO} 77 | -------------------------------------------------------------------------------- /man/read_annotations.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/read-annotations.R 3 | \name{read_annotations} 4 | \alias{read_annotations} 5 | \title{Import a SomaLogic Annotations File} 6 | \usage{ 7 | read_annotations(file) 8 | } 9 | \arguments{ 10 | \item{file}{A path to an annotations file location. 11 | This is a sanctioned, versioned file provided by 12 | Standard BioTools, Inc. and should be an \emph{unmodified} 13 | \verb{*.xlsx} file.} 14 | } 15 | \value{ 16 | A \code{tibble} containing analyte-specific annotations and 17 | related (e.g. lift/bridging) information, keyed on SomaLogic 18 | \link{SeqId}, the unique SomaScan analyte identifier. 19 | } 20 | \description{ 21 | Import a SomaLogic Annotations File 22 | } 23 | \examples{ 24 | \dontrun{ 25 | # for example 26 | file <- "~/Downloads/SomaScan_11K_Annotated_Content.xlsx" 27 | anno_tbl <- read_annotations(file) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dplyr-reexports.R, R/tidyr-reexports.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{count} 7 | \alias{rename} 8 | \alias{slice_sample} 9 | \alias{slice} 10 | \alias{sample_frac} 11 | \alias{sample_n} 12 | \alias{filter} 13 | \alias{mutate} 14 | \alias{arrange} 15 | \alias{group_by} 16 | \alias{ungroup} 17 | \alias{left_join} 18 | \alias{anti_join} 19 | \alias{full_join} 20 | \alias{inner_join} 21 | \alias{semi_join} 22 | \alias{right_join} 23 | \alias{unite} 24 | \alias{separate} 25 | \title{Objects exported from other packages} 26 | \keyword{internal} 27 | \description{ 28 | These objects are imported from other packages. Follow the links 29 | below to see their documentation. 30 | 31 | \describe{ 32 | \item{dplyr}{\code{\link[dplyr:filter-joins]{anti_join}}, \code{\link[dplyr]{arrange}}, \code{\link[dplyr]{count}}, \code{\link[dplyr]{filter}}, \code{\link[dplyr:mutate-joins]{full_join}}, \code{\link[dplyr]{group_by}}, \code{\link[dplyr:mutate-joins]{inner_join}}, \code{\link[dplyr:mutate-joins]{left_join}}, \code{\link[dplyr]{mutate}}, \code{\link[dplyr]{rename}}, \code{\link[dplyr:mutate-joins]{right_join}}, \code{\link[dplyr:sample_n]{sample_frac}}, \code{\link[dplyr]{sample_n}}, \code{\link[dplyr:filter-joins]{semi_join}}, \code{\link[dplyr]{slice}}, \code{\link[dplyr:slice]{slice_sample}}, \code{\link[dplyr:group_by]{ungroup}}} 33 | 34 | \item{tidyr}{\code{\link[tidyr]{separate}}, \code{\link[tidyr]{unite}}} 35 | }} 36 | 37 | -------------------------------------------------------------------------------- /man/rownames.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rownames.R 3 | \name{rownames} 4 | \alias{rownames} 5 | \alias{rn2col} 6 | \alias{col2rn} 7 | \alias{has_rn} 8 | \alias{rm_rn} 9 | \alias{set_rn} 10 | \alias{add_rowid} 11 | \title{Helpers for Working With Row Names} 12 | \usage{ 13 | rn2col(data, name = ".rn") 14 | 15 | col2rn(data, name = ".rn") 16 | 17 | has_rn(data) 18 | 19 | rm_rn(data) 20 | 21 | set_rn(data, value) 22 | 23 | add_rowid(data, name = ".rowid") 24 | } 25 | \arguments{ 26 | \item{data}{An object that inherits from class \code{data.frame}. Typically 27 | a \code{soma_adat} class object.} 28 | 29 | \item{name}{Character. The name of the column to move.} 30 | 31 | \item{value}{Character. The new set of names for the data frame. 32 | If duplicates exist they are modified on-the-fly via \code{\link[=make.unique]{make.unique()}}.} 33 | } 34 | \value{ 35 | All functions attempt to return an object of the same class as 36 | the input with fully intact and unmodified attributes (aside from those 37 | required by the desired action). \code{\link[=has_rn]{has_rn()}} returns a scalar logical. 38 | } 39 | \description{ 40 | Easily move row names to a column and vice-versa without the unwanted 41 | side-effects to object class and attributes. Drop-in replacement for 42 | \code{tibble::rownames_to_column()} and \code{tibble::column_to_rownames()} which 43 | can have undesired side-effects to complex object attributes. 44 | Does not import any external packages, modify the environment, or change 45 | the object (other than the desired column). When using \code{\link[=col2rn]{col2rn()}}, if 46 | explicit row names exist, they are overwritten with a warning. \code{\link[=add_rowid]{add_rowid()}} 47 | does \emph{not} affect row names, which differs from \code{tibble::rowid_to_column()}. 48 | } 49 | \section{Functions}{ 50 | \itemize{ 51 | \item \code{rn2col()}: moves the row names of \code{data} to an explicit column 52 | whether they are explicit or implicit. 53 | 54 | \item \code{col2rn()}: is the inverse of \code{\link[=rn2col]{rn2col()}}. If row names exist, they 55 | will be overwritten (with warning). 56 | 57 | \item \code{has_rn()}: returns a boolean indicating whether the data frame 58 | has explicit row names assigned. 59 | 60 | \item \code{rm_rn()}: removes existing row names, leaving only "implicit" row names. 61 | 62 | \item \code{set_rn()}: sets (and overwrites) existing row names for data frames only. 63 | 64 | \item \code{add_rowid()}: adds a sequential integer row identifier; starting at \code{1:nrow(data)}. 65 | It does \emph{not} remove existing row names currently, but may in the future 66 | (please code accordingly). 67 | 68 | }} 69 | \examples{ 70 | df <- data.frame(a = 1:5, b = rnorm(5), row.names = LETTERS[1:5]) 71 | df 72 | rn2col(df) # default name is `.rn` 73 | rn2col(df, "AptName") # pass `name =` 74 | 75 | # moving columns 76 | df$mtcars <- sample(names(mtcars), 5) 77 | col2rn(df, "mtcars") # with a warning 78 | 79 | # Move back and forth easily 80 | # Leaves original object un-modified 81 | identical(df, col2rn(rn2col(df))) 82 | 83 | # add "id" column 84 | add_rowid(mtcars) 85 | 86 | # remove row names 87 | has_rn(mtcars) 88 | mtcars2 <- rm_rn(mtcars) 89 | has_rn(mtcars2) 90 | } 91 | -------------------------------------------------------------------------------- /man/transform.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/s3-transform.R 3 | \name{transform} 4 | \alias{transform} 5 | \alias{transform.soma_adat} 6 | \title{Scale Transform \code{soma_adat} Columns/Rows} 7 | \usage{ 8 | \method{transform}{soma_adat}(`_data`, v, dim = 2L, ...) 9 | } 10 | \arguments{ 11 | \item{_data}{A \code{soma_adat} object.} 12 | 13 | \item{v}{A numeric vector of the appropriate length corresponding to \code{dim}.} 14 | 15 | \item{dim}{Integer. The dimension to apply elements of \code{v} to. 16 | \code{1} = rows; \code{2} = columns (default).} 17 | 18 | \item{...}{Currently not used but required by the S3 generic.} 19 | } 20 | \value{ 21 | A modified value of \verb{_data} with either the rows or columns 22 | linearly transformed by \code{v}. 23 | } 24 | \description{ 25 | Scale the \emph{i-th} row or column of a \code{soma_adat} object by the \emph{i-th} 26 | element of a vector. Designed to facilitate linear transformations 27 | of \emph{only} the analyte/RFU entries by scaling the data matrix. 28 | If scaling the analytes/RFU (columns), \code{v} \emph{must} have 29 | \code{getAnalytes(adat, n = TRUE)} elements. 30 | If scaling the samples (rows), \code{v} \emph{must} 31 | have \verb{nrow(_data)} elements. 32 | } 33 | \details{ 34 | Performs the following operations (quickly): 35 | 36 | Columns: 37 | \deqn{ 38 | M_{nxp} = A_{nxp} * diag(v)_{pxp} 39 | } 40 | 41 | Rows: 42 | \deqn{ 43 | M_{nxp} = diag(v)_{nxn} * A_{nxp} 44 | } 45 | } 46 | \note{ 47 | This method in intentionally naive, and assumes the user has 48 | ordered \code{v} to match the columns/rows of \verb{_data} appropriately. 49 | This must be done upstream. 50 | } 51 | \examples{ 52 | # simplified example of underlying operations 53 | M <- matrix(1:12, ncol = 4) 54 | M 55 | 56 | v <- 1:4 57 | M \%*\% diag(v) # transform columns 58 | 59 | v <- 1:3 60 | diag(v) \%*\% M # transform rows 61 | 62 | # dummy ADAT example: 63 | v <- c(2, 0.5) # double seq1; half seq2 64 | adat <- data.frame(sample = paste0("sample_", 1:3), 65 | seq.1234.56 = c(1, 2, 3), 66 | seq.9999.88 = c(4, 5, 6) * 10) 67 | adat 68 | 69 | # `soma_adat` to invoke S3 method dispatch 70 | class(adat) <- c("soma_adat", "data.frame") 71 | trans <- transform(adat, v) 72 | data.frame(trans) 73 | } 74 | \seealso{ 75 | \code{\link[=apply]{apply()}}, \code{\link[=sweep]{sweep()}} 76 | } 77 | -------------------------------------------------------------------------------- /man/write_adat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/write-adat.R 3 | \name{write_adat} 4 | \alias{write_adat} 5 | \title{Write an ADAT to File} 6 | \usage{ 7 | write_adat(x, file) 8 | } 9 | \arguments{ 10 | \item{x}{A \code{soma_adat} object (with intact attributes), 11 | typically created using \code{\link[=read_adat]{read_adat()}}.} 12 | 13 | \item{file}{Character. File path where the object should be written. 14 | For example, extensions should be \verb{*.adat}.} 15 | } 16 | \value{ 17 | Invisibly returns the input \code{x}. 18 | } 19 | \description{ 20 | One can write an existing modified internal ADAT 21 | (\code{soma_adat} R object) to an external file. 22 | However the ADAT object itself \emph{must} have intact 23 | attributes, see \code{\link[=is_intact_attr]{is_intact_attr()}}. 24 | } 25 | \details{ 26 | The ADAT specification \emph{no longer} requires Windows 27 | end of line (EOL) characters (\verb{"\r\n"}). 28 | The current EOL spec is \verb{"\n"} which is commonly used in POSIX systems, 29 | like MacOS and Linux. 30 | Since the EOL affects the resulting checksum, ADATs written on 31 | other systems generate slightly differing files. 32 | Standardizing to \verb{"\n"} attempts to solve this issue. 33 | For reference, see the EOL encoding for operating systems below:\cr 34 | \tabular{llc}{ 35 | Symbol \tab Platform \tab Character \cr 36 | LF \tab Linux \tab \verb{"\n"} \cr 37 | CR \tab MacOS \tab \verb{"\r"} \cr 38 | CRLF \tab DOS/Windows \tab \verb{"\r\n"} 39 | } 40 | } 41 | \examples{ 42 | # trim to 1 sample for speed 43 | adat_out <- head(example_data, 1L) 44 | 45 | # attributes must(!) be intact to write 46 | is_intact_attr(adat_out) 47 | 48 | write_adat(adat_out, file = tempfile(fileext = ".adat")) 49 | } 50 | \seealso{ 51 | \code{\link[=read_adat]{read_adat()}}, \code{\link[=is_intact_attr]{is_intact_attr()}} 52 | 53 | Other IO: 54 | \code{\link{loadAdatsAsList}()}, 55 | \code{\link{parseHeader}()}, 56 | \code{\link{read_adat}()}, 57 | \code{\link{soma_adat}} 58 | } 59 | \author{ 60 | Stu Field 61 | } 62 | \concept{IO} 63 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if ( requireNamespace("spelling", quietly = TRUE) ) { 2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE, skip_on_cran = TRUE) 3 | } 4 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(SomaDataIO) 3 | 4 | test_check("SomaDataIO") 5 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/S3-median.md: -------------------------------------------------------------------------------- 1 | # the `median.soma_adat()` method trips correct warning 2 | 3 | Code 4 | median(adat) 5 | Condition 6 | Warning: 7 | As with the `data.frame` class, numeric data is required for `stats::median()`. 8 | Please use either: 9 | 10 | `median(data.matrix(x[, getAnalytes(x)]))` 11 | OR 12 | `apply(x[, getAnalytes(x)] 2, median)` 13 | 14 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/S3-merge.md: -------------------------------------------------------------------------------- 1 | # `merge.soma_adat()` triggers the expected lifecycle error messages 2 | 3 | Using `merge()` on a `soma_adat` was deprecated in SomaDataIO 6.0.0 and is now defunct. 4 | i Please use any of the `dplyr::*_join()` alternatives instead. 5 | i Perhaps `dplyr::left_join(x = data, y = merge_data, by = "SampleId")`? 6 | 7 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/S3-summary.md: -------------------------------------------------------------------------------- 1 | # the printed output is as expected 2 | 3 | seq.1234.56 seq.9898.99 4 | Target : MMP-1 Target : MMP-3 5 | Min : 1934.8 Min : 3228.8 6 | 1Q : 2153.6 1Q : 3655.8 7 | Median : 2513.1 Median : 3821.3 8 | Mean : 2483.1 Mean : 3790.5 9 | 3Q : 2585.9 3Q : 3920.3 10 | Max : 3293.9 Max : 4317.8 11 | sd : 482.4 sd : 361.6 12 | MAD : 401.0 MAD : 250.0 13 | IQR : 432.3 IQR : 264.5 14 | 15 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/getTargetNames.md: -------------------------------------------------------------------------------- 1 | # `getTargetNames()` S3 print method for `target_map` snapshots 2 | 3 | == AptName-Target Lookup Map =================================================== 4 | # A tibble: 5,284 x 2 5 | AptName Target 6 | 7 | 1 seq.10000.28 Beta-crystallin B2 8 | 2 seq.10001.7 RAF proto-oncogene serine/threonine-protein kinase 9 | 3 seq.10003.15 Zinc finger protein 41 10 | 4 seq.10006.25 ETS domain-containing protein Elk-1 11 | 5 seq.10008.43 Guanylyl cyclase-activating protein 1 12 | 6 seq.10011.65 Inositol polyphosphate 5-phosphatase OCRL-1 13 | 7 seq.10012.5 SAM pointed domain-containing Ets transcription factor 14 | 8 seq.10013.34 Fc_MOUSE 15 | 9 seq.10014.31 Zinc finger protein SNAI2 16 | 10 seq.10015.119 Voltage-gated potassium channel subunit beta-2 17 | # i 5,274 more rows 18 | ================================================================================ 19 | 20 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/groupGenerics.md: -------------------------------------------------------------------------------- 1 | # the `Ops()` group generic generates the expected output 2 | 3 | Code 4 | expect_error(adat == adat, NA) 5 | Output 6 | == Checking ADAT attributes & characteristics ================================== 7 | Message 8 | > Attribute names are identical v 9 | > Attributes are identical v 10 | > ADAT dimensions are identical v 11 | > ADAT row names are identical v 12 | > ADATs contain identical Features v 13 | > ADATs contain same Meta Fields v 14 | Output 15 | -- Checking the data matrix ---------------------------------------------------- 16 | Message 17 | > All Clinical data is identical v 18 | > All Feature data is identical v 19 | Output 20 | ================================================================================ 21 | 22 | # error conditions generate the expected output for deprecated `soma.adat` 23 | 24 | Code 25 | readLines(catfile) 26 | Output 27 | [1] "The 'soma.adat' class is now 'soma_adat' ." 28 | [2] " Please either:" 29 | [3] " 1) Re-class with x <- addClass(x, 'soma_adat')" 30 | [4] " 2) Re-call 'x <- read_adat(file)' to pick up the new 'soma_adat' class." 31 | 32 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/is-intact-attr.md: -------------------------------------------------------------------------------- 1 | # user defined `verbose =` param overrides internal logic as expected 2 | 3 | Code 4 | is_intact_attr(iris, verbose = TRUE) 5 | Message 6 | x The object is not a `soma_adat` class object: "data.frame" 7 | Output 8 | [1] FALSE 9 | 10 | --- 11 | 12 | Code 13 | is_intact_attr(iris, verbose = FALSE) 14 | Output 15 | [1] FALSE 16 | 17 | # verbosity is triggered only when called directly 18 | 19 | Code 20 | is_intact_attr(iris) 21 | Message 22 | x The object is not a `soma_adat` class object: "data.frame" 23 | Output 24 | [1] FALSE 25 | 26 | --- 27 | 28 | Code 29 | f1(iris) 30 | Output 31 | [1] FALSE 32 | 33 | --- 34 | 35 | Code 36 | f2(iris) 37 | Output 38 | [1] FALSE 39 | 40 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/preProcessAdat.md: -------------------------------------------------------------------------------- 1 | # `preProcessAdat` applies default arguments as expected 2 | 3 | Code 4 | preProcessAdat(example_data) 5 | Message 6 | v 305 non-human protein features were removed. 7 | > 214 human proteins did not pass standard QC 8 | acceptance criteria and were flagged in `ColCheck`. These features 9 | were not removed, as they still may yield useful information in an 10 | analysis, but further evaluation may be needed. 11 | v 6 buffer samples were removed. 12 | v 10 calibrator samples were removed. 13 | v 6 QC samples were removed. 14 | v 2 samples flagged in `RowCheck` did not 15 | pass standard normalization acceptance criteria (0.4 <= x <= 2.5) 16 | and were removed. 17 | Output 18 | == SomaScan Data =============================================================== 19 | SomaScan version V4 (5k) 20 | Signal Space 5k 21 | Attributes intact v 22 | Rows 168 23 | Columns 5013 24 | Clinical Data 34 25 | Features 4979 26 | -- Column Meta ----------------------------------------------------------------- 27 | i SeqId, SeqIdVersion, SomaId, TargetFullName, Target, UniProt, EntrezGeneID, 28 | i EntrezGeneSymbol, Organism, Units, Type, Dilution, PlateScale_Reference, 29 | i CalReference, Cal_Example_Adat_Set001, ColCheck, 30 | i CalQcRatio_Example_Adat_Set001_170255, QcReference_170255, 31 | i Cal_Example_Adat_Set002, CalQcRatio_Example_Adat_Set002_170255, Dilution2 32 | -- Tibble ---------------------------------------------------------------------- 33 | # A tibble: 168 x 5,014 34 | row_names PlateId PlateRunDate ScannerID PlatePosition SlideId Subarray 35 | 36 | 1 258495800012_3 Example~ 2020-06-18 SG152144~ H9 2.58e11 3 37 | 2 258495800004_7 Example~ 2020-06-18 SG152144~ H8 2.58e11 7 38 | 3 258495800010_8 Example~ 2020-06-18 SG152144~ H7 2.58e11 8 39 | 4 258495800003_4 Example~ 2020-06-18 SG152144~ H6 2.58e11 4 40 | 5 258495800009_4 Example~ 2020-06-18 SG152144~ H5 2.58e11 4 41 | 6 258495800012_8 Example~ 2020-06-18 SG152144~ H4 2.58e11 8 42 | 7 258495800001_3 Example~ 2020-06-18 SG152144~ H3 2.58e11 3 43 | 8 258495800004_8 Example~ 2020-06-18 SG152144~ H2 2.58e11 8 44 | 9 258495800001_8 Example~ 2020-06-18 SG152144~ H12 2.58e11 8 45 | 10 258495800009_8 Example~ 2020-06-18 SG152144~ H10 2.58e11 8 46 | # i 158 more rows 47 | # i 5,007 more variables: SampleId , SampleType , 48 | # PercentDilution , SampleMatrix , Barcode , Barcode2d , 49 | # SampleName , SampleNotes , AliquotingNotes , 50 | # SampleDescription , ... 51 | ================================================================================ 52 | 53 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/preProcessAdat/preProcessAdat_qc_plot_Age.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/tests/testthat/_snaps/preProcessAdat/preProcessAdat_qc_plot_Age.png -------------------------------------------------------------------------------- /tests/testthat/_snaps/preProcessAdat/preProcessAdat_qc_plot_Sex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/tests/testthat/_snaps/preProcessAdat/preProcessAdat_qc_plot_Sex.png -------------------------------------------------------------------------------- /tests/testthat/_snaps/scaleAnalytes.md: -------------------------------------------------------------------------------- 1 | # no matches returns identical object, with a 1 message & 2 warnings 2 | 3 | Code 4 | new <- scaleAnalytes(short_adat, ref) 5 | Message 6 | x No matches between lists 7 | Condition 8 | Warning: 9 | Missing scalar value for (3) analytes. They will not be transformed. 10 | Please check the reference or its named SeqIds. 11 | Warning: 12 | There are extra scaling values (1) in the reference. 13 | They will be ignored. 14 | 15 | # `scaleAnalytes()` only accepts the `soma_adat` class 16 | 17 | Code 18 | scaleAnalytes(bad_adat) 19 | Condition 20 | Error: 21 | ! `scaleAnalytes()` must be called on a 'soma_adat' object, not a 'data.frame'. 22 | Perhaps: `scaleAnalytes(.data = addClass(bad_adat, "soma_adat"))`? 23 | 24 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/utils-read-adat.md: -------------------------------------------------------------------------------- 1 | # `checkHeader()` prints expected output 2 | 3 | Code 4 | checkHeader(header, verbose = TRUE) 5 | Message 6 | v Header passed checks and traps 7 | 8 | # .verbosity()` prints expected output 9 | 10 | Code 11 | .verbosity(adat, header) 12 | Output 13 | == Parsing Diagnostics ========================================================= 14 | Message 15 | v ADAT version > 1.2 16 | v Header skip > 41 17 | v Table begin > 20 18 | v Col.Meta start > 21 19 | v Col.Meta shift > 35 20 | v Is old ADAT > FALSE 21 | v no. clinical variables > 34 22 | v no. RFU variables > 5284 23 | v Dim data matrix > 1 x 5318 24 | v Dim Col.Meta (annot.) > 5284 x 20 25 | Output 26 | -- Head Col Meta --------------------------------------------------------------- 27 | # A tibble: 6 x 20 28 | SeqId SeqIdVersion SomaId TargetFullName Target UniProt EntrezGeneID 29 | 30 | 1 10000-28 3 SL019233 Beta-crystallin B2 CRBB2 P43320 1415 31 | 2 10001-7 3 SL002564 RAF proto-oncogene~ c-Raf P04049 5894 32 | 3 10003-15 3 SL019245 Zinc finger protei~ ZNF41 P51814 7592 33 | 4 10006-25 3 SL019228 ETS domain-contain~ ELK1 P19419 2002 34 | 5 10008-43 3 SL019234 Guanylyl cyclase-a~ GUC1A P43080 2978 35 | 6 10011-65 3 SL019246 Inositol polyphosp~ OCRL Q01968 4952 36 | # i 13 more variables: EntrezGeneSymbol , Organism , Units , 37 | # Type , Dilution , PlateScale_Reference , CalReference , 38 | # Cal_Example_Adat_Set001 , ColCheck , 39 | # CalQcRatio_Example_Adat_Set001_170255 , QcReference_170255 , 40 | # Cal_Example_Adat_Set002 , CalQcRatio_Example_Adat_Set002_170255 41 | -- Trailing 2 RFU features ----------------------------------------------------- 42 | # A tibble: 1 x 2 43 | seq.9997.12 seq.9999.1 44 | 45 | 1 11983. 1741. 46 | ================================================================================ 47 | 48 | -------------------------------------------------------------------------------- /tests/testthat/test-S3-median.R: -------------------------------------------------------------------------------- 1 | 2 | adat <- mock_adat() 3 | 4 | test_that("the `median.soma_adat()` method trips correct warning", { 5 | expect_snapshot(median(adat)) 6 | }) 7 | 8 | test_that("the `median.soma_adat()` method invisibly returns `NULL`", { 9 | withr::local_options(list(warn = -1)) # turn off warnings tests 10 | expect_invisible(median(adat)) 11 | expect_null(median(adat)) 12 | }) 13 | -------------------------------------------------------------------------------- /tests/testthat/test-S3-merge.R: -------------------------------------------------------------------------------- 1 | 2 | # Testing ---- 3 | test_that("`merge.soma_adat()` triggers the expected lifecycle error messages", { 4 | data <- mock_adat() 5 | merge_data <- data.frame(SampleId = sample(data$SampleId), NewData = 6:1) 6 | # check lifecycle defunct is dispatched 7 | lifecycle::expect_defunct( merge(data, merge_data) ) 8 | # check that error is triggered and messages are delivered 9 | expect_snapshot_error( merge(data, merge_data, by = "SampleId") ) 10 | }) 11 | -------------------------------------------------------------------------------- /tests/testthat/test-S3-print.R: -------------------------------------------------------------------------------- 1 | 2 | # Note ---- 3 | # Remote machines sometimes don't like the fancy UTF-8 unicode symbols 4 | # set to FALSE to enable ASCII fallbacks; see `testthat::local_test_context()` 5 | # unit tests for print output also don't colors/bold styles {from `pillar`} 6 | # Turn all these off for the unit testing context 7 | testthat::local_reproducible_output() 8 | adat <- example_data 9 | 10 | # Testing ---- 11 | test_that("`soma_adat` S3 print method returns expected default output", { 12 | # default 13 | expect_snapshot_output(adat) 14 | }) 15 | 16 | test_that("`soma_adat` S3 print method returns expected head output", { 17 | # head 18 | expect_snapshot_output(head(adat)) 19 | }) 20 | 21 | test_that("`soma_adat` S3 print method returns expected `show_header = TRUE` output", { 22 | # show_header is TRUE 23 | expect_snapshot_output(print(adat, show_header = TRUE)) 24 | }) 25 | 26 | test_that("`soma_adat` S3 print method returns expected `grouped_df` output", { 27 | # grouped_df 28 | grouped_adat <- dplyr::group_by(adat, SampleType) 29 | expect_snapshot_output(grouped_adat) 30 | }) 31 | 32 | test_that("`soma_adat` S3 print method returns expected broken attributes output", { 33 | # break atts 34 | attr(adat, "Header.Meta") <- NULL 35 | expect_false(is_intact_attr(adat, verbose = FALSE)) 36 | expect_snapshot_output(adat) 37 | }) 38 | -------------------------------------------------------------------------------- /tests/testthat/test-S3-summary.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | adat <- mock_adat() 4 | 5 | # get 2 analytes 6 | apts <- c("seq.1234.56", "seq.9898.99") 7 | summ <- summary(adat[, apts]) 8 | 9 | # Testing ---- 10 | test_that("summary method returns correct object", { 11 | expect_s3_class(summ, "adat_summary") 12 | expect_length(summ, length(apts)) 13 | expect_equal(dim(summ), c(10, length(apts))) 14 | expect_equal(dimnames(summ)[[2L]], apts) 15 | }) 16 | 17 | test_that("summary method returns correct values", { 18 | true <- data.frame( 19 | seq.1234.56 = c("Target : MMP-1 ", 20 | "Min : 1934.8 ", 21 | "1Q : 2153.6 ", 22 | "Median : 2513.1 ", 23 | "Mean : 2483.1 ", 24 | "3Q : 2585.9 ", 25 | "Max : 3293.9 ", 26 | "sd : 482.4 ", 27 | "MAD : 401.0 ", 28 | "IQR : 432.3 "), 29 | seq.9898.99 = c("Target : MMP-3 ", 30 | "Min : 3228.8 ", 31 | "1Q : 3655.8 ", 32 | "Median : 3821.3 ", 33 | "Mean : 3790.5 ", 34 | "3Q : 3920.3 ", 35 | "Max : 4317.8 ", 36 | "sd : 361.6 ", 37 | "MAD : 250.0 ", 38 | "IQR : 264.5 ") 39 | ) |> addClass("adat_summary") 40 | expect_equal(summ, true) 41 | }) 42 | 43 | test_that("summary method returns correct values when annotations tbl is passed", { 44 | anno <- getAnalyteInfo(adat) 45 | summ2 <- summary(adat[, apts], anno) 46 | expect_equal(summ, summ2) 47 | }) 48 | 49 | test_that("the printed output is as expected", { 50 | withr::with_options(list(width = 50), expect_snapshot_output(summ)) 51 | }) 52 | -------------------------------------------------------------------------------- /tests/testthat/test-S3-transform.R: -------------------------------------------------------------------------------- 1 | 2 | # this is to ensure the S3 method is available and dispatched 3 | # otherwise the base::transform.data.frame() method will not 4 | # transform the analytes inside scaleAnalytes() 5 | 6 | test_that("the transform S3 method exists in the namespace", { 7 | expect_error(getS3method("transform", "soma_adat"), NA) # expect no error 8 | }) 9 | 10 | test_that("the transform S3 method is listed in methods", { 11 | methods <- unclass(methods("transform", "soma_adat")) 12 | expect_true("transform.soma_adat" %in% methods) 13 | }) 14 | 15 | # dummy ADAT: 16 | # double seq1; half seq2 17 | v <- c(2, 0.5) 18 | df <- data.frame( 19 | sample = paste0("sample_", 1:3), 20 | seq.1234.56 = c(1, 2, 3), 21 | seq.9999.88 = c(4, 5, 6) * 10 22 | ) |> # `soma_adat` to invoke S3 method dispatch 23 | addClass("soma_adat") 24 | 25 | test_that("`transform()` dispatches the `soma_adat` method and correctly scales (cols)", { 26 | trans <- transform(df, v) 27 | expect_s3_class(trans, "soma_adat") 28 | expect_equal(trans$seq.1234.56, df$seq.1234.56 * 2) 29 | expect_equal(trans$seq.9999.88, df$seq.9999.88 * 0.5) 30 | expect_named(trans, names(df)) 31 | }) 32 | 33 | test_that("`transform()` dispatches the `soma_adat` method and correctly scales (rows)", { 34 | vec <- c(1, 2, 3) 35 | trans <- transform(df, vec, 1L) 36 | expect_s3_class(trans, "soma_adat") 37 | expect_equal(trans$seq.1234.56, df$seq.1234.56 * vec) 38 | expect_equal(trans$seq.9999.88, df$seq.9999.88 * vec) 39 | }) 40 | -------------------------------------------------------------------------------- /tests/testthat/test-adat2eSet.R: -------------------------------------------------------------------------------- 1 | 2 | if ( rlang::is_installed("Biobase") ) { 3 | suppressPackageStartupMessages(library(Biobase)) 4 | sub_adat <- example_data[1:10L, c(1:5L, 35:37L)] 5 | } 6 | 7 | 8 | 9 | test_that("`adat2eSet()` converts `soma_adt` -> eSet correctly", { 10 | testthat::skip_if_not_installed("Biobase") 11 | eSet <- adat2eSet(sub_adat) 12 | expect_s4_class(eSet, "ExpressionSet") 13 | expect_true(is.matrix(eSet@assayData$exprs)) 14 | expect_equal(dim(eSet@assayData$exprs), c(3L, 10L)) 15 | expect_type(eSet@experimentData@other, "list") 16 | expect_equal(eSet@experimentData@url, "www.standardbio.com") 17 | expect_equal(eSet@experimentData@title, "Example Adat Set001, Example Adat Set002") 18 | expect_equal(eSet@experimentData@lab, "Standard BioTools, Inc.") 19 | expect_equal(eSet@experimentData@other$Version, "1.2") 20 | expect_equal(eSet@experimentData@other$ExpDate, "2020-06-18, 2020-07-20") 21 | expect_equal(eSet@experimentData@other$AssaySite, "SW") 22 | expect_equal(eSet@experimentData@other$AssayVersion, "V4") 23 | expect_equal(eSet@experimentData@other$AssayRobot, "Fluent 1 L-307") 24 | expect_equal(eSet@experimentData@other$StudyMatrix, "EDTA Plasma") 25 | expect_equal(eSet@experimentData@other$CreatedBy, "PharmaServices") 26 | expect_equal(eSet@experimentData@other$CalibratorId, "170261") 27 | expect_equal(eSet@experimentData@title, eSet@experimentData@other$Title) 28 | anno <- getAnalyteInfo(sub_adat) |> data.frame() |> col2rn("AptName") 29 | expect_equal(rownames(eSet@featureData@varMetadata), names(anno)) 30 | expect_equal(rownames(eSet@featureData@data), getAnalytes(sub_adat)) 31 | expect_equal(eSet@featureData@data, anno) 32 | expect_equal(rownames(eSet@phenoData@data), rownames(sub_adat)) 33 | expect_named(eSet@phenoData@data, getMeta(sub_adat)) 34 | expect_equal(eSet@phenoData@data, data.frame(sub_adat[, getMeta(sub_adat)])) 35 | expect_equal(eSet@phenoData@dimLabels, c("sampleNames", "sampleColumns")) 36 | expect_equal(eSet@featureData@dimLabels, c("featureNames", "featureColumns")) 37 | expect_equal(colnames(eSet@assayData$exprs), rownames(sub_adat)) 38 | expect_equal(rownames(eSet@assayData$exprs), rownames(anno)) 39 | expect_equal(rownames(eSet@assayData$exprs), getAnalytes(sub_adat)) 40 | expect_equal(rownames(eSet@assayData$exprs), rownames(eSet@featureData@data)) 41 | }) 42 | -------------------------------------------------------------------------------- /tests/testthat/test-addAttributes.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("`addAttributes()` correctly adds the attr list", { 3 | df <- data.frame(num = 1:6) 4 | atts <- list(A = 1:10, B = LETTERS) 5 | new <- attributes(addAttributes(df, atts)) 6 | expect_type(new, "list") 7 | expect_named(new, c("names", "class", "row.names", names(atts))) 8 | expect_equal(new$A, 1:10) 9 | expect_equal(new$B, LETTERS) 10 | }) 11 | 12 | test_that("`addAttributes()` throws correct errors and messages", { 13 | expect_error( 14 | addAttributes(1:10L, list(A = 1:10)), 15 | "`data` must be a data frame, tibble, or similar.", 16 | ) 17 | df <- data.frame(num = 1:6) 18 | expect_error( 19 | addAttributes(df, c(A = 1)), 20 | "`new.atts` must be a *named* list.", fixed = TRUE 21 | ) 22 | expect_error( 23 | addAttributes(df, list(1:10)), 24 | "`new.atts` must be a *named* list.", fixed = TRUE 25 | ) 26 | }) 27 | -------------------------------------------------------------------------------- /tests/testthat/test-addClass.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("addClass() adds the class label to the object", { 3 | x <- addClass(mtcars, "foo") 4 | expect_equal(class(x), c("foo", "data.frame")) 5 | expect_equal(class(addClass(x, "foo")), class(x)) # no change 6 | 7 | expect_equal( 8 | class(addClass(mtcars, c("foo", "foo"))), # no duplicates 9 | c("foo", "data.frame") 10 | ) 11 | expect_equal( 12 | class(addClass(mtcars, c("foo", "bar"))), # multiple classes 13 | c("foo", "bar", "data.frame") 14 | ) 15 | expect_equal( 16 | class(addClass(mtcars, c("bar", "foo"))), # multiple classes in order 17 | c("bar", "foo", "data.frame") 18 | ) 19 | 20 | expect_equal( 21 | class(addClass(x, "data.frame")), c("data.frame", "foo") # re-orders 22 | ) 23 | 24 | expect_warning( 25 | expect_equal(class(addClass(mtcars, NULL)), "data.frame"), # NULL case no change 26 | "Passing `class = NULL` leaves class(x) unchanged.", 27 | fixed = TRUE 28 | ) 29 | expect_warning( 30 | expect_equal(class(addClass(x, NULL)), class(x)), # NULL case no change 31 | "Passing `class = NULL` leaves class(x) unchanged.", 32 | fixed = TRUE 33 | ) 34 | 35 | expect_error( 36 | addClass(mtcars, NA_character_), 37 | "The `class` param cannot contain `NA`: NA" 38 | ) 39 | expect_error( 40 | addClass(mtcars, c("foo", NA_character_)), 41 | "The `class` param cannot contain `NA`: 'foo', NA" 42 | ) 43 | }) 44 | -------------------------------------------------------------------------------- /tests/testthat/test-calc_eLOD.R: -------------------------------------------------------------------------------- 1 | # Setup ---- 2 | # soma_adat input filtered to "Buffer" samples 3 | buffer_samples <- example_data |> filter(SampleType == "Buffer") 4 | 5 | drop_seqs <- length(getAnalytes(example_data)) - 10 6 | drop_seqs <- getAnalytes(example_data)[1:drop_seqs] 7 | 8 | buffer_samples <- buffer_samples |> select(-all_of(drop_seqs)) 9 | 10 | # data.frame input 11 | df <- withr::with_seed(101, { 12 | data.frame( 13 | SampleType = rep(c("Sample", "Buffer"), each = 10), 14 | SampleId = paste0("Sample_", 1:20), 15 | seq.20.1.100 = runif(20, 1, 100), 16 | seq.21.1.100 = runif(20, 1, 100), 17 | seq.22.2.100 = runif(20, 1, 100) 18 | ) 19 | }) 20 | sample_ids <- paste0("Sample_", 11:20) 21 | selected_samples <- df |> filter(SampleId %in% sample_ids) 22 | 23 | # Testing ---- 24 | test_that("`calc_eLOD` produces a warning when it should", { 25 | expect_warning( 26 | calc_eLOD(example_data), 27 | "Ensure input data includes buffer samples only!" 28 | ) 29 | }) 30 | 31 | test_that("`calc_eLOD` produces an error when it should", { 32 | expect_error( 33 | calc_eLOD(list(SampleId = 1:3, seq.1000.123 = 100:102)), 34 | "`data` must be a soma_adat, tibble, or data.frame" 35 | ) 36 | }) 37 | 38 | test_that("`calc_eLOD` works on a soma_adat input filtered to buffer samples", { 39 | out <- calc_eLOD(buffer_samples) 40 | 41 | expect_s3_class(out, "tbl_df") 42 | expect_equal(dim(out), c(10L, 2L)) 43 | expect_equal( 44 | head(out, 3), 45 | tibble(SeqId = c("seq.9981.18", "seq.9983.97", "seq.9984.12"), 46 | eLOD = c(45.08555, 52.98848, 123.02824)), 47 | tolerance = 0.00001 48 | ) 49 | }) 50 | 51 | test_that("`calc_eLOD` works on a data.frame input", { 52 | out <- calc_eLOD(selected_samples) 53 | 54 | expect_s3_class(out, "tbl_df") 55 | expect_equal(dim(out), c(3L, 2L)) 56 | expect_equal( 57 | head(out, 3), 58 | tibble(SeqId = c("seq.20.1.100", "seq.21.1.100", "seq.22.2.100"), 59 | eLOD = c(168.0601, 130.7047, 115.9958)), 60 | tolerance = 0.0001 61 | ) 62 | }) 63 | -------------------------------------------------------------------------------- /tests/testthat/test-checkADAT.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | adat <- mock_adat() 4 | 5 | # Testing ---- 6 | test_that("`.checkADAT()` produces an error when it should", { 7 | # Success 8 | withr::local_options(list(usethis.quiet = TRUE)) 9 | expect_null(.checkADAT(adat)) # NULL 10 | expect_error(.checkADAT(adat), NA) # NA; no errors 11 | expect_invisible(.checkADAT(adat)) # invisible 12 | 13 | # Failure 14 | # break sync with attributes and adat 15 | attributes(adat)$Header.Meta$ROW_DATA$Name <- "Foo" 16 | expect_error( 17 | .checkADAT(adat), 18 | "Meta data mismatch between `Header Meta` and ADAT meta data." 19 | ) 20 | }) 21 | 22 | test_that("`.checkADAT()` produces an error if Col.Meta-Apts are out of sync", { 23 | attr(adat, "Col.Meta") <- attr(adat, "Col.Meta")[-2, ] 24 | expect_error( 25 | .checkADAT(adat), 26 | "Number of RFU features in ADAT does not match No. analytes in Col.Meta!" 27 | ) 28 | }) 29 | 30 | test_that("`.checkADAT()` produces a warning if ADAT has no rows", { 31 | withr::local_options(list(usethis.quiet = TRUE)) 32 | expect_warning( 33 | .checkADAT(adat[0L, ]), 34 | "ADAT has no rows! Writing just header and column meta data." 35 | ) 36 | }) 37 | -------------------------------------------------------------------------------- /tests/testthat/test-diffAdats.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | adat <- mock_adat() 4 | 5 | # Testing ---- 6 | test_that("`diffAdats()` generates 'all-passing' output with equal ADATs", { 7 | expect_snapshot(diffAdats(adat, adat)) 8 | }) 9 | 10 | test_that("`diffAdats()` generates correct output with 1 analyte missing", { 11 | # random remove analyte column 12 | expect_snapshot(diffAdats(adat, adat[, -9L])) 13 | }) 14 | 15 | test_that("`diffAdats()` generates correct output with 1 clin variable missing", { 16 | # random remove analyte column 17 | expect_snapshot(diffAdats(adat, adat[, -3L])) 18 | }) 19 | 20 | test_that("`diffAdats()` generates correct output with 1 clin variable added", { 21 | # add variable 'foo' 22 | new <- adat 23 | new$foo <- "bar" 24 | expect_snapshot(diffAdats(adat, new)) 25 | }) 26 | 27 | test_that("`diffAdats()` generates correct output with 1 variable changed", { 28 | # change 'Subarray' variable 29 | expect_snapshot( 30 | diffAdats(adat, dplyr::mutate(adat, Subarray = rev(Subarray))) 31 | ) 32 | }) 33 | 34 | test_that("`diffAdats()` generates correct output 2 random values changed", { 35 | new <- adat 36 | new[c(3L, 5L), c(8L, 10L)] <- 999.9 37 | expect_snapshot(diffAdats(adat, new)) 38 | }) 39 | -------------------------------------------------------------------------------- /tests/testthat/test-genRownames.R: -------------------------------------------------------------------------------- 1 | 2 | set.seed(1) 3 | n <- 50 4 | a <- sample(LETTERS, n, replace = TRUE) 5 | b <- sample(1:99, size = n, replace = TRUE) 6 | 7 | test_that("genRowNames generates correct rownames to data frame", { 8 | rn <- data.frame(SlideId = a, Subarray = b) |> genRowNames() 9 | expect_equal(rn, paste0(a, "_", b)) 10 | }) 11 | 12 | test_that("genRowNames numbers numerically if no SlideId or Subarray", { 13 | expect_warning( 14 | df <- data.frame(Slide = a, Sub = b) |> genRowNames(), 15 | "No SlideId_Subarray found in ADAT. Rows numbered sequentially." 16 | ) 17 | expect_equal(df, as.character(1:n)) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/testthat/test-getAdatVersion.R: -------------------------------------------------------------------------------- 1 | 2 | x <- list(Header.Meta = list(HEADER = list(Version = "1.2"))) 3 | 4 | test_that("`getAdatVersion()` returns the ADAT version string", { 5 | expect_equal(getAdatVersion(x), "1.2") 6 | x$Header.Meta$HEADER$Version <- "2.5" 7 | expect_equal(getAdatVersion(x), "2.5") 8 | }) 9 | 10 | test_that("`getAdatVersion()` errors out if no `Version` in HEADER", { 11 | x$Header.Meta$HEADER <- list(Dummy = "Cox", Yellow = 3) 12 | expect_error( 13 | getAdatVersion(x), 14 | "Unable to identify ADAT Version from Header information." 15 | ) 16 | }) 17 | 18 | test_that("`getAdatVersion()` throws warning if tabs after key-value pair", { 19 | x$Header.Meta$HEADER$Version <- c("1.2", "\t") 20 | expect_warning( 21 | y <- getAdatVersion(x), 22 | paste("Version length > 1 ... there may be empty tabs", 23 | "in the header block above the data matrix."), 24 | ) 25 | expect_equal(y, "1.2") 26 | }) 27 | 28 | test_that("`getAdatVersion()` catches JAVA version number format", { 29 | x$Header.Meta$HEADER$Version <- "1.01" 30 | expect_error( 31 | getAdatVersion(x), 32 | "Invalid Version ('1.01'). Please modify to `1.0.1`.", 33 | fixed = TRUE 34 | ) 35 | }) 36 | 37 | test_that("`getAdatVersion()` S3 method returns the same character", { 38 | expect_equal( 39 | getAdatVersion(example_data), # soma_adat 40 | getAdatVersion(attributes(example_data)) # list 41 | ) 42 | }) 43 | 44 | test_that("`getAdatVersion()` S3 default method trips errror", { 45 | expect_error( 46 | getAdatVersion(""), "Unable to find a method for class: 'character'" 47 | ) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-getAnalyteInfo.R: -------------------------------------------------------------------------------- 1 | 2 | anno <- getAnalyteInfo(example_data) 3 | 4 | test_that("`getAnalyteInfo()` generates correct objects", { 5 | expect_s3_class(anno, "tbl_df") 6 | expect_equal(dim(anno), c(getAnalytes(example_data, n = TRUE), 22L)) 7 | expect_setequal(anno$AptName, getAnalytes(example_data)) 8 | expect_equal(vapply(anno, typeof, ""), 9 | c(AptName = "character", 10 | SeqId = "character", 11 | SeqIdVersion = "double", 12 | SomaId = "character", 13 | TargetFullName = "character", 14 | Target = "character", 15 | UniProt = "character", 16 | EntrezGeneID = "character", 17 | EntrezGeneSymbol = "character", 18 | Organism = "character", 19 | Units = "character", 20 | Type = "character", 21 | Dilution = "character", 22 | PlateScale_Reference = "double", 23 | CalReference = "double", 24 | Cal_Example_Adat_Set001 = "double", 25 | ColCheck = "character", 26 | CalQcRatio_Example_Adat_Set001_170255 = "double", 27 | QcReference_170255 = "double", 28 | Cal_Example_Adat_Set002 = "double", 29 | CalQcRatio_Example_Adat_Set002_170255 = "double", 30 | Dilution2 = "double")) 31 | }) 32 | 33 | test_that("`getAnalyteInfo()` error conditions are triggered", { 34 | x <- example_data 35 | attr(x, "Col.Meta") <- as.data.frame(attr(x, "Col.Meta")) 36 | expect_error( 37 | getAnalyteInfo(x), 38 | "`Col.Meta` must be a `tbl_df`." 39 | ) 40 | attr(x, "Col.Meta") <- NULL 41 | expect_error( 42 | getAnalyteInfo(x), 43 | "`Col.Meta` is absent from ADAT." 44 | ) 45 | }) 46 | 47 | test_that("`getAnalyteInfo()` warning if Col.Meta out-of-sync with features", { 48 | # option 1; features in data but not in Col.Meta 49 | x <- example_data 50 | attr(x, "Col.Meta") <- head(attr(x, "Col.Meta"), -1L) # rm final row 51 | expect_warning( 52 | y <- getAnalyteInfo(x), 53 | "Features inconsistent between `AptName` vs `SeqId` in `getAnalyteInfo()`.", 54 | fixed = TRUE 55 | ) 56 | # tibble with NAs for missing features 57 | n <- getAnalytes(x, n = TRUE) 58 | expect_equal(dim(y), c(n, 22L)) 59 | # all NAs except for 2 columns (AptName, SeqId) 60 | expect_equal(sum(!is.na(tail(y, 1L))), 2) 61 | 62 | # option 2; features in Col.Meta but not in data 63 | # rename final column 64 | x <- example_data 65 | attr(x, "Col.Meta")[n + 1L, ] <- attr(x, "Col.Meta")[n, ] # add dummy feature 66 | attr(x, "Col.Meta")[n + 1L, 1L] <- "9999-99" 67 | expect_warning( 68 | y <- getAnalyteInfo(x), 69 | "Features inconsistent between `AptName` vs `SeqId` in `getAnalyteInfo()`.", 70 | fixed = TRUE 71 | ) 72 | # no NAs; extra 9999-99 feature dropped 73 | expect_equal(dim(y), c(n, 22L)) 74 | }) 75 | -------------------------------------------------------------------------------- /tests/testthat/test-getAnalytes.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | # Get full adat with controls 4 | full <- read_adat(test_path("testdata", "single_sample.adat")) 5 | 6 | # First 6 features of 'example_data' with controls 7 | seq_vec <- c("seq.10000.28", "seq.10001.7", "seq.10003.15", 8 | "seq.10006.25", "seq.10008.43", "seq.10011.65") 9 | plex <- 5284 10 | 11 | 12 | # Testing ---- 13 | test_that("`getAnalytes()` S3 `soma_adat` and `data.frame` methods", { 14 | apts <- getAnalytes(full) 15 | expect_type(apts, "character") 16 | expect_length(apts, plex) 17 | expect_equal(head(apts), seq_vec) 18 | expect_true(all(is.apt(apts))) 19 | expect_true(all(apts %in% names(full))) 20 | }) 21 | 22 | test_that("`getAnalytes()` S3 `recipe` methods work", { 23 | skip_on_cran() 24 | rec <- recipes::recipe(~ ., data = full) 25 | apts <- getAnalytes(rec) 26 | expect_equal(typeof(apts), "character") 27 | expect_length(apts, plex) 28 | expect_equal(head(apts), seq_vec) 29 | expect_true(all(apts %in% names(full))) 30 | }) 31 | 32 | test_that("`getAnalytes()` with the `n =` argument works", { 33 | expect_equal(getAnalytes(full, n = TRUE), plex) 34 | }) 35 | 36 | test_that("`getAnalytes()` with the `rm.controls =` argument works", { 37 | expect_equal(getAnalytes(full, n = TRUE, rm.controls = TRUE), plex - 64) 38 | }) 39 | 40 | test_that("`getAnalytes()` S3 `character` method kicks in", { 41 | apts <- getAnalytes(names(full)) 42 | expect_type(apts, "character") 43 | expect_length(apts, plex) 44 | expect_true(all(is.apt(apts))) 45 | expect_equal(head(apts), seq_vec) 46 | }) 47 | 48 | test_that("`getAnalytes()` S3 default method kicks in", { 49 | expect_error( 50 | getAnalytes(factor("A")), 51 | "Couldn't find a S3 method for this class object: 'factor'" 52 | ) 53 | expect_error( 54 | getAnalytes(1L), 55 | "Couldn't find a S3 method for this class object: 'integer'" 56 | ) 57 | expect_error( 58 | getAnalytes(1.1), 59 | "Couldn't find a S3 method for this class object: 'numeric'" 60 | ) 61 | expect_error( 62 | getAnalytes(TRUE), 63 | "Couldn't find a S3 method for this class object: 'logical'" 64 | ) 65 | }) 66 | 67 | test_that("`getAnalytes()` matrix S3 method kicks in", { 68 | apts <- as.matrix(full) |> getAnalytes() 69 | expect_type(apts, "character") 70 | expect_equal(head(apts), seq_vec) 71 | expect_true(all(is.apt(apts))) 72 | expect_true(all(apts %in% names(full))) 73 | }) 74 | 75 | test_that("the S3 list method kicks in", { 76 | apts <- setNames(as.list(seq_vec), seq_vec) 77 | expect_equal(getAnalytes(apts), head(getAnalytes(full))) 78 | expect_true(all(apts %in% names(full))) 79 | }) 80 | -------------------------------------------------------------------------------- /tests/testthat/test-getMeta.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | adat <- mock_adat() 4 | 5 | # Testing ---- 6 | test_that("`getMeta()` S3 `soma_adat` method", { 7 | meta <- getMeta(adat) 8 | expect_type(meta, "character") 9 | expect_length(meta, 7L) 10 | expect_true(sum(is.apt(meta)) == 0) 11 | }) 12 | 13 | test_that("`getMeta()` `n` argument works", { 14 | expect_equal(getMeta(adat, n = TRUE), 7) 15 | }) 16 | 17 | test_that("`getMeta()` S3 'character' method", { 18 | meta <- getMeta(names(adat)) 19 | expect_type(meta, "character") 20 | expect_length(meta, 7L) 21 | expect_true(sum(is.apt(meta)) == 0) 22 | }) 23 | 24 | test_that("`getMeta()` S3 default method kicks in", { 25 | expect_error( 26 | getMeta(factor("A")), 27 | "Couldn't find a S3 method for this class object: 'factor'" 28 | ) 29 | expect_error( 30 | getMeta(1L), 31 | "Couldn't find a S3 method for this class object: 'integer'" 32 | ) 33 | expect_error( 34 | getMeta(1.1), 35 | "Couldn't find a S3 method for this class object: 'numeric'" 36 | ) 37 | expect_error( 38 | getMeta(TRUE), 39 | "Couldn't find a S3 method for this class object: 'logical'" 40 | ) 41 | }) 42 | -------------------------------------------------------------------------------- /tests/testthat/test-getSeqIdMatches.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("`getSeqIdMatches()` returns correct and expected IDs", { 3 | adat <- mock_adat() 4 | apt_vec <- getAnalytes(adat) 5 | m <- getSeqIdMatches(apt_vec, names(adat)) 6 | expect_s3_class(m, "data.frame") 7 | expect_named(m, c("apt_vec", "names(adat)")) 8 | expect_equal(dim(m), c(3, 2)) 9 | expect_equal(dim(m), c(3, 2)) 10 | # should be identical b/c nothing changed (only getMeta() differs) 11 | expect_equal(m[[1L]], m[[2L]]) 12 | 13 | A1 <- apt_vec[1:2] 14 | A2 <- getSeqId(apt_vec[2:3]) 15 | # There should be 1 overlapping analyte: 95-100 16 | m <- getSeqIdMatches(A1, A2) 17 | true <- data.frame(A1 = "seq.3333.33", A2 = "3333-33") 18 | expect_equal(m, true) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/testthat/test-getSomaScanLiftCCC.R: -------------------------------------------------------------------------------- 1 | test_that("`getSomaScanLiftCCC()` returns expected output for plasma", { 2 | plasma <- getSomaScanLiftCCC("plasma") 3 | expect_s3_class(plasma, "tbl_df") 4 | expect_equal(dim(plasma), c(11083, 4)) 5 | expect_equal(names(plasma), c("SeqId", "plasma_11k_to_5k_ccc", 6 | "plasma_11k_to_7k_ccc", "plasma_7k_to_5k_ccc")) 7 | }) 8 | 9 | test_that("`getSomaScanLiftCCC()` returns expected output for serum", { 10 | serum <- getSomaScanLiftCCC("serum") 11 | expect_s3_class(serum, "tbl_df") 12 | expect_equal(dim(serum), c(11083, 4)) 13 | expect_equal(names(serum), c("SeqId", "serum_11k_to_5k_ccc", 14 | "serum_11k_to_7k_ccc", "serum_7k_to_5k_ccc")) 15 | }) 16 | 17 | test_that("`getSomaScanLiftCCC()` errors given invalid matrix type", { 18 | expect_error(getSomaScanLiftCCC("citrate"), "should be one of") 19 | }) 20 | -------------------------------------------------------------------------------- /tests/testthat/test-getSomaScanVersion.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("`getSomaScanVersion()` returns the ADAT version string", { 3 | expect_equal(getSomaScanVersion(example_data), "V4") 4 | }) 5 | 6 | test_that("`checkSomaScanVersion()` returns the correct error mode", { 7 | expect_null(checkSomaScanVersion("V4")) 8 | expect_null(checkSomaScanVersion("v4")) 9 | expect_null(checkSomaScanVersion("v4.1")) 10 | expect_null(checkSomaScanVersion("v5.0")) 11 | expect_null(checkSomaScanVersion("v5")) 12 | expect_error(checkSomaScanVersion("V2"), "Unsupported") 13 | expect_error(checkSomaScanVersion("foo"), "Unsupported") 14 | }) 15 | -------------------------------------------------------------------------------- /tests/testthat/test-getTargetNames.R: -------------------------------------------------------------------------------- 1 | 2 | anno <- getTargetNames(getAnalyteInfo(example_data)) 3 | 4 | test_that("`getTargetNames()` returns correct value(s)", { 5 | expect_s3_class(anno, "target_map") 6 | expect_length(anno, 5284L) 7 | expect_named(anno, getAnalytes(example_data)) 8 | expect_true(all(vapply(anno, typeof, "") == "character")) 9 | }) 10 | 11 | test_that("`getTargetNames()` S3 print method for `target_map` snapshots", { 12 | # maps to tibble for printing 13 | expect_snapshot_output(anno) 14 | }) 15 | 16 | test_that("`getTargetNames()` stop modes", { 17 | df <- data.frame(foo = 1) 18 | expect_error( 19 | getTargetNames(df), 20 | "`tbl` must contain Target info." 21 | ) 22 | df$Target <- "bar" 23 | expect_error( 24 | getTargetNames(df), 25 | "`tbl` must contain an `AptName` column." 26 | ) 27 | df$AptName <- "blah" 28 | expect_error(out <- getTargetNames(df), NA) # no error 29 | expect_equal(unclass(out), list(blah = "bar")) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/testthat/test-is-apt.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("is.apt produces a logical", { 3 | expect_type(is.apt("ABCD.1234.50.5"), "logical") 4 | expect_type(is.apt("HelloKitty"), "logical") 5 | }) 6 | 7 | test_that("is.apt returns TRUE by 'clone' ID", { 8 | expect_true(is.apt("ABCD9.1234.555.6")) 9 | expect_true(is.apt("ABCD9.1234.55.6")) 10 | expect_true(is.apt("ABCD9.1234.5.6")) 11 | expect_true(is.apt("ABCD9.1234.555.61")) 12 | expect_true(is.apt("ABCD9.1234.55.61")) 13 | expect_true(is.apt("ABCD9.1234.5.61")) 14 | expect_true(is.apt("ABCD9.1234.555.618")) 15 | expect_true(is.apt("ABCD9.1234.55.618")) 16 | expect_true(is.apt("ABCD9.1234.5.618")) 17 | }) 18 | 19 | test_that("is.apt returns TRUE by 'version' ID", { 20 | expect_true(is.apt("ABCD9.1234.5.3")) 21 | expect_true(is.apt("ABCD9.1234.5.36")) 22 | expect_true(is.apt("ABCD9.1234.5.367")) 23 | expect_true(is.apt("ABCD9.1234.55.3")) 24 | expect_true(is.apt("ABCD9.1234.55.36")) 25 | expect_true(is.apt("ABCD9.1234.55.367")) 26 | expect_true(is.apt("ABCD9.1234.955.3")) 27 | expect_true(is.apt("ABCD9.1234.955.36")) 28 | expect_true(is.apt("ABCD9.1234.955.367")) 29 | }) 30 | 31 | test_that("is.apt returns TRUE with 5 digit 'seqId'", { 32 | expect_true(is.apt("ABCD9.12349.5.3")) 33 | expect_true(is.apt("ABCD9.12349.5.36")) 34 | expect_true(is.apt("ABCD9.12349.5.367")) 35 | expect_true(is.apt("ABCD9.12349.55.3")) 36 | expect_true(is.apt("ABCD9.12349.55.36")) 37 | expect_true(is.apt("ABCD9.12349.55.367")) 38 | expect_true(is.apt("ABCD9.12349.955.3")) 39 | expect_true(is.apt("ABCD9.12349.955.36")) 40 | expect_true(is.apt("ABCD9.12349.955.367")) 41 | }) 42 | 43 | test_that("is.apt returns TRUE when missing 'version' ID", { 44 | expect_true(is.apt("ABCD9.1234.5")) 45 | expect_true(is.apt("ABCD9.1234.55")) 46 | expect_true(is.apt("ABCD9.1234.555")) 47 | expect_true(is.apt("ABCD9.12348.5")) 48 | expect_true(is.apt("ABCD9.12348.55")) 49 | expect_true(is.apt("ABCD9.12348.555")) 50 | }) 51 | 52 | test_that("is.apt returns FALSE when SeqId absent", { 53 | expect_false(is.apt(NA_character_)) 54 | expect_equal(is.apt(NULL), logical(0)) 55 | expect_false(is.apt("")) 56 | expect_false(is.apt("ABCD9.1234")) 57 | expect_false(is.apt("ABCD9.123.4")) 58 | expect_false(is.apt("ABCD9.23.4")) 59 | expect_false(is.apt("ABCD9.123.4.20")) 60 | expect_false(is.apt("ABCD9.12.4.20")) 61 | expect_false(is.apt("ABCD34.5656")) 62 | expect_false(is.apt("HelloKitty")) 63 | expect_false(is.apt("SomaLogic")) 64 | expect_false(is.apt("ABCD.9.44.0")) 65 | }) 66 | 67 | test_that("is.apt returns FALSE when SeqId is not trailing", { 68 | expect_false(is.apt("ABCD.1234.5_A")) 69 | expect_false(is.apt("seq.1234.5_A")) 70 | expect_true(is.apt("1234.5")) 71 | expect_false(is.apt("1234.5 ")) # trailing whitespace 72 | expect_false(is.apt("This 1234.5 not an Apt")) 73 | }) 74 | 75 | test_that("is.apt vectorized version works", { 76 | x <- is.apt(c("Super", "HB1A.9", "Sample", "ABCD.2343.2.12", "MMP.4342.12.1")) 77 | expect_type(x, "logical") 78 | expect_length(x, 5L) 79 | expect_equal(x, c(FALSE, FALSE, FALSE, TRUE, TRUE)) 80 | expect_equal(sum(is.apt(names(mock_adat()))), 3) # all analytes in `soma_adat` 81 | }) 82 | -------------------------------------------------------------------------------- /tests/testthat/test-is-intact-attr.R: -------------------------------------------------------------------------------- 1 | 2 | # generate mock `soma_adat` 3 | df <- mock_adat() 4 | 5 | # silence signalling for below tests 6 | withr::local_options(list(usethis.quiet = TRUE)) 7 | 8 | test_that("TRUE returned when attributes look good", { 9 | expect_true(is_intact_attr(df)) 10 | }) 11 | 12 | test_that("FALSE returned when attributes <= 3 in length", { 13 | df <- data.frame(df) 14 | expect_false(is_intact_attr(df, TRUE)) 15 | }) 16 | 17 | test_that("FALSE returned when Header.Meta is missing", { 18 | x <- df 19 | attributes(x)$Col.Meta <- NULL 20 | expect_false(is_intact_attr(x, TRUE)) 21 | }) 22 | 23 | test_that("FALSE returned when Col.Meta is missing", { 24 | x <- df 25 | attributes(x)$Header.Meta <- NULL 26 | expect_false(is_intact_attr(x, TRUE)) 27 | }) 28 | 29 | test_that("FALSE when Header.Meta has elements missing", { 30 | attributes(df)$Header.Meta <- c("this", "should", "fail") 31 | expect_false(is_intact_attr(df, TRUE)) 32 | }) 33 | 34 | test_that("FALSE when Col.Meta has elements missing", { 35 | attributes(df)$Col.Meta <- c("SeqId", "Target", "DUMMY", "Units") 36 | expect_false(is_intact_attr(df, TRUE)) 37 | }) 38 | 39 | test_that("FALSE when Col.Meta is not a tibble", { 40 | attr(df, "Col.Meta") <- as.list(attr(df, "Col.Meta")) 41 | expect_false(is_intact_attr(df, TRUE)) 42 | }) 43 | 44 | test_that("user defined `verbose =` param overrides internal logic as expected", { 45 | withr::local_options(list(usethis.quiet = FALSE)) # allow oops 46 | expect_snapshot( is_intact_attr(iris, verbose = TRUE) ) 47 | expect_snapshot( is_intact_attr(iris, verbose = FALSE) ) 48 | }) 49 | 50 | test_that("verbosity is triggered only when called directly", { 51 | withr::local_options(list(usethis.quiet = FALSE)) # allow oops 52 | .env <- parent.frame(sys.nframe()) # env at top of the stack 53 | # assign functions for use in local scope below 54 | .env$with_interactive <- with_interactive 55 | .env$f1 <- function(x) is_intact_attr(x) # 1 level 56 | .env$f2 <- function(x) f1(x) # 2 levels 57 | 58 | local(envir = .env, { 59 | # direct call (signals >> oops) 60 | with_interactive(TRUE, expect_snapshot(is_intact_attr(iris))) 61 | }) 62 | 63 | local(envir = .env, { 64 | with_interactive(TRUE, expect_snapshot(f1(iris))) # 1 level away 65 | }) 66 | 67 | local(envir = .env, { 68 | with_interactive(TRUE, expect_snapshot(f2(iris))) # 2 levels away 69 | }) 70 | 71 | rm(f1, f2, with_interactive, envir = .env) # clean up leftover functions 72 | expect_equal(ls(envir = .env), character(0)) # test successful cleanup 73 | 74 | }) 75 | -------------------------------------------------------------------------------- /tests/testthat/test-is-seqFormat.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("`is_seqFormat()` soma_adat method returns correct booleans", { 3 | expect_true(is_seqFormat(example_data)) 4 | example_data$ABCD.1234.5 <- 100 5 | expect_false(is_seqFormat(example_data)) 6 | }) 7 | 8 | test_that("`is_seqFormat()` data.frame method returns correct booleans", { 9 | expect_true(is_seqFormat(data.frame(seq.1243.5 = 1:10))) 10 | expect_false(is_seqFormat(data.frame(PlateId = 1:10))) 11 | expect_false(is_seqFormat(data.frame(1:10))) 12 | }) 13 | 14 | test_that("`is_seqFormat()` character method returns correct booleans", { 15 | expect_false(is_seqFormat(names(example_data))) # meta data makes false 16 | expect_false(is_seqFormat("SomaLogic")) 17 | expect_false(is_seqFormat("ABCD.1234.12")) 18 | expect_true(is_seqFormat("seq.1234.12")) 19 | }) 20 | 21 | test_that("`is_seqFormat()` character(0) is false", { 22 | expect_false(is_seqFormat(character(0))) 23 | }) 24 | 25 | test_that("`is_seqFormat()` default method is tripped for unknown classes", { 26 | withr::local_options(list(cli.num_colors = 1L)) 27 | expect_error( 28 | is_seqFormat(1:10), 29 | "Couldn't find a S3 method for this class object: 'integer'" 30 | ) 31 | expect_error( 32 | is_seqFormat(2.34), 33 | "Couldn't find a S3 method for this class object: 'numeric'" 34 | ) 35 | expect_error( 36 | is_seqFormat(factor(letters)), 37 | "Couldn't find a S3 method for this class object: 'factor'" 38 | ) 39 | expect_error( 40 | is_seqFormat(matrix(1:9, ncol = 3)), 41 | "Couldn't find a S3 method for this class object: 'matrix'" 42 | ) 43 | }) 44 | -------------------------------------------------------------------------------- /tests/testthat/test-loadAdatsAsList.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | # this ensures the list.files() call sorts the same across test() and check() 4 | # must set the local collation order 5 | # use 'C', the default during check(); 'en_US.UTF-8' common in SLIDE 6 | withr::local_collate("C") 7 | 8 | test_that("the default collation order has been set properly", { 9 | expect_equal(Sys.getlocale("LC_COLLATE"), "C") 10 | }) 11 | 12 | files <- system.file("extdata", package = "SomaDataIO", mustWork = TRUE) |> 13 | list.files(pattern = "[.]adat$", full.names = TRUE) 14 | files <- c(files, test_path("testdata", "single_sample.adat")) 15 | files <- normalizePath(files) 16 | adats <- loadAdatsAsList(files) 17 | foo <- collapseAdats(adats) 18 | atts <- attributes(foo) 19 | 20 | # Testing ---- 21 | test_that("adats list is named properly and has proper dimensions", { 22 | expect_named(adats, cleanNames(basename(files))) 23 | expect_snapshot(lapply(adats, dim)) 24 | }) 25 | 26 | # loadAdatsAsList() ----- 27 | test_that("throws warning when failure to load adat", { 28 | expect_snapshot( 29 | bad <- loadAdatsAsList(c(files, "fail.adat")) 30 | ) 31 | # failed file names are not returned 32 | expect_equal(bad, adats) 33 | 34 | # two invalid files 35 | expect_snapshot( 36 | bad2 <- loadAdatsAsList(c("a.adat", "b.adat")) 37 | ) 38 | expect_named(bad2, character(0)) 39 | expect_length(bad2, 0L) 40 | }) 41 | 42 | test_that("`collapse=` argument works same as `collapseAdats(x)`", { 43 | foo2 <- loadAdatsAsList(files, collapse = TRUE) 44 | expect_equal(foo2, foo) 45 | }) 46 | 47 | test_that("collapsed ADATs dimensions are correct", { 48 | expect_s3_class(foo, "soma_adat") 49 | expect_equal( 50 | dim(foo), 51 | c(sum(vapply(adats, nrow, 1L)), 52 | length(Reduce(intersect, lapply(adats, names))) 53 | ) 54 | ) 55 | }) 56 | 57 | test_that("collapsed ADATs attributes (HEADER) are correctly merged", { 58 | expect_type(atts, "list") 59 | expect_named(atts, c("names", "class", "row.names", "Header.Meta", 60 | "Col.Meta", "file_specs", "row_meta")) 61 | expect_named(atts$Header.Meta, c("HEADER", "COL_DATA", "ROW_DATA", "TABLE_BEGIN")) 62 | expect_snapshot( atts$Header.Meta$HEADER ) 63 | }) 64 | -------------------------------------------------------------------------------- /tests/testthat/test-locateSeqId.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | # locateSeqId() and regexSeqId() are both tested here 4 | 5 | # Testing ---- 6 | test_that("locateSeqId() returns NAs and integers with/without SeqId", { 7 | rex <- locateSeqId(c("Sample", "seq.1234.5")) 8 | expect_equal(rex, data.frame(x = c("Sample", "seq.1234.5"), 9 | start = c(NA_integer_, 5L), 10 | stop = c(NA_integer_, 10L))) 11 | expect_equal(locateSeqId(NA_character_), 12 | data.frame(x = NA_character_, 13 | start = NA_integer_, 14 | stop = NA_integer_) 15 | ) 16 | }) 17 | 18 | test_that("locateSeqId() works on aptamer names", { 19 | rex <- locateSeqId("ABCD.1234.5.5") 20 | expect_equal(rex, data.frame(x = "ABCD.1234.5.5", start = 6L, stop = 13L)) 21 | }) 22 | 23 | test_that("locateSeqId() works on SeqIds", { 24 | rex <- locateSeqId("1234-5_5") 25 | expect_equal(rex, data.frame(x = "1234-5_5", start = 1L, stop = 8L)) 26 | }) 27 | 28 | test_that("locateSeqId() works on funky aptamer names", { 29 | rex <- locateSeqId("SOMAmer.1.12345.666.13") 30 | expect_equal(rex, data.frame(x = "SOMAmer.1.12345.666.13", 31 | start = 11L, stop = 22L)) 32 | rex <- locateSeqId("ERVV.1.12531.5.3") 33 | expect_equal(rex, data.frame(x = "ERVV.1.12531.5.3", start = 8L, stop = 16L)) 34 | rex <- locateSeqId("ERVV1.12531.5.3") 35 | expect_equal(rex, data.frame(x = "ERVV1.12531.5.3", start = 7L, stop = 15L)) 36 | }) 37 | 38 | test_that("locateSeqId() works with missing version numbers in SeqIds", { 39 | # 1 digit 40 | rex <- locateSeqId("1231-8") 41 | expect_equal(rex, data.frame(x = "1231-8", start = 1L, stop = 6L)) 42 | # 2 digits 43 | rex <- locateSeqId("1231-54") 44 | expect_equal(rex, data.frame(x = "1231-54", start = 1L, stop = 7L)) 45 | }) 46 | 47 | test_that("locateSeqId() works with missing version numbers in Aptamers", { 48 | # 1 digit 49 | rex <- locateSeqId("ABCD.1231.8") 50 | expect_equal(rex, data.frame(x = "ABCD.1231.8", start = 6L, stop = 11L)) 51 | # 2 digits 52 | rex <- locateSeqId("ABCD.1231.54") 53 | expect_equal(rex, data.frame(x = "ABCD.1231.54", start = 6L, stop = 12L)) 54 | }) 55 | 56 | test_that("locateSeqId() uses the trailing argument", { 57 | rex <- locateSeqId("ABCD.1231.54.EFGH", trailing = TRUE) 58 | expect_equal(rex, data.frame(x = "ABCD.1231.54.EFGH", 59 | start = NA_integer_, stop = NA_integer_)) 60 | rex <- locateSeqId("ABCD.1231.54.EFGH", trailing = FALSE) 61 | expect_equal(rex, data.frame(x = "ABCD.1231.54.EFGH", start = 6L, stop = 12L)) 62 | }) 63 | -------------------------------------------------------------------------------- /tests/testthat/test-matchSeqIds.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("matchSeqIds returns correct 'y'", { 3 | x <- c("seq.4554.56", "seq.3714.49", "PlateId") 4 | y <- c("Fake", "3714-49", "Assay", "4554-56") 5 | # non-SeqId elements should be removed 6 | expect_equal(matchSeqIds(x, y), c("4554-56", "3714-49")) # ordered by 'x' 7 | expect_equal(matchSeqIds(x, y, order.by.x = FALSE), 8 | c("3714-49", "4554-56")) # ordered by 'y' 9 | # when SeqIds are passes as 'x' 10 | expect_equal(matchSeqIds(y, x), c("seq.3714.49", "seq.4554.56")) # ordered by 'y' 11 | expect_equal(matchSeqIds(y, x, order.by.x = FALSE), 12 | c("seq.4554.56", "seq.3714.49")) # ordered by 'x' 13 | }) 14 | -------------------------------------------------------------------------------- /tests/testthat/test-merge-clin.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | clin_file <- system.file("cli/merge", "meta.csv", package = "SomaDataIO", 4 | mustWork = TRUE) 5 | clin_df <- read.csv(clin_file, header = TRUE, colClasses = c(SampleId = "character")) 6 | apts <- withr::with_seed(123, sample(getAnalytes(example_data), 2L)) 7 | adat <- head(example_data, 9L) |> dplyr::select(SampleId, all_of(apts)) 8 | 9 | # Testing ---- 10 | test_that("`merge_clin()` generates expected, merged output", { 11 | merged <- merge_clin(adat, clin_df, by = "SampleId") 12 | expect_true(all(names(adat) %in% names(merged))) 13 | expect_equal(setdiff(names(merged), names(adat)), c("group", "newvar")) 14 | expect_equal(dim(merged), c(9, 5L)) 15 | expect_equal(sum(is.na(merged)), 8L) 16 | expect_equal(sum(merged$newvar, na.rm = TRUE), -1.779255) 17 | }) 18 | 19 | test_that("`merge_clin()` generates same result on `clin_data` argument", { 20 | expect_equal( 21 | merge_clin(adat, clin_df, by = "SampleId"), 22 | merge_clin(adat, clin_file, by = "SampleId", by_class = c(SampleId = "character")) 23 | ) 24 | }) 25 | 26 | test_that("`merge_clin()` errors on bad `clin_data` argument", { 27 | expect_error( merge_clin(adat, letters) ) 28 | expect_error( merge_clin(adat, 1:10L) ) 29 | expect_error( merge_clin(adat, "Samples") ) 30 | expect_error( merge_clin(adat, NA) ) 31 | expect_error( merge_clin(adat, NA_character_) ) 32 | expect_error( merge_clin(data.frame(adat)) ) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/testthat/test-objects.R: -------------------------------------------------------------------------------- 1 | 2 | # Testing ---- 3 | test_that("Sample objects are created properly", { 4 | expect_s3_class(example_data, "soma_adat") 5 | expect_s3_class(ex_target_names, "target_map") 6 | expect_length(ex_analytes, 5284) 7 | expect_length(ex_target_names, 5284) 8 | expect_equal(dim(ex_anno_tbl), c(5284, 22)) 9 | expect_named(ex_anno_tbl, c("AptName", 10 | "SeqId", 11 | "SeqIdVersion", 12 | "SomaId", 13 | "TargetFullName", 14 | "Target", 15 | "UniProt", 16 | "EntrezGeneID", 17 | "EntrezGeneSymbol", 18 | "Organism", 19 | "Units", 20 | "Type", 21 | "Dilution", 22 | "PlateScale_Reference", 23 | "CalReference", 24 | "Cal_Example_Adat_Set001", 25 | "ColCheck", 26 | "CalQcRatio_Example_Adat_Set001_170255", 27 | "QcReference_170255", 28 | "Cal_Example_Adat_Set002", 29 | "CalQcRatio_Example_Adat_Set002_170255", 30 | "Dilution2")) 31 | expect_s3_class(ex_anno_tbl, "tbl_df") 32 | expect_equal(ex_analytes, getAnalytes(example_data)) 33 | expect_named(ex_target_names, ex_analytes) 34 | expect_equal(unlist(ex_target_names, use.names = FALSE), 35 | ex_anno_tbl$TargetFullName) 36 | meta <- c("PlateId", "PlateRunDate", "ScannerID", "PlatePosition", 37 | "SlideId", "Subarray", "SampleId", "SampleType", 38 | "PercentDilution", "SampleMatrix", "Barcode", "Barcode2d", 39 | "SampleName", "SampleNotes", "AliquotingNotes", 40 | "SampleDescription", "AssayNotes", "TimePoint", 41 | "ExtIdentifier", "SsfExtId", "SampleGroup", "SiteId", 42 | "TubeUniqueID", "CLI", "HybControlNormScale", "RowCheck", 43 | "NormScale_20", "NormScale_0_005", "NormScale_0_5", 44 | "ANMLFractionUsed_20", "ANMLFractionUsed_0_005", 45 | "ANMLFractionUsed_0_5", "Age", "Sex") 46 | expect_equal(getMeta(example_data), meta) 47 | expect_equal(dim(example_data), c(192, 5318)) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-parseCheck.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | f <- system.file("extdata", "example_data10.adat", 4 | package = "SomaDataIO", mustWork = TRUE) 5 | lines <- .getHeaderLines(f) |> strsplit("\t", fixed = TRUE) 6 | 7 | # Testing ---- 8 | test_that("`parseCheck()` prints expected output", { 9 | specs <- capture.output(suppressMessages(parseCheck(lines))) 10 | expect_snapshot(specs) 11 | }) 12 | 13 | test_that("`parseCheck()` errors given incorrect `all.tokens` argument", { 14 | expect_error(parseCheck(f), "Format is wrong for the `all.tokens` argument.") 15 | }) 16 | -------------------------------------------------------------------------------- /tests/testthat/test-pivotExpressionSet.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ----- 3 | if ( rlang::is_installed("Biobase") ) { 4 | sub_adat <- example_data[1:10, c(1:5, 35:37)] 5 | long <- pivotExpressionSet(adat2eSet(sub_adat)) 6 | } 7 | 8 | # Testing ---- 9 | test_that("pivotExpressionSet() returns the correct class and dimensions", { 10 | testthat::skip_if_not_installed("Biobase") 11 | expect_s3_class(long, "tbl_df") 12 | expect_equal(dim(long), c(30, 29)) 13 | }) 14 | 15 | test_that("pivotExpressionSet() returns expected metadata columns", { 16 | testthat::skip_if_not_installed("Biobase") 17 | expect_setequal(rownames(sub_adat), long$array_id) 18 | expect_setequal(getAnalytes(sub_adat), long$feature) 19 | expect_true(all(names(attributes(sub_adat)$Col.Meta) %in% names(long))) 20 | expect_true(all(getMeta(sub_adat) %in% names(long))) 21 | }) 22 | 23 | test_that("pivotExpressionSet() preserves analyte values", { 24 | testthat::skip_if_not_installed("Biobase") 25 | expect_equal(sub_adat[order(row.names(sub_adat)),]$seq.10000.28, 26 | subset(long, feature == "seq.10000.28")$value) 27 | }) 28 | -------------------------------------------------------------------------------- /tests/testthat/test-preProcessAdat.R: -------------------------------------------------------------------------------- 1 | # Testing ---- 2 | test_that("`preProcessAdat` applies default arguments as expected", { 3 | expect_snapshot(preProcessAdat(example_data)) 4 | }) 5 | 6 | test_that("`preProcessAdat` produces qc plots as expected", { 7 | expect_snapshot_plot( 8 | out <- capture_output( 9 | sex_plt <- suppressMessages(preProcessAdat(example_data, data.qc = "Sex")) 10 | ), 11 | "preProcessAdat_qc_plot_Sex" 12 | ) 13 | 14 | expect_snapshot_plot( 15 | out <- capture_output( 16 | age_plt <- suppressMessages(preProcessAdat(example_data, data.qc = "Age")) 17 | ), 18 | "preProcessAdat_qc_plot_Age" 19 | ) 20 | }) 21 | 22 | test_that("`preProcessAdat` produces errors as expected", { 23 | # errors on object not `soma_adat` class 24 | expect_error( 25 | preProcessAdat(data.frame(a = 1:3, b = 4:6)), 26 | "`adat` must be a class `soma_adat` object" 27 | ) 28 | 29 | # data.qc variables are not in input adat 30 | expect_error( 31 | suppressMessages( 32 | preProcessAdat(example_data, data.qc = "SUBJECT_AGE_AS_OF_OBS_DATE") 33 | ), 34 | "All variable names passed in `data.qc` argument must exist in `adat`" 35 | ) 36 | }) 37 | -------------------------------------------------------------------------------- /tests/testthat/test-read-adat.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | f <- test_path("testdata", "single_sample.adat") 4 | adat <- read_adat(f) 5 | 6 | # Testing ---- 7 | test_that("`read_adat()` is the correct class", { 8 | expect_s3_class(adat, "soma_adat") 9 | expect_s3_class(adat, "data.frame") 10 | }) 11 | 12 | test_that("`read_adat()` attributes are correct", { 13 | atts <- attributes(adat) 14 | expect_named(atts, c("names", "class", "row.names", "Header.Meta", 15 | "Col.Meta", "file_specs", "row_meta")) 16 | expect_equal(atts$Header.Meta$TABLE_BEGIN, basename(f)) 17 | expect_equal(atts$Header.Meta$ROW_DATA$Name, .setAttr(getMeta(adat), "!Name")) 18 | expect_named(atts$Header.Meta, c("HEADER", "COL_DATA", "ROW_DATA", "TABLE_BEGIN")) 19 | expect_named(atts$Col.Meta,c("SeqId", "SeqIdVersion", "SomaId", "TargetFullName", 20 | "Target", "UniProt", "EntrezGeneID", "EntrezGeneSymbol", 21 | "Organism", "Units", "Type", "Dilution", 22 | "PlateScale_Reference", "CalReference", 23 | "Cal_Example_Adat_Set001", "ColCheck", 24 | "CalQcRatio_Example_Adat_Set001_170255", 25 | "QcReference_170255", "Cal_Example_Adat_Set002", 26 | "CalQcRatio_Example_Adat_Set002_170255", "Dilution2")) 27 | expect_true(all(lengths(atts$Col.Meta) == 5284L)) 28 | # do not test here -> too much output 29 | slim_atts <- atts[!names(atts) %in% c("names", "Col.Meta")] 30 | expect_snapshot( slim_atts ) 31 | }) 32 | 33 | test_that("`read_adat()` the dimensions of the 'soma_adat' object are correct", { 34 | expect_equal(dim(adat), c(1L, 5318L)) 35 | expect_equal(adat$Sex, "M") 36 | }) 37 | 38 | test_that("`read_adat()` produces the correct RFU values", { 39 | withr::local_options(list(digits = 14)) 40 | expect_snapshot(adat$seq.3343.1) # random specific analyte 41 | expect_snapshot(sum(adat[, getAnalytes(adat)])) # sum of all analytes 42 | }) 43 | 44 | test_that("`is_intact_attr()` produces an error when it should", { 45 | expect_true(is_intact_attr(adat)) # good attributes 46 | attributes(adat) <- attributes(adat)[ -c(5L, 6L)] # break attributes 47 | expect_false(is_intact_attr(adat)) 48 | }) 49 | 50 | test_that("an empty ADAT is correctly handled", { 51 | expect_warning( 52 | tbl <- read_adat(test_path("testdata/empty.adat")), 53 | paste("No RFU feature data in ADAT.", 54 | "Returning a `tibble` object with Column Meta data only."), 55 | ) 56 | expect_s3_class(tbl, "tbl_df") 57 | expect_equal(tbl, attr(adat, "Col.Meta"), ignore_attr = TRUE) 58 | }) 59 | 60 | # print.soma_adat ---- 61 | test_that("print.soma_adat() returns original object", { 62 | dump <- tempfile("print-", fileext = ".txt") 63 | withr::local_output_sink(dump) # dump console output 64 | y <- print(adat) 65 | z <- print(adat, show_header = TRUE) 66 | expect_equal(y, adat) 67 | expect_equal(z, adat) 68 | unlink(dump) 69 | }) 70 | 71 | # is.soma_adat ---- 72 | test_that("is.soma_adat() checks class correctly", { 73 | expect_true(is.soma_adat(adat)) 74 | expect_false(is.soma_adat(unclass(adat))) 75 | }) 76 | -------------------------------------------------------------------------------- /tests/testthat/test-read-annotations.R: -------------------------------------------------------------------------------- 1 | 2 | file <- test_path("testdata", "test-anno.xlsx") 3 | 4 | test_that("`ver_dict` is updated and correct", { 5 | expect_length(ver_dict, 6L) 6 | expect_named(ver_dict, 7 | c("SL-99999999-rev99-1999-01", 8 | "SL-12345678-rev0-2021-01", 9 | "SL-00000571-rev2-2021-06", 10 | "SL-00000246-rev5-2021-06", 11 | "SL-00000571-rev7-2024-02", 12 | "SL-00000906-rev4-2024-03")) 13 | }) 14 | 15 | test_that("`getAnnoVer()` parses the version correctly", { 16 | expect_equal(getAnnoVer(file), "SL-12345678-rev0-2021-01") 17 | }) 18 | 19 | test_that("`read_annotations()` parses the annotations file correctly", { 20 | tbl <- read_annotations(file) 21 | expect_s3_class(tbl, "tbl_df") 22 | expect_equal(dim(tbl), c(1L, 43L)) 23 | ver <- attr(tbl, "version") 24 | expect_equal(ver, "SL-12345678-rev0-2021-01") 25 | expect_true(ver_dict[[ver]]$col_serum == names(tbl)[ver_dict[[ver]]$which_serum]) 26 | expect_true(ver_dict[[ver]]$col_plasma == names(tbl)[ver_dict[[ver]]$which_plasma]) 27 | }) 28 | 29 | test_that("error conditions trigger stops when appropriate", { 30 | 31 | expect_error( 32 | read_annotations("foo.txt"), 33 | "Annotations file must be either" 34 | ) 35 | 36 | expect_error( 37 | with_pkg_object(SomaDataIO:::ver_dict[-2L], read_annotations(file)), 38 | "Unknown version of the annotations file:" 39 | ) 40 | 41 | # temp modify md5sha 42 | tmp <- SomaDataIO:::ver_dict 43 | tmp$`SL-12345678-rev0-2021-01`$sha <- "x0x0x0x0x" 44 | expect_warning( 45 | with_pkg_object(tmp, read_annotations(file)), 46 | "Checksum mismatch. 'test-anno.xlsx' may have been modified" 47 | ) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-scaleAnalytes.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | adat <- example_data 4 | apts <- withr::with_seed(101, sample(getAnalytes(adat), 3L)) 5 | short_adat <- adat[, c(getMeta(adat), apts)] |> head(3L) 6 | 7 | # this is to ensure the S3 method is available and dispatched 8 | # otherwise the base::transform.data.frame() method will not 9 | # transform the analytes inside scaleAnalytes() 10 | 11 | test_that("the transform() S3 method exists in the namespace", { 12 | expect_no_error(getS3method("transform", "soma_adat")) 13 | }) 14 | 15 | test_that("the transform() S3 method is listed in methods", { 16 | methods <- unclass(methods("transform", "soma_adat")) 17 | expect_true("transform.soma_adat" %in% methods) 18 | }) 19 | 20 | 21 | # Testing ---- 22 | test_that("`scaleAnalytes()` returns identical adat when scalars are 1.0", { 23 | ref <- setNames(rep(1.0, length(apts)), getSeqId(apts)) 24 | a <- scaleAnalytes(short_adat, ref) 25 | expect_equal(short_adat, a) 26 | }) 27 | 28 | test_that("specific analytes are scaled with non-1.0 values", { 29 | ref <- setNames(c(0.75, 1.1, 1.25), getSeqId(getAnalytes(short_adat))) 30 | # re-order puts reference out of order 31 | # ensures SeqId matching 32 | ref <- ref[c(2, 3, 1L)] 33 | a <- scaleAnalytes(short_adat, ref) 34 | expect_s3_class(a, "soma_adat") 35 | expect_equal(a$seq.3072.4, short_adat$seq.3072.4 * 0.75) 36 | expect_equal(a$seq.18184.28, short_adat$seq.18184.28 * 1.10) 37 | expect_equal(a$seq.4430.44, short_adat$seq.4430.44 * 1.25) 38 | }) 39 | 40 | # Warnings & Errors ---- 41 | test_that("extra refernce analytes are ignored with a warning", { 42 | # extra scaling analytes in reference 43 | ref <- setNames(rep(1.0, length(apts)), getSeqId(apts)) 44 | # ensure SeqId matching 45 | ref <- ref[c(2, 3, 1L)] 46 | ref <- c(ref, "1234-56" = 1.0) # add 1 extra scalar 47 | expect_warning( 48 | a <- scaleAnalytes(short_adat, ref), 49 | "There are extra scaling values (1) in the reference.", 50 | fixed = TRUE 51 | ) 52 | expect_equal(short_adat, a) 53 | }) 54 | 55 | test_that("missing analytes are skipped with a warning", { 56 | ref <- setNames(c(1.0, 1.0), getSeqId(getAnalytes(short_adat))[-2L]) 57 | expect_warning( 58 | b <- scaleAnalytes(short_adat, ref), 59 | "Missing scalar value for (1) analytes. They will not be transformed.", 60 | fixed = TRUE 61 | ) 62 | expect_equal(short_adat, b) 63 | }) 64 | 65 | test_that("no matches returns identical object, with a 1 message & 2 warnings", { 66 | ref <- c("1234-56" = 1.555) 67 | expect_snapshot( new <- scaleAnalytes(short_adat, ref) ) 68 | expect_equal(short_adat, new) 69 | }) 70 | 71 | test_that("`scaleAnalytes()` only accepts the `soma_adat` class", { 72 | bad_adat <- as.data.frame(short_adat) 73 | expect_snapshot(scaleAnalytes(bad_adat), error = TRUE) 74 | }) 75 | -------------------------------------------------------------------------------- /tests/testthat/test-syncColMeta.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("`syncColMeta()` updates the Col.Meta data properly", { 3 | # `syncColMeta()` is called internally in `[` 4 | # we must by-pass it to force the Col.Meta out-of-sync 5 | # call the `data.frame` S3 method directly 6 | x <- mock_adat() 7 | rmcol <- ncol(x) - 1L 8 | new <- `[.data.frame`(x, -rmcol) # rm analyte/column 9 | 10 | # add back Col.Meta to give `syncColMeta()` something to act upon 11 | attr(new, "Col.Meta") <- attr(x, "Col.Meta") 12 | 13 | # do the update 14 | new2 <- syncColMeta(new) 15 | truth <- tibble::tibble( 16 | SeqId = c("1234-56", "9898-99"), 17 | UniProt = c("P04321", "P04323"), 18 | EntrezGeneSymbol = c("MMP1", "MMP3"), 19 | Target = c("MMP-1", "MMP-3"), 20 | Organism = c("Human", "Human"), 21 | Units = c("RFU", "RFU"), 22 | Type = c("Protein", "Protein"), 23 | Dilution = c("0.005", "40"), 24 | CalReference = c(0.4, 0.8) 25 | ) 26 | expect_equal(truth, attr(new2, "Col.Meta")) 27 | }) 28 | -------------------------------------------------------------------------------- /tests/testthat/test-tidyr-verbs.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | data <- mock_adat() 4 | data$a <- LETTERS[1:6L] 5 | data$d <- as.character(1:6) 6 | data$b <- paste0(data$a, "-", data$d) 7 | 8 | 9 | # Testing ---- 10 | test_that("separate() method produces expected output", { 11 | new <- separate(data, b, into = c("b", "c")) 12 | expect_s3_class(new, "soma_adat") 13 | expect_true(is_intact_attr(new)) 14 | expect_equal(class(new), class(data)) 15 | expect_equal(dim(new), dim(data) + c(0, 1L)) # 1 new column 16 | expect_true("c" %in% names(new)) 17 | expect_true("b" %in% names(new)) 18 | expect_equal(rownames(new), rownames(data)) 19 | expect_equal(new$a, new$b) 20 | expect_equal(new$c, new$d) 21 | expect_true(is.soma_adat(new)) 22 | }) 23 | 24 | test_that("lazy eval works identically among 3 main inputs for `col=`", { 25 | x <- separate(data, "b", into = c("b", "c")) # quoted string 26 | v <- "b" 27 | y <- separate(data, v, into = c("b", "c")) # variable 28 | z <- separate(data, b, into = c("b", "c")) # unquoted string 29 | expect_equal(x, y) 30 | expect_equal(x, z) 31 | expect_equal(y, z) 32 | }) 33 | 34 | test_that("unite() method produces expected output", { 35 | new <- unite(data, "combo", c("a", "d"), sep = "-") 36 | expect_s3_class(new, "soma_adat") 37 | expect_true(is_intact_attr(new)) 38 | expect_equal(class(new), class(data)) 39 | expect_equal(dim(new), dim(data) + c(0, -1L)) # 1 less column 40 | expect_true("combo" %in% names(new)) 41 | expect_false("a" %in% names(new)) 42 | expect_false("d" %in% names(new)) 43 | expect_equal(rownames(new), rownames(data)) 44 | expect_equal(new$combo, new$b) 45 | expect_true(is.soma_adat(new)) 46 | }) 47 | -------------------------------------------------------------------------------- /tests/testthat/test-utils-release.R: -------------------------------------------------------------------------------- 1 | 2 | # Testing ---- 3 | test_that("`.bullets()` prints expected output", { 4 | expect_snapshot(.bullets("prepare")) 5 | expect_snapshot(.bullets("submit")) 6 | expect_snapshot(.bullets("wait")) 7 | }) 8 | 9 | test_that("`.ver_type()` returns expected release types", { 10 | expect_equal(.ver_type("1.0.0"), "major") 11 | expect_equal(.ver_type("0.1.0"), "minor") 12 | expect_equal(.ver_type("0.0.1"), "patch") 13 | expect_equal(.ver_type("0.0.0.1"), "dev") 14 | }) 15 | 16 | test_that("`.create_checklist()` prints expected output", { 17 | expect_snapshot(.create_checklist("1.0.0")) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/testthat/test-with-interactive.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("interactive session can be forced ON, but only within temp scope", { 3 | with_interactive(TRUE, { 4 | expect_true(interactive()) 5 | }) 6 | with_interactive(FALSE, { 7 | expect_false(interactive()) 8 | }) 9 | expect_false(interactive()) # FALSE during testthat 10 | }) 11 | -------------------------------------------------------------------------------- /tests/testthat/test-write-adat.R: -------------------------------------------------------------------------------- 1 | 2 | # Setup ---- 3 | f <- test_path("testdata", "single_sample.adat") 4 | adat <- read_adat(f) 5 | withr::local_options(list(usethis.quiet = TRUE)) # silence ui signalling 6 | 7 | # Testing ---- 8 | test_that("`write_adat()` produces unchanged out -> in -> out", { 9 | f_check <- tempfile("write-", fileext = ".adat") 10 | true_lines <- readLines(f) 11 | write_adat(adat, file = f_check) 12 | test_lines <- readLines(f_check) 13 | # certain lines are expected change: 14 | # the *History lines in HEADER; CreatedBy & CreatedDate changes 15 | test_lines <- grep("^!?Created[DB][ay]", test_lines, 16 | invert = TRUE, value = TRUE) 17 | true_lines <- grep("^!?Created[DB][ay]", true_lines, 18 | invert = TRUE, value = TRUE) 19 | expect_equal(true_lines, test_lines) 20 | unlink(f_check) 21 | }) 22 | 23 | test_that("`write_adat()` produces unchanged in -> out -> in", { 24 | f_check <- tempfile("write-", fileext = ".adat") 25 | write_adat(adat, file = f_check) 26 | # some attributes are expected to be false: 27 | # TABLE_BEGIN will shift due to CreatedBy & CreatedDate changes 28 | expect_equal(read_adat(f_check), adat, ignore_attr = TRUE) 29 | unlink(f_check) 30 | }) 31 | 32 | test_that("`write_adat()` throws error when no file name is passed", { 33 | expect_error(write_adat(adat), "Must provide output file name ...") 34 | }) 35 | 36 | test_that("`write_adat()` throws warning when passing invalid file format", { 37 | bad_ext <- tempfile("write-", fileext = ".txt") 38 | if ( tolower(Sys.info()[["sysname"]]) == "windows" ) { 39 | # path sep '\` on windows gets messy with the warning match 40 | match <- "File extension is not `*.adat`" 41 | } else { 42 | match <- paste0( 43 | "File extension is not `*.adat` ('", bad_ext, "'). ", 44 | "Are you sure this is the correct file extension?" 45 | ) 46 | } 47 | expect_warning(write_adat(adat, file = bad_ext), match, fixed = TRUE) 48 | unlink(bad_ext) 49 | }) 50 | 51 | test_that("`write_adat()` shifts Col.Meta correctly when clinical data added/removed", { 52 | # rm meta data 53 | f_check <- tempfile("write-", fileext = ".adat") 54 | short <- dplyr::select(head(adat), 55 | SlideId, Subarray, SampleGroup, 56 | seq.2182.54, seq.2190.55) 57 | write_adat(short, file = f_check) 58 | expect_equal(read_adat(f_check), short, ignore_attr = TRUE) 59 | expect_equal(getMeta(short), getMeta(read_adat(f_check))) 60 | unlink(f_check) 61 | 62 | # add meta data 63 | f_check2 <- tempfile("write-", fileext = ".adat") 64 | long <- head(adat) 65 | long$foo <- "bar" 66 | write_adat(long, file = f_check2) # write_adat() re-orders meta to come 1st! 67 | new <- read_adat(f_check2) 68 | expect_equal(new[, getMeta(new)], long[, getMeta(long)], ignore_attr = TRUE) 69 | expect_equal(new[, getAnalytes(new)], long[, getAnalytes(long)], ignore_attr = TRUE) 70 | unlink(f_check2) 71 | }) 72 | -------------------------------------------------------------------------------- /tests/testthat/testdata/test-anno.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomaLogic/SomaDataIO/254fd7148eab422caa6ada2e0f722c53ea6cf6cf/tests/testthat/testdata/test-anno.xlsx -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.png 3 | *.R 4 | -------------------------------------------------------------------------------- /vignettes/articles/tips-safely-map-values.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Safely Map Values via dplyr::left_join()" 3 | author: "Stu Field, SomaLogic Operating Co., Inc." 4 | description: > 5 | How to safely and reliably map variable between 6 | data frame columns. 7 | output: 8 | rmarkdown::html_vignette: 9 | fig_caption: yes 10 | vignette: > 11 | %\VignetteIndexEntry{Safely Map Values via dplyr::left_join()} 12 | %\VignetteEngine{knitr::rmarkdown} 13 | %\VignetteEncoding{UTF-8} 14 | --- 15 | 16 | ```{r setup, include = FALSE} 17 | library(dplyr) 18 | knitr::opts_chunk$set( 19 | collapse = TRUE, 20 | comment = "#>" 21 | ) 22 | ``` 23 | 24 | 25 | # Introduction 26 | 27 | Mapping values in one column to specific values in another (new) column 28 | of a data frame is a common task in data science. Doing so *safely* is 29 | often a struggle. There are some existing methods in the `tidyverse` 30 | that are useful, but in my opinion come with some drawbacks: 31 | 32 | * `dplyr::recode()` 33 | + can be clunky to implement -> LHS/RHS syntax 34 | difficult (for me) to remember 35 | * `dplyr::case_when()` 36 | + complex syntax -> difficult to remember; overkill 37 | for mapping purposes 38 | 39 | Below is what I see is a *safe* way to map (re-code) values in an existing 40 | column to a new column. 41 | 42 | 43 | -------------- 44 | 45 | 46 | ## Mapping Example 47 | 48 | ```{r map-values} 49 | # wish to map values of 'x' 50 | df <- withr::with_seed(101, { 51 | data.frame(id = 1:10L, 52 | value = rnorm(10), 53 | x = sample(letters[1:3L], 10, replace = TRUE) 54 | ) 55 | }) 56 | df 57 | 58 | # create a [n x 2] lookup-table (aka hash map) 59 | # n = no. values to map 60 | # x = existing values to map 61 | # new_x = new mapped values for each `x` 62 | map <- data.frame(x = letters[1:4L], new_x = c("cat", "dog", "bird", "turtle")) 63 | map 64 | 65 | # use `dplyr::left_join()` 66 | # note: 'turtle' is absent because `d` is not in `df$x` (thus ignored) 67 | dplyr::left_join(df, map) 68 | ``` 69 | 70 | 71 | ## Un-mapped Values -> `NAs` 72 | 73 | Notice that `b` maps to `NA`. This is because the mapping object now 74 | lacks a mapping for `b` (compare to row 2 above). 75 | Using a slightly different syntax via `tibble::enframe()`. 76 | 77 | ```{r unmapped-NA} 78 | # note: `b` is missing in the map 79 | map_vec <- c(a = "cat", c = "bird", d = "turtle") 80 | map2 <- tibble::enframe(map_vec, name = "x", value = "new_x") 81 | map2 82 | 83 | # note: un-mapped values generate NAs: `b -> NA` 84 | dplyr::left_join(df, map2, by = "x") 85 | ``` 86 | -------------------------------------------------------------------------------- /vignettes/articles/tips-safely-rename-df.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Safely Rename Data Frames" 3 | author: "Stu Field, Standard BioTools, Inc." 4 | description: > 5 | How to safely and reliably rename variable names of a 6 | data frame (or `soma_adat`) in R. 7 | output: 8 | rmarkdown::html_vignette: 9 | fig_caption: yes 10 | vignette: > 11 | %\VignetteIndexEntry{Safely Rename Data Frames} 12 | %\VignetteEngine{knitr::rmarkdown} 13 | %\VignetteEncoding{UTF-8} 14 | --- 15 | 16 | ```{r setup, include = FALSE} 17 | library(SomaDataIO) 18 | library(dplyr) 19 | knitr::opts_chunk$set( 20 | collapse = TRUE, 21 | comment = "#>" 22 | ) 23 | ``` 24 | 25 | 26 | # Introduction 27 | 28 | Renaming variables/features of a data frame (or `tibble`) 29 | is a common task in data science. Doing so *safely* is often a struggle. 30 | This can be achieved *safely* via the `dplyr::rename()` function via 2 31 | steps: 32 | 33 | 1. Set up the mapping in either a named vector 34 | 1. Apply the `dplyr::rename()` function via `!!!` syntax 35 | 1. Alternatively, roll-your-own `rename()` function 36 | 37 | * **Note**: all entries in the mapping (i.e. key) object *must* be 38 | present as `names` in the data frame object. 39 | 40 | 41 | ## Example with `mtcars` 42 | 43 | ```{r rename-df} 44 | # Create map/key of the names to map 45 | key <- c(MPG = "mpg", CARB = "carb") # named vector 46 | key 47 | 48 | # rename `mtcars` 49 | rename(mtcars, !!! key) |> head() 50 | ``` 51 | 52 | 53 | 54 | ## A SomaScan example (`example_data`) 55 | 56 | Occasionally it might be required to 57 | rename `AptNames` (`seq.1234.56`) -> `SeqIds` (`1234-56`) when 58 | analyzing SomaScan data. 59 | 60 | ```{r rename-sim} 61 | getAnalytes(example_data) |> 62 | head() 63 | 64 | # create map (named vector) 65 | key2 <- getAnalytes(example_data) 66 | names(key2) <- getSeqId(key2) # re-name `seq.XXXX` -> SeqIds 67 | key2 <- c(key2, ID = "SampleId") # SampleId -> ID 68 | head(key2, 10L) 69 | 70 | # rename analytes of `example_data` 71 | getAnalytes(example_data) |> 72 | head(10L) 73 | 74 | new <- rename(example_data, !!! key2) 75 | 76 | getAnalytes(new) |> 77 | head(10L) 78 | ``` 79 | 80 | ## Alternative to `dplyr` 81 | If you prefer to avoid the `dplyr` import/dependency, you can achieve a 82 | similar result with similar syntax by writing your own renaming function: 83 | 84 | ```{r rename2} 85 | rename2 <- function (.data, ...) { 86 | map <- c(...) 87 | loc <- setNames(match(map, names(.data), nomatch = 0L), names(map)) 88 | loc <- loc[loc > 0L] 89 | newnames <- names(.data) 90 | newnames[loc] <- names(loc) 91 | setNames(.data, newnames) 92 | } 93 | ``` 94 | 95 | Now, with *similar* syntax (but cannot use `!!!`): 96 | 97 | ```{r rename-usage} 98 | # rename `mtcars` in-line 99 | rename2(mtcars, MPG = "mpg", CARB = "carb") |> 100 | head() 101 | 102 | # rename `mtcars` via named `key` 103 | rename2(mtcars, key) |> 104 | head() 105 | ``` 106 | 107 | -------------------------------------------------------------------------------- /vignettes/articles/tips-train-test-setup.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Common train-test data setups" 3 | author: "Stu Field, Standard BioTools, Inc." 4 | description: > 5 | Simple code syntax for common machine learning (ML) 6 | training vs test setups in R. 7 | output: 8 | rmarkdown::html_vignette: 9 | fig_caption: yes 10 | vignette: > 11 | %\VignetteIndexEntry{Common train-test data setups} 12 | %\VignetteEngine{knitr::rmarkdown} 13 | %\VignetteEncoding{UTF-8} 14 | --- 15 | 16 | ```{r setup, include = FALSE} 17 | library(dplyr) 18 | knitr::opts_chunk$set( 19 | collapse = TRUE, 20 | comment = "#>" 21 | ) 22 | options(width = 90) 23 | ``` 24 | 25 | 26 | # Introduction 27 | 28 | Most machine learning (ML) analyses require a random split of original data 29 | into training/test data sets, where the model is fit on the training data 30 | and performance is evaluated on the test data set. The split proportions can 31 | vary, though 80/20 training/test is common. It often depends on the number of 32 | available samples and the class distribution in the splits. 33 | 34 | Among many alternatives, there are 3 common approaches, all are equally 35 | viable and depend on the analyst's weighing of pros/cons of each. 36 | I recommend one of these below: 37 | 38 | 1. base R data frame indexing with [sample()] and `[` 39 | 1. use `dplyr::slice_sample()` or `dplyr::sample_frac()` in 40 | combination with `dplyr::anti_join()` 41 | 1. use the [rsample](https://rsample.tidymodels.org) package (not demonstrated) 42 | 43 | --------- 44 | 45 | 46 | ## Original Raw Data 47 | 48 | In most analyses, you typically start with a raw original data set that 49 | you must split randomly into training and test sets. 50 | 51 | ```{r raw-data} 52 | raw <- SomaDataIO::rn2col(head(mtcars, 10L), "CarModel") |> 53 | SomaDataIO::add_rowid("id") |> # set up identifier variable for the join() 54 | tibble::as_tibble() 55 | raw 56 | ``` 57 | 58 | 59 | --------- 60 | 61 | 62 | ## Option #1: `sample()` 63 | 64 | ```{r train-test1} 65 | n <- nrow(raw) 66 | idx <- withr::with_seed(1, sample(1:n, floor(n / 2))) # sample random 50% (n = 5) 67 | train <- raw[idx, ] 68 | test <- raw[-idx, ] 69 | train 70 | 71 | test 72 | ``` 73 | 74 | 75 | ## Option #2: `anti_join()` 76 | 77 | ```{r train-test2} 78 | # sample random 50% (n = 5) 79 | train <- withr::with_seed(1, dplyr::slice_sample(raw, n = floor(n / 2))) 80 | 81 | # or using `dplyr::sample_frac()` 82 | # train <- withr::with_seed(1, dplyr::sample_frac(raw, size = 0.5)) 83 | 84 | # use anti_join() to get the sample setdiff 85 | test <- dplyr::anti_join(raw, train, by = "id") 86 | train 87 | 88 | test 89 | ``` 90 | --------------------------------------------------------------------------------