Metadata-Version: 2.5
Name: ClockCytePy
Version: 0.1.0
Summary: Python port of ClockCyteR.spatial — spatiotemporal and network analysis of circadian rhythms in SCN slices
Project-URL: Homepage, https://pypi.org/project/ClockCytePy/
Project-URL: Original R package, https://github.com/cabaJr/ClockCyteR.spatial
Author: Marco Ferrari
Author-email: Jamie Malcolm <jamiemalcolm12@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: ClockCyteR,SCN,bioluminescence,circadian,network analysis,organotypic slice,spatial analysis,suprachiasmatic nucleus
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.10
Requires-Dist: leidenalg>=0.10
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: python-igraph>=0.10
Requires-Dist: roifile>=2023.5.12
Requires-Dist: scikit-misc>=0.3
Requires-Dist: scipy>=1.10
Requires-Dist: shapely>=2.0
Provides-Extra: dev
Requires-Dist: pytest-xdist; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Provides-Extra: parquet
Requires-Dist: pyarrow>=12; extra == 'parquet'
Provides-Extra: plots
Requires-Dist: cmcrameri>=1.7; extra == 'plots'
Requires-Dist: matplotlib>=3.7; extra == 'plots'
Requires-Dist: pingouin>=0.5; extra == 'plots'
Requires-Dist: plotly>=5.15; extra == 'plots'
Provides-Extra: repro
Requires-Dist: reprofig>=0.5; extra == 'repro'
Description-Content-Type: text/markdown

# ClockCytePy

A Python port of [ClockCyteR.spatial](https://github.com/cabaJr/ClockCyteR.spatial) —
spatiotemporal and network analysis of circadian rhythms in organotypic suprachiasmatic
nucleus (SCN) slices.

Nothing here is new science. ClockCytePy reproduces the published R analysis
(Ferrari et al. 2026, *Advanced Science*, doi 10.1002/advs.75427) in Python, validated
number-for-number against the toy dataset that ships with the original package.

It ports **two trees**: the public `ClockCyteR.spatial` v0.1.0, and Marco Ferrari's
unpublished continuation of it. Neither is a superset of the other, so the port is a
merge — see
[docs/port-plan/private-fork-inventory.md](docs/port-plan/private-fork-inventory.md).
Because that merge includes work that exists nowhere public, it is published with
Marco's agreement and with him as co-author.

## Scope

**In scope** — everything either tree of ClockCyteR.spatial does, starting from
per-region intensity tables:

- outlier removal, LOESS smoothing, polynomial detrending
- FFT-NLLS rhythm fitting (period, phase, amplitude, RAE, AUC) per region
- spatial maps and local coherence of every fitted parameter
- correlation networks, Leiden clustering, node and cluster metrics
- circular statistics, group comparisons, batch orchestration and reports
- the whole-SCN Z-profile trace plot, and cross-slice aggregation, both from the
  unpublished fork

**Out of scope — deliberately.** Cell segmentation, tracking, drift correction, damping
estimates, rhythmicity significance testing, anatomical core/shell priors and
hierarchical statistics all live in [Auto-Organotypic](https://pypi.org/project/Auto-Organotypic/), upstream or downstream
of this package. ClockCytePy is not the place to extend the method; it is the place to
reproduce it.

## Boundary

```
  images  ──►  Auto-Organotypic  ──►  per-region CSVs  ──►  ClockCytePy  ──►  per-cell tables
 (upstream: registration,      (the ClockCyteR       (this package)     (downstream:
  outline, traces)              _results format)                        Auto-Organotypic)
```

ClockCytePy imports nothing from Auto-Organotypic, and Auto-Organotypic imports nothing from
ClockCytePy. They meet at a file format, never at a function call.

## Install

```bash
pip install ClockCytePy                    # analysis only
pip install "ClockCytePy[plots]"           # + the 34 figure types
pip install "ClockCytePy[plots,repro]"     # + each figure carries the table it was drawn from
```

From a checkout, `pip install -e .` with the same extras. The import name is
`clockcytepy`.

Needs Python 3.10+. No R at runtime, ever — R appears exactly once, by hand, to
bake the validation fixtures.

The `repro` extra pulls in [ReproFig](https://pypi.org/project/reprofig/), which draws nothing: it
wraps the *save*, embedding the plotted CSV, the source-file hashes and the
package version inside the image so a figure can be audited later. Everything
below works without it, and the image is identical either way.

## Use

```python
from clockcytepy import (
    Params, Channel, index_files, analyze_project, ranges_calculation, generate_plots,
)

params = Params(
    channels={
        "Ch1": Channel(enabled=False, label="red_channel",      grid_file="Ch1_1_grid_vals.csv"),
        "Ch2": Channel(enabled=True,  label="Syn-Axon-GCaMP6s", grid_file="Ch2_2_grid_vals.csv"),
        "Ch3": Channel(enabled=False, label="Brightfield",      grid_file="Ch3_3_grid_vals.csv"),
    },
    coherence=True, normalize_phase=True, time_res=0.5,
    intervals={"interval1": (0, 72)}, time_window=True, pixel_fct=2.82,
    base_dir=base_dir,
)

files   = index_files(params.base_dir)
results = analyze_project(files, params)
params.plotting.ranges = ranges_calculation(params, results)
generate_plots(results, params)
```

Anyone holding the R script should recognise that line for line. Output
filenames and folder layout match the R package's, so a ClockCytePy `_results`
folder and a ClockCyteR one are interchangeable.

## Status

**Stages 01-10 complete. 355 tests pass** — 150 parity tests against the frozen
R fixtures, 81 for the renderers and standalone helpers, 46 for paths the
reference run never reaches, and 78 independence checks.

Every quantity the R package reports is reproduced bit-exactly, with one
exception — the `offset` column, which differs on one cell in 1199 and which
nothing downstream reads. Full evidence in
[docs/parity-report.md](docs/parity-report.md).

| | |
|---|---|
| I/O, preprocessing, rhythm fitting | exact; cell membership identical |
| coherence, circular statistics | exact |
| correlation network | edge list identical, in order; node metrics exact |
| clustering | **cannot be exact — R is not reproducible against itself.** See below |
| orchestration, summaries, plot ranges | exact |
| plot data layer (8a) | exact, including the fork's Z-profile path |
| plot renderers (8b) | 30 of 34 types draw — the other four are dead branches in R. **249 of 249 files R writes are reproduced**, name for name, across all three configurations; same data, ranges and colours, not pixels |
| standalone helpers | all eight ported, including `network_plot`'s six styles |
| cross-slice aggregation (10) | done, validated to a lower standard by design |

**The finding worth knowing.** `cluster_leiden` is stochastic and the R package
sets no seed, so cluster assignments are not reproducible — two runs of the
published code on the identical graph agree on as little as 52% of cells on the
smallest slice. ClockCytePy samples the same distribution (it reproduces R's
stored partition exactly on all three slices) and exposes a `seed` argument so a
Python run can be made repeatable. It does not change the default, because that
would change behaviour.

**Four plot types build nothing** — in the R source, and therefore here.
`period_variance`, `phase_variance` and `two_phases_rayleigh` are empty
`switch` branches that report success and then hand the saver R's base `plot`
*function*; `overlap_hetmap` is disabled with `if (FALSE)`. None is in a
default list. They are reproduced as skips, with the reason recorded, rather
than invented.

## Seeing the figures

[docs/figures/r-vs-python-figure-parity](docs/figures/r-vs-python-figure-parity)
— a 28-frame review stack, ClockCyteR left and ClockCytePy right, one frame per
plot type on slice `L_1`. `preview.png` is the first frame.

## For Marco

[docs/handoff-for-marco.md](docs/handoff-for-marco.md) — the seeding decision,
two questions the code could not settle, the attribution gate, and a full list
of what was found and deliberately not fixed.

Plan and its corrections: [docs/port-plan/00_overview.md](docs/port-plan/00_overview.md),
[docs/port-plan/CORRECTIONS.md](docs/port-plan/CORRECTIONS.md).
