* feat: add multi-mask featurizer for Cell Painting workflows
Add a high-level Featurizer that orchestrates all cp_measure core and
correlation measurements across multiple named masks (e.g. nuclei,
cells, cytoplasm) and channels, returning a single DataFrame with
mask-prefixed columns and a union-of-labels index.
- make_featurizer() factory with per-feature toggle kwargs
- Multi-mask support: each mask plane produces prefixed columns,
outer-joined on label index (NaN for missing labels)
- Asymmetric correlation metrics (Pearson slope, Costes) use
permutations; symmetric metrics (Manders, RWC) use combinations
- Input validation: shape, dtype, contiguous labels, duplicate names
- Adds pandas>=1.5,<3 as a dependency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: sizeshape as shape feature, all features on by default
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* cleanup: remove lazy pandas import guard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: assert exact column count in end-to-end test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: use default params in end-to-end test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: migrate featurizer from pandas to polars
Replace pandas with polars for DataFrame construction and concatenation.
Label IDs are now a regular "label" column instead of the DataFrame index.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review findings
- Restore .gitignore accidentally deleted by branch
- Replace O(rows*cols) .as_py() join with vectorized pc.take
- Add pyarrow as hard dep, make polars/pandas optional via return_as
- Add channel name uniqueness validation
- Fix callable -> Callable type annotations
- Fix results type annotation (np.ndarray | list)
- Move all-masks-empty check into _validate (fail fast)
- Add negative label rejection in _validate
- Remove dead unreachable ValueError after _validate
- Update make_featurizer Raises docstring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR #32 review feedback
- Remove NaN note from module docstring
- Add usage example to make_featurizer() docstring
- Remove ReturnFormat alias, inline Literal in signature
- Simplify combinations/permutations with iter_fn pattern
- Add "dict" as return_as option
- Replace manual join with pa.Table.join()
- Remove sort_by("label") (join already produces sorted output)
- Clarify contiguous-labels as cp_measure convention
- Rename "plane" terminology to "mask" in errors
- Remove negative label check
- Use double-underscore separators in column names
- Rename masks parameter to objects in make_featurizer()
- Delete __init__.py (not present on main)
- Prune excessive tests, remove TestColumnNaming and brittle assertions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: support 2D and 3D (volumetric) spatial data in featurizer
Accept both (C, H, W) and (C, Z, H, W) images and masks. Four 2D-only
features (radial_distribution, radial_zernikes, zernike, ferret) are
automatically skipped with a warning for volumetric input. A ValueError
is raised if no features remain after filtering.
- Add volumetric flag to channel/shape feature tuples
- Relax _validate() to accept ndim 3 or 4, require matching ndim
- Filter 2D-only features at featurize() time with dynamic warning
- Use int32 for label arrays instead of int64
- Replace "plane" terminology with "object mask" throughout
- Update docstrings to document both 2D and 3D shapes
- Add TestVolumetric class (smoke, skip, raises, correlation tests)
- Replace ndim validation tests for actually-invalid dimensions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: rewrite featurizer as stateless functions per PR #32 review
Address maintainer feedback: remove OOP, remove PyArrow dependency,
return numpy matrix with column/row metadata instead of DataFrames.
- Replace Featurizer class with two plain functions: make_featurizer()
returns a serialisable config dict, featurize() takes config + data
and returns (ndarray, column_names, row_tuples)
- Remove pyarrow from dependencies
- Make channel names optional (warn + auto-assign ch0, ch1, ...)
- Remove 2D/3D feature differentiation
- Simplify validation (remove contiguous-label check, integer warning)
- Flatten multi-mask output: rows stacked with (image_id, object, label)
- Fix operator precedence bug in measureobjectintensity.py
- Reduce test suite from 765 to 312 lines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add end-to-end test for component output validation
Added an end-to-end test to validate output of components.
* Add docstring to TestSmoke class
Added a docstring to the TestSmoke class for clarity.
* Add featurizer usage section to README and fix ruff formatting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Make config optional in featurize(), rename make_featurizer to make_featurizer_config
Config now defaults to all features enabled when omitted. Updated README
to start with the minimal usage pattern and introduce config separately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Streamline README: align bulk API section with featurizer flow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add transition sentence to Bulk API section in README
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix Bulk API intro: motivate with direct function access
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix feret spelling to match upstream (ferret -> feret)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Make featurizer robust to 3D input by skipping 2D-only features
Detects 4D (volumetric) input and skips radial_distribution,
radial_zernikes, zernike, and feret with a user warning.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Document 3D support in featurizer README section
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix inconsistent code ticks in 3D README section
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract shared test fixtures into conftest.py
Move test data generation into reusable pytest fixtures (image_2d_1ch,
image_2d_2ch, mask_2d, image_3d_1ch, image_3d_2ch, mask_3d) and shared
constants (ALL_OFF, CELL_PAINTING_CHANNELS). Add get_rng() for
reproducible, order-independent test data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address review feedback and complete 2D/3D test matrix
- Fix stacklevel=4 -> 3 in correlation warning for consistency
- Document contiguous labels requirement in featurize() docstring
- Add comment explaining column order invariant safety
- Add test_3d_two_channels smoke test (3D + 2ch + correlation)
- Add parametrized test_3d_multi_mask (3D + multi-mask, 1ch and 2ch)
- Extract masks_3d_multi fixture to conftest.py with label sanity check
- Replace hardcoded sizes with SIZE_2D/CELL_PAINTING_CHANNELS constants
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract masks_2d_multi fixture and use in test
Move inline 2D multi-mask construction from test_multi_mask_stacks_rows
into a shared conftest fixture for consistency with masks_3d_multi.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* cleanup: remove redundant sanity-check asserts from fixtures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR #32 review feedback (round 2)
- Revert uv.lock to main (no dependency changes in this PR)
- Remove 3D warning, silently skip 2D-only features instead
- Make RNG seed a parameter in test fixture (default 42)
- Fix README: remove "with a warning", bulk API works on 3D too
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(readme): Remove unused pandas import from example
* fix: allow empty channels for shape-only features
`make_featurizer_config([])` no longer raises when only
channel-independent features (sizeshape, zernike, feret) are enabled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: Update README dependency notes and code snippets
Add a note regarding manual installation of DataFrame libraries and
clean up redundant or missing imports in code examples.
* docs: Update README instructions and examples
Clarify recommended usage for small and large datasets and add an
example output to the bulk API documentation.
* docs: Update mask example in README
Add a small modification to the cell mask example to illustrate more
complex shapes.
* chore: Bump version to 0.1.18 (featurizer)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alán F. Muñoz <afer.mg@gmail.com>