Planned maintenance share¶
How much of our maintenance effort is planned rather than reactive?
Current value (maintenance case data): 65.5% - ! Watch. Open the case.
| Field | Value |
|---|---|
| Definition | Labour hours on planned work orders divided by all maintenance labour hours. |
| Formula | sum(labor_hours where planned) / sum(labor_hours) |
| Unit | % |
| Granularity | Work order; reported monthly by crew. |
| Direction | Higher is better ↑ |
| Target and thresholds | ≥ 80.0% (amber ≥ 65.0%) |
| Parameters | – |
| Owner | Maintenance Manager |
| Source | Work-order generator (synthetic) |
| Input | table work_orders, columns planned, labor_hours |
| Recommended chart | bar - Stacked 100% bar per month (two parts only) or a line against target. |
| Related KPIs | Preventive maintenance compliance, Mean time between failures (MTBF) |
How to read it¶
A maturity indicator: reactive work costs more and disrupts production. It moves slowly; read it quarterly.
Common misreadings¶
- Counting work orders instead of hours - many small planned inspections can hide a few huge breakdowns.
- Raising it by re-labelling emergency work as "planned" the same day; planned means scheduled ahead.
Worked example (from the test suite)¶
| planned | labor_hours |
|---|---|
| True | 4.0 |
| True | 2.0 |
| False | 6.0 |
Expected result: 50.00%. Planned hours 4 + 2 = 6 of 12 total = 0.5 (counting work orders would give 2/3).
This example is asserted in tests/test_formulas.py against the Python function and in tests/test_sql.py against the SQL below.
Implementations¶
Python function (the single implementation used by the site):
@kpi("planned_work_share")
def planned_work_share(df: pd.DataFrame) -> float:
planned = df.loc[df["planned"].astype(bool), "labor_hours"].sum()
return safe_ratio(planned, df["labor_hours"].sum())
Reference SQL (tested on SQLite):
SELECT CAST(SUM(CASE WHEN planned = 1 THEN labor_hours ELSE 0 END) AS REAL) / SUM(labor_hours)
FROM work_orders