Turning complex data into decisions that matter — across finance, construction, telecommunications, real estate, transportation, and macroeconomics.
This repository brings together seven end-to-end data science projects spanning different industries, techniques, and business problems. Each project follows a consistent approach: start with a real business question, explore and prepare the data rigorously, build and validate models against statistical standards, and translate the results into actionable recommendations with quantified impact.
The work covers the full analytics spectrum — from interactive dashboards and hypothesis testing through regression modelling, time series forecasting, machine learning classification deployed via REST APIs and containerised microservices, and modern LLM workflows with schema-validated outputs.
| Domain | Techniques | Deliverables |
|---|---|---|
| Macroeconomics | Dashboard design, DAX, data modelling | Interactive Power BI report |
| Construction | MLR, logistic regression, hypothesis testing | Predictive formula, interactive calculator |
| Telecommunications | Decision trees, RFM analysis, FastAPI deployment | Deployed churn prediction API |
| Finance | Holt-Winters, ARIMA, ARIMA+GARCH | 365-day stock price forecast with volatility |
| Transportation | Gradient boosting, SHAP, MLflow, Docker Compose | Dockerised dynamic pricing service |
| Customer Analytics / AI | LLMs, Pydantic schemas, retries, fallback handling, gold-set evaluation | Schema-validated LLM feedback classifier |
| Real Estate | Gradient boosting, SHAP, Cross Validation, MLflow, Docker Compose, FastAPI deployment | Dockerised dynamic property evaluation and similarity matching service |
Exploring 20 years of economic performance across 180+ countries using the IMF World Economic Outlook dataset.
📖 Expand Full Project Details
A thinktank scenario required an analytical tool to help policymakers and the general public understand patterns of economic performance at the country and country-group level. The raw IMF dataset — 44 indicators across 180+ countries over 20 years — was too dense for manual analysis.
An interactive Power BI dashboard built on a star schema data model with one fact table and two dimension tables. The dashboard presents six headline indicators through multiple coordinated visualisations, all responsive to country, region, income group, and year-range filters.
┌──────────────────┐ ┌──────────────────┐
│ Countries │ │ Indicators │
│ (Dimension) │ │ (Dimension) │
├──────────────────┤ ├──────────────────┤
│ CountryKey (PK) │ │ Indicator Code │
│ ISO │◄──┐ │ Description │
│ Country │ │ └────────┬─────────┘
│ Region │ │ │
│ Income Group │ │ │ 1:*
└──────────────────┘ │ ┌────────▼─────────┐
1:* (ISO) └───┤ Economic Data │
│ (Fact) │
└──────────────────┘
- KPI Cards with Sparklines — six headline indicators with embedded trendlines and colour-coded cues
- Top 5 Bar Chart — dynamically ranks best-performing countries by GDP growth
- Interactive Line Chart — tracks any indicator over time via tile slicer switching
- Radar Chart — multi-dimensional economic profile powered by a
SWITCHDAX measure - Flexible Slicers — Country, Region, Income Group, Year Range
Custom measures for each indicator (card callout + sparkline pairs), a DATATABLE-based radar chart, and a field parameter for dynamic line chart switching.
Power BI · Power Query · DAX · Star Schema · Excel
| Source | Records | Variables | Period |
|---|---|---|---|
| IMF World Economic Outlook | 180+ countries | 44 indicators | 2001–2020 |
Reducing material costs by £21,600 per project through regression analysis on 1,030 historical mix trials.
📖 Expand Full Project Details
StrataForge Construction Materials Ltd., a UK-based firm specialising in commercial foundations and high-load structural slabs, had accumulated 1,030 concrete mix trial records but still relied on experience-based decision making. Conservative cement-heavy mixes were inflating costs and carbon emissions, while occasional 28-day strength failures triggered expensive rework cycles.
The business needed answers to three questions: What drives compressive strength? Does fly ash reduce performance? Can we predict strength before pouring?
A complete statistical analysis pipeline in R, progressing through 12 candidate regression models via forward stepwise selection, a logistic regression classifier for fly ash detection, and three hypothesis tests that proved fly ash can safely replace cement.
concrete_strength = 23.914
+ 0.0974 × cement
− 2.545 × ln(superplasticizer)
− 0.2374 × water
+ 9.759 × ln(age)
+ 0.0683 × slag
| Metric | Value |
|---|---|
| R² | 81.35% |
| All coefficients significant | p < 0.001 |
| VIF (multicollinearity) | All between 1.01 – 1.49 |
| Assumptions passed | 5 / 5 |
| Finding | Evidence | Business Impact |
|---|---|---|
| Cement and age are strongest drivers | Highest positive coefficients | Optimise cement dosage based on required curing time |
| Water reduces strength | β = −0.2374 | Minimise water content; use superplasticizer for workability |
| Fly ash does NOT reduce strength | Kruskal-Wallis p = 0.2324 | Confidently substitute cement with cheaper, greener fly ash |
| Concrete category irrelevant | Kruskal-Wallis p = 0.3364 | Coarse vs fine texture has no effect on strength |
| Metric | Before (Conservative) | After (Optimised) |
|---|---|---|
| Cement per m³ | 400 kg | 310 kg |
| Slag per m³ | 0 kg | 100 kg |
| Cement cost per m³ | £48.00 | £37.20 |
| Total cost (2,000 m³ project) | £96,000 | £74,400 |
| Saving | £21,600 (22.5%) | |
| CO₂ reduction | ~180 tonnes |
- Multiple Linear Regression model (R² = 81.35%)
- Logistic regression fly ash classifier (AIC = 145.12)
- Three hypothesis tests (Kruskal-Wallis, Chi-Square)
- Interactive React/Vite presentation with live strength calculator
R · ggplot2 · dplyr · readxl · corrplot · car · caret · RVAideMemoire · React · Vite
| Source | Records | Variables | Type |
|---|---|---|---|
| StrataForge materials lab | 1,030 mix trials | 9 continuous | Clean, no missing values |
Building and deploying a 97%-accuracy churn prediction model via FastAPI for a telecom provider with 15% annual attrition.
📖 Expand Full Project Details
Reder Telecommunications, a Norwegian telecom provider headquartered in Oslo, was losing customers at a rate of 15% annually — with acquisition costs 5–7x higher than retention. The company had over 2,000 customer records with 20+ attributes (many stored as nested JSON), but no predictive capability to identify at-risk customers before they left.
Four key obstacles compounded the problem: limited per-segment analytics, pricing inconsistencies across identical usage patterns, no proactive outage management, and purely reactive retention — interventions only after intent to leave was signalled.
An end-to-end machine learning pipeline from raw nested-JSON data to a deployed FastAPI prediction endpoint:
- Data Preprocessing — parsed 9 nested JSON columns using
ast.literal_eval, normalised into separate DataFrames, merged via CustomerID (12,483 rows × 55 columns) - Feature Engineering — RFM analysis (Recency, Frequency, Monetary) with quintile binning, one-hot/label/target encoding
- Feature Selection — Mutual Information scoring identified top 20 predictors; payment behaviour features dominated
- Model Training — Logistic Regression baseline (97% accuracy), then Decision Tree with
RandomizedSearchCVhyperparameter tuning (4-fold CV) - Deployment — FastAPI REST API with Pydantic validation, serialised model via
pickle
| Rank | Feature | MI Score |
|---|---|---|
| 1 | late_payment_rate |
0.97 |
| 2 | payment_risk_score |
0.95 |
| 3 | total_late_payments |
0.93 |
| 4 | total_interactions |
0.58 |
| 5 | TimeSpent(minutes) |
0.57 |
| 6 | NPS |
0.52 |
Payment behaviour is the single strongest predictor of churn — more than engagement, satisfaction, or demographics.
| Metric | Logistic Regression | Decision Tree (Tuned) |
|---|---|---|
| Accuracy | 97.0% | 96.9% |
| Precision | ~97% | ~97% |
| Recall | ~97% | ~97% |
| F1-Score | ~97% | ~97% |
Best hyperparameters: criterion='log_loss', splitter='random', max_depth=300, min_samples_split=6, min_samples_leaf=4, max_features='log2'
# POST /predict
{
"records": [{ "late_payment_rate": 0.4, "NPS": 3, ... }]
}
# Response
{
"prediction": 1, # 1 = will churn
"probability": 0.847 # 84.7% confidence
}| Provider | Estimated Monthly | Best For |
|---|---|---|
| GCP Cloud Run | $30–75 | POC (pay-per-request) |
| Azure App Service | $45–90 | Enterprise integration |
| AWS ECS Fargate | $55–95 | Max flexibility |
- Trained Decision Tree classifier (model.pkl)
- FastAPI prediction endpoint (app.py)
- RFM customer segmentation
- Cloud deployment cost comparison (AWS vs Azure vs GCP)
- Interactive React/Vite presentation
Python · Pandas · NumPy · Scikit-learn · FastAPI · Pydantic · Uvicorn · Pickle · Jupyter · React · Vite
| Source | Records | Raw Variables | Engineered Features |
|---|---|---|---|
| Reder Telecom CRM | 2,000+ customers | 20+ (incl. nested JSON) | 55 after normalisation |
Forecasting USB stock prices using an iterative approach through Holt-Winters, ARIMA, and ARIMA-GARCH — demonstrating why financial data requires specialised treatment.
📖 Expand Full Project Details
Financial markets exhibit volatility clustering — periods where large price swings follow large swings, and calm follows calm. This violates the constant-variance assumption of standard forecasting methods. An investment firm needed a model that could predict not just the expected price direction, but the range of uncertainty around those forecasts.
Three progressively sophisticated models fitted to 47 years (11,835 daily observations) of US Bancorp stock data, with rigorous diagnostic testing at each stage:
| Model | Ljung-Box p-value | Verdict |
|---|---|---|
| Holt-Winters (α=0.942, γ=FALSE) | < 2.2e-16 | ✗ Residuals autocorrelated |
| ARIMA(1,1,0) via auto.arima | < 2.2e-16 | ✗ Same volatility problem |
| ARIMA(1,0,1) + sGARCH(1,1) | 0.2369 | ✓ White noise residuals |
The iterative failure-diagnosis-improvement cycle is intentional — it builds a rigorous justification for why GARCH is necessary on financial data.
By modelling on log returns instead of raw prices and adding a sGARCH(1,1) conditional variance equation via the rugarch package, the model captured both the mean dynamics and time-varying volatility.
| Parameter | Value | Interpretation |
|---|---|---|
| μ | 0.000603 | Mean daily return (~16% annualised) |
| ar1 | −0.7185 | Autoregressive coefficient |
| ma1 | 0.7068 | Moving average coefficient |
| α₁ | 0.0938 | ARCH effect (impact of previous shock) |
| β₁ | 0.8924 | GARCH persistence (volatility memory) |
| α₁ + β₁ | 0.986 | Highly persistent volatility |
All parameters significant at p < 0.01. Log-Likelihood: 34,139.66.
| Horizon | Predicted Price | Change |
|---|---|---|
| Last observed | $31.93 | — |
| Day 90 | ~$33.73 | +5.6% |
| Day 180 | ~$35.39 | +10.8% |
| Day 365 | ~$39.79 | +24.6% |
Unlike Holt-Winters and ARIMA, the GARCH model also provides a volatility forecast (sigma) at each step — essential for confidence intervals and risk management.
- Three fitted models with full diagnostic output
- 365-day price forecast with return-to-price conversion
- Volatility (sigma) forecast showing decay from 6.7% to 1.8%
- Model performance comparison screenshots
- Interactive React/Vite presentation (17 slides)
R · quantmod · xts · forecast · rugarch · highcharter · tseries · ggplot2 · React · Vite
| Source | Records | Variables | Period |
|---|---|---|---|
| Yahoo Finance (USB) | 11,835 daily observations | 7 (OHLCV + Adj Close) | May 1973 – Apr 2020 |
An explainable machine learning framework for demand forecasting and price optimisation in the rail travel sector — containerised and served through a FastAPI + Streamlit stack.
📖 Expand Full Project Details
VoyageRail Ltd., a regional rail operator, had seen post-pandemic demand recover — but revenue per seat had not kept pace. Ticket volumes were stable, yet yield varied significantly across routes and travel windows, particularly during peak and near-peak periods. Static fare rules written a decade ago no longer responded to how modern travellers book.
Five core issues compounded the challenge: rule-based pricing unable to track real-time demand, revenue leakage from underpriced high-demand seats and overpriced low-demand ones, no segmentation-aware personalisation, operational complexity across multiple routes and classes, and — most critically — previous analytical models offered limited transparency, making it difficult for commercial teams to trust or act on their recommendations.
A full end-to-end dynamic pricing solution delivered as a containerised microservices architecture:
- Data Pipeline — cleaned and engineered 17 booking features including route characteristics, booking lead time, demand index, load factor, customer segment, and loyalty status
- Model Training — compared Linear Regression, Random Forest, Gradient Boosting, and XGBoost; selected the best performer via 5-fold CV on RMSE/MAE/R²
- Explainability First — SHAP values computed for every prediction, producing global feature importance and per-decision explanations that commercial teams can defend
- Experiment Tracking — MLflow logs every run's parameters, metrics, and artifacts to
mlflow.db, enabling reproducibility and model governance - Two-Service Deployment — FastAPI inference backend + Streamlit demo UI, orchestrated via Docker Compose
| Rank | Feature | Relative Importance |
|---|---|---|
| 1 | demand_index |
Highest |
| 2 | load_factor |
Very High |
| 3 | days_before_travel |
High |
| 4 | seat_class |
Moderate–High |
| 5 | route_category |
Moderate |
Demand signals dominate the pricing decision; customer and booking context refine it.
┌────────────────────────────────────────────────────────┐
│ Docker Compose Network │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ streamlit │─────▶│ backend │ │
│ │ (port 8501) │ HTTP │ (FastAPI) │ │
│ │ │◀─────│ (port 8000) │ │
│ │ User Interface │ JSON │ Inference API │ │
│ └──────────────────┘ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ model.pkl + │ │
│ │ MLflow runs │ │
│ └──────────────────┘ │
└────────────────────────────────────────────────────────┘
For a Leeds → York, Standard class, 21 days before travel booking with demand index 0.92:
| Metric | Value |
|---|---|
| Predicted Ticket Price | £37.30 |
| Demand Index | 0.92 |
| Days Before Travel | 21 |
| Estimated Fare Band | Moderate |
Streamlit surfaces three result views: Insight View (with fare band and comparative chart), API Response (raw JSON), and cURL (exportable for testing outside the UI).
| Provider | Estimated Monthly | Best For |
|---|---|---|
| GCP Cloud Run | $41–78 | POC (pay-per-request, scale-to-zero) |
| Azure Container Apps | $72–102 | Enterprise integration |
| AWS ECS Fargate | $72–104 | Max ML ecosystem (SageMaker) |
| Cost Type | Range |
|---|---|
| One-time setup (dev, testing, training) | £11,300 – £20,000 |
| Recurring monthly (infra, monitoring, retraining) | £100 – £220 |
| Year 1 total | £12,500 – £22,640 |
ROI projection: payback in 3–6 months at typical rail operator scale via incremental yield gains.
- Trained regression model (model.pkl) with SHAP explainability
- FastAPI inference endpoint with Pydantic validation
- Streamlit interactive demo for stakeholders
- Docker Compose orchestration (2 services)
- MLflow experiment tracking (SQLite backend)
- Interactive React/Vite presentation (23 slides)
- Full project budget breakdown
Python · Pandas · NumPy · Scikit-learn · XGBoost · SHAP · MLflow · FastAPI · Pydantic · Streamlit · Plotly · Docker · Docker Compose · React · Vite
| Source | Records | Variables | Target |
|---|---|---|---|
| VoyageRail booking system | Historical bookings | 17 features (route, timing, pricing, demand, customer) | ticket_price (regression) |
A production-shaped LLM workflow that turns unstructured customer feedback into typed, schema-validated JSON — demonstrating the engineering scaffolding around modern LLM systems.
📖 Expand Full Project Details
Every consumer business now sits on a pile of unstructured customer text — reviews, NPS comments, support tickets, cancellation reasons. The signal is real, but converting prose into reliable, structured data that downstream pipelines can trust is harder than it looks. Raw LLM calls return inconsistently-shaped outputs, occasionally invalid JSON, and failure modes shift with prompt and model. Without engineering scaffolding around the call, a single bad response can crash an entire pipeline.
The question this project answers: what does production-shaped LLM engineering actually look like in a focused, reviewable codebase?
A focused LLM workflow (~140 lines of core code) that classifies customer feedback into a strictly-typed schema, with the engineering patterns that turn a raw API call into something dependable.
| Pattern | Implementation |
|---|---|
| Schema-constrained outputs | Pydantic BaseModel with enums (Sentiment, ChurnRisk) and bounded floats; LLM responses validated before any downstream use |
| Retry with exponential backoff | Transient errors (rate limits, connection issues) retried with base × 2^attempt delay; 4xx errors bail fast |
| Defensive parsing | Markdown code-fence stripping handles the common LLM failure mode of wrapping JSON in ```json ... ``` despite prompt instructions |
| Graceful fallback | When LLM repeatedly fails, returns a safe neutral / medium default with rationale — pipelines never crash on a single bad call |
| Evaluation harness | Hand-labelled gold set + evaluation script reporting per-field accuracy and surfacing error cases |
{
"sentiment": "negative",
"themes": ["delivery_speed", "support_quality"],
"churn_risk": "high",
"confidence": 0.92,
"rationale": "Customer explicitly states intent to cancel due to repeated late deliveries."
}Useful as a building block for: customer-feedback dashboards, churn-risk pipelines, support-ticket triage, and any analytics workflow needing reliable structured signal from prose.
Rather than vibes-testing the model, I built a small labelled gold set (15 hand-labelled reviews covering positive/negative/neutral × low/medium/high churn risk) and an evaluation script reporting per-field accuracy with error analysis. This makes quality measurable rather than anecdotal, and provides the foundation for drift monitoring or A/B testing different models.
| Metric | Result |
|---|---|
| Sentiment accuracy | [run evaluate.py] |
| Churn-risk accuracy | [run evaluate.py] |
- Expand gold set to 100+ examples covering sarcasm, mixed sentiment, multilingual cases
- Add inter-annotator agreement (Cohen's kappa) as a dataset quality floor
- A/B test models (Haiku vs Sonnet) on accuracy / cost / latency tradeoffs
- Add observability — structured logging, latency histograms, per-failure-mode counters
- Batch mode with concurrency control and progress reporting
- Drift monitoring via scheduled re-evaluation against the gold set
Python 3.10+ · Anthropic API (Claude Haiku 4.5) · Pydantic 2 · python-dotenv
A 3-week internship prototype delivering an automated valuation model (AVM), comparables search, and stakeholder-facing inference application — deployed end-to-end on a $0/month stack.
📖 Expand Full Project Details
AlloyTower required a Phase 1 data platform prototype: a property valuation engine, a comparables search to surface similar listings, an analyst-facing data explorer, and a Power BI integration — all built from a 2,090-row property dataset within a 3-week scope. The work needed to be honest about the dataset's limitations rather than over-promising on Phase 1 deliverables.
A complete real estate intelligence prototype delivered as a deployed two-service application:
- Data validation and cleaning — surfaced 11 documented data quality findings, severity-ranked, and excluded compromised columns (latitude, longitude, zip_code were randomly generated in the source data) from modelling rather than letting them poison results
- Two-model AVM strategy — Model A uses assessed value when available; Model B excludes it for new builds and unassessed properties; the API auto-selects based on inputs
- Explainability first — SHAP TreeExplainer provides per-prediction feature contributions surfaced directly in the UI
- Comparables search — content-based property similarity for finding the closest matches in the dataset
- Power BI export pipeline — engineered features (assessment flag, median price per sqft by group) for stakeholder dashboarding
- Deployed two-service architecture — FastAPI backend on Render + Streamlit frontend on Streamlit Cloud, $0/month total
| Model | Use case | MAPE (5-fold CV) | R² |
|---|---|---|---|
| A — Assessment-aware | Property has a recent county assessment | 8.5% ± 1.0% | 0.97 ± 0.01 |
| B — Fundamentals-only | New build, unassessed, or assessment unavailable | 44.9% ± 2.8% | 0.55 ± 0.04 |
Model A is high-accuracy because assessed_value correlates 0.99 with last_sale_price in this dataset. Model B is the more honest test of what physical and locational features alone can predict, and the gap between the two is itself a finding — it shows precisely how much of Model A's accuracy is structural rather than learned.
Rather than glossing over dataset weaknesses, I documented them as a first-class output:
| Finding | Decision |
|---|---|
| Latitude/longitude/zip_code randomly generated (3% match) | Excluded from all modelling and analytics |
| Sale dates do not reflect real market trends | Built AVM as cross-sectional, no date features |
| Physical attributes weak (sqft correlation 0.22 vs 0.6–0.8 typical) | Documented; location features dominate |
| Small training set (1,956 rows post-cleaning) | Tail cases flagged with wider uncertainty bands |
These are dataset properties, not modelling errors — and surfacing them clearly gave stakeholders an accurate read on what the Phase 1 prototype could and could not be used for.
flowchart TD
A["🖥️ Streamlit Frontend<br/><i>alloy-avm.streamlit.app</i><br/>Streamlit Cloud · Free tier"]
B["⚡ FastAPI Backend<br/><i>alloytower-avm-api.onrender.com</i><br/>Render · Free tier"]
C["📦 Model Bundles<br/>model_A.pkl · model_B.pkl<br/>Committed to repo"]
A -->|"HTTPS / JSON"| B
B -->|"loads at startup"| C
subgraph Frontend["Frontend"]
A
end
subgraph Backend["Backend"]
B
end
subgraph Storage["Storage"]
C
end
style A fill:#FF4B4B,stroke:#333,stroke-width:1px,color:#fff
style B fill:#009688,stroke:#333,stroke-width:1px,color:#fff
style C fill:#6C757D,stroke:#333,stroke-width:1px,color:#fff
- Two trained AVM variants (model_A.pkl, model_B.pkl) with MLflow tracking
- FastAPI inference service with auto-model-selection logic
- Streamlit frontend with valuation form, comparables, and market explorer
- Content-based property similarity module (
similar_properties.py) - Power BI export pipeline with derived features
- Project Scope & Proposal document
- Data Quality Findings Report (11 ranked findings)
- Power BI dashboard
- Loom reflection video
Python · LightGBM · Scikit-learn · category-encoders · SHAP · MLflow · FastAPI · Pydantic v2 · Uvicorn · Streamlit · Altair · Power BI · Docker · Render · Streamlit Cloud
| Source | Records | Variables | Type |
|---|---|---|---|
| AlloyTower property feed | 2,090 raw → 1,956 clean | 28 columns | Mixed; 3 columns excluded (synthetic) |
📂 View Project → · 🌐 Live App →
Note: When loading the live app, wait 30-50 seconds and reload the app again if you experience a backend port error. This allows the api to spin up since its free tier.
| Category | Technologies |
|---|---|
| Statistical Computing | R (ggplot2, dplyr, forecast, rugarch, car, caret, corrplot) |
| Machine Learning | Python (Scikit-learn, XGBoost, LightGBM, category-encoders, SHAP, MLflow, Pandas, NumPy, Jupyter) |
| API Development | FastAPI, Pydantic, Uvicorn |
| Frontend & Demos | React, Vite, Streamlit, Plotly, JavaScript/JSX |
| Containerisation | Docker, Docker Compose |
| Dashboarding | Power BI (DAX, Power Query, Star Schema) |
| Generative AI / LLMs | Anthropic API (Claude), Pydantic schemas, structured outputs, prompt design |
| Area | Methods |
|---|---|
| Regression | Multiple Linear Regression, Logistic Regression, Gradient Boosting, XGBoost, Forward Stepwise Selection |
| Classification | Decision Trees, Logistic Regression, RandomizedSearchCV, Mutual Information |
| Time Series | Holt-Winters, ARIMA, sGARCH, Decomposition, ADF Testing, Ljung-Box |
| Hypothesis Testing | Kruskal-Wallis, Chi-Square, Shapiro-Wilk, VIF |
| Feature Engineering | RFM Analysis, Log Transformations, Interaction Features, Target/One-Hot/Label Encoding |
| Explainability | SHAP (global & per-prediction), feature importance, dependence plots |
| MLOps | MLflow experiment tracking, model registry, Dockerised deployment |
| Data Preparation | JSON Normalisation, Outlier Analysis, Correlation Matrices |
| Deployment | REST APIs, Model Serialisation (Pickle), Docker Compose, Render, Streamlit Cloud, Cloud Cost Analysis (AWS/Azure/GCP) |
| LLM Engineering | Schema-constrained outputs, retry/backoff, fallback handling, gold-set evaluation, drift awareness |
| Similarity & Search | Content-based recommendation, target encoding, weighted feature similarity |
data-science-projects/
│
├── imf-globaleconomy-analytics/ # Power BI dashboard (IMF 2001–2020)
│ ├── dashboard/ # .pbix file + live URL
│ ├── dax-measures/ # DAX formulas for all visuals
│ ├── source-data/ # IMF Excel datasets
│ └── report-documentation/ # Full project report (PDF)
│
├── strataforge-prediction/ # Concrete strength prediction (R)
│ ├── models/ # Linear + logistic regression scripts
│ ├── hypothesis-tests/ # Kruskal-Wallis, Chi-Square
│ ├── preprocessing/ # Data loading & transformation
│ ├── source-data/ # 1,030 mix trial records
│ ├── strataforge-presentation/ # Interactive React slide deck
│ └── report-documentation/ # Full project report (PDF)
│
├── reder-analytics/ # Telecom churn prediction (Python)
│ ├── preprocessing/ # JSON parsing, normalisation, RFM
│ ├── model/ # Training, tuning, serialisation
│ ├── app.py # FastAPI prediction endpoint
│ ├── reder-presentation/ # Interactive React slide deck
│ └── report-documentation/ # Full project report (PDF)
│
├── usb-stock-timeseries-model/ # Share price forecasting (R)
│ ├── eda/ # Exploration & decomposition
│ ├── models/ # Holt-Winters, ARIMA, GARCH scripts
│ ├── public/model-performance/ # Diagnostic plot screenshots
│ ├── source-data/ # USB.csv (11,835 records)
│ ├── usbforecast-presentation/ # Interactive React slide deck
│ └── report-documentation/ # Full project report (PDF)
│
├── voyage-rails-analytics/ # Rail dynamic pricing (Python + Docker)
│ ├── preprocessing/ # EDA, feature engineering, SHAP analysis
│ │ ├── preprocess.ipynb # Training pipeline notebook
│ │ └── mlruns/ # MLflow experiment tracking
│ ├── source-data/ # Historical booking records
│ ├── app.py # FastAPI inference service
│ ├── streamlit_app.py # Streamlit demo UI
│ ├── Dockerfile.backend # FastAPI container image
│ ├── Dockerfile.streamlit # Streamlit container image
│ ├── docker-compose.yml # Orchestrates both services
│ ├── voyage-presentation/ # Interactive React slide deck
│ └── report-documentation/ # Full project report (PDF)
|
├── alloy-tower-data-platform/ # Property valuation AVM (Python + Docker) [submodule]
│ ├── api/ # FastAPI backend (deployed to Render)
│ ├── app/ # Streamlit frontend (deployed to Streamlit Cloud)
│ ├── pages/ # Streamlit auto-discovered analyst pages
│ ├── model/models/ # Trained models + cleaned dataset
│ ├── source_data/ # Raw property feed (2,090 rows)
│ ├── eda/ # Exploratory analysis notebook
│ ├── train_avm.py # End-to-end training pipeline
│ ├── similar_properties.py # Content-based comparables search
│ ├── export_for_powerbi.py # Power BI export with derived features
│ ├── Dockerfile # Render deployment config
│ └── render.yaml # Render service definition
│
├── llm-feedback-analyzer/ # LLM feedback classifier (Python)
│ ├── classifier.py # Schema + LLM call + retries + fallback
│ ├── demo.py # CLI for single classification
│ ├── evaluate.py # Gold-set evaluation harness
│ ├── data/
│ │ └── gold_set.csv # 15 hand-labelled reviews
│ ├── requirements.txt
│ ├── .env.example
│ └── .gitignore
│
│
└── README.md # ← You are here
Each project follows the same disciplined approach, regardless of domain:
1. Start with the business question. Every project begins with a real problem facing a real organisation — not a dataset looking for a use case. The StrataForge project asks "can we cut cement costs without sacrificing strength?" The Reder project asks "which customers will leave, and can we stop them?" The VoyageRail project asks "can we price each seat based on its actual demand rather than a rule table?" The question shapes every subsequent decision.
2. Let the data dictate the method. The USB project is the clearest example: Holt-Winters and ARIMA both failed because the data exhibited volatility clustering. Rather than forcing a method, the iterative diagnostic process revealed why it failed and what was needed — leading to GARCH as a principled solution, not an arbitrary choice.
3. Validate rigorously. No model is presented without diagnostic evidence. The concrete regression passed all five classical assumptions (linearity, independence, normality, homoscedasticity, no multicollinearity). The GARCH model passed the Ljung-Box test where two predecessors failed. The churn classifier was evaluated on precision, recall, F1, and accuracy. The VoyageRail pricing model is tracked through MLflow and explained via SHAP for every prediction. The AlloyTower AVM was delivered with a documented data quality findings report ranking eleven dataset issues by severity, and a deliberate two-model strategy that exposes how much accuracy comes from assessed_value rather than learned valuation insight. Trust is earned through evidence.
4. Quantify the impact. Results are translated into the language the business cares about. Not just "R² = 81.35%" but "£21,600 saved per project." Not just "97% accuracy" but "5–7x cheaper than acquiring a new customer." Not just "p = 0.2369" but "this is the only model whose residuals are indistinguishable from white noise." Not just "predicted £37.30" but a defensible narrative explaining why that price.
5. Make it accessible. Every project includes an interactive React/Vite presentation that communicates the full analysis to non-technical stakeholders — complete with live calculators, model comparisons, worked cost examples, and (for VoyageRail) a full project budget breakdown.
Open to opportunities in data science, analytics engineering, and quantitative modelling.