stLearn - A Spatial Transcriptomics Toolkit¶
stLearn is designed to comprehensively analyse Spatial Transcriptomics (ST) data to investigate complex biological processes within an undissociated tissue. ST is emerging as the “next generation” of single-cell RNA sequencing because it adds spatial and morphological context to the transcriptional profile of cells in an intact tissue section. However, existing ST analysis methods typically use the captured spatial and/or morphological data as a visualisation tool rather than as informative features for model development. We have developed an analysis method that exploits all three data types: Spatial distance, tissue Morphology, and gene Expression measurements (SME) from ST data. This combinatorial approach allows us to more accurately model underlying tissue biology, and allows researchers to address key questions in three major research areas: cell type identification, cell trajectory reconstruction, and the study of cell-cell interactions within an undissociated tissue sample.
Latest Additions¶
1.5.1 (2026-11-09)¶
This fixes some improvements missed in the 1.5.0 release in the use of LR Results. The handoff from run to run_cci and statistics are stored once and aligned to other the LR data.
Features
st.tl.cci.runnow uses a spots-by-spots, sparse graph:adata.obsp["spot_neighbours"]. It replaces the comma-joined stringsadata.obsm["spot_neighbours"]andadata.obsm["spot_neigh_bcs"].st.tl.cci.run_cciandget_edgesread the stored graph directly.With
use_label,runcounts cell types over the same graph instead of searching the neighbours a second time (het.counttakes it asneighbours=).st.tl.cci.runno longer storeslrfeaturesin the LR table’suns.nonzero-median,zero-prop,median_rank,prop_rankandmean_rankare columns oflr_results.varordered byn_spots_sig.lr_results.varm["lr_quantiles"]stores ligand and receptor expression quantiles.st.tl.cache.write_subset_h5adtakesobsp_keys, andmerge_h5ad_into_adatacopiesobsp, so a cached CCI run keeps its neighbours.stlearn.tl.cci.neighbours.calc_neighboursreturns that sparse matrix instead of a numbaList.neighbourhood_matrixandneighbourhoods_from_barcodesare removed.LRScorertakes a sparse matrix.get_scores,adjacencyandflatten_neighboursare removed fromstlearn.tl.cci.scoring.stlearn.tl.cci.neighbours.get_neighbourhoodsand the edge helpers instlearn.tl.cci.edgesare removed.get_interaction_matrixandget_interaction_pvalstake the graph’s CSR arrays (neigh_indptr,neigh_indices) instead of a numbaList.get_edgesno longer needs to deduplicates in a quadratic loop.Removed
UndefinedExceptionfromresistance_matrix.
1.5.0 (2026-11-01)¶
This release contains changes in cell-cell interaction (CCI) mainly improvements in runtime and memory usage and requires code to be updated.
Features
Memory usage is reduced through chunking (~256MB) in
st.cci.runscoring and permutation testing. On a 5,000,000 bin synthetic Visium HD sample with connectomeDB2025, n_pairs=1000, runs take ~24GB in ~10 minutes.SpatialData support added with a new
st.iopackage. Supporting:atera,xenium,visium_hdandvisium. Start of spatial data native plotting withst.pl.sdata_plotand added AnnData’sst.pp.highly_variable_genes.New datasets
st.datasets.visium_hd_sgeandatera_sge. All four dataset functions now return SpatialData objects;visium_sgereturned AnnData andxenium_sgeonly downloaded the files.Upgraded ligand-receptor databases to connectomeDB2025 (human and mouse).
Ligand-receptor pairs are an
LRPairsobject instead of an array of ‘L_R’ strings. It tracks the species and where each pair came from. Pairs can be filtered and combined (lrs.filter(...),lrs1 | lrs2).st.tl.cci.runtakes onlyLRPairs; an array of ‘L_R’ names raisesTypeError. Wrap strings withst.tl.cci.LRPairs.from_names(names, species="human").st.tl.cci.lr_pairs(database, species)loads a database shipped with stLearn:"connectomeDB2025"(default; human or mouse) or"connectomeDB2020"(human).st.tl.cci.read_lr_pairs(path, species=...)reads your own csv/tsv of ligand and receptor columns.st.tl.cci.load_lrsis deprecated and all APIs now uselr_pairs, orlr_pairs(...).names()for the old array. Note thatlr_pairs()defaults to connectomeDB2025, whereload_lrs()defaults to connectomeDB2020.Previous databases names
"connectomeDB2020_lit"and"connectomeDB2020_put"are removed."connectomeDB2020"is the human only, literature pairs and the putative pairs are no longer shipped.st.add.lris removed; useread_lr_pairsorLRPairs.from_names.The LR table from
runstores each pair’s source invar["source"]and the species inuns["lr_species"], whichrun_lr_gouses as its default species.
Spatial trajectory and other APIs use cluster label strings (
cluster="6").AnnData backwards compatibility available through
st.io.attach_spatialfor Visium/Visium HD andst.io.attach_spatial_sdatafor Xenium/Atera.CCI results are now a LR table (AnnData) returned by
st.tl.cci.run. A spots x LR pairs with summaries invarand scores and p-values are in sparselayers. Downstream functions and plots now takelr_results.Support for microns through
microns_per_pixel,st.utils.microns_to_pixels,pixels_to_micronsandcreate_grid(bin_um=).SME computes weights only between nearby spots and data is kept sparse where possible.
st.pp.extract_featuresupports ConvNeXt and takesweights=.Upgrade libraries: pillow>=13.0.0,<14.0, numba>=0.64.0, scikit-image>=0.25.0, torch>=2.5.0, matplotlib>=3.11,<4.0.
Bugs
Fixed double application of scaling, adding instructions and warning for image alignment in
st.io.create_stlearnBug #303.A pair listed twice is now tested once - fixed Bug #305.
Incorrect labelling in CCI Plot - rows labelled “Senders”, columns labelled “Receivers” - should be the other way around Bugs #309, #363.
SME failed with “Input X contains NaN” when a spot had no
array_row/array_col- these are now left out of the spot spacing fit - fixed Bug #310.st.tl.cci.run(use_label=...)never applied cell heterogeneity weighting: it read the cell-type data from the wrong key, found nothing, and weighted every spot 1, so results were identical with and withoutuse_label. It now weights by heterogeneity from cell-type proportions inuns[use_label]or per-cell labels inobs[use_label], and takescell_prop_cutoff. CCI scores and significance change for any run that useduse_label.st.tl.cci.run_cci: p-values from permutation are now(k+1)/(N+1)and can no longer be 0, cutoff is nowp < p_cutoff, and each permutation thread is seeded throughrandom_state.spot_mixturesmatches cell types by exact name.When
min_expr > 0n_spotsin the LR summary counted spots with a score abovemin_expr, while themin_spotsfilter counted spots with a score above 0, so the reported count was not the one the filter used. Both now use score > 0.st.tl.cci.griddropped cells lying exactly on the inner edge of the last row or column of the grid, and on the outer edges of the corner square.st.pl.lr_plotstatistic names were changed fromp_val,p_adjand-log10(p_adj)top_vals,p_adjsandneg_log10_p_adjs. It always drewlr_sig_scoreswhateveruse_labelasked for, so a plot labelledp_adjsorneg_log10_p_adjsshowed the masked score instead.st.pl.lr_plotandst.pl.ccinet_plotalso draw on afigoraxyou passed in rather than opening a new figure, so they can go in subplots.Images in
uns["spatial"]are now uint8 (0-255), not floats in [0, 1].st.pp.extract_featuretiles were fed as floats between 0-255 instead of 0-1. Tiles are now read as uint8, soX_tile_featureandX_morphologychange.st.concatenate_spatial_adata: the x and y offsets added toobsm["spatial"]for each tile were swapped, so spots on any tile off the diagonal landed on the wrong tile. Tiles also keep their aspect ratio instead of being stretched tofixed_size, each sample is scaled by its own scale factor rather than the first sample’s, andfixed_sizeis (height, width) like a NumPy shape.st.io.read_merfishworks again with new notebook. Defaultsspot_diameter_fullresto 10 (it was 50).st.io.read_10xreads 16-bit / signed PNGs and reports missing images clearly.st.add.labels/st.add.add_loupe_clustersmatch by barcode.st.add.positionsreads Space Ranger 2.0+ files.st.add.parsingassigned spots the wrong coordinates (they were interleaved from the spots inobs), so every plot and anything usingobsm["spatial"]was wrong. Each spot now gets its own position.SME fits each axis separately when finding neighbours.
Spatial trajectory:
st.spatial.trajectory.pseudotimeorients by median pseudotime (paths may differ),shortest_path_spatial_pagareturns labels, plus fixes tolocal_level,weight_optimizing_local,de_transition_plotand the trajectory plots.st.pl.qc_plotno longer makes a dense copy.Readers were moved to
st.io:st.read_10x,st.read_xenium,st.read_old_st,st.read_slide_seq,st.read_merfish,st.read_seq_fishandst.create_stlearn.st.tl.cci.gridvectorised and no longer takesn_cpus.obsm["spatial"]is in full-resolution pixels (was image pixels), matching the rest of stLearn (used byst.tl.cci.grid).SME outputs are sparse when
Xis sparse; no weight matrices inuns.Removed:
extract_feature(cnn_base="xception"),deconvolution_plot(data_alpha=),local_plot(cluster=),Spatial(basis=).New defaults:
pl.trajectory.pseudotime_plot(node_size=60)(area);pl.trajectory.check_trajectory(size_spatial=7, img_key=None).st.spatial.trajectory.pseudotimeno longer takesn_neighbors,use_reporrun_knn- these must be run prior and will throw an exception if there’s no neighbourhood graph.
1.4.1 2026-06-16¶
Features
Improved speed of random spot generation used in permutation testing.
Upgrade libraries: pillow>=12.0.0,<13.0
Bugs
Fixed up docstring and types for various CCI methods.
Fixes for issues #350 and #300 with incorrect types.
1.4.0 2026-05-05¶
Features
Removed tensorflow and keras and replaced with torch and torchvision.
The CCI implementation removed string comparisons for LR pairs lists and replaced it with one set of two packed 32 bit integers. Removed deduplication pass (m^2) with a hash lookup (O(n)).
Added row and polygon annotations.
Upgrade libraries: numpy >=2.4.0, scanpy >= 1.12.0, scipy >= 1.17.0, added spatialdata, shapely, geopandas.
Bugs
Fixed exception handling to catch previous exceptions when rethrown.
1.3.0 2026-02-24¶
Features
Removed interactive stLearn/embedded web application.
Removed louvain clustering.
Renamed methods and classes to follow lowercase standard.
1.2.2 2025-10-20¶
Features
Added support for Python 3.11 and 3.12.
Upgraded scanpy to 1.11 - clustering will be different.
Added more CCI tests.
Bugs
Fixed copy-paste error in
leiden.pyfile.
1.1.5 2025-09-17¶
Features
Add Leiden clustering wrapper.
Fix documentation, refactor code in spatial.SME.
1.1.1 2025-07-07¶
Features
Support Python 3.10.x
Added quality checks black, ruff and mypy and fixed appropriate source code.
Copy parameters now work with the same semantics as scanpy.
Library upgrades for leidenalg, louvain, numba, numpy, scanpy, and tensorflow.
datasets.xenium_sge - loads Xenium data (and caches it) similar to scanpy.visium_sge.
Bug fixes
Xenium TIFF and cell positions are now aligned.
Consistent with type annotations - mainly missing None annotations.
pl.cluster_plot - Does not keep colours from previous runs when clustering.
pl.trajectory.pseudotime_plot - Fix typing of cluster values in .uns[“split_node”].
Removed datasets.example_bcba - Replaced with wrapper for scanpy.visium_sge.
Moved spatials directory to spatial, cleaned up pl and tl packages.
0.3.2 2021-03-29¶
Features
Add interactive plotting functions:
gene_plot_interactive(),cluster_plot_interactive(),het_plot_interactive()Add basic unittest (will add more in the future).
Add ‘contour’ parameter to use contour plot in
gene_plot()andhet_plot().Add
convert_scanpy()to convert object from scanpy to stLearn.
Bug fixes
Refactor
gene_plot()Refactor
cluster_plot()Refactor
het_plot()Fixed issue with networkx object cannot write h5ad file.