diff --git a/NEWS.md b/NEWS.md index 59fafd6..b56ac0e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -102,3 +102,18 @@ Initial feature-complete Python port of the [SelectSim R package](https://github * This round of work was developed with the assistance of Claude Code (Anthropic), directed and verified by Arvind Iyer throughout; see `git log` for detailed commit history. + +# SelectSim (Python) 0.3.1 + +* **Performance**: `import selectsim` dropped from ~1.5s to ~0.6s. The + top-level package eagerly imported `selectsim.plotting`, which pulls in + `matplotlib` and `scipy` (used nowhere outside `plotting.py`), even for + callers who only ever call `selectX()` 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 -- same `ss.` usage, just deferred. On the + bundled LUAD benchmark this cut whole-process wall time by ~23% and + peak memory by ~21%. +* **Removed**: the `seaborn` dependency. It was listed in + `pyproject.toml` and mentioned in a couple of docstrings, but never + actually imported or used anywhere in the codebase. diff --git a/docs/source/conf.py b/docs/source/conf.py index e845bce..871bc6a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,7 +13,7 @@ project = 'SelectSim' copyright = '2026, Arvind Iyer, Marco Mina, Miljan Petrovic, Giovanni Ciriello' author = 'Arvind Iyer, Marco Mina, Miljan Petrovic, Giovanni Ciriello' -release = '0.3.0' +release = '0.3.1' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/source/tutorials/introduction.ipynb b/docs/source/tutorials/introduction.ipynb index ccbfd0b..9f12bbc 100644 --- a/docs/source/tutorials/introduction.ipynb +++ b/docs/source/tutorials/introduction.ipynb @@ -40,10 +40,10 @@ "id": "d465b22a", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:47.821334Z", - "iopub.status.busy": "2026-07-14T19:10:47.820804Z", - "iopub.status.idle": "2026-07-14T19:10:49.335540Z", - "shell.execute_reply": "2026-07-14T19:10:49.334213Z" + "iopub.execute_input": "2026-07-14T21:19:47.586776Z", + "iopub.status.busy": "2026-07-14T21:19:47.586296Z", + "iopub.status.idle": "2026-07-14T21:19:48.517073Z", + "shell.execute_reply": "2026-07-14T21:19:48.515872Z" } }, "outputs": [], @@ -88,10 +88,10 @@ "id": "b116ad1e", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:49.339285Z", - "iopub.status.busy": "2026-07-14T19:10:49.338828Z", - "iopub.status.idle": "2026-07-14T19:10:49.449444Z", - "shell.execute_reply": "2026-07-14T19:10:49.448430Z" + "iopub.execute_input": "2026-07-14T21:19:48.521338Z", + "iopub.status.busy": "2026-07-14T21:19:48.520743Z", + "iopub.status.idle": "2026-07-14T21:19:48.624345Z", + "shell.execute_reply": "2026-07-14T21:19:48.623192Z" } }, "outputs": [ @@ -158,10 +158,10 @@ "id": "4e61ac58", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:49.452228Z", - "iopub.status.busy": "2026-07-14T19:10:49.451965Z", - "iopub.status.idle": "2026-07-14T19:10:51.127557Z", - "shell.execute_reply": "2026-07-14T19:10:51.126450Z" + "iopub.execute_input": "2026-07-14T21:19:48.626932Z", + "iopub.status.busy": "2026-07-14T21:19:48.626645Z", + "iopub.status.idle": "2026-07-14T21:19:50.237430Z", + "shell.execute_reply": "2026-07-14T21:19:50.236217Z" } }, "outputs": [], @@ -219,10 +219,10 @@ "id": "0de9e002", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:51.130731Z", - "iopub.status.busy": "2026-07-14T19:10:51.130441Z", - "iopub.status.idle": "2026-07-14T19:10:51.148445Z", - "shell.execute_reply": "2026-07-14T19:10:51.147133Z" + "iopub.execute_input": "2026-07-14T21:19:50.240462Z", + "iopub.status.busy": "2026-07-14T21:19:50.240142Z", + "iopub.status.idle": "2026-07-14T21:19:50.259191Z", + "shell.execute_reply": "2026-07-14T21:19:50.257902Z" } }, "outputs": [ @@ -368,10 +368,10 @@ "id": "72275c7f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:51.151166Z", - "iopub.status.busy": "2026-07-14T19:10:51.150892Z", - "iopub.status.idle": "2026-07-14T19:10:51.174015Z", - "shell.execute_reply": "2026-07-14T19:10:51.172684Z" + "iopub.execute_input": "2026-07-14T21:19:50.262765Z", + "iopub.status.busy": "2026-07-14T21:19:50.262453Z", + "iopub.status.idle": "2026-07-14T21:19:50.331965Z", + "shell.execute_reply": "2026-07-14T21:19:50.330701Z" } }, "outputs": [ @@ -515,10 +515,10 @@ "id": "054959bf", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:51.176540Z", - "iopub.status.busy": "2026-07-14T19:10:51.176217Z", - "iopub.status.idle": "2026-07-14T19:10:51.183444Z", - "shell.execute_reply": "2026-07-14T19:10:51.181999Z" + "iopub.execute_input": "2026-07-14T21:19:50.334898Z", + "iopub.status.busy": "2026-07-14T21:19:50.334601Z", + "iopub.status.idle": "2026-07-14T21:19:50.341525Z", + "shell.execute_reply": "2026-07-14T21:19:50.340259Z" } }, "outputs": [ @@ -554,10 +554,10 @@ "id": "591930bf", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:51.186313Z", - "iopub.status.busy": "2026-07-14T19:10:51.186040Z", - "iopub.status.idle": "2026-07-14T19:10:51.593042Z", - "shell.execute_reply": "2026-07-14T19:10:51.591820Z" + "iopub.execute_input": "2026-07-14T21:19:50.344474Z", + "iopub.status.busy": "2026-07-14T21:19:50.344084Z", + "iopub.status.idle": "2026-07-14T21:19:51.186802Z", + "shell.execute_reply": "2026-07-14T21:19:51.185538Z" } }, "outputs": [ @@ -596,10 +596,10 @@ "id": "a8fa7702", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:51.596645Z", - "iopub.status.busy": "2026-07-14T19:10:51.596329Z", - "iopub.status.idle": "2026-07-14T19:10:52.049139Z", - "shell.execute_reply": "2026-07-14T19:10:52.047728Z" + "iopub.execute_input": "2026-07-14T21:19:51.189656Z", + "iopub.status.busy": "2026-07-14T21:19:51.189220Z", + "iopub.status.idle": "2026-07-14T21:19:51.643855Z", + "shell.execute_reply": "2026-07-14T21:19:51.642662Z" } }, "outputs": [ @@ -640,10 +640,10 @@ "id": "fe136f2f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:52.052238Z", - "iopub.status.busy": "2026-07-14T19:10:52.051940Z", - "iopub.status.idle": "2026-07-14T19:10:52.725521Z", - "shell.execute_reply": "2026-07-14T19:10:52.724140Z" + "iopub.execute_input": "2026-07-14T21:19:51.646977Z", + "iopub.status.busy": "2026-07-14T21:19:51.646688Z", + "iopub.status.idle": "2026-07-14T21:19:52.330554Z", + "shell.execute_reply": "2026-07-14T21:19:52.329454Z" } }, "outputs": [ @@ -680,10 +680,10 @@ "id": "6ab2203a", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:52.729596Z", - "iopub.status.busy": "2026-07-14T19:10:52.729111Z", - "iopub.status.idle": "2026-07-14T19:10:53.260060Z", - "shell.execute_reply": "2026-07-14T19:10:53.258780Z" + "iopub.execute_input": "2026-07-14T21:19:52.334811Z", + "iopub.status.busy": "2026-07-14T21:19:52.334353Z", + "iopub.status.idle": "2026-07-14T21:19:52.834136Z", + "shell.execute_reply": "2026-07-14T21:19:52.832893Z" } }, "outputs": [ @@ -725,10 +725,10 @@ "id": "facae961", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:53.263937Z", - "iopub.status.busy": "2026-07-14T19:10:53.263606Z", - "iopub.status.idle": "2026-07-14T19:10:53.596257Z", - "shell.execute_reply": "2026-07-14T19:10:53.595263Z" + "iopub.execute_input": "2026-07-14T21:19:52.836950Z", + "iopub.status.busy": "2026-07-14T21:19:52.836652Z", + "iopub.status.idle": "2026-07-14T21:19:53.180973Z", + "shell.execute_reply": "2026-07-14T21:19:53.179559Z" } }, "outputs": [ @@ -767,10 +767,10 @@ "id": "f710be3d", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:53.600242Z", - "iopub.status.busy": "2026-07-14T19:10:53.599959Z", - "iopub.status.idle": "2026-07-14T19:10:55.886460Z", - "shell.execute_reply": "2026-07-14T19:10:55.883097Z" + "iopub.execute_input": "2026-07-14T21:19:53.184063Z", + "iopub.status.busy": "2026-07-14T21:19:53.183740Z", + "iopub.status.idle": "2026-07-14T21:19:55.358728Z", + "shell.execute_reply": "2026-07-14T21:19:55.357457Z" } }, "outputs": [ @@ -831,10 +831,10 @@ "id": "122cfeea", "metadata": { "execution": { - "iopub.execute_input": "2026-07-14T19:10:55.890574Z", - "iopub.status.busy": "2026-07-14T19:10:55.890147Z", - "iopub.status.idle": "2026-07-14T19:10:56.057809Z", - "shell.execute_reply": "2026-07-14T19:10:56.056266Z" + "iopub.execute_input": "2026-07-14T21:19:55.362288Z", + "iopub.status.busy": "2026-07-14T21:19:55.361948Z", + "iopub.status.idle": "2026-07-14T21:19:55.368105Z", + "shell.execute_reply": "2026-07-14T21:19:55.366668Z" } }, "outputs": [ @@ -843,18 +843,17 @@ "output_type": "stream", "text": [ "python: 3.12.13 | packaged by conda-forge | (main, Mar 5 2026, 16:50:00) [GCC 14.3.0]\n", - "selectsim: 0.2.0\n", + "selectsim: 0.3.0\n", "numpy: 2.1.3\n", "pandas: 2.2.3\n", "scipy: 1.14.1\n", - "matplotlib: 3.11.0\n", - "seaborn: 0.13.2\n" + "matplotlib: 3.11.0\n" ] } ], "source": [ "import sys\n", - "import numpy, pandas, scipy, matplotlib, seaborn\n", + "import numpy, pandas, scipy, matplotlib\n", "import selectsim\n", "\n", "print(\"python:\", sys.version)\n", @@ -862,8 +861,7 @@ "print(\"numpy:\", numpy.__version__)\n", "print(\"pandas:\", pandas.__version__)\n", "print(\"scipy:\", scipy.__version__)\n", - "print(\"matplotlib:\", matplotlib.__version__)\n", - "print(\"seaborn:\", seaborn.__version__)" + "print(\"matplotlib:\", matplotlib.__version__)" ] } ], diff --git a/pyproject.toml b/pyproject.toml index ddd78ae..405d348 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "selectsim" -version = "0.3.0" +version = "0.3.1" description = "A python package that implements the SelectSim algorithm" authors = [ { name = "Arvind Iyer", email = "ayalurarvind@gmail.com" }, @@ -22,7 +22,6 @@ dependencies = [ "tqdm>=4.66.0,<5", "pyarrow>=18.0.0,<19", "matplotlib>=3.9.0,<4", - "seaborn>=0.13.0,<0.14", ] [project.urls] diff --git a/selectsim/__init__.py b/selectsim/__init__.py index f7be984..120857f 100644 --- a/selectsim/__init__.py +++ b/selectsim/__init__.py @@ -49,16 +49,6 @@ stat_maf_gene, maf_to_gam ) -from selectsim.plotting import ( - theme_publication, - obs_exp_scatter, - significance_heatmap, - overlap_pair_extract, - ridge_plot_ed, - ridge_plot_ed_compare, - oncoprint_pair, - oncoprint -) from selectsim.io import ( read_parquet, write_parquet, @@ -66,7 +56,7 @@ open_zarr_store ) -__version__ = "0.3.0" +__version__ = "0.3.1" __all__ = [ # Main function @@ -133,3 +123,34 @@ "create_zarr_null_store", "open_zarr_store", ] + +# Plotting functions are resolved lazily (PEP 562), rather than imported +# eagerly above like everything else: selectsim.plotting pulls in +# matplotlib and scipy (measured ~1s import overhead combined, mostly +# matplotlib.pyplot) -- overhead every `import selectsim` would otherwise +# pay even for pure-computation use (e.g. just calling selectX()) that +# never touches a plotting function. scipy is in fact used nowhere +# outside plotting.py either. Deferring the import to first access of one +# of these names (only) keeps `selectsim.` working exactly +# as before, without forcing the cost on callers who never ask for it. +_LAZY_PLOTTING_ATTRS = frozenset({ + "theme_publication", + "obs_exp_scatter", + "significance_heatmap", + "overlap_pair_extract", + "ridge_plot_ed", + "ridge_plot_ed_compare", + "oncoprint_pair", + "oncoprint", +}) + + +def __getattr__(name): + if name in _LAZY_PLOTTING_ATTRS: + from selectsim import plotting + return getattr(plotting, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def __dir__(): + return sorted(set(globals()) | _LAZY_PLOTTING_ATTRS) diff --git a/selectsim/plotting.py b/selectsim/plotting.py index 10c8ba9..3c0775b 100644 --- a/selectsim/plotting.py +++ b/selectsim/plotting.py @@ -3,8 +3,8 @@ Python port of ``R/selectX_plot.R``. The R implementation is built on ``ggplot2``/``ggpubr``/``ggridges``; this module reproduces the same visual -intent using ``matplotlib`` + ``seaborn`` (the library choice made for the -Python port), since no display is assumed to be available. +intent using ``matplotlib`` (the library choice made for the Python port), +since no display is assumed to be available. Functions --------- @@ -503,7 +503,7 @@ def ridge_plot_ed( Implementation note: this is the standard "joyplot"/ridgeline technique -- per-row Gaussian KDEs evaluated on a shared x-grid and stacked with a constant y-offset -- rather than ``ggridges::geom_density_ridges``, - since matplotlib/seaborn has no ridge-plot geom built in. + since matplotlib has no ridge-plot geom built in. Parameters ----------