Machine learning models are powerful, but sometimes they produce predictions that break human intuition.
Imagine this: you’re predicting house prices. A 2,000 sq. ft. home is predicted cheaper than a 1,500 sq. ft. home. Sounds wrong, right?
This is where monotonicity constraints step in. They make sure models follow the logical business rules we expect.
Let’s follow two colleagues, Alan and Aida, on their journey to discover why monotonicity matters in machine learning.
The Story: Alan & Aida’s Discovery
Alan is a practical engineer. Aida is a principled scientist. Together, they’re building a house price prediction model.
Alan proudly shows Aida his model results:
“Look! R² is great, the error is low. We’re ready to deploy!”
Aida takes the model out for testing:
- For a house with 1500 sq ft → Model predicts $300,000
- For a house with 2000 sq ft → Model predicts $280,000 😮
Aida frowns as she looks at the predictions:
“Wait a second… Why is this 2,000 sq. ft. home predicted cheaper than a 1,500 sq. ft. home? That doesn’t make sense.”
Alan shrugs:
“That’s because the model found noise in the training data. It’s not always logical. But, the accuracy is good overall. Isn’t that enough?”
Aida shakes her head:
“Not really. A trustworthy model must not only be accurate but also follow logic people can trust. Customers won’t trust us if bigger homes sometimes look cheaper. We need a guarantee. This is a monotonicity problem.”
And just like that, Alan learns his next big ML lesson: metrics aren’t everything.
What is Monotonicity in ML?
Aida explains:
“Monotonicity means predictions move in a consistent direction as inputs change. It is like telling the model: as square footage goes up, price should never go down. We call it Monotone increasing. Or, as another example, as a house age gets older, predicted prices should not go up. We call this Monotone decreasing.”
Alan concludes that:
“So monotonicity here matters because it:
- Aligns with business logic, and
- Improves trust & interpretability.”
Aida nodded:
- “Yes, Plus, it helps meet fairness & regulatory expectations.”
Visualizing the Problem
Aida creates a toy dataset in Pandas to show the problem:
import pandas as pd
# Example toy dataset
data = pd.DataFrame({
"sqft": [1200, 1500, 1800, 2000, 2200, 2250],
"predicted_price": [250000, 270000, 260000, 280000, 290000, 285000] # Notice dip at 1800 sqft and 2250 sqft
})
# Sort by sqft
data_sorted = data.sort_values("sqft")
# Check differences in target
data_sorted["price_diff"] = data_sorted["predicted_price"].diff()
# Find monotonicity violations (where price decreases as sqft increases)
violations = data_sorted[data_sorted["price_diff"] < 0]
print("Monotonicity violations:n", violations)
Monotonicity violations:
sqft price price_diff
2 1800 260000 -10000.0
5 2250 285000 -5000.0
And then she plots the violations:
import matplotlib.pyplot as plt
plt.figure(figsize=(7,5))
plt.plot(data["sqft"], data["predicted_price"], marker="o", linestyle="-.", color="steelblue", label="Predicted Price")
# Highlight the dips
for sqft, price, price_diff in violations.values:
plt.scatter(sqft, price, color="red", zorder=5)
plt.text(x=sqft, y=price-3000, s="Dip!", color="red", ha="center")
# Labels and title
plt.title("Predicted House Prices vs. Square Footage")
plt.xlabel("Square Footage (sqft)")
plt.ylabel("Predicted Price ($)")
plt.grid(True, linestyle="--", alpha=0.6)
plt.legend()
Aida points to the Dips: “Here’s the problem: 1,800 sq. ft. is priced lower than 1,500 sq. ft. and 2,250 sq. ft. is priced lower than 2,200 sq. ft.”
Fixing It with Monotonicity Constraints in XGBoost
Alan retrains the model and sets a monotonic increasing constraint on square footage and monotonic decreasing constraint on house age.
This forces the model to always
- increase (or stay the same) when square footage increases given all other features are fixed.
- decrease (or stay the same) when house age increases given all other features are fixed.
He uses XGBoost that makes it easy to enforce monotonicity:
import xgboost as xgb
from sklearn.model_selection import train_test_split
df = pd.DataFrame({
"sqft": [1200, 1500, 1800, 2000, 2200],
"house_age": [30, 20, 15, 10, 5],
"price": [250000, 270000, 280000, 320000, 350000]
})
X = df[["sqft", "house_age"]]
y = df["price"]
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.2, random_state=42)
monotone_constraints = {
"sqft": 1, # Monotone increasing
"house_age": -1 # Monotone decreasing
}
model = xgb.XGBRegressor(
monotone_constraints=monotone_constraints,
n_estimators=200,
learning_rate=0.1,
max_depth=4,
random_state=42
)
model.fit(X_train, y_train)
print(X_test)
print("Predicted price:", model.predict(X_test.values))
sqft house_age
1 1500 20
Predicted price: [250000.84]
Alan hands over the new model to Aida. “Now the model respects domain knowledge. Predictions for larger houses will never dip below smaller ones.”
Aida tests the model again:
- 1500 sq ft → $300,000
- 2000 sq ft → $350,000
- 2500 sq ft → $400,000
Now she sees a smoother plot of house prices vs square footage.
import matplotlib.pyplot as plt
data2 = pd.DataFrame({
"sqft": [1200, 1500, 1800, 2000, 2200, 2250],
"predicted_price": [250000, 270000, 275000, 280000, 290000, 292000]
})
plt.figure(figsize=(7,5))
plt.plot(data2["sqft"], data2["predicted_price"], marker="o",
linestyle="-.", color="green", label="Predicted Price")
plt.title("Monotonic Predicted House Prices vs. Square Footage")
plt.xlabel("Square Footage (sqft)")
plt.ylabel("Predicted Price ($)")
plt.grid(True, linestyle="--", alpha=0.6)
plt.legend()

Aida: “Perfect! When homes are of the same age, a larger size consistently leads to a higher or equal price. Conversely, homes of the same square footage will always be priced lower if they are older.”
Alan: “Yes — we gave the model guardrails that align with domain knowledge.”
Real-World Examples
Alan: what other domains can benefit from monotonicity constraints?
Aida: Anywhere customers or money are involved, monotonicity can impact trust. Some domains where monotonicity really matters are:
- House pricing → Larger homes should not be priced lower.
- Loan approvals → Higher income should not reduce approval probability.
- Credit scoring → Longer repayment history should not lower the score.
- Customer lifetime value (CLV) → More purchases shouldn’t lower CLV predictions.
- Insurance pricing → More coverage should not reduce the premium.
Takeaways
- Accuracy alone doesn’t guarantee trustworthiness.
- Monotonicity ensures predictions align with common sense and business rules.
- Customers, regulators, and stakeholders are more likely to accept and use models that are both accurate and logical.
As Aida reminds Alan:
“Make models not just smart, but sensible.”
Closing Thoughts
Next time you build a model, don’t just ask: How accurate is it? Also ask: Does it make sense to the people who’ll use it?
Monotonicity constraints are one of many tools for designing trustworthy ML models — alongside explainability, fairness constraints, and transparency.
. . .
Thanks for reading! I often share insights on practical AI/ML techniques—let’s connect on LinkedIn if you’d like to continue the conversation.