diff --git a/STATUS.md b/STATUS.md index 6a4b476..ed5e471 100644 --- a/STATUS.md +++ b/STATUS.md @@ -7,6 +7,7 @@ moves; the plan of record for the current item is in the section below. | Version | What | Issues | |---|---|---| +| 0.17.0 | `convert` reads Visium (Space Ranger 1–4) and Xenium (XOA 1–4) with positions, images and Xenium's analysis | #79 | | 0.16.2 | Documentation site (guides, troubleshooting, a page per format); `export image` writes PNG to stdout without `-o` | #76, #77 | | 0.16.1 | `subset`, `split` and the MuData export narrow categoricals (and their colours) as anndata's subsetting does | #74 | | 0.16.0 | MuData `.h5mu` read (each modality, global parts on its cells) and written (split by feature type); copies of anndata 0.7 dataframes readable | #31, #72 | @@ -19,29 +20,8 @@ The conversion epic's sub-issues (#21–#30) are all done and ticked in #20. ## In progress -### Spatial import: standard Visium and Xenium (`feat/spatial`, for 0.17.0) - -Decisions: -- Standard Visium (Space Ranger 1–4) and Xenium (XOA 1–4) first, with no new - dependency: both come as CSV, JSON and PNG. Visium HD, whose positions are - Parquet only, waits for a `spatial` extra with pyarrow. -- Laid out as squidpy (Visium) and spatialdata-io's table (Xenium) lay - them out; images stay the PNGs' uint8 pixels. Xenium brings its own - clusters, UMAP and PCA, read from `analysis.tar.gz` as it ships. -- A table beside the matrix is matched to its barcodes while scanned - (`conversion/spatial.py`), and written straight through when in the - matrix's order (Xenium's cells, on every real bundle checked) or through - `core/scatter.py` when not. - -Steps: -- [x] `table.py`: headerless files, `on_index`, reusable columns -- [x] `core/scatter.py` -- [x] Visium: `visium.py`, flags `--library-id`, `--no-images`, `--unfiltered` -- [x] Xenium: `xenium.py`, analysis from the folder or the archive -- [x] Performance guards; squidpy and 10x-recipe comparisons; `import-xenium` benchmark -- [x] Real data by hand: Visium SR 1.1 and SR 2.1, Xenium XOA 3.0 and 4.0 tiny -- [x] Docs: two format pages, tables, guide, Troubleshooting, Developers -- [ ] PR, review, merge; release 0.17.0 +Nothing. Spatial (standard Visium and Xenium) is out in 0.17.0; Visium HD +is first among the follow-ups below. ## Follow-ups noticed diff --git a/docs/changelog.md b/docs/changelog.md index c857c49..230013f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,53 @@ nav_order: 7 Notable changes to `adata-cli`. Versions are `MAJOR.MINOR.PATCH`; tags carry no `v` prefix. +## 0.17.0 + +`convert` reads 10x's spatial outputs with their coordinates: a Space +Ranger folder for standard Visium, with its spot positions, tissue images +and scale factors where squidpy puts them, and a Xenium bundle, with its +cell table, centroids and Xenium's own clusters, UMAP and PCA. No new +dependency; Visium HD, whose positions are Parquet only, comes later. + +### Added + +- **Visium (Space Ranger 1–4)** + ([#79](https://github.com/cellgeni/adata-cli/pull/79)). `adata convert + outs -o section.h5ad` gives what `squidpy.read.visium` gives, checked + against it in CI: obs `in_tissue`, `array_row`, `array_col`; + `obsm["spatial"]` in full-resolution pixels; `uns["spatial"][library]` + with the images (uint8, as the PNGs hold them), the scale factors and the + metadata. + - New flags `--library-id` and `--no-images`; `--unfiltered` reads + `raw_feature_bc_matrix.h5`, as it reads Parse's unfiltered matrix. + - Space Ranger 1's headerless `tissue_positions_list.csv`, decimal + positions, and 10x's downloads named with their sample are read. + - Refused before anything is written: a barcode with no position, aggr + output, Visium HD, and a folder whose matrix is Matrix Market only. +- **Xenium (XOA 1–4)** + ([#79](https://github.com/cellgeni/adata-cli/pull/79)). `adata convert + bundle -o cells.h5ad` puts `cells.csv.gz`'s columns in obs and the + centroids in `obsm["spatial"]` (µm), and Xenium's graph-based clusters, + UMAP and PCA in `obs["graphclust"]`, `obsm["X_umap"]` and `obsm["X_pca"]`, + read from `analysis.tar.gz` as Xenium ships it. Checked against 10x's + scanpy recipe in CI. + - The cell table is matched to the matrix while it is scanned and + written straight through, as Xenium writes both in one order; a table + in another order goes through scratch buckets (`core/scatter.py`), so + memory stays at a block either way. +- **An `import-xenium` benchmark case**, against 10x's scanpy recipe. + +### Fixed + +- **A Space Ranger `outs/` folder was refused** + ([#79](https://github.com/cellgeni/adata-cli/pull/79)) as a Cell Ranger 2 + MTX layout ("pick one with --genome"): its two MEX folders were taken for + genomes. A Xenium bundle likewise. +- **A CSV saved with a byte order mark**, as Excel saves one, kept the + mark in its first column's name, so a table looked up by that name was + refused as lacking it, or a Visium positions file taken for one with no + header. + ## 0.16.2 `export image` writes to stdout without `--output`, as every other export diff --git a/pyproject.toml b/pyproject.toml index cf18775..1cb4926 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ # so the distribution is published as `pyadata-cli`. The import package # and the command are both still `adata`. name = "pyadata-cli" -version = "0.16.2" +version = "0.17.0" description = "Streaming CLI for exploring and editing large AnnData .h5ad and .zarr stores" readme = "README.md" requires-python = ">=3.12" diff --git a/uv.lock b/uv.lock index 3ac2367..2e0a7fd 100644 --- a/uv.lock +++ b/uv.lock @@ -486,7 +486,7 @@ wheels = [ [[package]] name = "pyadata-cli" -version = "0.16.2" +version = "0.17.0" source = { editable = "." } dependencies = [ { name = "h5py" },