Data Scientist & Analyst specializing in predictive modeling, statistical analysis, and translating complex datasets into actionable business intelligence.
The secondary automobile market in Zimbabwe suffers from significant pricing asymmetry, inconsistent valuation metrics, and market opacity. Buyers and sellers frequently struggle to determine the true fair market value of vehicles due to non-linear depreciation curves, varying mileage velocities, and brand prestige premiums.
The objective of this project was to build an end-to-end, data-driven machine learning system that:
Analyzing vehicle price against manufacturing year and age reveals strong exponential depreciation dynamics. Newer vehicles lose value rapidly within their first five years before stabilizing into a steady baseline curve.

Key Takeaway: Vehicle prices correlate positively with year ($r = 0.476$). The average price jumps significantly for post-2020 inventory due to import tariff structures and newer technology baselines.
Cumulative mileage acts as a continuous penalty against asset valuation. However, value loss per kilometer decays exponentially—the highest drop in value occurs within the first $30,000\text{ km}$.

Market share in the region is heavily concentrated. Toyota commands 41.6% of total marketplace listings, demonstrating strong liquidity and value retention compared to European luxury brands.

Automatic diesel configurations dominate the upper price brackets, driven by heavy utility demand (commercial trucks and off-road SUVs).

Calculating the annual usage rate ($\text{Annual Mileage} = \frac{\text{Mileage}}{\text{Age}}$) provides crucial insights into how intensely vehicles are driven relative to national baselines.

Different vehicle brands retain value at vastly different rates after 5 years of operation. Commercial brands like Isuzu retain up to 63.1% of their original value, whereas luxury sedans suffer steep 5-year drops.

By mapping vehicle cost per operational year across market segments (Luxury, Premium, Standard), we isolate high-value listings that offer maximum functional lifespan per dollar spent.

A dual-axis linear model is insufficient to capture price behavior. Mapping Price against Year and Mileage simultaneously reveals a non-linear regression surface.

A comprehensive correlation matrix confirms that while Year ($r = 0.476$) and Vehicle Age ($r = -0.476$) are strong linear drivers, interaction effects with Mileage and Market Segment require gradient-boosted decision trees.

When training standard decision tree regressors on raw dollar values, the objective function minimizes absolute dollar errors ($MAE = \frac{1}{n}\sum \vert{}y_i - \hat{y}_i\vert{}$). Consequently, a $$10,000$ error on a $$200,000$ Land Cruiser receives the same weight as a $$10,000$ error on a $$2,000$ Toyota Harrier.
This caused the baseline model to heavily overvalue low-cost, budget vehicles:

To force the algorithm to evaluate relative percentage errors rather than raw dollar magnitudes, we applied a logarithmic transformation to the target variable. The XGBoost regressor was retrained on $y_{\text{transformed}}$ and evaluated by applying the inverse exponential function ($\text{expm1}$) to predictions.
To differentiate genuine market bargains from typos or wrecked vehicles, a statistical residual governance engine was built using Z-score classification:
\[Z = \frac{\text{Actual Price} - \text{Predicted Price}}{\sigma_{\text{residuals}}}\]| Z-Score Range | System Classification | Actionable Meaning |
|---|---|---|
| $Z \ge +1.5$ | ❌ Overpriced | Asset listed above fair market trend line |
| $-1.2 \le Z < +1.5$ | 🟢 Standard Market Value | Fair competitive pricing tier |
| $-2.5 \le Z < -1.2$ | 🔥 Verified High-Value Deal | Strong arbitrage opportunity |
| $Z < -2.5$ | ⚠️ High Risk / Deep Discount | Suspected mechanical damage, salvage title, or listing typo |
The final model was compiled with serialized encoders (joblib) and deployed via a dynamic Gradio web application featuring:
```python
if name == “main”: demo.launch(server_name=”0.0.0.0”, server_port=int(os.environ.get(“PORT”, 7860)))