Skip to content

Collect

Turn a batch into one tidy, multi-cell frame: summaries, cycle/capacity curves, or dQ/dV (ICA).

cellpy.collect is the 2.1 collectors redesign (it replaced the BatchCollector family). cellpy.utils.collectors remains as a thin re-export. Each collector returns a Collection.

Collectors

collect

cellpy.collect -- collection as a first-class product.

A collection is a product, not a side effect: Collection = a tidy frame plus provenance. Built on cellpy.batch.aggregate (Epic A), replacing the utils/collectors "elevated arguments" machinery and fixing the cross-cell cycle-narrowing bug by design. cellpy.utils.collectors is now a thin shim whose legacy Batch*Collector family is removed in 2.1.

Arcs: options/collection/collect_summaries + per-cell curves; rate/group pipeline; convenience class + recipes; ICA collection + plotting handover (Collection.plot -> cellpy.plotting) + collectors shim.

BatchCollector

BatchCollector(batch: Any, collector: Collector, options: Any | None = None, *, name: str | None = None, autorun: bool = True, **overrides)

Run a collect function and hold its Collection.

Parameters:

  • batch (Any) –

    the Batch to collect from.

  • collector (Collector) –

    a collect callable, e.g. collect_summaries.

  • options (Any | None, default: None ) –

    the options dataclass for collector (optional).

  • name (str | None, default: None ) –

    display/base name (defaults to the batch/journal name).

  • autorun (bool, default: True ) –

    run the collector immediately (default True).

  • **overrides

    option overrides forwarded to collector.

plot

plot(**kwargs) -> Any

Draw the collection via cellpy.plotting.

Parameters:

  • **kwargs

    Forwarded to plot -- e.g. height, backend, legend_title or layout / kind (cycles / ICA / DVA).

Returns:

  • Any

    A backend-native figure object.

save

save(directory: str | Path, **kwargs) -> list[Path]

Persist the collected frame (+ meta.json) -- data only.

Explicit directory, no cwd fallback. This writes no figures: use save_figure for an image file, to_image for the bytes, or save_image_files for a png/svg/json set from a figure you already have.

Parameters:

  • directory (str | Path) –

    Where to write <name>.<fmt> and <name>.meta.json.

  • **kwargs

    Forwarded to save (e.g. formats=("parquet", "csv", "json", "xlsx")).

Returns:

save_figure

save_figure(path: str | Path, *, scale: float = 1.0, **plot_kwargs) -> Path

Render plot and write the figure to path (needs kaleido).

The image format comes from the suffix (.png when there is none). save writes the data; this writes the picture.

Parameters:

  • path (str | Path) –

    Target file, e.g. "out/cycle_life.png".

  • scale (float, default: 1.0 ) –

    Pixel scale factor for kaleido.

  • **plot_kwargs

    Forwarded to plot.

Returns:

  • Path

    The path written.

to_image

to_image(fmt: str = 'png', *, scale: float = 1.0, **plot_kwargs) -> bytes

Render plot and return static image bytes (needs kaleido).

Parameters:

  • fmt (str, default: 'png' ) –

    png, svg, pdf, jpg/jpeg, or webp.

  • scale (float, default: 1.0 ) –

    Pixel scale factor for kaleido.

  • **plot_kwargs

    Forwarded to plot.

Returns:

  • bytes

    Encoded image bytes (see save_figure to write a file).

update

update(**overrides) -> 'BatchCollector'

(Re)run the collector; extra kwargs merge into the option overrides.

CellItem dataclass

CellItem(label: str, group: Any, sub_group: Any, cell: Any)

One cell in a collection pass, with its group metadata.

Collection dataclass

Collection(data: DataFrame, kind: str, name: str, meta: CollectionMeta)

A tidy collected frame plus its provenance.

is_grouped property

is_grouped: bool

True when at least one multi-member group was averaged.

Mixed multi+singleton selections stay True (long frame). All-singleton group_it=True stays wide / False so callers can adapt without sniffing for a mean column.

plot

plot(*, family_kind: str | None = None, **kwargs)

Draw the collection via collected_plot.

The drawing lives in cellpy.plotting; a collection just hands it the tidy frame and the family. The only reconciliation needed is the summary family's cycle column, which the summary plotter spells cycle (capacity/ICA curves keep cycle_num / cycle).

For summary collections (Plotly), pass share_y / match_axes and optional y_ranges={variable: [lo, hi], ...} for per-facet y-limits (see summary_plotter). App chrome: plotly_template, layout_updates, y_label_mapper, height / height_per_panel. Cycles / ICA: prefer layout= (per_cell / per_cycle) and kind= (line / film / spread); layout="film" aliases kind="film". Unknown layout/kind/method values raise ValueError. Plotly facet strips are pretty-printed by default (Cycle N / cell label).

layout="per_cell" colours by cycle: more than legend_cycle_limit cycles (default 8) get a colorbar instead of a long legend, and force_colorbar / force_legend override (#928).

Summary facets follow the collected columns= order (top → bottom) unless order_variables= is given (#923); derived series (the CV split, a normalized retention curve) keep their own order after them. Default y-axis titles include units (#947).

save

save(directory: Path | str | None = None, formats: tuple[str, ...] = ('parquet', 'csv')) -> list[Path]

Save the collected frame (+ meta.json) -- data only, no figures.

No cwd fallback -- directory is explicit. Figures are a separate product: use save_figure to write one to disk, to_image for the bytes, or save_image_files for a png/svg/json set from a figure you already have.

Parameters:

  • directory (Path | str | None, default: None ) –

    Where to write <name>.<fmt> and <name>.meta.json.

  • formats (tuple[str, ...], default: ('parquet', 'csv') ) –

    Frame formats to write (parquet, csv, json, xlsx).

Returns:

save_figure

save_figure(path: Path | str, *, scale: float = 1.0, **plot_kwargs) -> Path

Render via plot and write the figure to path (needs kaleido).

The image format comes from the suffix (.png when there is none). This is the file-writing counterpart of to_image; save_image_files writes a whole png/svg/json set from an existing figure instead.

Parameters:

  • path (Path | str) –

    Target file. A missing suffix defaults to .png.

  • scale (float, default: 1.0 ) –

    Pixel scale factor for kaleido.

  • **plot_kwargs

    Forwarded to plot.

Returns:

  • Path

    The path written.

to_image

to_image(fmt: str = 'png', *, scale: float = 1.0, **plot_kwargs) -> bytes

Render via plot and return static image bytes (needs kaleido).

Parameters:

  • fmt (str, default: 'png' ) –

    png, svg, pdf, jpg/jpeg, or webp.

  • scale (float, default: 1.0 ) –

    Pixel scale factor for kaleido.

  • **plot_kwargs

    Forwarded to plot.

Returns:

  • bytes

    Encoded image bytes. Use

  • bytes

    image_media_type for a download MIME type.

Raises:

  • OptionalDependencyError

    If plotly or kaleido is not installed.

to_wide

to_wide(values: str, index: str = 'cycle_num', columns: str = 'cell') -> pl.DataFrame

Explicit, tested pivot to wide layout (replaces the try/except pivots).

CollectionMeta dataclass

CollectionMeta(kind: str, batch_name: str | None = None, options: dict = dict(), cellpy_version: str = _cellpy_version(), created_utc: str = _now_utc(), cells_included: list[str] = list(), cells_skipped: list[str] = list(), grouped: bool = False)

Provenance for a Collection.

CurveOptions dataclass

CurveOptions(cycles: tuple[int, ...] | None = None, rate: float | None = None, rate_on: str | None = None, rate_std: float | None = None, inverse: bool = False, mode: str | None = None, method: str | None = None, transforms: tuple[Transform, ...] = ())

Options for cycle/capacity-curve collection.

IcaOptions dataclass

IcaOptions(cycles: tuple[int, ...] | None = None, voltage_resolution: float | None = None, capacity_resolution: float | None = None, transforms: tuple[Transform, ...] = ())

Options for dQ/dV (ICA) and dV/dQ (DVA) collection.

Shared by collect_ica and collect_dva -- each forwards only the resolution knob its own transform actually uses: dqdv differentiates along voltage (needs voltage_resolution for the q(V) interpolation); dvdq differentiates along capacity (needs capacity_resolution for the V(q) interpolation).

SaveOptions dataclass

SaveOptions(directory: Path | None = None, formats: tuple[str, ...] = ('parquet', 'csv'))

Where/how a Collection is saved (no cwd fallback).

SummaryOptions dataclass

SummaryOptions(columns: tuple[str, ...] | None = None, max_cycle: int | None = None, remove_last: bool = False, only_selected: bool = False, rate: float | None = None, rate_on: str | tuple[str, ...] | None = None, rate_std: float | None = None, rate_column: str | None = None, rate_inverse: bool = False, rate_inverted: bool = False, partition_by_cv: bool = False, normalize_cycles: bool = False, group_it: bool = False, average_method: str = 'mean', custom_group_labels: Mapping | None = None, replace_inf_with_nan: bool = True, replace_extremes_with_nan: bool = True, low_limit: float = -1000000.0, high_limit: float = 1000000.0, transforms: tuple[Transform, ...] = ())

Options for collect_summaries.

One source of truth for the whole summary pipeline, replacing the ~30 keyword arguments of the legacy helpers.concat_summaries. The three meaty feature families it carries:

  • rate filtering (rate / rate_on / rate_std / ...): keep only cycles whose rate_on step ran at the requested C-rate.
  • group averaging (group_it / average_method / custom_group_labels): average per journal group, emitting a tidy long frame (group, cycle_num, variable, mean, std).
  • CV partition (partition_by_cv): split each capacity metric into *_non_cv / *_cv contributions.

collect_cycles

collect_cycles(batch: Any, options: CurveOptions | None = None, **overrides) -> Collection

Collect capacity-voltage curves per cell/cycle into one tidy Collection.

collect_dva

collect_dva(batch: Any, options: IcaOptions | None = None, **overrides) -> Collection

Collect dV/dQ (differential voltage) curves per cell into one Collection.

Cycle selection is derived per cell from the originally requested cycles every iteration, so a cell missing a cycle never narrows the request for the cells after it (mirrors collect_ica).

collect_ica

collect_ica(batch: Any, options: IcaOptions | None = None, **overrides) -> Collection

Collect dQ/dV (incremental capacity) curves per cell into one Collection.

Cycle selection is derived per cell from the originally requested cycles every iteration, so a cell missing a cycle never narrows the request for the cells after it.

collect_summaries

collect_summaries(batch: Any, options: SummaryOptions | None = None, **overrides) -> Collection

Collect per-cell summaries into one tidy Collection.

With defaults this is a plain long-format concatenation (one row per cell/cycle). The options add rate filtering, CV partition, per-group averaging and inf/extreme cleanup -- see SummaryOptions.

cycles_collector

cycles_collector(batch: Any, options: CurveOptions | None = None, *, cycles: Any = None, rate: float | None = None, mode: str | None = None, method: str | None = None, autorun: bool = True, **overrides) -> BatchCollector

Collect voltage-capacity curves per cell and cycle.

Returns a BatchCollector holding a Collection: .data is the tidy frame, .plot() draws it and .save(dir) writes the frame plus its metadata.

Examples:

>>> curves = cycles_collector(b, cycles=[1, 10, 20])
>>> curves.plot(layout="per_cell")  # or layout="per_cycle", kind="film"

Parameters:

  • batch (Any) –

    The Batch to collect from.

  • options (CurveOptions, default: None ) –

    A ready options object; the keyword arguments below still update it.

  • cycles (sequence of int, default: None ) –

    Cycles to collect. Resolved per cell, so a cell missing one cycle does not narrow the others.

  • rate (float, default: None ) –

    Rate-based cycle selection (see rate_on / rate_std / inverse on CurveOptions).

  • mode (str, default: None ) –

    Capacity mode forwarded to CellpyCell.get_cap (gravimetric / areal / absolute).

  • method (str, default: None ) –

    forth-and-forth / back-and-forth / forth, forwarded to CellpyCell.get_cap.

  • autorun (bool, default: True ) –

    Run the collector immediately (default True).

  • **overrides

    Any other field of CurveOptions -- rate_on, rate_std, inverse, transforms.

Returns:

dva_collector

dva_collector(batch: Any, options: IcaOptions | None = None, *, cycles: Any = None, capacity_resolution: float | None = None, autorun: bool = True, **overrides) -> BatchCollector

Collect differential-voltage (dV/dQ) curves per cell and cycle.

Returns a BatchCollector holding a Collection: .data is the tidy frame, .plot() draws it and .save(dir) writes the frame plus its metadata.

Examples:

>>> dva = dva_collector(b, cycles=[1, 10, 20])
>>> dva.plot(layout="per_cell")

Parameters:

  • batch (Any) –

    The Batch to collect from.

  • options (IcaOptions, default: None ) –

    A ready options object; the keyword arguments below still update it.

  • cycles (sequence of int, default: None ) –

    Cycles to collect (resolved per cell).

  • capacity_resolution (float, default: None ) –

    Capacity step for the V(q) interpolation dV/dQ differentiates along.

  • autorun (bool, default: True ) –

    Run the collector immediately (default True).

  • **overrides

    Any other field of IcaOptions -- transforms.

Returns:

from_cells

from_cells(cells, **kwargs) -> Batch

Build a Batch from already-loaded cells (see from_cells) -- feed it to collect_summaries / collect_cycles or call batch.plot().

ica_collector

ica_collector(batch: Any, options: IcaOptions | None = None, *, cycles: Any = None, voltage_resolution: float | None = None, autorun: bool = True, **overrides) -> BatchCollector

Collect incremental-capacity (dQ/dV) curves per cell and cycle.

Returns a BatchCollector holding a Collection: .data is the tidy frame, .plot() draws it and .save(dir) writes the frame plus its metadata.

Examples:

>>> ica = ica_collector(b, cycles=[1, 10, 20])
>>> ica.plot(layout="per_cell")

Parameters:

  • batch (Any) –

    The Batch to collect from.

  • options (IcaOptions, default: None ) –

    A ready options object; the keyword arguments below still update it.

  • cycles (sequence of int, default: None ) –

    Cycles to collect (resolved per cell).

  • voltage_resolution (float, default: None ) –

    Voltage step for the q(V) interpolation dQ/dV differentiates along.

  • autorun (bool, default: True ) –

    Run the collector immediately (default True).

  • **overrides

    Any other field of IcaOptions -- transforms.

Returns:

iter_cells

iter_cells(batch: Any, label_mapper: Mapping[str, str] | None = None) -> Iterator[CellItem]

Yield the batch's loaded cells with their journal group/sub_group.

load_collection

load_collection(path: Path | str) -> Collection

Load a saved collection (parquet/csv frame + meta.json if present).

normalize_column

normalize_column(column: str, norm_factor: float, out: str | None = None) -> Callable[[pl.DataFrame], pl.DataFrame]

Build a transform that adds 100 * column / norm_factor as a new series.

Works on both the wide (non-grouped) frame -- adding an <out> column -- and the grouped long frame -- adding an <out> variable with mean (and std) scaled. Missing columns are left untouched.

normalize_column_on_max

normalize_column_on_max(column: str, out: str | None = None, scaler: float = 100.0) -> Callable[[pl.DataFrame], pl.DataFrame]

Build a transform that adds scaler * column / max(column) as a new series.

The counterpart of normalize_column for the case where the reference value is not known up front — retention against the cell's own best cycle, which is what summary_plot does by default (fullcell_standard_normalization_type="max").

Works on both the wide (non-grouped) frame and the grouped long frame, and leaves the frame untouched when column is missing.

Parameters:

  • column (str) –

    source column (wide) or variable value (long).

  • out (str, default: None ) –

    name of the series to add. Defaults to "<column>_norm".

  • scaler (float, default: 100.0 ) –

    factor applied after dividing by the maximum.

Returns:

  • Callable[[DataFrame], DataFrame]

    A polars.DataFrame -> polars.DataFrame transform.

standard_gravimetric

standard_gravimetric(batch: Any, norm_factor: float = 120.0, *, group_it: bool = True, columns: tuple[str, ...] = ('charge_capacity', 'discharge_capacity', 'coulombic_efficiency'), retention_on: str = 'discharge_capacity', autorun: bool = True, **overrides) -> BatchCollector

The standard gravimetric summary recipe.

Successor of collectors.standard_gravimetric_collector: collect the charge/discharge/CE metrics, partition capacity by CV step, average per group, and add a normalized discharge-capacity-retention series (100 * discharge / norm_factor). Uses native cellpycore column names (the legacy *_gravimetric suffix is not part of the native schema). The figure itself lands with the plotting handover.

summary_collector

summary_collector(batch: Any, options: SummaryOptions | None = None, *, family: str | None = None, y: str | None = None, columns: Any = None, group_it: bool | None = None, custom_group_labels: Any = None, rate: float | None = None, max_cycle: int | None = None, partition_by_cv: bool | None = None, autorun: bool = True, **overrides) -> BatchCollector

Collect the per-cell summaries of a batch (cycle-life data).

Returns a BatchCollector holding a Collection: .data is the tidy frame, .plot() draws it and .save(dir) writes the frame plus its metadata.

Examples:

>>> caps = summary_collector(b, family="fullcell_standard_gravimetric",
...                          group_it=True)
>>> caps.plot(height=600)

Parameters:

  • batch (Any) –

    The Batch to collect from.

  • options (SummaryOptions, default: None ) –

    A ready options object. Wins over family and is still updated by the keyword arguments below.

  • family (str, default: None ) –

    Name of a registered plot family, the same names summary_plot(y=...) takes (capacities_gravimetric, fullcell_standard_gravimetric, ...). Its columns and transforms are resolved against the first loaded cell's summary schema. An unknown name raises ValueError listing the known families; cellpy.plotting.families() lists them too.

  • y (str, default: None ) –

    Alias of family, matching summary_plot(y=...).

  • columns (sequence of str, default: None ) –

    Summary columns to keep. Wins over family. Journal keys (cell/group/...) are always kept. Plot facet rows follow this order top → bottom (Plotly first row on top).

  • group_it (bool, default: None ) –

    Average per journal group -> tidy long frame (group, cycle_num, variable, mean, std).

  • custom_group_labels (mapping, default: None ) –

    group id -> display label, used for the plot legend (int or str keys both match).

  • rate (float, default: None ) –

    Keep only cycles run at this C-rate (see rate_on / rate_std / rate_inverted on SummaryOptions).

  • max_cycle (int, default: None ) –

    Drop cycles above this number.

  • partition_by_cv (bool, default: None ) –

    Also emit *_non_cv / *_cv capacity contributions.

  • autorun (bool, default: True ) –

    Run the collector immediately (default True).

  • **overrides

    Any other field of SummaryOptions -- rate_on, rate_std, rate_inverted, only_selected, remove_last, normalize_cycles, average_method, transforms, ...

Returns:

Raises:

  • ValueError

    family/y is not a registered family, or no loaded cell is available to resolve it against.

Summary collection

summary

Summary collection.

Assembles per-cell summaries into one tidy Collection, carrying the full rate-filtering / grouping / CV-partition feature set of the legacy helpers.concat_summaries on the SummaryOptions model. Cycle-level work (rate/CV/max-cycle) happens per cell in _summary_ops; combination, column selection, group averaging and cleanup happen on the combined frame.

collect_summaries

collect_summaries(batch: Any, options: SummaryOptions | None = None, **overrides) -> Collection

Collect per-cell summaries into one tidy Collection.

With defaults this is a plain long-format concatenation (one row per cell/cycle). The options add rate filtering, CV partition, per-group averaging and inf/extreme cleanup -- see SummaryOptions.

Cycle / capacity curves

curves

Cycle/capacity-curve collection.

Per-cell rate/cycle selection is computed per cell from the originally requested cycles -- the fix for the cross-cell narrowing bug where the legacy cycles_collector/ica_collector reassigned the shared cycles list inside the per-cell loop (collectors.py:1609/1691), silently dropping cycles for every cell after the first that lacked them.

collect_cycles

collect_cycles(batch: Any, options: CurveOptions | None = None, **overrides) -> Collection

Collect capacity-voltage curves per cell/cycle into one tidy Collection.

Incremental capacity (ICA)

ica

dQ/dV (ICA) collection.

Per-cell dQ/dV curves via dqdv, concatenated into one tidy frame with cell / group / sub_group keys. Mirrors collect_cycles -- including the per-cell cycle isolation that fixes the legacy cross-cell narrowing bug (collectors.py:1691) -- but emits the specced ICA frame: cycle, direction, voltage, capacity, dqdv (+ the deprecated dq duplicate until 2.1).

collect_ica

collect_ica(batch: Any, options: IcaOptions | None = None, **overrides) -> Collection

Collect dQ/dV (incremental capacity) curves per cell into one Collection.

Cycle selection is derived per cell from the originally requested cycles every iteration, so a cell missing a cycle never narrows the request for the cells after it.

The Collection product

collection

The Collection product.

A collection is a product, not a side effect: a tidy frame plus provenance (what was collected, from which batch, with which options, by which cellpy version, when). It can be saved, re-loaded and re-plotted without re-collecting -- meta.json next to the data makes collections reproducible artifacts.

Collection dataclass

Collection(data: DataFrame, kind: str, name: str, meta: CollectionMeta)

A tidy collected frame plus its provenance.

is_grouped property

is_grouped: bool

True when at least one multi-member group was averaged.

Mixed multi+singleton selections stay True (long frame). All-singleton group_it=True stays wide / False so callers can adapt without sniffing for a mean column.

plot

plot(*, family_kind: str | None = None, **kwargs)

Draw the collection via collected_plot.

The drawing lives in cellpy.plotting; a collection just hands it the tidy frame and the family. The only reconciliation needed is the summary family's cycle column, which the summary plotter spells cycle (capacity/ICA curves keep cycle_num / cycle).

For summary collections (Plotly), pass share_y / match_axes and optional y_ranges={variable: [lo, hi], ...} for per-facet y-limits (see summary_plotter). App chrome: plotly_template, layout_updates, y_label_mapper, height / height_per_panel. Cycles / ICA: prefer layout= (per_cell / per_cycle) and kind= (line / film / spread); layout="film" aliases kind="film". Unknown layout/kind/method values raise ValueError. Plotly facet strips are pretty-printed by default (Cycle N / cell label).

layout="per_cell" colours by cycle: more than legend_cycle_limit cycles (default 8) get a colorbar instead of a long legend, and force_colorbar / force_legend override (#928).

Summary facets follow the collected columns= order (top → bottom) unless order_variables= is given (#923); derived series (the CV split, a normalized retention curve) keep their own order after them. Default y-axis titles include units (#947).

save

save(directory: Path | str | None = None, formats: tuple[str, ...] = ('parquet', 'csv')) -> list[Path]

Save the collected frame (+ meta.json) -- data only, no figures.

No cwd fallback -- directory is explicit. Figures are a separate product: use save_figure to write one to disk, to_image for the bytes, or save_image_files for a png/svg/json set from a figure you already have.

Parameters:

  • directory (Path | str | None, default: None ) –

    Where to write <name>.<fmt> and <name>.meta.json.

  • formats (tuple[str, ...], default: ('parquet', 'csv') ) –

    Frame formats to write (parquet, csv, json, xlsx).

Returns:

save_figure

save_figure(path: Path | str, *, scale: float = 1.0, **plot_kwargs) -> Path

Render via plot and write the figure to path (needs kaleido).

The image format comes from the suffix (.png when there is none). This is the file-writing counterpart of to_image; save_image_files writes a whole png/svg/json set from an existing figure instead.

Parameters:

  • path (Path | str) –

    Target file. A missing suffix defaults to .png.

  • scale (float, default: 1.0 ) –

    Pixel scale factor for kaleido.

  • **plot_kwargs

    Forwarded to plot.

Returns:

  • Path

    The path written.

to_image

to_image(fmt: str = 'png', *, scale: float = 1.0, **plot_kwargs) -> bytes

Render via plot and return static image bytes (needs kaleido).

Parameters:

  • fmt (str, default: 'png' ) –

    png, svg, pdf, jpg/jpeg, or webp.

  • scale (float, default: 1.0 ) –

    Pixel scale factor for kaleido.

  • **plot_kwargs

    Forwarded to plot.

Returns:

  • bytes

    Encoded image bytes. Use

  • bytes

    image_media_type for a download MIME type.

Raises:

  • OptionalDependencyError

    If plotly or kaleido is not installed.

to_wide

to_wide(values: str, index: str = 'cycle_num', columns: str = 'cell') -> pl.DataFrame

Explicit, tested pivot to wide layout (replaces the try/except pivots).

CollectionMeta dataclass

CollectionMeta(kind: str, batch_name: str | None = None, options: dict = dict(), cellpy_version: str = _cellpy_version(), created_utc: str = _now_utc(), cells_included: list[str] = list(), cells_skipped: list[str] = list(), grouped: bool = False)

Provenance for a Collection.

load_collection

load_collection(path: Path | str) -> Collection

Load a saved collection (parquet/csv frame + meta.json if present).