Projects

Project · Academic

Ink-on-bone engraving: a refinement pipeline read left to right — a scatter of raw data-marks funneled through a sieve into a tidy star-schema lattice, which splits into an indigo thread rising to a bar-chart dashboard and another passing through a decision tree, the two converging on a balance scale that tips — raw data distilled, modeled, and resolved into one prediction.

Predicting Negative Reviews in the Olist Ecosystem

An integrated approach to Business Intelligence and Machine Learning. This was my capstone for the Curso Superior de Tecnologia em Banco de Dados at PUCRS, for the discipline Projeto em Business Intelligence e Analytics: an end-to-end pipeline that takes the public Olist e-commerce dataset and predicts which orders are headed for a negative review — and, more importantly, says why.

The question

Brazilian e-commerce moved R$204.3 billion in 2024. In marketplaces like Olist, customer dissatisfaction is a direct risk to lifetime value — through negative reviews and tacit churn — so anticipating at-risk orders becomes a measurable operational advantage. The research question:

Which operational, logistical, and financial factors carry the most statistical weight in determining a negative review in Brazilian e-commerce?

What I built

A six-layer, cloud-native, zero-cost pipeline following CRISP-DM. Raw Olist CSVs are cleaned and feature-engineered in a Python/Pandas ETL, loaded into a SQLite star-schema warehouse (one fact table, five dimensions), then split into two branches: a BI branch that renders four interactive Plotly dashboards, and an ML branch that trains and evaluates the predictive models — all reproducible from the same warehouse.

Six-layer architecture diagram: Data sources (Olist Kaggle dataset) → ETL processing (Python/Pandas) → Load & SQL (SQLite star-schema data warehouse) → Machine Learning (classification modeling) → Visualization & BI (performance dashboards) → Decision-making (risk/retention prediction).

Three hypotheses, tested against the data

  • H1 — Delivery delay (confirmed, by a wide margin). Late orders carry a 73.3% negative-review rate versus 17.3% for on-time ones. The three delay-related features alone account for ~52% of the Random Forest's importance.
  • H2 — Freight on low-ticket items (confirmed). It isn't absolute freight but freight relative to price: the freight/price ratio averages 0.57 on low-ticket orders versus 0.11 on high-ticket ones. The second driver of dissatisfaction.
  • H3 — Multi-vendor orders (partially confirmed). Multi-seller orders show a 3× higher negative rate (60.5% vs 20.5%), but the proposed causal channel was refuted by the data: multi-vendor orders actually consolidate faster (1.38 vs 2.30 days). Strong association, real mechanism still open — and I reported it as such.

The model

Binary classification on review_score <= 3, with Logistic Regression as a baseline and Random Forest as the main model (80/20 stratified split, balanced class weights). The trade-off is honest: Random Forest wins on accuracy and precision, Logistic Regression on recall.

MetricLogistic Reg.Random Forest
Accuracy0.7550.789
Precision0.4260.498
Recall0.4700.398
F1-score0.4470.442
ROC-AUC0.6980.702

Run as live inference, the model separates profiles cleanly: a low-risk order (early delivery, high ticket, light freight, single-vendor) scores a 37.3% chance of a negative review; a high-risk one (12 days late, low ticket, heavy freight, multi-vendor) scores 74.7% — a 37.5-point spread that makes real-time preventive churn monitoring viable.

Interactive dashboards

Four standalone Plotly dashboards, hosted on GitHub Pages — open them straight in the browser:

Project documents

What it demonstrates

End-to-end data work across the stack: ETL and feature engineering, dimensional modeling (star schema in a SQLite warehouse), BI and data visualization (Plotly), supervised machine learning (model selection, evaluation, feature-importance reading), statistical hypothesis testing, and the business framing that ties it together — KPIs, churn, and lifetime value — all under a CRISP-DM process and reported with its limitations stated plainly.

Built with

PythonPandasSQLitePlotlyscikit-learnJupyter