Initial feature-complete Python port of the SelectSim R package.
- Ported the core algorithm:
selectX()end-to-end pipeline,AlterationLandscape, template-matrix generation (template.py), TMB-based weighting (weights.py), the permutation null model (null_model.py), and all overlap / effect-size / FDR statistics (stats.py). - Added an optional accelerated backend for
null_model_parallel(backend="jax", via theselectsim[fast]extra) alongside the default NumPy backend, and an optional on-disk Zarr store for large null-model runs (store="zarr", via theselectsim[storage]extra) so simulations no longer need to be held entirely in memory. - Added
selectsim.gam: a full port ofR/gam_utils.r's MAF-ingestion pipeline (filter_maf_*,stat_maf_*,maf_to_gam,TCGA_maf_schema,GENIE_maf_schema,mutation_type) for building Gene Alteration Matrices (GAMs) directly from raw MAF files. - Added
selectsim.plotting: matplotlib/seaborn ports of the R package's visualization helpers --theme_publication,obs_exp_scatter,overlap_pair_extract,ridge_plot_ed,ridge_plot_ed_compare. - Added
selectsim.io: Parquet read/write helpers and Zarr null-model store utilities. - Bundled real TCGA LUAD example data (
tests/data/*.parquet, exported from the R package'sluad_run_data/luad_result/luad_mafdatasets) and added an R-parity test suite (TestSelectXWithRDataintests/test_selectsim.py) validating the Python port's output against a realSelectSim::selectX()R reference run. - 126 tests passing across algorithm, statistics, GAM ingestion, plotting, and I/O modules.
- Added Sphinx documentation (
docs/) with a full API reference, an "Overview" quick-start, and two tutorial notebooks (introduction,data_processing) mirroring the R package's vignettes, plus standard open-source project scaffolding (LICENSE,CITATION.cff, CI workflows, issue templates). - Supports Python 3.10-3.12 (
requires-python = ">=3.10,<3.13"). The upper bound is intentional, not a typo: numpy/scipy/jax/zarr don't all publish prebuilt wheels for 3.13+ yet, and installing on an unsupported interpreter can silently trigger a slow (or failing) from-source build of numpy. 3.10/3.11/3.12 were each verified to resolve prebuilt wheels and pass the full test suite.
- Fixed several correctness bugs found in code review:
filter_maf_columncomputed a union instead of an intersection when excluding rows matching several substring values (inclusive=False, fixed=False);store="zarr"fully materialized all permutations in memory before writing them to disk, defeating its own purpose (now streams in bounded batches, ~5x lower peak memory); the jax backend recompiled from scratch on every call instead of reusing JAX's compilation cache;n_coreswas silently ignored withbackend="jax"(now warns);ridge_plot_edcrashed on an empty null model; andretrieve_outliers'sn_simparameter was dead code and its 90th-percentile threshold was off-by-one relative to the R original. - This round of work (the fixes above, plus the earlier module ports,
documentation, and uv migration) was developed with the assistance of
Claude Code (Anthropic), directed and verified by Arvind Iyer throughout;
see
git logfor detailed commit history.
- Performance: the default numpy null-model backend
(
null_model_parallel,store="memory"or"zarr") no longer loops over genes in Python to pick each row's top-k residual columns; it now uses the same vectorized double-argsort rank trick the (now removed) jax backend used, with no per-gene Python loop. Benchmarked as bit-identical to the old implementation (full R-parity test suite still passes) and consistently faster, especially at highern_cores. - Performance:
store="zarr"null-model runs now chunk the on-disk Zarr array to match the internal write-batch size instead of defaulting to one chunk per permutation; writing in batches of 200 (the default) previously forced 200 separate compress/write calls per batch. Benchmarked at ~2.5x faster zarr writes in isolation. - Performance:
estimate_p_val'sgene_names: List[str]parameter is nowgene_index: Dict[str, int]-- callers looking up many pairs (asestimate_pairwise_pdoes, used whenestimate_pairwise=True) now build the name-to-index mapping once instead of doing an O(n_genes) list scan per gene per pair. - Removed: the jax null-model backend (
backend="jax"onnull_model_parallel, theselectsim[fast]extra). Benchmarked against the numpy backend on real LUAD-sized data on CPU (no GPU): jax was consistently 5-10x slower, dominated by per-call JIT dispatch overhead that this workload's array sizes never amortize. If GPU-backed acceleration is needed in the future, it should be re-evaluated against the current (vectorized) numpy backend on representative GPU hardware, not reintroduced by default. - Fixed:
significance_heatmapwas implemented and tested but never exported from the top-levelselectsimpackage (only reachable viaselectsim.plotting.significance_heatmap); it's nowss.significance_heatmap. - Added
null_model_parallel/selectXstore="auto": estimates the null model's memory footprint from cohort size andn_permutand picks"memory"or"zarr"automatically against a detected (or configured,memory_budget_gb) RAM budget, so one call scales safely across machines without the caller needing to know cohort dimensions up front. - Added
oncoprintandoncoprint_pair: new (not present in the R package) multi-gene and two-gene co-mutation visualizations comparing the observed alteration pattern to a retained null-model simulation, with a per-sample TMB track. - Extended the
introductiontutorial notebook withsignificance_heatmap,oncoprint,oncoprint_pair, andridge_plot_ed_compare(previously onlyobs_exp_scatterandridge_plot_edwere demonstrated). - This round of work was developed with the assistance of Claude Code
(Anthropic), directed and verified by Arvind Iyer throughout; see
git logfor detailed commit history.
- Performance:
import selectsimdropped from ~1.5s to ~0.6s. The top-level package eagerly importedselectsim.plotting, which pulls inmatplotlibandscipy(used nowhere outsideplotting.py), even for callers who only ever callselectX()and never plot anything. Plotting functions (obs_exp_scatter,ridge_plot_ed,oncoprint, etc.) are now resolved lazily on first access (PEP 562 module__getattr__) instead -- samess.<name>usage, just deferred. On the bundled LUAD benchmark this cut whole-process wall time by ~23% and peak memory by ~21%. - Removed: the
seaborndependency. It was listed inpyproject.tomland mentioned in a couple of docstrings, but never actually imported or used anywhere in the codebase.