Skip to content

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]
""")

← Bivariate Analysis · Next: Interview Questions →