spatialdata-io 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spatialdata_io-0.1.2/.github/workflows/release.yaml +31 -0
- spatialdata_io-0.1.1/.github/workflows/test_and_deploy.yaml → spatialdata_io-0.1.2/.github/workflows/test.yaml +1 -27
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.pre-commit-config.yaml +1 -1
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/CHANGELOG.md +12 -1
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/PKG-INFO +10 -8
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/README.md +8 -7
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/_version.py +2 -2
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/api.md +6 -5
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/conf.py +2 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/pyproject.toml +1 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/__init__.py +2 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/_constants/_constants.py +57 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/converters/legacy_anndata.py +3 -3
- spatialdata_io-0.1.2/src/spatialdata_io/readers/visium_hd.py +370 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/xenium.py +24 -20
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.bumpversion.cfg +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.codecov.yaml +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.editorconfig +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.flake8 +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.github/workflows/build.yaml +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.gitignore +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.mypy.ini +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/.readthedocs.yaml +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/LICENSE +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/Makefile +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/_static/.gitkeep +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/_templates/.gitkeep +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/_templates/autosummary/class.rst +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/changelog.md +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/contributing.md +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/extensions/.gitkeep +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/extensions/typed_returns.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/index.md +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/make.bat +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/references.bib +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/references.md +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/docs/template_usage.md +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/_constants/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/_constants/_enum.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/_docs.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/_utils.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/converters/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/experimental/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/_utils/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/_utils/_read_10x_h5.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/_utils/_utils.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/codex.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/cosmx.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/curio.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/dbit.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/mcmicro.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/merscope.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/steinbock.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/visium.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/tests/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/tests/converters/__init__.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/tests/converters/test_legacy_anndata.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/tests/test_basic.py +0 -0
- {spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/tests/test_xenium.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
# requires that you have put your twine API key in your
|
|
10
|
+
# github secrets (see readme for details)
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
if: contains(github.ref, 'tags')
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
uses: actions/checkout@v3
|
|
16
|
+
|
|
17
|
+
- name: Set up Python 3.10
|
|
18
|
+
uses: actions/setup-python@v4
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.10"
|
|
21
|
+
|
|
22
|
+
- name: Install hatch
|
|
23
|
+
run: pip install hatch
|
|
24
|
+
|
|
25
|
+
- name: Build project for distribution
|
|
26
|
+
run: hatch build
|
|
27
|
+
|
|
28
|
+
- name: Publish a Python distribution to PyPI
|
|
29
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
30
|
+
with:
|
|
31
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
steps:
|
|
29
29
|
- uses: actions/checkout@v2
|
|
30
30
|
- name: Set up Python ${{ matrix.python }}
|
|
31
|
-
uses: actions/setup-python@
|
|
31
|
+
uses: actions/setup-python@v4
|
|
32
32
|
with:
|
|
33
33
|
python-version: ${{ matrix.python }}
|
|
34
34
|
|
|
@@ -62,29 +62,3 @@ jobs:
|
|
|
62
62
|
with:
|
|
63
63
|
name: coverage
|
|
64
64
|
verbose: true
|
|
65
|
-
deploy:
|
|
66
|
-
# this will run when you have tagged a commit, starting with "v*"
|
|
67
|
-
# and requires that you have put your twine API key in your
|
|
68
|
-
# github secrets (see readme for details)
|
|
69
|
-
needs: [test]
|
|
70
|
-
runs-on: ubuntu-latest
|
|
71
|
-
if: contains(github.ref, 'tags')
|
|
72
|
-
steps:
|
|
73
|
-
- name: Checkout code
|
|
74
|
-
uses: actions/checkout@v3
|
|
75
|
-
|
|
76
|
-
- name: Set up Python 3.10
|
|
77
|
-
uses: actions/setup-python@v4
|
|
78
|
-
with:
|
|
79
|
-
python-version: "3.10"
|
|
80
|
-
|
|
81
|
-
- name: Install hatch
|
|
82
|
-
run: pip install hatch
|
|
83
|
-
|
|
84
|
-
- name: Build project for distribution
|
|
85
|
-
run: hatch build
|
|
86
|
-
|
|
87
|
-
- name: Publish a Python distribution to PyPI
|
|
88
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
89
|
-
with:
|
|
90
|
-
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -8,7 +8,18 @@ and this project adheres to [Semantic Versioning][].
|
|
|
8
8
|
[keep a changelog]: https://keepachangelog.com/en/1.0.0/
|
|
9
9
|
[semantic versioning]: https://semver.org/spec/v2.0.0.html
|
|
10
10
|
|
|
11
|
-
## [0.1.
|
|
11
|
+
## [0.1.2] - 2024-03-30
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- (Visium HD) added reader, coauthored by @LLehner
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- (Xenium) reader for 1.0.1 (paper data) and unknown versions
|
|
20
|
+
- (Xenium) fix in reading "minimalistic" Xenium datasets #132
|
|
21
|
+
|
|
22
|
+
## [0.1.1] - 2024-03-24
|
|
12
23
|
|
|
13
24
|
### Added
|
|
14
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: spatialdata-io
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: SpatialData IO for common techs
|
|
5
5
|
Project-URL: Documentation, https://spatialdata-io.readthedocs.io/
|
|
6
6
|
Project-URL: Source, https://github.com/scverse/spatialdata-io
|
|
@@ -46,6 +46,7 @@ Requires-Dist: joblib
|
|
|
46
46
|
Requires-Dist: numpy
|
|
47
47
|
Requires-Dist: pyarrow
|
|
48
48
|
Requires-Dist: readfcs
|
|
49
|
+
Requires-Dist: scanpy
|
|
49
50
|
Requires-Dist: scikit-image
|
|
50
51
|
Requires-Dist: spatialdata
|
|
51
52
|
Provides-Extra: dev
|
|
@@ -75,21 +76,22 @@ Description-Content-Type: text/markdown
|
|
|
75
76
|
[![Documentation][badge-docs]][link-docs]
|
|
76
77
|
[](https://zenodo.org/badge/latestdoi/544045123)
|
|
77
78
|
|
|
78
|
-
[badge-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/
|
|
79
|
-
[link-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/
|
|
79
|
+
[badge-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/test.yaml/badge.svg
|
|
80
|
+
[link-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/test.yaml
|
|
80
81
|
[badge-docs]: https://img.shields.io/readthedocs/spatialdata-io
|
|
81
82
|
|
|
82
83
|
This package contains reader functions to load common spatial omics formats into SpatialData. Currently, we provide support for:
|
|
83
84
|
|
|
84
|
-
- NanoString CosMx
|
|
85
|
-
- MCMICRO (output data)
|
|
86
|
-
- Steinbock (output data)
|
|
87
85
|
- 10x Genomics Visium
|
|
86
|
+
- 10x Genomics Visium HD
|
|
88
87
|
- 10x Genomics Xenium
|
|
89
|
-
- Curio Seeker
|
|
90
88
|
- Akoya PhenoCycler (formerly CODEX)
|
|
91
|
-
-
|
|
89
|
+
- Curio Seeker
|
|
92
90
|
- DBiT-seq
|
|
91
|
+
- MCMICRO (output data)
|
|
92
|
+
- NanoString CosMx
|
|
93
|
+
- Steinbock (output data)
|
|
94
|
+
- Vizgen MERSCOPE (MERFISH)
|
|
93
95
|
|
|
94
96
|
## Getting started
|
|
95
97
|
|
|
@@ -6,21 +6,22 @@
|
|
|
6
6
|
[![Documentation][badge-docs]][link-docs]
|
|
7
7
|
[](https://zenodo.org/badge/latestdoi/544045123)
|
|
8
8
|
|
|
9
|
-
[badge-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/
|
|
10
|
-
[link-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/
|
|
9
|
+
[badge-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/test.yaml/badge.svg
|
|
10
|
+
[link-tests]: https://github.com/scverse/spatialdata-io/actions/workflows/test.yaml
|
|
11
11
|
[badge-docs]: https://img.shields.io/readthedocs/spatialdata-io
|
|
12
12
|
|
|
13
13
|
This package contains reader functions to load common spatial omics formats into SpatialData. Currently, we provide support for:
|
|
14
14
|
|
|
15
|
-
- NanoString CosMx
|
|
16
|
-
- MCMICRO (output data)
|
|
17
|
-
- Steinbock (output data)
|
|
18
15
|
- 10x Genomics Visium
|
|
16
|
+
- 10x Genomics Visium HD
|
|
19
17
|
- 10x Genomics Xenium
|
|
20
|
-
- Curio Seeker
|
|
21
18
|
- Akoya PhenoCycler (formerly CODEX)
|
|
22
|
-
-
|
|
19
|
+
- Curio Seeker
|
|
23
20
|
- DBiT-seq
|
|
21
|
+
- MCMICRO (output data)
|
|
22
|
+
- NanoString CosMx
|
|
23
|
+
- Steinbock (output data)
|
|
24
|
+
- Vizgen MERSCOPE (MERFISH)
|
|
24
25
|
|
|
25
26
|
## Getting started
|
|
26
27
|
|
|
@@ -15,14 +15,15 @@ I/O for the `spatialdata` project.
|
|
|
15
15
|
:toctree: generated
|
|
16
16
|
|
|
17
17
|
codex
|
|
18
|
-
curio
|
|
19
18
|
cosmx
|
|
19
|
+
curio
|
|
20
|
+
dbit
|
|
21
|
+
mcmicro
|
|
22
|
+
merscope
|
|
23
|
+
steinbock
|
|
20
24
|
visium
|
|
25
|
+
visium_hd
|
|
21
26
|
xenium
|
|
22
|
-
steinbock
|
|
23
|
-
merscope
|
|
24
|
-
mcmicro
|
|
25
|
-
dbit
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
### Conversion functions
|
|
@@ -98,6 +98,7 @@ intersphinx_mapping = {
|
|
|
98
98
|
"xarray": ("https://docs.xarray.dev/en/stable/", None),
|
|
99
99
|
"datatree": ("https://datatree.readthedocs.io/en/latest/", None),
|
|
100
100
|
"shapely": ("https://shapely.readthedocs.io/en/stable/", None),
|
|
101
|
+
"imageio": ("https://imageio.readthedocs.io/en/stable/", None),
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
# List of patterns, relative to source directory, that match files and
|
|
@@ -125,6 +126,7 @@ nitpick_ignore = [
|
|
|
125
126
|
("py:class", "Path"),
|
|
126
127
|
("py:class", "AnnData"),
|
|
127
128
|
("py:class", "SpatialData"),
|
|
129
|
+
("py:func", "imageio.imread"), # maybe this can be fixed
|
|
128
130
|
]
|
|
129
131
|
|
|
130
132
|
|
|
@@ -8,6 +8,7 @@ from spatialdata_io.readers.mcmicro import mcmicro
|
|
|
8
8
|
from spatialdata_io.readers.merscope import merscope
|
|
9
9
|
from spatialdata_io.readers.steinbock import steinbock
|
|
10
10
|
from spatialdata_io.readers.visium import visium
|
|
11
|
+
from spatialdata_io.readers.visium_hd import visium_hd
|
|
11
12
|
from spatialdata_io.readers.xenium import (
|
|
12
13
|
xenium,
|
|
13
14
|
xenium_aligned_image,
|
|
@@ -26,6 +27,7 @@ __all__ = [
|
|
|
26
27
|
"xenium_aligned_image",
|
|
27
28
|
"xenium_explorer_selection",
|
|
28
29
|
"dbit",
|
|
30
|
+
"visium_hd",
|
|
29
31
|
]
|
|
30
32
|
|
|
31
33
|
__version__ = version("spatialdata-io")
|
|
@@ -227,3 +227,60 @@ class DbitKeys(ModeEnum):
|
|
|
227
227
|
BARCODE_POSITION = "barcode_list"
|
|
228
228
|
# image
|
|
229
229
|
IMAGE_LOWRES_FILE = "tissue_lowres_image.png"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@unique
|
|
233
|
+
class VisiumHDKeys(ModeEnum):
|
|
234
|
+
"""Keys for *10X Genomics Visium hd* formatted dataset."""
|
|
235
|
+
|
|
236
|
+
# directories
|
|
237
|
+
SPATIAL = "spatial"
|
|
238
|
+
DEFAULT_BIN = "square_008um"
|
|
239
|
+
BIN_PREFIX = "square_"
|
|
240
|
+
MICROSCOPE_IMAGE = "microscope_image"
|
|
241
|
+
|
|
242
|
+
# counts and locations files
|
|
243
|
+
FILTERED_COUNTS_FILE = "filtered_feature_bc_matrix.h5"
|
|
244
|
+
RAW_COUNTS_FILE = "raw_feature_bc_matrix.h5"
|
|
245
|
+
TISSUE_POSITIONS_FILE = "tissue_positions.parquet"
|
|
246
|
+
|
|
247
|
+
# images
|
|
248
|
+
IMAGE_HIRES_FILE = "spatial/tissue_hires_image.png"
|
|
249
|
+
IMAGE_LOWRES_FILE = "spatial/tissue_lowres_image.png"
|
|
250
|
+
IMAGE_CYTASSIST = "spatial/cytassist_image.tiff"
|
|
251
|
+
|
|
252
|
+
# scalefactors
|
|
253
|
+
SCALEFACTORS_FILE = "scalefactors_json.json"
|
|
254
|
+
|
|
255
|
+
# scalefactors keys
|
|
256
|
+
SCALEFACTORS_HIRES = "tissue_hires_scalef"
|
|
257
|
+
SCALEFACTORS_LOWRES = "tissue_lowres_scalef"
|
|
258
|
+
SCALEFACTORS_SPOT_DIAMETER_FULLRES = "spot_diameter_fullres"
|
|
259
|
+
SCALEFACTORS_BIN_SIZE_UM = "bin_size_um"
|
|
260
|
+
SCALEFACTORS_MICRONS_PER_PIXEL = "microns_per_pixel"
|
|
261
|
+
|
|
262
|
+
# locations keys
|
|
263
|
+
LOCATIONS_X = "pxl_col_in_fullres"
|
|
264
|
+
LOCATIONS_Y = "pxl_row_in_fullres"
|
|
265
|
+
BARCODE = "barcode"
|
|
266
|
+
IN_TISSUE = "in_tissue"
|
|
267
|
+
ARRAY_ROW = "array_row"
|
|
268
|
+
ARRAY_COL = "array_col"
|
|
269
|
+
|
|
270
|
+
# table keys
|
|
271
|
+
REGION_KEY = "region"
|
|
272
|
+
INSTANCE_KEY = "location_id"
|
|
273
|
+
|
|
274
|
+
# feature slice file (it contains transformation matrices in the metadata)
|
|
275
|
+
FEATURE_SLICE_FILE = "feature_slice.h5"
|
|
276
|
+
|
|
277
|
+
# METADATA_KEYS
|
|
278
|
+
METADATA_JSON = "metadata_json"
|
|
279
|
+
HD_LAYOUT_JSON = "hd_layout_json"
|
|
280
|
+
TRANSFORM = "transform"
|
|
281
|
+
TRANSFORM_MATRICES = "transform_matrices"
|
|
282
|
+
CYTASSIST_COLROW_TO_SPOT_COLROW = ("cytassist_colrow_to_spot_colrow",)
|
|
283
|
+
SPOT_COLROW_TO_CYTASSIST_COLROW = ("spot_colrow_to_cytassist_colrow",)
|
|
284
|
+
MICROSCOPE_COLROW_TO_SPOT_COLROW = ("microscope_colrow_to_spot_colrow",)
|
|
285
|
+
SPOT_COLROW_TO_MICROSCOPE_COLROW = ("spot_colrow_to_microscope_colrow",)
|
|
286
|
+
FILE_FORMAT = "file_format"
|
{spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/converters/legacy_anndata.py
RENAMED
|
@@ -8,7 +8,7 @@ from spatialdata import (
|
|
|
8
8
|
SpatialData,
|
|
9
9
|
get_centroids,
|
|
10
10
|
get_extent,
|
|
11
|
-
|
|
11
|
+
join_spatialelement_table,
|
|
12
12
|
to_circles,
|
|
13
13
|
)
|
|
14
14
|
from spatialdata._core.operations._utils import transform_to_data_extent
|
|
@@ -149,8 +149,8 @@ def to_legacy_anndata(
|
|
|
149
149
|
shapes = to_circles(element)
|
|
150
150
|
circles_sdata = SpatialData(tables={table_name: table}, shapes={region_name: shapes})
|
|
151
151
|
|
|
152
|
-
joined_elements, new_table =
|
|
153
|
-
sdata=circles_sdata,
|
|
152
|
+
joined_elements, new_table = join_spatialelement_table(
|
|
153
|
+
sdata=circles_sdata, spatial_element_names=region_name, table_name=table_name, how="inner", match_rows="left"
|
|
154
154
|
)
|
|
155
155
|
|
|
156
156
|
# the table after the filtering must not be empty
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import warnings
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from types import MappingProxyType
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import h5py
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas as pd
|
|
15
|
+
import scanpy as sc
|
|
16
|
+
from dask_image.imread import imread
|
|
17
|
+
from imageio import imread as imread2
|
|
18
|
+
from multiscale_spatial_image import MultiscaleSpatialImage
|
|
19
|
+
from spatial_image import SpatialImage
|
|
20
|
+
from spatialdata import SpatialData
|
|
21
|
+
from spatialdata.models import Image2DModel, ShapesModel, TableModel
|
|
22
|
+
from spatialdata.transformations import (
|
|
23
|
+
Affine,
|
|
24
|
+
Identity,
|
|
25
|
+
Scale,
|
|
26
|
+
Sequence,
|
|
27
|
+
set_transformation,
|
|
28
|
+
)
|
|
29
|
+
from xarray import DataArray
|
|
30
|
+
|
|
31
|
+
from spatialdata_io._constants._constants import VisiumHDKeys
|
|
32
|
+
from spatialdata_io._docs import inject_docs
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@inject_docs(vx=VisiumHDKeys)
|
|
36
|
+
def visium_hd(
|
|
37
|
+
path: str | Path,
|
|
38
|
+
dataset_id: str | None = None,
|
|
39
|
+
filtered_counts_file: bool = True,
|
|
40
|
+
bin_size: int | list[int] | None = None,
|
|
41
|
+
fullres_image_file: str | Path | None = None,
|
|
42
|
+
load_all_images: bool = False,
|
|
43
|
+
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
44
|
+
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
45
|
+
anndata_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
46
|
+
) -> SpatialData:
|
|
47
|
+
"""
|
|
48
|
+
Read *10x Genomics* Visium HD formatted dataset.
|
|
49
|
+
|
|
50
|
+
.. seealso::
|
|
51
|
+
|
|
52
|
+
- `Space Ranger output <https://support.10xgenomics.com/spatial-gene-expression/software/pipelines/latest/output/overview>`_.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
path
|
|
57
|
+
Path to directory containing the *10x Genomics* Visium HD output.
|
|
58
|
+
dataset_id
|
|
59
|
+
Unique identifier of the dataset. If `None`, it tries to infer it from the file name of the feature slice file.
|
|
60
|
+
filtered_counts_file
|
|
61
|
+
It sets the value of `counts_file` to ``{vx.FILTERED_COUNTS_FILE!r}`` (when `True`) or to
|
|
62
|
+
``{vx.RAW_COUNTS_FILE!r}`` (when `False`).
|
|
63
|
+
bin_size
|
|
64
|
+
When specified, load the data of a specific bin size, or a list of bin sizes. By default, it loads all the
|
|
65
|
+
available bin sizes.
|
|
66
|
+
fullres_image_file
|
|
67
|
+
Path to the full-resolution image. By default the image is searched in the ``{vx.MICROSCOPE_IMAGE!r}``
|
|
68
|
+
directory.
|
|
69
|
+
load_all_images
|
|
70
|
+
If `False`, load only the full resolution, high resolution and low resolution images. If `True`, also the
|
|
71
|
+
following images: ``{vx.IMAGE_CYTASSIST!r}``.
|
|
72
|
+
imread_kwargs
|
|
73
|
+
Keyword arguments for :func:`imageio.imread`.
|
|
74
|
+
image_models_kwargs
|
|
75
|
+
Keyword arguments for :class:`spatialdata.models.Image2DModel`.
|
|
76
|
+
anndata_kwargs
|
|
77
|
+
Keyword arguments for :func:`anndata.read_h5ad`.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
SpatialData object for the Visium HD data.
|
|
82
|
+
"""
|
|
83
|
+
path = Path(path)
|
|
84
|
+
tables = {}
|
|
85
|
+
shapes = {}
|
|
86
|
+
images: dict[str, Any] = {}
|
|
87
|
+
|
|
88
|
+
if dataset_id is None:
|
|
89
|
+
dataset_id = _infer_dataset_id(path)
|
|
90
|
+
filename_prefix = f"{dataset_id}_"
|
|
91
|
+
else:
|
|
92
|
+
filename_prefix = ""
|
|
93
|
+
|
|
94
|
+
def load_image(path: Path, suffix: str, scale_factors: list[int] | None = None) -> None:
|
|
95
|
+
_load_image(
|
|
96
|
+
path=path,
|
|
97
|
+
images=images,
|
|
98
|
+
suffix=suffix,
|
|
99
|
+
dataset_id=dataset_id,
|
|
100
|
+
imread_kwargs=imread_kwargs,
|
|
101
|
+
image_models_kwargs=image_models_kwargs,
|
|
102
|
+
scale_factors=scale_factors,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
metadata, hd_layout = _parse_metadata(path, filename_prefix)
|
|
106
|
+
transform_matrices = _get_transform_matrices(metadata, hd_layout)
|
|
107
|
+
file_format = hd_layout[VisiumHDKeys.FILE_FORMAT]
|
|
108
|
+
if file_format != "1.0":
|
|
109
|
+
warnings.warn(
|
|
110
|
+
f"File format {file_format} is not supported. A more recent file format may be supported in a newer version"
|
|
111
|
+
f"of the spatialdata-io package.",
|
|
112
|
+
UserWarning,
|
|
113
|
+
stacklevel=2,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
path_bins = path
|
|
117
|
+
all_bin_sizes = sorted(
|
|
118
|
+
[
|
|
119
|
+
bin_size
|
|
120
|
+
for bin_size in os.listdir(path_bins)
|
|
121
|
+
if os.path.isdir(os.path.join(path_bins, bin_size)) and bin_size.startswith(VisiumHDKeys.BIN_PREFIX)
|
|
122
|
+
]
|
|
123
|
+
)
|
|
124
|
+
if bin_size is None:
|
|
125
|
+
bin_sizes = all_bin_sizes
|
|
126
|
+
elif isinstance(bin_size, int) or isinstance(bin_size, list) and len(bin_size) == 0:
|
|
127
|
+
if f"square_{bin_size:03}um" not in all_bin_sizes:
|
|
128
|
+
warnings.warn(
|
|
129
|
+
f"Requested bin size {bin_size} not found (available {all_bin_sizes}). Using all available bins.",
|
|
130
|
+
UserWarning,
|
|
131
|
+
stacklevel=2,
|
|
132
|
+
)
|
|
133
|
+
bin_sizes = all_bin_sizes
|
|
134
|
+
else:
|
|
135
|
+
bin_sizes = [f"square_{bin_size:03}um"]
|
|
136
|
+
|
|
137
|
+
# iterate over the given bins and load the data
|
|
138
|
+
for bin_size_str in bin_sizes:
|
|
139
|
+
path_bin = path_bins / bin_size_str
|
|
140
|
+
counts_file = VisiumHDKeys.FILTERED_COUNTS_FILE if filtered_counts_file else VisiumHDKeys.RAW_COUNTS_FILE
|
|
141
|
+
adata = sc.read_10x_h5(
|
|
142
|
+
path_bin / counts_file,
|
|
143
|
+
gex_only=False,
|
|
144
|
+
**anndata_kwargs,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
path_bin_spatial = path_bin / VisiumHDKeys.SPATIAL
|
|
148
|
+
|
|
149
|
+
with open(path_bin_spatial / VisiumHDKeys.SCALEFACTORS_FILE) as file:
|
|
150
|
+
scalefactors = json.load(file)
|
|
151
|
+
|
|
152
|
+
# consistency check
|
|
153
|
+
found_bin_size = re.search(r"\d{3}", bin_size_str)
|
|
154
|
+
assert found_bin_size is not None
|
|
155
|
+
assert float(found_bin_size.group()) == scalefactors[VisiumHDKeys.SCALEFACTORS_BIN_SIZE_UM]
|
|
156
|
+
assert np.isclose(
|
|
157
|
+
scalefactors[VisiumHDKeys.SCALEFACTORS_BIN_SIZE_UM]
|
|
158
|
+
/ scalefactors[VisiumHDKeys.SCALEFACTORS_SPOT_DIAMETER_FULLRES],
|
|
159
|
+
scalefactors[VisiumHDKeys.SCALEFACTORS_MICRONS_PER_PIXEL],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
tissue_positions_file = path_bin_spatial / VisiumHDKeys.TISSUE_POSITIONS_FILE
|
|
163
|
+
|
|
164
|
+
# read coordinates and set up adata.obs and adata.obsm
|
|
165
|
+
coords = pd.read_parquet(tissue_positions_file)
|
|
166
|
+
assert all(
|
|
167
|
+
coords.columns.values
|
|
168
|
+
== [
|
|
169
|
+
VisiumHDKeys.BARCODE,
|
|
170
|
+
VisiumHDKeys.IN_TISSUE,
|
|
171
|
+
VisiumHDKeys.ARRAY_ROW,
|
|
172
|
+
VisiumHDKeys.ARRAY_COL,
|
|
173
|
+
VisiumHDKeys.LOCATIONS_Y,
|
|
174
|
+
VisiumHDKeys.LOCATIONS_X,
|
|
175
|
+
]
|
|
176
|
+
)
|
|
177
|
+
coords.set_index(VisiumHDKeys.BARCODE, inplace=True, drop=True)
|
|
178
|
+
coords_filtered = coords.loc[adata.obs.index]
|
|
179
|
+
adata.obs = pd.merge(adata.obs, coords_filtered, how="left", left_index=True, right_index=True)
|
|
180
|
+
# compatibility to legacy squidpy
|
|
181
|
+
adata.obsm["spatial"] = adata.obs[[VisiumHDKeys.LOCATIONS_X, VisiumHDKeys.LOCATIONS_Y]].values
|
|
182
|
+
# dropping the spatial coordinates (will be stored in shapes)
|
|
183
|
+
adata.obs.drop(
|
|
184
|
+
columns=[
|
|
185
|
+
VisiumHDKeys.LOCATIONS_X,
|
|
186
|
+
VisiumHDKeys.LOCATIONS_Y,
|
|
187
|
+
],
|
|
188
|
+
inplace=True,
|
|
189
|
+
)
|
|
190
|
+
adata.obs[VisiumHDKeys.INSTANCE_KEY] = np.arange(len(adata))
|
|
191
|
+
|
|
192
|
+
# scaling
|
|
193
|
+
transform_original = Identity()
|
|
194
|
+
transform_lowres = Scale(
|
|
195
|
+
np.array(
|
|
196
|
+
[
|
|
197
|
+
scalefactors[VisiumHDKeys.SCALEFACTORS_LOWRES],
|
|
198
|
+
scalefactors[VisiumHDKeys.SCALEFACTORS_LOWRES],
|
|
199
|
+
]
|
|
200
|
+
),
|
|
201
|
+
axes=("x", "y"),
|
|
202
|
+
)
|
|
203
|
+
transform_hires = Scale(
|
|
204
|
+
np.array(
|
|
205
|
+
[
|
|
206
|
+
scalefactors[VisiumHDKeys.SCALEFACTORS_HIRES],
|
|
207
|
+
scalefactors[VisiumHDKeys.SCALEFACTORS_HIRES],
|
|
208
|
+
]
|
|
209
|
+
),
|
|
210
|
+
axes=("x", "y"),
|
|
211
|
+
)
|
|
212
|
+
# parse shapes
|
|
213
|
+
circles = ShapesModel.parse(
|
|
214
|
+
adata.obsm["spatial"],
|
|
215
|
+
geometry=0,
|
|
216
|
+
radius=scalefactors[VisiumHDKeys.SCALEFACTORS_SPOT_DIAMETER_FULLRES] / 2.0,
|
|
217
|
+
index=adata.obs[VisiumHDKeys.INSTANCE_KEY].copy(),
|
|
218
|
+
transformations={
|
|
219
|
+
"global": transform_original,
|
|
220
|
+
"downscaled_hires": transform_hires,
|
|
221
|
+
"downscaled_lowres": transform_lowres,
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
shapes_name = dataset_id + "_" + bin_size_str
|
|
225
|
+
shapes[shapes_name] = circles
|
|
226
|
+
|
|
227
|
+
# parse table
|
|
228
|
+
adata.obs[VisiumHDKeys.REGION_KEY] = shapes_name
|
|
229
|
+
adata.obs[VisiumHDKeys.REGION_KEY] = adata.obs[VisiumHDKeys.REGION_KEY].astype("category")
|
|
230
|
+
tables[bin_size_str] = TableModel.parse(
|
|
231
|
+
adata,
|
|
232
|
+
region=shapes_name,
|
|
233
|
+
region_key=str(VisiumHDKeys.REGION_KEY),
|
|
234
|
+
instance_key=str(VisiumHDKeys.INSTANCE_KEY),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# read full resolution image
|
|
238
|
+
if fullres_image_file is not None:
|
|
239
|
+
fullres_image_file = Path(fullres_image_file)
|
|
240
|
+
else:
|
|
241
|
+
path_fullres = path / VisiumHDKeys.MICROSCOPE_IMAGE
|
|
242
|
+
if path_fullres.exists():
|
|
243
|
+
fullres_image_filenames = [
|
|
244
|
+
f for f in os.listdir(path_fullres) if os.path.isfile(os.path.join(path_fullres, f))
|
|
245
|
+
]
|
|
246
|
+
if len(fullres_image_filenames) > 1:
|
|
247
|
+
warnings.warn(
|
|
248
|
+
f"Multiple files found in {path_fullres}, using the first one: {fullres_image_filenames[0]}. Please"
|
|
249
|
+
" specify the path to the full resolution image manually using the `fullres_image_file` argument.",
|
|
250
|
+
UserWarning,
|
|
251
|
+
stacklevel=2,
|
|
252
|
+
)
|
|
253
|
+
fullres_image_filename = fullres_image_filenames[0]
|
|
254
|
+
fullres_image_file = path_fullres / fullres_image_filename
|
|
255
|
+
|
|
256
|
+
if fullres_image_file is not None:
|
|
257
|
+
load_image(
|
|
258
|
+
path=path / fullres_image_file,
|
|
259
|
+
suffix="_full_image",
|
|
260
|
+
scale_factors=[2, 2, 2, 2],
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# hires image
|
|
264
|
+
load_image(
|
|
265
|
+
path=path / VisiumHDKeys.IMAGE_HIRES_FILE,
|
|
266
|
+
suffix="_hires_image",
|
|
267
|
+
)
|
|
268
|
+
set_transformation(
|
|
269
|
+
images[dataset_id + "_hires_image"],
|
|
270
|
+
{"downscaled_hires": Identity()},
|
|
271
|
+
set_all=True,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# lowres image
|
|
275
|
+
load_image(
|
|
276
|
+
path=path / VisiumHDKeys.IMAGE_LOWRES_FILE,
|
|
277
|
+
suffix="_lowres_image",
|
|
278
|
+
)
|
|
279
|
+
set_transformation(
|
|
280
|
+
images[dataset_id + "_lowres_image"],
|
|
281
|
+
{"downscaled_lowres": Identity()},
|
|
282
|
+
set_all=True,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# cytassist image
|
|
286
|
+
if load_all_images:
|
|
287
|
+
load_image(
|
|
288
|
+
path=path / VisiumHDKeys.IMAGE_CYTASSIST,
|
|
289
|
+
suffix="_cytassist_image",
|
|
290
|
+
)
|
|
291
|
+
image = images[dataset_id + "_cytassist_image"]
|
|
292
|
+
affine0 = transform_matrices["cytassist_colrow_to_spot_colrow"]
|
|
293
|
+
affine1 = transform_matrices["spot_colrow_to_microscope_colrow"]
|
|
294
|
+
set_transformation(image, Sequence([affine0, affine1]), "global")
|
|
295
|
+
|
|
296
|
+
return SpatialData(tables=tables, images=images, shapes=shapes)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _infer_dataset_id(path: Path) -> str:
|
|
300
|
+
suffix = f"_{VisiumHDKeys.FEATURE_SLICE_FILE.value}"
|
|
301
|
+
files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) and f.endswith(suffix)]
|
|
302
|
+
if len(files) == 0 or len(files) > 1:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Cannot infer `dataset_id` from the feature slice file in {path}, please pass `dataset_id` as an argument."
|
|
305
|
+
)
|
|
306
|
+
return files[0].replace(suffix, "")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _load_image(
|
|
310
|
+
path: Path,
|
|
311
|
+
images: dict[str, SpatialImage | MultiscaleSpatialImage],
|
|
312
|
+
suffix: str,
|
|
313
|
+
dataset_id: str,
|
|
314
|
+
imread_kwargs: Mapping[str, Any],
|
|
315
|
+
image_models_kwargs: Mapping[str, Any],
|
|
316
|
+
scale_factors: list[int] | None,
|
|
317
|
+
) -> None:
|
|
318
|
+
if path.exists():
|
|
319
|
+
if path.suffix != ".btf":
|
|
320
|
+
data = imread(path, **imread_kwargs)
|
|
321
|
+
if len(data.shape) == 4:
|
|
322
|
+
# this happens for the cytassist, hires and lowres images; the umi image doesn't need processing
|
|
323
|
+
data = data.squeeze().transpose(2, 0, 1)
|
|
324
|
+
else:
|
|
325
|
+
if "MAX_IMAGE_PIXELS" in imread_kwargs:
|
|
326
|
+
from PIL import Image as ImagePIL
|
|
327
|
+
|
|
328
|
+
ImagePIL.MAX_IMAGE_PIXELS = dict(imread_kwargs).pop("MAX_IMAGE_PIXELS")
|
|
329
|
+
# dask_image doesn't recognize .btf automatically and imageio v3 throws error due to pixel limit -> use imageio v2
|
|
330
|
+
data = imread2(path, **imread_kwargs).squeeze().transpose(2, 0, 1)
|
|
331
|
+
image = DataArray(data, dims=("c", "y", "x"))
|
|
332
|
+
parsed = Image2DModel.parse(image, scale_factors=scale_factors, **image_models_kwargs)
|
|
333
|
+
images[dataset_id + suffix] = parsed
|
|
334
|
+
else:
|
|
335
|
+
warnings.warn(f"File {path} does not exist, skipping it.", UserWarning, stacklevel=2)
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _get_affine(coefficients: list[int]) -> Affine:
|
|
340
|
+
matrix = np.array(coefficients).reshape(3, 3)
|
|
341
|
+
# the last row doesn't match with machine precision, let's check the matrix it's still close to a homogeneous
|
|
342
|
+
# matrix, and fix this
|
|
343
|
+
assert np.allclose(matrix[2], [0, 0, 1], atol=1e-2), matrix
|
|
344
|
+
matrix[2] = [0, 0, 1]
|
|
345
|
+
return Affine(matrix, input_axes=("x", "y"), output_axes=("x", "y"))
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _parse_metadata(path: Path, filename_prefix: str) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
349
|
+
with h5py.File(path / f"{filename_prefix}{VisiumHDKeys.FEATURE_SLICE_FILE.value}", "r") as f5:
|
|
350
|
+
metadata = json.loads(dict(f5.attrs)[VisiumHDKeys.METADATA_JSON])
|
|
351
|
+
hd_layout = json.loads(metadata[VisiumHDKeys.HD_LAYOUT_JSON])
|
|
352
|
+
return metadata, hd_layout
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _get_transform_matrices(metadata: dict[str, Any], hd_layout: dict[str, Any]) -> dict[str, Affine]:
|
|
356
|
+
transform_matrices = {}
|
|
357
|
+
|
|
358
|
+
# not used
|
|
359
|
+
transform_matrices["hd_layout_transform"] = _get_affine(hd_layout[VisiumHDKeys.TRANSFORM])
|
|
360
|
+
|
|
361
|
+
for key in [
|
|
362
|
+
VisiumHDKeys.CYTASSIST_COLROW_TO_SPOT_COLROW,
|
|
363
|
+
VisiumHDKeys.SPOT_COLROW_TO_CYTASSIST_COLROW,
|
|
364
|
+
VisiumHDKeys.MICROSCOPE_COLROW_TO_SPOT_COLROW,
|
|
365
|
+
VisiumHDKeys.SPOT_COLROW_TO_MICROSCOPE_COLROW,
|
|
366
|
+
]:
|
|
367
|
+
data = metadata[VisiumHDKeys.TRANSFORM_MATRICES][key]
|
|
368
|
+
transform_matrices[key.value] = _get_affine(data)
|
|
369
|
+
|
|
370
|
+
return transform_matrices
|
|
@@ -61,6 +61,7 @@ def xenium(
|
|
|
61
61
|
transcripts: bool = True,
|
|
62
62
|
morphology_mip: bool = True,
|
|
63
63
|
morphology_focus: bool = True,
|
|
64
|
+
aligned_images: bool = True,
|
|
64
65
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
65
66
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
66
67
|
labels_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
@@ -107,9 +108,11 @@ def xenium(
|
|
|
107
108
|
transcripts
|
|
108
109
|
Whether to read transcripts.
|
|
109
110
|
morphology_mip
|
|
110
|
-
Whether to read morphology mip.
|
|
111
|
+
Whether to read the morphology mip image (available in versions < 2.0.0).
|
|
111
112
|
morphology_focus
|
|
112
|
-
Whether to read morphology focus.
|
|
113
|
+
Whether to read the morphology focus image.
|
|
114
|
+
aligned_images
|
|
115
|
+
Whether to also parse, when available, additional H&E or IF aligned images.
|
|
113
116
|
imread_kwargs
|
|
114
117
|
Keyword arguments to pass to the image reader.
|
|
115
118
|
image_models_kwargs
|
|
@@ -147,7 +150,7 @@ def xenium(
|
|
|
147
150
|
else:
|
|
148
151
|
table = return_values
|
|
149
152
|
|
|
150
|
-
if version >= packaging.version.parse("2.0.0"):
|
|
153
|
+
if version is not None and version >= packaging.version.parse("2.0.0"):
|
|
151
154
|
cell_summary_table = _get_cells_metadata_table_from_zarr(path, XeniumKeys.CELLS_ZARR, specs)
|
|
152
155
|
if not cell_summary_table[XeniumKeys.CELL_ID].equals(table.obs[XeniumKeys.CELL_ID]):
|
|
153
156
|
warnings.warn(
|
|
@@ -190,17 +193,17 @@ def xenium(
|
|
|
190
193
|
labels_name="cell_labels",
|
|
191
194
|
labels_models_kwargs=labels_models_kwargs,
|
|
192
195
|
)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
196
|
+
if cell_labels_indices_mapping is not None:
|
|
197
|
+
if not pd.DataFrame.equals(cell_labels_indices_mapping["cell_id"], table.obs[str(XeniumKeys.CELL_ID)]):
|
|
198
|
+
warnings.warn(
|
|
199
|
+
"The cell_id column in the cell_labels_table does not match the cell_id column derived from the cell "
|
|
200
|
+
"labels data. This could be due to trying to read a new version that is not supported yet. Please "
|
|
201
|
+
"report this issue.",
|
|
202
|
+
UserWarning,
|
|
203
|
+
stacklevel=2,
|
|
204
|
+
)
|
|
205
|
+
else:
|
|
206
|
+
table.obs["cell_labels"] = cell_labels_indices_mapping["label_index"]
|
|
204
207
|
|
|
205
208
|
if nucleus_boundaries:
|
|
206
209
|
polygons["nucleus_boundaries"] = _get_polygons(
|
|
@@ -223,7 +226,7 @@ def xenium(
|
|
|
223
226
|
if transcripts:
|
|
224
227
|
points["transcripts"] = _get_points(path, specs)
|
|
225
228
|
|
|
226
|
-
if version < packaging.version.parse("2.0.0"):
|
|
229
|
+
if version is None or version < packaging.version.parse("2.0.0"):
|
|
227
230
|
if morphology_mip:
|
|
228
231
|
images["morphology_mip"] = _get_images(
|
|
229
232
|
path,
|
|
@@ -302,9 +305,10 @@ def xenium(
|
|
|
302
305
|
sdata = SpatialData(**elements_dict)
|
|
303
306
|
|
|
304
307
|
# find and add additional aligned images
|
|
305
|
-
aligned_images
|
|
306
|
-
|
|
307
|
-
|
|
308
|
+
if aligned_images:
|
|
309
|
+
extra_images = _add_aligned_images(path, imread_kwargs, image_models_kwargs)
|
|
310
|
+
for key, value in extra_images.items():
|
|
311
|
+
sdata.images[key] = value
|
|
308
312
|
|
|
309
313
|
return sdata
|
|
310
314
|
|
|
@@ -380,7 +384,7 @@ def _get_labels_and_indices_mapping(
|
|
|
380
384
|
if mask_index == 0:
|
|
381
385
|
# nuclei currently not supported
|
|
382
386
|
return labels, None
|
|
383
|
-
if version is not None and version < packaging.version.parse("1.3.0"):
|
|
387
|
+
if version is None or version is not None and version < packaging.version.parse("1.3.0"):
|
|
384
388
|
# supported in version 1.3.0 and not supported in version 1.0.2; conservatively, let's assume it is not
|
|
385
389
|
# supported in versions < 1.3.0
|
|
386
390
|
return labels, None
|
|
@@ -640,7 +644,7 @@ def _parse_version_of_xenium_analyzer(
|
|
|
640
644
|
hide_warning: bool = True,
|
|
641
645
|
) -> packaging.version.Version | None:
|
|
642
646
|
string = specs[XeniumKeys.ANALYSIS_SW_VERSION]
|
|
643
|
-
pattern = r"^
|
|
647
|
+
pattern = r"^(?:x|X)enium-(\d+\.\d+\.\d+(\.\d+-\d+)?)"
|
|
644
648
|
|
|
645
649
|
result = re.search(pattern, string)
|
|
646
650
|
# Example
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdata_io-0.1.1 → spatialdata_io-0.1.2}/src/spatialdata_io/readers/_utils/_read_10x_h5.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|