Skip to content

Add SPIDER: epigenetically-seeded PANDA - #397

Open
dcolinmorgan wants to merge 4 commits into
netZoo:masterfrom
dcolinmorgan:add-spider
Open

dcolinmorgan wants to merge 4 commits into
netZoo:masterfrom
dcolinmorgan:add-spider

Conversation

@dcolinmorgan

Copy link
Copy Markdown
Contributor

Add SPIDER: epigenetically-seeded PANDA

Summary

Python port of netZooR::spider (Sonawane et al., npj Systems Biology and
Applications
2021). SPIDER extends PANDA by masking the motif prior with a
binary epigenetic filter (e.g. open-chromatin / ATAC-seq / DNase-seq) so that
only motif interactions in accessible chromatin are retained, then applies a
degree-adjustment step, and runs the standard PANDA message-passing loop.

Closes: n/a — new feature.

Design

Spider subclasses Panda and reuses the shared numerical primitives
already in the repo:

  • Panda.processData — input alignment and prior parsing
  • netZooPy.panda.calculations.normalize_network — z-score normalization
  • netZooPy.panda.calculations.compute_panda — the CPU/GPU message-passing loop
    (t_function, update_diagonal)

This mirrors how Puma is structured, and means the message-passing math is
identical to PANDA by construction — the only SPIDER-specific code is:

  1. _apply_epifilter — element-wise mask of motif weights by the filter
    (epifilter=None short-circuits to plain PANDA)
  2. _degree_adjust — hub-node degree correction, ported line-for-line from
    netZooR::degreeAdjust

Files added / changed

netZooPy/__init__.py                       # + from netZooPy import spider
netZooPy/spider/__init__.py                # exports Spider
netZooPy/spider/spider.py                  # Spider(Panda)
netZooPy/spider/run_spider.py              # CLI, mirrors run_puma.py
tests/test_spider.py                       # 5 pytest cases
tests/spider/ToyData/ToyExpressionData.txt # 15 genes × 25 samples
tests/spider/ToyData/ToyMotifData.txt      # 30 TF-gene motif edges
tests/spider/ToyData/ToyEpiFilterData.txt  # matched binary filter
tests/spider/ToyData/ToyPPIData.txt        # 4 PPI edges

No existing files touched other than the one-line registration in
netZooPy/__init__.py.

Usage

from netZooPy.spider.spider import Spider

spider_obj = Spider(
    "expression.txt",   # genes × samples (path or DataFrame)
    "motif.txt",        # TF, gene, weight
    "epifilter.txt",    # TF, gene, {0,1} — same rows/order as motif;
                        # None reduces SPIDER to PANDA
    "ppi.txt",          # TF, TF, weight
    save_memory=False,
)
spider_obj.spider_network            # nTFs × nGenes
spider_obj.export_spider_results     # [tf, gene, motif, force]
spider_obj.save_spider_results("spider.txt")

CLI:

python -m netZooPy.spider.run_spider \
    -e expression.txt -m motif.txt -f epifilter.txt -p ppi.txt -o spider.txt

Tests

$ pytest tests/test_spider.py -v
tests/test_spider.py::test_spider_runs                    PASSED
tests/test_spider.py::test_no_epifilter_equals_all_ones   PASSED
tests/test_spider.py::test_epifilter_changes_network      PASSED
tests/test_spider.py::test_epifilter_size_mismatch_raises PASSED
tests/test_spider.py::test_export_and_save                PASSED
5 passed in 1.48s

The key correctness invariant — epifilter=None produces the same network as
an all-ones filter — is explicitly asserted; this guarantees the epifilter path
degenerates cleanly to PANDA and any observed difference under a real filter is
attributable to the mask alone.

Regression: existing PUMA tests continue to pass. The pre-existing
test_panda.py::test_panda failure on this machine (missing netzoopy CLI on
PATH) is unrelated — it also fails on master without this branch.

Reference

Sonawane, A. R., Weiss, S. T., Glass, K., & Sharma, A. (2021). "Constructing
gene regulatory networks using epigenetic data." npj Systems Biology and
Applications
, 7(1), 1–13. https://doi.org/10.1038/s41540-021-00181-x

Ports netZooR::spider:
https://github.com/netZoo/netZooR/blob/master/R/SPIDER.R

Notes for reviewers

  • I do not have an R environment here to run netZooR::spider for
    bit-for-bit numerical parity. Because SPIDER reuses PANDA's exact numerical
    loop and its own transforms are direct ports of the R helpers, the risk is
    low, but a maintainer with R can validate on netZooR::pandaToyData by
    constructing an epifilter with the demo snippet from the SPIDER
    documentation.
  • Happy to add GPU-path coverage, a LionessSpider variant, or a tutorial
    notebook in a follow-up if wanted.

Port of netZooR::spider (Sonawane et al. 2021, npj Sys Bio Appl).
SPIDER extends PANDA by masking the motif prior with a binary
epigenetic filter (e.g. open-chromatin/ATAC-seq) and applying a
degree-adjustment step, then running the standard PANDA
message-passing loop.

Implementation:
- netZooPy/spider/{__init__.py, spider.py, run_spider.py}
- Spider subclasses Panda and reuses shared primitives from
  netZooPy.panda.calculations (compute_panda, normalize_network,
  t_function, update_diagonal) and Panda.processData, mirroring
  the Puma design.
- SPIDER-specific logic is limited to _apply_epifilter and
  _degree_adjust, ported line-for-line from netZooR.
- epifilter=None reduces SPIDER exactly to PANDA (tested).

Tests: tests/test_spider.py + tests/spider/ToyData
- runs and returns (nTFs x nGenes) with no NaN
- epifilter=None == all-ones filter (SPIDER null-mask invariant)
- non-trivial epifilter changes the network
- size-mismatched filter raises ValueError
- edge-list export and save round-trip

Registered in netZooPy/__init__.py alongside puma.
pkg_resources was removed in setuptools >= 81 (deprecated since
2023, slated for removal 2025-11-30). Its absence currently blocks
'from netZooPy import sambar', which cascades through
netZooPy/__init__.py and fails collection for every test in CI
(including this PR's test_spider.py).

Try to import pkg_resources for backward compatibility; if it is
gone, use importlib.resources.files() to resolve the bundled data
files. Behaviour is unchanged on older setuptools.
@dcolinmorgan

Copy link
Copy Markdown
Contributor Author

CI failed on pre-existing pkg_resources removal (setuptools >= 81) in netZooPy/sambar/sambar.py — this blocks test collection for every test, not just test_spider. Pushed a small drive-by fix (commit 2259108) that keeps pkg_resources when available and falls back to importlib.resources.files when it is missing. Behaviour unchanged on older setuptools; verified locally by blocking pkg_resources at import time.

Happy to split that fix into a separate PR if you'd prefer to keep this one purely additive.

setuptools >= 81 leaves a pkg_resources shim importable but strips
resource_filename, so the previous try/except-on-ImportError path
still crashed with AttributeError in CI. importlib.resources is
stdlib since Python 3.9; use it directly.
Inside sambar.py, __name__ is 'netZooPy.sambar.sambar' (the module);
importlib.resources.files() needs the package 'netZooPy.sambar',
which is __package__.
@dcolinmorgan

Copy link
Copy Markdown
Contributor Author

sambar fix worked — CI is now down to 21 passed, 2 failed, and tests/test_spider.py passes.

The two remaining failures are pre-existing on master and unrelated to this PR:

  • test_dragon — ValueError: Cannot load file containing pickled data when allow_pickle=False (numpy default, fixture file). Fails identically on clean master.
  • test_panda — shape mismatch (1000000, 3) vs (2, 3) on the ground-truth file (or FileNotFoundError locally). Also fails on clean master.

Verified by running pytest tests/test_dragon.py tests/test_panda.py on master with no other changes.

Happy to fix them in a separate PR if you'd like, but I'd rather not fold two more drive-bys into this one.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant