Skip to content

quality_tracking

quality_tracking ¤

Quality and NOK (defective parts) tracking.

Essential module for daily quality analysis: - NOK parts by shift and part number - Scrap tracking - First pass yield - Quality rates

QualityEvents ¤

QualityEvents(
    dataframe: DataFrame,
    *,
    time_column: str = "systime",
    shift_definitions: (
        dict[str, tuple[str, str]] | None
    ) = None
)

Bases: Base

Track NOK (defective) parts and quality metrics.

Each UUID represents one signal: - ok_counter_uuid: counter for good parts - nok_counter_uuid: counter for NOK parts - part_id_uuid: part number signal (optional) - defect_reason_uuid: defect reason code (optional)

Merge keys: [date, shift] for shift-level, [date] for daily, [part_number] for part-level.

Pipeline outputs include quality_pct (alias for first_pass_yield_pct) so downstream modules (ShiftHandoverEvents, PeriodSummaryEvents, OEEEvents) can join on a consistent column name.

Example

import pandas as pd, ts_shape kw = dict(n_points=20, freq="1min", noise=0, value_column="value_integer") df = pd.concat([ts_shape.make_timeseries(["ok"], drift=190, **kw), ... ts_shape.make_timeseries(["nok"], drift=19, **kw), ... ts_shape.make_id_signal("part", ["A", "B"], freq="1min")]) out = ts_shape.QualityEvents(df).quality_by_part("ok", "nok", "part") out[["part_number", "ok_parts", "nok_parts", "first_pass_yield_pct"]].round(1) part_number ok_parts nok_parts first_pass_yield_pct 0 A 90.0 9.0 90.9 1 B 100.0 10.0 90.9

Initialize quality tracker.

Parameters:

Name Type Description Default
dataframe DataFrame

Input DataFrame with timeseries data

required
time_column str

Name of timestamp column (default: 'systime')

'systime'
shift_definitions dict[str, tuple[str, str]] | None

Dictionary mapping shift names to (start, end) times Default: 3-shift operation (06:00-14:00, 14:00-22:00, 22:00-06:00)

None

nok_by_shift ¤

nok_by_shift(
    ok_counter_uuid: str,
    nok_counter_uuid: str,
    *,
    value_column: str = "value_integer",
    handle_resets: bool = True,
    production_day: bool = True
) -> pd.DataFrame

Calculate NOK (defective) parts per shift.

Parameters:

Name Type Description Default
ok_counter_uuid str

UUID for good parts counter

required
nok_counter_uuid str

UUID of the NOK part counter

required
value_column str

Column containing counter values

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the parts accumulated after the reset, for both the OK and NOK counters. The increments are computed once over each whole counter signal; the increment between two readings is spread over the time between them and split at the shift boundaries in proportion to time (ISO 22400-2: a part belongs to the period it was produced in), so the shifts add up to the signal totals and the parts made in the last hour of a night shift stay in that shift even though the reading that reports them falls in the morning shift. Quantities can therefore be fractional. A reset counts in the shift of the reading that shows it. The result gains a resets column with the number of resets observed in each shift (across both counters). When False, each counter uses max(0, last - first) of the shift's own readings; a shift containing a reset reports too little (often 0) and the production between two shifts' readings is lost.

True
production_day bool

Attribute an overnight shift's post-midnight readings to the shift's start date, so a night shift is one (date, shift) row (default). False groups by raw calendar date, which splits a night shift across two rows.

True

Returns:

Type Description
DataFrame

DataFrame with quality metrics by shift:

DataFrame
  • start: Start of the shift window (date at the shift start time from shift_definitions, tz-aware in the data's tz)
DataFrame
  • end: End of the shift window (next day for overnight shifts)
DataFrame
  • duration_seconds: Shift window length in seconds
DataFrame
  • date: Production date
DataFrame
  • shift: Shift name
DataFrame
  • ok_parts: Good parts produced
DataFrame
  • nok_parts: NOK parts
DataFrame
  • total_parts: Total parts produced
DataFrame
  • nok_rate_pct: Percentage of NOK parts
DataFrame
  • first_pass_yield_pct: Percentage of good parts
DataFrame
  • quality_pct: Same as first_pass_yield_pct
DataFrame
  • resets: Number of counter resets in the shift (only when handle_resets=True)
Example

import pandas as pd, ts_shape kw = dict(n_points=16, freq="30min", start="2025-01-01 06:00", noise=0, ... value_column="value_integer") df = pd.concat([ts_shape.make_timeseries(["ok"], drift=150, **kw), ... ts_shape.make_timeseries(["nok"], drift=15, **kw)]) out = ts_shape.QualityEvents(df).nok_by_shift("ok", "nok") out[["shift", "ok_parts", "nok_parts", "first_pass_yield_pct"]].round(1) shift ok_parts nok_parts first_pass_yield_pct 0 shift_1 150.0 15.0 90.9

quality_by_part ¤

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

Calculate quality metrics by part number.

Parameters:

Name Type Description Default
ok_counter_uuid str

UUID for good parts counter

required
nok_counter_uuid str

UUID of the NOK part counter

required
part_id_uuid str

UUID for part number signal

required
value_column_counter str

Column containing counter values

'value_integer'
value_column_part str

Column containing part numbers

'value_string'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the parts accumulated after the reset. The part number is attached to a single global counter, so the reset-aware quantity is the sum of per-reading increments over the full time-sorted series, grouped by part -- this also corrects the over-count that last - first produces when parts alternate over a non-contiguous group. The result gains a resets column with the number of resets attributed to each part (across the OK and NOK counters). When False, the quantity is max(0, last - first) per part group and a reset is lost.

True

Returns:

Type Description
DataFrame

DataFrame with quality by part:

DataFrame
  • part_number: Part number/ID
DataFrame
  • ok_parts: Good parts produced
DataFrame
  • nok_parts: NOK parts
DataFrame
  • total_parts: Total parts produced
DataFrame
  • nok_rate_pct: Percentage of NOK parts
DataFrame
  • first_pass_yield_pct: Percentage of good parts
DataFrame
  • quality_pct: Same as first_pass_yield_pct
DataFrame
  • resets: Number of counter resets for the part (only when handle_resets=True)
Example

import pandas as pd, ts_shape kw = dict(n_points=20, freq="1min", noise=0, value_column="value_integer") df = pd.concat([ts_shape.make_timeseries(["ok"], drift=190, **kw), ... ts_shape.make_timeseries(["nok"], drift=19, **kw), ... ts_shape.make_id_signal("part", ["A", "B"], freq="1min")]) out = ts_shape.QualityEvents(df).quality_by_part("ok", "nok", "part") out[["part_number", "ok_parts", "nok_parts", "nok_rate_pct"]] part_number ok_parts nok_parts nok_rate_pct 0 A 90.0 9.0 9.090909 1 B 100.0 10.0 9.090909

nok_by_reason ¤

nok_by_reason(
    nok_counter_uuid: str,
    defect_reason_uuid: str,
    *,
    value_column_counter: str = "value_integer",
    value_column_reason: str = "value_string",
    handle_resets: bool = True
) -> pd.DataFrame

Analyze NOK parts by defect reason.

Parameters:

Name Type Description Default
nok_counter_uuid str

UUID of the NOK part counter

required
defect_reason_uuid str

UUID for defect reason signal

required
value_column_counter str

Column containing counter values

'value_integer'
value_column_reason str

Column containing reason codes

'value_string'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the parts accumulated after the reset. The reason is attached to a single global counter, so the reset-aware quantity is the sum of per-reading increments over the full time-sorted series, grouped by reason -- this also corrects the over-count that last - first produces when reasons alternate over a non-contiguous group. The result gains a resets column with the number of resets attributed to each reason. When False, the quantity is max(0, last - first) per reason group and a reset is lost.

True

Returns:

Type Description
DataFrame

DataFrame with NOK by reason:

DataFrame
  • reason: Defect reason code
DataFrame
  • nok_parts: Number of NOK parts
DataFrame
  • pct_of_total: Percentage of total NOK
DataFrame
  • resets: Number of counter resets for the reason (only when handle_resets=True)
DataFrame

Canonical twin columns: share_of_total_pct (after pct_of_total); each

DataFrame

holds the same values as the deprecated column right before it, which is

DataFrame

removed in the next major release.

Example

import pandas as pd, ts_shape kw = dict(n_points=20, freq="1min", noise=0, value_column="value_integer") df = pd.concat([ts_shape.make_timeseries(["nok"], drift=19, **kw), ... ts_shape.make_id_signal("reason", ["Scratch", "Dent"], freq="1min")]) out = ts_shape.QualityEvents(df).nok_by_reason("nok", "reason") out[["reason", "nok_parts", "share_of_total_pct", "resets"]] reason nok_parts share_of_total_pct resets 0 Dent 10.0 52.631579 0 1 Scratch 9.0 47.368421 0

daily_quality_summary ¤

daily_quality_summary(
    ok_counter_uuid: str,
    nok_counter_uuid: str,
    *,
    value_column: str = "value_integer",
    handle_resets: bool = True,
    production_day: bool = True
) -> pd.DataFrame

Daily quality summary.

Parameters:

Name Type Description Default
ok_counter_uuid str

UUID for good parts counter

required
nok_counter_uuid str

UUID of the NOK part counter

required
value_column str

Column containing counter values

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the parts accumulated after the reset (see :meth:nok_by_shift; the day totals add up to the signal totals). The result gains a resets column with the number of resets observed that day. When False, a shift containing a reset reports too little.

True
production_day bool

Forwarded to :meth:nok_by_shift; attributes an overnight shift's post-midnight readings to the shift's start date. Default True; False groups by raw calendar date, which splits a night shift across two rows.

True

Returns:

Type Description
DataFrame

DataFrame with daily quality:

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
  • ok_parts: Good parts produced
DataFrame
  • nok_parts: NOK parts
DataFrame
  • total_parts: Total parts produced
DataFrame
  • nok_rate_pct: Percentage of NOK parts
DataFrame
  • first_pass_yield_pct: Percentage of good parts
DataFrame
  • quality_pct: Same as first_pass_yield_pct
DataFrame
  • resets: Number of counter resets that day (only when handle_resets=True)
Example

import pandas as pd, ts_shape kw = dict(n_points=16, freq="30min", start="2025-01-01 06:00", noise=0, ... value_column="value_integer") df = pd.concat([ts_shape.make_timeseries(["ok"], drift=150, **kw), ... ts_shape.make_timeseries(["nok"], drift=15, **kw)]) out = ts_shape.QualityEvents(df).daily_quality_summary("ok", "nok") out[["date", "ok_parts", "nok_parts", "nok_rate_pct"]].round(1) date ok_parts nok_parts nok_rate_pct 0 2025-01-01 150.0 15.0 9.1