Build a customer health score without hiding missing inputs
In this article (5 sections)
A customer health score combines several signals into a summary for a particular decision. Its value depends on the quality of those signals, the meaning of the weights and the action attached to the result. A precise-looking score can be misleading when missing inputs are silently replaced or when the score is presented as a probability without validation.
Keep the component values, coverage and score version beside the total. Reviewers should be able to explain why a customer received a particular classification.
Define a small, explicit score
The synthetic health-input fixture contains three normalized components between zero and one, with higher values defined as healthier:
| Customer | Activity | Support experience | Payment |
|---|---|---|---|
| U1 | 0.8 | 0.9 | 1.0 |
| U2 | 0.2 | 0.5 | 1.0 |
| U3 | 0.6 | Missing | 1.0 |
Use illustrative weights of 50%, 30% and 20%. These weights were chosen for the exercise; they were not learned from customer outcomes. The fixture supplies normalized inputs, so a real implementation still needs definitions for their source windows, scaling and direction.
SELECT user_id,
.5*activity_score+.3*support_score+.2*payment_score AS complete_score,
CASE WHEN activity_score IS NOT NULL THEN .5 ELSE 0 END
+ CASE WHEN support_score IS NOT NULL THEN .3 ELSE 0 END
+ CASE WHEN payment_score IS NOT NULL THEN .2 ELSE 0 END AS observed_weight
FROM health_inputs ORDER BY user_id;U1 scores 0.87 and U2 scores 0.45. U3's complete score is unknown, with observed weight 0.70. SQL's null propagation preserves the missing required component rather than inventing its value.
Show why two common repairs change the meaning
Replacing U3's missing support input with zero produces 0.50, treating unknown support experience as the worst possible score. Dividing its known weighted subtotal by 0.70 produces approximately 0.7143, redistributing the missing component's weight across the observed components.
Either can be a deliberate policy, but neither is the original complete score. The second also makes customers' totals depend on different effective weighting schemes.
from math import isclose
from build_and_verify import database
weights = (.5,.3,.2)
db = database()
rows = db.execute('SELECT * FROM health_inputs ORDER BY user_id').fetchall()
db.close()
results = {}
for user,*values in rows:
assert all(v is None or 0 <= v <= 1 for v in values)
observed_weight = sum(w for w,v in zip(weights,values) if v is not None)
subtotal = sum(w*v for w,v in zip(weights,values) if v is not None)
complete = subtotal if all(v is not None for v in values) else None
results[user] = (complete,observed_weight,subtotal)
assert isclose(results['U1'][0], .87)
assert isclose(results['U2'][0], .45)
assert results['U3'][0] is None
assert isclose(results['U3'][1], .7)
assert isclose(results['U3'][2], .5)
print(results)
print({'U3_renormalized_alternative':results['U3'][2]/results['U3'][1]})Missing support data may mean no survey response, no support contact, a failed integration or an inapplicable component. Those reasons are not interchangeable. Store a missingness reason where the source can establish it.
Match the score to an action
For an operational queue, show U3 as requiring data review rather than assigning a confident health category. Show U2's component breakdown so a reviewer can see that low activity drives its lower total despite a healthy payment signal.
Do not call 0.87 an 87% chance of renewal. A weighted index has no such probabilistic interpretation unless an appropriate model and calibration evaluation establish it. Even an accurate risk model does not identify which intervention will improve outcomes.
Validate usefulness over time
Freeze component definitions and weights before evaluating on later outcomes. Compare the score with a simple baseline, examine performance across relevant customer groups and assess whether the resulting queue fits operational capacity.
If the score triggers human outreach, record that intervention. Subsequent outcomes may reflect both baseline risk and the action taken, complicating naive comparisons between score groups. Avoid using information recorded after the outcome as a predictor of that outcome.
Check sensitivity to reasonable weight changes. A customer who jumps from the top to the bottom of a queue after a tiny weight adjustment deserves scrutiny before the score drives consequential decisions.
Exercise: create a separate status for “support component not applicable” and propose a documented scoring policy. Compare it with “support integration failed” without treating those two cases as equivalent.
NeuraPath's Data Analytics with Generative AI course connects data preparation with interpretable business measures. A useful health score exposes uncertainty and gives the reviewer a reason for the next action.
Continue learning
This article is part of the Customer and product analytics sequence. Use the neighbouring tasks when you need the prerequisite or the next application.
- Review the prerequisite or neighbouring task in Measure subscription expansion and contraction revenue.
- Continue with Analyze support contacts per active customer.
Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.
This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.
Explore Data Analytics with Generative AI