├── .Rbuildignore
├── .air.toml
├── .github
├── .gitignore
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ ├── R-CMD-check.yaml
│ ├── lint.yaml
│ ├── pkgdown.yaml
│ └── pr-commands.yaml
├── .gitignore
├── .lintr
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── NAMESPACE
├── NEWS.md
├── R
├── AbstractAnnData.R
├── AnnData-usage.R
├── AnnData.R
├── HDF5AnnData.R
├── InMemoryAnnData.R
├── anndataR-package.R
├── anndata_constructors.R
├── as_AnnData.R
├── as_Seurat.R
├── as_SingleCellExperiment.R
├── check_requires.R
├── from_Seurat.R
├── from_SingleCellExperiment.R
├── generate_dataframe.R
├── generate_dataset.R
├── generate_matrix.R
├── generate_vector.R
├── known_issues.R
├── read_h5ad.R
├── read_h5ad_helpers.R
├── ui.R
├── utils.R
├── write_h5ad.R
├── write_h5ad_helpers.R
└── write_hdf5_helpers.R
├── README.md
├── _pkgdown.yml
├── anndataR.Rproj
├── inst
├── extdata
│ └── example.h5ad
├── known_issues.yaml
└── scripts
│ └── example_h5ad.py
├── man
├── AbstractAnnData.Rd
├── AnnData-usage.Rd
├── AnnData.Rd
├── HDF5AnnData.Rd
├── InMemoryAnnData.Rd
├── anndataR-package.Rd
├── as_AnnData.Rd
├── as_HDF5AnnData.Rd
├── as_InMemoryAnnData.Rd
├── as_Seurat.Rd
├── as_SingleCellExperiment.Rd
├── figures
│ ├── lifecycle-deprecated.svg
│ ├── lifecycle-experimental.svg
│ ├── lifecycle-stable.svg
│ ├── lifecycle-superseded.svg
│ ├── logo.png
│ └── logo.svg
├── from_Seurat.Rd
├── from_SingleCellExperiment.Rd
├── generate_dataset.Rd
├── read_h5ad.Rd
├── to_Seurat.Rd
├── to_SingleCellExperiment.Rd
└── write_h5ad.Rd
├── tests
├── testthat.R
└── testthat
│ ├── helper-expect_equal_py.R
│ ├── helper-py-R-equivalences.R
│ ├── helper-skip_if_no_anndata.R
│ ├── helper-skip_if_no_h5diff.R
│ ├── test-HDF5-read.R
│ ├── test-HDF5-write.R
│ ├── test-HDF5AnnData.R
│ ├── test-InMemoryAnnData.R
│ ├── test-as_Seurat.R
│ ├── test-as_SingleCellExperiment.R
│ ├── test-from_Seurat.R
│ ├── test-from_SingleCellExperiment.R
│ ├── test-generate_dataset.R
│ ├── test-h5ad-fileclosure.R
│ ├── test-h5ad-read.R
│ ├── test-has_row_names.R
│ ├── test-roundtrip-X.R
│ ├── test-roundtrip-empty.R
│ ├── test-roundtrip-layers.R
│ ├── test-roundtrip-obsmvarm.R
│ ├── test-roundtrip-obspvarp.R
│ ├── test-roundtrip-obsvar.R
│ ├── test-roundtrip-uns-nested.R
│ ├── test-roundtrip-uns.R
│ └── test-utils.R
└── vignettes
├── .gitignore
├── anndataR.Rmd
├── development_status.Rmd
├── diagrams
├── class_diagram.mmd
├── class_diagram.svg
└── script.sh
├── known_issues.Rmd
├── seurat_mapping.Rmd
├── singlecellexperiment_mapping.Rmd
├── software_design.Rmd
├── usage_h5ad.Rmd
├── usage_seurat.Rmd
└── usage_singlecellexperiment.Rmd
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^LICENSE\.md$
2 | ^.*\.Rproj$
3 | ^\.Rproj\.user$
4 | ^\.github$
5 | ^README\.qmd$
6 | ^\.metals$
7 | ^\.vscode$
8 | ^\.lintr$
9 | ^\.air.toml$
10 | ^_pkgdown\.yml$
11 | ^docs$
12 | ^pkgdown$
13 | ^vignettes/diagrams/*\.mmd$
14 | ^vignettes/diagrams/*\.svg$
15 | ^vignettes/design\.Rmd$
--------------------------------------------------------------------------------
/.air.toml:
--------------------------------------------------------------------------------
1 | [format]
2 | line-width = 80
3 | indent-width = 2
4 | indent-style = "space"
5 | line-ending = "auto"
6 | persistent-line-breaks = true
7 | exclude = []
8 | default-exclude = true
9 |
--------------------------------------------------------------------------------
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | # github:
4 | # patreon: # Replace with a single Patreon username
5 | open_collective: anndatar
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | # polar: # Replace with a single Polar username
13 | # buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | custom: https://www.data-intuitive.com
15 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 |
8 | name: R-CMD-check
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | R-CMD-check:
16 | runs-on: ${{ matrix.config.os }}
17 |
18 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
19 |
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | config:
24 | - {os: macos-latest, r: 'release'}
25 | - {os: windows-latest, r: 'release'}
26 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
27 | - {os: ubuntu-latest, r: 'release'}
28 | - {os: ubuntu-latest, r: 'oldrel-1'}
29 |
30 | env:
31 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
32 | R_KEEP_PKG_SOURCE: yes
33 |
34 | steps:
35 | - uses: actions/checkout@v4
36 |
37 | - uses: r-lib/actions/setup-pandoc@v2
38 |
39 | - uses: r-lib/actions/setup-r@v2
40 | with:
41 | r-version: ${{ matrix.config.r }}
42 | http-user-agent: ${{ matrix.config.http-user-agent }}
43 | use-public-rspm: true
44 |
45 | - uses: r-lib/actions/setup-r-dependencies@v2
46 | with:
47 | extra-packages: any::rcmdcheck
48 | needs: check
49 |
50 | - name: Cache miniconda
51 | uses: actions/cache@v4
52 | if: runner.os == 'Linux'
53 | # no need to cache on macOS or Windows, since it takes longer to unpack
54 | # the cache than to install miniconda and dependencies
55 | with:
56 | path: ~/.local/share/r-miniconda
57 | key: ${{ runner.os }}-v${{ matrix.config.r }}-reticulate-miniconda
58 |
59 | - name: Install Python dependencies for reticulate
60 | run: |
61 | if (!reticulate:::miniconda_exists()) {
62 | reticulate::install_miniconda()
63 | }
64 | reticulate::py_install(c("anndata", "scanpy", "dummy-anndata"), pip = TRUE)
65 | shell: Rscript {0}
66 |
67 | - name: Install h5diff
68 | run: |
69 | if [ "$RUNNER_OS" == "Linux" ]; then
70 | sudo apt-get install hdf5-tools
71 | fi
72 | shell: bash
73 |
74 | - uses: r-lib/actions/check-r-package@v2
75 | with:
76 | upload-snapshots: true
77 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 |
8 | name: lint
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
12 | cancel-in-progress: true
13 |
14 | jobs:
15 | lint:
16 | runs-on: ubuntu-latest
17 | env:
18 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - uses: r-lib/actions/setup-r@v2
23 | with:
24 | use-public-rspm: true
25 |
26 | - name: Install air
27 | run: curl -LsSf https://github.com/posit-dev/air/releases/latest/download/air-installer.sh | sh
28 |
29 | - uses: r-lib/actions/setup-r-dependencies@v2
30 | with:
31 | extra-packages: any::lintr, any::styler, local::.
32 | needs: lint
33 |
34 | - name: Lint
35 | run: lintr::lint_package()
36 | shell: Rscript {0}
37 | env:
38 | LINTR_ERROR_ON_LINT: true
39 |
40 | - name: Check code style
41 | run: air format --check .
42 |
43 | - name: Check vignette style
44 | run: styler::style_dir("vignettes", dry = "fail")
45 | shell: Rscript {0}
46 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | release:
8 | types: [published]
9 | workflow_dispatch:
10 |
11 | name: pkgdown
12 |
13 | jobs:
14 | pkgdown:
15 | runs-on: ubuntu-latest
16 | # Only restrict concurrency for non-PR jobs
17 | concurrency:
18 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
19 | env:
20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
21 | permissions:
22 | contents: write
23 | steps:
24 | - uses: actions/checkout@v4
25 |
26 | - uses: r-lib/actions/setup-pandoc@v2
27 |
28 | - uses: r-lib/actions/setup-r@v2
29 | with:
30 | use-public-rspm: true
31 |
32 | - uses: r-lib/actions/setup-r-dependencies@v2
33 | with:
34 | extra-packages: local::.
35 | needs: website
36 |
37 | - name: Build site
38 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
39 | shell: Rscript {0}
40 |
41 | - name: Deploy to GitHub pages 🚀
42 | if: github.event_name != 'pull_request'
43 | uses: JamesIves/github-pages-deploy-action@v4
44 | with:
45 | clean: false
46 | repository-name: data-intuitive/anndataR
47 | branch: gh-pages
48 | folder: docs
49 | token: ${{ secrets.GTHB_PAT }}
50 |
--------------------------------------------------------------------------------
/.github/workflows/pr-commands.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 | issue_comment:
5 | types: [created]
6 |
7 | name: pr-commands
8 |
9 | permissions: read-all
10 |
11 | jobs:
12 | document:
13 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR') && startsWith(github.event.comment.body, '/document') }}
14 | name: document
15 | runs-on: ubuntu-latest
16 | env:
17 | GITHUB_PAT: ${{ secrets.GTHB_PAT }}
18 | permissions:
19 | contents: write
20 | steps:
21 | - uses: actions/checkout@v4
22 | with:
23 | persist-credentials: false
24 |
25 | - uses: r-lib/actions/pr-fetch@v2
26 | with:
27 | repo-token: ${{ secrets.GTHB_PAT }}
28 |
29 | - uses: r-lib/actions/setup-r@v2
30 | with:
31 | use-public-rspm: true
32 |
33 | - uses: r-lib/actions/setup-r-dependencies@v2
34 | with:
35 | extra-packages: any::roxygen2
36 | needs: pr-document
37 |
38 | - name: Document
39 | run: roxygen2::roxygenise()
40 | shell: Rscript {0}
41 |
42 | - name: commit
43 | run: |
44 | git config --local user.name "$GITHUB_ACTOR"
45 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
46 | git add man/\* NAMESPACE DESCRIPTION
47 | git commit -m 'Document (GHA)' || echo "No changes to commit"
48 |
49 | - uses: r-lib/actions/pr-push@v2
50 | with:
51 | repo-token: ${{ secrets.GTHB_PAT }}
52 |
53 | style:
54 | if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR') && startsWith(github.event.comment.body, '/style') }}
55 | name: style
56 | runs-on: ubuntu-latest
57 | env:
58 | GITHUB_PAT: ${{ secrets.GTHB_PAT }}
59 | permissions:
60 | contents: write
61 | steps:
62 | - uses: actions/checkout@v4
63 | with:
64 | persist-credentials: false
65 |
66 | - uses: r-lib/actions/pr-fetch@v2
67 | with:
68 | repo-token: ${{ secrets.GTHB_PAT }}
69 |
70 | - uses: r-lib/actions/setup-r@v2
71 |
72 | - name: Install air
73 | run: curl -LsSf https://github.com/posit-dev/air/releases/latest/download/air-installer.sh | sh
74 |
75 | - name: Install styler
76 | uses: r-lib/actions/setup-r-dependencies@v2
77 | with:
78 | packages: styler, roxygen2
79 |
80 | - name: Style code
81 | run: air format .
82 |
83 | - name: Style vignettes
84 | run: styler::style_dir("vignettes/")
85 | shell: Rscript {0}
86 |
87 | - name: Commit changes
88 | run: |
89 | if FILES_TO_COMMIT=($(git diff-index --name-only ${{ github.sha }} \
90 | | egrep --ignore-case '\.(R|[qR]md|Rmarkdown|Rnw|Rprofile)$'))
91 | then
92 | git config --local user.name "$GITHUB_ACTOR"
93 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
94 | git commit ${FILES_TO_COMMIT[*]} -m "Style code (GHA)"
95 | else
96 | echo "No changes to commit"
97 | fi
98 |
99 | - uses: r-lib/actions/pr-push@v2
100 | with:
101 | repo-token: ${{ secrets.GTHB_PAT }}
102 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # History files
2 | .Rhistory
3 | .Rapp.history
4 |
5 | # Session Data files
6 | .RData
7 | .RDataTmp
8 |
9 | # User-specific files
10 | .Ruserdata
11 |
12 | # Example code in package build process
13 | *-Ex.R
14 |
15 | # Output files from R CMD build
16 | /*.tar.gz
17 |
18 | # Output files from R CMD check
19 | /*.Rcheck/
20 |
21 | # RStudio files
22 | .Rproj.user/
23 |
24 | # VScode files
25 | .metals/
26 | .vscode/
27 |
28 | # produced vignettes
29 | vignettes/*.html
30 | vignettes/*.pdf
31 | inst/doc
32 |
33 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
34 | .httr-oauth
35 |
36 | # knitr and R markdown default cache directories
37 | *_cache/
38 | /cache/
39 |
40 | # Temporary files created by R markdown
41 | *.utf8.md
42 | *.knit.md
43 |
44 | # R Environment Variables
45 | .Renviron
46 |
47 | # pkgdown site
48 | docs/
49 |
50 | # translation temp files
51 | po/*~
52 |
53 | # RStudio Connect folder
54 | rsconnect/
55 |
--------------------------------------------------------------------------------
/.lintr:
--------------------------------------------------------------------------------
1 | linters: linters_with_defaults(
2 | line_length_linter = line_length_linter(120L),
3 | object_name_linter = object_name_linter(styles = c("snake_case", "symbols", "CamelCase", "SNAKE_CASE"))
4 | )
5 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: anndataR
2 | Title: AnnData interoperability in R
3 | Version: 0.99.0
4 | Authors@R: c(
5 | person("Robrecht", "Cannoodt", , "robrecht@data-intuitive.com", role = c("aut", "cre"),
6 | comment = c(ORCID = "0000-0003-3641-729X", github = "rcannood")),
7 | person("Luke", "Zappia", , "luke@lazappi.id.au", role = "aut",
8 | comment = c(ORCID = "0000-0001-7744-8565", github = "lazappi")),
9 | person("Martin", "Morgan", , "mtmorgan.bioc@gmail.com", role = "aut",
10 | comment = c(ORCID = "0000-0002-5874-8148", github = "mtmorgan")),
11 | person("Louise", "Deconinck", , "louise.deconinck@gmail.com", role = "aut",
12 | comment = c(ORCID = "0000-0001-8100-6823", github = "LouiseDck")),
13 | person("Danila", "Bredikhin", , "danila.bredikhin@embl.de", role = "ctb",
14 | comment = c(ORCID = "0000-0001-8089-6983", github = "gtca")),
15 | person("Isaac", "Virshup", role = "ctb",
16 | comment = c(ORCID = "0000-0002-1710-8945", github = "ivirshup")),
17 | person("Brian", "Schilder", , "brian_schilder@alumni.brown.edu", role = "ctb",
18 | comment = c(ORCID = "0000-0001-5949-2191", github = "bschilder")),
19 | person("Chananchida", "Sang-aram", role = "ctb",
20 | comment = c(ORCID = "0000-0002-0922-0822", github = "csangara")),
21 | person("Data Intuitive", , , "info@data-intuitive.com", role = c("fnd", "cph")),
22 | person("Chan Zuckerberg Initiative", role = "fnd")
23 | )
24 | Description: Bring the power and flexibility of AnnData to the R
25 | ecosystem, allowing you to effortlessly manipulate and analyze your
26 | single-cell data. This package lets you work with backed h5ad and zarr
27 | files, directly access various slots (e.g. X, obs, var), or convert
28 | the data into SingleCellExperiment and Seurat objects.
29 | License: MIT + file LICENSE
30 | URL: https://anndatar.data-intuitive.com/,
31 | https://github.com/scverse/anndataR
32 | BugReports: https://github.com/scverse/anndataR/issues
33 | Depends:
34 | R (>= 4.0.0)
35 | Imports:
36 | cli,
37 | lifecycle,
38 | Matrix,
39 | methods,
40 | purrr,
41 | R6 (>= 2.4.0),
42 | rlang,
43 | stats
44 | Suggests:
45 | anndata,
46 | BiocStyle,
47 | hdf5r (>= 1.3.11),
48 | knitr,
49 | processx,
50 | reticulate (>= 1.36.1),
51 | rmarkdown,
52 | S4Vectors,
53 | Seurat,
54 | SeuratObject,
55 | SingleCellExperiment,
56 | SummarizedExperiment,
57 | testthat (>= 3.0.0),
58 | vctrs,
59 | withr,
60 | yaml
61 | VignetteBuilder:
62 | knitr
63 | biocViews: SingleCell, DataImport, DataRepresentation
64 | Config/Needs/website: pkgdown, tibble, knitr, rprojroot, stringr, readr,
65 | purrr, dplyr, tidyr
66 | Config/testthat/edition: 3
67 | Encoding: UTF-8
68 | Roxygen: list(markdown = TRUE, r6 = TRUE)
69 | RoxygenNote: 7.3.2
70 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2023
2 | COPYRIGHT HOLDER: Robrecht Cannoodt, Luke Zappia, Martin Morgan, Louise Deconinck
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2023 Robrecht Cannoodt, Luke Zappia, Martin Morgan, Louise Deconinck
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 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | S3method(as_AnnData,Seurat)
4 | S3method(as_AnnData,SingleCellExperiment)
5 | export(AnnData)
6 | export(as_AnnData)
7 | export(from_Seurat)
8 | export(from_SingleCellExperiment)
9 | export(generate_dataset)
10 | export(read_h5ad)
11 | export(to_Seurat)
12 | export(to_SingleCellExperiment)
13 | export(write_h5ad)
14 | importFrom(Matrix,as.matrix)
15 | importFrom(Matrix,sparseMatrix)
16 | importFrom(Matrix,t)
17 | importFrom(R6,R6Class)
18 | importFrom(cli,cli_abort)
19 | importFrom(cli,cli_inform)
20 | importFrom(cli,cli_warn)
21 | importFrom(lifecycle,deprecated)
22 | importFrom(methods,as)
23 | importFrom(methods,new)
24 | importFrom(purrr,map_dfr)
25 | importFrom(purrr,map_lgl)
26 | importFrom(rlang,`%||%`)
27 | importFrom(rlang,caller_env)
28 | importFrom(stats,setNames)
29 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # anndataR 0.99.0
2 |
3 | ## New features
4 |
5 | * PR #158: Change package version from 0.0.0.9000 --> 0.99.0 to align with Bioc devel
6 | versioning standards.
7 | - Update DESCRIPTION file to reflect current release R version used by Bio:
8 | R (>= 3.2.0) --> R (>= 4.3.0).
9 | - Reformat NEWS file to follow some conventions.
10 |
11 | * Various PRs: Initial release of anndataR, providing support for working with
12 | AnnData objects in R. Feature list:
13 | - Slots:
14 | - X
15 | - layers
16 | - obs
17 | - obs_names
18 | - var
19 | - var_names
20 | - Backends:
21 | - HDF5AnnData
22 | - InMemoryAnnData
23 | - Converters:
24 | - SingleCellExperiment
25 | - Seurat
26 |
27 |
--------------------------------------------------------------------------------
/R/AnnData-usage.R:
--------------------------------------------------------------------------------
1 | #' AnnData structure and usage
2 | #'
3 | #' @description
4 | #' The `AnnData` object stores a data matrix `X` together with annotations of
5 | #' observations `obs` (`obsm`, `obsp`) and variables `var` (`varm`, `varp`).
6 | #' Additional layers of data can be stored in `layers` and unstructured
7 | #' annotations in `uns`.
8 | #'
9 | #' ## Back ends
10 | #'
11 | #' There are different back ends for `AnnData` objects that inherit from the
12 | #' abstract [AbstractAnnData] class. For example, the [InMemoryAnnData] stores
13 | #' data in memory or the [HDF5AnnData] is backed by a H5AD file.
14 | #'
15 | #' ## Usage
16 | #'
17 | #' The items listed as **"Slots"** are elements of the `AnnData` object that
18 | #' contain data and can be accessed or set. **"Fields"** return information
19 | #' about the `AnnData` object but cannot be set directly. Both, as well as
20 | #' methods, can be accessed using the `$` operator
21 | #'
22 | #' For example:
23 | #'
24 | #' - `adata$X` returns the `X` matrix
25 | #' - `adata$X <- x` sets the `X` matrix
26 | #' - `adata$method()` calls a method
27 | #'
28 | #' @slot X The main data matrix. Either `NULL` or an observation x variable
29 | #' matrix (without dimnames) with dimensions consistent with `n_obs` and
30 | #' `n_vars`.
31 | #' @slot layers Additional data layers. Must be `NULL` or a named list of
32 | #' matrices having dimensions consistent with `n_obs` and `n_vars`.
33 | #' @slot obs Observation annotations. A `data.frame` with columns containing
34 | #' information about observations. The number of rows of `obs` defines the
35 | #' observation dimension of the `AnnData` object (`n_obs`). If `NULL`, an
36 | #' `n_obs` × 0 `data.frame` will automatically be generated.
37 | #' @slot var Variable annotations. A `data.frame` with columns containing
38 | #' information about variables. The number of rows of `var` defines the
39 | #' variable dimension of the `AnnData` object (`n_vars`). If `NULL`, an
40 | #' `n_vars` × 0 `data.frame` will automatically be generated.
41 | #' @slot obs_names Observation names. Either `NULL` or a vector of unique
42 | #' identifiers used to identify each row of `obs` and to act as an index into
43 | #' the observation dimension of the `AnnData` object. For compatibility with
44 | #' _R_ representations, `obs_names` should be a unique character vector.
45 | #' @slot var_names Variable names. Either `NULL` or a vector of unique
46 | #' identifiers used to identify each row of `var` and to act as an index into
47 | #' the variable dimension of the `AnnData` object. For compatibility with _R_
48 | #' representations, `var_names` should be a unique character vector.
49 | #' @slot obsm Multi-dimensional observation annotation. Must be `NULL` or a
50 | #' named list of array-like elements with number of rows equal to `n_obs`.
51 | #' @slot varm Multi-dimensional variable annotations. Must be `NULL` or a named
52 | #' list of array-like elements with number of rows equal to `n_vars`.
53 | #' @slot obsp Observation pairs. Must be `NULL` or a named list of array-like
54 | #' elements with number of rows and columns equal to `n_obs`.
55 | #' @slot varp Variable pairs. Must be `NULL` or a named list of array-like
56 | #' elements with number of rows and columns equal to `n_vars`.
57 | #' @slot uns Unstructured annotations. Must be `NULL` or a named list.
58 | #'
59 | #' @field shape Dimensions (observations x variables) of the `AnnData` object
60 | #' @field n_obs Number of observations
61 | #' @field n_vars Number of variables
62 | #' @field obs_keys Keys (column names) of `obs`
63 | #' @field var_keys Keys (column names) of `var`
64 | #' @field layers_keys Keys (element names) of `layers`
65 | #' @field obsm_keys Keys (element names) of `obsm`
66 | #' @field varm_keys Keys (element names) of `varm`
67 | #' @field obsp_keys Keys (element names) of `obsp`
68 | #' @field varp_keys Keys (element names) of `varp`
69 | #' @field uns_keys Keys (element names) of `uns`
70 | #'
71 | #' @section Methods:
72 | #'
73 | #' ## Conversion methods:
74 | #'
75 | #' \describe{
76 | #' \item{
77 | #' `as_SingleCellExperiment()`
78 | #' }{
79 | #' Convert to [`SingleCellExperiment::SingleCellExperiment`], see
80 | #' [as_SingleCellExperiment()]
81 | #' }
82 | #' \item{`as_Seurat()`}{Convert to [`SeuratObject::Seurat`], see [as_Seurat()]}
83 | #' \item{`as_InMemoryAnnData()`}{Convert to [`InMemoryAnnData`], as [as_InMemoryAnnData()]}
84 | #' \item{`as_HDF5AnnData()`}{Convert to [`HDF5AnnData`], see [as_HDF5AnnData()]}
85 | #' }
86 | #'
87 | #' ## Output methods:
88 | #'
89 | #' \describe{
90 | #' \item{
91 | #' `write_h5ad()`
92 | #' }{
93 | #' Write the `AnnData` object to an HDF5 file, see [write_h5ad()]
94 | #' }
95 | #' }
96 | #'
97 | #' ## General methods:
98 | #'
99 | #' \describe{
100 | #' \item{`print()`}{Print a summary of the `AnnData` object}
101 | #' }
102 | #'
103 | #' @section Functions that can be used to create AnnData objects:
104 | #'
105 | #' \describe{
106 | #' \item{[AnnData()]}{Create an [InMemoryAnnData] object}
107 | #' \item{[read_h5ad()]}{Read an `AnnData` from a H5AD file}
108 | #' \item{[as_AnnData()]}{Convert other objects to an `AnnData` object}
109 | #' }
110 | #'
111 | #' @seealso The documentation for the Python `anndata` package
112 | #'
113 | #' @seealso [AbstractAnnData] for the abstract class that all `AnnData` objects
114 | #' inherit from
115 | #' @seealso [InMemoryAnnData] for the in-memory implementation of `AnnData`
116 | #' @seealso [HDF5AnnData] for the HDF5-backed implementation of `AnnData`
117 | #'
118 | #' @name AnnData-usage
119 | NULL
120 |
--------------------------------------------------------------------------------
/R/AnnData.R:
--------------------------------------------------------------------------------
1 | #' Create an in-memory AnnData object.
2 | #'
3 | #' For more information on the functionality of an AnnData object, see [AnnData-usage]
4 | #'
5 | #' @param X See the `X` slot in [AnnData-usage]
6 | #' @param layers See the `layers` slot in [AnnData-usage]
7 | #' @param obs See the `obs` slot in [AnnData-usage]
8 | #' @param var See the `var` slot in [AnnData-usage]
9 | #' @param obsm See the `obsm` slot in [AnnData-usage]
10 | #' @param varm See the `varm` slot in [AnnData-usage]
11 | #' @param obsp See the `obsp` slot in [AnnData-usage]
12 | #' @param varp See the `varp` slot in [AnnData-usage]
13 | #' @param uns See the `uns` slot in [AnnData-usage]
14 | #' @param shape Shape tuple (e.g. `c(n_obs, n_vars)`). Can be provided if both
15 | #' `X` or `obs` and `var` are not provided.
16 | #'
17 | #' @return An [InMemoryAnnData] object
18 | #' @export
19 | #'
20 | #' @seealso [AnnData-usage] for details of `AnnData` structure and usage
21 | #' @family AnnData creators
22 | #'
23 | #' @examples
24 | #' adata <- AnnData(
25 | #' X = matrix(1:12, nrow = 3, ncol = 4),
26 | #' obs = data.frame(
27 | #' row.names = paste0("obs", 1:3),
28 | #' n_counts = c(1, 2, 3),
29 | #' n_cells = c(1, 2, 3)
30 | #' ),
31 | #' var = data.frame(
32 | #' row.names = paste0("var", 1:4),
33 | #' n_cells = c(1, 2, 3, 4)
34 | #' )
35 | #' )
36 | #'
37 | #' adata
38 | AnnData <- function(
39 | X = NULL,
40 | obs = NULL,
41 | var = NULL,
42 | layers = NULL,
43 | obsm = NULL,
44 | varm = NULL,
45 | obsp = NULL,
46 | varp = NULL,
47 | uns = NULL,
48 | shape = NULL
49 | ) {
50 | InMemoryAnnData$new(
51 | X = X,
52 | obs = obs,
53 | var = var,
54 | layers = layers,
55 | obsm = obsm,
56 | varm = varm,
57 | obsp = obsp,
58 | varp = varp,
59 | uns = uns,
60 | shape = shape
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/R/anndataR-package.R:
--------------------------------------------------------------------------------
1 | #' @keywords internal
2 | "_PACKAGE"
3 |
4 | ## usethis namespace: start
5 | #' @importFrom cli cli_abort cli_warn cli_inform
6 | #' @importFrom lifecycle deprecated
7 | #' @importFrom methods as new
8 | #' @importFrom purrr map_lgl map_dfr
9 | #' @importFrom R6 R6Class
10 | #' @importFrom rlang `%||%`
11 | #' @importFrom stats setNames
12 | ## usethis namespace: end
13 | NULL
14 |
--------------------------------------------------------------------------------
/R/anndata_constructors.R:
--------------------------------------------------------------------------------
1 | #' List the available AnnData generators.
2 | #'
3 | #' @noRd
4 | anndata_constructors <- function() {
5 | list(
6 | "HDF5AnnData" = HDF5AnnData,
7 | "InMemoryAnnData" = InMemoryAnnData
8 | )
9 | }
10 |
11 | #' Retrieve the AnnData constructor for a given class.
12 | #'
13 | #' @param class Name of the AnnData class. Must be one of `"HDF5AnnData"`
14 | #' or `"InMemoryAnnData"`.
15 | #'
16 | #' @noRd
17 | get_anndata_constructor <- function(
18 | class = c("HDF5AnnData", "InMemoryAnnData")
19 | ) {
20 | # TODO: also support directly passing the correct class?
21 | class <- match.arg(class)
22 | anndata_constructors()[[class]]
23 | }
24 |
--------------------------------------------------------------------------------
/R/check_requires.R:
--------------------------------------------------------------------------------
1 | #' Check required packages
2 | #'
3 | #' Check that required packages are available and give a nice error message with
4 | #' install instructions if not
5 | #'
6 | #' @param what A message stating what the packages are required for. Used at the
7 | #' start of the error message e.g. "{what} requires...".
8 | #' @param requires Character vector of required package names
9 | #' @param where Where to install the packages from. Either "CRAN" or "Bioc"
10 | #'
11 | #' @return `'TRUE` invisibly if all packages are available, otherwise calls
12 | #' [cli::cli_abort()]
13 | #'
14 | #' @importFrom rlang caller_env
15 | #' @noRd
16 | check_requires <- function(what, requires, where = c("CRAN", "Bioc")) {
17 | where <- match.arg(where)
18 |
19 | is_available <- map_lgl(requires, requireNamespace, quietly = TRUE)
20 |
21 | if (any(!is_available)) {
22 | missing <- requires[!is_available]
23 |
24 | # nolint start object_usage_linter
25 | missing_str <- paste0("\"", paste(missing, collapse = "\", \""), "\"")
26 | if (length(missing) > 1) {
27 | missing_str <- paste0("c(", missing_str, ")")
28 | }
29 | fun <- switch(
30 | where,
31 | CRAN = "install.packages",
32 | Bioc = "install.packages(\"BiocManager\"); BiocManager::install"
33 | )
34 | # nolint end object_usage_linter
35 |
36 | cli_abort(
37 | c(
38 | "{what} requires the {.pkg {missing}} package{?s}",
39 | "i" = paste(
40 | "To continue, install {cli::qty(missing)}{?it/them} using",
41 | "{.code {fun}({missing_str})}"
42 | )
43 | ),
44 | call = caller_env()
45 | )
46 | }
47 |
48 | invisible(TRUE)
49 | }
50 |
--------------------------------------------------------------------------------
/R/generate_dataframe.R:
--------------------------------------------------------------------------------
1 | #' Generate a dataframe
2 | #'
3 | #' Generate a dataframe with different types of columns
4 | #'
5 | #' @param num_rows Number of rows to generate
6 | #' @param types Types of columns to generate
7 | #'
8 | #' @return A dataframe with the generated columns
9 | #'
10 | #' @noRd
11 | #'
12 | #' @examples
13 | #' generate_dataframe(10L)
14 | generate_dataframe <- function(num_rows, types = names(vector_generators)) {
15 | data <- lapply(types, generate_vector, n = num_rows)
16 | names(data) <- types
17 | as.data.frame(data)
18 | }
19 |
--------------------------------------------------------------------------------
/R/generate_matrix.R:
--------------------------------------------------------------------------------
1 | # nolint start
2 | generate_numeric_matrix <- function(n_obs, n_vars, NAs = FALSE) {
3 | # byrow = TRUE to mimic the way a matrix gets filled in Python
4 | m <- matrix(
5 | seq(0.5, n_obs * n_vars),
6 | nrow = n_obs,
7 | ncol = n_vars,
8 | byrow = TRUE
9 | )
10 | if (NAs) {
11 | m[1, 1] <- NA_real_
12 | }
13 | m
14 | }
15 |
16 | generate_integer_matrix <- function(n_obs, n_vars, NAs = FALSE) {
17 | # byrow = TRUE to mimic the way a matrix gets filled in Python
18 | m <- matrix(
19 | seq(0L, n_obs * n_vars - 1),
20 | nrow = n_obs,
21 | ncol = n_vars,
22 | byrow = TRUE
23 | )
24 | if (NAs) {
25 | m[1, 1] <- NA_integer_
26 | }
27 | m
28 | }
29 |
30 | matrix_generators <- list(
31 | numeric_matrix = function(n_obs, n_vars) {
32 | generate_numeric_matrix(n_obs, n_vars)
33 | },
34 | numeric_dense = function(n_obs, n_vars) {
35 | m <- generate_numeric_matrix(n_obs, n_vars)
36 | as(m, "denseMatrix")
37 | },
38 | numeric_csparse = function(n_obs, n_vars) {
39 | m <- generate_numeric_matrix(n_obs, n_vars)
40 | as(m, "CsparseMatrix")
41 | },
42 | numeric_rsparse = function(n_obs, n_vars) {
43 | m <- generate_numeric_matrix(n_obs, n_vars)
44 | as(m, "RsparseMatrix")
45 | },
46 | numeric_matrix_with_nas = function(n_obs, n_vars) {
47 | generate_numeric_matrix(n_obs, n_vars, NAs = TRUE)
48 | },
49 | numeric_dense_with_nas = function(n_obs, n_vars) {
50 | m <- generate_numeric_matrix(n_obs, n_vars, NAs = TRUE)
51 | as(m, "denseMatrix")
52 | },
53 | numeric_csparse_with_nas = function(n_obs, n_vars) {
54 | m <- generate_numeric_matrix(n_obs, n_vars, NAs = TRUE)
55 | as(m, "CsparseMatrix")
56 | },
57 | numeric_rsparse_with_nas = function(n_obs, n_vars) {
58 | m <- generate_numeric_matrix(n_obs, n_vars, NAs = TRUE)
59 | as(m, "RsparseMatrix")
60 | },
61 | integer_matrix = function(n_obs, n_vars) {
62 | generate_integer_matrix(n_obs, n_vars)
63 | },
64 | integer_dense = function(n_obs, n_vars) {
65 | m <- generate_integer_matrix(n_obs, n_vars)
66 | as(m, "denseMatrix")
67 | },
68 | integer_csparse = function(n_obs, n_vars) {
69 | m <- generate_integer_matrix(n_obs, n_vars)
70 | as(m, "CsparseMatrix")
71 | },
72 | integer_rsparse = function(n_obs, n_vars) {
73 | m <- generate_integer_matrix(n_obs, n_vars)
74 | as(m, "RsparseMatrix")
75 | },
76 | integer_matrix_with_nas = function(n_obs, n_vars) {
77 | m <- generate_integer_matrix(n_obs, n_vars, NAs = TRUE)
78 | m
79 | },
80 | integer_dense_with_nas = function(n_obs, n_vars) {
81 | m <- generate_integer_matrix(n_obs, n_vars, NAs = TRUE)
82 | as(m, "denseMatrix")
83 | },
84 | integer_csparse_with_nas = function(n_obs, n_vars) {
85 | m <- generate_integer_matrix(n_obs, n_vars, NAs = TRUE)
86 | as(m, "CsparseMatrix")
87 | },
88 | integer_rsparse_with_nas = function(n_obs, n_vars) {
89 | m <- generate_integer_matrix(n_obs, n_vars, NAs = TRUE)
90 | as(m, "RsparseMatrix")
91 | }
92 | )
93 | # nolint end
94 |
95 | #' Generate a matrix
96 | #'
97 | #' Generate a matrix of a given type
98 | #'
99 | #' @param n_obs Number of observations to generate
100 | #' @param n_vars Number of variables to generate
101 | #'
102 | #' @return A matrix of the given type
103 | #'
104 | #' @noRd
105 | #'
106 | #' @examples
107 | #' generate_matrix(10L, 20L)
108 | generate_matrix <- function(n_obs, n_vars, type = names(matrix_generators)) {
109 | type <- match.arg(type)
110 | matrix_generators[[type]](n_obs, n_vars)
111 | }
112 |
--------------------------------------------------------------------------------
/R/generate_vector.R:
--------------------------------------------------------------------------------
1 | vector_generators <- list(
2 | character = function(n) paste0("value_", seq(from = 0, to = n - 1)),
3 | integer = function(n) seq(from = 0, to = n - 1),
4 | factor = function(n) factor(rep(c("Value1", "Value2"), length.out = n)),
5 | factor_ordered = function(n) {
6 | factor(rep(c("Value1", "Value2"), length.out = n), ordered = TRUE)
7 | },
8 | logical = function(n) sample(c(TRUE, FALSE), n, replace = TRUE),
9 | numeric = function(n) seq(from = 0.5, to = n),
10 | character_with_nas = function(n) {
11 | x <- paste0("value", seq_len(n))
12 | x[seq(1, n, by = 2)] <- NA_character_
13 | x
14 | },
15 | integer_with_nas = function(n) {
16 | x <- seq(from = 0, to = n - 1)
17 | x[1] <- NA_integer_
18 | x
19 | },
20 | factor_with_nas = function(n) {
21 | x <- factor(rep(c("Value1", "Value2"), length.out = n))
22 | x[1] <- NA_character_
23 | x
24 | },
25 | factor_ordered_with_nas = function(n) {
26 | x <- factor(rep(c("Value1", "Value2"), length.out = n), ordered = TRUE)
27 | x[1] <- NA_character_
28 | x
29 | },
30 | logical_with_nas = function(n) {
31 | x <- sample(c(TRUE, FALSE), n, replace = TRUE)
32 | x[seq(1, n, by = 2)] <- NA
33 | x
34 | },
35 | numeric_with_nas = function(n) {
36 | x <- runif(n)
37 | x[seq(1, n, by = 2)] <- NA_real_
38 | x
39 | }
40 | )
41 |
42 | #' Generate a vector
43 | #'
44 | #' Generate a vector of a given type
45 | #'
46 | #' @param n Number of elements to generate
47 | #' @param type Type of vector to generate
48 | #'
49 | #' @return A vector of the given type
50 | #'
51 | #' @noRd
52 | #'
53 | #' @examples
54 | #' generate_vector(10L)
55 | generate_vector <- function(n, type = names(vector_generators)) {
56 | type <- match.arg(type)
57 | vector_generators[[type]](n)
58 | }
59 |
--------------------------------------------------------------------------------
/R/known_issues.R:
--------------------------------------------------------------------------------
1 | # This file contains the known issues that are currently present in the package.
2 | # It can be used to generate documentation, but also throw warnings instead of errors
3 | # in tests.
4 | read_known_issues <- function() {
5 | check_requires("Reading known issues", "yaml")
6 |
7 | data <- yaml::read_yaml(system.file(
8 | "known_issues.yaml",
9 | package = "anndataR"
10 | ))
11 |
12 | map_dfr(
13 | data$known_issues,
14 | function(row) {
15 | expected_names <- c(
16 | "backend",
17 | "slot",
18 | "dtype",
19 | "process",
20 | "error_message",
21 | "description",
22 | "proposed_solution",
23 | "to_investigate",
24 | "to_fix"
25 | )
26 | if (!all(expected_names %in% names(row))) {
27 | cli_abort(c(
28 | "Unexpected columns in {.file known_issues.yaml}",
29 | "i" = "Expected columns: {.val {expected_names}}",
30 | "i" = "Actual columns: {.val {names(row)}}"
31 | ))
32 | }
33 |
34 | expand.grid(row)
35 | }
36 | )
37 | }
38 |
39 | is_known <- function(backend, slot, dtype, process, known_issues = NULL) {
40 | if (is.null(known_issues)) {
41 | known_issues <- read_known_issues()
42 | }
43 |
44 | filt <- rep(TRUE, nrow(known_issues))
45 |
46 | if (!is.null(backend)) {
47 | filt <- filt & known_issues$backend %in% backend
48 | }
49 | if (!is.null(slot)) {
50 | filt <- filt & known_issues$slot %in% slot
51 | }
52 | if (!is.null(dtype)) {
53 | filt <- filt & known_issues$dtype %in% dtype
54 | }
55 | if (!is.null(process)) {
56 | filt <- filt & known_issues$process %in% process
57 | }
58 |
59 | filt
60 | }
61 |
62 | message_if_known <- function(
63 | backend,
64 | slot,
65 | dtype,
66 | process,
67 | known_issues = NULL
68 | ) {
69 | if (is.null(known_issues)) {
70 | known_issues <- read_known_issues()
71 | }
72 |
73 | filt <- is_known(backend, slot, dtype, process, known_issues)
74 |
75 | if (any(filt)) {
76 | # take first
77 | row <- known_issues[which(filt)[[1]], ]
78 |
79 | paste0(
80 | "Known issue for backend '",
81 | row$backend,
82 | "', slot '",
83 | row$slot,
84 | "', dtype '",
85 | row$dtype,
86 | "', process '",
87 | row$process,
88 | "': ",
89 | row$description
90 | )
91 | } else {
92 | NULL
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/R/read_h5ad.R:
--------------------------------------------------------------------------------
1 | #' Read H5AD
2 | #'
3 | #' Read data from a H5AD file
4 | #'
5 | #' @param path Path to the H5AD file to read
6 | #' @param as The type of object to return. One of:
7 | #'
8 | #' * `"InMemoryAnnData"`: Read the H5AD file into memory as an
9 | #' [`InMemoryAnnData`] object
10 | #' * `"HDF5AnnData"`: Read the H5AD file as an [`HDF5AnnData`] object
11 | #' * `"SingleCellExperiment"`: Read the H5AD file as a
12 | #' [`SingleCellExperiment::SingleCellExperiment`] object
13 | #' * `"Seurat"`: Read the H5AD file as a
14 | #' [`SeuratObject::Seurat`] object
15 | #' @param to `r lifecycle::badge('deprecated')` Deprecated, use `as` instead
16 | #' @param mode The mode to open the HDF5 file.
17 | #'
18 | #' * `a` creates a new file or opens an existing one for read/write.
19 | #' * `r` opens an existing file for reading.
20 | #' * `r+` opens an existing file for read/write.
21 | #' * `w` creates a file, truncating any existing ones.
22 | #' * `w-`/`x` are synonyms, creating a file and failing if it already exists.
23 | #' @param ... Extra arguments provided to the `as_*` conversion function for the
24 | #' object specified by `as`
25 | #'
26 | #' @return The object specified by `as`
27 | #' @export
28 | #'
29 | #' @family AnnData creators
30 | #'
31 | #' @examples
32 | #' h5ad_file <- system.file("extdata", "example.h5ad", package = "anndataR")
33 | #'
34 | #' # Read the H5AD as a SingleCellExperiment object
35 | #' if (requireNamespace("SingleCellExperiment", quietly = TRUE)) {
36 | #' sce <- read_h5ad(h5ad_file, as = "SingleCellExperiment")
37 | #' }
38 | #'
39 | #' # Read the H5AD as a Seurat object
40 | #' if (requireNamespace("SeuratObject", quietly = TRUE)) {
41 | #' seurat <- read_h5ad(h5ad_file, as = "Seurat")
42 | #' }
43 | read_h5ad <- function(
44 | path,
45 | as = c("InMemoryAnnData", "HDF5AnnData", "SingleCellExperiment", "Seurat"),
46 | to = deprecated(),
47 | mode = c("r", "r+", "a", "w", "w-", "x"),
48 | ...
49 | ) {
50 | as <- match.arg(as)
51 | mode <- match.arg(mode)
52 |
53 | if (lifecycle::is_present(to)) {
54 | lifecycle::deprecate_warn(
55 | when = "0.99.0",
56 | what = "read_h5ad(to = )",
57 | with = "read_h5ad(as = )",
58 | details = "Overwriting `as` with `to`."
59 | )
60 | as <- to
61 | }
62 |
63 | hdf5_adata <- HDF5AnnData$new(path, mode = mode)
64 |
65 | if (as == "HDF5AnnData") {
66 | return(hdf5_adata)
67 | }
68 |
69 | adata <- switch(
70 | as,
71 | "SingleCellExperiment" = hdf5_adata$as_SingleCellExperiment(...),
72 | "Seurat" = hdf5_adata$as_Seurat(...),
73 | "InMemoryAnnData" = hdf5_adata$as_InMemoryAnnData(...)
74 | )
75 | hdf5_adata$close()
76 | adata
77 | }
78 |
--------------------------------------------------------------------------------
/R/ui.R:
--------------------------------------------------------------------------------
1 | #' Style a vector
2 | #'
3 | #' Style a vector for use in **{cli}** calls
4 | #'
5 | #' @param x The vector to style
6 | #' @param last The separator before the last element in the vector
7 | #' @param trunc The maxium number of elements to show
8 | #' @param trunc_style The truncation style to use, either "both-ends"
9 | #' (1, 2, ..., n-1, 1) or "head" (1, 2, 3, 4, ...)
10 | #' @param wrap Whether to wrap the output to appear as a vector, if `TRUE` then
11 | #' "c(1, 2, 3)" else "1, 2, 3"
12 | #'
13 | #' @returns A styled string that can be included directly using "{syle_vec(...)}"
14 | #' @noRd
15 | style_vec <- function(
16 | x,
17 | last = ", ",
18 | trunc = 20L,
19 | trunc_style = c("both-ends", "head"),
20 | wrap = FALSE
21 | ) {
22 | trunc_style <- match.arg(trunc_style)
23 |
24 | style <- list(
25 | "vec-last" = last,
26 | "vec-trunc" = trunc,
27 | "vec-trunc-style" = trunc_style
28 | )
29 |
30 | x <- cli::cli_vec(x, style)
31 |
32 | if (isTRUE(wrap)) {
33 | x_str <- "c({.val {x}})"
34 | } else {
35 | x_str <- "{.val {x}}"
36 | }
37 |
38 | cli::cli_fmt(cli::cli_text(x_str))
39 | }
40 |
--------------------------------------------------------------------------------
/R/utils.R:
--------------------------------------------------------------------------------
1 | wrap_message <- function(...) {
2 | txt <- paste0(..., collapse = "")
3 | paste(strwrap(txt, exdent = 2L), collapse = "\n")
4 | }
5 |
6 | has_row_names <- function(x) {
7 | if (is.data.frame(x)) {
8 | .row_names_info(x) > 0
9 | } else {
10 | !is.null(dimnames(x)[[1]])
11 | }
12 | }
13 |
14 | get_shape <- function(obs, var, X, shape) {
15 | n_obs <-
16 | if (!is.null(obs)) {
17 | nrow(obs)
18 | } else if (!is.null(X)) {
19 | nrow(X)
20 | } else if (!is.null(shape)) {
21 | shape[[1]]
22 | } else {
23 | 0L
24 | }
25 | n_vars <-
26 | if (!is.null(var)) {
27 | nrow(var)
28 | } else if (!is.null(X)) {
29 | ncol(X)
30 | } else if (!is.null(shape)) {
31 | shape[[2]]
32 | } else {
33 | 0L
34 | }
35 | c(n_obs, n_vars)
36 | }
37 |
38 | get_initial_obs <- function(obs, X, shape) {
39 | if (is.null(obs)) {
40 | obs <- data.frame(matrix(NA, nrow = shape[[1]], ncol = 0))
41 | if (!is.null(X)) {
42 | rownames(obs) <- rownames(X)
43 | }
44 | }
45 | obs
46 | }
47 |
48 | get_initial_var <- function(var, X, shape) {
49 | if (is.null(var)) {
50 | var <- data.frame(matrix(NA, nrow = shape[[2]], ncol = 0))
51 | if (!is.null(X)) {
52 | rownames(var) <- colnames(X)
53 | }
54 | }
55 | var
56 | }
57 |
58 | to_py_matrix <- function(mat) {
59 | if (inherits(mat, "dgCMatrix")) {
60 | mat <- as(mat, "RsparseMatrix")
61 | } else if (!inherits(mat, "dgRMatrix")) {
62 | mat <- as.matrix(mat)
63 | }
64 | Matrix::t(mat)
65 | }
66 |
67 | # nolint start: object_name_linter
68 | to_R_matrix <- function(mat) {
69 | # nolint end: object_name_linter
70 | if (inherits(mat, "dgRMatrix")) {
71 | mat <- as(mat, "CsparseMatrix")
72 | } else if (!inherits(mat, "dgCMatrix")) {
73 | mat <- as.matrix(mat)
74 | }
75 | Matrix::t(mat)
76 | }
77 |
78 | self_name <- function(x) {
79 | if (is.null(names(x))) {
80 | x <- setNames(x, x)
81 | } else if (any(names(x) == "")) {
82 | is_missing <- names(x) == ""
83 | names(x)[is_missing] <- x[is_missing]
84 | }
85 |
86 | x
87 | }
88 |
89 | #' Get mapping
90 | #'
91 | #' Get a mapping argument for a conversion function
92 | #'
93 | #' @param mapping The user-supplied mapping argument. Can be a named vector,
94 | #' `TRUE` or `FALSE`.
95 | #' @param guesser A function that guesses the default mapping from `obj` if
96 | #' `mapping` is `TRUE`
97 | #' @param obj The object that is being converted and is passed to `guesser`
98 | #' if needed
99 | #' @param name The name of the mapping argument, used for error messages
100 | #' @param ... Additional arguments passed to `guesser`
101 | #'
102 | #' @description
103 | #' If `mapping` is `NULL` or empty it is set to `FALSE` with a warning. `FALSE`
104 | #' values return an empty mapping.
105 | #'
106 | #' @returns A named mapping vector
107 | #' @noRd
108 | get_mapping <- function(mapping, guesser, obj, name, ...) {
109 | if (rlang::is_empty(mapping)) {
110 | cli_warn(c(
111 | "The {.arg {name}} argument is empty, setting it to {.val {FALSE}}"
112 | ))
113 |
114 | mapping <- FALSE
115 | }
116 |
117 | # If FALSE, return an empty mapping
118 | if (isFALSE(mapping)) {
119 | return(list())
120 | }
121 |
122 | # If TRUE, use the guesser function to get the default mapping
123 | if (isTRUE(mapping)) {
124 | return(guesser(obj, ...))
125 | }
126 |
127 | if (!is.vector(mapping)) {
128 | cli_abort(paste(
129 | "{.arg {name}} must be a vector, {.val {TRUE}} or {.val {FALSE}}, not",
130 | "{.cls {class(mapping)}}"
131 | ))
132 | }
133 |
134 | # Make sure provided mapping has names
135 | self_name(mapping)
136 | }
137 |
--------------------------------------------------------------------------------
/R/write_h5ad.R:
--------------------------------------------------------------------------------
1 | #' Write H5AD
2 | #'
3 | #' Write an H5AD file
4 | #'
5 | #' @param object The object to write, either a
6 | #' [`SingleCellExperiment::SingleCellExperiment`] or a
7 | #' [`SeuratObject::Seurat`] object
8 | #' @param path Path of the file to write to
9 | #' @param compression The compression algorithm to use when writing the HDF5
10 | #' file. Can be one of `"none"`, `"gzip"` or `"lzf"`. Defaults to `"none"`.
11 | #' @param mode The mode to open the HDF5 file.
12 | #'
13 | #' * `a` creates a new file or opens an existing one for read/write
14 | #' * `r+` opens an existing file for read/write
15 | #' * `w` creates a file, truncating any existing ones
16 | #' * `w-`/`x` are synonyms creating a file and failing if it already exists
17 | #' @param ... Additional arguments passed to [as_AnnData()]
18 | #'
19 | #' @return `path` invisibly
20 | #' @export
21 | #'
22 | #' @examples
23 | #' adata <- AnnData(
24 | #' X = matrix(1:5, 3L, 5L),
25 | #' layers = list(
26 | #' A = matrix(5:1, 3L, 5L),
27 | #' B = matrix(letters[1:5], 3L, 5L)
28 | #' ),
29 | #' obs = data.frame(row.names = LETTERS[1:3], cell = 1:3),
30 | #' var = data.frame(row.names = letters[1:5], gene = 1:5)
31 | #' )
32 | #' h5ad_file <- tempfile(fileext = ".h5ad")
33 | #' adata$write_h5ad(h5ad_file)
34 | #'
35 | #' # Write a SingleCellExperiment as an H5AD
36 | #' if (requireNamespace("SingleCellExperiment", quietly = TRUE)) {
37 | #' ncells <- 100
38 | #' counts <- matrix(rpois(20000, 5), ncol = ncells)
39 | #' logcounts <- log2(counts + 1)
40 | #'
41 | #' pca <- matrix(runif(ncells * 5), ncells)
42 | #' tsne <- matrix(rnorm(ncells * 2), ncells)
43 | #'
44 | #' sce <- SingleCellExperiment::SingleCellExperiment(
45 | #' assays = list(counts = counts, logcounts = logcounts),
46 | #' reducedDims = list(PCA = pca, tSNE = tsne)
47 | #' )
48 | #'
49 | #' adata <- as_AnnData(sce)
50 | #' h5ad_file <- tempfile(fileext = ".h5ad")
51 | #' adata$write_h5ad(h5ad_file)
52 | #' }
53 | #'
54 | #' # Write a Seurat as a H5AD
55 | #' if (requireNamespace("Seurat", quietly = TRUE)) {
56 | #' library(Seurat)
57 | #'
58 | #' counts <- matrix(1:15, 5L, 3L)
59 | #' dimnames(counts) <- list(
60 | #' LETTERS[1:5],
61 | #' letters[1:3]
62 | #' )
63 | #' cell.metadata <- data.frame(
64 | #' row.names = letters[1:3],
65 | #' cell = 1:3
66 | #' )
67 | #' obj <- CreateSeuratObject(counts, meta.data = cell.metadata)
68 | #' gene.metadata <- data.frame(
69 | #' row.names = LETTERS[1:5],
70 | #' gene = 1:5
71 | #' )
72 | #' obj[["RNA"]] <- AddMetaData(GetAssay(obj), gene.metadata)
73 | #'
74 | #' adata <- as_AnnData(obj)
75 | #' h5ad_file <- tempfile(fileext = ".h5ad")
76 | #' adata$write_h5ad(h5ad_file)
77 | #' }
78 | write_h5ad <- function(
79 | object,
80 | path,
81 | compression = c("none", "gzip", "lzf"),
82 | mode = c("w-", "r", "r+", "a", "w", "x"),
83 | ...
84 | ) {
85 | mode <- match.arg(mode)
86 | adata <- if (inherits(object, "AbstractAnnData")) {
87 | object$as_HDF5AnnData(
88 | path,
89 | compression = compression,
90 | mode = mode
91 | )
92 | } else {
93 | as_AnnData(
94 | object,
95 | output_class = "HDF5AnnData",
96 | file = path,
97 | compression = compression,
98 | mode = mode,
99 | ...
100 | )
101 | }
102 |
103 | adata$close()
104 | rm(adata)
105 | gc()
106 |
107 | invisible(path)
108 | }
109 |
--------------------------------------------------------------------------------
/R/write_hdf5_helpers.R:
--------------------------------------------------------------------------------
1 | #' Create a dataset in a HDF5 file
2 | #'
3 | #' Write HDF5 dataset with chosen compression (can be none)
4 | #'
5 | #' @noRd
6 | #'
7 | #' @param file Path to a HDF5 file
8 | #' @param name Name of the element within the H5AD file containing the data
9 | #' frame
10 | #' @param value Value to write. Must be a vector to the same length as the data
11 | #' frame.
12 | #' @param compression The compression to use when writing the element. Can be
13 | #' one of `"none"`, `"gzip"` or `"lzf"`. Defaults to `"none"`.
14 | #' @param scalar Whether to write the value as a scalar or not
15 | #' @param dtype The data type of the attribute to write. If `NULL` then the data
16 | #' type is guessed using `hdf5r::guess_dtype()`.
17 | #' @param space The space of the attribute to write. If `NULL` then the space
18 | #' is guessed using `hdf5r::guess_space()`.
19 | #'
20 | #' @return Whether the `path` exists in `file`
21 | hdf5_create_dataset <- function(
22 | file,
23 | name,
24 | value,
25 | compression = c("none", "gzip", "lzf"),
26 | scalar = FALSE,
27 | dtype = NULL,
28 | space = NULL
29 | ) {
30 | compression <- match.arg(compression)
31 |
32 | if (!is.null(dim(value))) {
33 | dims <- dim(value)
34 | } else {
35 | dims <- length(value)
36 | }
37 |
38 | if (is.null(dtype)) {
39 | dtype <- hdf5r::guess_dtype(value, scalar = scalar, string_len = Inf)
40 | }
41 |
42 | if (is.null(space)) {
43 | space <- hdf5r::guess_space(value, dtype = dtype, chunked = FALSE)
44 | }
45 |
46 | # TODO: lzf compression is not supported in hdf5r
47 | # TODO: allow the user to choose compression level
48 | gzip_level <- if (compression == "none") 0 else 9
49 |
50 | out <- file$create_dataset(
51 | name = name,
52 | dims = dims,
53 | gzip_level = gzip_level,
54 | robj = value,
55 | chunk_dims = NULL,
56 | space = space,
57 | dtype = dtype
58 | )
59 | # todo: create attr?
60 |
61 | out
62 | }
63 |
64 |
65 | #' HDF5 path exists
66 | #'
67 | #' Check that a path in HDF5 exists
68 | #'
69 | #' @noRd
70 | #'
71 | #' @param file Path to a HDF5 file
72 | #' @param target_path The path within the file to test for
73 | #'
74 | #' @return Whether the `path` exists in `file`
75 | hdf5_path_exists <- function(file, target_path) {
76 | tryCatch(
77 | {
78 | file$exists(target_path)
79 | },
80 | error = function(e) {
81 | FALSE
82 | }
83 | )
84 | }
85 |
86 | #' Create a HDF5 attribute
87 | #'
88 | #' Create a HDF5 attribute in a HDF5 file
89 | #'
90 | #' @noRd
91 | #'
92 | #' @param file Path to a H5AD file or an open H5AD handle
93 | #' @param name Name of the element within the H5AD file
94 | #' @param attr_name Name of the attribute to write
95 | #' @param attr_value Value of the attribute to write
96 | #' @param is_scalar Whether to write attributes as scalar values. Can be `TRUE`
97 | #' to write all attributes as scalars, `FALSE` to write no attributes as
98 | #' scalars, or a vector of the names of `attributes` that should be written.
99 | #' @param dtype The data type of the attribute to write. If `NULL` then the data
100 | #' type is guessed using `hdf5r::guess_dtype()`.
101 | #' @param space The space of the attribute to write. If `NULL` and `is_scalar` then
102 | #' the space is set to a scalar space. If `NULL` and `!is_scalar` then the space
103 | #' is guessed using `hdf5r::guess_space()`.
104 | hdf5_create_attribute <- function(
105 | file,
106 | name,
107 | attr_name,
108 | attr_value,
109 | is_scalar = TRUE,
110 | dtype = NULL,
111 | space = NULL
112 | ) {
113 | if (!inherits(file, "H5File")) {
114 | cli_abort("{.arg file} must be an open H5AD handle")
115 | }
116 |
117 | if (is.null(dtype)) {
118 | dtype <- hdf5r::guess_dtype(
119 | attr_value,
120 | scalar = is_scalar,
121 | string_len = Inf
122 | )
123 | }
124 | if (is.null(space)) {
125 | space <-
126 | if (is_scalar) {
127 | hdf5r::H5S$new(type = "scalar")
128 | } else {
129 | hdf5r::guess_space(attr_value, dtype = dtype, chunked = FALSE)
130 | }
131 | }
132 | file$create_attr_by_name(
133 | attr_name = attr_name,
134 | obj_name = name,
135 | robj = attr_value,
136 | dtype = dtype,
137 | space = space
138 | )
139 | }
140 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # {anndataR}: An R package for working with AnnData objects
2 |
3 | [](https://lifecycle.r-lib.org/articles/stages.html#experimental)
4 | [](https://CRAN.R-project.org/package=anndataR)
5 | [](https://github.com/scverse/anndataR/actions/workflows/R-CMD-check.yaml)
6 |
7 |
8 | **{anndataR}** aims to make the AnnData format a first-class citizen in
9 | the R ecosystem, and to make it easy to work with AnnData files in R,
10 | either directly or by converting them to a SingleCellExperiment or Seurat
11 | object.
12 |
13 | **{anndataR}** is an scverse® community project maintained by [Data Intuitive](https://data-intuitive.com/), and is fiscally sponsored by the [Chan Zuckerberg Initiative](https://chanzuckerberg.com/).
14 |
15 |
16 | ## Features of {anndataR}
17 |
18 | - Provide an `R6` class to work with AnnData objects in R (either in-memory or on-disk).
19 | - Read/write `*.h5ad` files natively
20 | - Convert to/from `SingleCellExperiment` objects
21 | - Convert to/from `Seurat` objects
22 |
23 | > [!WARNING]
24 | >
25 | > This package is still in the experimental stage, and may not work as
26 | > expected. You can find the status of development of anndataR on the
27 | > [feature tracking page](https://anndatar.data-intuitive.com/articles/design.html#feature-tracking)
28 | > of the website. Please [report](https://github.com/scverse/anndataR/issues) any issues you encounter.
29 |
30 | ## Installation
31 |
32 | You can install the development version of **{anndataR}** like so:
33 |
34 | ``` r
35 | # install.packages("pak")
36 | pak::pak("scverse/anndataR")
37 | ```
38 |
39 | You will need to install additional dependencies, depending on
40 | the task you want to perform.
41 |
42 | - To read/write `*.h5ad` files, install [hdf5r](https://cran.r-project.org/package=hdf5r):
43 | `install.packages("hdf5r")`
44 | - To convert to/from `SingleCellExperiment` objects, install [SingleCellExperiment](https://bioconductor.org/packages/release/bioc/html/SingleCellExperiment.html):
45 | `BiocManager::install("SingleCellExperiment")`
46 | - To convert to/from `Seurat` objects, install [SeuratObject](https://cran.r-project.org/package=SeuratObject):
47 | `install.packages("SeuratObject")`
48 |
49 | Alternatively, you can install all suggested dependencies at once:
50 |
51 | ``` r
52 | pak::pak("scverse/anndataR", dependencies = TRUE)
53 | ```
54 |
55 | ## Getting started
56 |
57 | The best way to get started with **{anndataR}** is to explore the package vignettes (available at https://anndatar.data-intuitive.com/articles/).
58 |
59 | - **Getting started**: An introduction to the package and its features.
60 | `vignette("anndataR", package = "anndataR")`
61 | - **Reading and writing H5AD files**: How to read and write `*.h5ad` files.
62 | `vignette("usage_h5ad", package = "anndataR")`
63 | - **Converting to/from Seurat objects**: How to convert between `AnnData` and `Seurat` objects.
64 | `vignette("usage_seurat", package = "anndataR")`
65 | - **Converting to/from SingleCellExperiment objects**: How to convert between `AnnData` and `SingleCellExperiment` objects.
66 | `vignette("usage_singlecellexperiment", package = "anndataR")`
67 | - **Software Design**: An overview of the design of the package.
68 | `vignette("software_design", package = "anndataR")`
69 | - **Development Status**: An overview of the development status of the package.
70 | `vignette("development_status", package = "anndataR")`
71 | - **Known Isses**: An overview of known issues with the package.
72 | `vignette("known_issues", package = "anndataR")`
73 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | url: https://anndataR.data-intuitive.com
2 |
3 | template:
4 | bootstrap: 5
5 |
6 | navbar:
7 | components:
8 | articles:
9 | text: Articles
10 | menu:
11 | - text: Usage
12 | - text: Read/write H5AD files
13 | href: articles/usage_h5ad.html
14 | - text: Read/write Seurat objects
15 | href: articles/usage_seurat.html
16 | - text: Read/write SingleCellExperiment objects
17 | href: articles/usage_singlecellexperiment.html
18 | - text: Mapping between SingleCellExperiment and AnnData
19 | href: articles/singlecellexperiment_mapping.html
20 | - text: -------
21 | - text: Development
22 | - text: Software design
23 | href: articles/software_design.html
24 | - text: Development status
25 | href: articles/development_status.html
26 | - text: Known issues
27 | href: articles/known_issues.html
28 |
--------------------------------------------------------------------------------
/anndataR.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 | ProjectId: 74a02069-5853-40a0-aa2f-7c3b8d406e45
3 |
4 | RestoreWorkspace: Default
5 | SaveWorkspace: Default
6 | AlwaysSaveHistory: Default
7 |
8 | EnableCodeIndexing: Yes
9 | UseSpacesForTab: Yes
10 | NumSpacesForTab: 2
11 | Encoding: UTF-8
12 |
13 | RnwWeave: Sweave
14 | LaTeX: pdfLaTeX
15 |
16 | AutoAppendNewline: Yes
17 | StripTrailingWhitespace: Yes
18 |
19 | BuildType: Package
20 | PackageUseDevtools: Yes
21 | PackageInstallArgs: --no-multiarch --with-keep.source
22 | PackageRoxygenize: rd,collate,namespace
23 |
--------------------------------------------------------------------------------
/inst/extdata/example.h5ad:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scverse/anndataR/8dfa46ae06fd5cc1d9b19479c5512a56d2357042/inst/extdata/example.h5ad
--------------------------------------------------------------------------------
/inst/scripts/example_h5ad.py:
--------------------------------------------------------------------------------
1 | # python v3.10.10
2 | import anndata # anndata v0.8.0
3 | import scanpy # scanpy v1.9.3
4 | import numpy # numpy v1.23.5
5 | import pandas # pandas v2.0.0
6 | import scipy.sparse # scipy v1.10.1
7 |
8 | # This script uses Python to create an example H5AD file for testing
9 | # interoperability between languages. It is designed to be a small but
10 | # relatively complex file that tests reading of different types and data
11 | # structures. The standard scanpy workflow has also been applied to populate
12 | # some of the most common information from real analyses. It should be updated
13 | # to test new issues as they are discovered.
14 | #
15 | # NOTE: When updating this script for the {anndataR} example H5AD file please
16 | # update the package versions used above, update the script version, date and
17 | # changelog below and format the file using Python Black
18 | # (https://black.readthedocs.io/en/stable/).
19 | #
20 | # Version: 0.2.0
21 | # Date: 2023-05-11
22 | #
23 | # CHANGELOG
24 | #
25 | # v0.2.0 (2023-05-11)
26 | # - Add 1D sparse matrix to `adata.uns["Sparse1D"]
27 | # - Reduce the size of `adata.uns["String2D"]` and add columns to values
28 | # v0.1.1 (2023-05-09)
29 | # - Reduce the size of `adata.uns["String2D"]` to save space
30 | # - Reduce dimension to 50 x 100 to save space
31 | # v0.1.0 (2023-05-08)
32 | # - Initial version
33 |
34 | numpy.random.seed(0)
35 |
36 | # Randomly generate a counts matrix
37 | counts = numpy.random.poisson(2, size=(50, 100))
38 |
39 | # Create an AnnData
40 | adata = anndata.AnnData(scipy.sparse.csr_matrix(counts.copy(), dtype=numpy.float32))
41 | adata.obs_names = [f"Cell{i:03d}" for i in range(adata.n_obs)]
42 | adata.var_names = [f"Gene{i:03d}" for i in range(adata.n_vars)]
43 |
44 | # Populate layers with different matrix types
45 | adata.layers["counts"] = adata.X.copy()
46 | adata.layers["dense_counts"] = counts.copy()
47 | adata.layers["csc_counts"] = scipy.sparse.csc_matrix(counts.copy(), dtype=numpy.float32)
48 |
49 | # Populate adata.var with different types
50 | adata.var["String"] = [f"String{i}" for i in range(adata.n_vars)]
51 |
52 | # Populate adata.obs with different types
53 | adata.obs["Float"] = 42.42
54 | adata.obs["FloatNA"] = adata.obs["Float"]
55 | adata.obs["FloatNA"][0] = float("nan")
56 | adata.obs["Int"] = numpy.arange(adata.n_obs)
57 | adata.obs["IntNA"] = pandas.array([None] + [42] * (adata.n_obs - 1))
58 | adata.obs["Bool"] = pandas.array([False] + [True] * (adata.n_obs - 1))
59 | adata.obs["BoolNA"] = pandas.array([False, None] + [True] * (adata.n_obs - 2))
60 |
61 | # Populate adata.uns with different types
62 | adata.uns["Category"] = pandas.array(["a", "b", None], dtype="category")
63 | adata.uns["Bool"] = [True, True, False]
64 | adata.uns["BoolNA"] = pandas.array([True, False, None])
65 | adata.uns["Int"] = [1, 2, 3]
66 | adata.uns["IntNA"] = pandas.array([1, 2, None])
67 | adata.uns["IntScalar"] = 1
68 | adata.uns["Sparse1D"] = scipy.sparse.csc_matrix([1, 2, 0, 0, 0, 3])
69 | adata.uns["StringScalar"] = "A string"
70 | adata.uns["String"] = [f"String {i}" for i in range(10)]
71 | adata.uns["String2D"] = [[f"row{i}col{j}" for i in range(10)] for j in range(5)]
72 | adata.uns["DataFrameEmpty"] = pandas.DataFrame(index=adata.obs.index)
73 |
74 | # Run the standard scanpy workflow
75 | scanpy.pp.calculate_qc_metrics(adata, percent_top=None, inplace=True)
76 | scanpy.pp.normalize_total(adata, inplace=True)
77 | adata.layers["dense_X"] = adata.X.copy().toarray()
78 | scanpy.pp.log1p(adata)
79 | scanpy.pp.highly_variable_genes(adata)
80 | scanpy.tl.pca(adata)
81 | scanpy.pp.neighbors(adata)
82 | scanpy.tl.umap(adata)
83 | scanpy.tl.leiden(adata)
84 | scanpy.tl.rank_genes_groups(adata, "leiden")
85 |
86 | # Write the H5AD file
87 | adata.write("example.h5ad")
88 |
--------------------------------------------------------------------------------
/man/AnnData-usage.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/AnnData-usage.R
3 | \name{AnnData-usage}
4 | \alias{AnnData-usage}
5 | \title{AnnData structure and usage}
6 | \description{
7 | The \code{AnnData} object stores a data matrix \code{X} together with annotations of
8 | observations \code{obs} (\code{obsm}, \code{obsp}) and variables \code{var} (\code{varm}, \code{varp}).
9 | Additional layers of data can be stored in \code{layers} and unstructured
10 | annotations in \code{uns}.
11 | \subsection{Back ends}{
12 |
13 | There are different back ends for \code{AnnData} objects that inherit from the
14 | abstract \link{AbstractAnnData} class. For example, the \link{InMemoryAnnData} stores
15 | data in memory or the \link{HDF5AnnData} is backed by a H5AD file.
16 | }
17 |
18 | \subsection{Usage}{
19 |
20 | The items listed as \strong{"Slots"} are elements of the \code{AnnData} object that
21 | contain data and can be accessed or set. \strong{"Fields"} return information
22 | about the \code{AnnData} object but cannot be set directly. Both, as well as
23 | methods, can be accessed using the \code{$} operator
24 |
25 | For example:
26 | \itemize{
27 | \item \code{adata$X} returns the \code{X} matrix
28 | \item \code{adata$X <- x} sets the \code{X} matrix
29 | \item \code{adata$method()} calls a method
30 | }
31 | }
32 | }
33 | \section{Fields}{
34 |
35 | \describe{
36 | \item{\code{shape}}{Dimensions (observations x variables) of the \code{AnnData} object}
37 |
38 | \item{\code{n_obs}}{Number of observations}
39 |
40 | \item{\code{n_vars}}{Number of variables}
41 |
42 | \item{\code{obs_keys}}{Keys (column names) of \code{obs}}
43 |
44 | \item{\code{var_keys}}{Keys (column names) of \code{var}}
45 |
46 | \item{\code{layers_keys}}{Keys (element names) of \code{layers}}
47 |
48 | \item{\code{obsm_keys}}{Keys (element names) of \code{obsm}}
49 |
50 | \item{\code{varm_keys}}{Keys (element names) of \code{varm}}
51 |
52 | \item{\code{obsp_keys}}{Keys (element names) of \code{obsp}}
53 |
54 | \item{\code{varp_keys}}{Keys (element names) of \code{varp}}
55 |
56 | \item{\code{uns_keys}}{Keys (element names) of \code{uns}}
57 | }}
58 |
59 | \section{Slots}{
60 |
61 | \describe{
62 | \item{\code{X}}{The main data matrix. Either \code{NULL} or an observation x variable
63 | matrix (without dimnames) with dimensions consistent with \code{n_obs} and
64 | \code{n_vars}.}
65 |
66 | \item{\code{layers}}{Additional data layers. Must be \code{NULL} or a named list of
67 | matrices having dimensions consistent with \code{n_obs} and \code{n_vars}.}
68 |
69 | \item{\code{obs}}{Observation annotations. A \code{data.frame} with columns containing
70 | information about observations. The number of rows of \code{obs} defines the
71 | observation dimension of the \code{AnnData} object (\code{n_obs}). If \code{NULL}, an
72 | \code{n_obs} × 0 \code{data.frame} will automatically be generated.}
73 |
74 | \item{\code{var}}{Variable annotations. A \code{data.frame} with columns containing
75 | information about variables. The number of rows of \code{var} defines the
76 | variable dimension of the \code{AnnData} object (\code{n_vars}). If \code{NULL}, an
77 | \code{n_vars} × 0 \code{data.frame} will automatically be generated.}
78 |
79 | \item{\code{obs_names}}{Observation names. Either \code{NULL} or a vector of unique
80 | identifiers used to identify each row of \code{obs} and to act as an index into
81 | the observation dimension of the \code{AnnData} object. For compatibility with
82 | \emph{R} representations, \code{obs_names} should be a unique character vector.}
83 |
84 | \item{\code{var_names}}{Variable names. Either \code{NULL} or a vector of unique
85 | identifiers used to identify each row of \code{var} and to act as an index into
86 | the variable dimension of the \code{AnnData} object. For compatibility with \emph{R}
87 | representations, \code{var_names} should be a unique character vector.}
88 |
89 | \item{\code{obsm}}{Multi-dimensional observation annotation. Must be \code{NULL} or a
90 | named list of array-like elements with number of rows equal to \code{n_obs}.}
91 |
92 | \item{\code{varm}}{Multi-dimensional variable annotations. Must be \code{NULL} or a named
93 | list of array-like elements with number of rows equal to \code{n_vars}.}
94 |
95 | \item{\code{obsp}}{Observation pairs. Must be \code{NULL} or a named list of array-like
96 | elements with number of rows and columns equal to \code{n_obs}.}
97 |
98 | \item{\code{varp}}{Variable pairs. Must be \code{NULL} or a named list of array-like
99 | elements with number of rows and columns equal to \code{n_vars}.}
100 |
101 | \item{\code{uns}}{Unstructured annotations. Must be \code{NULL} or a named list.}
102 | }}
103 |
104 | \section{Methods}{
105 |
106 | \subsection{Conversion methods:}{
107 |
108 | \describe{
109 | \item{
110 | \code{as_SingleCellExperiment()}
111 | }{
112 | Convert to \code{\link[SingleCellExperiment:SingleCellExperiment]{SingleCellExperiment::SingleCellExperiment}}, see
113 | \code{\link[=as_SingleCellExperiment]{as_SingleCellExperiment()}}
114 | }
115 | \item{\code{as_Seurat()}}{Convert to \code{\link[SeuratObject:Seurat-class]{SeuratObject::Seurat}}, see \code{\link[=as_Seurat]{as_Seurat()}}}
116 | \item{\code{as_InMemoryAnnData()}}{Convert to \code{\link{InMemoryAnnData}}, as \code{\link[=as_InMemoryAnnData]{as_InMemoryAnnData()}}}
117 | \item{\code{as_HDF5AnnData()}}{Convert to \code{\link{HDF5AnnData}}, see \code{\link[=as_HDF5AnnData]{as_HDF5AnnData()}}}
118 | }
119 | }
120 |
121 | \subsection{Output methods:}{
122 |
123 | \describe{
124 | \item{
125 | \code{write_h5ad()}
126 | }{
127 | Write the \code{AnnData} object to an HDF5 file, see \code{\link[=write_h5ad]{write_h5ad()}}
128 | }
129 | }
130 | }
131 |
132 | \subsection{General methods:}{
133 |
134 | \describe{
135 | \item{\code{print()}}{Print a summary of the \code{AnnData} object}
136 | }
137 | }
138 | }
139 |
140 | \section{Functions that can be used to create AnnData objects}{
141 |
142 |
143 | \describe{
144 | \item{\code{\link[=AnnData]{AnnData()}}}{Create an \link{InMemoryAnnData} object}
145 | \item{\code{\link[=read_h5ad]{read_h5ad()}}}{Read an \code{AnnData} from a H5AD file}
146 | \item{\code{\link[=as_AnnData]{as_AnnData()}}}{Convert other objects to an \code{AnnData} object}
147 | }
148 | }
149 |
150 | \seealso{
151 | The documentation for the Python \code{anndata} package
152 | \url{https://anndata.readthedocs.io/en/stable/}
153 |
154 | \link{AbstractAnnData} for the abstract class that all \code{AnnData} objects
155 | inherit from
156 |
157 | \link{InMemoryAnnData} for the in-memory implementation of \code{AnnData}
158 |
159 | \link{HDF5AnnData} for the HDF5-backed implementation of \code{AnnData}
160 | }
161 |
--------------------------------------------------------------------------------
/man/AnnData.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/AnnData.R
3 | \name{AnnData}
4 | \alias{AnnData}
5 | \title{Create an in-memory AnnData object.}
6 | \usage{
7 | AnnData(
8 | X = NULL,
9 | obs = NULL,
10 | var = NULL,
11 | layers = NULL,
12 | obsm = NULL,
13 | varm = NULL,
14 | obsp = NULL,
15 | varp = NULL,
16 | uns = NULL,
17 | shape = NULL
18 | )
19 | }
20 | \arguments{
21 | \item{X}{See the \code{X} slot in \link{AnnData-usage}}
22 |
23 | \item{obs}{See the \code{obs} slot in \link{AnnData-usage}}
24 |
25 | \item{var}{See the \code{var} slot in \link{AnnData-usage}}
26 |
27 | \item{layers}{See the \code{layers} slot in \link{AnnData-usage}}
28 |
29 | \item{obsm}{See the \code{obsm} slot in \link{AnnData-usage}}
30 |
31 | \item{varm}{See the \code{varm} slot in \link{AnnData-usage}}
32 |
33 | \item{obsp}{See the \code{obsp} slot in \link{AnnData-usage}}
34 |
35 | \item{varp}{See the \code{varp} slot in \link{AnnData-usage}}
36 |
37 | \item{uns}{See the \code{uns} slot in \link{AnnData-usage}}
38 |
39 | \item{shape}{Shape tuple (e.g. \code{c(n_obs, n_vars)}). Can be provided if both
40 | \code{X} or \code{obs} and \code{var} are not provided.}
41 | }
42 | \value{
43 | An \link{InMemoryAnnData} object
44 | }
45 | \description{
46 | For more information on the functionality of an AnnData object, see \link{AnnData-usage}
47 | }
48 | \examples{
49 | adata <- AnnData(
50 | X = matrix(1:12, nrow = 3, ncol = 4),
51 | obs = data.frame(
52 | row.names = paste0("obs", 1:3),
53 | n_counts = c(1, 2, 3),
54 | n_cells = c(1, 2, 3)
55 | ),
56 | var = data.frame(
57 | row.names = paste0("var", 1:4),
58 | n_cells = c(1, 2, 3, 4)
59 | )
60 | )
61 |
62 | adata
63 | }
64 | \seealso{
65 | \link{AnnData-usage} for details of \code{AnnData} structure and usage
66 |
67 | Other AnnData creators:
68 | \code{\link{as_AnnData}()},
69 | \code{\link{read_h5ad}()}
70 | }
71 | \concept{AnnData creators}
72 |
--------------------------------------------------------------------------------
/man/anndataR-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/anndataR-package.R
3 | \docType{package}
4 | \name{anndataR-package}
5 | \alias{anndataR}
6 | \alias{anndataR-package}
7 | \title{anndataR: AnnData interoperability in R}
8 | \description{
9 | \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}}
10 |
11 | Bring the power and flexibility of AnnData to the R ecosystem, allowing you to effortlessly manipulate and analyze your single-cell data. This package lets you work with backed h5ad and zarr files, directly access various slots (e.g. X, obs, var), or convert the data into SingleCellExperiment and Seurat objects.
12 | }
13 | \seealso{
14 | Useful links:
15 | \itemize{
16 | \item \url{https://anndatar.data-intuitive.com/}
17 | \item \url{https://github.com/scverse/anndataR}
18 | \item Report bugs at \url{https://github.com/scverse/anndataR/issues}
19 | }
20 |
21 | }
22 | \author{
23 | \strong{Maintainer}: Robrecht Cannoodt \email{robrecht@data-intuitive.com} (\href{https://orcid.org/0000-0003-3641-729X}{ORCID}) (rcannood)
24 |
25 | Authors:
26 | \itemize{
27 | \item Luke Zappia \email{luke@lazappi.id.au} (\href{https://orcid.org/0000-0001-7744-8565}{ORCID}) (lazappi)
28 | \item Martin Morgan \email{mtmorgan.bioc@gmail.com} (\href{https://orcid.org/0000-0002-5874-8148}{ORCID}) (mtmorgan)
29 | \item Louise Deconinck \email{louise.deconinck@gmail.com} (\href{https://orcid.org/0000-0001-8100-6823}{ORCID}) (LouiseDck)
30 | }
31 |
32 | Other contributors:
33 | \itemize{
34 | \item Danila Bredikhin \email{danila.bredikhin@embl.de} (\href{https://orcid.org/0000-0001-8089-6983}{ORCID}) (gtca) [contributor]
35 | \item Isaac Virshup (\href{https://orcid.org/0000-0002-1710-8945}{ORCID}) (ivirshup) [contributor]
36 | \item Brian Schilder \email{brian_schilder@alumni.brown.edu} (\href{https://orcid.org/0000-0001-5949-2191}{ORCID}) (bschilder) [contributor]
37 | \item Chananchida Sang-aram (\href{https://orcid.org/0000-0002-0922-0822}{ORCID}) (csangara) [contributor]
38 | \item Data Intuitive \email{info@data-intuitive.com} [funder, copyright holder]
39 | \item Chan Zuckerberg Initiative [funder]
40 | }
41 |
42 | }
43 | \keyword{internal}
44 |
--------------------------------------------------------------------------------
/man/as_HDF5AnnData.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/HDF5AnnData.R
3 | \name{as_HDF5AnnData}
4 | \alias{as_HDF5AnnData}
5 | \title{Convert an \code{AnnData} to an \code{HDF5AnnData}}
6 | \usage{
7 | as_HDF5AnnData(
8 | adata,
9 | file,
10 | compression = c("none", "gzip", "lzf"),
11 | mode = c("w-", "r", "r+", "a", "w", "x")
12 | )
13 | }
14 | \arguments{
15 | \item{adata}{An \code{AnnData} object to be converted to \code{\link{HDF5AnnData}}}
16 |
17 | \item{file}{The file name (character) of the \code{.h5ad} file}
18 |
19 | \item{compression}{The compression algorithm to use when writing the
20 | HDF5 file. Can be one of \code{"none"}, \code{"gzip"} or \code{"lzf"}. Defaults to
21 | \code{"none"}.}
22 |
23 | \item{mode}{The mode to open the HDF5 file:
24 | \itemize{
25 | \item \code{a} creates a new file or opens an existing one for read/write
26 | \item \code{r} opens an existing file for reading
27 | \item \verb{r+} opens an existing file for read/write
28 | \item \code{w} creates a file, truncating any existing ones
29 | \item \verb{w-}/\code{x} are synonyms, creating a file and failing if it already exists
30 | }}
31 | }
32 | \value{
33 | An \code{\link{HDF5AnnData}} object with the same data as the input \code{AnnData}
34 | object.
35 | }
36 | \description{
37 | Convert another \code{AnnData} object to an \code{\link{HDF5AnnData}} object
38 | }
39 | \examples{
40 | ad <- AnnData(
41 | X = matrix(1:5, 3L, 5L),
42 | layers = list(
43 | A = matrix(5:1, 3L, 5L),
44 | B = matrix(letters[1:5], 3L, 5L)
45 | ),
46 | obs = data.frame(row.names = LETTERS[1:3], cell = 1:3),
47 | var = data.frame(row.names = letters[1:5], gene = 1:5),
48 | )
49 | ad$as_HDF5AnnData("test.h5ad")
50 | # remove file
51 | unlink("test.h5ad")
52 | }
53 | \seealso{
54 | Other object converters:
55 | \code{\link{as_AnnData}()},
56 | \code{\link{as_InMemoryAnnData}()},
57 | \code{\link{as_Seurat}()},
58 | \code{\link{as_SingleCellExperiment}()}
59 | }
60 | \concept{object converters}
61 | \keyword{internal}
62 |
--------------------------------------------------------------------------------
/man/as_InMemoryAnnData.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/InMemoryAnnData.R
3 | \name{as_InMemoryAnnData}
4 | \alias{as_InMemoryAnnData}
5 | \title{Convert an \code{AnnData} to an \code{InMemoryAnnData}}
6 | \usage{
7 | as_InMemoryAnnData(adata)
8 | }
9 | \arguments{
10 | \item{adata}{An \code{AnnData} object to be converted to \code{\link{InMemoryAnnData}}}
11 | }
12 | \value{
13 | An \code{\link{InMemoryAnnData}} object with the same data as the input
14 | \code{AnnData} object
15 | }
16 | \description{
17 | Convert another \code{AnnData} object to an \code{\link{InMemoryAnnData}} object
18 | }
19 | \examples{
20 | ad <- AnnData(
21 | X = matrix(1:5, 3L, 5L),
22 | layers = list(
23 | A = matrix(5:1, 3L, 5L),
24 | B = matrix(letters[1:5], 3L, 5L)
25 | ),
26 | obs = data.frame(row.names = LETTERS[1:3], cell = 1:3),
27 | var = data.frame(row.names = letters[1:5], gene = 1:5)
28 | )
29 | ad$as_InMemoryAnnData()
30 | }
31 | \seealso{
32 | Other object converters:
33 | \code{\link{as_AnnData}()},
34 | \code{\link{as_HDF5AnnData}()},
35 | \code{\link{as_Seurat}()},
36 | \code{\link{as_SingleCellExperiment}()}
37 | }
38 | \concept{object converters}
39 | \keyword{internal}
40 |
--------------------------------------------------------------------------------
/man/as_Seurat.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/as_Seurat.R
3 | \name{as_Seurat}
4 | \alias{as_Seurat}
5 | \title{Convert an \code{AnnData} to a \code{Seurat}}
6 | \usage{
7 | as_Seurat(
8 | adata,
9 | assay_name = "RNA",
10 | x_mapping = NULL,
11 | layers_mapping = TRUE,
12 | object_metadata_mapping = TRUE,
13 | assay_metadata_mapping = TRUE,
14 | reduction_mapping = TRUE,
15 | graph_mapping = TRUE,
16 | misc_mapping = TRUE
17 | )
18 | }
19 | \arguments{
20 | \item{adata}{The \code{AnnData} object to convert.}
21 |
22 | \item{assay_name}{Name of the assay to be created in the new \code{Seurat} object}
23 |
24 | \item{x_mapping}{A string specifying the name of the layer in the resulting
25 | \code{Seurat} object where the data in the \code{X} slot of \code{adata} will be mapped to}
26 |
27 | \item{layers_mapping}{A named vector where names are names of \code{Layers} in the
28 | resulting \code{Seurat} object and values are keys of \code{layers} in \code{adata}. See
29 | below for details.}
30 |
31 | \item{object_metadata_mapping}{A named vector where names are cell metadata
32 | columns in the resulting \code{Seurat} object and values are columns of \code{obs} in
33 | \code{adata}. See below for details.}
34 |
35 | \item{assay_metadata_mapping}{A named vector where names are variable
36 | metadata columns in the assay of the resulting \code{Seurat} object and values
37 | are columns of \code{var} in \code{adata}. See below for details.}
38 |
39 | \item{reduction_mapping}{A named vector where names are names of \code{Embeddings}
40 | in the resulting \code{Seurat} object and values are keys of \code{obsm} in \code{adata}.
41 | Alternatively, a named list where names are names of \code{Embeddings} in the
42 | resulting \code{Seurat} object and values are vectors with the items \code{"key"},
43 | \code{"embeddings"} and (optionally) \code{"loadings"}. See below for details.}
44 |
45 | \item{graph_mapping}{A named vector where names are names of \code{Graphs} in the
46 | resulting \code{Seurat} object and values are keys of \code{obsp} in \code{adata}. See
47 | below for details.}
48 |
49 | \item{misc_mapping}{A named vector where names are names of \code{Misc} in the
50 | resulting \code{Seurat} object and values are keys of \code{uns} in \code{adata}. See
51 | below for details.}
52 | }
53 | \value{
54 | A \code{Seurat} object containing the requested data from \code{adata}
55 | }
56 | \description{
57 | Convert an \code{AnnData} object to a \code{Seurat} object
58 | }
59 | \details{
60 | \subsection{Mapping arguments}{
61 |
62 | All mapping arguments expect a named character vector where names are the
63 | names of the slot in the \code{Seurat} object and values are the keys of the
64 | corresponding slot of \code{adata}. If \code{TRUE}, the conversion function will guess
65 | which items to copy as described in the conversion table below. In most
66 | cases, the default is to copy all items using the same names except where the
67 | correspondence between objects is unclear. The \code{reduction_mapping} argument
68 | can also accept a more complex list format, see below for details. To avoid
69 | copying anything to a slot, set the mapping argument to \code{FALSE}. Empt
70 | mapping arguments (\code{NULL}, \code{c()}, \code{list()}) will be treated as \code{FALSE} with
71 | a warning. If an unnamed vector is provided, the values will be used as
72 | names.
73 | \subsection{Examples:}{
74 | \itemize{
75 | \item \code{TRUE} will guess which items to copy as described in the conversion
76 | table
77 | \item \code{c(seurat_item = "adata_item")} will copy \code{adata_item} from the slot in
78 | \code{adata} to \code{seurat_item} in the corresponding slot of the new \code{Seurat}
79 | object
80 | \item \code{FALSE} will avoid copying anything to the slot
81 | \item \code{c("adata_item")} is equivalent to \code{c(adata_item = "adata_item")}
82 | }
83 | }
84 |
85 | }
86 |
87 | \subsection{Conversion table}{\tabular{llll}{
88 | \strong{From \code{AnnData}} \tab \strong{To \code{Seurat}} \tab \strong{Example mapping argument} \tab \strong{Default if \code{NULL}} \cr
89 | \code{adata$X} \tab \code{Layers(seurat)} \tab \code{x_mapping = "counts"} \emph{OR} \code{layers_mapping = c(counts = NA)} \tab The data in \code{adata$X} is copied to a layer named \code{X} \cr
90 | \code{adata$layers} \tab \code{Layers(seurat)} \tab \code{layers_mapping = c(counts = "counts")} \tab All items are copied by name \cr
91 | \code{adata$obs} \tab \code{seurat[[]]} \tab \code{object_metadata_mapping = c(n_counts = "n_counts", cell_type = "CellType")} \tab All columns are copied by name \cr
92 | \code{adata$var} \tab \code{seurat[[assay_name]][[]]} \tab \code{assay_metadata_mapping = c(n_cells = "n_cells", pct_zero = "PctZero")} \tab All columns are copied by name \cr
93 | \code{adata$obsm} \tab \code{Embeddings(sce)} \tab \code{reduction_mapping = c(pca = "X_pca")} \strong{OR} \code{reduction_mapping = list(pca = c(key = "PC_", obsm = "X_pca", varm = "PCs"))} \tab All items that can be coerced to a numeric matrix are copied by name without loadings except for \code{"X_pca"} for which loadings are added from \code{"PCs"} \cr
94 | \code{adata$obsp} \tab \code{Graphs(seurat)} \tab \code{graph_mapping = c(nn = "connectivities")} \tab All items are copied by name \cr
95 | \code{adata$varp} \tab \emph{NA} \tab \emph{NA} \tab There is no corresponding slot for \code{varp} \cr
96 | \code{adata$uns} \tab \code{Misc(seurat)} \tab \code{misc_mapping = c(project_metadata = "metadata")} \tab All items are copied by name \cr
97 | }
98 |
99 | }
100 |
101 | \subsection{The \code{reduction_mapping} argument}{
102 |
103 | For the simpler named vector format, the names should be the names of
104 | \code{Embeddings} in the resulting \code{Seurat} object, and the values
105 | should be the keys of \code{obsm} in \code{adata}. A key will created from the \code{obsm}
106 | key.
107 |
108 | For more advanced mapping, use the list format where each item is a vector
109 | with the following names defining arguments to
110 | \code{\link[SeuratObject:CreateDimReducObject]{SeuratObject::CreateDimReducObject()}}:
111 | \itemize{
112 | \item \code{key}: the key of the resulting \code{\link[SeuratObject:DimReduc-class]{SeuratObject::DimReduc}} object, passed
113 | to the \code{key} argument after processing with \code{\link[SeuratObject:Key]{SeuratObject::Key()}}
114 | \item \code{embeddings}: a key of the \code{obsm} slot in \code{adata},
115 | \code{adata$obsm[[embeddings]]} is passed to the \code{embeddings} argument
116 | \item \code{loadings}: a key of the \code{varm} slot in \code{adata} (optional),
117 | \code{adata$varm[[loadings]]} is passed to the \code{loadings} argument
118 | }
119 | }
120 |
121 | \subsection{The \code{x_mapping} and \code{layers_mapping} arguments}{
122 |
123 | In order to specify where the data in \code{adata$X} will be stored in the
124 | \code{Layers(seurat)} slot of the resulting object, you can use either the \code{x_mapping}
125 | argument or the \code{layers_mapping} argument.
126 | If you use \code{x_mapping}, it should be a string specifying the name of the layer
127 | in \code{Layers(seurat)} where the data in \code{adata$X} will be stored.
128 | If you use \code{layers_mapping}, it should be a named vector where names are names
129 | of \code{Layers(seurat)} and values are keys of \code{layers} in \code{adata}.
130 | In order to indicate the \code{adata$X} slot, you use \code{NA} as the value in the vector.
131 | The name you provide for \code{x_mapping} may not be a name in \code{layers_mapping}.
132 | You must provide a layer named \code{counts} or \code{data} in either \code{x_mapping} or
133 | \code{layers_mapping}.
134 | }
135 | }
136 | \examples{
137 | \dontshow{if (rlang::is_installed("Seurat")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
138 | ad <- AnnData(
139 | X = matrix(1:5, 3L, 5L),
140 | obs = data.frame(row.names = LETTERS[1:3], cell = 1:3),
141 | var = data.frame(row.names = letters[1:5], gene = 1:5)
142 | )
143 |
144 | # Default usage
145 | seurat <- ad$as_Seurat(
146 | assay_name = "RNA",
147 | x_mapping = "counts",
148 | layers_mapping = TRUE,
149 | object_metadata_mapping = TRUE,
150 | assay_metadata_mapping = TRUE,
151 | reduction_mapping = TRUE,
152 | graph_mapping = TRUE,
153 | misc_mapping = TRUE
154 | )
155 | \dontshow{\}) # examplesIf}
156 | }
157 | \seealso{
158 | Other object converters:
159 | \code{\link{as_AnnData}()},
160 | \code{\link{as_HDF5AnnData}()},
161 | \code{\link{as_InMemoryAnnData}()},
162 | \code{\link{as_SingleCellExperiment}()}
163 | }
164 | \concept{object converters}
165 | \keyword{internal}
166 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-deprecated.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-experimental.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-stable.svg:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/man/figures/lifecycle-superseded.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/man/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scverse/anndataR/8dfa46ae06fd5cc1d9b19479c5512a56d2357042/man/figures/logo.png
--------------------------------------------------------------------------------
/man/from_Seurat.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/from_Seurat.R
3 | \name{from_Seurat}
4 | \alias{from_Seurat}
5 | \title{Convert a Seurat object to an AnnData object}
6 | \usage{
7 | from_Seurat(
8 | seurat_obj,
9 | assay_name = NULL,
10 | x_mapping = NULL,
11 | layers_mapping = TRUE,
12 | obs_mapping = TRUE,
13 | var_mapping = TRUE,
14 | obsm_mapping = TRUE,
15 | varm_mapping = TRUE,
16 | obsp_mapping = TRUE,
17 | varp_mapping = TRUE,
18 | uns_mapping = TRUE,
19 | output_class = c("InMemory", "HDF5AnnData"),
20 | ...
21 | )
22 | }
23 | \arguments{
24 | \item{seurat_obj}{See \code{\link[=as_AnnData]{as_AnnData()}}}
25 |
26 | \item{assay_name}{See \code{\link[=as_AnnData]{as_AnnData()}}}
27 |
28 | \item{x_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
29 |
30 | \item{layers_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
31 |
32 | \item{obs_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
33 |
34 | \item{var_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
35 |
36 | \item{obsm_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
37 |
38 | \item{varm_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
39 |
40 | \item{obsp_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
41 |
42 | \item{varp_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
43 |
44 | \item{uns_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
45 |
46 | \item{output_class}{See \code{\link[=as_AnnData]{as_AnnData()}}}
47 |
48 | \item{...}{See \code{\link[=as_AnnData]{as_AnnData()}}}
49 | }
50 | \value{
51 | An \code{AnnData} object
52 | }
53 | \description{
54 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
55 |
56 | This function is deprecated, use \code{\link[=as_AnnData]{as_AnnData()}} instead
57 | }
58 |
--------------------------------------------------------------------------------
/man/from_SingleCellExperiment.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/from_SingleCellExperiment.R
3 | \name{from_SingleCellExperiment}
4 | \alias{from_SingleCellExperiment}
5 | \title{Convert a SingleCellExperiment object to an AnnData object}
6 | \usage{
7 | from_SingleCellExperiment(
8 | sce,
9 | x_mapping = NULL,
10 | layers_mapping = TRUE,
11 | obs_mapping = TRUE,
12 | var_mapping = TRUE,
13 | obsm_mapping = TRUE,
14 | varm_mapping = TRUE,
15 | obsp_mapping = TRUE,
16 | varp_mapping = TRUE,
17 | uns_mapping = TRUE,
18 | output_class = c("InMemory", "HDF5AnnData"),
19 | ...
20 | )
21 | }
22 | \arguments{
23 | \item{sce}{See \code{\link[=as_AnnData]{as_AnnData()}}}
24 |
25 | \item{x_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
26 |
27 | \item{layers_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
28 |
29 | \item{obs_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
30 |
31 | \item{var_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
32 |
33 | \item{obsm_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
34 |
35 | \item{varm_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
36 |
37 | \item{obsp_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
38 |
39 | \item{varp_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
40 |
41 | \item{uns_mapping}{See \code{\link[=as_AnnData]{as_AnnData()}}}
42 |
43 | \item{output_class}{See \code{\link[=as_AnnData]{as_AnnData()}}}
44 |
45 | \item{...}{See \code{\link[=as_AnnData]{as_AnnData()}}}
46 | }
47 | \value{
48 | An \code{AnnData} object
49 | }
50 | \description{
51 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
52 |
53 | This function is deprecated, use \code{\link[=as_AnnData]{as_AnnData()}} instead
54 | }
55 |
--------------------------------------------------------------------------------
/man/generate_dataset.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/generate_dataset.R
3 | \name{generate_dataset}
4 | \alias{generate_dataset}
5 | \title{Generate a dataset}
6 | \usage{
7 | generate_dataset(
8 | n_obs = 10L,
9 | n_vars = 20L,
10 | x_type = "numeric_matrix",
11 | layer_types = c("numeric_matrix", "numeric_dense", "numeric_csparse",
12 | "numeric_rsparse", "numeric_matrix_with_nas", "numeric_dense_with_nas",
13 | "numeric_csparse_with_nas", "numeric_rsparse_with_nas", "integer_matrix",
14 | "integer_dense", "integer_csparse", "integer_rsparse", "integer_matrix_with_nas",
15 | "integer_dense_with_nas", "integer_csparse_with_nas", "integer_rsparse_with_nas"),
16 | obs_types = c("character", "integer", "factor", "factor_ordered", "logical", "numeric",
17 | "character_with_nas", "integer_with_nas", "factor_with_nas",
18 | "factor_ordered_with_nas", "logical_with_nas", "numeric_with_nas"),
19 | var_types = c("character", "integer", "factor", "factor_ordered", "logical", "numeric",
20 | "character_with_nas", "integer_with_nas", "factor_with_nas",
21 | "factor_ordered_with_nas", "logical_with_nas", "numeric_with_nas"),
22 | obsm_types = c("numeric_matrix", "numeric_dense", "numeric_csparse", "numeric_rsparse",
23 | "numeric_matrix_with_nas", "numeric_dense_with_nas", "numeric_csparse_with_nas",
24 | "numeric_rsparse_with_nas", "integer_matrix", "integer_dense", "integer_csparse",
25 | "integer_rsparse", "integer_matrix_with_nas", "integer_dense_with_nas",
26 | "integer_csparse_with_nas", "integer_rsparse_with_nas", "character", "integer",
27 | "factor", "factor_ordered", "logical", "numeric", "character_with_nas",
28 | "integer_with_nas", "factor_with_nas",
29 | "factor_ordered_with_nas",
30 | "logical_with_nas", "numeric_with_nas"),
31 | varm_types = c("numeric_matrix", "numeric_dense", "numeric_csparse", "numeric_rsparse",
32 | "numeric_matrix_with_nas", "numeric_dense_with_nas", "numeric_csparse_with_nas",
33 | "numeric_rsparse_with_nas", "integer_matrix", "integer_dense", "integer_csparse",
34 | "integer_rsparse", "integer_matrix_with_nas", "integer_dense_with_nas",
35 | "integer_csparse_with_nas", "integer_rsparse_with_nas", "character", "integer",
36 | "factor", "factor_ordered", "logical", "numeric", "character_with_nas",
37 | "integer_with_nas", "factor_with_nas",
38 | "factor_ordered_with_nas",
39 | "logical_with_nas", "numeric_with_nas"),
40 | obsp_types = c("numeric_matrix", "numeric_dense", "numeric_csparse", "numeric_rsparse",
41 | "numeric_matrix_with_nas", "numeric_dense_with_nas", "numeric_csparse_with_nas",
42 | "numeric_rsparse_with_nas", "integer_matrix", "integer_dense", "integer_csparse",
43 | "integer_rsparse", "integer_matrix_with_nas", "integer_dense_with_nas",
44 | "integer_csparse_with_nas", "integer_rsparse_with_nas"),
45 | varp_types = c("numeric_matrix", "numeric_dense", "numeric_csparse", "numeric_rsparse",
46 | "numeric_matrix_with_nas", "numeric_dense_with_nas", "numeric_csparse_with_nas",
47 | "numeric_rsparse_with_nas", "integer_matrix", "integer_dense", "integer_csparse",
48 | "integer_rsparse", "integer_matrix_with_nas", "integer_dense_with_nas",
49 | "integer_csparse_with_nas", "integer_rsparse_with_nas"),
50 | uns_types = c("scalar_character", "scalar_integer", "scalar_factor",
51 | "scalar_factor_ordered", "scalar_logical", "scalar_numeric",
52 | "scalar_character_with_nas", "scalar_integer_with_nas", "scalar_factor_with_nas",
53 | "scalar_factor_ordered_with_nas", "scalar_logical_with_nas",
54 | "scalar_numeric_with_nas", "vec_character", "vec_integer", "vec_factor",
55 | "vec_factor_ordered", "vec_logical", "vec_numeric", "vec_character_with_nas",
56 | "vec_integer_with_nas", "vec_factor_with_nas", "vec_factor_ordered_with_nas",
57 | "vec_logical_with_nas",
58 | "vec_numeric_with_nas", "df_character", "df_integer",
59 | "df_factor", "df_factor_ordered", "df_logical", "df_numeric",
60 | "df_character_with_nas", "df_integer_with_nas", "df_factor_with_nas",
61 | "df_factor_ordered_with_nas", "df_logical_with_nas", "df_numeric_with_nas",
62 | "mat_numeric_matrix", "mat_numeric_dense", "mat_numeric_csparse",
63 | "mat_numeric_rsparse", "mat_numeric_matrix_with_nas", "mat_numeric_dense_with_nas",
64 | "mat_numeric_csparse_with_nas", "mat_numeric_rsparse_with_nas", "mat_integer_matrix",
65 |
66 | "mat_integer_dense", "mat_integer_csparse", "mat_integer_rsparse",
67 | "mat_integer_matrix_with_nas", "mat_integer_dense_with_nas",
68 | "mat_integer_csparse_with_nas", "mat_integer_rsparse_with_nas", "list"),
69 | example = FALSE,
70 | format = c("list", "AnnData", "SingleCellExperiment", "Seurat")
71 | )
72 | }
73 | \arguments{
74 | \item{n_obs}{Number of observations to generate}
75 |
76 | \item{n_vars}{Number of variables to generate}
77 |
78 | \item{x_type}{Type of matrix to generate for X}
79 |
80 | \item{layer_types}{Types of matrices to generate for layers}
81 |
82 | \item{obs_types}{Types of vectors to generate for obs}
83 |
84 | \item{var_types}{Types of vectors to generate for var}
85 |
86 | \item{obsm_types}{Types of matrices to generate for obsm}
87 |
88 | \item{varm_types}{Types of matrices to generate for varm}
89 |
90 | \item{obsp_types}{Types of matrices to generate for obsp}
91 |
92 | \item{varp_types}{Types of matrices to generate for varp}
93 |
94 | \item{uns_types}{Types of objects to generate for uns}
95 |
96 | \item{example}{If \code{TRUE}, the types will be overridden to a small set of
97 | types. This is useful for documentations.}
98 |
99 | \item{format}{Object type to output, one of "list", "AnnData",
100 | "SingleCellExperiment", or "Seurat".}
101 | }
102 | \value{
103 | Object containing the generated dataset as defined by \code{output}
104 | }
105 | \description{
106 | Generate a dataset with different types of columns and layers
107 | }
108 | \examples{
109 | dummy <- generate_dataset()
110 | \dontrun{
111 | dummy <- generate_dataset(format = "AnnData")
112 | dummy <- generate_dataset(format = "SingleCellExperiment")
113 | dummy <- generate_dataset(format = "Seurat")
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/man/read_h5ad.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/read_h5ad.R
3 | \name{read_h5ad}
4 | \alias{read_h5ad}
5 | \title{Read H5AD}
6 | \usage{
7 | read_h5ad(
8 | path,
9 | as = c("InMemoryAnnData", "HDF5AnnData", "SingleCellExperiment", "Seurat"),
10 | to = deprecated(),
11 | mode = c("r", "r+", "a", "w", "w-", "x"),
12 | ...
13 | )
14 | }
15 | \arguments{
16 | \item{path}{Path to the H5AD file to read}
17 |
18 | \item{as}{The type of object to return. One of:
19 | \itemize{
20 | \item \code{"InMemoryAnnData"}: Read the H5AD file into memory as an
21 | \code{\link{InMemoryAnnData}} object
22 | \item \code{"HDF5AnnData"}: Read the H5AD file as an \code{\link{HDF5AnnData}} object
23 | \item \code{"SingleCellExperiment"}: Read the H5AD file as a
24 | \code{\link[SingleCellExperiment:SingleCellExperiment]{SingleCellExperiment::SingleCellExperiment}} object
25 | \item \code{"Seurat"}: Read the H5AD file as a
26 | \code{\link[SeuratObject:Seurat-class]{SeuratObject::Seurat}} object
27 | }}
28 |
29 | \item{to}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Deprecated, use \code{as} instead}
30 |
31 | \item{mode}{The mode to open the HDF5 file.
32 | \itemize{
33 | \item \code{a} creates a new file or opens an existing one for read/write.
34 | \item \code{r} opens an existing file for reading.
35 | \item \verb{r+} opens an existing file for read/write.
36 | \item \code{w} creates a file, truncating any existing ones.
37 | \item \verb{w-}/\code{x} are synonyms, creating a file and failing if it already exists.
38 | }}
39 |
40 | \item{...}{Extra arguments provided to the \verb{as_*} conversion function for the
41 | object specified by \code{as}}
42 | }
43 | \value{
44 | The object specified by \code{as}
45 | }
46 | \description{
47 | Read data from a H5AD file
48 | }
49 | \examples{
50 | h5ad_file <- system.file("extdata", "example.h5ad", package = "anndataR")
51 |
52 | # Read the H5AD as a SingleCellExperiment object
53 | if (requireNamespace("SingleCellExperiment", quietly = TRUE)) {
54 | sce <- read_h5ad(h5ad_file, as = "SingleCellExperiment")
55 | }
56 |
57 | # Read the H5AD as a Seurat object
58 | if (requireNamespace("SeuratObject", quietly = TRUE)) {
59 | seurat <- read_h5ad(h5ad_file, as = "Seurat")
60 | }
61 | }
62 | \seealso{
63 | Other AnnData creators:
64 | \code{\link{AnnData}()},
65 | \code{\link{as_AnnData}()}
66 | }
67 | \concept{AnnData creators}
68 |
--------------------------------------------------------------------------------
/man/to_Seurat.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/as_Seurat.R
3 | \name{to_Seurat}
4 | \alias{to_Seurat}
5 | \title{Convert an AnnData object to a Seurat object}
6 | \usage{
7 | to_Seurat(...)
8 | }
9 | \arguments{
10 | \item{...}{Arguments passed to \code{\link[=as_Seurat]{as_Seurat()}}}
11 | }
12 | \value{
13 | A \code{Seurat} object
14 | }
15 | \description{
16 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
17 |
18 | This function is deprecated, use \code{adata$as_Seurat()} instead
19 | }
20 |
--------------------------------------------------------------------------------
/man/to_SingleCellExperiment.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/as_SingleCellExperiment.R
3 | \name{to_SingleCellExperiment}
4 | \alias{to_SingleCellExperiment}
5 | \title{Convert an AnnData object to a SingleCellExperiment object}
6 | \usage{
7 | to_SingleCellExperiment(...)
8 | }
9 | \arguments{
10 | \item{...}{Arguments passed to \code{\link[=as_SingleCellExperiment]{as_SingleCellExperiment()}}}
11 | }
12 | \value{
13 | A \code{SingleCellExperiment} object
14 | }
15 | \description{
16 | \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}}
17 |
18 | This function is deprecated, use \code{adata$as_SingleCellExperiment()} instead
19 | }
20 |
--------------------------------------------------------------------------------
/man/write_h5ad.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/write_h5ad.R
3 | \name{write_h5ad}
4 | \alias{write_h5ad}
5 | \title{Write H5AD}
6 | \usage{
7 | write_h5ad(
8 | object,
9 | path,
10 | compression = c("none", "gzip", "lzf"),
11 | mode = c("w-", "r", "r+", "a", "w", "x"),
12 | ...
13 | )
14 | }
15 | \arguments{
16 | \item{object}{The object to write, either a
17 | \code{\link[SingleCellExperiment:SingleCellExperiment]{SingleCellExperiment::SingleCellExperiment}} or a
18 | \code{\link[SeuratObject:Seurat-class]{SeuratObject::Seurat}} object}
19 |
20 | \item{path}{Path of the file to write to}
21 |
22 | \item{compression}{The compression algorithm to use when writing the HDF5
23 | file. Can be one of \code{"none"}, \code{"gzip"} or \code{"lzf"}. Defaults to \code{"none"}.}
24 |
25 | \item{mode}{The mode to open the HDF5 file.
26 | \itemize{
27 | \item \code{a} creates a new file or opens an existing one for read/write
28 | \item \verb{r+} opens an existing file for read/write
29 | \item \code{w} creates a file, truncating any existing ones
30 | \item \verb{w-}/\code{x} are synonyms creating a file and failing if it already exists
31 | }}
32 |
33 | \item{...}{Additional arguments passed to \code{\link[=as_AnnData]{as_AnnData()}}}
34 | }
35 | \value{
36 | \code{path} invisibly
37 | }
38 | \description{
39 | Write an H5AD file
40 | }
41 | \examples{
42 | adata <- AnnData(
43 | X = matrix(1:5, 3L, 5L),
44 | layers = list(
45 | A = matrix(5:1, 3L, 5L),
46 | B = matrix(letters[1:5], 3L, 5L)
47 | ),
48 | obs = data.frame(row.names = LETTERS[1:3], cell = 1:3),
49 | var = data.frame(row.names = letters[1:5], gene = 1:5)
50 | )
51 | h5ad_file <- tempfile(fileext = ".h5ad")
52 | adata$write_h5ad(h5ad_file)
53 |
54 | # Write a SingleCellExperiment as an H5AD
55 | if (requireNamespace("SingleCellExperiment", quietly = TRUE)) {
56 | ncells <- 100
57 | counts <- matrix(rpois(20000, 5), ncol = ncells)
58 | logcounts <- log2(counts + 1)
59 |
60 | pca <- matrix(runif(ncells * 5), ncells)
61 | tsne <- matrix(rnorm(ncells * 2), ncells)
62 |
63 | sce <- SingleCellExperiment::SingleCellExperiment(
64 | assays = list(counts = counts, logcounts = logcounts),
65 | reducedDims = list(PCA = pca, tSNE = tsne)
66 | )
67 |
68 | adata <- as_AnnData(sce)
69 | h5ad_file <- tempfile(fileext = ".h5ad")
70 | adata$write_h5ad(h5ad_file)
71 | }
72 |
73 | # Write a Seurat as a H5AD
74 | if (requireNamespace("Seurat", quietly = TRUE)) {
75 | library(Seurat)
76 |
77 | counts <- matrix(1:15, 5L, 3L)
78 | dimnames(counts) <- list(
79 | LETTERS[1:5],
80 | letters[1:3]
81 | )
82 | cell.metadata <- data.frame(
83 | row.names = letters[1:3],
84 | cell = 1:3
85 | )
86 | obj <- CreateSeuratObject(counts, meta.data = cell.metadata)
87 | gene.metadata <- data.frame(
88 | row.names = LETTERS[1:5],
89 | gene = 1:5
90 | )
91 | obj[["RNA"]] <- AddMetaData(GetAssay(obj), gene.metadata)
92 |
93 | adata <- as_AnnData(obj)
94 | h5ad_file <- tempfile(fileext = ".h5ad")
95 | adata$write_h5ad(h5ad_file)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | # This file is part of the standard setup for testthat.
2 | # It is recommended that you do not modify it.
3 | #
4 | # Where should you do additional test configuration?
5 | # Learn more about the roles of various files in:
6 | # * https://r-pkgs.org/tests.html
7 | # * https://testthat.r-lib.org/reference/test_package.html#special-files
8 |
9 | library(testthat)
10 | library(anndataR)
11 |
12 | test_check("anndataR")
13 |
--------------------------------------------------------------------------------
/tests/testthat/helper-expect_equal_py.R:
--------------------------------------------------------------------------------
1 | expect_equal_py <- function(a, b) {
2 | requireNamespace("testthat")
3 | requireNamespace("reticulate")
4 |
5 | bi <- reticulate::import_builtins()
6 |
7 | testthat::expect_equal(bi$type(a), bi$type(b)) # does this always work?
8 |
9 | if (inherits(a, "pandas.core.frame.DataFrame")) {
10 | pd <- reticulate::import("pandas")
11 | testthat::expect_null(
12 | pd$testing$assert_frame_equal(
13 | a,
14 | b,
15 | check_dtype = FALSE,
16 | check_exact = FALSE
17 | )
18 | )
19 | } else if (
20 | inherits(a, "np.ndarray") || inherits(a, "scipy.sparse.base.spmatrix")
21 | ) {
22 | scipy <- reticulate::import("scipy")
23 | np <- reticulate::import("numpy")
24 |
25 | testthat::expect_equal(a$dtype, b$dtype)
26 |
27 | testthat::expect_equal(
28 | py_to_r_ifneedbe(a$shape),
29 | py_to_r_ifneedbe(b$shape)
30 | )
31 |
32 | a_dense <-
33 | if (scipy$sparse$issparse(a)) {
34 | a$toarray()
35 | } else {
36 | a
37 | }
38 | b_dense <-
39 | if (scipy$sparse$issparse(b)) {
40 | b$toarray()
41 | } else {
42 | b
43 | }
44 |
45 | testthat::expect_null(
46 | np$testing$assert_allclose(a_dense, b_dense)
47 | )
48 | }
49 | }
50 |
51 | py_to_r_ifneedbe <- function(x) {
52 | if (inherits(x, "python.builtin.object")) {
53 | requireNamespace("reticulate")
54 | reticulate::py_to_r(x)
55 | } else {
56 | x
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/testthat/helper-py-R-equivalences.R:
--------------------------------------------------------------------------------
1 | matrix_equivalences <- list(
2 | c("float_matrix", "numeric_matrix"),
3 | c("float_matrix", "numeric_dense"),
4 | c("float_matrix_nas", "numeric_matrix_with_nas"),
5 | c("float_matrix_nas", "numeric_dense_with_nas"),
6 | c("integer_matrix", "integer_matrix"),
7 | c("integer_matrix", "integer_dense"),
8 | c("float_csparse", "numeric_csparse"),
9 | c("float_csparse_nas", "numeric_csparse_with_nas"),
10 | c("float_rsparse", "numeric_rsparse"),
11 | c("float_rsparse_nas", "numeric_rsparse_with_nas")
12 | )
13 |
14 | # python, R
15 | vector_equivalences <- list(
16 | c("categorical", "factor"),
17 | c("categorical_ordered", "factor_ordered"),
18 | c("categorical_missing_values", "factor_with_nas"),
19 | c("categorical_ordered_missing_values", "factor_ordered_with_nas"),
20 | c("string_array", "character"),
21 | c("dense_array", "numeric"),
22 | c("integer_array", "integer"),
23 | c("boolean_array", "logical"),
24 | c("nullable_integer_array", "integer_with_nas"),
25 | c("nullable_boolean_array", "logical_with_nas")
26 | )
27 |
28 | all_equivalences <- c(matrix_equivalences, vector_equivalences)
29 |
30 | check_arg <- function(args, name, falseval) {
31 | if (name %in% names(args)) {
32 | args[[name]][[1]]
33 | } else {
34 | falseval
35 | }
36 | }
37 |
38 | r_generate_dataset <- function(n_obs, n_vars, write = FALSE, ...) {
39 | args <- list(...)
40 |
41 | data <- generate_dataset(
42 | n_obs,
43 | n_vars,
44 | x_type = check_arg(args, "x_type", "numeric_matrix"),
45 | layer_types = check_arg(args, "layer_types", character()),
46 | obs_types = ifelse("obs_types" %in% names(args), args$obs_types, "integer"),
47 | var_types = ifelse("var_types" %in% names(args), args$var_types, "integer"),
48 | obsm_types = check_arg(args, "obsm_types", character()),
49 | varm_types = check_arg(args, "varm_types", character()),
50 | obsp_types = check_arg(args, "obsp_types", character()),
51 | varp_types = check_arg(args, "varp_types", character()),
52 | uns_types = check_arg(args, "uns_types", character()),
53 | format = "AnnData"
54 | )
55 | if (write) {
56 | r_write_dataset(data)
57 | }
58 |
59 | data
60 | }
61 |
62 | r_write_dataset <- function(dataset, file = NULL) {
63 | if (is.null(file)) {
64 | file <- tempfile(pattern = "hdf5_write_R_", fileext = ".h5ad")
65 | }
66 | write_h5ad(dataset, file)
67 | file
68 | }
69 |
--------------------------------------------------------------------------------
/tests/testthat/helper-skip_if_no_anndata.R:
--------------------------------------------------------------------------------
1 | # helper function to skip tests if we don't have the Python 'anndata' module
2 | # or the R {anndata} package
3 | skip_if_no_anndata <- function() {
4 | testthat::skip_if_not_installed("reticulate")
5 | testthat::skip_if_not_installed("anndata")
6 | requireNamespace("reticulate")
7 | testthat::skip_if_not(
8 | reticulate::py_module_available("anndata"),
9 | message = "Python anndata module not available for testing"
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/tests/testthat/helper-skip_if_no_h5diff.R:
--------------------------------------------------------------------------------
1 | # helper function to skip tests if h5diff is not available
2 | skip_if_no_h5diff <- function() {
3 | testthat::skip_if_not(
4 | {
5 | s <- system2(
6 | command = "which",
7 | args = "h5diff",
8 | stdout = TRUE,
9 | stderr = TRUE
10 | )
11 | is.null(attr(s, "status"))
12 | },
13 | message = "h5diff not available for testing"
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/tests/testthat/test-HDF5-read.R:
--------------------------------------------------------------------------------
1 | skip_if_not_installed("hdf5r")
2 |
3 | requireNamespace("vctrs")
4 |
5 | filename <- system.file("extdata", "example.h5ad", package = "anndataR")
6 | file <- hdf5r::H5File$new(
7 | filename,
8 | mode = "r"
9 | )
10 |
11 | test_that("reading encoding works", {
12 | encoding <- read_h5ad_encoding(file, "obs")
13 | expect_equal(names(encoding), c("type", "version"))
14 | })
15 |
16 | test_that("reading dense matrices works", {
17 | mat <- read_h5ad_dense_array(file, "layers/dense_counts")
18 | expect_true(is.matrix(mat))
19 | expect_type(mat, "integer")
20 | expect_equal(dim(mat), c(50, 100))
21 |
22 | mat <- read_h5ad_dense_array(file, "layers/dense_X")
23 | expect_true(is.matrix(mat))
24 | expect_type(mat, "double")
25 | expect_equal(dim(mat), c(50, 100))
26 | })
27 |
28 | test_that("reading sparse matrices works", {
29 | mat <- read_h5ad_sparse_array(file, "layers/csc_counts", type = "csc")
30 | expect_s4_class(mat, "dgCMatrix")
31 | expect_equal(dim(mat), c(50, 100))
32 |
33 | mat <- read_h5ad_sparse_array(file, "layers/counts", type = "csr")
34 | expect_s4_class(mat, "dgRMatrix")
35 | expect_equal(dim(mat), c(50, 100))
36 | })
37 |
38 | test_that("reading recarrays works", {
39 | array_list <- read_h5ad_rec_array(
40 | file,
41 | "uns/rank_genes_groups/logfoldchanges"
42 | )
43 | expect_true(is.list(array_list))
44 | expect_equal(names(array_list), c("0", "1", "2", "3", "4", "5"))
45 | for (array in array_list) {
46 | expect_true(is.vector(array))
47 | expect_type(array, "double")
48 | expect_equal(length(array), 100)
49 | }
50 | })
51 |
52 | test_that("reading 1D numeric arrays works", {
53 | array_1d <- read_h5ad_dense_array(file, "obs/Int")
54 | expect_equal(array_1d, array(0L:49L))
55 |
56 | array_1d <- read_h5ad_dense_array(file, "obs/Float")
57 | expect_equal(array_1d, array(rep(42.42, 50)))
58 | })
59 |
60 | test_that("reading 1D sparse numeric arrays works", {
61 | array_1d <- read_h5ad_sparse_array(file, "uns/Sparse1D", type = "csc")
62 | expect_s4_class(array_1d, "dgCMatrix")
63 | expect_equal(dim(array_1d), c(1, 6))
64 | })
65 |
66 | test_that("reading 1D nullable arrays works", {
67 | array_1d <- read_h5ad_nullable_integer(file, "obs/IntNA")
68 | expect_vector(array_1d, ptype = integer(), size = 50)
69 | expect_true(any(is.na(array_1d)))
70 |
71 | array_1d <- read_h5ad_dense_array(file, "obs/FloatNA")
72 | expected <- array(rep(42.42, 50))
73 | expected[1] <- NA
74 | expect_equal(array_1d, expected)
75 |
76 | array_1d <- read_h5ad_nullable_boolean(file, "obs/Bool")
77 | expect_vector(array_1d, ptype = logical(), size = 50)
78 | expect_false(any(is.na(array_1d)))
79 |
80 | array_1d <- read_h5ad_nullable_boolean(file, "obs/BoolNA")
81 | expect_vector(array_1d, ptype = logical(), size = 50)
82 | expect_true(any(is.na(array_1d)))
83 | })
84 |
85 | test_that("reading string scalars works", {
86 | scalar <- read_h5ad_string_scalar(file, "uns/StringScalar")
87 | expect_equal(scalar, "A string")
88 | })
89 |
90 | test_that("reading numeric scalars works", {
91 | scalar <- read_h5ad_numeric_scalar(file, "uns/IntScalar")
92 | expect_equal(scalar, 1)
93 | })
94 |
95 | test_that("reading string arrays works", {
96 | array <- read_h5ad_string_array(file, "uns/String")
97 | expect_equal(array, array(paste0("String ", 0L:9L)))
98 |
99 | array <- read_h5ad_string_array(file, "uns/String2D")
100 | expect_true(is.matrix(array))
101 | expect_type(array, "character")
102 | expect_equal(dim(array), c(5, 10))
103 | })
104 |
105 | test_that("reading mappings works", {
106 | mapping <- read_h5ad_mapping(file, "uns")
107 | expect_type(mapping, "list")
108 | expect_type(names(mapping), "character")
109 | })
110 |
111 | test_that("reading dataframes works", {
112 | df <- read_h5ad_data_frame(file, "obs")
113 | expect_s3_class(df, "data.frame")
114 | expect_equal(
115 | colnames(df),
116 | c(
117 | "Float",
118 | "FloatNA",
119 | "Int",
120 | "IntNA",
121 | "Bool",
122 | "BoolNA",
123 | "n_genes_by_counts",
124 | "log1p_n_genes_by_counts",
125 | "total_counts",
126 | "log1p_total_counts",
127 | "leiden"
128 | )
129 | )
130 | })
131 |
132 | file$close_all()
133 |
134 | test_that("reading H5AD as SingleCellExperiment works", {
135 | skip_if_not_installed("SingleCellExperiment")
136 |
137 | sce <- read_h5ad(filename, as = "SingleCellExperiment")
138 | expect_s4_class(sce, "SingleCellExperiment")
139 | })
140 |
141 | test_that("reading H5AD as Seurat works", {
142 | skip_if_not_installed("Seurat")
143 |
144 | seurat <- read_h5ad(filename, as = "Seurat")
145 | expect_s4_class(seurat, "Seurat")
146 | })
147 |
148 | test_that("deprecated to argument in read_h5ad() works", {
149 | expect_warning(mem_ad <- read_h5ad(filename, to = "InMemoryAnnData"))
150 | expect_true(inherits(mem_ad, "InMemoryAnnData"))
151 | })
152 |
--------------------------------------------------------------------------------
/tests/testthat/test-HDF5AnnData.R:
--------------------------------------------------------------------------------
1 | skip_if_not_installed("hdf5r")
2 |
3 | requireNamespace("vctrs")
4 |
5 | file <- system.file("extdata", "example.h5ad", package = "anndataR")
6 |
7 | test_that("opening H5AD works", {
8 | adata <- HDF5AnnData$new(file)
9 | expect_true(inherits(adata, "HDF5AnnData"))
10 | adata$close()
11 | })
12 |
13 | adata <- HDF5AnnData$new(file)
14 |
15 | # GETTERS ----------------------------------------------------------------
16 | # trackstatus: class=HDF5AnnData, feature=test_get_X, status=done
17 | test_that("reading X works", {
18 | X <- adata$X
19 | expect_s4_class(X, "dgRMatrix")
20 | expect_equal(dim(X), c(50, 100))
21 | })
22 |
23 | # trackstatus: class=HDF5AnnData, feature=test_get_layers, status=done
24 | test_that("reading layers works", {
25 | layers <- adata$layers
26 | expect_true(is.list(layers), "list")
27 | expect_equal(
28 | names(layers),
29 | c("counts", "csc_counts", "dense_X", "dense_counts")
30 | )
31 | })
32 |
33 | # trackstatus: class=HDF5AnnData, feature=test_get_obsm, status=done
34 | test_that("reading obsm works", {
35 | obsm <- adata$obsm
36 | expect_true(is.list(obsm), "list")
37 | expect_equal(
38 | names(obsm),
39 | c("X_pca", "X_umap")
40 | )
41 | })
42 |
43 | # trackstatus: class=HDF5AnnData, feature=test_get_varm, status=done
44 | test_that("reading varm works", {
45 | varm <- adata$varm
46 | expect_true(is.list(varm), "list")
47 | expect_equal(
48 | names(varm),
49 | c("PCs")
50 | )
51 | })
52 |
53 | test_that("obsm/varm validation works", {
54 | N_OBS <- 5
55 | N_VAR <- 3
56 |
57 | mtx <- matrix(
58 | 0,
59 | N_OBS,
60 | N_VAR
61 | )
62 |
63 | adata <- AnnData(
64 | X = mtx,
65 | obs = data.frame(row.names = as.character(1:N_OBS)),
66 | var = data.frame(row.names = as.character(1:N_VAR))
67 | )
68 |
69 | adata$obsm <- list(PCA = matrix(0, N_OBS, 4))
70 | adata$varm <- list(PCs = matrix(0, N_VAR, 4))
71 |
72 | expect_error(adata$obsm <- list(PCA = matrix(0, 4, 4)))
73 | expect_error(adata$varm <- list(PCs = matrix(0, 4, 4)))
74 | })
75 |
76 | test_that("obsp/varp validation works", {
77 | N_OBS <- 5
78 | N_VAR <- 3
79 |
80 | adata <- AnnData(
81 | obs = data.frame(row.names = as.character(1:N_OBS)),
82 | var = data.frame(row.names = as.character(1:N_VAR))
83 | )
84 |
85 | adata$obsp <- list(graph1 = matrix(0, N_OBS, N_OBS))
86 | adata$varp <- list(graph1 = matrix(0, N_VAR, N_VAR))
87 |
88 | expect_error(adata$obsp <- list(graph1 = matrix(0, 4, 4)))
89 | expect_error(adata$varp <- list(graph1 = matrix(0, 4, 4)))
90 | })
91 |
92 |
93 | # trackstatus: class=HDF5AnnData, feature=test_get_obs, status=done
94 | test_that("reading obs works", {
95 | obs <- adata$obs
96 | expect_s3_class(obs, "data.frame")
97 | expect_equal(
98 | colnames(obs),
99 | c(
100 | "Float",
101 | "FloatNA",
102 | "Int",
103 | "IntNA",
104 | "Bool",
105 | "BoolNA",
106 | "n_genes_by_counts",
107 | "log1p_n_genes_by_counts",
108 | "total_counts",
109 | "log1p_total_counts",
110 | "leiden"
111 | )
112 | )
113 | })
114 |
115 |
116 | # trackstatus: class=HDF5AnnData, feature=test_get_var, status=done
117 | test_that("reading var works", {
118 | var <- adata$var
119 | expect_s3_class(var, "data.frame")
120 | expect_equal(
121 | colnames(var),
122 | c(
123 | "String",
124 | "n_cells_by_counts",
125 | "mean_counts",
126 | "log1p_mean_counts",
127 | "pct_dropout_by_counts",
128 | "total_counts",
129 | "log1p_total_counts",
130 | "highly_variable",
131 | "means",
132 | "dispersions",
133 | "dispersions_norm"
134 | )
135 | )
136 | })
137 |
138 | # trackstatus: class=HDF5AnnData, feature=test_get_obs_names, status=done
139 | test_that("reading obs names works", {
140 | obs_names <- adata$obs_names
141 | expect_vector(obs_names, ptype = character(), size = 50)
142 | })
143 |
144 | # trackstatus: class=HDF5AnnData, feature=test_get_var_names, status=done
145 | test_that("reading var names works", {
146 | var_names <- adata$var_names
147 | expect_vector(var_names, ptype = character(), size = 100)
148 | })
149 |
150 | # SETTERS ----------------------------------------------------------------
151 | test_that("creating empty H5AD works", {
152 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
153 | expect_silent(HDF5AnnData$new(file = h5ad_file, mode = "w-"))
154 | })
155 |
156 | # trackstatus: class=HDF5AnnData, feature=test_set_X, status=done
157 | test_that("writing X works", {
158 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
159 | obs <- data.frame(row.names = 1:10)
160 | var <- data.frame(row.names = 1:20)
161 | h5ad <- HDF5AnnData$new(h5ad_file, obs = obs, var = var, mode = "w-")
162 |
163 | X <- matrix(rnorm(10 * 20), nrow = 10, ncol = 20)
164 | expect_silent(h5ad$X <- X)
165 | })
166 |
167 | # trackstatus: class=HDF5AnnData, feature=test_set_layers, status=done
168 | test_that("writing layers works", {
169 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
170 | obs <- data.frame(row.names = 1:10)
171 | var <- data.frame(row.names = 1:20)
172 | h5ad <- HDF5AnnData$new(h5ad_file, obs = obs, var = var, mode = "w-")
173 |
174 | X <- matrix(rnorm(10 * 20), nrow = 10, ncol = 20)
175 | expect_silent(h5ad$layers <- list(layer1 = X, layer2 = X))
176 | })
177 |
178 | # trackstatus: class=HDF5AnnData, feature=test_set_obs, status=done
179 | test_that("writing obs works", {
180 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
181 | obs <- data.frame(row.names = 1:10)
182 | var <- data.frame(row.names = 1:20)
183 | h5ad <- HDF5AnnData$new(h5ad_file, obs = obs, var = var, mode = "w-")
184 |
185 | obs <- data.frame(
186 | Letters = LETTERS[1:10],
187 | Numbers = 1:10,
188 | row.names = paste0("Row", 1:10)
189 | )
190 | h5ad$obs <- obs
191 | expect_identical(h5ad$obs_names, paste0("Row", 1:10))
192 | })
193 |
194 | # trackstatus: class=HDF5AnnData, feature=test_set_var, status=done
195 | test_that("writing var works", {
196 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
197 | obs <- data.frame(row.names = 1:10)
198 | var <- data.frame(row.names = 1:20)
199 | h5ad <- HDF5AnnData$new(h5ad_file, obs = obs, var = var, mode = "w-")
200 |
201 | var <- data.frame(
202 | Letters = LETTERS[1:20],
203 | Numbers = 1:20,
204 | row.names = paste0("Row", 1:20)
205 | )
206 | h5ad$var <- var
207 | expect_identical(h5ad$var_names, paste0("Row", 1:20))
208 | })
209 |
210 | # trackstatus: class=HDF5AnnData, feature=test_set_obs_names, status=done
211 | test_that("writing obs names works", {
212 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
213 | obs <- data.frame(row.names = 1:10)
214 | var <- data.frame(row.names = 1:20)
215 | h5ad <- HDF5AnnData$new(h5ad_file, obs = obs, var = var, mode = "w-")
216 |
217 | h5ad$obs_names <- LETTERS[1:10]
218 | expect_identical(h5ad$obs_names, LETTERS[1:10])
219 | })
220 |
221 | # trackstatus: class=HDF5AnnData, feature=test_set_var_names, status=done
222 | test_that("writing var names works", {
223 | h5ad_file <- withr::local_tempfile(fileext = ".h5ad")
224 | obs <- data.frame(row.names = 1:10)
225 | var <- data.frame(row.names = 1:20)
226 | h5ad <- HDF5AnnData$new(h5ad_file, obs = obs, var = var, mode = "w-")
227 |
228 | h5ad$var_names <- LETTERS[1:20]
229 | expect_identical(h5ad$var_names, LETTERS[1:20])
230 | })
231 |
232 | test_that("deprecated to_InMemoryAnnData() works", {
233 | expect_warning(mem_ad <- adata$to_InMemoryAnnData())
234 | expect_true(inherits(mem_ad, "InMemoryAnnData"))
235 | })
236 |
--------------------------------------------------------------------------------
/tests/testthat/test-InMemoryAnnData.R:
--------------------------------------------------------------------------------
1 | dummy <- generate_dataset(10L, 20L)
2 |
3 | # GETTERS ----------------------------------------------------------------
4 | test_that("Creating InMemoryAnnData works", {
5 | ad <- AnnData(
6 | X = dummy$X,
7 | obs = dummy$obs,
8 | var = dummy$var
9 | )
10 |
11 | # trackstatus: class=InMemoryAnnData, feature=test_get_X, status=done
12 | expect_equal(ad$X, dummy$X)
13 | # trackstatus: class=InMemoryAnnData, feature=test_get_obs, status=done
14 | expect_equal(ad$obs, dummy$obs)
15 | # trackstatus: class=InMemoryAnnData, feature=test_get_var, status=done
16 | expect_equal(ad$var, dummy$var)
17 | # trackstatus: class=InMemoryAnnData, feature=test_get_obs_names, status=done
18 | expect_equal(ad$obs_names, rownames(dummy$obs))
19 | # trackstatus: class=InMemoryAnnData, feature=test_get_var_names, status=done
20 | expect_equal(ad$var_names, rownames(dummy$var))
21 | expect_identical(ad$shape(), c(10L, 20L))
22 | })
23 |
24 | test_that("Creating InMemoryAnnData works with empty obs", {
25 | ad <- AnnData(
26 | obs = data.frame(),
27 | var = dummy$var
28 | )
29 | expect_identical(ad$shape(), c(0L, 20L))
30 | })
31 |
32 | test_that("Creating InMemoryAnnData works with empty var", {
33 | ad <- AnnData(
34 | obs = dummy$obs,
35 | var = data.frame()
36 | )
37 | expect_identical(ad$shape(), c(10L, 0L))
38 | })
39 |
40 | test_that("Creating AnnData works with only X, no obs or var", {
41 | X <- dummy$X
42 | dimnames(X) <- list(
43 | rownames(dummy$obs),
44 | rownames(dummy$var)
45 | )
46 | ad <- AnnData(X = X)
47 | expect_identical(ad$shape(), c(10L, 20L))
48 | expect_identical(ad$obs_names, rownames(dummy$obs))
49 | expect_identical(ad$var_names, rownames(dummy$var))
50 | })
51 |
52 | # trackstatus: class=InMemoryAnnData, feature=test_get_layers, status=done
53 | test_that("'layers' works", {
54 | ## layers test helper function
55 | layers_test <- function(obs, var, layers) {
56 | expect_no_condition({
57 | ad <- AnnData(obs = obs, var = var, layers = layers)
58 | })
59 | expect_identical(ad$layers, layers)
60 | }
61 |
62 | obs <- var <- data.frame()
63 | layers_test(obs, var, NULL)
64 | layers_test(obs, var, setNames(list(), character()))
65 | layers_test(obs, var, list(A = matrix(0, 0, 0)))
66 | ## must be a named list
67 | expect_error(AnnData(obs, var, list()))
68 |
69 | obs <- data.frame(row.names = letters[1:3])
70 | var <- data.frame(row.names = LETTERS[1:5])
71 | layers_test(obs, var, NULL)
72 | layers_test(obs, var, list(A = matrix(0, 3, 5)))
73 | layers_test(obs, var, list(A = matrix(0, 3, 5), B = matrix(1, 3, 5)))
74 |
75 | ## must be a named list
76 | layers <- list(matrix(0, 3, 5))
77 | expect_error(AnnData(obs = obs, var = var, layers = layers))
78 | ## matching dimensions
79 | layers <- list(A = matrix(0, 0, 0))
80 | expect_error(AnnData(obs = obs, var = var, layers = layers))
81 | layers <- list(A = matrix(0, 3, 5), B = matrix(1, 5, 3))
82 | expect_error(AnnData(obs = obs, var = var, layers = layers))
83 | })
84 |
85 | test_that("*_keys() works", {
86 | obs <- var <- data.frame()
87 | ad <- AnnData(obs = obs, var = var)
88 | expect_identical(ad$layers_keys(), NULL)
89 | expect_identical(ad$obs_keys(), character())
90 | expect_identical(ad$var_keys(), character())
91 |
92 | layers <- setNames(list(), character())
93 | ad <- AnnData(obs = obs, var = var, layers = layers)
94 | expect_identical(ad$layers_keys(), character())
95 |
96 | layers <- list(A = matrix(0, 3, 5), B = matrix(1, 3, 5))
97 | obs <- data.frame(row.names = letters[1:3], x = 1:3)
98 | var <- data.frame(row.names = letters[1:5], y = 1:5)
99 | ad <- AnnData(obs = obs, var = var, layers = layers)
100 | expect_identical(ad$layers_keys(), c("A", "B"))
101 | expect_identical(ad$obs_keys(), "x")
102 | expect_identical(ad$var_keys(), "y")
103 | })
104 |
105 | # SETTERS -----------------------------------------------------------------
106 |
107 | # trackstatus: class=InMemoryAnnData, feature=test_set_X, status=done
108 | test_that("write to X", {
109 | ad <- AnnData(
110 | X = dummy$X,
111 | obs = dummy$obs,
112 | var = dummy$var
113 | )
114 |
115 | X2 <- Matrix::rsparsematrix(nrow = 10, ncol = 20, density = .1)
116 | ad$X <- X2
117 |
118 | expect_equal(ad$X, X2)
119 |
120 | # change row in X
121 | ad$X[2, ] <- 10
122 | expect_equal(ad$X[2, ], rep(10, 20L))
123 |
124 | # change column in X
125 | ad$X[, 3] <- 5
126 | expect_equal(ad$X[, 3], rep(5, 10L))
127 | })
128 |
129 | # trackstatus: class=InMemoryAnnData, feature=test_set_obs, status=done
130 | test_that("write to obs", {
131 | ad <- AnnData(
132 | X = dummy$X,
133 | obs = dummy$obs,
134 | var = dummy$var
135 | )
136 |
137 | obs2 <- data.frame(
138 | foo = sample(letters, 10, replace = TRUE),
139 | bar = sample.int(4, 10, replace = TRUE),
140 | zing = sample(c(TRUE, FALSE), 10, replace = TRUE)
141 | )
142 | ad$obs <- obs2
143 |
144 | expect_equal(ncol(ad$obs), 3)
145 | expect_equal(nrow(ad$obs), 10)
146 | expect_equal(ad$obs, obs2)
147 |
148 | # change row in obs
149 | obs2row <- data.frame(foo = "a", bar = 3, zing = FALSE)
150 | ad$obs[2, ] <- obs2row
151 | expect_equal(ad$obs[2, ], obs2row, ignore_attr = TRUE)
152 |
153 | # change column in obs
154 | ad$obs[, 3] <- FALSE
155 | expect_equal(ad$obs[, 3], rep(FALSE, 10L))
156 | })
157 |
158 | # trackstatus: class=InMemoryAnnData, feature=test_set_var, status=done
159 | test_that("write to var", {
160 | ad <- AnnData(
161 | X = dummy$X,
162 | obs = dummy$obs,
163 | var = dummy$var
164 | )
165 |
166 | var2 <- data.frame(
167 | foo = sample(letters, 20L, replace = TRUE),
168 | bar = sample.int(4, 20L, replace = TRUE),
169 | zing = sample(c(TRUE, FALSE), 20L, replace = TRUE)
170 | )
171 | ad$var <- var2
172 |
173 | expect_equal(ncol(ad$var), 3)
174 | expect_equal(nrow(ad$var), 20)
175 | expect_equal(ad$var, var2)
176 |
177 | # change row in var
178 | var2row <- data.frame(foo = "a", bar = 3, zing = FALSE)
179 | ad$var[2, ] <- var2row
180 | expect_equal(ad$var[2, ], var2row, ignore_attr = TRUE)
181 |
182 | # change column in var
183 | ad$var[, 3] <- FALSE
184 | expect_equal(ad$var[, 3], rep(FALSE, 20L))
185 | })
186 |
187 | # trackstatus: class=InMemoryAnnData, feature=test_set_obs_names, status=done
188 | test_that("write to obs_names", {
189 | ad <- AnnData(
190 | X = dummy$X,
191 | obs = dummy$obs,
192 | var = dummy$var
193 | )
194 |
195 | obs_names2 <- letters[1:10]
196 | ad$obs_names <- obs_names2
197 |
198 | expect_equal(ad$obs_names, obs_names2)
199 |
200 | # change value in obs_names
201 | ad$obs_names[2:3] <- c("foo", "bar")
202 | expect_equal(ad$obs_names[1:4], c("a", "foo", "bar", "d"))
203 | })
204 |
205 | # trackstatus: class=InMemoryAnnData, feature=test_set_var_names, status=done
206 | test_that("write to var_names", {
207 | ad <- AnnData(
208 | X = dummy$X,
209 | obs = dummy$obs,
210 | var = dummy$var
211 | )
212 |
213 | var_names2 <- LETTERS[1:20]
214 | ad$var_names <- var_names2
215 |
216 | expect_equal(ad$var_names, var_names2)
217 |
218 | # change value in var_names
219 | ad$var_names[2:3] <- c("foo", "bar")
220 | expect_equal(ad$var_names[1:4], c("A", "foo", "bar", "D"))
221 | })
222 |
223 | # trackstatus: class=InMemoryAnnData, feature=test_set_layers, status=done
224 | test_that("write to layers", {
225 | ad <- AnnData(
226 | X = dummy$X,
227 | obs = dummy$obs,
228 | var = dummy$var,
229 | layers = dummy$layers
230 | )
231 |
232 | ## element assignment
233 | ad$layers[["X2"]] <- dummy$X + 2
234 | expect_equal(ad$layers[["X2"]], dummy$X + 2)
235 |
236 | ad$layers[["X4"]] <- dummy$X + 4
237 | expect_equal(ad$layers[["X4"]], dummy$X + 4)
238 |
239 | ## list assignment
240 | ad$layers <- rev(dummy$layers)
241 | expect_equal(ad$layers, rev(dummy$layers))
242 |
243 | ## remove
244 | ad$layers <- NULL
245 | expect_true(is.null(ad$layers))
246 | ## add to NULL
247 | ad$layers <- dummy$layers
248 | expect_identical(ad$layers, dummy$layers)
249 | })
250 |
251 | test_that("deprecated to_HDF5AnnData() works", {
252 | ad <- AnnData(
253 | X = dummy$X,
254 | obs = dummy$obs,
255 | var = dummy$var,
256 | layers = dummy$layers
257 | )
258 | expect_warning(h5_ad <- ad$to_HDF5AnnData(file = tempfile()))
259 | expect_true(inherits(h5_ad, "HDF5AnnData"))
260 | })
261 |
--------------------------------------------------------------------------------
/tests/testthat/test-as_Seurat.R:
--------------------------------------------------------------------------------
1 | test_that("as_Seurat() fails gracefully", {
2 | expect_error(as_Seurat(), regexp = "adata.*is missing")
3 | expect_error(as_Seurat("foo"), regexp = "must be a ")
4 | })
5 |
6 | skip_if_not_installed("Seurat")
7 | library(Seurat)
8 |
9 | known_issues <- read_known_issues()
10 |
11 | ad <- generate_dataset(n_obs = 10L, n_vars = 20L, format = "AnnData")
12 | ad$obsm[["X_pca"]] <- matrix(1:50, 10, 5)
13 | ad$varm[["PCs"]] <- matrix(1:100, 20, 5)
14 |
15 | layers_mapping <- c(NA, names(ad$layers))
16 | names(layers_mapping) <- c("counts", names(ad$layers))
17 | seu <- ad$as_Seurat(layers_mapping = layers_mapping)
18 |
19 | test_that("as_Seurat retains number of observations and features", {
20 | expect_equal(nrow(seu), 20)
21 | expect_equal(ncol(seu), 10)
22 |
23 | # trackstatus: class=Seurat, feature=test_get_var_names, status=done
24 | expect_equal(rownames(seu), rownames(ad$var))
25 | # trackstatus: class=Seurat, feature=test_get_obs_names, status=done
26 | expect_equal(colnames(seu), rownames(ad$obs))
27 | })
28 |
29 | # trackstatus: class=Seurat, feature=test_get_obs, status=done
30 | for (obs_key in colnames(ad$obs)) {
31 | test_that(paste0("as_Seurat retains obs key: ", obs_key), {
32 | msg <- message_if_known(
33 | backend = "to_Seurat",
34 | slot = c("obs"),
35 | dtype = obs_key,
36 | process = "convert",
37 | known_issues = known_issues
38 | )
39 | skip_if(!is.null(msg), message = msg)
40 |
41 | expect_true(obs_key %in% colnames(seu@meta.data))
42 | expect_equal(
43 | seu@meta.data[[obs_key]],
44 | ad$obs[[obs_key]],
45 | info = paste0("obs_key: ", obs_key)
46 | )
47 | })
48 | }
49 |
50 | # trackstatus: class=Seurat, feature=test_get_var, status=done
51 | for (var_key in colnames(ad$var)) {
52 | test_that(paste0("as_Seurat retains var key: ", var_key), {
53 | msg <- message_if_known(
54 | backend = "to_Seurat",
55 | slot = c("var"),
56 | dtype = var_key,
57 | process = "convert",
58 | known_issues = known_issues
59 | )
60 | skip_if(!is.null(msg), message = msg)
61 |
62 | active_assay <- seu[[DefaultAssay(seu)]]
63 | expect_true(var_key %in% colnames(active_assay@meta.data))
64 | expect_equal(active_assay@meta.data[[var_key]], ad$var[[var_key]])
65 | })
66 | }
67 |
68 |
69 | # trackstatus: class=Seurat, feature=test_get_layers, status=done
70 | for (layer_key in names(ad$layers)) {
71 | test_that(paste0("as_Seurat retains layer: ", layer_key), {
72 | msg <- message_if_known(
73 | backend = "to_Seurat",
74 | slot = c("layers"),
75 | dtype = layer_key,
76 | process = "convert",
77 | known_issues = known_issues
78 | )
79 | skip_if(!is.null(msg), message = msg)
80 |
81 | active_assay <- seu@assays[[seu@active.assay]]
82 |
83 | expect_true(layer_key %in% names(active_assay@layers))
84 | expect_true(
85 | all.equal(
86 | as.matrix(t(active_assay@layers[[layer_key]])),
87 | as.matrix(ad$layers[[layer_key]]),
88 | check.attributes = FALSE
89 | ),
90 | info = paste0("layer_key: ", layer_key)
91 | )
92 | })
93 | }
94 |
95 | # trackstatus: class=Seurat, feature=test_get_uns, status=done
96 | for (uns_key in names(ad$uns)) {
97 | test_that(paste0("as_Seurat retains uns key: ", uns_key), {
98 | msg <- message_if_known(
99 | backend = "to_Seurat",
100 | slot = c("uns"),
101 | dtype = uns_key,
102 | process = "convert",
103 | known_issues = known_issues
104 | )
105 | skip_if(!is.null(msg), message = msg)
106 |
107 | expect_true(uns_key %in% names(seu@misc))
108 | expect_equal(seu@misc[[uns_key]], ad$uns[[uns_key]], ignore_attr = TRUE)
109 | })
110 | }
111 |
112 | test_that("as_Seurat retains pca dimred", {
113 | msg <- message_if_known(
114 | backend = "to_Seurat",
115 | slot = c("obsm"),
116 | dtype = "pca",
117 | process = "convert",
118 | known_issues = known_issues
119 | )
120 |
121 | skip_if(!is.null(msg), message = msg)
122 |
123 | # trackstatus: class=Seurat, feature=test_get_obsm, status=wip
124 | expect_true("X_pca" %in% names(seu@reductions))
125 | expect_equal(
126 | Embeddings(seu, reduction = "X_pca"),
127 | ad$obsm[["X_pca"]],
128 | ignore_attr = TRUE
129 | )
130 |
131 | # trackstatus: class=Seurat, feature=test_get_varm, status=wip
132 | expect_equal(
133 | Loadings(seu, reduction = "X_pca"),
134 | ad$varm[["PCs"]],
135 | ignore_attr = TRUE
136 | )
137 | })
138 |
139 | test_that("as_Seurat works with list mappings", {
140 | expect_no_error(
141 | ad$as_Seurat(
142 | x_mapping = "counts",
143 | object_metadata_mapping = as.list(.as_Seurat_guess_object_metadata(ad)),
144 | layers_mapping = as.list(.as_Seurat_guess_layers(ad)),
145 | assay_metadata_mapping = as.list(.as_Seurat_guess_assay_metadata(ad)),
146 | reduction_mapping = as.list(.as_Seurat_guess_reductions(ad)),
147 | graph_mapping = as.list(.as_Seurat_guess_graphs(ad)),
148 | misc_mapping = as.list(.as_Seurat_guess_misc(ad))
149 | )
150 | )
151 |
152 | expect_error(
153 | ad$as_Seurat(
154 | reduction_mapping = list(numeric = "numeric_matrix")
155 | )
156 | )
157 | })
158 |
159 | test_that("as_Seurat works with a vector reduction_mapping", {
160 | expect_no_error(
161 | ad$as_Seurat(
162 | x_mapping = "counts",
163 | reduction_mapping = c(numeric = "numeric_matrix")
164 | )
165 | )
166 | })
167 |
168 | test_that("as_Seurat works with unnamed mappings", {
169 | expect_no_error(
170 | ad$as_Seurat(
171 | object_metadata_mapping = unname(.as_Seurat_guess_object_metadata(ad)),
172 | layers_mapping = c(
173 | na.omit(unname(.as_Seurat_guess_layers(ad))),
174 | counts = NA
175 | ),
176 | assay_metadata_mapping = unname(.as_Seurat_guess_assay_metadata(ad)),
177 | graph_mapping = unname(.as_Seurat_guess_graphs(ad)),
178 | misc_mapping = unname(.as_Seurat_guess_misc(ad))
179 | )
180 | )
181 | })
182 |
183 | test_that("deprecated to_Seurat() works", {
184 | expect_warning(seu <- ad$to_Seurat(x_mapping = "counts"))
185 | expect_s4_class(seu, "Seurat")
186 | })
187 |
--------------------------------------------------------------------------------
/tests/testthat/test-as_SingleCellExperiment.R:
--------------------------------------------------------------------------------
1 | skip_if_not_installed("SingleCellExperiment")
2 | library(SingleCellExperiment)
3 |
4 | known_issues <- read_known_issues()
5 |
6 | ad <- generate_dataset(n_obs = 10L, n_vars = 20L, format = "AnnData")
7 | ad$obsm[["X_pca"]] <- matrix(1:50, 10, 5)
8 | ad$varm[["PCs"]] <- matrix(1:100, 20, 5)
9 |
10 | sce <- ad$as_SingleCellExperiment()
11 |
12 | test_that("as_SCE retains nr of observations and features", {
13 | expect_equal(nrow(sce), 20)
14 | expect_equal(ncol(sce), 10)
15 |
16 | # trackstatus: class=SingleCellExperiment, feature=test_get_var_names, status=done
17 | expect_equal(rownames(sce), rownames(ad$var))
18 | # trackstatus: class=SingleCellExperiment, feature=test_get_obs_names, status=done
19 | expect_equal(colnames(sce), rownames(ad$obs))
20 | })
21 |
22 | # trackstatus: class=SingleCellExperiment, feature=test_get_obs, status=done
23 | for (obs_key in colnames(ad$obs)) {
24 | test_that(paste0("as_SCE retains obs key: ", obs_key), {
25 | msg <- message_if_known(
26 | backend = "to_SCE",
27 | slot = c("obs"),
28 | dtype = obs_key,
29 | process = "convert",
30 | known_issues = known_issues
31 | )
32 | skip_if(!is.null(msg), message = msg)
33 |
34 | expect_true(obs_key %in% colnames(colData(sce)))
35 | expect_equal(
36 | colData(sce)[[obs_key]],
37 | ad$obs[[obs_key]],
38 | info = paste0("obs_key: ", obs_key)
39 | )
40 | })
41 | }
42 |
43 | # trackstatus: class=SingleCellExperiment, feature=test_get_var, status=done
44 | for (var_key in colnames(ad$var)) {
45 | test_that(paste0("as_SCE retains var key: ", var_key), {
46 | msg <- message_if_known(
47 | backend = "to_SCE",
48 | slot = c("var"),
49 | dtype = var_key,
50 | process = "convert",
51 | known_issues = known_issues
52 | )
53 | skip_if(!is.null(msg), message = msg)
54 |
55 | expect_true(var_key %in% colnames(rowData(sce)))
56 | expect_equal(
57 | rowData(sce)[[var_key]],
58 | ad$var[[var_key]],
59 | info = paste0("var_key: ", var_key)
60 | )
61 | })
62 | }
63 |
64 | # trackstatus: class=SingleCellExperiment, feature=test_get_layers, status=done
65 | for (layer_key in names(ad$layers)) {
66 | test_that(paste0("as_SCE retains layer: ", layer_key), {
67 | msg <- message_if_known(
68 | backend = "to_SCE",
69 | slot = c("layers"),
70 | dtype = layer_key,
71 | process = "convert",
72 | known_issues = known_issues
73 | )
74 | skip_if(!is.null(msg), message = msg)
75 |
76 | expect_true(layer_key %in% assayNames(sce))
77 | expect_true(
78 | all.equal(
79 | as.matrix(t(assay(sce, layer_key))),
80 | as.matrix(ad$layers[[layer_key]]),
81 | check.attributes = FALSE
82 | ),
83 | info = paste0("layer_key: ", layer_key)
84 | )
85 | })
86 | }
87 |
88 | test_that("as_SCE fails when providing duplicate assay names", {
89 | expect_error(
90 | ad$as_SingleCellExperiment(
91 | x_mapping = "counts",
92 | assays_mapping = c(counts = "numeric_matrix", integer = "integer_matrix")
93 | ),
94 | regexp = "duplicate names"
95 | )
96 | })
97 |
98 | test_that("as_SCE works when only providing x_mapping", {
99 | sce <- ad$as_SingleCellExperiment(x_mapping = "counts")
100 | assay_names <- assayNames(sce)
101 | expect_true("counts" %in% assay_names)
102 | expect_true(all(ad$layers_keys() %in% assay_names))
103 | })
104 |
105 | test_that("as_SCE works with assays_mapping and x_mapping", {
106 | sce <- ad$as_SingleCellExperiment(
107 | x_mapping = "counts",
108 | assays_mapping = c(data = "numeric_matrix", integer = "integer_matrix")
109 | )
110 | assay_names <- assayNames(sce)
111 | expect_true("counts" %in% assay_names)
112 | expect_true("data" %in% assay_names)
113 | expect_true("integer" %in% assay_names)
114 | })
115 |
116 | test_that("as_SCE works with no x_mapping and no layers_mapping", {
117 | sce <- ad$as_SingleCellExperiment()
118 | assay_names <- assayNames(sce)
119 | expect_true("X" %in% assay_names)
120 | expect_true(all(ad$layers_keys() %in% assay_names))
121 | })
122 |
123 | # trackstatus: class=SingleCellExperiment, feature=test_get_obsp, status=done
124 | for (obsp_key in names(ad$obsp)) {
125 | test_that(paste0("as_SCE retains obsp key: ", obsp_key), {
126 | msg <- message_if_known(
127 | backend = "to_SCE",
128 | slot = c("obsp"),
129 | dtype = obsp_key,
130 | process = "convert",
131 | known_issues = known_issues
132 | )
133 | skip_if(!is.null(msg), message = msg)
134 |
135 | expect_true(obsp_key %in% names(colPairs(sce)))
136 |
137 | sce_matrix <- as.matrix(colPair(sce, obsp_key, asSparse = TRUE))
138 | ad_matrix <- as.matrix(ad$obsp[[obsp_key]])
139 |
140 | expect_equal(sce_matrix, ad_matrix, info = paste0("obsp_key: ", obsp_key))
141 | })
142 | }
143 |
144 | # trackstatus: class=SingleCellExperiment, feature=test_get_varp, status=done
145 | for (varp_key in names(ad$varp)) {
146 | test_that(paste0("as_SCE retains varp key: ", varp_key), {
147 | msg <- message_if_known(
148 | backend = "to_SCE",
149 | slot = c("obsp"),
150 | dtype = varp_key,
151 | process = "convert",
152 | known_issues = known_issues
153 | )
154 | skip_if(!is.null(msg), message = msg)
155 |
156 | expect_true(varp_key %in% names(rowPairs(sce)))
157 |
158 | sce_matrix <- as.matrix(rowPair(sce, varp_key, asSparse = TRUE))
159 | ad_matrix <- as.matrix(ad$varp[[varp_key]])
160 |
161 | expect_equal(sce_matrix, ad_matrix, info = paste0("varp_key: ", varp_key))
162 | })
163 | }
164 |
165 | # trackstatus: class=SingleCellExperiment, feature=test_get_uns, status=done
166 | for (uns_key in names(ad$uns)) {
167 | test_that(paste0("as_SCE retains uns key: ", uns_key), {
168 | msg <- message_if_known(
169 | backend = "to_SCE",
170 | slot = c("uns"),
171 | dtype = uns_key,
172 | process = "convert",
173 | known_issues = known_issues
174 | )
175 | skip_if(!is.null(msg), message = msg)
176 |
177 | expect_true(uns_key %in% names(metadata(sce)))
178 | expect_equal(
179 | metadata(sce)[[uns_key]],
180 | ad$uns[[uns_key]],
181 | info = paste0("uns_key: ", uns_key)
182 | )
183 | })
184 | }
185 |
186 | test_that("as_SCE retains pca dimred", {
187 | msg <- message_if_known(
188 | backend = "to_SCE",
189 | slot = c("obsm", "varm"),
190 | dtype = "pca",
191 | process = "convert",
192 | known_issues = known_issues
193 | )
194 | skip_if(!is.null(msg), message = msg)
195 |
196 | # trackstatus: class=SingleCellExperiment, feature=test_get_obsm, status=wip
197 | expect_true("pca" %in% names(reducedDims(sce)))
198 | expect_equal(
199 | sampleFactors(reducedDims(sce)$pca),
200 | ad$obsm[["X_pca"]],
201 | ignore.attributes = TRUE
202 | )
203 |
204 | # trackstatus: class=SingleCellExperiment, feature=test_get_varm, status=wip
205 | expect_equal(
206 | featureLoadings(reducedDims(sce)$pca),
207 | ad$varm[["PCs"]]
208 | )
209 | })
210 |
211 | test_that("as_SCE works with list mappings", {
212 | expect_no_error(
213 | ad$as_SingleCellExperiment(
214 | assays_mapping = as.list(.as_SCE_guess_all(ad, "layers")),
215 | colData_mapping = as.list(.as_SCE_guess_all(ad, "obs")),
216 | rowData_mapping = as.list(.as_SCE_guess_all(ad, "var")),
217 | reducedDims_mapping = as.list(.as_SCE_guess_reducedDims(ad)),
218 | colPairs_mapping = as.list(.as_SCE_guess_all(ad, "obsp")),
219 | rowPairs_mapping = as.list(.as_SCE_guess_all(ad, "varp")),
220 | metadata_mapping = as.list(.as_SCE_guess_all(ad, "uns"))
221 | )
222 | )
223 |
224 | expect_error(
225 | ad$as_SingleCellExperiment(
226 | reducedDims_mapping = list(numeric = "numeric_matrix")
227 | )
228 | )
229 | })
230 |
231 | test_that("as_SCE works with a vector reducedDims_mapping", {
232 | expect_no_error(
233 | ad$as_SingleCellExperiment(
234 | reducedDims_mapping = c(numeric = "numeric_matrix")
235 | )
236 | )
237 | })
238 |
239 | test_that("as_SCE works with unnamed mappings", {
240 | expect_no_error(
241 | ad$as_SingleCellExperiment(
242 | assays_mapping = unname(.as_SCE_guess_all(ad, "layers")),
243 | colData_mapping = unname(.as_SCE_guess_all(ad, "obs")),
244 | rowData_mapping = unname(.as_SCE_guess_all(ad, "var")),
245 | colPairs_mapping = unname(.as_SCE_guess_all(ad, "obsp")),
246 | rowPairs_mapping = unname(.as_SCE_guess_all(ad, "varp")),
247 | metadata_mapping = unname(.as_SCE_guess_all(ad, "uns"))
248 | )
249 | )
250 | })
251 |
252 | test_that("deprecated to_SingleCellExperiment() works", {
253 | expect_warning(sce <- ad$to_SingleCellExperiment())
254 | expect_s4_class(sce, "SingleCellExperiment")
255 | })
256 |
--------------------------------------------------------------------------------
/tests/testthat/test-from_Seurat.R:
--------------------------------------------------------------------------------
1 | skip_if_not_installed("Seurat")
2 | library(Seurat)
3 |
4 | known_issues <- read_known_issues()
5 |
6 | suppressWarnings({
7 | counts <- matrix(rbinom(20000, 1000, .001), nrow = 100)
8 | obj <- CreateSeuratObject(counts = counts)
9 | obj <- NormalizeData(obj)
10 | obj <- FindVariableFeatures(obj)
11 | obj <- ScaleData(obj)
12 | obj <- RunPCA(obj, npcs = 10L)
13 | obj <- FindNeighbors(obj)
14 | obj <- RunUMAP(obj, dims = 1:10)
15 | })
16 |
17 | active_assay <- obj@assays[[obj@active.assay]]
18 |
19 | ad <- as_AnnData(obj)
20 |
21 | test_that("as_AnnData (Seurat) retains number of observations and features", {
22 | expect_equal(ad$n_obs(), 200L)
23 | expect_equal(ad$n_vars(), 100L)
24 |
25 | # trackstatus: class=Seurat, feature=test_set_var_names, status=done
26 | expect_equal(rownames(ad$var), rownames(obj))
27 | # trackstatus: class=Seurat, feature=test_set_obs_names, status=done
28 | expect_equal(rownames(ad$obs), colnames(obj))
29 | })
30 |
31 | # trackstatus: class=Seurat, feature=test_set_obs, status=done
32 | for (obs_key in colnames(obj@meta.data)) {
33 | test_that(paste0("as_AnnData (Seurat) retains obs key: ", obs_key), {
34 | msg <- message_if_known(
35 | backend = "from_Seurat",
36 | slot = c("obs"),
37 | dtype = obs_key,
38 | process = "convert",
39 | known_issues = known_issues
40 | )
41 | skip_if(!is.null(msg), message = msg)
42 |
43 | expect_true(obs_key %in% colnames(ad$obs))
44 | expect_equal(
45 | ad$obs[[obs_key]],
46 | obj@meta.data[[obs_key]],
47 | info = paste0("obs_key: ", obs_key)
48 | )
49 | })
50 | }
51 |
52 | # trackstatus: class=Seurat, feature=test_set_var, status=done
53 | for (var_key in colnames(active_assay@meta.data)) {
54 | test_that(paste0("as_AnnData (Seurat) retains var key: ", var_key), {
55 | msg <- message_if_known(
56 | backend = "from_Seurat",
57 | slot = c("var"),
58 | dtype = var_key,
59 | process = "convert",
60 | known_issues = known_issues
61 | )
62 | skip_if(!is.null(msg), message = msg)
63 |
64 | expect_true(var_key %in% colnames(ad$var))
65 | expect_equal(ad$var[[var_key]], active_assay@meta.data[[var_key]])
66 | })
67 | }
68 |
69 | # trackstatus: class=Seurat, feature=test_set_layers, status=done
70 | for (layer_key in names(active_assay@layers)) {
71 | test_that(paste0("as_AnnData (Seurat) retains layer: ", layer_key), {
72 | msg <- message_if_known(
73 | backend = "from_Seurat",
74 | slot = c("layers"),
75 | dtype = layer_key,
76 | process = "convert",
77 | known_issues = known_issues
78 | )
79 | skip_if(!is.null(msg), message = msg)
80 |
81 | expect_true(layer_key %in% names(ad$layers))
82 | expect_true(
83 | all.equal(
84 | as.matrix(t(active_assay@layers[[layer_key]])),
85 | as.matrix(ad$layers[[layer_key]]),
86 | check.attributes = FALSE
87 | ),
88 | info = paste0("layer_key: ", layer_key)
89 | )
90 | })
91 | }
92 |
93 | # trackstatus: class=Seurat, feature=test_set_uns, status=done
94 | for (uns_key in names(obj@misc)) {
95 | test_that(paste0("as_AnnData (Seurat) retains uns key: ", uns_key), {
96 | msg <- message_if_known(
97 | backend = "from_Seurat",
98 | slot = c("uns"),
99 | dtype = uns_key,
100 | process = "convert",
101 | known_issues = known_issues
102 | )
103 | skip_if(!is.null(msg), message = msg)
104 |
105 | expect_true(uns_key %in% names(ad$uns))
106 | expect_equal(ad$uns[[uns_key]], obj@misc[[uns_key]], ignore_attr = TRUE)
107 | })
108 | }
109 |
110 | test_that("as_AnnData (Seurat) retains pca", {
111 | msg <- message_if_known(
112 | backend = "from_Seurat",
113 | slot = c("obsm"),
114 | dtype = "pca",
115 | process = "convert",
116 | known_issues = known_issues
117 | )
118 | skip_if(!is.null(msg), message = msg)
119 |
120 | # trackstatus: class=Seurat, feature=test_set_obsm, status=wip
121 | expect_equal(
122 | ad$obsm[["pca"]],
123 | Embeddings(obj, reduction = "pca"),
124 | ignore_attr = TRUE
125 | )
126 |
127 | # trackstatus: class=Seurat, feature=test_set_varm, status=wip
128 | expect_equal(
129 | ad$varm[["pca"]],
130 | Loadings(obj, reduction = "pca"),
131 | ignore_attr = TRUE
132 | )
133 | })
134 |
135 | test_that("as_AnnData (Seurat) retains umap", {
136 | msg <- message_if_known(
137 | backend = "from_Seurat",
138 | slot = c("obsm"),
139 | dtype = "umap",
140 | process = "convert",
141 | known_issues = known_issues
142 | )
143 |
144 | skip_if(!is.null(msg), message = msg)
145 |
146 | expect_equal(
147 | ad$obsm[["umap"]],
148 | Embeddings(obj, reduction = "umap"),
149 | ignore_attr = TRUE
150 | )
151 | })
152 |
153 | # trackstatus: class=Seurat, feature=test_set_obsp, status=done
154 | test_that("as_AnnData (Seurat) retains connectivities", {
155 | msg <- message_if_known(
156 | backend = "from_Seurat",
157 | slot = c("graphs"),
158 | dtype = "connectivities",
159 | process = "convert",
160 | known_issues = known_issues
161 | )
162 |
163 | skip_if(!is.null(msg), message = msg)
164 |
165 | expect_equal(
166 | as.matrix(ad$obsp[["nn"]]),
167 | as.matrix(obj@graphs[["RNA_nn"]]),
168 | ignore_attr = TRUE
169 | )
170 | expect_equal(
171 | as.matrix(ad$obsp[["snn"]]),
172 | as.matrix(obj@graphs[["RNA_snn"]]),
173 | ignore_attr = TRUE
174 | )
175 | })
176 |
177 | test_that("as_AnnData (Seurat) works with v3 Assays", {
178 | obj_v3_assay <- obj
179 | expect_warning(
180 | obj_v3_assay[["RNA"]] <- as(Seurat::GetAssay(obj, "RNA"), "Assay")
181 | )
182 |
183 | adata_v3_assay <- as_AnnData(obj_v3_assay)
184 |
185 | expect_identical(
186 | to_R_matrix(adata_v3_assay$layers$counts),
187 | SeuratObject::GetAssayData(obj_v3_assay, layer = "counts")
188 | )
189 | })
190 |
191 | test_that("as_AnnData (Seurat) works with list mappings", {
192 | active_assay <- SeuratObject::DefaultAssay(obj)
193 | expect_no_error(
194 | as_AnnData(
195 | obj,
196 | layers_mapping = as.list(.from_Seurat_guess_layers(obj, active_assay)),
197 | obs_mapping = as.list(.from_Seurat_guess_obs(obj, active_assay)),
198 | var_mapping = as.list(.from_Seurat_guess_var(obj, active_assay)),
199 | obsm_mapping = as.list(.from_Seurat_guess_obsms(obj, active_assay)),
200 | varm_mapping = as.list(.from_Seurat_guess_varms(obj, active_assay)),
201 | obsp_mapping = as.list(.from_Seurat_guess_obsps(obj, active_assay)),
202 | varp_mapping = as.list(.from_Seurat_guess_varps(obj)),
203 | uns_mapping = as.list(.from_Seurat_guess_uns(obj))
204 | )
205 | )
206 | })
207 |
208 | test_that("as_AnnData (Seurat) works with unnamed mappings", {
209 | active_assay <- SeuratObject::DefaultAssay(obj)
210 | expect_no_error(
211 | as_AnnData(
212 | obj,
213 | layers_mapping = unname(.from_Seurat_guess_layers(obj, active_assay)),
214 | obs_mapping = unname(.from_Seurat_guess_obs(obj, active_assay)),
215 | var_mapping = unname(.from_Seurat_guess_var(obj, active_assay)),
216 | obsm_mapping = unname(.from_Seurat_guess_obsms(obj, active_assay)),
217 | varm_mapping = unname(.from_Seurat_guess_varms(obj, active_assay)),
218 | obsp_mapping = unname(.from_Seurat_guess_obsps(obj, active_assay)),
219 | varp_mapping = unname(.from_Seurat_guess_varps(obj)),
220 | uns_mapping = unname(.from_Seurat_guess_uns(obj))
221 | )
222 | )
223 | })
224 |
225 | test_that("as_AnnData (Seurat) works with empty mappings", {
226 | expect_warning(as_AnnData(obj, layers_mapping = NULL), "argument is empty")
227 | expect_warning(as_AnnData(obj, layers_mapping = c()), "argument is empty")
228 | })
229 |
--------------------------------------------------------------------------------
/tests/testthat/test-generate_dataset.R:
--------------------------------------------------------------------------------
1 | test_that("generating dummy data works", {
2 | dataset <- generate_dataset()
3 | expect_type(dataset, "list")
4 | expect_setequal(
5 | names(dataset),
6 | .anndata_slots
7 | )
8 | expect_identical(dim(dataset$X), c(10L, 20L))
9 | })
10 |
11 | test_that("generating dummy SingleCellExperiment works", {
12 | dummy <- generate_dataset(format = "SingleCellExperiment")
13 | expect_s4_class(dummy, "SingleCellExperiment")
14 | })
15 |
16 | suppressPackageStartupMessages(library(SeuratObject))
17 |
18 | test_that("generating dummy Seurat works", {
19 | dummy <- generate_dataset(format = "Seurat")
20 | expect_s4_class(dummy, "Seurat")
21 | })
22 |
23 |
24 | args1 <- formals(generate_dataset)
25 | args2 <- formals(.generate_dataset_as_list)
26 |
27 | # If any of these tests are failing, the arguments of generate_dataset()
28 | # need to be updated to include the new generator types.
29 | for (arg in intersect(names(args1), names(args2))) {
30 | test_that(
31 | paste0(
32 | "generate_dataset(): argument '",
33 | arg,
34 | "' has correct default value"
35 | ),
36 | {
37 | expect_equal(eval(args1[[arg]]), eval(args2[[arg]]))
38 | }
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/tests/testthat/test-h5ad-fileclosure.R:
--------------------------------------------------------------------------------
1 | dummy <- generate_dataset(
2 | n_obs = 10L,
3 | n_vars = 20L,
4 | format = "AnnData",
5 | )
6 |
7 | file <- tempfile(pattern = "h5ad_write_", fileext = ".h5ad")
8 |
9 | test_that("writing H5AD works", {
10 | expect_no_condition({
11 | write_h5ad(dummy, file, mode = "w")
12 | })
13 | })
14 |
15 | test_that("reading H5AD to InMemoryAnnData closes file", {
16 | expect_no_condition({
17 | read_h5ad(file)
18 | write_h5ad(dummy, file, mode = "w")
19 | })
20 | })
21 |
22 | test_that("closing HDF5AnnData file works", {
23 | expect_no_condition({
24 | adata <- read_h5ad(file, as = "HDF5AnnData")
25 | adata$close()
26 | write_h5ad(dummy, file, mode = "w")
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/tests/testthat/test-h5ad-read.R:
--------------------------------------------------------------------------------
1 | file <- system.file("extdata", "example.h5ad", package = "anndataR")
2 |
3 | test_that("reading H5AD as SingleCellExperiment works", {
4 | skip_if_not_installed("SingleCellExperiment")
5 |
6 | sce <- read_h5ad(file, as = "SingleCellExperiment", mode = "r")
7 | expect_s4_class(sce, "SingleCellExperiment")
8 | })
9 |
10 | test_that("reading H5AD as Seurat works", {
11 | skip_if_not_installed("SeuratObject")
12 | # TODO: remove this suppression when the to_seurat, from_seurat functions are updated.
13 | seurat <- suppressWarnings(read_h5ad(file, as = "Seurat"))
14 | expect_s4_class(seurat, "Seurat")
15 | })
16 |
17 | test_that("reading H5AD as InMemoryAnnData works", {
18 | adata <- read_h5ad(file, as = "InMemoryAnnData", mode = "r")
19 | expect_equal(class(adata), c("InMemoryAnnData", "AbstractAnnData", "R6"))
20 | })
21 |
--------------------------------------------------------------------------------
/tests/testthat/test-has_row_names.R:
--------------------------------------------------------------------------------
1 | test_that("has_row_names works on matrix", {
2 | expect_false(has_row_names(matrix(1:26, ncol = 1)))
3 | expect_true(
4 | has_row_names(matrix(1:26, ncol = 1, dimnames = list(letters, NULL)))
5 | )
6 | })
7 |
8 | test_that("has_row_names works on data.frame", {
9 | expect_false(has_row_names(data.frame(x = 1:26)))
10 | expect_true(has_row_names(data.frame(x = 1:26, row.names = letters)))
11 | })
12 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-X.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- names(da$matrix_generators)
17 |
18 | for (name in test_names) {
19 | # first generate a python h5ad
20 | adata_py <- da$generate_dataset(
21 | x_type = name,
22 | obs_types = list(),
23 | var_types = list(),
24 | layer_types = list(),
25 | obsm_types = list(),
26 | varm_types = list(),
27 | obsp_types = list(),
28 | varp_types = list(),
29 | uns_types = list(),
30 | nested_uns_types = list()
31 | )
32 |
33 | # create a couple of paths
34 | file_py <- withr::local_file(
35 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
36 | )
37 | file_r <- withr::local_file(
38 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
39 | )
40 | file_r2 <- withr::local_file(
41 | tempfile(paste0("anndata_r2_", name), fileext = ".h5ad")
42 | )
43 |
44 | # write to file
45 | adata_py$write_h5ad(file_py)
46 |
47 | test_that(paste0("Reading an AnnData with X '", name, "' works"), {
48 | msg <- message_if_known(
49 | backend = "HDF5AnnData",
50 | slot = c("X"),
51 | dtype = name,
52 | process = "read",
53 | known_issues = known_issues
54 | )
55 | skip_if(!is.null(msg), message = msg)
56 |
57 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
58 | expect_equal(
59 | adata_r$shape(),
60 | unlist(reticulate::py_to_r(adata_py$shape))
61 | )
62 |
63 | # check that the print output is the same
64 | str_r <- capture.output(print(adata_r))
65 | str_py <- capture.output(print(adata_py))
66 | expect_equal(str_r, str_py)
67 | })
68 |
69 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
70 | test_that(
71 | paste0("Comparing an anndata with X '", name, "' with reticulate works"),
72 | {
73 | msg <- message_if_known(
74 | backend = "HDF5AnnData",
75 | slot = c("X"),
76 | dtype = name,
77 | process = c("read", "reticulate"),
78 | known_issues = known_issues
79 | )
80 | skip_if(!is.null(msg), message = msg)
81 |
82 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
83 |
84 | expect_equal(
85 | adata_r$X,
86 | py_to_r(adata_py$X),
87 | tolerance = 1e-6
88 | )
89 | }
90 | )
91 |
92 | test_that(paste0("Writing an AnnData with X '", name, "' works"), {
93 | msg <- message_if_known(
94 | backend = "HDF5AnnData",
95 | slot = c("X"),
96 | dtype = name,
97 | process = c("read", "write"),
98 | known_issues = known_issues
99 | )
100 | skip_if(!is.null(msg), message = msg)
101 |
102 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
103 | write_h5ad(adata_r, file_r)
104 |
105 | # read from file
106 | adata_py2 <- ad$read_h5ad(file_r)
107 |
108 | # expect that the objects are the same
109 | expect_equal_py(
110 | adata_py2$X,
111 | adata_py$X
112 | )
113 | })
114 |
115 | skip_if_no_h5diff()
116 | # Get all R datatypes that are equivalent to the python datatype (name)
117 | res <- Filter(function(x) x[[1]] == name, matrix_equivalences)
118 | r_datatypes <- sapply(res, function(x) x[[2]])
119 |
120 | for (r_name in r_datatypes) {
121 | test_msg <- paste0(
122 | "Comparing a python generated .h5ad with X '",
123 | name,
124 | "' with an R generated .h5ad '",
125 | r_name,
126 | "' works"
127 | )
128 | test_that(test_msg, {
129 | msg <- message_if_known(
130 | backend = "HDF5AnnData",
131 | slot = c("X"),
132 | dtype = c(name, r_name),
133 | process = c("h5diff"),
134 | known_issues = known_issues
135 | )
136 | skip_if(!is.null(msg), message = msg)
137 |
138 | # generate an R h5ad
139 | adata_r <- r_generate_dataset(10L, 20L, x_type = list(r_name))
140 | write_h5ad(adata_r, file_r2)
141 |
142 | # run h5diff
143 | res <- processx::run(
144 | "h5diff",
145 | c("-v", file_py, file_r2, "/X"),
146 | error_on_status = FALSE
147 | )
148 |
149 | expect_equal(res$status, 0, info = res$stdout)
150 | })
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-empty.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | bi <- reticulate::import_builtins()
12 |
13 | known_issues <- read_known_issues()
14 |
15 | # first generate a python h5ad
16 | adata_py <- ad$AnnData()
17 |
18 | name <- "empty"
19 |
20 | # create a couple of paths
21 | file_py <- withr::local_file(
22 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
23 | )
24 | file_r <- withr::local_file(
25 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
26 | )
27 |
28 | # write to file
29 | adata_py$write_h5ad(file_py)
30 |
31 | test_that(paste0("Reading an AnnData with layer '", name, "' works"), {
32 | msg <- message_if_known(
33 | backend = "HDF5AnnData",
34 | slot = c("none"),
35 | dtype = name,
36 | process = "read",
37 | known_issues = known_issues
38 | )
39 | skip_if(!is.null(msg), message = msg)
40 |
41 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
42 | expect_equal(
43 | adata_r$shape(),
44 | unlist(reticulate::py_to_r(adata_py$shape))
45 | )
46 |
47 | # check that the print output is the same
48 | str_r <- capture.output(print(adata_r))
49 | str_py <- capture.output(print(adata_py))
50 | expect_equal(str_r, str_py)
51 | })
52 |
53 | test_that(paste0("Writing an AnnData with layer '", name, "' works"), {
54 | msg <- message_if_known(
55 | backend = "HDF5AnnData",
56 | slot = c("none"),
57 | dtype = name,
58 | process = c("read", "write"),
59 | known_issues = known_issues
60 | )
61 | skip_if(!is.null(msg), message = msg)
62 |
63 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
64 | write_h5ad(adata_r, file_r)
65 |
66 | # read from file
67 | adata_py2 <- ad$read_h5ad(file_r)
68 |
69 | # check that the print output is the same
70 | expect_equal(
71 | unlist(reticulate::py_to_r(adata_py2$shape)),
72 | unlist(reticulate::py_to_r(adata_py$shape))
73 | )
74 |
75 | # check that the print output is the same
76 | str_py2 <- capture.output(print(adata_py2))
77 | str_py <- capture.output(print(adata_py))
78 | expect_equal(str_py2, str_py)
79 | })
80 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-layers.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- names(da$matrix_generators)
17 |
18 | for (name in test_names) {
19 | # first generate a python h5ad
20 | adata_py <- da$generate_dataset(
21 | x_type = NULL,
22 | obs_types = list(),
23 | var_types = list(),
24 | layer_types = list(name),
25 | obsm_types = list(),
26 | varm_types = list(),
27 | obsp_types = list(),
28 | varp_types = list(),
29 | uns_types = list(),
30 | nested_uns_types = list()
31 | )
32 |
33 | # create a couple of paths
34 | file_py <- withr::local_file(
35 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
36 | )
37 | file_r <- withr::local_file(
38 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
39 | )
40 | file_r2 <- withr::local_file(
41 | tempfile(paste0("anndata_r2_", name), fileext = ".h5ad")
42 | )
43 |
44 | # write to file
45 | adata_py$write_h5ad(file_py)
46 |
47 | test_that(paste0("Reading an AnnData with layer '", name, "' works"), {
48 | msg <- message_if_known(
49 | backend = "HDF5AnnData",
50 | slot = c("layers"),
51 | dtype = name,
52 | process = "read",
53 | known_issues = known_issues
54 | )
55 | skip_if(!is.null(msg), message = msg)
56 |
57 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
58 | expect_equal(
59 | adata_r$shape(),
60 | unlist(reticulate::py_to_r(adata_py$shape))
61 | )
62 | expect_equal(
63 | adata_r$layers_keys(),
64 | bi$list(adata_py$layers$keys())
65 | )
66 |
67 | # check that the print output is the same
68 | str_r <- capture.output(print(adata_r))
69 | str_py <- capture.output(print(adata_py))
70 | expect_equal(str_r, str_py)
71 | })
72 |
73 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
74 | test_that(
75 | paste0(
76 | "Comparing an anndata with layer '",
77 | name,
78 | "' with reticulate works"
79 | ),
80 | {
81 | msg <- message_if_known(
82 | backend = "HDF5AnnData",
83 | slot = c("layers"),
84 | dtype = name,
85 | process = c("read", "reticulate"),
86 | known_issues = known_issues
87 | )
88 | skip_if(!is.null(msg), message = msg)
89 |
90 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
91 |
92 | expect_equal(
93 | adata_r$layers[[name]],
94 | py_to_r(py_get_item(adata_py$layers, name)),
95 | tolerance = 1e-6
96 | )
97 | }
98 | )
99 |
100 | test_that(paste0("Writing an AnnData with layer '", name, "' works"), {
101 | msg <- message_if_known(
102 | backend = "HDF5AnnData",
103 | slot = c("layers"),
104 | dtype = name,
105 | process = c("read", "write"),
106 | known_issues = known_issues
107 | )
108 | skip_if(!is.null(msg), message = msg)
109 |
110 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
111 | write_h5ad(adata_r, file_r)
112 |
113 | # read from file
114 | adata_py2 <- ad$read_h5ad(file_r)
115 |
116 | # expect name is one of the keys
117 | expect_contains(
118 | bi$list(adata_py2$layers$keys()),
119 | name
120 | )
121 |
122 | # expect that the objects are the same
123 | expect_equal_py(
124 | py_get_item(adata_py2$layers, name),
125 | py_get_item(adata_py$layers, name)
126 | )
127 | })
128 |
129 | skip_if_no_h5diff()
130 | # Get all R datatypes that are equivalent to the python datatype (name)
131 | res <- Filter(function(x) x[[1]] == name, matrix_equivalences)
132 | r_datatypes <- sapply(res, function(x) x[[2]])
133 |
134 | for (r_name in r_datatypes) {
135 | test_msg <- paste0(
136 | "Comparing a python generated .h5ad with layer '",
137 | name,
138 | "' with an R generated .h5ad '",
139 | r_name,
140 | "' works"
141 | )
142 | test_that(test_msg, {
143 | msg <- message_if_known(
144 | backend = "HDF5AnnData",
145 | slot = c("X"),
146 | dtype = c(name, r_name),
147 | process = c("h5diff"),
148 | known_issues = known_issues
149 | )
150 |
151 | skip_if(!is.null(msg), message = msg)
152 |
153 | # generate an R h5ad
154 | adata_r <- r_generate_dataset(10L, 20L, layer_types = list(r_name))
155 | write_h5ad(adata_r, file_r2)
156 |
157 | # run h5diff
158 | res <- processx::run(
159 | "h5diff",
160 | c(
161 | "-v",
162 | file_py,
163 | file_r2,
164 | paste0("/layers/", name),
165 | paste0("/layers/", r_name)
166 | ),
167 | error_on_status = FALSE
168 | )
169 |
170 | expect_equal(res$status, 0, info = res$stdout)
171 | })
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-obsmvarm.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- c(
17 | names(da$matrix_generators),
18 | names(da$vector_generators)
19 | )
20 |
21 | # temporary workaround for
22 | # https://github.com/data-intuitive/dummy-anndata/issues/12
23 | test_names <- setdiff(
24 | test_names,
25 | c(
26 | "categorical",
27 | "categorical_missing_values",
28 | "categorical_ordered",
29 | "categorical_ordered_missing_values",
30 | "nullable_boolean_array",
31 | "nullable_integer_array"
32 | )
33 | )
34 |
35 | for (name in test_names) {
36 | # first generate a python h5ad
37 | adata_py <- da$generate_dataset(
38 | x_type = NULL,
39 | obs_types = list(),
40 | var_types = list(),
41 | layer_types = list(),
42 | obsm_types = list(name),
43 | varm_types = list(name),
44 | obsp_types = list(),
45 | varp_types = list(),
46 | uns_types = list(),
47 | nested_uns_types = list()
48 | )
49 |
50 | # create a couple of paths
51 | file_py <- withr::local_file(
52 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
53 | )
54 | file_r <- withr::local_file(
55 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
56 | )
57 | file_r2 <- withr::local_file(
58 | tempfile(paste0("anndata_r2_", name), fileext = ".h5ad")
59 | )
60 |
61 | # write to file
62 | adata_py$write_h5ad(file_py)
63 |
64 | test_that(
65 | paste0("Reading an AnnData with obsm and varm '", name, "' works"),
66 | {
67 | msg <- message_if_known(
68 | backend = "HDF5AnnData",
69 | slot = c("obsm", "varm"),
70 | dtype = name,
71 | process = "read",
72 | known_issues = known_issues
73 | )
74 | skip_if(!is.null(msg), message = msg)
75 |
76 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
77 | expect_equal(
78 | adata_r$shape(),
79 | unlist(reticulate::py_to_r(adata_py$shape))
80 | )
81 | expect_equal(
82 | adata_r$obsm_keys(),
83 | bi$list(adata_py$obsm$keys())
84 | )
85 | expect_equal(
86 | adata_r$varm_keys(),
87 | bi$list(adata_py$varm$keys())
88 | )
89 |
90 | # check that the print output is the same
91 | str_r <- capture.output(print(adata_r))
92 | str_py <- capture.output(print(adata_py))
93 | expect_equal(str_r, str_py)
94 | }
95 | )
96 |
97 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
98 | test_that(
99 | paste0(
100 | "Comparing an anndata with obsm and varm '",
101 | name,
102 | "' with reticulate works"
103 | ),
104 | {
105 | msg <- message_if_known(
106 | backend = "HDF5AnnData",
107 | slot = c("obsm", "varm"),
108 | dtype = name,
109 | process = c("read", "reticulate"),
110 | known_issues = known_issues
111 | )
112 | skip_if(!is.null(msg), message = msg)
113 |
114 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
115 |
116 | expect_equal(
117 | adata_r$obsm[[name]],
118 | py_to_r(py_get_item(adata_py$obsm, name)),
119 | tolerance = 1e-6
120 | )
121 | expect_equal(
122 | adata_r$varm[[name]],
123 | py_to_r(py_get_item(adata_py$varm, name)),
124 | tolerance = 1e-6
125 | )
126 | }
127 | )
128 |
129 | test_that(
130 | paste0("Writing an AnnData with obsm and varm '", name, "' works"),
131 | {
132 | msg <- message_if_known(
133 | backend = "HDF5AnnData",
134 | slot = c("obsm", "varm"),
135 | dtype = name,
136 | process = c("read", "write"),
137 | known_issues = known_issues
138 | )
139 | skip_if(!is.null(msg), message = msg)
140 |
141 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
142 | write_h5ad(adata_r, file_r)
143 |
144 | # read from file
145 | adata_py2 <- ad$read_h5ad(file_r)
146 |
147 | # expect name is one of the keys
148 | expect_contains(
149 | bi$list(adata_py2$obsm$keys()),
150 | name
151 | )
152 | expect_contains(
153 | bi$list(adata_py2$obsm$keys()),
154 | name
155 | )
156 |
157 | # expect that the objects are the same
158 | expect_equal_py(
159 | py_get_item(adata_py2$obsm, name),
160 | py_get_item(adata_py$obsm, name)
161 | )
162 | expect_equal_py(
163 | py_get_item(adata_py2$varm, name),
164 | py_get_item(adata_py$varm, name)
165 | )
166 | }
167 | )
168 |
169 | skip_if_no_h5diff()
170 | # Get all R datatypes that are equivalent to the python datatype (name)
171 | res <- Filter(function(x) x[[1]] == name, all_equivalences)
172 | r_datatypes <- sapply(res, function(x) x[[2]])
173 |
174 | for (r_name in r_datatypes) {
175 | test_msg <- paste0(
176 | "Comparing a python generated .h5ad with obsm and varm '",
177 | name,
178 | "' with an R generated .h5ad '",
179 | r_name,
180 | "' works"
181 | )
182 | test_that(test_msg, {
183 | msg <- message_if_known(
184 | backend = "HDF5AnnData",
185 | slot = c("obsm", "varm"),
186 | dtype = c(name, r_name),
187 | process = c("h5diff"),
188 | known_issues = known_issues
189 | )
190 |
191 | skip_if(!is.null(msg), message = msg)
192 | # generate an R h5ad
193 | adata_r <- r_generate_dataset(
194 | 10L,
195 | 20L,
196 | obsm_types = list(r_name),
197 | varm_types = list(r_name)
198 | )
199 | write_h5ad(adata_r, file_r2)
200 |
201 | # run h5diff
202 | res_obsm <- processx::run(
203 | "h5diff",
204 | c(
205 | "-v",
206 | file_py,
207 | file_r2,
208 | paste0("/obsm/", name),
209 | paste0("/obsm/", r_name)
210 | ),
211 | error_on_status = FALSE
212 | )
213 | expect_equal(res_obsm$status, 0, info = res_obsm$stdout)
214 |
215 | res_varm <- processx::run(
216 | "h5diff",
217 | c(
218 | "-v",
219 | file_py,
220 | file_r2,
221 | paste0("/varm/", name),
222 | paste0("/varm/", r_name)
223 | ),
224 | error_on_status = FALSE
225 | )
226 | expect_equal(res_varm$status, 0, info = res_varm$stdout)
227 | })
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-obspvarp.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- names(da$matrix_generators)
17 |
18 | for (name in test_names) {
19 | # first generate a python h5ad
20 | adata_py <- da$generate_dataset(
21 | x_type = NULL,
22 | obs_types = list(),
23 | var_types = list(),
24 | layer_types = list(),
25 | obsm_types = list(),
26 | varm_types = list(),
27 | obsp_types = list(name),
28 | varp_types = list(name),
29 | uns_types = list(),
30 | nested_uns_types = list()
31 | )
32 |
33 | # create a couple of paths
34 | file_py <- withr::local_file(
35 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
36 | )
37 | file_r <- withr::local_file(
38 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
39 | )
40 | file_r2 <- withr::local_file(
41 | tempfile(paste0("anndata_r2_", name), fileext = ".h5ad")
42 | )
43 |
44 | # write to file
45 | adata_py$write_h5ad(file_py)
46 |
47 | test_that(
48 | paste0("Reading an AnnData with obsp and varp '", name, "' works"),
49 | {
50 | msg <- message_if_known(
51 | backend = "HDF5AnnData",
52 | slot = c("obsp", "varp"),
53 | dtype = name,
54 | process = "read",
55 | known_issues = known_issues
56 | )
57 | skip_if(!is.null(msg), message = msg)
58 |
59 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
60 | expect_equal(
61 | adata_r$shape(),
62 | unlist(reticulate::py_to_r(adata_py$shape))
63 | )
64 | expect_equal(
65 | adata_r$obsp_keys(),
66 | bi$list(adata_py$obsp$keys())
67 | )
68 | expect_equal(
69 | adata_r$varp_keys(),
70 | bi$list(adata_py$varp$keys())
71 | )
72 |
73 | # check that the print output is the same
74 | str_r <- capture.output(print(adata_r))
75 | str_py <- capture.output(print(adata_py))
76 | expect_equal(str_r, str_py)
77 | }
78 | )
79 |
80 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
81 | test_that(
82 | paste0(
83 | "Comparing an anndata with obsp and varp '",
84 | name,
85 | "' with reticulate works"
86 | ),
87 | {
88 | msg <- message_if_known(
89 | backend = "HDF5AnnData",
90 | slot = c("obsp", "varp"),
91 | dtype = name,
92 | process = c("read", "reticulate"),
93 | known_issues = known_issues
94 | )
95 | skip_if(!is.null(msg), message = msg)
96 |
97 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
98 |
99 | expect_equal(
100 | adata_r$obsp[[name]],
101 | py_to_r(py_get_item(adata_py$obsp, name)),
102 | tolerance = 1e-6
103 | )
104 | expect_equal(
105 | adata_r$varp[[name]],
106 | py_to_r(py_get_item(adata_py$varp, name)),
107 | tolerance = 1e-6
108 | )
109 | }
110 | )
111 |
112 | test_that(
113 | paste0("Writing an AnnData with obsp and varp '", name, "' works"),
114 | {
115 | msg <- message_if_known(
116 | backend = "HDF5AnnData",
117 | slot = c("obsp", "varp"),
118 | dtype = name,
119 | process = c("read", "write"),
120 | known_issues = known_issues
121 | )
122 | skip_if(!is.null(msg), message = msg)
123 |
124 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
125 | write_h5ad(adata_r, file_r)
126 |
127 | # read from file
128 | adata_py2 <- ad$read_h5ad(file_r)
129 |
130 | # expect name is one of the keys
131 | expect_contains(
132 | bi$list(adata_py2$obsp$keys()),
133 | name
134 | )
135 | expect_contains(
136 | bi$list(adata_py2$varp$keys()),
137 | name
138 | )
139 |
140 | # expect that the objects are the same
141 | expect_equal_py(
142 | py_get_item(adata_py2$obsp, name),
143 | py_get_item(adata_py$obsp, name)
144 | )
145 | expect_equal_py(
146 | py_get_item(adata_py2$varp, name),
147 | py_get_item(adata_py$varp, name)
148 | )
149 | }
150 | )
151 |
152 | skip_if_no_h5diff()
153 | # Get all R datatypes that are equivalent to the python datatype (name)
154 | res <- Filter(function(x) x[[1]] == name, matrix_equivalences)
155 | r_datatypes <- sapply(res, function(x) x[[2]])
156 |
157 | for (r_name in r_datatypes) {
158 | test_msg <- paste0(
159 | "Comparing a python generated .h5ad with obsp and varp '",
160 | name,
161 | "' with an R generated .h5ad '",
162 | r_name,
163 | "' works"
164 | )
165 | test_that(test_msg, {
166 | msg <- message_if_known(
167 | backend = "HDF5AnnData",
168 | slot = c("obsp", "varp"),
169 | dtype = c(name, r_name),
170 | process = c("h5diff"),
171 | known_issues = known_issues
172 | )
173 | skip_if(!is.null(msg), message = msg)
174 | # generate an R h5ad
175 | adata_r <- r_generate_dataset(
176 | 10L,
177 | 20L,
178 | obsp_types = list(r_name),
179 | varp_types = list(r_name)
180 | )
181 | write_h5ad(adata_r, file_r2)
182 |
183 | # run h5diff
184 | res_obsp <- processx::run(
185 | "h5diff",
186 | c(
187 | "-v",
188 | file_py,
189 | file_r2,
190 | paste0("/obsp/", name),
191 | paste0("/obsp/", r_name)
192 | ),
193 | error_on_status = FALSE
194 | )
195 | expect_equal(res_obsp$status, 0, info = res_obsp$stdout)
196 |
197 | res_varp <- processx::run(
198 | "h5diff",
199 | c(
200 | "-v",
201 | file_py,
202 | file_r2,
203 | paste0("/varp/", name),
204 | paste0("/varp/", r_name)
205 | ),
206 | error_on_status = FALSE
207 | )
208 | expect_equal(res_varp$status, 0, info = res_varp$stdout)
209 | })
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-obsvar.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- names(da$vector_generators)
17 |
18 | for (name in test_names) {
19 | # first generate a python h5ad
20 | adata_py <- da$generate_dataset(
21 | x_type = NULL,
22 | obs_types = list(name),
23 | var_types = list(name),
24 | layer_types = list(),
25 | obsm_types = list(),
26 | varm_types = list(),
27 | obsp_types = list(),
28 | varp_types = list(),
29 | uns_types = list(),
30 | nested_uns_types = list()
31 | )
32 |
33 | # create a couple of paths
34 | file_py <- withr::local_file(
35 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
36 | )
37 | file_r <- withr::local_file(
38 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
39 | )
40 | file_r2 <- withr::local_file(
41 | tempfile(paste0("anndata_r2_", name), fileext = ".h5ad")
42 | )
43 |
44 | # write to file
45 | adata_py$write_h5ad(file_py)
46 |
47 | test_that(paste0("reading an AnnData with obs and var '", name, "' works"), {
48 | msg <- message_if_known(
49 | backend = "HDF5AnnData",
50 | slot = c("obs", "var"),
51 | dtype = name,
52 | process = "read",
53 | known_issues = known_issues
54 | )
55 | skip_if(!is.null(msg), message = msg)
56 |
57 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
58 | expect_equal(
59 | adata_r$shape(),
60 | unlist(reticulate::py_to_r(adata_py$shape))
61 | )
62 | expect_equal(
63 | adata_r$obs_keys(),
64 | bi$list(adata_py$obs_keys())
65 | )
66 | expect_equal(
67 | adata_r$var_keys(),
68 | bi$list(adata_py$var_keys())
69 | )
70 |
71 | # check that the print output is the same
72 | str_r <- capture.output(print(adata_r))
73 | str_py <- capture.output(print(adata_py))
74 | expect_equal(str_r, str_py)
75 | })
76 |
77 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
78 | test_that(
79 | paste0(
80 | "Comparing an anndata with obs and var '",
81 | name,
82 | "' with reticulate works"
83 | ),
84 | {
85 | msg <- message_if_known(
86 | backend = "HDF5AnnData",
87 | slot = c("obs", "var"),
88 | dtype = name,
89 | process = c("read", "reticulate"),
90 | known_issues = known_issues
91 | )
92 | skip_if(!is.null(msg), message = msg)
93 |
94 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
95 |
96 | expect_equal(
97 | adata_r$obs[[name]],
98 | py_to_r(adata_py$obs)[[name]],
99 | tolerance = 1e-6
100 | )
101 | expect_equal(
102 | adata_r$var[[name]],
103 | py_to_r(adata_py$var)[[name]],
104 | tolerance = 1e-6
105 | )
106 | }
107 | )
108 |
109 | test_that(paste0("Writing an AnnData with obs and var '", name, "' works"), {
110 | msg <- message_if_known(
111 | backend = "HDF5AnnData",
112 | slot = c("obsp", "varp"),
113 | dtype = name,
114 | process = c("read", "write"),
115 | known_issues = known_issues
116 | )
117 | skip_if(!is.null(msg), message = msg)
118 |
119 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
120 | write_h5ad(adata_r, file_r)
121 |
122 | # read from file
123 | adata_py2 <- ad$read_h5ad(file_r)
124 |
125 | # expect name is one of the keys
126 | expect_contains(
127 | bi$list(adata_py2$obs$keys()),
128 | name
129 | )
130 | expect_contains(
131 | bi$list(adata_py2$var$keys()),
132 | name
133 | )
134 |
135 | # expect that the objects are the same
136 | expect_equal_py(adata_py2$obs, adata_py$obs)
137 | expect_equal_py(adata_py2$var, adata_py$var)
138 | })
139 |
140 | skip_if_no_h5diff()
141 | # Get all R datatypes that are equivalent to the python datatype (name)
142 | res <- Filter(function(x) x[[1]] == name, vector_equivalences)
143 | r_datatypes <- sapply(res, function(x) x[[2]])
144 |
145 | for (r_name in r_datatypes) {
146 | test_msg <- paste0(
147 | "Comparing a python generated .h5ad with obs and var '",
148 | name,
149 | "' with an R generated .h5ad '",
150 | r_name,
151 | "' works"
152 | )
153 | test_that(test_msg, {
154 | msg <- message_if_known(
155 | backend = "HDF5AnnData",
156 | slot = c("obs", "var"),
157 | dtype = c(name, r_name),
158 | process = c("h5diff"),
159 | known_issues = known_issues
160 | )
161 | skip_if(!is.null(msg), message = msg)
162 | # generate an R h5ad
163 | adata_r <- r_generate_dataset(
164 | 10L,
165 | 20L,
166 | obs_types = list(r_name),
167 | var_types = list(r_name)
168 | )
169 | write_h5ad(adata_r, file_r2)
170 |
171 | # run h5diff
172 | res_obs <- processx::run(
173 | "h5diff",
174 | c(
175 | "-v",
176 | file_py,
177 | file_r2,
178 | paste0("/obs/", name),
179 | paste0("/obs/", r_name)
180 | ),
181 | error_on_status = FALSE
182 | )
183 | expect_equal(res_obs$status, 0, info = res_obs$stdout)
184 |
185 | res_var <- processx::run(
186 | "h5diff",
187 | c(
188 | "-v",
189 | file_py,
190 | file_r2,
191 | paste0("/var/", name),
192 | paste0("/var/", r_name)
193 | ),
194 | error_on_status = FALSE
195 | )
196 | expect_equal(res_var$status, 0, info = res_var$stdout)
197 | })
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-uns-nested.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- c(
17 | names(da$matrix_generators),
18 | names(da$vector_generators),
19 | names(da$scalar_generators)
20 | )
21 |
22 | for (name in test_names) {
23 | # first generate a python h5ad
24 | adata_py <- da$generate_dataset(
25 | x_type = NULL,
26 | obs_types = list(),
27 | var_types = list(),
28 | layer_types = list(),
29 | obsm_types = list(),
30 | varm_types = list(),
31 | obsp_types = list(),
32 | varp_types = list(),
33 | uns_types = list(),
34 | nested_uns_types = list(name)
35 | )
36 |
37 | # create a couple of paths
38 | file_py <- withr::local_file(
39 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
40 | )
41 | file_r <- withr::local_file(
42 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
43 | )
44 |
45 | # write to file
46 | adata_py$write_h5ad(file_py)
47 |
48 | test_that(paste0("Reading an AnnData with uns_nested '", name, "' works"), {
49 | msg <- message_if_known(
50 | backend = "HDF5AnnData",
51 | slot = c("uns_nested"),
52 | dtype = name,
53 | process = "read",
54 | known_issues = known_issues
55 | )
56 | skip_if(!is.null(msg), message = msg)
57 |
58 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
59 | expect_equal(
60 | names(adata_r$uns$nested),
61 | bi$list(adata_py$uns$nested$keys())
62 | )
63 |
64 | # check that the print output is the same
65 | str_r <- capture.output(print(adata_r))
66 | str_py <- capture.output(print(adata_py))
67 | expect_equal(str_r, str_py)
68 | })
69 |
70 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
71 | test_that(
72 | paste0(
73 | "Comparing an anndata with uns_nested '",
74 | name,
75 | "' with reticulate works"
76 | ),
77 | {
78 | msg <- message_if_known(
79 | backend = "HDF5AnnData",
80 | slot = c("uns_nested"),
81 | dtype = name,
82 | process = c("read", "reticulate"),
83 | known_issues = known_issues
84 | )
85 | skip_if(!is.null(msg), message = msg)
86 |
87 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
88 |
89 | expect_equal(
90 | adata_r$uns[["nested"]][[name]],
91 | reticulate::py_to_r(adata_py$uns$nested[[name]])
92 | )
93 | }
94 | )
95 |
96 | test_that(paste0("Writing an AnnData with uns_nested '", name, "' works"), {
97 | msg <- message_if_known(
98 | backend = "HDF5AnnData",
99 | slot = c("uns_nested"),
100 | dtype = name,
101 | process = c("read", "write"),
102 | known_issues = known_issues
103 | )
104 | skip_if(!is.null(msg), message = msg)
105 |
106 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
107 | write_h5ad(adata_r, file_r)
108 |
109 | # read from file
110 | adata_py2 <- ad$read_h5ad(file_r)
111 |
112 | # expect name is one of the keys
113 | expect_contains(
114 | bi$list(adata_py2$uns$nested$keys()),
115 | name
116 | )
117 |
118 | # expect that the objects are the same
119 | expect_equal_py(
120 | py_get_item(adata_py2$uns$nested, name),
121 | py_get_item(adata_py$uns$nested, name)
122 | )
123 | })
124 | }
125 |
--------------------------------------------------------------------------------
/tests/testthat/test-roundtrip-uns.R:
--------------------------------------------------------------------------------
1 | skip_if_no_anndata()
2 | skip_if_not_installed("reticulate")
3 |
4 | library(reticulate)
5 | testthat::skip_if_not(
6 | reticulate::py_module_available("dummy_anndata"),
7 | message = "Python dummy_anndata module not available for testing"
8 | )
9 |
10 | ad <- reticulate::import("anndata", convert = FALSE)
11 | da <- reticulate::import("dummy_anndata", convert = FALSE)
12 | bi <- reticulate::import_builtins()
13 |
14 | known_issues <- read_known_issues()
15 |
16 | test_names <- c(
17 | names(da$matrix_generators),
18 | names(da$vector_generators),
19 | names(da$scalar_generators)
20 | )
21 |
22 | for (name in test_names) {
23 | # first generate a python h5ad
24 | adata_py <- da$generate_dataset(
25 | x_type = NULL,
26 | obs_types = list(),
27 | var_types = list(),
28 | layer_types = list(),
29 | obsm_types = list(),
30 | varm_types = list(),
31 | obsp_types = list(),
32 | varp_types = list(),
33 | uns_types = list(name),
34 | nested_uns_types = list()
35 | )
36 |
37 | # create a couple of paths
38 | file_py <- withr::local_file(
39 | tempfile(paste0("anndata_py_", name), fileext = ".h5ad")
40 | )
41 | file_r <- withr::local_file(
42 | tempfile(paste0("anndata_r_", name), fileext = ".h5ad")
43 | )
44 |
45 | # write to file
46 | adata_py$write_h5ad(file_py)
47 |
48 | test_that(paste0("Reading an AnnData with uns '", name, "' works"), {
49 | msg <- message_if_known(
50 | backend = "HDF5AnnData",
51 | slot = c("uns"),
52 | dtype = name,
53 | process = "read",
54 | known_issues = known_issues
55 | )
56 | skip_if(!is.null(msg), message = msg)
57 |
58 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
59 | expect_equal(
60 | names(adata_r$uns),
61 | bi$list(adata_py$uns$keys())
62 | )
63 |
64 | # check that the print output is the same
65 | str_r <- capture.output(print(adata_r))
66 | str_py <- capture.output(print(adata_py))
67 | expect_equal(str_r, str_py)
68 | })
69 |
70 | # maybe this test simply shouldn't be run if there is a known issue with reticulate
71 | test_that(
72 | paste0("Comparing an anndata with uns '", name, "' with reticulate works"),
73 | {
74 | msg <- message_if_known(
75 | backend = "HDF5AnnData",
76 | slot = c("uns"),
77 | dtype = name,
78 | process = c("read", "reticulate"),
79 | known_issues = known_issues
80 | )
81 | skip_if(!is.null(msg), message = msg)
82 |
83 | adata_r <- read_h5ad(file_py, as = "HDF5AnnData")
84 |
85 | expect_equal(
86 | adata_r$uns[[name]],
87 | reticulate::py_to_r(adata_py$uns[[name]])
88 | )
89 | }
90 | )
91 |
92 | test_that(paste0("Writing an AnnData with uns '", name, "' works"), {
93 | msg <- message_if_known(
94 | backend = "HDF5AnnData",
95 | slot = c("uns"),
96 | dtype = name,
97 | process = c("read", "write"),
98 | known_issues = known_issues
99 | )
100 | skip_if(!is.null(msg), message = msg)
101 |
102 | adata_r <- read_h5ad(file_py, as = "InMemoryAnnData")
103 | write_h5ad(adata_r, file_r)
104 |
105 | # read from file
106 | adata_py2 <- ad$read_h5ad(file_r)
107 |
108 | # expect name is one of the keys
109 | expect_contains(
110 | bi$list(adata_py2$uns$keys()),
111 | name
112 | )
113 |
114 | # expect that the objects are the same
115 | expect_equal_py(
116 | py_get_item(adata_py2$uns, name),
117 | py_get_item(adata_py$uns, name)
118 | )
119 | })
120 | }
121 |
--------------------------------------------------------------------------------
/tests/testthat/test-utils.R:
--------------------------------------------------------------------------------
1 | col_matrix <- Matrix::rsparsematrix(nrow = 10, ncol = 20, density = 0.1)
2 | row_matrix <- as(col_matrix, "RsparseMatrix")
3 |
4 | # we expect to receive a transposed dgRMatrix from to_py_matrix
5 | test_that("to_py_matrix works with dgCMatrix", {
6 | result <- to_py_matrix(col_matrix)
7 | expect_s4_class(result, "dgRMatrix")
8 | expect_equal(result, Matrix::t(row_matrix))
9 | })
10 |
11 | test_that("to_py_matrix works with dgRMatrix", {
12 | result <- to_py_matrix(row_matrix)
13 | expect_s4_class(result, "dgRMatrix")
14 | expect_equal(result, Matrix::t(row_matrix))
15 | })
16 |
17 | # we expect to receive a transposed dgCMatrix from to_R_matrix
18 | test_that("to_R_matrix works with dgRMatrix", {
19 | result <- to_R_matrix(row_matrix)
20 | expect_s4_class(result, "dgCMatrix")
21 | expect_equal(result, Matrix::t(col_matrix))
22 | })
23 |
24 | test_that("to_R_matrix works with dgCMatrix", {
25 | result <- to_R_matrix(col_matrix)
26 | expect_s4_class(result, "dgCMatrix")
27 | expect_equal(result, Matrix::t(col_matrix))
28 | })
29 |
--------------------------------------------------------------------------------
/vignettes/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.R
3 |
--------------------------------------------------------------------------------
/vignettes/anndataR.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Using anndataR"
3 | author:
4 | - name: Robrecht Cannoodt
5 | - name: Luke Zappia
6 | - name: Martin Morgan
7 | - name: Louise Deconinck
8 | package: anndataR
9 | output: BiocStyle::html_document
10 | vignette: >
11 | %\VignetteIndexEntry{Using anndataR to read and convert}
12 | %\VignetteEngine{knitr::rmarkdown}
13 | %\VignetteEncoding{UTF-8}
14 | ---
15 |
16 | ```{r, include = FALSE}
17 | knitr::opts_chunk$set(
18 | collapse = TRUE,
19 | comment = "#>"
20 | )
21 |
22 | # already load SeuratObject and SingleCellExperiment
23 | # so warnings generated by these packages are not shown
24 | library(SeuratObject)
25 | library(SingleCellExperiment)
26 | ```
27 |
28 | **{anndataR}** allows users to work with `.h5ad` files, access various slots in the datasets and convert these files to `SingleCellExperiment` objects and `SeuratObject`s, and vice versa.
29 |
30 | Check out `?anndataR` for a full list of the functions provided by this package.
31 |
32 | ## Installation
33 |
34 | Install using:
35 | ```{r, eval = FALSE}
36 | if (!require("pak", quietly = TRUE)) {
37 | install.packages("pak")
38 | }
39 | pak::pak("scverse/anndataR")
40 | ```
41 |
42 | ## Usage
43 |
44 | Here's a quick example of how to use **{anndataR}**.
45 |
46 | First, we fetch an example `.h5ad` file included in the package:
47 |
48 | ```{r}
49 | library(anndataR)
50 |
51 | h5ad_path <- system.file("extdata", "example.h5ad", package = "anndataR")
52 | ```
53 |
54 | Read an h5ad file in memory:
55 |
56 | ```{r}
57 | adata <- read_h5ad(h5ad_path)
58 | ```
59 |
60 | Read an h5ad file on disk:
61 |
62 | ```{r}
63 | adata <- read_h5ad(h5ad_path, as = "HDF5AnnData")
64 | ```
65 |
66 | View structure:
67 |
68 | ```{r}
69 | adata
70 | ```
71 |
72 | Access AnnData slots:
73 |
74 | ```{r}
75 | dim(adata$X)
76 | adata$obs[1:5, 1:6]
77 | adata$var[1:5, 1:6]
78 | ```
79 |
80 | ## Interoperability
81 |
82 | Convert the AnnData object to a SingleCellExperiment object:
83 |
84 | ```{r}
85 | sce <- adata$as_SingleCellExperiment()
86 | sce
87 | ```
88 |
89 | Convert the AnnData object to a Seurat object:
90 |
91 | ```{r as-Seurat}
92 | obj <- adata$as_Seurat()
93 | obj
94 | ```
95 |
96 | ## Manually create an object
97 |
98 | ```{r}
99 | adata <- AnnData(
100 | X = matrix(rnorm(100), nrow = 10),
101 | obs = data.frame(
102 | cell_type = factor(rep(c("A", "B"), each = 5))
103 | ),
104 | var = data.frame(
105 | gene_name = paste0("gene_", 1:10)
106 | )
107 | )
108 |
109 | adata
110 | ```
111 |
112 | ## Session info
113 |
114 | ```{r}
115 | sessionInfo()
116 | ```
117 |
--------------------------------------------------------------------------------
/vignettes/development_status.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Development status
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Development status}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | The following tables show the status of the implementation of each feature in the package:
11 |
12 | ```{r include=FALSE}
13 | library(tibble)
14 | library(knitr)
15 | library(rprojroot)
16 | library(stringr)
17 | library(purrr)
18 | library(dplyr)
19 | library(tidyr)
20 |
21 | proj_root <- find_root(has_file("DESCRIPTION"))
22 | source <- list.files(proj_root, pattern = "*.R$", recursive = TRUE)
23 |
24 | # look for trackstatus comments
25 | status_lines <- map_df(source, function(path) {
26 | lines <- readLines(paste0(proj_root, "/", path))
27 | line_numbers <- grep("# trackstatus:", lines)
28 |
29 | map_df(line_numbers, function(line_number) {
30 | tryCatch(
31 | {
32 | line_stripped <- gsub(" *# trackstatus: *", "", lines[[line_number]])
33 | line_split <- str_split_1(line_stripped, ", *")
34 | vals_split <- str_split(line_split, " *= *")
35 | names <- sapply(vals_split, function(x) x[[1]])
36 | values <- lapply(vals_split, function(x) x[[2]])
37 | df <- data.frame(setNames(values, names), check.names = FALSE)
38 | df$source_file <- path
39 | df$line_number <- line_number
40 | df
41 | },
42 | error = function(e) {
43 | message("Error in ", path, " at line ", line_number, ": ", e$message)
44 | NULL
45 | }
46 | )
47 | })
48 | })
49 |
50 | # split feature id into prefix and slot columns
51 | strip_prefix <- c(
52 | "get_" = "Getter",
53 | "test_get_" = "Getter test",
54 | "set_" = "Setter",
55 | "test_set_" = "Setter test"
56 | )
57 | status_lines_proc <- status_lines %>%
58 | mutate(
59 | prefix = str_extract(feature, "^(test_)?[gs]et_"),
60 | slot = str_replace(feature, "^(test_)?[gs]et_", "")
61 | ) |>
62 | select(-feature)
63 |
64 | # combine with missing fields
65 | status_lines_required <- crossing(
66 | class = c("InMemoryAnnData", "HDF5AnnData", "Seurat", "SingleCellExperiment"),
67 | prefix = c("get_", "test_get_", "set_", "test_set_"),
68 | slot = c(
69 | "X",
70 | "layers",
71 | "obs",
72 | "var",
73 | "obs_names",
74 | "var_names",
75 | "obsm",
76 | "varm",
77 | "obsp",
78 | "varp",
79 | "uns",
80 | "raw"
81 | ),
82 | status = "missing"
83 | )
84 | status_required <- bind_rows(
85 | status_lines_proc,
86 | anti_join(
87 | status_lines_required,
88 | status_lines_proc,
89 | by = c("class", "prefix", "slot")
90 | )
91 | )
92 |
93 | # Fill in non-required slots
94 | status <- status_required |>
95 | group_by(class) |>
96 | complete(prefix, slot, fill = list(status = "missing")) |>
97 | ungroup()
98 |
99 | # check duplicated status
100 | status_dup <- status |>
101 | group_by(class, prefix, slot) |>
102 | filter(n() > 1) |>
103 | ungroup() |>
104 | mutate(
105 | str = paste0(
106 | source_file, "#", line_number, " -- ", class, " ", prefix, slot
107 | )
108 | )
109 | if (nrow(status_dup) > 0) {
110 | stop(
111 | "Duplicated status lines found:\n", paste(status_dup$str, collapse = "\n")
112 | )
113 | }
114 |
115 | # create formatted data frame for printing
116 | status_formatted <- status |>
117 | mutate(
118 | prefix_formatted = strip_prefix[prefix],
119 | status_formatted = ifelse(
120 | status == "missing",
121 | "",
122 | paste0(
123 | "[", c("done" = "✅", "wip" = "🚧", "missing" = "")[status],
124 | "](https://github.com/scverse/anndataR/blob/main/", source_file,
125 | "#L", line_number, ")"
126 | )
127 | )
128 | ) |>
129 | select(class, Slot = slot, prefix_formatted, status_formatted) |>
130 | spread(prefix_formatted, status_formatted)
131 | ```
132 |
133 | ## Objects
134 |
135 | ```{r echo=FALSE, results="asis"}
136 | # loop over each of the classes and print the table as markdown
137 | all_classes <- unique(status_formatted$class)
138 | object_classes <- all_classes[!all_classes %in% c("Seurat", "SingleCellExperiment")]
139 |
140 | for (class_name in object_classes) {
141 | cat("### ", class_name, "\n\n", sep = "")
142 |
143 | df <- status_formatted |>
144 | filter(class == class_name) |>
145 | select(-class) |>
146 | knitr::kable(escape = FALSE, align = "lcccc")
147 |
148 | cat(paste(as.character(df), collapse = "\n"))
149 | cat("\n\n")
150 | }
151 | ```
152 |
153 | ## Conversion
154 |
155 | ```{r echo=FALSE, results="asis"}
156 | # loop over each of the classes and print the table as markdown
157 | for (class_name in c("SingleCellExperiment", "Seurat")) {
158 | cat("### ", class_name, "\n\n", sep = "")
159 |
160 | df <- status_formatted |>
161 | filter(class == class_name) |>
162 | select(
163 | Slot,
164 | From = Setter,
165 | `From test` = `Setter test`,
166 | To = Getter,
167 | `To test` = `Getter test`
168 | ) |>
169 | knitr::kable(escape = FALSE, align = "lcccc")
170 |
171 | cat(paste(as.character(df), collapse = "\n"))
172 | cat("\n\n")
173 | }
174 | ```
175 |
--------------------------------------------------------------------------------
/vignettes/diagrams/class_diagram.mmd:
--------------------------------------------------------------------------------
1 |
2 | classDiagram
3 | class AbstractAnnData {
4 | *X: Matrix
5 | *layers: List[Matrix]
6 | *obs: DataFrame
7 | *var: DataFrame
8 | *obsp: List[Matrix]
9 | *varp: List[Matrix]
10 | *obsm: List[Matrix]
11 | *varm: List[Matrix]
12 | *uns: List
13 | *n_obs: int
14 | *n_vars: int
15 | *obs_names: Array[String]
16 | *var_names: Array[String]
17 | *subset(...): AbstractAnnData
18 | *write_h5ad(): Unit
19 |
20 | as_SingleCellExperiment(): SingleCellExperiment
21 | as_Seurat(): Seurat
22 |
23 | as_HDF5AnnData(): HDF5AnnData
24 | as_ZarrAnnData(): ZarrAnnData
25 | as_InMemoryAnnData(): InMemoryAnnData
26 | }
27 |
28 | AbstractAnnData <|-- HDF5AnnData
29 | class HDF5AnnData {
30 | init(h5file): HDF5AnnData
31 | }
32 |
33 | AbstractAnnData <|-- ZarrAnnData
34 | class ZarrAnnData {
35 | init(zarrFile): ZarrAnnData
36 | }
37 |
38 | AbstractAnnData <|-- InMemoryAnnData
39 | class InMemoryAnnData {
40 | init(X, obs, var, shape, ...): InMemoryAnnData
41 | }
42 |
43 | AbstractAnnData <|-- ReticulateAnnData
44 | class ReticulateAnnData {
45 | init(pyobj): ReticulateAnnData
46 | }
47 |
48 | class anndataR {
49 | read_h5ad(path, backend): Either[AbstractAnnData, SingleCellExperiment, Seurat]
50 | }
51 | anndataR --> AbstractAnnData
52 |
--------------------------------------------------------------------------------
/vignettes/diagrams/script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Convert mermaid diagrams to different formats
4 | # because RMarkdown doesn't support mermaid diagrams
5 |
6 | docker run --rm -w /pwd -u `id -u`:`id -g` \
7 | -v `pwd`:/pwd minlag/mermaid-cli \
8 | -i vignettes/diagrams/class_diagram.mmd -o vignettes/diagrams/class_diagram.svg
9 |
--------------------------------------------------------------------------------
/vignettes/known_issues.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Known issues
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Known issues}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | This document lists known issues with the package and suggests possible solutions.
11 |
12 | ```{r echo=FALSE, results="asis"}
13 | # how to get a file from a package
14 | data <- yaml::read_yaml(system.file("known_issues.yaml", package = "anndataR"))$known_issues
15 |
16 | for (i in seq_along(data)) {
17 | str <- paste0(
18 | "## Issue: ", data[[i]]$description, "\n\n",
19 | " * Affected backend: ", paste0("`", data[[i]]$backend, "`", collapse = ", "), "\n",
20 | " * Affected slot(s): ", paste0("`", data[[i]]$slot, "`", collapse = ", "), "\n",
21 | " * Affected dtype(s): ", paste0("`", data[[i]]$dtype, "`", collapse = ", "), "\n",
22 | " * Probable cause: ", data[[i]]$process, "\n",
23 | " * To investigate: ", data[[i]]$to_investigate, "\n",
24 | " * To fix: ", data[[i]]$to_fix, "\n\n",
25 | "### Error message\n\n",
26 | paste(paste0(" ", strsplit(data[[i]]$error_message, "\n")[[1]], "\n"), collapse = ""), "\n\n",
27 | "### Proposed solution\n\n",
28 | data[[i]]$proposed_solution, "\n\n"
29 | )
30 | cat(str)
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/vignettes/software_design.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Software design
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Software design}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 |
11 | ```{r, include = FALSE}
12 | knitr::opts_chunk$set(
13 | collapse = TRUE,
14 | comment = ""
15 | )
16 | ```
17 |
18 |
19 | `{anndataR}` is designed to offer the combined functionality of the following packages:
20 |
21 | * [theislab/zellkonverter](https://github.com/theislab/zellkonverter): Convert AnnData files to/from `SingleCellExperiment` objects.
22 | * [mtmorgan/h5ad](https://github.com/mtmorgan/h5ad/): Read/write `*.h5ad` files natively using `rhdf5`.
23 | * [dynverse/anndata](https://github.com/dynverse/anndata): An R implementation of the AnnData data structures, uses `reticulate` to read/write `*.h5ad` files.
24 |
25 | Ideally, this package will be a complete replacement for all of these packages, and will be the go-to package for working with AnnData files in R.
26 |
27 | ## Desired feature list
28 |
29 | * Provide an `R6` class to work with AnnData objects in R (either in-memory or on-disk).
30 | * Read/write `*.h5ad` files natively
31 | * Convert to/from `SingleCellExperiment` objects
32 | * Convert to/from `Seurat` objects
33 |
34 | ## Class diagram
35 |
36 | Here is a diagram of the main R6 classes provided by the package:
37 |
38 | 
39 |
40 | Notation:
41 |
42 | - `X: Matrix` - variable `X` is of type `Matrix`
43 | - `*X: Matrix` - variable `X` is abstract
44 | - `as_SingleCellExperiment(): SingleCellExperiment` - function `as_SingleCellExperiment` returns object of type `SingleCellExperiment`
45 | - `*as_SingleCellExperiment()` - function `as_SingleCellExperiment` is abstract
46 |
--------------------------------------------------------------------------------
/vignettes/usage_h5ad.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Read/write H5AD files
3 | package: anndataR
4 | output: BiocStyle::html_document
5 | vignette: >
6 | %\VignetteIndexEntry{Read/write H5AD files}
7 | %\VignetteEngine{knitr::rmarkdown}
8 | %\VignetteEncoding{UTF-8}
9 | ---
10 |
11 | ```{r, include = FALSE}
12 | knitr::opts_chunk$set(
13 | collapse = TRUE,
14 | comment = "#>"
15 | )
16 | ```
17 |
18 | This vignette demonstrates how to read and write `.h5ad` files using the **{anndataR}** package.
19 |
20 | Check out `?anndataR` for a full list of the functions provided by this package.
21 |
22 | ## Example data
23 |
24 | A great place for finding single-cell datasets as `.h5ad` files is the [CELLxGENE](https://cellxgene.cziscience.com/datasets) website.
25 |
26 | We will use an example file included in the package for demonstration.
27 |
28 | ```{r}
29 | library(anndataR)
30 |
31 | h5ad_path <- system.file("extdata", "example.h5ad", package = "anndataR")
32 | ```
33 |
34 | ## Reading in memory
35 |
36 | To read an h5ad file into memory, use the `read_h5ad` function. By default, the data will be read entirely into memory:
37 |
38 | ```{r}
39 | adata <- read_h5ad(h5ad_path)
40 | ```
41 |
42 | This reads the entire `.h5ad` file into memory as an `AnnData` object. You can then inspect its structure:
43 |
44 | ```{r}
45 | adata
46 | ```
47 |
48 | ## Reading backed by HDF5
49 |
50 | For large datasets that do not fit into memory, you can read the h5ad file in a "backed" mode. This means that the data remains on disk, and only parts that are actively being used are loaded into memory.
51 |
52 | To do this, set the `to` parameter in the `read_h5ad` to `HDF5AnnData`:
53 |
54 | ```{r}
55 | adata <- read_h5ad(h5ad_path, as = "HDF5AnnData")
56 | ```
57 | The structure of the object will look similar to the in-memory representation, but the data is stored on disk.
58 |
59 | ```{r}
60 | adata
61 | ```
62 |
63 | Note that any changes made to the object will be reflected in the `.h5ad` file!
64 |
65 | ## Writing h5ad files
66 |
67 | You can write an `AnnData` object to an h5ad file using the `write_h5ad` function:
68 |
69 | ``` r
70 | # Create a temporary file for demonstration
71 | temp_h5ad <- tempfile(fileext = ".h5ad")
72 |
73 | adata$write_h5ad(temp_h5ad)
74 | ```
75 |
76 | ## Accessing AnnData slots
77 |
78 | The `AnnData` object is a list-like object containing various slots. Here's how you can access some of them:
79 |
80 | ```{r}
81 | dim(adata$X)
82 | adata$obs[1:5, 1:6]
83 | adata$var[1:5, 1:6]
84 | ```
85 |
86 | You can also access other slots like `layers`, `uns`, `obsm`, `varm`, and `obsp` in a similar way.
87 |
88 | ## Session info
89 |
90 | ```{r}
91 | sessionInfo()
92 | ```
93 |
--------------------------------------------------------------------------------
/vignettes/usage_seurat.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Read/write Seurat objects using anndataR
3 | package: anndataR
4 | output: BiocStyle::html_document
5 | vignette: >
6 | %\VignetteIndexEntry{Read/write Seurat objects using anndataR}
7 | %\VignetteEngine{knitr::rmarkdown}
8 | %\VignetteEncoding{UTF-8}
9 | ---
10 |
11 | ```{r, include = FALSE}
12 | knitr::opts_chunk$set(
13 | collapse = TRUE,
14 | comment = "#>",
15 | warning = FALSE
16 | )
17 | ```
18 |
19 | This vignette demonstrates how to read and write `Seurat` objects using the **{anndataR}** package, leveraging the interoperability between `Seurat` and the `AnnData` format.
20 |
21 | Check out `?anndataR` for a full list of the functions provided by this package.
22 |
23 | ## Introduction
24 |
25 | Seurat is a widely used toolkit for single-cell analysis in R.
26 | **{anndataR}** enables conversion between `Seurat` objects and `AnnData` objects,
27 | allowing you to leverage the strengths of both the scverse and Seurat ecosystems.
28 |
29 | ## Prerequisites
30 |
31 | Before you begin, make sure you have both Seurat and **{anndataR}** installed. You can install them using the following code:
32 |
33 | ```r
34 | if (!requireNamespace("pak", quietly = TRUE)) {
35 | install.packages("pak")
36 | }
37 | pak::pak("Seurat")
38 | pak::pak("scverse/anndataR")
39 | ```
40 |
41 | ## Converting an AnnData Object to a Seurat Object
42 |
43 | Using an example `.h5ad` file included in the package, we will demonstrate how to read an `.h5ad` file and convert it to a `Seurat` object.
44 |
45 | ```{r prep_file}
46 | library(anndataR)
47 | library(Seurat)
48 |
49 | h5ad_file <- system.file("extdata", "example.h5ad", package = "anndataR")
50 | ```
51 |
52 | Read the `.h5ad` file and convert it to a `Seurat` object:
53 |
54 | ```{r read_data}
55 | seurat_obj <- read_h5ad(h5ad_file, as = "Seurat")
56 | seurat_obj
57 | ```
58 |
59 | This is equivalent to reading in the `.h5ad` file and explicitly converting.
60 |
61 | ```{r convert_seurat}
62 | adata <- read_h5ad(h5ad_file)
63 | seurat_obj <- adata$as_Seurat()
64 | seurat_obj
65 | ```
66 |
67 | Note that there is no one-to-one mapping possible between the AnnData and SeuratObject data structures,
68 | so some information might be lost during conversion. It is recommended to carefully inspect the converted object
69 | to ensure that all necessary information has been transferred.
70 |
71 | See `?as_Seurat` for more details on how to customize the conversion process. For instance:
72 |
73 | ```{r customize_seurat_conversion}
74 | adata$as_Seurat(
75 | assay_name = "ADT",
76 | layers_mapping = c(counts = "dense_counts", data = "dense_X")
77 | )
78 | ```
79 |
80 | ## Convert a Seurat Object to an AnnData Object
81 |
82 | Here's an example demonstrating how to create a `Seurat` object from scratch, then convert it to `AnnData` and save it as `.h5ad`
83 |
84 | ```{r create_seurat}
85 | counts <- matrix(rbinom(20000, 1000, .001), nrow = 100)
86 | seurat_obj <- CreateSeuratObject(counts = counts) |>
87 | NormalizeData() |>
88 | FindVariableFeatures() |>
89 | ScaleData() |>
90 | RunPCA(npcs = 10) |>
91 | FindNeighbors() |>
92 | RunUMAP(dims = 1:10)
93 | seurat_obj
94 | ```
95 |
96 | You can write out the `Seurat` object to an `.h5ad` file using the `write_h5ad` function:
97 |
98 | ```r
99 | write_h5ad(seurat_obj, "seurat_obj.h5ad")
100 | ```
101 |
102 | If you need more customized conversion, you first need to convert the `Seurat` object to an `AnnData` object.
103 | Again note that there is no one-to-one mapping possible between the AnnData and SeuratObject data structures,
104 | so some information might be lost during conversion. It is recommended to carefully inspect the converted object
105 | to ensure that all necessary information has been transferred.
106 |
107 | See `?as_AnnData` for more details on how to customize the conversion process. Example:
108 |
109 | ```{r customize_as_AnnData}
110 | as_AnnData(
111 | seurat_obj,
112 | assay_name = "RNA",
113 | x_mapping = "data",
114 | layers_mapping = c(foo = "counts")
115 | )
116 | ```
117 |
118 | ## Session info
119 |
120 | ```{r}
121 | sessionInfo()
122 | ```
123 |
--------------------------------------------------------------------------------
/vignettes/usage_singlecellexperiment.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Read/write SingleCellExperiment objects using anndataR
3 | package: anndataR
4 | output: BiocStyle::html_document
5 | vignette: >
6 | %\VignetteIndexEntry{Read/write SingleCellExperiment objects using anndataR}
7 | %\VignetteEngine{knitr::rmarkdown}
8 | %\VignetteEncoding{UTF-8}
9 | ---
10 |
11 | ```{r, include = FALSE}
12 | knitr::opts_chunk$set(
13 | collapse = TRUE,
14 | comment = "#>",
15 | warning = FALSE
16 | )
17 | ```
18 |
19 | This vignette demonstrates how to read and write `SingleCellExperiment` objects using the **{anndataR}** package, leveraging the interoperability between `SingleCellExperiment` and the `AnnData` format.
20 |
21 | Check out `?anndataR` for a full list of the functions provided by this package.
22 |
23 | ## Introduction
24 |
25 | SingleCellExperiment is a widely used class for storing single-cell data in R, especially within the Bioconductor ecosystem.
26 | **{anndataR}** enables conversion between `SingleCellExperiment` objects and `AnnData` objects, allowing you to leverage the strengths of both the scverse and Bioconductor ecosystems.
27 |
28 | ## Prerequisites
29 |
30 | Before you begin, make sure you have both SingleCellExperiment and **{anndataR}** installed. You can install them using the following code:
31 |
32 | ```r
33 | if (!requireNamespace("pak", quietly = TRUE)) {
34 | install.packages("pak")
35 | }
36 | pak::pak(c("SingleCellExperiment", "SummarizedExperiment"))
37 | pak::pak("scverse/anndataR")
38 | ```
39 |
40 | ## Converting an AnnData Object to a SingleCellExperiment Object
41 |
42 | Using an example `.h5ad` file included in the package, we will demonstrate how to read an `.h5ad` file and convert it to a `SingleCellExperiment` object.
43 |
44 | ```{r prep_h5ad_file}
45 | library(anndataR)
46 | library(SingleCellExperiment)
47 |
48 | h5ad_file <- system.file("extdata", "example.h5ad", package = "anndataR")
49 | ```
50 |
51 | Read the `.h5ad` file:
52 |
53 | ```{r read_h5ad}
54 | adata <- read_h5ad(h5ad_file)
55 | adata
56 | ```
57 |
58 | Convert to a `SingleCellExperiment` object:
59 |
60 | ```{r convert_implicit}
61 | sce_obj <- adata$as_SingleCellExperiment()
62 | sce_obj
63 | ```
64 |
65 | Note that there is no one-to-one mapping possible between the AnnData and SingleCellExperiment data structures, so some information might be lost during conversion. It is recommended to carefully inspect the converted object to ensure that all necessary information has been transferred.
66 |
67 | See the [in-depth vignette on conversion to and from SingleCellExperiment objects](singlecellexperiment.html) for more details on how to customize the conversion process. For instance:
68 |
69 | ```{r ex_mapping, eval=FALSE}
70 | adata$as_SingleCellExperiment(
71 | assays_mapping = FALSE,
72 | colData_mapping = list(coldata1 = "integer", coldata2 = "numeric"),
73 | rowData_mapping = list(rowdata1 = "character", rowdata2 = "logical")
74 | )
75 | ```
76 |
77 | ## Convert a SingleCellExperiment Object to an AnnData Object
78 |
79 | Here's an example demonstrating how to create a `SingleCellExperiment` object from scratch, then convert it to `AnnData` and save it as `.h5ad`
80 |
81 | ```{r construct_sce}
82 | counts <- matrix(rbinom(20000, 1000, .001), nrow = 100)
83 | sce_obj <- SingleCellExperiment(list(counts = counts))
84 |
85 | sce_obj
86 | ```
87 |
88 | You can convert the `SingleCellExperiment` object to an `AnnData` object using the `as_AnnData` function:
89 |
90 | ```{r from_SCE}
91 | adata <- as_AnnData(sce_obj)
92 | adata
93 | ```
94 |
95 | Again note that there is no one-to-one mapping possible between the AnnData and SingleCellExperiment data structures, so some information might be lost during conversion. It is recommended to carefully inspect the converted object to ensure that all necessary information has been transferred.
96 |
97 | See the [in-depth vignette on conversion to and from SingleCellExperiment objects](singlecellexperiment.html) for more details on how to customize the conversion process. Example:
98 |
99 | ```{r from_SCE_ex}
100 | as_AnnData(
101 | sce_obj,
102 | x_mapping = "counts",
103 | layers_mapping = FALSE
104 | )
105 | ```
106 |
107 | ## Session info
108 |
109 | ```{r}
110 | sessionInfo()
111 | ```
112 |
--------------------------------------------------------------------------------