Skip to content

counters

counters ¤

Reset-aware helpers for monotonic production counters.

Many industrial counters -- parts produced, scrap, NOK parts, cumulative energy -- are expected to increase monotonically. In the field they frequently reset back to zero (or to a lower value) at a shift change, a part change, or a controller (PLC) restart. A naive last - first calculation then silently loses all of the production accumulated in the window where the reset happened.

These helpers treat any decrease in the counter as a reset and count the post-reset value as new production, mirroring the approach in :class:ts_shape.events.production.part_tracking.PartProductionEvents. Use them wherever a quantity is derived from a cumulative counter so the behavior is consistent across the library.

Example::

from ts_shape.utils.counters import counter_quantity

# A shift where the counter reset once: 100, 300, 500, 0, 150
values = pd.Series([100, 300, 500, 0, 150])
counter_quantity(values)                        # 550  (400 + 150)
counter_quantity(values, handle_resets=False)   # 50   (plain last - first)

Non-numeric readings are treated as gaps; a :class:~ts_shape.errors.DataQualityWarning reports how many there were.

counter_increments ¤

counter_increments(values: Series) -> pd.Series

Per-reading production increments from a (possibly resetting) counter.

A decrease between consecutive readings is interpreted as a reset: the counter is assumed to have dropped to (at most) zero and then climbed to its current value, so the new value is the production that happened after the reset. The first reading has no predecessor and therefore contributes 0 -- it establishes the baseline rather than counting as production.

Parameters:

Name Type Description Default
values Series

Counter readings, ordered in time. Non-numeric entries are coerced to NaN and treated as gaps (with a :class:~ts_shape.errors.DataQualityWarning).

required

Returns:

Type Description
Series

A float Series, index-aligned with values, of the parts produced

Series

since the previous reading.

counter_quantity ¤

counter_quantity(
    values: Series, *, handle_resets: bool = True
) -> float

Total production represented by a counter series.

Parameters:

Name Type Description Default
values Series

Counter readings, ordered in time.

required
handle_resets bool

When True (default) the quantity is the sum of reset-aware per-reading increments (see :func:counter_increments), so production before and after a reset is both counted. When False the quantity is max(0, last - first); use it only for counters known never to reset, because a window containing a reset then reports too little (often 0).

True

Returns:

Type Description
float

The total quantity as a float. Empty input returns 0.0.

counter_by_group ¤

counter_by_group(
    values: Series,
    keys: Series | list[Series],
    *,
    handle_resets: bool = True
) -> pd.DataFrame

Counter quantity and resets per group (shift, day, operator, ...).

Computing :func:counter_quantity separately inside each group loses the production between the last reading of one group and the first reading of the next: every group boundary drops one increment. Here the reset-aware increments are computed once over the whole signal and each one is booked to the group of the reading that reports it, so the group quantities add up to the signal's total.

Parameters:

Name Type Description Default
values Series

Counter readings of one signal, ordered in time.

required
keys Series | list[Series]

Group label(s) per reading, index-aligned with values.

required
handle_resets bool

When True (default) sum the reset-aware increments. When False each group reports max(0, last - first) of its own readings (the naive quantity, which loses production at group boundaries and resets).

True

Returns:

Type Description
DataFrame

A DataFrame indexed by the group keys with quantity (float) and

DataFrame

resets (int; the readings lower than their predecessor).

count_resets ¤

count_resets(values: Series) -> int

Number of resets (strictly decreasing steps) in a counter series.

Parameters:

Name Type Description Default
values Series

Counter readings, ordered in time.

required

Returns:

Type Description
int

The count of readings that are lower than their predecessor.

counter_per_window ¤

counter_per_window(
    values: Series,
    window: str,
    *,
    handle_resets: bool = True
) -> pd.Series

Counter quantity per time window.

Parameters:

Name Type Description Default
values Series

Counter readings with a DatetimeIndex, ordered in time.

required
window str

Resample frequency (e.g. '1h', '1D').

required
handle_resets bool

When True (default) the reset-aware per-reading increments (see :func:counter_increments) are computed on the whole series and summed per window, so every increment is booked exactly once, in the window of the reading that reports it. When False each window reports the max - min of the readings inside it (0 for a window with fewer than two readings). That loses the production between the last reading of one window and the first of the next, counts nothing for a single-reading window, and mis-counts a reset.

True

Returns:

Type Description
Series

A Series indexed by window start, clipped at 0 (float when

Series

handle_resets is set; the reading dtype otherwise).