Repository navigation
Per-channel group statistics and explicit refusal of cyclic mask hierarchies - #178
Conversation
group_summaries pooled every channel of every XYC plane of a group into one summary, so two fluorescence markers (C0 near 10, C1 near 1000) gave one mixed mean near 505, and its plane_count counted channel histograms as planes. Each group now has one summary per channel (GroupSummary.channel), with xyc_plane_count and channel_histogram_count reported apart; group_table adds a channel column and the notebook's step 4 plots one panel per channel and counts groups, not summaries. The all-channel aggregate is only made with pool_channels=True and is labelled "all channels (pooled)". A group whose planes have different channel counts is refused. plane_count stays as a deprecated alias of channel_histogram_count. No file schema changes: plane_histograms.csv, the manifest and every output are unchanged; step 4 writes nothing. Single-channel tables give the same numbers. Python API change documented in the changelog with migration notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VbytvF3Gsa85ix8Ne2yMDW
The hierarchy text started only at mask types without parents and followed children without a visited set. Pairwise evidence summed over different images can make A inside B, B inside C and C inside A: no type was a root, so the report printed nothing under "Hierarchy (direct parents only)", and a cycle below a root would recurse without end. MaskReport.hierarchy_cycles (mask_scan.hierarchy_cycles) names each group of types whose direct parents form a cycle, self-parents included. The hierarchy lines (CLI summary, notebook) start with the named warning mask_hierarchy_cycle listing every type and link of the cycle, and draw the condensation: the group as one "[cycle: A, B, C]" entry, so every type is listed and no direction is guessed. Acyclic hierarchies print as before; mask_relationships.json is unchanged (report version 1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VbytvF3Gsa85ix8Ne2yMDW
Independent review of 71ce36d: approve, no defects foundI reviewed this in a worktree of #104 (group statistics per channel): all claims hold in the code
#105 (cycles in the mask hierarchy)
Every shape terminated. Each cycle got one Tests are real. Run against Gate: Nits, not blocking:
Safe to merge once it is out of draft. Generated by Claude Code |
Fixes #104, fixes #105
Summary (one commit per issue)
#104,
report.py,notebook_results.py,notebook_plots.py.group_summariesnow gives oneGroupSummaryper group and channel. It no longer pools every channel of every XYC plane. With C0 near 10 and C1 near 1000, the old code reported a mixed mean near 505; it now reports two rows near 10 and near 1000.GroupSummary:channel: the channel index, orALL_CHANNELS(None) for an aggregate.xyc_plane_count: the XYC planes in the group.channel_histogram_count: the channel histograms pooled.plane_countis kept as a deprecated property. It equalschannel_histogram_countand emits aDeprecationWarning.group_tableadds the columnschannel,XYC planesandchannel histograms, which replaceplanes.pool_channels=True) and is labelledall channels (pooled).ValueError.#105,
mask_scan.py,mask_text.py.MaskReport.hierarchy_cyclesandmask_scan.hierarchy_cycles. They find the groups of types whose direct parents form a cycle (strongly connected groups, including a type that is its own parent).maskssummary and notebook) now starts with the named warningmask_hierarchy_cycle. It names every type and link in the cycle.[cycle: A, B, C]entry. Every type is listed and no direction is guessed. The infinite recursion when a cycle sits below a root is gone.Consumer and schema analysis
plane_histograms.csv(schema 4),run_manifest.json,experiment_design.csvandmask_relationships.json(report version 1) are byte-for-byte unaffected. Step 4 writes no file.mask_relationships.jsonalready kept every direct link; adding a cycle key would need a report-version bump, so it is left as a possible follow-up.GroupSummaryhas new fields, and its positional constructor order changed.group_tablehas new columns.CHANGELOG.mdunder Unreleased: readxyc_plane_countorchannel_histogram_countand select rows bychannel; usepool_channels=Trueto reproduce a pre-[Sad Catapella B9][Scientific reporting] Group statistics silently pool distinct fluorescence channels and mislabel channel measurements as planes #104 table.group_summaries,GroupSummary,group_table,channel_histograms,mask_hierarchy_linesormask_report_summary.mask_relationships.json; it appears only in design docs.fitting._channel_histograms) already pools only the selected channels.maskssummary. All are updated and tested.mask_hierarchy_cyclecode, the API example), AGENTS.md, and a plan §12 Q27 amendment.Tests
New regressions; each fails on main:
run_experiment;python tools/check.py(ruff check, ruff format --check, pytest.[dev,notebook,zarr], code rules): 2291 passed, 4 skipped. The skips are environment-dependent: 3 because this file system distinguishes case, 1 because root ignores file modes. Linux and Python 3.13 only; CI covers Windows and macOS.Notes for review
Generated by Claude Code