downtime_tracking
downtime_tracking ¤
Downtime tracking by shift and reason.
Essential module for daily downtime analysis: - Downtime by shift - Downtime by reason code - Availability calculations - Downtime trends
DowntimeEvents ¤
DowntimeEvents(
dataframe: DataFrame,
*,
time_column: str = "systime",
shift_definitions: (
dict[str, tuple[str, str]] | None
) = None,
specs: Mapping[str, SignalSpec] | None = None
)
Bases: Base
Track machine downtimes by shift and reason.
Each UUID represents one signal: - run_state_uuid: machine state (running/stopped/idle) - reason_uuid: downtime reason code (optional)
Merge keys: [date, shift] for shift-level, [period] for trend data.
Run state comes in one of two forms, and both give the same result:
- a string state signal in
value_string(running_value="Running",stopped_value="Stopped"), or - a boolean run signal in
value_bool(True = running). It is used when the state rows carryvalue_boolbut novalue_string, or whenrunning_value/stopped_valueis a bool (then matched as given).
Durations are reported in seconds (*_seconds); the *_minutes
columns are kept as a convenience.
Pipeline example::
downtime = DowntimeEvents(df)
shift_dt = downtime.downtime_by_shift('machine_state')
# → merge with QualityEvents.nok_by_shift() on [date, shift]
# → merge with ShiftReportEvents.shift_production() on [date, shift]
# → feed into ShiftHandoverEvents.from_shift_data()
Example
import ts_shape df = ts_shape.make_id_signal("state", ["Running", "Stopped", "Running"], ... hold=20, freq="1min", start="2025-01-01 08:00") out = ts_shape.DowntimeEvents(df).downtime_by_shift("state") out[["date", "shift", "downtime_minutes", "availability_pct"]].round(1) date shift downtime_minutes availability_pct 0 2025-01-01 shift_1 20.0 66.1
Initialize downtime 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
|
specs
|
Mapping[str, SignalSpec] | None
|
Optional signal semantics (see :mod: |
None
|
downtime_by_shift ¤
downtime_by_shift(
run_state_uuid: str,
*,
running_value: str | bool = "Running",
value_column: str = "value_string",
max_gap: str | None = None,
production_day: bool = True
) -> pd.DataFrame
Calculate downtime duration per shift.
Each reading's state holds until the next reading, and that interval is split at the shift boundaries (the ISO 22400-2 time model): a stop from 07:00 to 15:00 is 7 h of downtime in the 06:00-14:00 shift and 1 h in the 14:00-22:00 shift. A shift is reported when any observed time falls in it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_state_uuid
|
str
|
UUID for machine state signal |
required |
running_value
|
str | bool
|
Value that indicates machine is running. A bool
matches a boolean run signal ( |
'Running'
|
value_column
|
str
|
Column containing state values |
'value_string'
|
max_gap
|
str | None
|
Optional cap (e.g. "15min"). A duration between two
consecutive readings longer than this is treated as a logging
gap ("no data") and excluded, instead of being attributed to
the preceding state. Without it, a logger outage between a
|
None
|
production_day
|
bool
|
Attribute an overnight shift's post-midnight
readings to the shift's start date, so a night shift is one
|
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with downtime by shift: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
The |
Example
import ts_shape df = ts_shape.make_id_signal( ... "state", ["Running", "Stopped", "Running"], hold=20, freq="1min", ... start="2025-01-01 08:00") out = ts_shape.DowntimeEvents(df).downtime_by_shift("state") out[["shift", "downtime_minutes", "uptime_minutes", "availability_pct"]] shift downtime_minutes uptime_minutes availability_pct 0 shift_1 20.0 39.0 66.101695
downtime_by_reason ¤
downtime_by_reason(
run_state_uuid: str,
reason_uuid: str,
*,
stopped_value: str | bool = "Stopped",
value_column_state: str = "value_string",
value_column_reason: str = "value_string"
) -> pd.DataFrame
Analyze downtime by reason code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_state_uuid
|
str
|
UUID for machine state signal |
required |
reason_uuid
|
str
|
UUID for downtime reason signal |
required |
stopped_value
|
str | bool
|
Value indicating machine is stopped. A bool
matches a boolean run signal ( |
'Stopped'
|
value_column_state
|
str
|
Column containing state values |
'value_string'
|
value_column_reason
|
str
|
Column containing reason codes |
'value_string'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with downtime by reason: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
The |
DataFrame
|
Canonical twin columns: |
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 states = ["Running", "Stopped", "Running", "Stopped", "Running"] df = pd.concat([ts_shape.make_id_signal("state", states, hold=10, freq="1min"), ... ts_shape.make_id_signal("reason", ["-", "Jam", "-", "Tool", "-"], hold=10, ... freq="1min")]) out = ts_shape.DowntimeEvents(df).downtime_by_reason("state", "reason") out[["reason", "occurrences", "total_minutes", "share_of_total_pct"]] reason occurrences total_minutes share_of_total_pct 0 Jam 10 10.0 50.0 1 Tool 10 10.0 50.0
top_downtime_reasons ¤
top_downtime_reasons(
run_state_uuid: str,
reason_uuid: str,
*,
top_n: int = 5,
stopped_value: str | bool = "Stopped",
value_column_state: str = "value_string",
value_column_reason: str = "value_string"
) -> pd.DataFrame
Get top N downtime reasons (Pareto analysis).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_state_uuid
|
str
|
UUID for machine state signal |
required |
reason_uuid
|
str
|
UUID for downtime reason signal |
required |
top_n
|
int
|
Number of top reasons to return |
5
|
stopped_value
|
str | bool
|
Value indicating machine is stopped (a string, or
a bool for a boolean run signal; see :meth: |
'Stopped'
|
value_column_state
|
str
|
Column containing state values |
'value_string'
|
value_column_reason
|
str
|
Column containing reason codes |
'value_string'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with top N reasons and cumulative percentage: reason, |
DataFrame
|
total_minutes, total_seconds, pct_of_total, cumulative_pct |
DataFrame
|
Canonical twin columns: |
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 states = ["Running", "Stopped", "Running", "Stopped", "Running"] df = pd.concat([ts_shape.make_id_signal("state", states, hold=10, freq="1min"), ... ts_shape.make_id_signal("reason", ["-", "Jam", "-", "Tool", "-"], hold=10, ... freq="1min")]) top = ts_shape.DowntimeEvents(df).top_downtime_reasons("state", "reason", top_n=1) top[["reason", "total_minutes", "cumulative_pct"]] reason total_minutes cumulative_pct 0 Jam 10.0 50.0
availability_trend ¤
availability_trend(
run_state_uuid: str,
*,
running_value: str | bool = "Running",
value_column: str = "value_string",
window: str = "1D",
max_gap: str | None = None
) -> pd.DataFrame
Calculate availability trend over time.
Each reading's state holds until the next reading, and that interval is split at the window boundaries (the ISO 22400-2 time model), so every window holds exactly the up / down time that fell inside it. A window is reported when any observed time falls in it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_state_uuid
|
str
|
UUID for machine state signal |
required |
running_value
|
str | bool
|
Value that indicates machine is running (a string,
or a bool for a boolean run signal; see
:meth: |
'Running'
|
value_column
|
str
|
Column containing state values |
'value_string'
|
window
|
str
|
Time window for aggregation (e.g., '1D', '1W') |
'1D'
|
max_gap
|
str | None
|
Optional cap (e.g. "15min"). A gap between consecutive
readings longer than this is treated as "no data" and excluded,
instead of being attributed to the preceding state (which would
inflate availability across a logging outage). |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with availability trend: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
The |
Example
import ts_shape df = ts_shape.make_id_signal( ... "state", ["Running", "Stopped", "Running"], hold=20, freq="1min") out = ts_shape.DowntimeEvents(df).availability_trend("state", window="30min") out[["period", "availability_pct", "downtime_minutes"]] period availability_pct downtime_minutes 0 2025-01-01 00:00:00 66.666667 10.0 1 2025-01-01 00:30:00 65.517241 10.0