spatialdata 0.7.3a2__tar.gz → 0.8.0__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-0.8.0/.claude/skills/memray/SKILL.md +37 -0
- spatialdata-0.8.0/.claude/skills/profimp/SKILL.md +30 -0
- spatialdata-0.8.0/.claude/skills/pyspy/SKILL.md +45 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/workflows/test.yaml +4 -4
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.gitignore +9 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.mypy.ini +1 -1
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.pre-commit-config.yaml +3 -3
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/PKG-INFO +11 -7
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/README.md +6 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/_version.py +2 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/benchmarks/README.md +26 -0
- spatialdata-0.8.0/benchmarks/benchmark_dataloader.py +75 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/models_utils.md +1 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/index.md +7 -1
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/pyproject.toml +39 -5
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/_utils.py +3 -3
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/concatenate.py +0 -8
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/rasterize_bins.py +4 -8
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/transform.py +35 -12
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/query/_utils.py +5 -6
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/query/relational_query.py +188 -133
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/query/spatial_query.py +119 -120
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/spatialdata.py +44 -22
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/validation.py +4 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/_utils.py +65 -13
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/io_points.py +1 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/io_raster.py +112 -15
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/io_shapes.py +1 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_types.py +2 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/dataloader/__init__.py +3 -5
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/dataloader/datasets.py +35 -17
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/models/__init__.py +2 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/models/_utils.py +3 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/models/chunks_utils.py +2 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/models/models.py +51 -2
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_rasterize_bins.py +0 -18
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_spatialdata_operations.py +8 -5
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_transform.py +74 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/query/test_relational_query.py +37 -1
- spatialdata-0.8.0/tests/core/query/test_relational_query_match_sdata_to_table.py +235 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/query/test_spatial_query.py +101 -1
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_multi_table.py +1 -1
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_readwrite.py +72 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_utils.py +24 -1
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/models/test_models.py +8 -8
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/utils/test_sanitize.py +16 -0
- spatialdata-0.7.3a2/tests/core/query/test_relational_query_match_sdata_to_table.py +0 -147
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.bumpversion.cfg +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.editorconfig +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/codecov.yml +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/release.yml +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/workflows/build_image.yml +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.github/workflows/release.yaml +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.gitmodules +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/.readthedocs.yaml +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/CHANGELOG.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/Dockerfile +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/LICENSE +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/asv.conf.json +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/benchmarks/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/benchmarks/benchmark_imports.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/benchmarks/spatialdata_benchmark.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/benchmarks/utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/Makefile +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/_static/.gitkeep +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/_static/css/custom.css +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/_static/img/spatialdata_horizontal.png +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/SpatialData.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/data_formats.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/dataloader.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/datasets.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/io.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/models.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/operations.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/testing.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/transformations.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api/transformations_utils.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/api.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/changelog.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/conf.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/contributing.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/design_doc.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/extensions/typed_returns.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/glossary.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/installation.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/references.bib +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/references.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/docs/user_guide.md +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/__main__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_bridges/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/_deepcopy.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/_elements.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/centroids.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/data_extent.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/aggregate.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/map.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/rasterize.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/vectorize.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/query/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_docs.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/format.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/io_table.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_io/io_zarr.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_logging.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/config.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/datasets.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/io/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/models/_accessor.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/models/pyramids_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/testing.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/ngff_coordinate_system.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/ngff/ngff_transformations.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/operations.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/transformations/transformations.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/conftest.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/conftest.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_aggregations.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_map.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_rasterize.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/operations/test_vectorize.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/query/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/test_centroids.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/test_data_extent.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/test_deepcopy.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/test_get_attrs.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/core/test_validation.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/data/multipolygon.json +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/data/points.json +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/data/polygon.json +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/dataloader/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/dataloader/conftest.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/dataloader/test_datasets.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/datasets/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/datasets/test_datasets.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_attrs_io.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_format.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_metadata.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_partial_read.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/io/test_pyramids_performance.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/models/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/models/test_accessor.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/models/test_chunks_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/models/test_pyramids_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/test_init.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/ngff/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/ngff/conftest.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/ngff/test_ngff_coordinate_system.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/ngff/test_ngff_transformations.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/test_transformations.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/transformations/test_transformations_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/utils/__init__.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/utils/test_element_utils.py +0 -0
- {spatialdata-0.7.3a2 → spatialdata-0.8.0}/tests/utils/test_testing.py +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memray
|
|
3
|
+
description: Profile the memory usage of a Python script using memray and visualize a temporal flamegraph in the browser. Use when the user wants to investigate memory consumption, find leaks, or understand allocation patterns.
|
|
4
|
+
compatibility: Requires the pixi profiling environment (pixi run -e profiling). Supports Linux and macOS.
|
|
5
|
+
allowed-tools: Bash(pixi run -e profiling memray-run:*) Bash(pixi run -e profiling memray-flame:*) Bash(open:*) Bash(xdg-open:*) Bash(python -m webbrowser:*)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
1. Ask the user which script to profile (full or relative path).
|
|
11
|
+
|
|
12
|
+
2. Run the script under memray:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pixi run -e profiling memray-run script.py
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This produces a binary file named `memray-script.py.<pid>.bin` in the current directory.
|
|
19
|
+
|
|
20
|
+
3. Generate the flamegraph HTML report from the `.bin` file:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pixi run -e profiling memray-flame memray-script.py.<pid>.bin
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Replace `<pid>` with the actual PID shown in the filename. This writes `memray-flamegraph-script.py.<pid>.html`.
|
|
27
|
+
|
|
28
|
+
4. Open the report in the browser:
|
|
29
|
+
- macOS: `open memray-flamegraph-script.py.<pid>.html`
|
|
30
|
+
- Linux: `xdg-open memray-flamegraph-script.py.<pid>.html`
|
|
31
|
+
- Either: `python -m webbrowser memray-flamegraph-script.py.<pid>.html`
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
|
|
35
|
+
- The `--temporal` flag (included in `memray-flame`) shows memory over time, not just peak — use this to spot leaks and allocation bursts.
|
|
36
|
+
- To find the `.bin` file if unsure of the name: `ls memray-*.bin`
|
|
37
|
+
- To compare runs, save the previous report: `cp memray-flamegraph-script.py.<pid>.html memray-flamegraph-before.html`
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: profimp
|
|
3
|
+
description: Profile Python import time using profimp and open a waterfall HTML report. Use when investigating slow startup or wanting to identify which imports are most expensive.
|
|
4
|
+
compatibility: Requires profimp (available as a pixi dependency). macOS or Linux.
|
|
5
|
+
allowed-tools: Bash(profimp:*) Bash(open:*) Bash(xdg-open:*) Bash(python -m webbrowser:*)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
1. Ask what to profile. Suggest common patterns for this repo:
|
|
11
|
+
- `import spatialdata`
|
|
12
|
+
- `from spatialdata import SpatialData`
|
|
13
|
+
- `from spatialdata_io import xenium`
|
|
14
|
+
|
|
15
|
+
2. Run:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
profimp --html "<import_stmt>" > /tmp/profimp.html
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3. Open the report:
|
|
22
|
+
- macOS: `open /tmp/profimp.html`
|
|
23
|
+
- Linux: `xdg-open /tmp/profimp.html`
|
|
24
|
+
- Either: `python -m webbrowser /tmp/profimp.html`
|
|
25
|
+
|
|
26
|
+
## Notes
|
|
27
|
+
|
|
28
|
+
- The report is a waterfall chart showing every sub-import and its timing.
|
|
29
|
+
- To compare before/after: `cp /tmp/profimp.html /tmp/profimp-before.html` before re-running.
|
|
30
|
+
- `pixi run python -m profimp` also works if `profimp` is not on PATH.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pyspy
|
|
3
|
+
description: Profile the execution time of a Python script using py-spy and visualize the result with speedscope. Use when the user wants to benchmark performance, find slow code paths, or profile CPU time.
|
|
4
|
+
compatibility: Requires the pixi profiling environment (pixi run -e profiling). Speedscope must be installed separately (npm install -g speedscope). sudo is required on macOS.
|
|
5
|
+
allowed-tools: Bash(pixi run -e profiling pyspy:*) Bash(pixi run -e profiling speedscope:*) Bash(sudo pixi run -e profiling pyspy:*)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
1. Ask the user which script to profile (full or relative path).
|
|
11
|
+
|
|
12
|
+
2. Run py-spy to record the profile. The output is always written to `profile.speedscope.json` in the current directory.
|
|
13
|
+
|
|
14
|
+
**Linux** (no sudo needed):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pixi run -e profiling pyspy script.py
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**macOS** (sudo required — py-spy needs to attach to the process):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
sudo pixi run -e profiling pyspy script.py
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If sudo fails to find the pixi environment, use absolute paths:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
sudo /path/to/.pixi/envs/profiling/bin/py-spy record --gil \
|
|
30
|
+
-o profile.speedscope.json --format speedscope \
|
|
31
|
+
-- /path/to/.pixi/envs/profiling/bin/python script.py
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
3. Open the result in speedscope:
|
|
35
|
+
```bash
|
|
36
|
+
pixi run -e profiling speedscope
|
|
37
|
+
```
|
|
38
|
+
This opens `profile.speedscope.json` in the browser via the local speedscope CLI.
|
|
39
|
+
|
|
40
|
+
## Notes
|
|
41
|
+
|
|
42
|
+
- If the speedscope view is blank, switch threads using the thread selector in the top-right corner.
|
|
43
|
+
- To save a profile before overwriting: `cp profile.speedscope.json profile-before.speedscope.json`
|
|
44
|
+
- `--gil` records only time when the GIL is held (Python-level CPU time). Drop it to include C extension time.
|
|
45
|
+
- speedscope must be installed globally: `npm install -g speedscope`
|
|
@@ -19,11 +19,12 @@ jobs:
|
|
|
19
19
|
fail-fast: false
|
|
20
20
|
matrix:
|
|
21
21
|
include:
|
|
22
|
-
- {os: windows-latest, python: "3.
|
|
22
|
+
- {os: windows-latest, python: "3.12", dask-version: "2026.3.0", name: "min dask"}
|
|
23
23
|
- {os: windows-latest, python: "3.14", dask-version: "latest"}
|
|
24
|
-
- {os: ubuntu-latest, python: "3.
|
|
24
|
+
- {os: ubuntu-latest, python: "3.12", dask-version: "latest"}
|
|
25
|
+
- {os: ubuntu-latest, python: "3.13", dask-version: "latest"}
|
|
25
26
|
- {os: ubuntu-latest, python: "3.14", dask-version: "latest"}
|
|
26
|
-
- {os: macos-latest, python: "3.
|
|
27
|
+
- {os: macos-latest, python: "3.12", dask-version: "latest"}
|
|
27
28
|
- {os: macos-latest, python: "3.14", prerelease: "allow", name: "prerelease"}
|
|
28
29
|
env:
|
|
29
30
|
OS: ${{ matrix.os }}
|
|
@@ -41,7 +42,6 @@ jobs:
|
|
|
41
42
|
- name: Install dependencies
|
|
42
43
|
run: |
|
|
43
44
|
if [[ "${PRERELEASE}" == "allow" ]]; then
|
|
44
|
-
sed -i '' 's/requires-python.*//' pyproject.toml # otherwise uv complains that anndata requires python>=3.12 and we only do >=3.11 😱
|
|
45
45
|
uv add git+https://github.com/scverse/anndata.git
|
|
46
46
|
uv add pandas>=3.dev0
|
|
47
47
|
fi
|
|
@@ -46,11 +46,18 @@ spatialdata-sandbox
|
|
|
46
46
|
# version file
|
|
47
47
|
_version.py
|
|
48
48
|
|
|
49
|
-
#
|
|
50
|
-
|
|
49
|
+
# agents configurations
|
|
50
|
+
.claude/settings.local.json
|
|
51
51
|
|
|
52
|
+
# benchmarking and profiling
|
|
52
53
|
.asv/
|
|
54
|
+
profile.speedscope.json
|
|
55
|
+
|
|
56
|
+
# other
|
|
57
|
+
node_modules/
|
|
53
58
|
|
|
54
59
|
.mypy_cache
|
|
55
60
|
.ruff_cache
|
|
56
61
|
uv.lock
|
|
62
|
+
pixi.lock
|
|
63
|
+
|
|
@@ -9,18 +9,18 @@ ci:
|
|
|
9
9
|
skip: []
|
|
10
10
|
repos:
|
|
11
11
|
- repo: https://github.com/rbubley/mirrors-prettier
|
|
12
|
-
rev: v3.8.
|
|
12
|
+
rev: v3.8.4
|
|
13
13
|
hooks:
|
|
14
14
|
- id: prettier
|
|
15
15
|
exclude: ^.github/workflows/test.yaml
|
|
16
16
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
17
|
-
rev:
|
|
17
|
+
rev: v2.1.0
|
|
18
18
|
hooks:
|
|
19
19
|
- id: mypy
|
|
20
20
|
additional_dependencies: [numpy, types-requests]
|
|
21
21
|
exclude: tests/|docs/
|
|
22
22
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
23
|
-
rev: v0.15.
|
|
23
|
+
rev: v0.15.18
|
|
24
24
|
hooks:
|
|
25
25
|
- id: ruff
|
|
26
26
|
args: [--fix, --exit-non-zero-on-fix]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Spatial data format.
|
|
5
5
|
Project-URL: Documentation, https://spatialdata.scverse.org/en/latest
|
|
6
6
|
Project-URL: Source, https://github.com/scverse/spatialdata.git
|
|
@@ -37,21 +37,21 @@ License: BSD 3-Clause License
|
|
|
37
37
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
38
38
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
39
|
License-File: LICENSE
|
|
40
|
-
Requires-Python: >=3.
|
|
40
|
+
Requires-Python: >=3.12
|
|
41
41
|
Requires-Dist: anndata>=0.9.1
|
|
42
42
|
Requires-Dist: annsel>=0.1.2
|
|
43
43
|
Requires-Dist: click
|
|
44
44
|
Requires-Dist: dask-image
|
|
45
|
-
Requires-Dist: dask
|
|
45
|
+
Requires-Dist: dask>=2026.3.0
|
|
46
46
|
Requires-Dist: datashader
|
|
47
|
-
Requires-Dist: distributed
|
|
47
|
+
Requires-Dist: distributed>=2026.3.0
|
|
48
48
|
Requires-Dist: fsspec[http,s3]
|
|
49
49
|
Requires-Dist: geopandas>=0.14
|
|
50
50
|
Requires-Dist: multiscale-spatial-image==2.0.3
|
|
51
51
|
Requires-Dist: networkx
|
|
52
52
|
Requires-Dist: numba>=0.55.0
|
|
53
53
|
Requires-Dist: numpy
|
|
54
|
-
Requires-Dist: ome-zarr>=0.
|
|
54
|
+
Requires-Dist: ome-zarr>=0.16.0
|
|
55
55
|
Requires-Dist: pandas
|
|
56
56
|
Requires-Dist: pooch
|
|
57
57
|
Requires-Dist: pyarrow
|
|
@@ -82,7 +82,7 @@ Description-Content-Type: text/markdown
|
|
|
82
82
|
[](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)
|
|
83
83
|
[](https://codecov.io/gh/scverse/spatialdata)
|
|
84
84
|
[](https://spatialdata.scverse.org/en/latest/)
|
|
85
|
-
[](https://doi.org/10.5281/zenodo.20056407)
|
|
86
86
|
[](https://pepy.tech/project/spatialdata)
|
|
87
87
|
[](https://github.com/scverse/spatialdata/actions/workflows/release.yaml)
|
|
88
88
|
[![Documentation][badge-pypi]][link-pypi]
|
|
@@ -91,11 +91,15 @@ Description-Content-Type: text/markdown
|
|
|
91
91
|
[badge-pypi]: https://badge.fury.io/py/spatialdata.svg
|
|
92
92
|
[link-pypi]: https://pypi.org/project/spatialdata/
|
|
93
93
|
|
|
94
|
-
SpatialData is a data framework
|
|
94
|
+
SpatialData is a data framework, a schema and a serialization format for uni- and multi-modal spatial omics datasets. It is supported by a growing suite of software libraries for performant access, manipulation and analysis in multiple languages, including Python, R and JavaScript.
|
|
95
|
+
|
|
96
|
+
This repository contains the core spatialdata library. See the links below to learn more about other packages in the SpatialData ecosystem.
|
|
95
97
|
|
|
96
98
|
- [spatialdata-io](https://github.com/scverse/spatialdata-io): load data from common spatial omics technologies into spatialdata.
|
|
97
99
|
- [spatialdata-plot](https://github.com/scverse/spatialdata-plot): Static plotting library for spatialdata.
|
|
98
100
|
- [napari-spatialdata](https://github.com/scverse/napari-spatialdata): napari plugin for interactive exploration and annotation of spatial data.
|
|
101
|
+
- [SpatialData (R)](https://github.com/HelenaLC/SpatialData): R implementation of the SpatialData framework.
|
|
102
|
+
- [SpatialData.js](https://github.com/Taylor-CCB-Group/SpatialData.js): JavaScript/TypeScript implementation of the SpatialData framework.
|
|
99
103
|
|
|
100
104
|
[//]: # "numfocus-fiscal-sponsor-attribution"
|
|
101
105
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)
|
|
7
7
|
[](https://codecov.io/gh/scverse/spatialdata)
|
|
8
8
|
[](https://spatialdata.scverse.org/en/latest/)
|
|
9
|
-
[](https://doi.org/10.5281/zenodo.20056407)
|
|
10
10
|
[](https://pepy.tech/project/spatialdata)
|
|
11
11
|
[](https://github.com/scverse/spatialdata/actions/workflows/release.yaml)
|
|
12
12
|
[![Documentation][badge-pypi]][link-pypi]
|
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
[badge-pypi]: https://badge.fury.io/py/spatialdata.svg
|
|
16
16
|
[link-pypi]: https://pypi.org/project/spatialdata/
|
|
17
17
|
|
|
18
|
-
SpatialData is a data framework
|
|
18
|
+
SpatialData is a data framework, a schema and a serialization format for uni- and multi-modal spatial omics datasets. It is supported by a growing suite of software libraries for performant access, manipulation and analysis in multiple languages, including Python, R and JavaScript.
|
|
19
|
+
|
|
20
|
+
This repository contains the core spatialdata library. See the links below to learn more about other packages in the SpatialData ecosystem.
|
|
19
21
|
|
|
20
22
|
- [spatialdata-io](https://github.com/scverse/spatialdata-io): load data from common spatial omics technologies into spatialdata.
|
|
21
23
|
- [spatialdata-plot](https://github.com/scverse/spatialdata-plot): Static plotting library for spatialdata.
|
|
22
24
|
- [napari-spatialdata](https://github.com/scverse/napari-spatialdata): napari plugin for interactive exploration and annotation of spatial data.
|
|
25
|
+
- [SpatialData (R)](https://github.com/HelenaLC/SpatialData): R implementation of the SpatialData framework.
|
|
26
|
+
- [SpatialData.js](https://github.com/Taylor-CCB-Group/SpatialData.js): JavaScript/TypeScript implementation of the SpatialData framework.
|
|
23
27
|
|
|
24
28
|
[//]: # "numfocus-fiscal-sponsor-attribution"
|
|
25
29
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.8.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 8, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -65,6 +65,32 @@ git checkout - && git stash pop
|
|
|
65
65
|
asv compare main HEAD
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
### Dataloader benchmarks
|
|
69
|
+
|
|
70
|
+
Dataloader benchmarks live in `benchmarks/benchmark_dataloader.py`. They use a synthetic in-memory `SpatialData` (2048×2048 image, 500 circle regions) and compute two metrics:
|
|
71
|
+
|
|
72
|
+
- `time_init` — constructing `ImageTilesDataset` (includes bounding-box pre-computation).
|
|
73
|
+
- `time_fetch` — iterating over all 500 tiles once (pure `__getitem__` calls, no `DataLoader` overhead).
|
|
74
|
+
|
|
75
|
+
Run both in your current environment:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
asv run --python=same --show-stderr -b TimeDataloader
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or a single method:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
asv run --python=same --show-stderr -b TimeDataloader.time_init
|
|
85
|
+
asv run --python=same --show-stderr -b TimeDataloader.time_fetch
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Compare against `main` in one shot:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
asv continuous --show-stderr -v -b TimeDataloader main HEAD
|
|
92
|
+
```
|
|
93
|
+
|
|
68
94
|
### Querying benchmarks
|
|
69
95
|
|
|
70
96
|
Querying using a bounding box without a spatial index is highly impacted by large amounts of points (transcripts), more than table rows (cells).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
"""Benchmarks for ImageTilesDataset: init time and iteration time."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import anndata as ad
|
|
7
|
+
import geopandas as gpd
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from shapely.geometry import Point
|
|
11
|
+
|
|
12
|
+
import spatialdata as sd
|
|
13
|
+
from spatialdata.dataloader import ImageTilesDataset
|
|
14
|
+
from spatialdata.models import Image2DModel, ShapesModel, TableModel
|
|
15
|
+
from spatialdata.transformations import Identity
|
|
16
|
+
|
|
17
|
+
_RNG = np.random.default_rng(42)
|
|
18
|
+
|
|
19
|
+
_IMAGE_SIZE = 2048
|
|
20
|
+
_N_CIRCLES = 500
|
|
21
|
+
_N_CHANNELS = 3
|
|
22
|
+
|
|
23
|
+
_DATASET_KWARGS = {
|
|
24
|
+
"regions_to_images": {"circles": "image"},
|
|
25
|
+
"regions_to_coordinate_systems": {"circles": "global"},
|
|
26
|
+
"table_name": "table",
|
|
27
|
+
"return_annotations": "instance_id",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _make_sdata() -> sd.SpatialData:
|
|
32
|
+
img_data = _RNG.integers(0, 256, size=(_N_CHANNELS, _IMAGE_SIZE, _IMAGE_SIZE), dtype=np.uint8).astype(np.float32)
|
|
33
|
+
image = Image2DModel.parse(img_data, dims=["c", "y", "x"], transformations={"global": Identity()})
|
|
34
|
+
|
|
35
|
+
radius = 32.0
|
|
36
|
+
cx = _RNG.uniform(radius, _IMAGE_SIZE - radius, size=_N_CIRCLES)
|
|
37
|
+
cy = _RNG.uniform(radius, _IMAGE_SIZE - radius, size=_N_CIRCLES)
|
|
38
|
+
geom = gpd.GeoDataFrame({"geometry": [Point(x, y) for x, y in zip(cx, cy, strict=True)]})
|
|
39
|
+
geom["radius"] = radius
|
|
40
|
+
circles = ShapesModel.parse(geom, transformations={"global": Identity()})
|
|
41
|
+
|
|
42
|
+
table = ad.AnnData(
|
|
43
|
+
_RNG.random((_N_CIRCLES, 10)).astype(np.float32),
|
|
44
|
+
obs=pd.DataFrame(
|
|
45
|
+
{
|
|
46
|
+
"region": pd.Categorical(["circles"] * _N_CIRCLES),
|
|
47
|
+
"instance_id": np.arange(_N_CIRCLES, dtype=np.int64),
|
|
48
|
+
},
|
|
49
|
+
index=[str(i) for i in range(_N_CIRCLES)],
|
|
50
|
+
),
|
|
51
|
+
)
|
|
52
|
+
table = TableModel.parse(table, region="circles", region_key="region", instance_key="instance_id")
|
|
53
|
+
|
|
54
|
+
return sd.SpatialData(images={"image": image}, shapes={"circles": circles}, tables={"table": table})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TimeDataloader:
|
|
58
|
+
"""Time ImageTilesDataset construction and tile iteration."""
|
|
59
|
+
|
|
60
|
+
def setup(self):
|
|
61
|
+
self.sdata = _make_sdata()
|
|
62
|
+
self.ds = ImageTilesDataset(sdata=self.sdata, **_DATASET_KWARGS)
|
|
63
|
+
|
|
64
|
+
def teardown(self):
|
|
65
|
+
del self.ds
|
|
66
|
+
del self.sdata
|
|
67
|
+
|
|
68
|
+
def time_init(self):
|
|
69
|
+
"""Time constructing ImageTilesDataset (bounding-box pre-computation)."""
|
|
70
|
+
ImageTilesDataset(sdata=self.sdata, **_DATASET_KWARGS)
|
|
71
|
+
|
|
72
|
+
def time_fetch(self):
|
|
73
|
+
"""Time iterating over every tile once."""
|
|
74
|
+
for i in range(len(self.ds)):
|
|
75
|
+
_ = self.ds[i]
|
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
# An open and universal framework for processing spatial omics data.
|
|
8
8
|
|
|
9
|
-
SpatialData is a data framework
|
|
9
|
+
SpatialData is a data framework, a schema and a serialization format for uni- and multi-modal spatial omics datasets. It is supported by a growing suite of software libraries for performant access, manipulation and analysis in multiple languages, including Python, R and JavaScript.
|
|
10
|
+
|
|
11
|
+
This page provides documentation on how to install, use, and extend the core `spatialdata` library. See the links below to learn more about other packages in the SpatialData ecosystem.
|
|
10
12
|
|
|
11
13
|
- `spatialdata-io`: load data from common spatial omics technologies into `spatialdata` ([repository][spatialdata-io-repo], [documentation][spatialdata-io-docs]).
|
|
12
14
|
- `spatialdata-plot`: Static plotting library for `spatialdata` ([repository][spatialdata-plot-repo], [documentation][spatialdata-plot-docs]).
|
|
13
15
|
- `napari-spatialdata-repo`: napari plugin for interactive exploration and annotation of `spatialdata` ([repository][napari-spatialdata-repo], [documentation][napari-spatialdata-docs]).
|
|
16
|
+
- `SpatialData` (R): R implementation of the SpatialData framework ([repository][spatialdata-r-repo]).
|
|
17
|
+
- `SpatialData.js`: JavaScript/TypeScript implementation of the SpatialData framework ([repository][spatialdata-js-repo]).
|
|
14
18
|
|
|
15
19
|
Please see our publication {cite}`marconatoSpatialDataOpenUniversal2024` for citation and to learn more.
|
|
16
20
|
|
|
@@ -119,3 +123,5 @@ references.md
|
|
|
119
123
|
[napari-spatialdata-docs]: https://spatialdata.scverse.org/projects/napari/en/stable/notebooks/spatialdata.html
|
|
120
124
|
[spatialdata-io-docs]: https://spatialdata.scverse.org/projects/io/en/stable/
|
|
121
125
|
[spatialdata-plot-docs]: https://spatialdata.scverse.org/projects/plot/en/stable/api.html
|
|
126
|
+
[spatialdata-r-repo]: https://github.com/HelenaLC/SpatialData
|
|
127
|
+
[spatialdata-js-repo]: https://github.com/Taylor-CCB-Group/SpatialData.js
|
|
@@ -15,7 +15,7 @@ maintainers = [
|
|
|
15
15
|
urls.Documentation = "https://spatialdata.scverse.org/en/latest"
|
|
16
16
|
urls.Source = "https://github.com/scverse/spatialdata.git"
|
|
17
17
|
urls.Home-page = "https://github.com/scverse/spatialdata.git"
|
|
18
|
-
requires-python = ">=3.
|
|
18
|
+
requires-python = ">=3.12"
|
|
19
19
|
dynamic= [
|
|
20
20
|
"version" # allow version to be set by git tags
|
|
21
21
|
]
|
|
@@ -26,8 +26,8 @@ dependencies = [
|
|
|
26
26
|
"annsel>=0.1.2",
|
|
27
27
|
"click",
|
|
28
28
|
"dask-image",
|
|
29
|
-
"dask>=
|
|
30
|
-
"distributed
|
|
29
|
+
"dask>=2026.3.0",
|
|
30
|
+
"distributed>=2026.3.0",
|
|
31
31
|
"datashader",
|
|
32
32
|
"fsspec[s3,http]",
|
|
33
33
|
"geopandas>=0.14",
|
|
@@ -35,7 +35,7 @@ dependencies = [
|
|
|
35
35
|
"networkx",
|
|
36
36
|
"numba>=0.55.0",
|
|
37
37
|
"numpy",
|
|
38
|
-
"ome_zarr>=0.
|
|
38
|
+
"ome_zarr>=0.16.0",
|
|
39
39
|
"pandas",
|
|
40
40
|
"pooch",
|
|
41
41
|
"pyarrow",
|
|
@@ -145,7 +145,7 @@ exclude = [
|
|
|
145
145
|
|
|
146
146
|
]
|
|
147
147
|
line-length = 120
|
|
148
|
-
target-version = "
|
|
148
|
+
target-version = "py312"
|
|
149
149
|
|
|
150
150
|
[tool.ruff.lint]
|
|
151
151
|
ignore = [
|
|
@@ -210,3 +210,37 @@ convention = "numpy"
|
|
|
210
210
|
|
|
211
211
|
# pyupgrade typing rewrite TODO: remove at some point from per-file ignore
|
|
212
212
|
# "UP006", "UP007"
|
|
213
|
+
|
|
214
|
+
[tool.pixi.workspace]
|
|
215
|
+
channels = ["conda-forge"]
|
|
216
|
+
platforms = ["osx-arm64", "linux-64"]
|
|
217
|
+
|
|
218
|
+
[tool.pixi.dependencies]
|
|
219
|
+
proj = "*"
|
|
220
|
+
|
|
221
|
+
[tool.pixi.tasks]
|
|
222
|
+
test = "pytest"
|
|
223
|
+
test-parallel = "pytest -n auto --dist worksteal"
|
|
224
|
+
pre-commit = "pre-commit run"
|
|
225
|
+
pre-commit-all = "pre-commit run --all-files"
|
|
226
|
+
|
|
227
|
+
[tool.pixi.feature.profiling.dependencies]
|
|
228
|
+
py-spy = "*"
|
|
229
|
+
|
|
230
|
+
[tool.pixi.feature.profiling.pypi-dependencies]
|
|
231
|
+
spatialdata = { path = ".", editable = true }
|
|
232
|
+
profimp = "*"
|
|
233
|
+
memray = "*"
|
|
234
|
+
|
|
235
|
+
[tool.pixi.feature.profiling.tasks]
|
|
236
|
+
# Usage: pixi run -e profiling pyspy script.py (prefix with sudo on macOS)
|
|
237
|
+
pyspy = "py-spy record --gil -o profile.speedscope.json --format speedscope -- python"
|
|
238
|
+
# Usage: pixi run -e profiling speedscope
|
|
239
|
+
speedscope = "npx --yes speedscope profile.speedscope.json"
|
|
240
|
+
# Usage: pixi run -e profiling memray-run script.py
|
|
241
|
+
memray-run = "memray run"
|
|
242
|
+
# Usage: pixi run -e profiling memray-flame memray-script.py.<pid>.bin
|
|
243
|
+
memray-flame = "memray flamegraph --temporal"
|
|
244
|
+
|
|
245
|
+
[tool.pixi.environments]
|
|
246
|
+
profiling = { features = ["profiling"], solve-group = "default" }
|
|
@@ -158,9 +158,9 @@ def sanitize_table(data: AnnData, inplace: bool = True) -> AnnData | None:
|
|
|
158
158
|
# Handle other attributes
|
|
159
159
|
for attr in ("obsm", "obsp", "varm", "varp", "uns", "layers"):
|
|
160
160
|
d = getattr(sanitized, attr)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
new_dict = {new_keys[old]: value for old, value in d.items()}
|
|
161
|
+
# None is a valid key in layers (anndata >= 0.13: represents X); skip sanitizing it
|
|
162
|
+
new_keys = {old: get_unique_name(old, attr) for old in d if old is not None}
|
|
163
|
+
new_dict = {(new_keys[old] if old is not None else old): value for old, value in d.items()}
|
|
164
164
|
setattr(sanitized, attr, new_dict)
|
|
165
165
|
|
|
166
166
|
return None if inplace else sanitized
|
|
@@ -219,14 +219,6 @@ def concatenate(
|
|
|
219
219
|
return sdata
|
|
220
220
|
|
|
221
221
|
|
|
222
|
-
def _filter_table_in_coordinate_systems(table: AnnData, coordinate_systems: list[str]) -> AnnData:
|
|
223
|
-
table_mapping_metadata = table.uns[TableModel.ATTRS_KEY]
|
|
224
|
-
region_key = table_mapping_metadata[TableModel.REGION_KEY_KEY]
|
|
225
|
-
new_table = table[table.obs[region_key].isin(coordinate_systems)].copy()
|
|
226
|
-
new_table.uns[TableModel.ATTRS_KEY][TableModel.REGION_KEY] = new_table.obs[region_key].unique().tolist()
|
|
227
|
-
return new_table
|
|
228
|
-
|
|
229
|
-
|
|
230
222
|
def _fix_ensure_unique_element_names(
|
|
231
223
|
sdatas: dict[str, SpatialData],
|
|
232
224
|
rename_tables: bool,
|
{spatialdata-0.7.3a2 → spatialdata-0.8.0}/src/spatialdata/_core/operations/rasterize_bins.py
RENAMED
|
@@ -87,14 +87,10 @@ def rasterize_bins(
|
|
|
87
87
|
table = sdata.tables[table_name]
|
|
88
88
|
if not isinstance(element, GeoDataFrame | DaskDataFrame | DataArray):
|
|
89
89
|
raise ValueError("The bins should be a GeoDataFrame, a DaskDataFrame or a DataArray.")
|
|
90
|
-
if isinstance(element, DataArray):
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
f" 'c': {element.dims}."
|
|
95
|
-
)
|
|
96
|
-
if not np.issubdtype(element.dtype, np.integer):
|
|
97
|
-
raise ValueError(f"If bins is a DataArray, it should hold integers. Found dtype {element.dtype}.")
|
|
90
|
+
if isinstance(element, DataArray) and "c" in element.dims:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"If bins is a DataArray, it should hold labels; found a image element instead, with 'c': {element.dims}."
|
|
93
|
+
)
|
|
98
94
|
|
|
99
95
|
_, region_key, instance_key = get_table_keys(table)
|
|
100
96
|
if not table.obs[region_key].dtype == "category":
|
|
@@ -6,10 +6,11 @@ import warnings
|
|
|
6
6
|
from functools import singledispatch
|
|
7
7
|
from typing import TYPE_CHECKING, Any, cast
|
|
8
8
|
|
|
9
|
+
import dask
|
|
9
10
|
import dask.array as da
|
|
11
|
+
import dask.dataframe as dd
|
|
10
12
|
import dask_image.ndinterp
|
|
11
13
|
import numpy as np
|
|
12
|
-
import pandas as pd
|
|
13
14
|
from dask.array.core import Array as DaskArray
|
|
14
15
|
from dask.dataframe import DataFrame as DaskDataFrame
|
|
15
16
|
from geopandas import GeoDataFrame
|
|
@@ -442,20 +443,26 @@ def _(
|
|
|
442
443
|
axes = get_axes_names(data)
|
|
443
444
|
arrays = []
|
|
444
445
|
|
|
445
|
-
#
|
|
446
|
+
# Dask's expression optimizer can collapse partitions at compute time, making the partition
|
|
447
|
+
# structure inside vs. outside a disable_dask_tune_optimization() context inconsistent. To avoid
|
|
448
|
+
# index-alignment failures (e.g. "cannot reindex on an axis with duplicate labels" from parquet
|
|
449
|
+
# files that each start their index at 0) and length-mismatch errors, we materialise the non-axis
|
|
450
|
+
# columns and compute the axis arrays inside a single context where the partition structure is
|
|
451
|
+
# stable, then do plain pandas operations and re-wrap with dd.from_delayed (not dd.from_pandas,
|
|
452
|
+
# which sorts by index and would scramble rows for non-monotonic or duplicate indices).
|
|
446
453
|
with disable_dask_tune_optimization() if data.npartitions > 1 else contextlib.nullcontext():
|
|
454
|
+
lengths = [len(part) for part in data.partitions]
|
|
447
455
|
for ax in axes:
|
|
448
456
|
# TODO We have to pass on the lengths explicitly as automatic determination with dask graph optimization
|
|
449
|
-
# leads to
|
|
457
|
+
# leads to collapse of the partitions. However this causes a missing dependency problem, which for now is
|
|
450
458
|
# prevented by setting the optimization to False when performing this operation.
|
|
451
|
-
arrays.append(data[ax].to_dask_array(lengths=
|
|
459
|
+
arrays.append(data[ax].to_dask_array(lengths=lengths).reshape(-1, 1))
|
|
452
460
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
transformed.attrs[TRANSFORM_KEY] = default_cs
|
|
461
|
+
xdata = DataArray(da.concatenate(arrays, axis=1), coords={"points": range(sum(lengths)), "dim": list(axes)})
|
|
462
|
+
xtransformed = transformation._transform_coordinates(xdata)
|
|
463
|
+
|
|
464
|
+
# Compute non-axis columns while the partition structure is still stable; preserves original index.
|
|
465
|
+
transformed_pd = data.drop(columns=list(axes)).compute()
|
|
459
466
|
|
|
460
467
|
for ax in axes:
|
|
461
468
|
indices = xtransformed["dim"] == ax
|
|
@@ -463,8 +470,24 @@ def _(
|
|
|
463
470
|
# TODO: discuss with dask team
|
|
464
471
|
# This is not nice, but otherwise there is a problem with the joint graph of new_ax and transformed, causing
|
|
465
472
|
# a getattr missing dependency of dependent from_dask_array.
|
|
466
|
-
|
|
467
|
-
|
|
473
|
+
# Assigning a numpy array is positional (no index alignment), so the original index is preserved.
|
|
474
|
+
transformed_pd[ax] = new_ax.data.flatten().compute()
|
|
475
|
+
|
|
476
|
+
# Reconstruct as a dask DataFrame via delayed partitions so that:
|
|
477
|
+
# (a) row order matches the original (dd.from_pandas sorts by index, which scrambles rows for
|
|
478
|
+
# non-monotonic or duplicate indices such as those produced by multi-file parquet reads), and
|
|
479
|
+
# (b) the original index is preserved exactly.
|
|
480
|
+
offsets = np.cumsum([0] + lengths)
|
|
481
|
+
delayed_parts = [dask.delayed(transformed_pd.iloc[offsets[i] : offsets[i + 1]]) for i in range(len(lengths))]
|
|
482
|
+
transformed = dd.from_delayed(delayed_parts, meta=transformed_pd.iloc[:0])
|
|
483
|
+
# Preserve spatialdata_attrs (feature_key, instance_key, …) from the original element;
|
|
484
|
+
# dd.from_delayed starts with empty attrs so we must copy them explicitly.
|
|
485
|
+
for k, v in data.attrs.items():
|
|
486
|
+
if k != TRANSFORM_KEY:
|
|
487
|
+
transformed.attrs[k] = v
|
|
488
|
+
# dummy transformation that will be replaced by _adjust_transformation()
|
|
489
|
+
default_cs = {DEFAULT_COORDINATE_SYSTEM: Identity()}
|
|
490
|
+
transformed.attrs[TRANSFORM_KEY] = default_cs
|
|
468
491
|
|
|
469
492
|
old_transformations = cast(dict[str, Any], get_transformation(data, get_all=True))
|
|
470
493
|
|