Batteries don’t die suddenly. They cross invisible thresholds into irreversible regimes.
Irreversible Degradation Threshold Detector is an interactive system for finding points of no return in laptop battery health.
Instead of predicting “battery health %” directly, this project:
- treats each device as a system under stress
- computes forces (stress & buffer indices) from real usage patterns
- discovers operating regimes (Stable / Drifting / Irreversible)
- learns interpretable threshold rules with a shallow Decision Tree
- exposes everything through a visual Streamlit app and a CLI
The goal is early warning, not perfect prediction:
detect when a battery is drifting into a regime where “good habits” stop helping.
This project is built around the Kaggle dataset:
Laptop Battery Health and Usage Dataset by prince7489
Dataset link: https://www.kaggle.com/datasets/prince7489/laptop-battery-health-and-usage-dataset
Key columns used (names may vary slightly depending on the version):
device_id| unique device identifierbrand,os,usage_type| context for interpretationdaily_usage_hours| usage intensity (load stress)charging_cycles| cumulative wear signalavg_charge_limit_percent| charging policy (protective vs stressful)battery_health_percent| degradation indicator (used diagnostically)battery_age_months| time exposure to degradationoverheating_issues| “Yes/No” thermal stress proxyperformance_rating| perceived performance under degradation
The detector treats these as stress drivers and buffers, not just features.
Irreversible-Degradation-Threshold-Detector/
├── app/
│ └── app.py # Streamlit UI (3 tabs)
├── data/
│ ├── raw/
│ │ └── laptop_battery_health_usage.csv
│ └── processed/
│ └── battery_degradation_processed.parquet # created by CLI
├── models/
│ └── irreversible_tree.joblib # trained Decision Tree
├── reports/
│ ├── figures/
│ └── metrics/
│ └── degradation_snapshot.csv # optional export
├── src/
│ ├── config.py # paths & column names
│ ├── data_prep.py # loading & processing pipeline
│ ├── thresholds.py # forces, regimes, pseudo labels
│ ├── train_model.py # Decision Tree training
│ └── cli.py # CLI entry points
├── README.md
└── requirements.txt
The engine is built around four ideas:
-
Stress Index
A composite measure of degradation pressure from:- charging cycles
- usage intensity
- age
- overheating issues
- low charge limits
-
Buffer Index
A protective margin representing how much “room” the battery has before collapse:- fewer cycles
- younger age
- conservative charge limits
- no overheating
-
Degradation Intensity
Stress relative to remaining health: [ \text{Intensity} \approx \frac{\text{Stress Index}}{\text{Health}} ] If stress is high but health is still high, the system is tense but not yet collapsing.
If stress is high and health is low, small changes can push the battery over a threshold. -
Regimes & Thresholds
- A Gaussian Mixture Model (GMM) clusters devices in the space of (health, stress)
- Clusters are mapped to Stable / Drifting / Irreversible based on mean health
- A DecisionTreeClassifier learns simple rules for the irreversible regime
- A Threshold Score (0–1) measures distance from the Stable region
This combination gives you both:
- a cohort-level picture (regime map)
- a device-level explanation (driver breakdown + threshold proximity)
The Streamlit app exposes the model through three main views.
Explain the current regime of one device and why it sits where it sits.
This tab answers: “What regime is this battery in, and what is pushing it there?”
Key elements:
- Device selector | browse devices by index or
device_id, showing brand and current regime. - Battery Health (%) | current health reading from the dataset.
- Regime |
🟢 Stable,🟡 Drifting, or🔴 Irreversible, derived from GMM clustering. - Threshold Score | 0–1 score:
- close to 0 → safely inside Stable region
- closer to 1 → near boundary or already in irreversible regime
- Stress Index | overall degradation pressure from cycles, usage, age, heat, and charging policy.
- Tree P(Irreversible) | probability from the shallow Decision Tree trained on a pseudo “irreversible” label.
Below the metrics you get:
A bar chart that decomposes the Stress Index into components:
- Cycles
- Usage Intensity
- Age
- Charging Policy (1 – protection)
- Overheating Flag
High bars = dominant forces dragging the device towards the irreversible regime.
This makes the model explainable: you can say
“This battery is stable now, but cycles + high age are building pressure.”
At the bottom, an expandable table shows the raw row for debugging or deeper inspection.
Visualize regimes and thresholds across the entire fleet.
This view plots each device by:
- x-axis: Stress Index
- y-axis: Battery Health (%)
You can color the points by:
- Regime | shows how the GMM partitions the space into Stable / Drifting / Irreversible
- Threshold Score | continuous coloring from low risk (green) to high risk (red)
Interpretation patterns:
- High health & low stress → dense green cluster (clearly Stable)
- Intermediate health & mid stress → orange region (Drifting)
- Low health & high stress → red region (Irreversible)
Together this plot answers questions like:
- Where does the irreversible regime start in practice?
- Are there devices that look “healthy” but live in high-stress zones?
- How sharply does the boundary curve in stress–health space?
An expandable section at the bottom lets you inspect the processed dataset, including:
- stress and buffer indices
- regime labels
- threshold scores
- pseudo irreversible labels used for the Decision Tree
Counterfactual analysis: “What if we changed how this device is used?”
This tab turns the detector into a what-if engine.
Controls:
- Row index | choose a reference device in the dataset
- Δ Daily Usage (hours) | simulate heavier or lighter daily workloads
- Δ Charging Cycles | simulate more or fewer charge cycles
- Δ Age (months) | advance the clock or roll it back (e.g., compare to earlier life)
- New Charge Limit (%) | test more conservative or aggressive charge limits
- Overheating issues | force “Yes” or “No” to see the structural effect of heat
When you click Run Scenario, the app:
- Creates a modified copy of the dataset with your adjusted row
- Recomputes stress, buffer, regimes, and threshold scores for the whole cohort
- Compares the selected device before vs after the intervention
You get:
- Stress Index (Before / After / Δ)
- Regime (Before / After), sometimes small changes push a device over a regime boundary
- Threshold Score (Before / After / Δ)
- Tree P(Irreversible) (Before / After / Δ) if the model is trained
Plus a small Scenario Comparison table showing raw metrics before and after:
- Daily usage
- Charging cycles
- Charge limit
- Age
- Overheating flag
This view is intentionally counterfactual: it does not predict exact future health.
It shows how different usage policies change the regime and risk profile.
The internal pipeline is simple but structured:
-
Data Loading
- Raw CSV is read from
data/raw/laptop_battery_health_usage.csv.
- Raw CSV is read from
-
Force Computation (
thresholds.compute_forces)- Normalize cycles, usage, age, charge limit, and health to
[0, 1]. - Build:
stress_index| weighted combination of cycles, usage, age, overheating, and low charge limitsbuffer_index| inverse of cycles & age, plus protection from higher charge limits and no overheatingdegradation_intensity| stress normalized by remaining health
- Normalize cycles, usage, age, charge limit, and health to
-
Regime Assignment (GMM)
- Fit a Gaussian Mixture Model in
(health_norm, stress_index)space. - Map components to regimes:
- highest mean health →
🟢 Stable - middle →
🟡 Drifting(if 3 components) - lowest →
🔴 Irreversible
- highest mean health →
- Derive a Threshold Score =
1 - P(Stable).
- Fit a Gaussian Mixture Model in
-
Pseudo Irreversible Label
- Define a data-driven label based on low health and high cycles (quantiles).
- This creates a pseudo “irreversible” target without manual labeling.
-
Interpretable Threshold Model (Decision Tree)
- Train a shallow
DecisionTreeClassifier(max_depth=3)on:- daily usage hours
- charging cycles
- charge limit
- age
- performance rating
- overheating flag
- Export plain-text rules like:
if battery_health_percent <= 68 and charging_cycles >= 820 → irreversible
- Train a shallow
-
Caching & Reuse
- Processed data is saved to
data/processed/battery_degradation_processed.parquet. - The trained tree is saved to
models/irreversible_tree.joblib.
- Processed data is saved to
The Streamlit app and CLI both read from these artifacts.
From the repo root:
# 1) Compute forces, regimes, and cache processed data
python -m src.cli prepare-data
# 2) Train the Decision Tree threshold model
python -m src.cli train
# 3) Export a processed snapshot for analysis
python -m src.cli export-snapshot --out reports/metrics/degradation_snapshot.csv
# 4) Inspect a specific device (by id)
python -m src.cli show-device --id LB001
# or by index
python -m src.cli show-device --index 0pip install -r requirements.txt
# Make sure processed data exists
python -m src.cli prepare-data
# (Optional but recommended) train the threshold model
python -m src.cli train
# Launch the UI
streamlit run app/app.pyThis project is intentionally interpretable, not hyper-optimized.
Possible extensions:
- Add time-aware analysis if per-day logs become available
- Use change-point detection on degradation intensity to refine thresholds
- Incorporate temperature curves instead of a binary overheating flag
- Export SHAP-style explanations for the Decision Tree rules
- Connect to real telemetry to monitor fleets over time