Skip to content

part_tracking

part_tracking ¤

Production tracking by part number.

Simple, practical module for daily production reporting: - How many parts were produced? - Production by part number and time window - Daily summaries

Counters are assumed to be monotonically increasing during normal operation. Many real-world counters reset back to zero (or to a lower value) - for example at a shift change, a part change, or a controller restart. A naive last - first calculation silently loses all of the production that happened in a window where a reset occurred. Pass handle_resets=True to account for these resets, and use :meth:PartProductionEvents.detect_resets to inspect when they happened.

PartProductionEvents ¤

PartProductionEvents(
    dataframe: DataFrame,
    *,
    time_column: str = "systime",
    event_uuid: str = "prod:part_tracking"
)

Bases: Base

Track production quantities by part number.

Each UUID represents one signal: - part_id_uuid: string signal with current part number - counter_uuid: monotonic counter for production count

The counter is expected to increase as parts are produced. If the counter is reset during operation (back to zero or a lower value), enable handle_resets so the reset is treated as new production instead of being discarded.

Example

import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.production_by_part("part", "counter")[["start", "part_number", "quantity"]] start part_number quantity 0 2025-01-01 00:00:00 A 59 1 2025-01-01 01:00:00 B 60

Initialize part production tracker.

Parameters:

Name Type Description Default
dataframe DataFrame

Input DataFrame with timeseries data

required
time_column str

Name of timestamp column (default: 'systime')

'systime'
event_uuid str

UUID tagged on derived point events (the uuid column of :meth:detect_resets; default: 'prod:part_tracking')

'prod:part_tracking'

production_by_part ¤

production_by_part(
    part_id_uuid: str,
    counter_uuid: str,
    *,
    window: str = "1h",
    value_column_part: str = "value_string",
    value_column_counter: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Calculate production quantity per part number.

Parameters:

Name Type Description Default
part_id_uuid str

UUID for part number signal

required
counter_uuid str

UUID for production counter

required
window str

Time window for aggregation (e.g., '1h', '8h', '1d')

'1h'
value_column_part str

Column containing part numbers

'value_string'
value_column_counter str

Column containing counter values

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the production after the reset instead of losing it. The result gains a resets column with the number of resets observed in each window. When False, the quantity is simply max(0, last_count - first_count) and a window containing a reset reports 0.

True

Returns:

Type Description
DataFrame

DataFrame with columns:

DataFrame
  • start: Start of time window
DataFrame
  • end: End of time window (start + window)
DataFrame
  • duration_seconds: Window length in seconds
DataFrame
  • part_number: Part number/ID
DataFrame
  • quantity: Parts produced in window
DataFrame
  • first_count: Counter value at window start
DataFrame
  • last_count: Counter value at window end
DataFrame
  • resets: Number of counter resets in window (only when handle_resets=True)
Example

import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.production_by_part("part", "counter")[["part_number", "quantity"]] part_number quantity 0 A 59 1 B 60

With handle_resets=True (the default), a window where the counter drops from 432 to 61 reports a quantity of 61 (and resets=1) instead of 0.

detect_resets ¤

detect_resets(
    part_id_uuid: str,
    counter_uuid: str,
    *,
    value_column_part: str = "value_string",
    value_column_counter: str = "value_integer"
) -> pd.DataFrame

Find the points in time where the counter was reset.

A reset is any reading where the counter value is lower than the previous reading.

Parameters:

Name Type Description Default
part_id_uuid str

UUID for part number signal

required
counter_uuid str

UUID for production counter

required
value_column_part str

Column containing part numbers

'value_string'
value_column_counter str

Column containing counter values

'value_integer'

Returns:

Type Description
DataFrame

DataFrame with one row per reset and columns:

DataFrame
  • : Timestamp of the reading after the reset
DataFrame
  • uuid: Event UUID of this tracker
DataFrame
  • source_uuid: The counter UUID
DataFrame
  • part_number: Active part number at the reset
DataFrame
  • count_before: Counter value just before the reset
DataFrame
  • count_after: Counter value just after the reset
DataFrame
  • drop: How far the counter fell (count_before - count_after)
Example

import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A"], hold=6, freq="1min") counter = parts.assign(uuid="counter", value_string=pd.NA, ... value_integer=[430, 431, 432, 61, 62, 63]) pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.detect_resets("part", "counter")[["part_number", "count_before", "count_after"]] part_number count_before count_after 0 A 432.0 61.0

daily_production_summary ¤

daily_production_summary(
    part_id_uuid: str,
    counter_uuid: str,
    *,
    value_column_part: str = "value_string",
    value_column_counter: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Daily production summary by part number.

Parameters:

Name Type Description Default
part_id_uuid str

UUID for part number signal

required
counter_uuid str

UUID for production counter

required
value_column_part str

Column containing part numbers

'value_string'
value_column_counter str

Column containing counter values

'value_integer'
handle_resets bool

Account for counter resets (see :meth:production_by_part). Adds a resets column with the number of resets observed that day.

True

Returns:

Type Description
DataFrame

DataFrame with columns:

DataFrame
  • start: Midnight of date (tz-aware in the data's tz)
DataFrame
  • end: Midnight of the following day
DataFrame
  • duration_seconds: Day length in seconds
DataFrame
  • date: Production date
DataFrame
  • part_number: Part number/ID
DataFrame
  • total_quantity: Total parts produced that day
DataFrame
  • hours_active: Number of hours with production
DataFrame
  • resets: Number of counter resets that day (only when handle_resets=True)
Example

import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) daily = pp.daily_production_summary("part", "counter") daily[["date", "part_number", "total_quantity", "hours_active"]] date part_number total_quantity hours_active 0 2025-01-01 A 59 1 1 2025-01-01 B 60 1

production_totals ¤

production_totals(
    part_id_uuid: str,
    counter_uuid: str,
    *,
    start_date: str | None = None,
    end_date: str | None = None,
    value_column_part: str = "value_string",
    value_column_counter: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Total production by part number for a date range.

Parameters:

Name Type Description Default
part_id_uuid str

UUID for part number signal

required
counter_uuid str

UUID for production counter

required
start_date str | None

Start date 'YYYY-MM-DD' (optional)

None
end_date str | None

End date 'YYYY-MM-DD' (optional)

None
value_column_part str

Column containing part numbers

'value_string'
value_column_counter str

Column containing counter values

'value_integer'
handle_resets bool

Account for counter resets (see :meth:production_by_part). Adds a resets column with the total number of resets in the range.

True

Returns:

Type Description
DataFrame

DataFrame with total production per part

Example

import pandas as pd, ts_shape parts = ts_shape.make_id_signal("part", ["A", "B"], hold=60, freq="1min") counter = ts_shape.make_timeseries(["counter"], n_points=120, freq="1min", ... noise=0, drift=119, value_column="value_integer") pp = ts_shape.PartProductionEvents(pd.concat([parts, counter])) pp.production_totals("part", "counter", start_date="2025-01-01") part_number total_quantity days_produced resets 0 A 59 1 0 1 B 60 1 0