EDA Case Study — E-commerce Sales Analysis¶
End-to-end exploratory analysis of an e-commerce sales dataset. This mirrors a real analyst task: you've been given cleaned data and asked to understand it before building a dashboard.
Business Context¶
You're a data analyst at an e-commerce company. The product team wants to understand sales performance for Q1 2024. Before they build a strategy, they need to understand: who's buying, what they're buying, when, and how the patterns vary across segments.
Step 1 — Load and Quick Audit¶
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv("orders_q1_2024.csv", parse_dates=["order_date"])
df["total"] = df["quantity"] * df["unit_price"]
print(f"Shape: {df.shape}")
print(f"\nDate range: {df['order_date'].min().date()} to {df['order_date'].max().date()}")
print(f"\nStatus counts:\n{df['status'].value_counts()}")
print(f"\nMissing:\n{df.isnull().sum()[df.isnull().sum() > 0]}")
Step 2 — Revenue Overview¶
completed = df[df["status"] == "completed"]
# Key metrics
print(f"Total revenue: £{completed['total'].sum():,.2f}")
print(f"Orders: {len(completed):,}")
print(f"Unique customers: {completed['customer_id'].nunique():,}")
print(f"Avg order value: £{completed['total'].mean():,.2f}")
print(f"Median order: £{completed['total'].median():,.2f}")
print(f"Completion rate: {len(completed)/len(df):.1%}")
Step 3 — Distribution Analysis¶
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Distribution of order total
axes[0,0].hist(completed["total"], bins=40, edgecolor="white", color="#0D9488")
axes[0,0].axvline(completed["total"].mean(), color="red", linestyle="--", label="Mean")
axes[0,0].axvline(completed["total"].median(), color="orange", linestyle="--", label="Median")
axes[0,0].set_title("Order Total Distribution")
axes[0,0].legend()
# Status breakdown
status_counts = df["status"].value_counts()
axes[0,1].pie(status_counts.values, labels=status_counts.index, autopct="%1.1f%%",
colors=["#0D9488", "#F59E0B", "#EF4444", "#94A3B8"])
axes[0,1].set_title("Orders by Status")
# Revenue by category
cat_rev = completed.groupby("category")["total"].sum().sort_values(ascending=False)
axes[1,0].barh(cat_rev.index[::-1], cat_rev.values[::-1] / 1000, color="#0D9488")
axes[1,0].set_title("Revenue by Category (£k)")
axes[1,0].set_xlabel("Revenue (£k)")
# Quantity distribution
axes[1,1].hist(completed["quantity"], bins=20, edgecolor="white", color="#F59E0B")
axes[1,1].set_title("Quantity Distribution")
axes[1,1].set_xlabel("Items per Order")
plt.suptitle("Q1 2024 Sales — Distribution Analysis", fontsize=14, y=1.02)
plt.tight_layout()
plt.show()
Step 4 — Time Trend¶
# Daily revenue
daily = completed.groupby("order_date")["total"].sum()
ma7 = daily.rolling(7).mean()
fig, ax = plt.subplots(figsize=(14, 5))
ax.plot(daily.index, daily.values, alpha=0.3, color="#94A3B8", linewidth=1)
ax.plot(ma7.index, ma7.values, color="#0D9488", linewidth=2, label="7-day MA")
ax.set_title("Daily Revenue — Q1 2024")
ax.set_ylabel("Revenue (£)")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Day of week pattern
completed["dow"] = completed["order_date"].dt.day_name()
day_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
dow = completed.groupby("dow")["total"].mean().reindex(day_order)
plt.figure(figsize=(10, 4))
dow.plot(kind="bar", color="#0D9488", alpha=0.8)
plt.axhline(dow.mean(), color="red", linestyle="--", alpha=0.7, label="Weekly avg")
plt.title("Average Order Value by Day of Week")
plt.ylabel("Avg Order Value (£)")
plt.xticks(rotation=45, ha="right")
plt.legend()
plt.tight_layout()
plt.show()
Step 5 — Segment and Category Analysis¶
# Revenue × category heatmap by month
completed["month"] = completed["order_date"].dt.month_name()
month_order = ["January", "February", "March"]
pivot = pd.crosstab(
completed["month"],
completed["category"],
values=completed["total"],
aggfunc="sum",
).reindex(month_order).fillna(0)
plt.figure(figsize=(12, 4))
sns.heatmap(pivot / 1000, annot=True, fmt=".1f", cmap="YlGn",
cbar_kws={"label": "Revenue (£k)"})
plt.title("Revenue by Month and Category (£k)")
plt.tight_layout()
plt.show()
Step 6 — Correlation¶
# Correlation matrix
numeric = completed[["quantity", "unit_price", "total"]].corr()
plt.figure(figsize=(6, 5))
sns.heatmap(numeric, annot=True, fmt=".2f", cmap="RdYlGn", center=0, vmin=-1, vmax=1)
plt.title("Correlation Matrix")
plt.tight_layout()
plt.show()
print(f"\nKey finding: unit_price vs total correlation = {numeric.loc['unit_price','total']:.2f}")
Step 7 — Key Findings (Template for Stakeholder Presentation)¶
print("""
Q1 2024 EDA SUMMARY
===================
1. REVENUE
- Total Q1 revenue: £[X]
- Month with highest revenue: [Month] (£[X])
- Average order value: £[X] (median £[X] — right-skewed distribution)
2. PRODUCTS
- Top category by revenue: [Category] (£[X], [X]% of total)
- Lowest cancellation rate: [Category] ([X]%)
- Highest average order value: [Category]
3. TIMING
- Strongest day: [Day] (average £[X] per order)
- Weakest day: [Day]
- Week-over-week trend: [growing / flat / declining]
4. NOTABLE FINDINGS
- [Finding 1]
- [Finding 2]
- [Finding 3]
5. RECOMMENDATIONS
- [Action 1 based on finding]
- [Action 2 based on finding]
""")