ep-touche 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. ep_touche-0.1.0/.gitattributes +2 -0
  2. ep_touche-0.1.0/.github/workflows/ci.yml +48 -0
  3. ep_touche-0.1.0/.github/workflows/publish.yml +42 -0
  4. ep_touche-0.1.0/.gitignore +187 -0
  5. ep_touche-0.1.0/CLAUDE.md +150 -0
  6. ep_touche-0.1.0/LICENSE +21 -0
  7. ep_touche-0.1.0/PKG-INFO +177 -0
  8. ep_touche-0.1.0/README.md +155 -0
  9. ep_touche-0.1.0/docs/README.md +35 -0
  10. ep_touche-0.1.0/docs/api.md +163 -0
  11. ep_touche-0.1.0/docs/cli.md +497 -0
  12. ep_touche-0.1.0/docs/images/apa-flv-vs-dmso-obsoverexp.png +0 -0
  13. ep_touche-0.1.0/docs/images/apa-trp-vs-dmso-obsoverexp.png +0 -0
  14. ep_touche-0.1.0/docs/images/background-flv-vs-dmso.png +0 -0
  15. ep_touche-0.1.0/docs/images/background-trp-vs-dmso.png +0 -0
  16. ep_touche-0.1.0/docs/images/background-trp-vs-flv.png +0 -0
  17. ep_touche-0.1.0/docs/images/local-decay-violinplot.png +0 -0
  18. ep_touche-0.1.0/docs/micro-c-preprocessing.md +132 -0
  19. ep_touche-0.1.0/docs/reproducing-reference-plots.md +326 -0
  20. ep_touche-0.1.0/docs/testing-and-publishing.md +75 -0
  21. ep_touche-0.1.0/notes/benchmarks/benchmark_numba_kernels.py +375 -0
  22. ep_touche-0.1.0/notes/numba-implementation-plan.md +955 -0
  23. ep_touche-0.1.0/notes/touche-implementation-plan.md +873 -0
  24. ep_touche-0.1.0/pyproject.toml +43 -0
  25. ep_touche-0.1.0/scripts/_report.py +594 -0
  26. ep_touche-0.1.0/scripts/reference_replication.md +176 -0
  27. ep_touche-0.1.0/scripts/reference_replication.py +802 -0
  28. ep_touche-0.1.0/src/touche/__init__.py +12 -0
  29. ep_touche-0.1.0/src/touche/__main__.py +7 -0
  30. ep_touche-0.1.0/src/touche/anchors.py +27 -0
  31. ep_touche-0.1.0/src/touche/apa.py +581 -0
  32. ep_touche-0.1.0/src/touche/api.py +60 -0
  33. ep_touche-0.1.0/src/touche/backends.py +79 -0
  34. ep_touche-0.1.0/src/touche/background.py +417 -0
  35. ep_touche-0.1.0/src/touche/cli/__init__.py +5 -0
  36. ep_touche-0.1.0/src/touche/cli/__main__.py +7 -0
  37. ep_touche-0.1.0/src/touche/cli/apa.py +160 -0
  38. ep_touche-0.1.0/src/touche/cli/background.py +166 -0
  39. ep_touche-0.1.0/src/touche/cli/local_decay.py +270 -0
  40. ep_touche-0.1.0/src/touche/cli/main.py +32 -0
  41. ep_touche-0.1.0/src/touche/cli/preprocess.py +119 -0
  42. ep_touche-0.1.0/src/touche/cli/utils.py +59 -0
  43. ep_touche-0.1.0/src/touche/contacts.py +415 -0
  44. ep_touche-0.1.0/src/touche/instrumentation.py +75 -0
  45. ep_touche-0.1.0/src/touche/io.py +115 -0
  46. ep_touche-0.1.0/src/touche/local_decay.py +1020 -0
  47. ep_touche-0.1.0/src/touche/models.py +84 -0
  48. ep_touche-0.1.0/src/touche/numba/__init__.py +10 -0
  49. ep_touche-0.1.0/src/touche/numba/_kernel_imports.py +19 -0
  50. ep_touche-0.1.0/src/touche/numba/apa.py +212 -0
  51. ep_touche-0.1.0/src/touche/numba/background.py +313 -0
  52. ep_touche-0.1.0/src/touche/numba/local_decay.py +320 -0
  53. ep_touche-0.1.0/src/touche/numba/stats.py +66 -0
  54. ep_touche-0.1.0/src/touche/pair_stats.py +108 -0
  55. ep_touche-0.1.0/src/touche/pipelines.py +407 -0
  56. ep_touche-0.1.0/src/touche/preprocess.py +90 -0
  57. ep_touche-0.1.0/src/touche/stats.py +67 -0
  58. ep_touche-0.1.0/tests/fixtures/distiller_pairs.tsv +3 -0
  59. ep_touche-0.1.0/tests/test_apa.py +69 -0
  60. ep_touche-0.1.0/tests/test_apa_aggregate.py +160 -0
  61. ep_touche-0.1.0/tests/test_api.py +20 -0
  62. ep_touche-0.1.0/tests/test_background.py +85 -0
  63. ep_touche-0.1.0/tests/test_background_count.py +255 -0
  64. ep_touche-0.1.0/tests/test_contacts.py +215 -0
  65. ep_touche-0.1.0/tests/test_local_decay.py +642 -0
  66. ep_touche-0.1.0/tests/test_pair_stats.py +68 -0
  67. ep_touche-0.1.0/tests/test_pipelines.py +120 -0
  68. ep_touche-0.1.0/tests/test_preprocess.py +67 -0
  69. ep_touche-0.1.0/tests/test_stats.py +87 -0
  70. ep_touche-0.1.0/tests/test_version.py +15 -0
  71. ep_touche-0.1.0/uv.lock +1769 -0
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,48 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: Python ${{ matrix.python-version }}
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version:
20
+ - "3.10"
21
+ - "3.11"
22
+ - "3.12"
23
+
24
+ steps:
25
+ - name: Check out repository
26
+ uses: actions/checkout@v4
27
+
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v5
30
+ with:
31
+ enable-cache: true
32
+
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v5
35
+ with:
36
+ python-version: ${{ matrix.python-version }}
37
+
38
+ - name: Install dependencies
39
+ run: uv sync --dev --extra legacy --python ${{ matrix.python-version }}
40
+
41
+ - name: Lint
42
+ run: uv run ruff check src tests
43
+
44
+ - name: Test
45
+ run: uv run pytest
46
+
47
+ - name: Build package
48
+ run: uv build
@@ -0,0 +1,42 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write
12
+
13
+ jobs:
14
+ publish:
15
+ name: Build and publish ep-touche
16
+ runs-on: ubuntu-latest
17
+ environment:
18
+ name: pypi
19
+ url: https://pypi.org/project/ep-touche/
20
+
21
+ steps:
22
+ - name: Check out repository
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v5
27
+ with:
28
+ enable-cache: true
29
+
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v5
32
+ with:
33
+ python-version: "3.12"
34
+
35
+ - name: Build distributions
36
+ run: uv build
37
+
38
+ - name: Check distributions
39
+ run: uvx twine check dist/*
40
+
41
+ - name: Publish to PyPI
42
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,187 @@
1
+ **/.DS_Store
2
+ **/.vscode
3
+ _reference/
4
+ benchmark/
5
+
6
+ # Byte-compiled / optimized / DLL files
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+
11
+ # C extensions
12
+ *.so
13
+
14
+ # Distribution / packaging
15
+ .Python
16
+ build/
17
+ develop-eggs/
18
+ dist/
19
+ downloads/
20
+ eggs/
21
+ .eggs/
22
+ lib/
23
+ lib64/
24
+ parts/
25
+ sdist/
26
+ var/
27
+ wheels/
28
+ share/python-wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+
34
+ # PyInstaller
35
+ # Usually these files are written by a python script from a template
36
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
37
+ *.manifest
38
+ *.spec
39
+
40
+ # Installer logs
41
+ pip-log.txt
42
+ pip-delete-this-directory.txt
43
+
44
+ # Unit test / coverage reports
45
+ htmlcov/
46
+ .tox/
47
+ .nox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ *.py,cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+ cover/
58
+
59
+ # Translations
60
+ *.mo
61
+ *.pot
62
+
63
+ # Django stuff:
64
+ *.log
65
+ local_settings.py
66
+ db.sqlite3
67
+ db.sqlite3-journal
68
+
69
+ # Flask stuff:
70
+ instance/
71
+ .webassets-cache
72
+
73
+ # Scrapy stuff:
74
+ .scrapy
75
+
76
+ # Sphinx documentation
77
+ docs/_build/
78
+
79
+ # PyBuilder
80
+ .pybuilder/
81
+ target/
82
+
83
+ # Jupyter Notebook
84
+ .ipynb_checkpoints
85
+
86
+ # IPython
87
+ profile_default/
88
+ ipython_config.py
89
+
90
+ # pyenv
91
+ # For a library or package, you might want to ignore these files since the code is
92
+ # intended to run in multiple environments; otherwise, check them in:
93
+ # .python-version
94
+
95
+ # pipenv
96
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
97
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
98
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
99
+ # install all needed dependencies.
100
+ #Pipfile.lock
101
+
102
+ # UV
103
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
104
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
105
+ # commonly ignored for libraries.
106
+ #uv.lock
107
+ .uv-cache/
108
+
109
+ # poetry
110
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
111
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
112
+ # commonly ignored for libraries.
113
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
114
+ #poetry.lock
115
+
116
+ # pdm
117
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
118
+ #pdm.lock
119
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
120
+ # in version control.
121
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
122
+ .pdm.toml
123
+ .pdm-python
124
+ .pdm-build/
125
+
126
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
127
+ __pypackages__/
128
+
129
+ # Celery stuff
130
+ celerybeat-schedule
131
+ celerybeat.pid
132
+
133
+ # SageMath parsed files
134
+ *.sage.py
135
+
136
+ # Environments
137
+ .env
138
+ .venv
139
+ env/
140
+ venv/
141
+ ENV/
142
+ env.bak/
143
+ venv.bak/
144
+
145
+ # Spyder project settings
146
+ .spyderproject
147
+ .spyproject
148
+
149
+ # Rope project settings
150
+ .ropeproject
151
+
152
+ # mkdocs documentation
153
+ /site
154
+
155
+ # mypy
156
+ .mypy_cache/
157
+ .dmypy.json
158
+ dmypy.json
159
+
160
+ # Pyre type checker
161
+ .pyre/
162
+
163
+ # pytype static type analyzer
164
+ .pytype/
165
+
166
+ # Cython debug symbols
167
+ cython_debug/
168
+
169
+ # PyCharm
170
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
171
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
172
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
173
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
174
+ #.idea/
175
+
176
+ # Ruff stuff:
177
+ .ruff_cache/
178
+
179
+ # PyPI configuration file
180
+ .pypirc
181
+
182
+ # Cursor
183
+ # Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
184
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
185
+ # refer to https://docs.cursor.com/context/ignore-files
186
+ .cursorignore
187
+ .cursorindexingignore
@@ -0,0 +1,150 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## What this is
6
+
7
+ `touche` (PyPI: `ep-touche`) is a Python package + CLI that refactors the
8
+ Danko Lab `E-P_contacts` R/shell reference workflows for high-resolution
9
+ chromatin contact (Micro-C) analysis into a dependency-light, faster
10
+ implementation. It starts from processed pairs files (from `distiller-nf` or
11
+ equivalent) — it does not do FASTQ alignment, dedup, or cooler generation.
12
+
13
+ The original reference implementation lives at `_reference/E-P_contacts/`
14
+ (R/awk/shell scripts). New Python implementations are expected to reproduce
15
+ its numeric behavior; when in doubt about expected output shape or a
16
+ threshold/default, check the corresponding reference script there before
17
+ changing behavior.
18
+
19
+ `touche` uses `polars` as its only dataframe library, both internally and at
20
+ the public `touche.api` surface (`pl.DataFrame`/`pl.LazyFrame`) — `pandas` is
21
+ not a dependency. CSV outputs have no implicit index column (polars has no
22
+ index concept); anywhere a row label matters (e.g. APA's pixel-bin matrices)
23
+ it's an explicit `bin_label` column instead.
24
+
25
+ ## Commands
26
+
27
+ Development uses `uv`.
28
+
29
+ ```bash
30
+ uv sync --dev # install package + dev deps
31
+ uv run touche --help # run the CLI
32
+ uv run pytest # run the full test suite
33
+ uv run pytest tests/test_apa.py # run one test file
34
+ uv run pytest tests/test_apa.py::test_name # run one test
35
+ uv run ruff check src tests # lint (this is what CI runs)
36
+ uv build # build sdist/wheel
37
+ ```
38
+
39
+ `mypy` is listed as a dev dependency but is not run in CI (`ci.yml` only runs
40
+ `ruff check` and `pytest`).
41
+
42
+ Numba is a core dependency (installed by `uv sync --dev`), so `backend="numba"`
43
+ code paths are always exercisable without an extra install step.
44
+
45
+ CI (`.github/workflows/ci.yml`) runs on Python 3.10/3.11/3.12: `uv sync --dev`,
46
+ `ruff check src tests`, `pytest`, `uv build`. See `docs/testing-and-publishing.md`
47
+ for the release/publish process (trusted PyPI publishing via `publish.yml`).
48
+
49
+ ## Architecture
50
+
51
+ ### Layering
52
+
53
+ - `touche.io` — `scan_pairs` builds a lazy `polars.LazyFrame` over pairs files
54
+ (canonical 9-column `touche` format vs. 10+ column `distiller`/pairtools
55
+ format, `source="auto"` sniffs the layout from the first data row's field
56
+ count). All downstream code reads pairs through this module.
57
+ - `touche.models` — shared frozen dataclasses: `ContactIndex` (numpy arrays
58
+ for one chromosome: `pos_a`, `pos_b`, strand, mapq), `PairStats`,
59
+ `NamedPath`/`NamedDepth` (used for `NAME=PATH` / `NAME=INTEGER` CLI
60
+ arguments across treatment/control commands).
61
+ - `touche.contacts` — builds `ContactIndex` per chromosome from a pairs file
62
+ and reads/writes chromosome-sharded NPZ caches (`build_contact_indexes`,
63
+ `build_npz_cache`, `load_npz_cache`, `write_npz_cache`). This is the
64
+ in-memory numeric representation almost every compute function operates on.
65
+ `build_npz_cache`'s default (non-`"all"`) path spools the scanned pairs to a
66
+ local Parquet file once, then materializes and writes one chromosome's rows
67
+ at a time — peak memory is bounded by the largest single chromosome, not the
68
+ whole file.
69
+ - `touche.preprocess` — pairs filtering/conversion (mapq + cis/trans filters,
70
+ distiller→touche format conversion).
71
+ - `touche.pair_stats` — QC accumulation (row counts, cis/trans, mapq
72
+ pass/fail, per-chromosome, distance histogram) shared by `preprocess qc`,
73
+ `preprocess summarize`, and cache building (so a large file is scanned once
74
+ for both the cache and its QC).
75
+ - Domain modules — `touche.local_decay`, `touche.apa`, `touche.background`:
76
+ each exposes an in-memory `compute_*` function operating on `ContactIndex`
77
+ objects/anchors, plus a file-driven wrapper (e.g. `call_local_decay`,
78
+ `aggregate_apa`, `count_ep_and_background`) that reads inputs, calls the
79
+ compute function, and writes output files.
80
+ - `touche.pipelines` — `run_*_pipeline` functions that chain the file-driven
81
+ wrappers for a full command-group workflow (e.g. call → assign → plot for
82
+ local-decay) and write a `manifest.json` (inputs, parameters, outputs,
83
+ metrics, `touche` version, elapsed time, optional per-step timings).
84
+ - `touche.api` — the curated notebook-facing re-export surface (`import
85
+ touche.api as tt`); update `__all__` here when adding a new public
86
+ compute/plot function intended for interactive use.
87
+ - `touche.cli/` — one argparse module per command group (`preprocess.py`,
88
+ `local_decay.py`, `background.py`, `apa.py`), wired together in `main.py`.
89
+ Each subcommand's `func` callback maps CLI args to the corresponding
90
+ domain/pipeline function and prints a JSON summary via `cli/utils.py`.
91
+ - `touche.backends` / `touche.numba/` — Numba acceleration, a core
92
+ dependency. `touche.numba` is a subpackage split by the domain that uses
93
+ each kernel (`touche.numba.apa`, `.background`, `.local_decay`, `.stats`);
94
+ numba is never imported at package import time — only when a
95
+ numba-accelerated function is actually called, so every `from
96
+ touche.numba.<domain> import ...` happens inside a function body, never at
97
+ a domain module's own top level. Counting (APA matrix/1D signal,
98
+ EP/background pairs, local-decay's observed-count helper) always uses its
99
+ Numba kernel — each was verified exact-equivalent to the plain NumPy
100
+ implementation it replaced (see `notes/numba-implementation-plan.md`), so
101
+ there's no `backend` choice exposed for it. `lowess_backend`
102
+ (`"numba"`/`"statsmodels"`) and `fisher_backend` (`"numba"`/`"scipy"`) are
103
+ different: each numba path is a validated but inexact approximation of its
104
+ alternative, so both choices remain exposed, defaulting to `"numba"`.
105
+ - `touche.instrumentation` — the shared `Instrumentation` dataclass
106
+ (`progress`, `profile`) threaded through compute/pipeline functions as the
107
+ `progress=` argument. `instrument.iter(...)` wraps iterables with `tqdm`
108
+ when enabled; `instrument.step("name")` is a context manager that records
109
+ step timings when `profile=True`. CLI flags: `--progress`/`--profile`.
110
+
111
+ ### Contact indexing strategies
112
+
113
+ Local-decay (and similar chromosome-scoped work) supports three
114
+ `index_strategy` values, most relevant when touching caching or performance
115
+ code:
116
+
117
+ - `cache` (default): build/reuse chromosome-sharded NPZ caches under
118
+ `--cache-dir` (or `contact_index_cache/` next to the output), loading one
119
+ chromosome shard at a time — lowest memory.
120
+ - `all`: read the whole pairs file once, hold every chromosome in memory —
121
+ fastest given enough RAM.
122
+ - `chromosome`: re-scan the pairs file once per bait chromosome, keeping only
123
+ one chromosome resident — no persistent cache files, but slow on gzipped
124
+ input.
125
+
126
+ ### Pairs source formats
127
+
128
+ Most commands take `--source {auto,distiller,touche}`. `touche` format is the
129
+ canonical 9-column layout this package writes (`preprocess filter-pairs`/
130
+ `convert-pairs`); `distiller` is pairtools/distiller-nf's 10+ column layout
131
+ (leading read_id column). `auto` infers from field count — don't rely on
132
+ `auto` when column counts could plausibly overlap between formats.
133
+
134
+ ### Repository conventions
135
+
136
+ - `docs/` — human-facing usage guides a `touche` user should read directly
137
+ (CLI reference, API guide, preprocessing, reproducing reference plots,
138
+ testing/publishing). Keep these current when changing CLI flags or public
139
+ API signatures.
140
+ - `notes/` — agent-facing implementation plans, benchmark logs, and design
141
+ sketches. Do not put user-facing documentation here, and don't move scratch
142
+ planning into `docs/` unless it's rewritten for users.
143
+ - `scripts/` — standalone, runnable tools promoted out of `notes/` once
144
+ they're more than a one-off benchmark log (e.g. `reference_replication.py`,
145
+ which downloads the upstream E-P_contacts example data and replicates its
146
+ reference workflows end to end). Not part of the `touche` package build.
147
+ - `_reference/E-P_contacts/` — the original reference workflows being ported;
148
+ treat as read-only prior art, not something to modify.
149
+ - Manifests, cache directories, and other generated outputs (`.cache/`,
150
+ `contact_index_cache/`, `results/`) are run artifacts, not source.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adam Youlin He
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: ep-touche
3
+ Version: 0.1.0
4
+ Summary: Tools for preprocessing and analyzing high-resolution chromatin contact data.
5
+ Project-URL: Homepage, https://github.com/adamyhe/touche
6
+ Project-URL: Repository, https://github.com/adamyhe/touche
7
+ Project-URL: Issues, https://github.com/adamyhe/touche/issues
8
+ Author: Adam He
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: matplotlib>=3.7
13
+ Requires-Dist: numba>=0.59
14
+ Requires-Dist: numpy>=1.24
15
+ Requires-Dist: polars>=1.0
16
+ Requires-Dist: scipy>=1.10
17
+ Requires-Dist: seaborn>=0.12
18
+ Requires-Dist: tqdm>=4.65
19
+ Provides-Extra: legacy
20
+ Requires-Dist: statsmodels>=0.14; extra == 'legacy'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # touche
24
+
25
+ [![PyPI](https://img.shields.io/pypi/v/ep-touche)](https://pypi.org/project/ep-touche/)
26
+ [![Tests](https://github.com/adamyhe/touche/actions/workflows/ci.yml/badge.svg)](https://github.com/adamyhe/touche/actions/workflows/ci.yml)
27
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/ep-touche?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/ep-touche)
28
+
29
+ Python API and CLI tools for analyzing enhancer-promoter contacts (touches) from high-resolution chromatin contact data, refactored from the [Danko Lab E-P_contacts reference workflows](https://github.com/Danko-Lab/E-P_contacts).
30
+
31
+ `touche` starts from processed pairs files. Raw FASTQ processing, alignment,
32
+ deduplication, and cooler generation should be handled by an external workflow
33
+ such as [distiller-nf](https://github.com/open2c/distiller-nf).
34
+
35
+ ## Status
36
+
37
+ The current implementation includes CLI tools+API for:
38
+
39
+ - Micro-C pairs conversion, filtering, QC, and chromosome-sharded NPZ caches
40
+ - local-decay contact calling, pair-type assignment, and plotting
41
+ - Aggregated peak analysis (APA) aggregation and inter-sample APA comparison
42
+ - enhancer/promoter local-background counting and treatment comparison
43
+ - pipeline `run` wrappers that preserve intermediate outputs and write JSON
44
+ manifests
45
+
46
+ ## Installation
47
+
48
+ Install with `pip` from PyPI (package name is `ep-touche`):
49
+
50
+ ```bash
51
+ pip install ep-touche
52
+ touche --help
53
+ ```
54
+
55
+ `uv` can also install and run the package:
56
+
57
+ ```bash
58
+ uv add ep-touche
59
+ uv run touche --help
60
+ ```
61
+
62
+ `local-decay` also supports a statsmodels-backed LOWESS path
63
+ (`lowess_backend="statsmodels"`) for exact reference comparisons
64
+ (at the cost of being much slower than our custom implementation).
65
+ Install the optional `legacy` extra only if you need that backend:
66
+
67
+ ```bash
68
+ pip install ep-touche[legacy]
69
+ # or: uv sync --extra legacy
70
+ ```
71
+
72
+ ### Older CPUs and Rosetta
73
+
74
+ Polars publishes an `lts-cpu` wheel for older CPUs and for x86-64 Python on
75
+ Apple Silicon under Rosetta. Because `polars-lts-cpu` provides the same
76
+ `import polars` module but is a different Python distribution, it does not
77
+ automatically satisfy `ep-touche`'s normal `polars>=1.0` dependency. If the
78
+ standard Polars wheel does not run on your machine, install `ep-touche`, then
79
+ replace Polars in that environment:
80
+
81
+ ```bash
82
+ pip install ep-touche
83
+ pip uninstall polars
84
+ pip install "polars-lts-cpu>=1.0"
85
+ ```
86
+
87
+ ## CLI Overview
88
+
89
+ ```bash
90
+ touche preprocess --help
91
+ touche local-decay --help
92
+ touche apa --help
93
+ touche background --help
94
+ ```
95
+
96
+ Available command groups:
97
+
98
+ - `touche preprocess`: convert/filter pairs, write QC summaries, and build NPZ
99
+ caches.
100
+ - `touche local-decay`: call observed/expected contacts, assign pair types, plot
101
+ distributions, or run the full local-decay workflow.
102
+ - `touche apa`: aggregate APA matrices, compare treatment/control APAs, or run a
103
+ paired APA workflow.
104
+ - `touche background`: count EP/background contacts, compare treatment ratios,
105
+ or run the full EP/background workflow.
106
+
107
+ See the [CLI reference](docs/cli.md) for examples, common options, and expected
108
+ outputs.
109
+
110
+ ## Typical workflow
111
+
112
+ Start from analysis-ready `.pairs` or `.pairs.gz` files produced by distiller-nf
113
+ or an equivalent workflow.
114
+
115
+ 1. Filter or convert pairs with `touche preprocess`.
116
+ 2. For real or repeated local-decay runs, build a position-only cache with
117
+ `touche preprocess build-cache --no-metadata`.
118
+ 3. Use the `run` wrappers for end-to-end analyses:
119
+ `touche local-decay run`, `touche apa run`, and `touche background run`.
120
+ 4. Use individual subcommands such as `local-decay call` or `background count`
121
+ when debugging or replacing one stage.
122
+
123
+ Many of the computation-heavy steps use `numba` acceleration and parallelism.
124
+ Set `NUMBA_NUM_THREADS` before running to control CPU usage (default is to use
125
+ all threads on machine):
126
+
127
+ ```bash
128
+ NUMBA_NUM_THREADS=8 touche background run ...
129
+ ```
130
+
131
+ ## Python API
132
+
133
+ For notebooks and custom scripts, import the provisional API surface:
134
+
135
+ ```python
136
+ import touche.api as tt
137
+
138
+ indexes = tt.build_contact_indexes("sample.nodups_30_intra.pairs.gz", source="touche")
139
+ ```
140
+
141
+ The API is organized around reading pairs and anchors once, running in-memory
142
+ compute functions such as `compute_apa`, `compute_local_decay`, and
143
+ `compute_ep_and_background`, then displaying or saving returned Matplotlib
144
+ figures as needed. Long-running CLI and API calls support optional progress
145
+ bars and lightweight profiling. See the [API guide](docs/api.md) for examples.
146
+
147
+ ## Documentation
148
+
149
+ Detailed usage notes live under `docs/`:
150
+
151
+ - [Docs index](docs/README.md): human-facing guides and documentation
152
+ conventions.
153
+ - [CLI reference](docs/cli.md): command groups, common options, examples,
154
+ outputs, and run-wrapper manifests.
155
+ - [Micro-C preprocessing](docs/micro-c-preprocessing.md): distiller-nf boundary,
156
+ pairs format expectations, filtering, QC, and cache building.
157
+ - [API](docs/api.md): provisional in-memory APIs for notebooks, interactive analyses,
158
+ and custom scripts.
159
+ - [Reproducing reference plots](docs/reproducing-reference-plots.md): end-to-end
160
+ commands for the reference local-decay, APA, and EP/background plots.
161
+ - [Testing and publishing](docs/testing-and-publishing.md): CI, local checks,
162
+ and PyPI release workflow for the `ep-touche` distribution.
163
+
164
+ Implementation plans, experiment logs, and agent-facing notes belong in
165
+ `notes/`.
166
+
167
+ ## Development
168
+
169
+ Development is managed with `uv`.
170
+
171
+ ```bash
172
+ git clone https://github.com/adamyhe/touche.git
173
+ cd touche/
174
+ uv sync --dev
175
+ uv run touche --help
176
+ uv run pytest
177
+ ```