Data Science: End-to-End Project, Raw Data to Insights

Build a complete Python data science project from start to finish. You load a real dataset, clean it, run exploratory analysis, engineer features, create visualizations, and present actionable business insights.

“The goal is to turn data into information, and information into insight.”

Carly Fiorina

Last Updated: July 2026 | Tested on: Python 3.14.6, Pandas 2.3.3, NumPy 2.4.6, Seaborn 0.13.2, Matplotlib 3.11.0 | Difficulty: Advanced | Reading Time: 23 minutes

This is where everything in Part 4 comes together. You have learned NumPy, Pandas, statistics, and visualization as separate skills. Now you combine them into one workflow, the way a data scientist actually works on the job. We take a raw dataset, clean it, explore it, find patterns, and turn those patterns into insights that could drive a business decision.

Think of it like cooking a full meal for the first time. Up to now you have practised each skill on its own: chopping, boiling, seasoning. This is the moment you walk into the kitchen with raw ingredients and walk out with a plate someone would actually pay for. The raw dataset is your groceries. The cleaning is your prep. The charts and findings are the finished dish you put in front of the stakeholder.

This project is the milestone for Part 4. If you can follow this workflow on your own with a fresh dataset, you have the foundation you need for machine learning in Part 5.

What We Are Building

Iterate1. Define Problemand Collect Data2. Exploratory DataAnalysis (EDA)3. Data Cleaningand Preprocessing4. Feature Engineeringand Selection5. Model Buildingand Training6. Evaluationand Tuning7. Presentationand StorytellingPython Data Science Project: 7 Stages from Problem to Presentation

The diagram above is the full data science lifecycle. This post lives in the top half of it: define the question, explore, clean, then analyze, visualize, and tell the story. The modeling boxes (feature engineering, model building, evaluation) are where Part 5 picks up, so do not worry about them yet. For now you are doing the work that makes any model possible later.

We will analyze a simulated e-commerce dataset with 2,000 orders. The goal is simple to say and harder to do: understand customer behavior, spot patterns in what people buy, and recommend actions a business could take. The final deliverable is a dashboard of charts plus a short findings report, the kind of thing you would show in a job interview or a stakeholder meeting. Every number in this post came from running the code on Python 3.14.6 (latest stable at the time of writing), so the figures here are real output, not made-up examples.

Prerequisites

Finish the Exploratory Data Analysis (EDA) workflow tutorial first. This project leans on all of Part 4: NumPy, Pandas (groupby, fillna, value_counts), and plotting with Matplotlib and Seaborn. You also need the libraries installed. One line does it: pip install pandas numpy matplotlib seaborn. The post was tested on Python 3.14.6 with Pandas 2.3.3, NumPy 2.4.6, Matplotlib 3.11.0, and Seaborn 0.13.2.

Step 1: Generate & Load Dataset

In a real job you would load a CSV (comma-separated values) file with pd.read_csv("orders.csv"). Here we generate the data instead, so you can run this post end to end with no download. We seed the random generator with 42, which means everyone who runs this gets the exact same 2,000 orders and the exact same numbers you see below. A seed is like a recipe: follow the same one and you get the same dish every time. We also sprinkle in a few missing values on purpose, because real data is never tidy and you need practice cleaning it.

📄 project_step1.py: create a realistic e-commerce dataset

import pandas as pd
import numpy as np

rng = np.random.default_rng(42)
n = 2000

# Generate realistic e-commerce data
dates = pd.date_range("2025-01-01", periods=365, freq="D")
order_dates = rng.choice(dates, n)

df = pd.DataFrame({
    "order_id": range(1, n + 1),
    "customer_id": rng.integers(1, 400, n),
    "order_date": order_dates,
    "category": rng.choice(["Electronics", "Clothing", "Books", "Home", "Sports"], n,
                            p=[0.25, 0.3, 0.15, 0.2, 0.1]),
    "quantity": rng.integers(1, 6, n),
    "unit_price": rng.choice([29.99, 49.99, 79.99, 129.99, 199.99, 499.99], n),
    "payment_method": rng.choice(["Credit Card", "UPI", "Debit Card", "Cash on Delivery"], n,
                                   p=[0.35, 0.30, 0.20, 0.15]),
    "region": rng.choice(["North", "South", "East", "West"], n, p=[0.3, 0.25, 0.2, 0.25])
})

df["total_amount"] = df["quantity"] * df["unit_price"]
df["month"] = df["order_date"].dt.month
df["day_of_week"] = df["order_date"].dt.day_name()

# Inject missing values (realistic)
df.loc[rng.choice(n, 30, replace=False), "payment_method"] = np.nan
df.loc[rng.choice(n, 15, replace=False), "region"] = np.nan

print(f"Dataset: {df.shape[0]} orders, {df.shape[1]} columns")
print(f"\n{df.head()}")
print(f"\nMissing:\n{df.isnull().sum()}")
print(f"\nDate range: {df['order_date'].min()} to {df['order_date'].max()}")
print(f"Revenue: ${df['total_amount'].sum():,.0f}")

▶ Output

Dataset: 2000 orders, 11 columns

   order_id  customer_id order_date  ... total_amount  month  day_of_week
0         1          351 2025-02-02  ...       319.96      2       Sunday
1         2           25 2025-10-10  ...       199.96     10       Friday
2         3          159 2025-08-27  ...       259.98      8    Wednesday
3         4          183 2025-06-10  ...       259.98      6      Tuesday
4         5          319 2025-06-08  ...       249.95      6       Sunday

[5 rows x 11 columns]

Missing:
order_id           0
customer_id        0
order_date         0
category           0
quantity           0
unit_price         0
payment_method    30
region            15
total_amount       0
month              0
day_of_week        0
dtype: int64

Date range: 2025-01-01 00:00:00 to 2025-12-31 00:00:00
Revenue: $986,510

What happened here: We built a 2,000 row table with 11 columns and a year of orders. Pandas shows ... in the middle of df.head() because the table is wider than the terminal, so it hides the middle columns to fit. The isnull().sum() line is the one to watch: 30 orders are missing a payment method and 15 are missing a region. That is the mess we clean up next. Total revenue across the whole year is about $986,510. Your run will print these exact numbers too, because of the seed.

Step 2: Clean & Prepare

Cleaning is where most of a data scientist’s time actually goes, and it is the least glamorous part. The techniques come straight from the Pandas data cleaning tutorial, applied to a real mess this time. Two columns have holes in them. For payment method we fill the gaps with the most common value (the mode), because a missing payment is most likely just an unrecorded version of the usual choice.

For region we cannot guess, so we label the blanks "Unknown" and keep them honest. Think of it like a sign-in sheet at a clinic: if someone skipped the phone number, you write down the front-desk default, but if they skipped their city, you just mark it unknown rather than inventing one.

📄 project_step2.py: data cleaning pipeline

# Fill missing payment methods with the most common value (the mode)
df["payment_method"] = df["payment_method"].fillna(df["payment_method"].mode()[0])

# Fill missing regions with "Unknown" (we cannot guess a real region)
df["region"] = df["region"].fillna("Unknown")

# Verify there are no holes left
assert df.isnull().sum().sum() == 0, "Still has missing values!"
print(f"Clean dataset: {df.shape[0]} rows, 0 missing values")

# Add two derived features for later analysis
df["is_high_value"] = df["total_amount"] > df["total_amount"].quantile(0.75)
df["month_name"] = df["order_date"].dt.strftime("%b")

print(f"\nHigh-value orders: {df['is_high_value'].sum()} ({df['is_high_value'].mean():.0%})")

▶ Output

Clean dataset: 2000 rows, 0 missing values

High-value orders: 476 (24%)

What happened here: After filling both columns, df.isnull().sum().sum() is 0, so the assert passes silently and we know the table is clean. Then we add two new columns. is_high_value is True for any order in the top 25% by amount (above the 75th percentile, what statisticians call the third quartile), and it works out to 476 orders, roughly 24%. month_name turns the order date into a short label like Jan or Feb so charts read nicely. Building columns like these is called feature engineering, and it is what turns raw data into something you can actually analyze.

Step 3: Analyze the Data

Now the fun part. We ask the data five plain questions and let groupby answer them: which category brings in the most money, how revenue moves month to month, who the top customers are, how people pay, and what the average order is worth in each region. The groupby method is the workhorse of analysis: it splits the rows into buckets, applies a calculation to each bucket, and stitches the answers back together. It is the same idea as sorting your monthly bank statement by category to see where your money actually went.

📄 project_step3.py: five quick analyses

# 1. Revenue by category
category_revenue = df.groupby("category")["total_amount"].sum().sort_values()
print(f"Revenue by Category:\n{category_revenue}\n")

# 2. Monthly trend
monthly = df.groupby("month")["total_amount"].sum()
print(f"Monthly Revenue Trend:\n{monthly}\n")

# 3. Top customers
top_customers = (df.groupby("customer_id")
                 .agg(orders=("order_id", "count"),
                      total_spent=("total_amount", "sum"))
                 .sort_values("total_spent", ascending=False)
                 .head(10))
print(f"Top 10 Customers:\n{top_customers}\n")

# 4. Payment method breakdown
print(f"Payment Methods:\n{df['payment_method'].value_counts()}\n")

# 5. Average order value by region
aov = df.groupby("region")["total_amount"].mean().round(2)
print(f"Average Order Value by Region:\n{aov}")

▶ Output

Revenue by Category:
category
Sports         121373.64
Books          138500.80
Home           172809.19
Electronics    261024.39
Clothing       292801.76
Name: total_amount, dtype: float64

Monthly Revenue Trend:
month
1     81924.72
2     91314.43
3     86915.06
4     88044.89
5     74065.65
6     81654.57
7     82204.98
8     83305.05
9     66745.37
10    81715.23
11    75535.13
12    93084.70
Name: total_amount, dtype: float64

Top 10 Customers:
             orders  total_spent
customer_id
42               11      8909.65
298               9      8799.66
318              10      8179.70
116              12      7449.60
374              13      7319.63
56                8      7229.68
305               7      6929.72
242               5      6919.80
299               8      6919.75
100               7      6899.80

Payment Methods:
payment_method
Credit Card         705
UPI                 607
Debit Card          392
Cash on Delivery    296
Name: count, dtype: int64

Average Order Value by Region:
region
East       454.16
North      521.40
South      518.49
Unknown    785.96
West       460.03
Name: total_amount, dtype: float64

What happened here: Five questions, five answers, no manual counting. Clothing is the top category by revenue at about $292,802, with Electronics close behind. Revenue is fairly steady month to month, with December the strongest and September the weakest. The top customer (id 42) spent about $8,910 across 11 orders. Credit Card is the most used payment method, then UPI (Unified Payments Interface). One number jumps out: the Unknown region has a much higher average order value ($785.96) than the real regions, but that is just an artifact of the 15 rows we labeled Unknown, so we set it aside when comparing actual regions. Among the real ones, North and South lead and East trails.

Step 4: Create Dashboard

A dashboard works like the dashboard in your car: you do not stop to read the engine manual, you glance at a few dials and instantly know whether anything needs attention. Tables are great for you. Charts are great for everyone else. A stakeholder will not scroll through a column of numbers, but they will glance at four panels and get it in five seconds. Here we build a 2 by 2 dashboard using the Matplotlib subplots technique: revenue by category, the monthly trend, orders by day of week, and a payment-method pie. One image, four stories.

📄 project_step4.py: multi-panel dashboard

import seaborn as sns
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2, figsize=(16, 12))

# Top left: revenue by category (horizontal bars)
cat_rev = df.groupby("category")["total_amount"].sum().sort_values()
axes[0, 0].barh(cat_rev.index, cat_rev.values, color=["#50fa7b", "#8be9fd", "#ffb86c", "#ff79c6", "#bd93f9"])
axes[0, 0].set_title("Revenue by Category", fontsize=14)
axes[0, 0].set_xlabel("Total Revenue ($)")

# Top right: monthly revenue trend (line)
monthly = df.groupby("month")["total_amount"].sum()
axes[0, 1].plot(monthly.index, monthly.values, marker="o", color="#bd93f9", linewidth=2)
axes[0, 1].set_title("Monthly Revenue Trend", fontsize=14)
axes[0, 1].set_xlabel("Month")
axes[0, 1].set_ylabel("Revenue ($)")
axes[0, 1].grid(True, alpha=0.3)

# Bottom left: orders by day of week (count bars)
# Assign 'hue' and set legend=False: the modern Seaborn 0.13+ way to color bars
day_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
sns.countplot(data=df, x="day_of_week", ax=axes[1, 0], order=day_order,
              hue="day_of_week", palette="viridis", legend=False)
axes[1, 0].set_title("Orders by Day of Week", fontsize=14)
axes[1, 0].tick_params(axis="x", rotation=45)

# Bottom right: payment method pie chart
payment_counts = df["payment_method"].value_counts()
axes[1, 1].pie(payment_counts, labels=payment_counts.index, autopct="%1.1f%%",
               colors=["#50fa7b", "#8be9fd", "#ffb86c", "#ff79c6"])
axes[1, 1].set_title("Payment Method Distribution", fontsize=14)

fig.suptitle("E-Commerce Analytics Dashboard (2025)", fontsize=18, fontweight="bold", y=1.02)
plt.tight_layout()
plt.savefig("ecommerce_dashboard.png", dpi=150, bbox_inches="tight")
plt.show()
print("Dashboard saved!")

▶ Output

Dashboard saved!

What happened here: plt.subplots(2, 2) gives you a grid of four plots in one figure, and axes[row, col] picks which cell to draw in. Each panel uses the chart type that fits its data: bars for categories, a line for the trend over time, count bars for days, and a pie for the payment split. The script prints Dashboard saved! and writes ecommerce_dashboard.png to your folder. One small but important detail: in Seaborn 0.13 and later, passing palette without a hue is deprecated, so we set hue="day_of_week" and legend=False to color the bars the modern way and avoid a warning.

Step 5: Key Findings & Recommendations

A chart is not the finish line. The job is to turn what you see into a sentence a manager can act on. It is like a doctor reading your blood test: you do not want the raw printout, you want a plain line like “your iron is low, add more spinach and dal.” So we end by computing each headline number straight from the cleaned data, then pairing it with a recommendation. Notice that nothing is hardcoded here: every figure in the report comes from df, so if the data changes, the findings change with it. That is the honest way to write a findings report. You never type a number you did not calculate.

📄 project_findings.py: build the report from the data, not by hand

# Compute every headline number directly from the cleaned data
cat_rev = df.groupby("category")["total_amount"].sum().sort_values(ascending=False)
cat_aov = df.groupby("category")["total_amount"].mean().sort_values(ascending=False)
top_cat = cat_rev.index[0]
top_aov_cat = cat_aov.index[0]

monthly = df.groupby("month")["total_amount"].sum()
month_label = {1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun",
               7: "Jul", 8: "Aug", 9: "Sep", 10: "Oct", 11: "Nov", 12: "Dec"}
peak_month = month_label[monthly.idxmax()]
slow_month = month_label[monthly.idxmin()]

cust = df.groupby("customer_id")["total_amount"].sum().sort_values(ascending=False)
n_cust = len(cust)
top10_n = int(np.ceil(n_cust * 0.10))
top10_share = cust.head(top10_n).sum() / cust.sum()
avg_orders = df.shape[0] / n_cust

pay_share = (df["payment_method"].value_counts(normalize=True) * 100).round(1)

# Average order value by REAL region only (the "Unknown" bucket is an artifact)
aov_region = (df[df["region"] != "Unknown"]
              .groupby("region")["total_amount"].mean()
              .sort_values(ascending=False))
best_region, worst_region = aov_region.index[0], aov_region.index[-1]

findings = f"""
E-COMMERCE ANALYSIS: KEY FINDINGS AND RECOMMENDATIONS
=====================================================

1. REVENUE DRIVERS
   - {top_cat} is the top category by revenue (${cat_rev.iloc[0]:,.0f})
   - {top_aov_cat} has the highest average order value (${cat_aov.iloc[0]:,.2f} per order)
   Recommendation: push {top_aov_cat} bundles to lift the overall average order value

2. SEASONAL PATTERNS
   - Revenue peaks in {peak_month} (${monthly.max():,.0f})
   - The slowest month is {slow_month} (${monthly.min():,.0f})
   Recommendation: plan inventory and ads around the {peak_month} peak

3. CUSTOMER CONCENTRATION
   - Top 10% of customers ({top10_n} of {n_cust}) drive {top10_share:.0%} of revenue
   - The average customer places {avg_orders:.1f} orders per year
   Recommendation: a loyalty program for the top spenders, win-back offers for the rest

4. PAYMENT TRENDS
   - UPI is at {pay_share['UPI']:.0f}% of orders
   - Cash on Delivery is still at {pay_share['Cash on Delivery']:.0f}% (higher return risk)
   Recommendation: nudge shoppers toward digital payments to cut COD returns

5. REGIONAL INSIGHTS
   - {best_region} has the highest average order value (${aov_region.iloc[0]:,.2f})
   - {worst_region} is the lowest (${aov_region.iloc[-1]:,.2f})
   Recommendation: dig into {worst_region}: is it a marketing gap or a logistics issue?
"""
print(findings)

▶ Output

E-COMMERCE ANALYSIS: KEY FINDINGS AND RECOMMENDATIONS
=====================================================

1. REVENUE DRIVERS
   - Clothing is the top category by revenue ($292,802)
   - Sports has the highest average order value ($606.87 per order)
   Recommendation: push Sports bundles to lift the overall average order value

2. SEASONAL PATTERNS
   - Revenue peaks in Dec ($93,085)
   - The slowest month is Sep ($66,745)
   Recommendation: plan inventory and ads around the Dec peak

3. CUSTOMER CONCENTRATION
   - Top 10% of customers (40 of 398) drive 25% of revenue
   - The average customer places 5.0 orders per year
   Recommendation: a loyalty program for the top spenders, win-back offers for the rest

4. PAYMENT TRENDS
   - UPI is at 30% of orders
   - Cash on Delivery is still at 15% (higher return risk)
   Recommendation: nudge shoppers toward digital payments to cut COD returns

5. REGIONAL INSIGHTS
   - North has the highest average order value ($521.40)
   - East is the lowest ($454.16)
   Recommendation: dig into East: is it a marketing gap or a logistics issue?

What happened here: The report writes itself from the numbers. Clothing earns the most in total, but Sports has the fattest average order, so a “Sports bundle” push targets margin rather than raw volume. December is the busiest month and September the quietest, which tells the inventory team when to stock up. The top 40 customers (10% of 398) account for a quarter of all revenue, the classic long-tail pattern that makes loyalty programs worth it. And East trails on average order value, so that is where you would send the next investigation. This is the whole point of data science: not the chart, but the decision the chart unlocks.

What Done Looks Like

A tutorial you followed and a project you actually finished are two different things. This is the checklist that separates them. Treat it like a restaurant pass sheet: before a plate leaves the kitchen, someone checks it against a standard, not against “looks fine to me.” Your python data science project is done when every line below is true. If one fails, go back to the step that owns it before you call the build complete.

  • Data loads and is the right shape. Step 1 prints Dataset: 2000 orders, 11 columns. If your row or column count is different, the generator changed and everything downstream will drift.
  • Zero missing values after cleaning. Step 2 passes its assert df.isnull().sum().sum() == 0 and prints 0 missing values. No holes are allowed to survive into analysis.
  • Every number is computed, never typed. Search your findings script for a hardcoded figure like 292802. If you find one, you failed this check. Each headline number must come from df.
  • The dashboard PNG exists. After Step 4 you have ecommerce_dashboard.png on disk with four labeled panels, each with a title and readable axes.
  • The “Unknown” region is handled honestly. Your regional ranking excludes the Unknown bucket, so you never recommend investing in a market that is really just missing data.
  • Every finding ends in a recommendation. A number on its own is trivia. Each of your five findings pairs an insight with one concrete action a business could take on Monday morning.
  • It runs top to bottom on a clean machine. A friend can clone your repo, run one script per step in order, and reproduce your exact numbers because of the seed.

Make It Yours: One Variation to Try

Copying a project teaches you the steps. Changing it teaches you the thinking. Here is one small variation you can add without any new tools, and it turns your build into your build, which matters a lot when a recruiter asks “what did you actually do here?” The question: do weekends behave differently from weekdays? A shopkeeper feels this in their bones, but you can prove it in six lines. We add one feature, is_weekend, and split the same cleaned data by it.

📄 project_variation.py: weekend vs weekday, built on the same df

# Flag each order as weekend or weekday (Sat=5, Sun=6)
df["is_weekend"] = df["order_date"].dt.dayofweek >= 5

grp = df.groupby(df["is_weekend"].map({True: "Weekend", False: "Weekday"}))
summary = grp.agg(orders=("order_id", "count"),
                  revenue=("total_amount", "sum"),
                  avg_order=("total_amount", "mean")).round(2)
# Normalize revenue per calendar day: 5 weekdays vs 2 weekend days
summary["rev_per_day"] = (summary["revenue"] / [5, 2]).round(0)
print("Weekend vs Weekday performance:")
print(summary)

wk = summary.loc["Weekend", "avg_order"]
wd = summary.loc["Weekday", "avg_order"]
print(f"\nWeekend average order value is ${wk:,.2f} vs ${wd:,.2f} on weekdays.")
print(f"Difference: {(wk - wd) / wd:+.1%}")

▶ Output

Weekend vs Weekday performance:
            orders    revenue  avg_order  rev_per_day
is_weekend
Weekday       1455  714696.17     491.20     142939.0
Weekend        545  271813.61     498.74     135907.0

Weekend average order value is $498.74 vs $491.20 on weekdays.
Difference: +1.5%

What happened here: The average weekend order is only about 1.5% larger than a weekday order, so basket size barely changes with the day. But look at rev_per_day: a single weekday pulls in about $142,939 against $135,907 for a weekend day, because there are simply more weekday orders. That is a real finding you can defend: this shop does not get a weekend bump, so blowing the ad budget on Saturday would be a guess, not a decision. Swap is_weekend for any question you care about (holidays, morning versus evening, first-time versus repeat buyers) and the pattern of the code stays the same. That reusable pattern is the actual skill.

Put It On Your Portfolio

A finished project that lives only on your laptop does nothing for your career. The version on GitHub, described well, is what gets you the interview. Think of your repo like a tiffin packed for someone else to open: they should understand the meal without you standing there to explain it. Here is exactly what to publish and how to talk about it.

What to put on GitHub

  • The step scripts (project_step1.py through project_findings.py) or one clean notebook that runs them in order.
  • The generated ecommerce_dashboard.png committed to the repo, so it shows inline in the README without anyone running the code.
  • A requirements.txt pinning pandas, numpy, matplotlib, and seaborn, so a reviewer can install and run in one line.
  • A .gitignore for __pycache__ and any local data files you do not want tracked. Keep the repo clean.

What to write in the README

Open with the question and the answer, not the tools. A reviewer spends about thirty seconds, so front-load the payoff. A structure that works every time:

  1. One-line summary: “An end-to-end analysis of 2,000 e-commerce orders that finds where revenue concentrates and what to do about it.”
  2. The dashboard image right up top, so the result is visible before any code.
  3. Key findings as three or four bullets, each an insight plus its recommendation.
  4. How to run it: the install line and the order to run the scripts.
  5. What you would do next: one honest sentence, like adding customer segmentation, which shows you know the project is not the ceiling.

How to describe it to a recruiter

Skip the library list. A recruiter cares about the outcome and your thinking, not your imports. Lead with the business result and keep it to two sentences you could say out loud: “I took a raw 2,000-order dataset, cleaned the missing payment and region fields, and found that the top 10% of customers drive a quarter of revenue, so my recommendation was a loyalty program for that segment. The whole thing is reproducible from a seed and reads as a four-panel dashboard plus a findings report.” That answer shows you can go from mess to decision, which is the entire job.

If they want the stack, they will ask, and then you mention Pandas and Seaborn in one breath and move back to the result.

Part 4: Data Science is Complete

Congratulations, you have finished Part 4: Data Science. Across 23 posts (099 to 121), you went from “what is a matrix?” to building a complete data analysis project. You know NumPy, Pandas, descriptive statistics, probability, hypothesis testing, Matplotlib, Seaborn, Plotly, and EDA workflows. That is the full data science toolkit. In this post specifically you learned how to string those skills into one honest workflow: load a raw dataset, clean the holes, ask it five plain questions, build a dashboard, and hand a stakeholder findings they can act on.

Next up is Part 5, which opens by explaining what artificial intelligence really is and how AI, machine learning, and generative AI fit together, before you go on to train models on the data you now know how to prepare. For the full roadmap and every other topic in order, head to the Python + AI/ML tutorial series home.

What Could Go Wrong

  • Your numbers do not match mine. Check that you kept the seed line rng = np.random.default_rng(42) and did not re-run the data generation a second time. The seed is the only reason everyone gets identical figures. Remove it and your dataset, and every number, will change on each run.
  • FutureWarning from Seaborn about palette. On Seaborn 0.13 and later you must pass hue= alongside palette= and set legend=False, exactly as the dashboard code does. The old form still runs but warns.
  • The chart window never appears. If you run the script outside a notebook on a headless machine, add import matplotlib; matplotlib.use("Agg") at the top and rely on the saved PNG instead of plt.show().
  • KeyError on a column. The steps build on each other and share one df. Run them in order in the same session, because month_name and is_high_value only exist after Step 2.

Extend It

The report above is a solid start, not the ceiling. Pick one of these and try it on your own. The point is to struggle a little, that is where the learning sticks.

  • Add customer segmentation with RFM analysis (Recency, Frequency, Monetary) to score each buyer.
  • Build a cohort analysis that tracks customer retention month over month.
  • Rebuild the dashboard in Plotly so the stakeholder can hover and filter.
  • Forecast next month’s revenue from the monthly trend (you will learn how in Part 5).

Frequently Asked Questions

Can I use a real Kaggle dataset for this Python data science project instead of generated data?

Absolutely. The workflow is identical. Good starter datasets are Kaggle’s ‘Online Retail II’, ‘E-Commerce Shipping’, or ‘Customer Personality Analysis’. Load it with pd.read_csv() instead of the generator, then follow the same clean, analyze, visualize, report steps. The skills transfer directly. Only the dataset changes, the approach stays the same.

How do I present data science findings to non-technical stakeholders?

Lead with findings, not methodology. Use charts, not code. Each insight should be one sentence backed by one visual, then end with a specific, actionable recommendation. ‘Revenue peaks in December, so stock up by 20% in November’ lands far better than ‘seasonal decomposition shows cyclical patterns’.

Why do my numbers differ from the post?

They should not, as long as you keep the seed line rng = np.random.default_rng(42) and run the steps in order in one session. The seed makes the random data identical for everyone. If you drop it or change the seed, your dataset and every figure shift accordingly, which is fine, just expect different totals.

What comes after EDA in the machine learning path?

Machine learning. Part 5 starts with ML fundamentals and builds on everything here. The features you explored in EDA become the inputs to ML models, and the patterns you found guide model selection. EDA is the foundation that makes ML work.

Interview Questions on Python Data Science Projects

How interviewers actually probe this topic: real scenarios, with answers you can say out loud.

Q: Walk me through the steps of an end-to-end data science project.

Define the question and collect the data, then explore it (EDA) to understand shape and quality. Clean it by handling missing values, duplicates, and wrong types. Engineer useful features, analyze with grouping and aggregation, visualize the findings, and finally present clear, actionable recommendations. In this post the flow was generate/load, clean, analyze with groupby, build a dashboard, and write a findings report. Modeling and evaluation come later, in the machine learning stage.

Q: How do you handle missing values, and why not always drop them?

It depends on the column and how much is missing. If a lot of rows share a sensible default, imputation is better than dropping, since dropping throws away real data in the other columns. In this project we filled missing payment methods with the mode (the most common value) and labeled missing regions as "Unknown" instead of guessing. Dropping rows is only safe when the missing count is tiny and the rows are not systematically different from the rest.

Q: Why do we set a random seed like np.random.default_rng(42)?

A seed makes random generation reproducible. Anyone who runs the code with the same seed gets the exact same data and the exact same numbers, which is essential for tutorials, debugging, and sharing results. Without it, every run produces a different dataset and your figures would never match a colleague’s. In production you often skip the seed for genuine randomness, but you fix it whenever reproducibility matters.

Q: Scenario: your teammate Aditi reports that her revenue-by-category totals do not match yours, even though she copied your code. What do you check first?

First check the random seed: if Aditi dropped or changed np.random.default_rng(42), or re-ran the data generation an extra time, her dataset is different and every downstream number will shift. Next confirm she ran the steps in order in one session, since later columns like is_high_value depend on Step 2. Then check library versions, because Pandas behavior can differ slightly across major releases. Nine times out of ten it is the seed.

Q: Scenario: a stakeholder named Anvay says the “Unknown” region has by far the highest average order value and wants to invest there. How do you respond?

Explain that “Unknown” is not a real market, it is a bucket for the 15 orders whose region was missing, so its average is an artifact of a tiny, biased sample rather than a genuine high-value area. You would exclude it before ranking regions, exactly as the findings script does with df[df["region"] != "Unknown"]. The honest recommendation is to fix the data collection so region is captured, then compare only the real regions like North, South, East, and West.

Q: What is the difference between total revenue and average order value, and why report both?

Total revenue is the sum of all order amounts in a group; average order value (AOV) is that sum divided by the number of orders. A category can top total revenue on sheer volume while having a modest AOV, or lead on AOV while selling fewer, pricier items. In this project Clothing won on total revenue but Sports had the highest AOV, which points to different actions: protect volume for one, push premium bundles for the other.

Q: How would you present these findings to a non-technical audience?

Lead with the insight, not the method. Pair each finding with one visual and one concrete recommendation, in plain language: “Revenue peaks in December, so stock up by November” beats “the monthly series shows a Q4 seasonal component.” Avoid code and jargon, keep it to a few slides, and always close on the decision the number unlocks rather than the technique that produced it.

Next (Part 5 begins): ML math intuition tutorial

Series Home: Python + AI/ML Cookbook. Complete Tutorial Series

Further reading: for the full reference, see the official Python documentation.

Previous: EDA: Exploratory Data Analysis Complete Workflow

Next: What is Artificial Intelligence? AI, ML, and GenAI Explained

Series Home: Python + AI/ML Tutorial Series

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *