Suffolk University • Sawyer Business School

Data Wrangling & EDA for Prediction

Real data arrives in eight tables, three date formats, and a thousand blanks. pandas joins, groupbys, and datetime features on Olist e-commerce — and how to spot the leak that makes a model look brilliant and ship useless

Session 2 of 13 · Mon Sep 21
Module: Data & Features
150 Minutes
Mondays 5:00 PM
Hands-On Learning
01 // LEARNING OBJECTIVES

What You'll Learn Today

By the end of tonight you'll have both the concept and the working code — every objective maps to a cell you run yourself.

01

Assemble a Modeling Table from Many

Olist ships as eight CSVs: orders, items, payments, reviews, customers, sellers, products, geolocation. One row per order with the right columns is the deliverable — and a join that silently triples your rows is the trap.

02

Ask Questions with groupby

Late-delivery rate by state, review score by delivery delay, subscription rate by past outcome. EDA is not making charts — it is interrogating the data before the model does.

03

Name the Missingness

Missing completely at random, missing at random, missing not at random — three patterns in plain English, and why the third one is a feature rather than a problem.

04

Spot Target Leakage Before It Ships

Call duration predicts a bank deposit with AUC 0.91 — and is useless, because you only know it after the call ends. Three real leaks, and the one question that catches all of them.

02 // SESSION AGENDA

Today's Journey

A business question, a dataset, a model — and the judgment to evaluate it.

5:00 PM - 5:20 PM

Python Review (Functions & Loops) & The Data Problem

20 minutes

We open with the two remaining Python for Everybody decks — functions (Ch. 4) and loops (Ch. 5) — because tonight's pandas code is loops and functions in disguise. Then the uncomfortable truth: in Session 1 the data came as one clean table. It never does. Tonight is about the 80% of the job that happens before fit().

  • Lab 0 — the Python Foundations Check — opens tonight: thirteen self-checking exercises on variables, conditionals, functions and loops, due Mon Oct 5, 11:59 PM
  • Kaggle 2022 survey: data scientists still report spending most of their time cleaning and organizing data
  • The modeling table: one row per prediction unit, features known at prediction time, one target column
5:20 PM - 5:40 PM

Meet Olist: Eight Tables, One Question

20 minutes

About 100,000 Brazilian e-commerce orders from 2016–2018 across orders, items, payments, reviews, customers, sellers, products and geolocation. Business question: which orders will arrive late — and will the customer leave a one-star review?

  • Read the schema first: which column links which table, and what is the grain of each
  • Frame it: unit = one order · target = delivered after the estimated date · horizon = at purchase · decision = proactive apology or upgraded shipping
5:40 PM - 6:05 PM

Joins That Lie: merge, validate, count

25 minutes

Orders join to items one-to-many: an order with three items becomes three rows, and your late-delivery rate is now weighted by basket size. The fix is a habit: print the row count before and after every merge, and use validate='one_to_one' to make pandas refuse a bad join.

  • merge(how='left', validate='one_to_one') — the argument nobody uses and everyone should
  • Aggregate the many side first (items → total price, item count), then join at the order grain
6:05 PM - 6:15 PM

Break

10 minutes

Stretch, coffee, compare row counts with a neighbor.

6:15 PM - 6:45 PM

EDA That Asks Questions

30 minutes

groupby, agg, and pivot_table as interview tools. Late-delivery rate by customer state, review score by days late, and datetime features pulled from the purchase timestamp: weekday, hour, month, and days-to-estimate. Every chart ends with a sentence a manager could act on.

  • pd.to_datetime, then .dt.dayofweek, .dt.hour, and date arithmetic — three lines that create ten features
  • Missing values: TotalCharges is blank for exactly the 11 Telco customers with tenure 0 — that is a pattern, not noise
6:45 PM - 7:15 PM

The Anatomy of a Leak

30 minutes

Bank Marketing: predicting whether a customer subscribes to a term deposit. Add call duration and AUC jumps from 0.76 to 0.91 — and the model is worthless, because duration is only known after the call in which the customer said yes. Three real leaks, one test: would you know this value at the moment you have to predict?

  • Leak types: features recorded after the outcome, target proxies (refund amount for 'was refunded'), and test data bleeding into preprocessing
  • The prediction-time test: freeze the clock at the moment of decision and delete every column the clock hasn't reached
7:15 PM - 7:30 PM

Wrap-Up & Homework #1

15 minutes

The wrangling checklist: grain, join, validate, aggregate, datetime, missingness, leakage. Homework #1 applies it end to end on Telco with a baseline model.

  • Next week: turning the modeling table into a leak-proof scikit-learn Pipeline
  • Homework #1 due Mon Oct 5 on Canvas
03 // INTERACTIVE RESOURCES

Tools & Learning Materials

Notebooks, readings, explorers, and the stories behind the methods.

Hands-On

Session 2 Notebook — Data Wrangling & EDA (Olist + Telco)

The notebook we build in class: load Olist via kagglehub (or the no-login Telco fallback), validate every merge, aggregate items to the order grain, pull datetime features, interrogate missingness, and reproduce the call-duration leak on Bank Marketing.

Open in Colab
In Class

Session 2 Class Notebook — What We Typed Together

The live notebook from our meeting, cell for cell: comparisons and if/elif, try/except on a bad int(), a first function, while and for loops, the largest-so-far pattern, then pandas on the Titanic passenger list — head, dtypes, shape, column selection, means, isnull().sum(), and filtering rows. Outputs cleared so you get them by running. Four questions at the end.

Open in Colab
Explorer

The Leakage Time Machine — when did you know that?

Drag the moment a prediction is made and watch which columns exist yet, how good a real model looks, and whether there is still a decision left to make — on a bank's phone campaign (AUC 0.73 → 0.93 once the call length leaks in) and on the hotel bookings from the competition. Tonight's one question, made draggable.

Explore now
Lab 0

Lab 0 — Python Foundations Check (Colab)

Thirteen short exercises on the four Python chapters from Sessions 1–2, each with a check cell that prints ✓ or tells you what is off. Due Mon Oct 5, 11:59 PM; completion credit under Participation & in-class labs.

Explore now
Hands-On

The Churn Detective — a self-checking case file (HW1 companion)

Seven case files on the Telco data, one per part of Homework 1: the tampered TotalCharges column, groupby interrogations, engineered clues, the sign-up-day time machine, a model that beats the baseline, and framing your own prediction problem. Guess before you run, then a check cell prints ✓. 60–90 minutes; ends with a scorecard.

Open in Colab
Review

Python foundations — the four decks, what to take away, worked churn examples

The review page for Python for Everybody chapters 2–5: what we covered, what you should be able to do, one worked example per chapter on the churn problem, and the slides embedded.

Explore now
Data

Olist Brazilian E-Commerce — the dataset

Roughly 100,000 real orders from a Brazilian marketplace, 2016–2018, in eight linked tables with a published schema diagram. Requires a free Kaggle account (two minutes to create with your suffolk.edu email); the notebook has a no-login fallback on Telco if you would rather not.

Open on Kaggle
Reading

pandas User Guide — Merge, Join, Concatenate and Compare

The official reference for merge(), including the validate argument that turns a silent row explosion into a loud error. Read the 'Checking for duplicate keys' section twice.

Read the guide
Reading

Python for Data Analysis, 3rd ed. — Ch. 7 Data Cleaning & Ch. 8 Wrangling

Wes McKinney wrote pandas; this is his free online book. Chapters 7 and 8 are the definitive treatment of missing data, joins, reshaping, and groupby.

Read online (free)
Story

Story: The Pneumonia Model That Learned Asthma Was Safe

Caruana et al. (KDD 2015): a model trained on hospital data concluded asthma patients had a lower risk of dying from pneumonia — because asthma patients were rushed to intensive care. The data was right, the model was right, and deploying it would have killed people. The classic story of a signal that is really a leak.

Read the paper
Reading

scikit-learn — Common Pitfalls: Data Leakage

The library's own page on leakage during preprocessing and how Pipelines prevent it. The bridge from tonight's warning to next week's fix.

Read the page
Go Deeper

Kaggle Learn — Data Leakage (Intermediate ML)

A short, free tutorial with two worked examples of target leakage and train-test contamination. Twenty minutes, and you will never look at a suspiciously high score the same way.

Open the tutorial
Refresher

Refresher: Exploratory Data Analysis with pandas (ISOM 730 notebook)

Prof. Arslan's earlier notebook on matplotlib and seaborn — histograms, pair plots, bar plots. If tonight's charts went fast, this is the slow version. Hosted in the course repo — opens in Colab with one click.

Open in Colab
04 // HANDS-ON ACTIVITIES

Learning by Doing

Open the notebook and build it with us.

Activity 1

From Eight Tables to One Modeling Table

Build the Olist modeling table at the order grain: one row per order, total basket value, item count, customer state, purchase weekday and hour, and the target — delivered after the estimated date. Every merge is validated and counted.

1

Know the grain

orders is one row per order; order_items is one row per item. Print both shapes and the number of unique order_id in each. That difference is the trap.

2

Aggregate the many side first

groupby('order_id') on items to get item_count and basket_value. Now items is one row per order and the join is safe.

3

Merge with validate

orders.merge(items_agg, on='order_id', how='left', validate='one_to_one'). Print the row count before and after. They must match.

4

Build the target and the time features

late = delivered_date > estimated_date. Then weekday, hour, and month from the purchase timestamp. Check the late rate overall and by customer state.

python
import pandas as pd, kagglehub, os

path = kagglehub.dataset_download('olistbr/brazilian-ecommerce')
orders = pd.read_csv(os.path.join(path, 'olist_orders_dataset.csv'), parse_dates=[
    'order_purchase_timestamp', 'order_delivered_customer_date', 'order_estimated_delivery_date'])
items = pd.read_csv(os.path.join(path, 'olist_order_items_dataset.csv'))

print(len(orders), orders.order_id.nunique())   # one row per order
print(len(items),  items.order_id.nunique())    # many rows per order!

items_agg = (items.groupby('order_id')
                  .agg(item_count=('order_item_id', 'size'), basket_value=('price', 'sum'))
                  .reset_index())

df = orders.merge(items_agg, on='order_id', how='left', validate='one_to_one')
assert len(df) == len(orders)

df['late'] = (df.order_delivered_customer_date > df.order_estimated_delivery_date).astype(int)
df['weekday'] = df.order_purchase_timestamp.dt.dayofweek
df['hour'] = df.order_purchase_timestamp.dt.hour
print(df['late'].mean())

Notice which columns you are not allowed to use: the actual delivery date builds the target, so anything derived from it — days late, carrier hand-off date — is a leak. The clock stops at order_purchase_timestamp. Everything the model sees must exist at that moment.

Activity 2

Reproduce the Leak: Bank Marketing and Call Duration

45,211 phone calls from a Portuguese bank's deposit campaign; 11.7% said yes. Fit the same logistic regression twice — once with call duration, once without — and watch a worthless feature produce a spectacular score.

1

Fit without duration

Age, balance, job, prior-campaign outcome and the rest. Cross-validated AUC lands around 0.76.

2

Fit with duration

Add the length of the call. AUC jumps to about 0.91. A manager would be thrilled.

3

Apply the prediction-time test

When do we need the prediction? Before dialing, to choose whom to call. Is call duration known then? No. Delete it, and explain why the 0.91 was a lie.

The UCI documentation for this dataset says it outright: duration 'highly affects the output target' and 'should be discarded if the intention is to have a realistic predictive model.' Most datasets don't come with that warning. Your job is to write it yourself.

05 // KEY TAKEAWAYS

What to Remember

The deliverable of wrangling is a modeling table: one row per prediction unit, features known at prediction time, one target column. Get the grain right and everything downstream gets easier.

Joins lie silently. Aggregate the many side first, merge with validate=, and print the row count before and after. A model trained on tripled rows is a model trained on a different population.

Leakage is the #1 way smart people build useless models. Freeze the clock at the moment of decision and delete every column the clock hasn't reached — call duration, refund amount, days late.

06 // HOMEWORK

Before Next Session

Complete these before next Monday — they are the practice that makes the next session land.

Homework #1 Due Mon Oct 5, 11:59 PM, on Canvas. Using the Telco churn data from the Session 1 notebook: (1) fix the TotalCharges column (it is stored as text and is blank for the 11 customers with tenure 0 — decide what to do and justify it in one sentence); (2) produce three groupby tables that each answer a business question about who churns (contract, internet service, payment method are good starts), with one sentence of interpretation each; (3) engineer at least two new features (for example number of add-on services, or average monthly charge over tenure) and explain why they might carry signal; (4) list every column you would have to drop if the prediction were made at sign-up rather than today, and why; (5) refit the Session 1 logistic regression with your new features and report accuracy, the confusion matrix, and the baseline; (6) in a final markdown cell, frame one prediction problem from your own work or life — the unit, the target, the horizon, and the decision it would change; it may become your final project. Submit the notebook with outputs visible.
Lab 0 — Python Foundations Check Due Mon Oct 5, 11:59 PM, on Canvas — completion credit under Participation & in-class labs. Thirteen self-checking exercises on variables, conditionals, functions and loops, all on the churn problem; every check cell must print ✓ before you upload. Open it in Colab from the resources above. If it takes more than 90 minutes, do Lessons 0 and 1 of Python, Guided first and come to office hours.
07 // PRACTICE

Test Yourself

Exam-style questions for the ideas in this session — Data & Leak-Proof Features. Every wrong answer names the misconception behind it.

Q07 The feature that makes your model brilliant offline and useless live. Q08 One-hot, ordinal, or target encoding for 3,000 zip codes? Q09 A column with too many categories — keep it, drop it, or encode it smarter? Q10 Six candidate features for a readmission model. One of them is from the future. Q11 Two students, same scaler, different order of operations. One of them leaked. Q12 The blanks default at 31%. What do you do with a missing value that talks?

All of Features →

Coming Next

Session 3: Feature Engineering & Leak-Proof Pipelines

Encoding, scaling, imputation and feature engineering as one scikit-learn Pipeline that fits on training data only — so leakage becomes structurally impossible. Plus skrub's TableVectorizer, the 2025 shortcut.

PipelinesEncodingFeature Engineering