Skip to content

Releases: GenomicAI/truecell

truecell 1.1.0

Choose a tag to compare

@shanikawm shanikawm released this 07 Aug 21:02
0e891b5

Fourteen pull requests since 1.0.0. Backwards compatible — nothing removed or
renamed — so the additions are safe, and the reasons to upgrade are the
corrections
.

Fixed — these are live in 1.0.0 today

The categorical palette repeated colours, so two clusters could render identically #86
vln_plot drew the wrong violin three ways — bandwidth, trim, and points #89
aggregate_expression(return_object=True) left raw sums in data where Seurat log-normalizes (14 against 6.98) #93
_get_expression_matrix returned the wrong layer when asked for scale.data, and labelled the right one with the wrong features #93
The pbmc3k annotated UMAP captioned the platelet cluster "DC" #98
feature_plot alone rasterised its points, and did so unconditionally #86

If you run pseudobulk, read a violin, or trust a cluster colour, 1.0.0 is giving
you something wrong.

Added

  • average_expression, mirroring Seurat's AverageExpression — the
    back-transformed per-group mean, distinct from aggregate_expression's sums.
  • find_clusters accepts several resolutions and writes a
    {graph}_res.{r} column per resolution, as FindClusters does. Choosing a
    resolution means comparing a few.
  • split_by on dim_plot, feature_plot and vln_plot.
  • A theme layerset_theme, theme_context, get_theme, reset_theme.
  • ridge_plot warns when an explicit figsize cannot fit the groups.

Performance

find_markers computes its min_pct and logfc_threshold masks on the sparse
matrix and densifies only the survivors, instead of building a dense
genes × cells array per group first. Marker tables are byte-identical:

dataset (clusters) time peak memory
ifnb (15) 25.37s → 8.03s 9420 MB → 3580 MB
pbmc8k (9) 12.71s → 6.32s 9253 MB → 2406 MB
thp1 (7) 45.52s → 23.87s 10620 MB → 9428 MB

A cross-language benchmark suite against R Seurat landed alongside it —
tutorials/benchmark/PERFORMANCE.md.

Verification

  • add_module_score is now verified against Seurat as an equality, not a
    correlation.
    At nbin=1 with ctrl = pool size, sample(n, n) is a
    permutation, so the control set is forced and the two tools agree to
    6.66e-15 across 20,729 cells — against 1.8e-01 between two R seeds.
  • The guided tutorial scans four resolutions, each scored against Seurat.
    Cluster counts match exactly at 0.4, 0.8 and 1.2; the 8-vs-9 split is specific
    to 0.5.
  • CI now runs the PBMC 3k tutorials against real data, with a skip counted
    as a failure. The 1.0.0 mislabelling above shipped under a guard that was
    correct and had never executed.
  • Float64 round-trips fixed across every tutorial's R↔Python handoff.

Install

pip install --upgrade truecell

Full detail in CHANGELOG.md.

truecell 1.0.0 — the rename release

Choose a tag to compare

@shanikawm shanikawm released this 30 Jul 16:08
25d9180

shanuz is now truecell. Same codebase, renamed and re-verified — the API is the one 0.9.0 shipped. The version moves to 1.0.0 rather than continuing the 0.x line because neither neighbouring number was free on PyPI: shanuz 0.9.0 is the last release under the old name, and truecell 0.9.0 was published from the same code before the rename settled. It is not a maturity claim.

pip install truecell

⚠️ Breaking: there is no compatibility shim

import shanuz will not work, and the top-level class is Truecell, not Shanuz. Every import, skill directory, documentation page and logo asset moved with the name.

shanuz stays on PyPI at 0.9.0 and is not being withdrawn, but it will receive no further releases.

The GitHub repository was renamed in place rather than forked, so stars, forks, issues and pull requests carried over and the old URL redirects — including for git clone and pip install git+…. Existing clones keep working through that redirect, though git remote set-url is worth running.

Fixed

The sdist shipped the tutorial data — and a different amount of it every time. docs/tutorials is a symlink to tutorials/, hatchling follows it, and the tutorials write their intermediates into the directory they live in: BPCells stores, .lazy matrices, R handoff CSVs. .gitignore covers all of those, but an sdist is not built from git, so they were packaged anyway.

The published shanuz 0.9.0 sdist is 23.5 MB against a 0.24 MB wheel for that reason, and a build on a machine that had just run the tutorials produced 63 MB — same version number, different tarball, depending on the disk it was built from. That non-reproducibility was the actual defect; the size was the symptom.

[tool.hatch.build.targets.sdist] now lists its contents explicitly. The sdist is 532 KB. The wheel is unaffected — it was always scoped by packages = ["truecell"]. tests/test_packaging.py builds an sdist and looks inside it, rather than asserting on configuration that would pass just as happily against a broken artifact.

Most figures on the docs site 404'd. MkDocs re-anchors relative paths written in Markdown onto the built page, which under use_directory_urls sits a directory deeper than its source — but it passes raw HTML through untouched, and the vignettes write most figures as <img> inside HTML tables so the R and Python plots sit side by side. 110 of the site's 133 figures were broken, and the ten vignettes built entirely from those tables showed no images at all. A build hook now applies the same rewrite to raw <img src>. The existing figure-existence test only understood Markdown image syntax, so it was blind to 126 of the 149 references it was meant to guard; it now reads both.

The API reference claimed the whole API is exported from the top level. True for eleven of thirteen pages, false for two: 66 of the 73 public generics live only on truecell.generics, so truecell.features(obj) — the call that sentence invites — raises AttributeError.

tutorials/README.md documented only 10 of the 18 tutorials. The eight added in Waves 2 and 3 had no written section. All eight added.

Added

A logo, generated by tools/make_logo.py rather than drawn. The mark is a point cloud whose density traces a lowercase c, for cell — building a picture out of dots is the nod to Georges Seurat, the pointillist the R package is named for. The c is two arcs meeting at the waist in two colours: the two implementations, one shape. It is also the letter the wordmark's own c draws, so the mark is a letter of the name enlarged rather than an ornament beside it.

Ships as a mark, horizontal lockup, wordmark, hex sticker, a simplified single-stroke glyph for sizes below ~48px where the dots silt up, and favicons — each in a light-ground and dark-ground variant.

Agent skills for LLM-assisted work (skills/). Ten skills in the Claude Agent Skills format — plain Markdown with YAML frontmatter, so they work as context for any model. Aimed at the mistakes a model actually makes against this API rather than re-describing the docs: that analysis functions mutate in place and return None (so obj = normalize_data(obj) leaves you holding None), that dims is 0-based, that matrices are features × cells, that the generics are not top-level, and which differences from Seurat are deliberate and must not be "fixed".

Verifying these artifacts

The attached wheel and sdist were rebuilt from the v1.0.0 tag and are byte-identical to the files on PyPI:

Artifact SHA-256
truecell-1.0.0-py3-none-any.whl 58c56e96b09651785a9228295fdd2ba7214bed12d7365fde7622cebd2288a652
truecell-1.0.0.tar.gz 867490a00fdc80eeabf49170cc718f01e5827942ae47a90d900e21c181f14d35

📦 PyPI · 📖 Documentation · 📓 Tutorials · 📝 Full changelog

shanuz v0.9.0 — reference mapping, spatial, scale, and five more milestones

Choose a tag to compare

@shanikawm shanikawm released this 26 Jul 19:43
96b2f8c

Six milestones' worth of work that had been sitting on main since 0.2.0, released in one jump: reference mapping, extra reductions, pseudobulk DE, spatial, scale, and the specialized assays — plus a breaking anchor/integration fix and the tutorial fidelity infrastructure (measured bands, staleness guards) that followed it. All of it was on main; as of this release, all of it is on PyPI.

Version jumps 0.2.0 → 0.9.0 to match the ROADMAP milestone number this release closes — a one-time coincidence, not a new versioning policy. See CHANGELOG.md for the complete, itemized list.

Highlights

Reference mapping, integration, anchors

  • find_transfer_anchors / map_query / transfer_data — label transfer via anchor projection.
  • Anchor scoring and filtering brought in line with Seurat's FindWeightsC/FindIntegrationAnchors: four-neighbour-table scoring, TopDimFeatures filtering, constant-feature dropping, exact-SVD PCA loadings.
  • Breaking: integrate_layers(method="cca"|"rpca") was running the wrong algorithm; RPCA now corrects onto the larger batch and closes most of its anchor-recall gap vs Seurat.

Extra reductions

  • run_ica, run_tsne, run_spca, glm_pca.
  • run_pca moved off sklearn's randomized SVD to an exact SVD, fixing drift in downstream reductions.

Pseudobulk DE

  • DESeq2-backed pseudobulk differential expression (find_markers(test_use="deseq2", ...)).
  • negbinom no longer runs a likelihood-ratio test against a moment-estimated dispersion.

Spatial

  • Visium loading defaults fixed: in-tissue filtering, lowres image, "slice1" image key regardless of which optional imaging package is installed.
  • Centroids now carries a radius; Segmentation polygons are closed; Moran's I uses Seurat's actual weight scheme.

Scale / out-of-core

  • LazyMatrix, a BPCells-comparable on-disk backend — five functions that used to densify an entire on-disk layer on read now stream it; object construction no longer forces materialization before analysis runs.

Specialized assays, cell hashing, Mixscape

  • Layered v5 Assay split/JoinLayers round-trips correctly; fetch_data addresses embedding columns and reads the right layer; the command log and orig.ident are populated.
  • Cell hashing (hto_demux, multiseq_demux) and Mixscape, verified against cross-species ground truth (99.81% call-concordant).

Also in this release

  • A documentation site: MkDocs + Material + mkdocstrings, published to genomicai.github.io/shanuz, generated from the same docstrings that carry the fidelity notes.
  • find_neighbors/find_clusters/run_umap graph fixes (directed KNN kept, SNN diagonal preserved, group_singletons) that closed a clustering divergence on integrated data.
  • leverage_score and sketch_data fixed to match Seurat's sketching path.
  • jack_straw null distribution rebuilt against a refit-per-replicate basis, matching ScoreJackStraw.

Install

pip install shanuz               # core
pip install "shanuz[analysis]"   # + clustering, UMAP/t-SNE, plotting, more DE tests
pip install "shanuz[all]"        # everything, incl. dev tooling

Notes

  • Python 3.12–3.13 (SPEC 0); 3.14 is blocked only by harmonypy's missing cp314 wheel.
  • 970 tests passing, 18 tutorials each checked side-by-side against real Seurat 5.5.1.

Tutorial R Seurat ↔ Shanuz side-by-side comparisons

Choose a tag to compare

@shanikawm shanikawm released this 06 Jul 03:30
aabaa7c

Documentation-only release. No library code changed — the installable shanuz package remains 0.2.0 on PyPI and is byte-identical to the 0.2.0 wheel. This tags a batch of tutorial improvements only.

What changed

Every tutorial now presents a genuine left-R (Seurat) / right-Python (Shanuz) side-by-side comparison. Two tutorials were previously pure ports whose "R (Seurat)" column held only code (no R figures), which read as an "R-only" page; others had one-sided or orphaned figures.

New R reproduction scripts

Each mirrors its Python tutorial's exact pipeline and writes r_* figures titled "R Seurat – …":

Script Tutorial
tutorials/pbmc8k_subclustering_verify.R Advanced PBMC 8k clustering + subclustering
tutorials/cbmc_citeseq_verify.R Multimodal CITE-seq (RNA + ADT)
tutorials/pbmc3k_verify.R PBMC 3k (the RidgePlot the vignette omits)
tutorials/pbmc3k_sctransform_verify.R SCTransform (cell-type UMAP + SCT-vs-standard)

Balance & cleanup

  • 23 new R Seurat figures across the advanced, multimodal, sctransform, and pbmc3k tutorials.
  • All 6 tutorials verified two-sided — R-side figure count equals Shanuz count everywhere.
  • Surfaced two orphaned xenium figures (QC violin, clusters-in-space) and removed redundant duplicate QC-scatter panels. Zero orphaned figures repo-wide.

Notable finding

Seurat's CLR normalization (margin=2) has a different absolute scale than Shanuz's, so the CITE-seq annotation thresholds are re-calibrated to Seurat's per-cluster CLR values (documented in cbmc_citeseq_verify.R); both resolve the same 9 lineages.

Full changelog: v0.2.0...tutorials-2026.07.06

v0.2.0 — Harmony batch integration + WNN multimodal analysis

Choose a tag to compare

@shanikawm shanikawm released this 05 Jul 17:22
5ec1bcc

First feature release since 0.1.x. Adds batch-effect correction, weighted multimodal integration, and two more dimensionality reductions — all validated against the published wheel (156 tests + all 9 tutorial scripts).

Highlights

Batch correction / integration (v0.2.0)

  • run_harmony(...) — Harmony batch correction via harmonypy; stores a DimReduc("harmony") usable downstream (find_neighbors(reduction="harmony"), etc.). Verified to lower per-batch silhouette while preserving cell-type separation.
  • integrate_layers(...) — Seurat v5 dispatch API (method="harmony"; cca/rpca raise NotImplementedError, on the roadmap).
  • New [integration] extra: pip install "shanuz[integration]" (pulls harmonypy).

Multimodal WNN (v0.4.0)

  • find_multi_modal_neighbors(...) — Weighted Nearest Neighbor analysis (Hao et al. 2021). Learns per-cell modality weights, builds joint wknn/wsnn graphs, and writes <assay>.weight columns.
  • run_umap(graph=...) — embed a precomputed graph (e.g. wsnn) directly, so find_clusters / run_umap work on the joint WNN graph.

Additional reductions (v0.5.0)

  • run_ica (embeddings + loadings) and run_tsne.

Tutorials & docs

  • CBMC CITE-seq tutorial extended with a WNN section (run_wnn, Step 8).
  • README links made absolute so they render on the PyPI page; test count updated (156).

Install

pip install "shanuz[integration]"   # + Harmony
pip install "shanuz[all]"           # everything

Notes

  • WNN uses the roadmap-sanctioned scale-invariant weight approximation (validated by structure recovery, not bit-exact R parity).
  • Deferred to future cycles: CCA/RPCA + IntegrateData, v0.3.0 reference mapping, DESeq2 pseudobulk, SketchData/BPCells.

PyPI: https://pypi.org/project/shanuz/0.2.0/
Full diff: v0.1.2...v0.2.0

Shanuz v0.1.1

Choose a tag to compare

@shanikawm shanikawm released this 04 Jul 13:11

Spatial transcriptomics (Xenium / Visium / CosMx)

This release adds a spatial analysis layer to shanuz — Seurat-parity loaders and
neighbourhood/niche analysis, validated end-to-end against R Seurat.

Added

  • Loaders: load_xenium, load_visium, load_cosmx — each returns a Shanuz
    object with expression and populated per-FOV centroids (.images).
    load_xenium keeps only Gene Expression features by default (matching
    LoadXenium's assay split; keep_controls=True to retain controls).
  • Spatial-aware from_anndata — rebuilds .images from obsm['spatial'] +
    obs['fov'] instead of misfiling it as a bogus PCA-style reduction.
  • Neighbourhood / niche analysis: get_tissue_coordinates, spatial_knn,
    nearest_neighbor_distance, local_neighborhood, build_niche_assay
    (Seurat v5's BuildNicheAssay), composition_test (directional Fisher/BH
    enrichment across a categorical split).
  • Spatial plots: image_dim_plot / image_feature_plot — matplotlib
    centroid scatter, immune to the ggplot2 4.x ImageDimPlot blank-render bug.
  • add_module_score(search=True) — case/punctuation-insensitive gene-symbol
    resolution (local UpdateSymbolList stand-in).
  • datasets.xenium_mouse_brain() — one-line auto-download (~20 MB) of a
    public 10x Xenium dataset for the new tutorial.
  • Tutorial 5 — Xenium spatial (R vs Python): side-by-side R Seurat / shanuz
    walkthrough on a public 10x Xenium mouse-brain section (36,602 cells x 248
    genes — the dataset in Seurat's own spatial vignette). Every deterministic
    anchor (cell counts, marker-defined cell types, nearest-neighbour distances,
    local density, composition test) matches R to 8 significant figures.
  • GitHub Actions CI (ruff + pytest across Python 3.10-3.12), py.typed marker.

Notes

  • Still open for the spatial milestone (tracked in ROADMAP.md): a MERSCOPE
    loader, FindSpatiallyVariableFeatures (Moran's I), and Visium tissue-image
    (SpatialDimPlot/SpatialFeaturePlot) plots.
  • No breaking changes — all additions are backward compatible; the four
    existing tutorials (PBMC 3k, PBMC 8k, CBMC CITE-seq, SCTransform) were rerun
    end-to-end post-merge with no regressions.

Full changelog: v0.1.0...v0.1.1

Shanuz v0.1.0

Choose a tag to compare

@shanikawm shanikawm released this 30 Jun 14:58

Shanuz v0.1.0 — First Release

A Python port of the Seurat single-cell RNA-seq analysis framework, algorithmically faithful to Seurat v5.

Features

Core data structures

  • Shanuz object mirroring the R Seurat S4 class
  • Assay5 — sparse-matrix-backed multi-layer assay with per-layer feature/cell name tracking
  • Graph, DimReduc, Neighbor — faithful ports of Seurat's internal structures

Preprocessing

  • normalize_data — LogNormalize and CLR (Seurat-exact formula)
  • find_variable_features — VST with Seurat-faithful dispersion and LOESS
  • scale_data — z-score with ddof=1 (sample SD, matching R)
  • percentage_feature_set

Normalization

  • sctransform — regularized negative-binomial Pearson residuals; vectorised Poisson IRLS, moment-estimated theta, LOESS regularisation, vars_to_regress support

Signature scoring

  • add_module_score — binned control-gene scoring (Tirosh 2016)
  • cell_cycle_scoring — S/G2M phase assignment with built-in CC_GENES

Dimensionality reduction & neighbours

  • run_pca (scikit-learn, ddof=1 stdev)
  • find_neighbors — KNN + fully-sparse SNN (no dense n×n materialisation)
  • jack_straw / score_jackstraw — JackStraw permutation test
  • run_umap (umap-learn)

Clustering

  • find_clusters — Louvain (python-igraph, deterministic igraph RNG seeding) and Leiden (leidenalg)

Differential expression

  • find_markers / find_all_markers with test types: wilcox (tie-corrected Mann-Whitney U), t, LR (logistic LRT), negbinom (NB GLM LRT), roc (AUC + power)

Plotting

  • dim_plot, feature_plot, vln_plot, dot_plot, elbow_plot, do_heatmap, dim_heatmap, feature_scatter, variable_feature_plot, ridge_plot

AnnData interoperability

  • as_anndata, from_anndata

Tutorials

Four end-to-end tutorials validated against the official Seurat vignettes, each pairing R and Python code with side-by-side output plots:

# Tutorial
1 PBMC 3k — Guided Clustering
2 PBMC 8k — Advanced Subclustering
3 CBMC CITE-seq — Multimodal (RNA + ADT)
4 PBMC 3k — SCTransform

Tests

129 unit tests, all passing.

Installation

git clone https://github.com/GenomicAI/shanuz.git
cd shanuz
pip install -e ".[analysis]"

Algorithms faithfully ported from R Seurat

  • Seurat CLR formula (log1p(x / exp(Σ log1p(x>0) / n)))
  • VST dispersion with Bessel correction
  • Wilcoxon with tie correction (scipy.stats.mannwhitneyu, asymptotic)
  • SCTransform NB model (Hafemeister & Satija 2019; Choudhary & Satija 2022)
  • AddModuleScore (Tirosh et al. 2016)
  • JackStraw permutation test