Lesson 29 · Data analytics zero to hero
Business Operations Analytics Deep Dive | Data Analytics #29
Video twenty-nine of the 30-part series, the second capstone-style project: a genuine logistics and customer-satisfaction analysis. The business question:…
- CourseData analytics zero to hero
- Lesson29 of 30
- Video12 min
- FormatJupyter notebook · 9 code cells
- Data4 datasets
What you'll learn
Datasets used in this lesson
Save these next to the notebook. In Google Colab, upload them with the 📁 icon on the left first.
- orders_sample.csv205.5 KB
- customers_sample.csv101.0 KB
- payments_sample.csv66.1 KB
- reviews_sample.csv164.1 KB
📓 Full notebook
Download .ipynbData Analytics Zero to Hero, Video 29: Real-World Project, Business Operations Deep Dive#
- Video twenty-nine of the 30-part series, the second capstone-style project: a genuine logistics and customer-satisfaction analysis.
- The business question: is late delivery actually hurting real customer satisfaction, and where should operations focus first?
- Back to the real, multi-table Olist Brazilian E-Commerce dataset, this time including real delivery dates and real customer reviews.
- Let's jump straight in.
Before You Start#
- Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
- Place orders_sample.csv, customers_sample.csv, order_items_sample.csv, payments_sample.csv, and reviews_sample.csv in the same folder as this notebook.
Part 1: Load and Merge#
import pandas as pd
orders = pd.read_csv('orders_sample.csv', parse_dates=['order_purchase_timestamp', 'order_delivered_customer_date', 'order_estimated_delivery_date'])
customers = pd.read_csv('customers_sample.csv')
payments = pd.read_csv('payments_sample.csv')
reviews = pd.read_csv('reviews_sample.csv')
print(orders.shape, customers.shape, payments.shape, reviews.shape)
delivered = orders[orders['order_status'] == 'delivered'].copy()
df = (delivered
.merge(customers, on='customer_id', how='left')
.merge(reviews[['order_id', 'review_score']], on='order_id', how='left')
.merge(payments.groupby('order_id')['payment_value'].sum().reset_index(), on='order_id', how='left'))
print(df.shape)
print(df[['order_id', 'customer_state', 'review_score', 'payment_value']].head(3))
Part 2: Delivery Performance#
df['delay_days'] = (df['order_delivered_customer_date'] - df['order_estimated_delivery_date']).dt.days
late_share = (df['delay_days'] > 0).mean()
print(f'{late_share:.1%} of real delivered orders arrived after the estimate')
print(df['delay_days'].describe())
Part 3: Does Delay Hurt Satisfaction?#
delay_bins = pd.cut(df['delay_days'], bins=[-100, -1, 0, 7, 100], labels=['Early', 'On Time', '1-7 Days Late', '8+ Days Late'])
review_by_delay = df.groupby(delay_bins, observed=True)['review_score'].agg(['mean', 'count']).round(2)
print(review_by_delay)
from scipy import stats
clean = df.dropna(subset=['delay_days', 'review_score'])
corr, p_val = stats.pearsonr(clean['delay_days'], clean['review_score'])
print(f'Delay-Satisfaction correlation: {corr:.3f} (p={p_val:.6f})')
Part 4: Payment Behavior#
payment_types = pd.read_csv('payments_sample.csv')
print(payment_types['payment_type'].value_counts())
print(payment_types.groupby('payment_type')['payment_installments'].mean().round(1))
Part 5: Regional Delivery Performance#
state_delay = df.groupby('customer_state')['delay_days'].agg(['mean', 'count'])
state_delay = state_delay[state_delay['count'] >= 10].sort_values('mean', ascending=False)
print(state_delay.round(1).head(6))
A Quick Sanity Check on 'Worst'#
print((state_delay['mean'] < 0).all())
print(f"Overall mean delay: {df['delay_days'].mean():.1f} days")
Part 6: Executive Summary#
worst_state = state_delay.index[0]
worst_state_delay = state_delay['mean'].iloc[0]
on_time_score = review_by_delay.loc['On Time', 'mean']
late_score = review_by_delay.loc['8+ Days Late', 'mean']
summary = f'''
BUSINESS OPERATIONS SUMMARY
{late_share:.1%} of real delivered orders arrived after the estimated date.
Average review score falls from {on_time_score:.2f} on-time to {late_score:.2f} when delivery runs 8+ days late.
{worst_state} has the smallest average delivery buffer among states with sufficient real order volume, at {worst_state_delay:.1f} days before the estimate, the state genuinely closest to tipping into late.
Recommendation: monitor {worst_state} carrier performance closely as volume grows, since it has the least real margin for error before deliveries start arriving late.
'''
print(summary)
Wrap-Up: What You Learned#
- Merging four real tables into one analysis-ready view, filtered to a specific real business scope.
- Measuring real delivery performance directly from date arithmetic.
- Testing whether delay genuinely relates to satisfaction, with both binned averages and a formal correlation.
- Reading real payment behavior as an operational signal, not just a sales metric.
- Finding where a real problem concentrates geographically, not just its overall average.
- A real, generated executive summary tying every finding to one concrete recommendation.
- Video thirty is the final capstone: one complete, end-to-end real project bringing together the entire series. Subscribe so it lands automatically see you there.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



