Skip to content

oee_calculator

oee_calculator ¤

Overall Equipment Effectiveness (OEE) calculator.

OEE = Availability x Performance x Quality

Industry-standard metric for manufacturing productivity: - Availability: actual run time / planned production time - Performance: actual throughput / ideal throughput - Quality: good parts / total parts

OEEEvents ¤

OEEEvents(
    dataframe: DataFrame, *, time_column: str = "systime"
)

Bases: Base

Calculate Overall Equipment Effectiveness from timeseries signals.

Combines availability (from run/idle state), performance (from part counters and ideal cycle time), and quality (from total/reject counters) into a single OEE metric per day. Each daily row uses the canonical summary schema: start (midnight) / end (next midnight) / duration_seconds (the real day length: 86400, or 23 h / 25 h on DST days with tz-aware data).

Example

import pandas as pd, ts_shape kw = dict(n_points=60, freq="1min") run = ts_shape.make_timeseries(["run"], value_column="value_bool", **kw) parts = ts_shape.make_timeseries(["parts"], noise=0, drift=59, ... value_column="value_integer", **kw) daily = ts_shape.OEEEvents(pd.concat([run, parts])).oee( ... "run", "parts", ideal_cycle_time=30.0) daily[["availability_pct", "performance_pct", "quality_pct", "oee_pct"]].round(1) availability_pct performance_pct quality_pct oee_pct 0 54.2 92.2 100.0 50.0

Initialize OEE calculator.

Parameters:

Name Type Description Default
dataframe DataFrame

Input DataFrame with timeseries data.

required
time_column str

Name of timestamp column (default: 'systime').

'systime'

availability ¤

availability(
    run_state_uuid: str,
    planned_time_hours: float | None = None,
    *,
    value_column: str = "value_bool",
    max_gap: str | None = None
) -> pd.DataFrame

Calculate availability percentage from run/idle intervals.

Availability = run_time / planned_time (ISO 22400-2: APT / PBT). Each reading's state holds until the next reading; the interval is split at midnight, so a run across midnight counts in both days. When planned_time_hours is None the planned time is the time the state signal covers that day. A day the state signal does not cover is not reported. A value above 100 % (run time longer than a fixed planned time) is kept and reported with a :class:~ts_shape.errors.DataQualityWarning naming the day.

Parameters:

Name Type Description Default
run_state_uuid str

UUID of the boolean run-state signal (True = running).

required
planned_time_hours float | None

Fixed planned production hours per day. If None, the time the state signal covers each day is used.

None
value_column str

Column holding the boolean state.

'value_bool'
max_gap str | None

Optional cap (e.g. "15min"). A sample's duration is the gap to the next reading; a gap longer than this is treated as a logging outage ("no data") and excluded from both run time and planned time. Without it, an overnight or logger gap while the last state was running is booked as run time, inflating availability. Only meaningful when planned_time_hours is None (the planned time is derived from the data). None (default) applies no cap: every interval between readings, however long, counts toward the preceding state.

None

Returns:

Type Description
DataFrame

Summary-shape DataFrame with columns:

DataFrame
  • start, end, duration_seconds (canonical day boundaries; real day length)
DataFrame
  • run_seconds
DataFrame
  • planned_seconds
DataFrame
  • availability_pct
DataFrame
  • excluded_seconds: time removed by max_gap as "no data" (split at midnight like the rest; 0 without max_gap)

performance ¤

performance(
    counter_uuid: str,
    ideal_cycle_time: float,
    run_state_uuid: str | None = None,
    *,
    value_column: str = "value_integer",
    value_column_state: str = "value_bool",
    handle_resets: bool = True,
    max_gap: str | None = None
) -> pd.DataFrame

Calculate performance percentage (actual vs ideal throughput).

Performance = (actual_parts * ideal_cycle_time) / run_time (ISO 22400-2: PQ x pICT / APT). If run_state_uuid is None, the time the counter readings cover each day is used as run time. With a state signal, a day it does not cover has unknown run time: run_seconds, ideal_parts and performance_pct are NaN. A value above 100 % means the inputs disagree; it is kept (not clipped) and reported with a :class:~ts_shape.errors.DataQualityWarning naming the day.

Parameters:

Name Type Description Default
counter_uuid str

UUID of the monotonic part counter.

required
ideal_cycle_time float

Ideal cycle time in seconds per part.

required
run_state_uuid str | None

Optional UUID for run-state to compute actual run time.

None
value_column str

Column holding counter values.

'value_integer'
value_column_state str

Column holding boolean run state.

'value_bool'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the production after the reset instead of losing it, so actual_parts is the reset-aware total. The increments are computed once over the whole signal; the increment between two readings is spread over the time between them and split at midnight in proportion to time (ISO 22400-2: a part belongs to the period it was produced in), so the days add up to the signal total and actual_parts can be fractional. The result gains a resets column with the number of resets observed each day. When False, actual_parts is simply max(0, last - first) and a day containing a reset reports too little.

True
max_gap str | None

Optional cap forwarded to :meth:availability when run_state_uuid is given, so a logging gap does not inflate the run time used as the performance denominator. None (default) applies no cap.

None

Returns:

Type Description
DataFrame

Summary-shape DataFrame with columns:

DataFrame
  • start, end, duration_seconds
DataFrame
  • actual_parts
DataFrame
  • ideal_parts
DataFrame
  • run_seconds
DataFrame
  • performance_pct
DataFrame
  • resets (only when handle_resets=True)

quality ¤

quality(
    total_uuid: str,
    reject_uuid: str,
    *,
    value_column: str = "value_integer",
    handle_resets: bool = True
) -> pd.DataFrame

Calculate quality percentage (good parts / total parts).

Parameters:

Name Type Description Default
total_uuid str

UUID of the total-parts counter.

required
reject_uuid str

UUID of the reject-parts counter.

required
value_column str

Column holding counter values.

'value_integer'
handle_resets bool

When True (default), treat a decreasing counter as a reset and count the production after the reset for both the total and the reject counters, so a day containing a reset is not under-reported. Increments are spread over the time between two readings and split at midnight in proportion to time (see :meth:performance), so the days add up to the signal totals and the part counts can be fractional. The result gains a resets column with the combined number of resets (total + reject) observed each day. When False, both quantities are max(0, last - first).

True

Returns:

Type Description
DataFrame

Summary-shape DataFrame with columns:

DataFrame
  • start, end, duration_seconds
DataFrame
  • total_parts
DataFrame
  • reject_parts
DataFrame
  • good_parts
DataFrame
  • quality_pct
DataFrame
  • resets (only when handle_resets=True)

oee ¤

oee(
    run_state_uuid: str,
    counter_uuid: str,
    ideal_cycle_time: float,
    total_uuid: str | None = None,
    reject_uuid: str | None = None,
    *,
    planned_time_hours: float | None = None,
    handle_resets: bool = True,
    max_gap: str | None = None
) -> pd.DataFrame

Calculate daily OEE = Availability * Performance * Quality.

When total_uuid / reject_uuid are not provided, quality is assumed to be 100 %.

Parameters:

Name Type Description Default
run_state_uuid str

UUID of the boolean run-state signal.

required
counter_uuid str

UUID of the monotonic part counter.

required
ideal_cycle_time float

Ideal cycle time in seconds per part.

required
total_uuid str | None

Optional UUID of total-parts counter (for quality).

None
reject_uuid str | None

Optional UUID of reject-parts counter (for quality).

None
planned_time_hours float | None

Fixed planned production hours per day.

None
handle_resets bool

When True (default), the part / total / reject counters are treated as reset-aware (see :meth:performance and :meth:quality), so a day where a counter resets does not silently drag OEE toward zero. Forwarded to the performance and quality components. Default True.

True
max_gap str | None

Optional cap (e.g. "15min") forwarded to the availability and performance components so a logging gap is not booked as run time (which would inflate availability and understate performance). None (default) applies no cap: every interval between readings, however long, counts toward the preceding state.

None

Returns:

Type Description
DataFrame

Summary-shape DataFrame with columns:

DataFrame
  • start, end, duration_seconds
DataFrame
  • availability_pct
DataFrame
  • performance_pct
DataFrame
  • quality_pct
DataFrame
  • oee_pct
DataFrame

followed by the basis each percentage was computed from, taken

DataFrame

from the components (a day missing from a component reports 0, except

DataFrame

that a day the state signal does not cover has NaN

DataFrame

availability_pct, performance_pct, oee_pct,

DataFrame

run_seconds, planned_seconds and ideal_parts: its run

DataFrame

time is unknown):

DataFrame
  • run_seconds, planned_seconds, excluded_seconds: from :meth:availability (excluded_seconds is the time max_gap removed as "no data")
DataFrame
  • actual_parts, ideal_parts: from :meth:performance
DataFrame
  • total_parts, reject_parts, good_parts: from :meth:quality; NaN when total_uuid / reject_uuid are not given (quality is then assumed to be 100 %)
DataFrame
  • resets (only when handle_resets=True): counter resets of the part counter plus the total / reject counters