Rule-based classifier
Before anyone reaches for a neural network, the smart move is a rule-based classifier — a handful of explicit if/elif thresholds drawn from domain knowledge. It's fast, fully explainable, and an honest baseline: if your fancy model can't beat the rules, the fancy model isn't earning its keep.
The core shape is a predict(x) -> label function: take one record, run it down a chain of conditions, return a category. Because Python's if/elif/else stops at the first true branch, order is everything — put the strongest, most specific conditions first, or a broad rule will swallow the narrow ones.
A classifier over a whole dataset is just predict in a loop — pair each record with its predicted label. That (record, label) pairing is the same shape a trained model produces; here the "model" is your rules.
*In production this kind of logic is often a Decision Tree (sklearn.tree.DecisionTreeClassifier) learned from labelled data. A decision tree is, in the end, a learned if/elif chain — you're writing the same thing by hand.*
Syntax
def predict(record):
spend = record['spend']
age = record['age']
if spend >= 400: # most specific / strongest first
return 'high'
elif spend >= 150 and age >= 30:
return 'high'
elif spend >= 150:
return 'medium'
elif spend >= 50 and age >= 25:
return 'medium'
else:
return 'low'
# batch: predict in a loop → list of (record, label) pairs
[(r, predict(r)) for r in records]Worked examples
The predict() shape — one record in, one label out
def classify_customer(record):
spend = record["spend"]
age = record["age"]
if spend >= 400:
return "high"
elif spend >= 150 and age >= 30:
return "high"
elif spend >= 150:
return "medium"
elif spend >= 50 and age >= 25:
return "medium"
else:
return "low"
print(classify_customer({"age": 25, "spend": 500.0})) # high (spend >= 400)
print(classify_customer({"age": 45, "spend": 200.0})) # high (spend>=150 & age>=30)
print(classify_customer({"age": 22, "spend": 180.0})) # medium (age too low for high)
print(classify_customer({"age": 28, "spend": 75.0})) # medium (spend>=50 & age>=25)
print(classify_customer({"age": 20, "spend": 60.0})) # low (age < 25)
# Output:
# high
# high
# medium
# medium
# lowClassify a whole batch into (record, label) pairs
def classify_customer(record):
if record["spend"] >= 400:
return "high"
elif record["spend"] >= 150:
return "medium"
else:
return "low"
customers = [
{"id": "C001", "spend": 520.0},
{"id": "C002", "spend": 180.0},
{"id": "C003", "spend": 40.0},
]
labelled = [(c, classify_customer(c)) for c in customers]
for record, label in labelled:
print(record["id"], "->", label)
# Output:
# C001 -> high
# C002 -> medium
# C003 -> lowCommon mistakes & gotchas
Branch order changes the answer
Because elif stops at the first match, a broad rule placed early hides narrower ones. If elif spend >= 150 came *before* elif spend >= 150 and age >= 30, the age rule would never run — everything ≥150 would already be 'medium'. Most specific conditions go first.
Every path must return a label
Drop the final else and a record that matches no branch returns None — a silent, invalid label that breaks scoring downstream. A classifier should ALWAYS produce a class; keep the catch-all else.
Rules are deterministic, not learned
A rule classifier never improves on its own — it's exactly as good as the thresholds you hand-coded. That's its strength (explainable, predictable) and its ceiling (someone has to tune it). It's a baseline, not the finish line.
Why it matters
Most production ML projects should start here. A rule baseline ships in an afternoon, is trivial to explain to a stakeholder, and tells you the bar a learned model has to clear. Teams that skip it often spend weeks training a model that, it turns out, barely beats five `if` statements.