Skip to main content
Xenors Xenors
Home › Data Science › Project: Predictive Maintenance with IoT Sensor Data

Module 13: Advanced Real-World Projects & CapstoneBy Ashok Kumar Yadav4 min read

Project: Predictive Maintenance with IoT Sensor Data

Unplanned downtime is expensive. This project predicts whether a machine will fail within the next 24 hours from vibration, temperature and pressure readings.

Industrial sensor stream feeding a machine learning model that predicts equipment failure
Quick answer: Create rolling-window features from sensor readings, label each row by whether a failure occurs in the next 24 hours, split the data by time, train a gradient boosting classifier, evaluate recall and precision, and score new readings in a streaming loop.

Frame the problem

Predictive maintenance is usually a rare-event classification. Missing a failure costs far more than a false alarm, so recall at an acceptable precision matters more than accuracy. Never split randomly: use earlier data for training and later data for testing, or the model sees the future.

Create a sample dataset

A synthetic dataset lets you run the whole pipeline. Replace it with a public dataset such as NASA C-MAPSS when you build the portfolio version.

Synthetic sensor stream with three failures
import numpy as np, pandas as pd

rng = np.random.default_rng(1)
n = 6000
t = pd.date_range("2026-01-01", periods=n, freq="h")
vib = rng.normal(1.0, 0.1, n); temp = rng.normal(70, 3, n); pres = rng.normal(30, 1, n)
fail_at = [1500, 3200, 5000]
for f in fail_at:                      # readings degrade before each failure
    ramp = np.linspace(0, 1, 48)
    vib[f-48:f] += ramp * 0.8; temp[f-48:f] += ramp * 9
df = pd.DataFrame({"ts": t, "vib": vib, "temp": temp, "pres": pres})
df["fail_next_24h"] = 0
for f in fail_at:
    df.loc[f-24:f, "fail_next_24h"] = 1
print(df["fail_next_24h"].mean())

Feature engineering

Rolling statistics expose slow degradation that a single reading hides. Use only past values.

Rolling mean, spread and trend
for c in ["vib", "temp", "pres"]:
    r = df[c].rolling(12)
    df[f"{c}_mean12"] = r.mean()
    df[f"{c}_std12"] = r.std()
    df[f"{c}_delta"] = df[c] - df[c].shift(6)
df = df.dropna().reset_index(drop=True)
FEATS = [c for c in df.columns if c not in ("ts", "fail_next_24h")]

Train and evaluate

Handle class imbalance with a weight and judge by the precision-recall trade-off.

Time-based split and weighted training
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import classification_report

cut = int(len(df) * 0.7)
train, test = df.iloc[:cut], df.iloc[cut:]
w = (train.fail_next_24h == 0).sum() / max((train.fail_next_24h == 1).sum(), 1)
model = HistGradientBoostingClassifier(max_depth=4, random_state=0)
model.fit(train[FEATS], train.fail_next_24h,
          sample_weight=train.fail_next_24h.map({0: 1, 1: w}))
pred = model.predict_proba(test[FEATS])[:, 1] > 0.5
print(classification_report(test.fail_next_24h, pred, zero_division=0))

Real-time scoring loop

In production, readings arrive from MQTT or Kafka. The loop keeps a rolling buffer, builds the same features and raises an alert above a threshold.

Streaming score function with alerting
from collections import deque

buf = {c: deque(maxlen=12) for c in ["vib", "temp", "pres"]}
def score(reading):
    for c in buf: buf[c].append(reading[c])
    if len(buf["vib"]) < 12: return None
    row = {}
    for c in buf:
        s = pd.Series(buf[c])
        row[f"{c}"] = s.iloc[-1]; row[f"{c}_mean12"] = s.mean(); row[f"{c}_std12"] = s.std()
        row[f"{c}_delta"] = s.iloc[-1] - s.iloc[-6]
    return float(model.predict_proba(pd.DataFrame([row])[FEATS])[0, 1])

for _, r in test.head(30).iterrows():
    p = score(r)
    if p and p > 0.7: print("ALERT", r.ts, round(p, 2))

Hands-on practice

Run the examples in the downloadable Jupyter notebook. The extra checks below focus on mistakes that appear in real projects, not just syntax.

Create rolling sensor features

Practice example
import pandas as pd
s=pd.Series([40,42,41,50,55])
print(pd.DataFrame({'temp':s,'roll3':s.rolling(3).mean()}))
temp roll3
40 NaN
42 NaN
41 41.0
50 44.3
55 48.7

Rolling features capture trend but must use only past data at prediction time.

Translate probabilities into maintenance priorities

Practice example
assets=[('A',.12),('B',.81),('C',.46)]
print(sorted(assets,key=lambda x:x[1],reverse=True))
[('B', 0.81), ('C', 0.46), ('A', 0.12)]

Ranking risk can be more useful operationally than a hard binary prediction.

Self-check

  • Can you explain why the first practice result is trustworthy rather than just plausible?
  • What would you monitor or test before using this Predictive Maintenance workflow in production?
  • Which assumption in the example would break first on a larger or messier dataset?

Frequently asked questions

Which metric matters most in predictive maintenance?

Recall for failures at an acceptable precision, because missed failures are costly while false alarms waste inspections.

Why not split data randomly?

Random splits leak future information into training and give unrealistically good scores for time-ordered data.

Which public dataset can I use?

NASA C-MAPSS turbofan data and the AI4I 2020 predictive maintenance dataset are commonly used.

Primary references

Use these official sources when exact syntax, defaults or definitions matter beyond the examples here.

Written by Ashok Kumar Yadav
Ashok Kumar Yadav writes Xenors tutorials on applied AI, programming and data workflows. Every lesson is built around code you can run and check yourself.
GitHub · LinkedIn

Continue exploring