Analyze Coffee Shop Sales with Python

Load, clean, and visualize coffee shop sales data using pandas and seaborn.

Introduction

30 Second Summary

Every coffee shop generates hundreds of transactions a day. Hidden in that data are patterns that reveal which products drive the most revenue, which days are busiest, and which customers spend the most.

In this project, you will build a Python script that loads raw sales data from a CSV file, cleans it, calculates key business metrics, and produces professional visualizations that tell a story about the business.

What You'll Build

A complete data analysis pipeline that transforms 100 coffee shop transactions into actionable business insights using pandas, matplotlib, and seaborn.

By the end of this project, you'll have:

  • A CSV dataset with 100 coffee shop transactions that you created and understand from the ground up.
  • A Python analysis script that loads, cleans, transforms, and aggregates data using pandas DataFrames.
  • Four publication-quality visualizations (bar charts, line charts, histograms) that reveal business insights from the data.
  • Secret Mission: Add a customer spending analysis that segments customers into tiers and visualizes spending patterns.

Are there any prerequisites?

You should have basic familiarity with Python syntax (variables, loops, functions). No data analysis experience is required. This project will teach you everything you need to know about pandas, matplotlib, and seaborn from scratch.

Set Up Your Python Environment

Before you can analyze any data, you need the right tools installed on your machine.

In this step, you will install Python and three libraries that professional data analysts rely on every day: pandas for data manipulation, matplotlib for plotting, and seaborn for statistical visualizations.

In this step, get ready to:
  • Install and verify Python 3.13.
  • Create a project folder with a virtual environment.
  • Install pandas, matplotlib, and seaborn.
Install Python 3.13

Python is the programming language that powers the data analysis libraries you will use throughout this project.

  • Open your terminal by pressing Cmd+Space (macOS) or the Windows key (Windows), then typing Terminal (macOS) or PowerShell (Windows) and pressing Enter.
  • Check if Python 3.13 is already installed by running the version check command for your operating system:

macOS

python3 --version

On macOS, the Python command is python3.

Windows

python --version

On Windows, the Python command is python.

What do you see in the terminal output?

✔️ I see version 3.13 or higher

You should see something like Python 3.13.13 (any 3.13.x version works). Python is ready to go.

ⓧ I see an older version

If you see a version like 3.11 or 3.12, you need to install Python 3.13 alongside your existing version.

On macOS, the system often ships with an older Python. Download and install Python 3.13 from the official site:

  • Go to python.org/downloads.
  • Download the Python 3.13 installer for your platform (macOS or Windows).
  • Run the installer and follow the prompts. On Windows, make sure to check Add python.exe to PATH during installation.
  • Close and reopen your terminal, then run the version check command again.

Why does macOS show an older version?

macOS often includes an older Python version by default. After installing Python 3.13, the python3 command should point to the new version. If it still shows the old version after reopening your terminal, try running python3.13 --version directly.

ⓧ Command not found

Python is not installed yet. Download and install it from the official site:

  • Go to python.org/downloads.
  • Download the Python 3.13 installer for your platform (macOS or Windows).
  • Run the installer and follow the prompts. On Windows, make sure to check Add python.exe to PATH during installation.
  • Close and reopen your terminal, then run the version check command again to confirm it shows 3.13.x.
Create your project folder and virtual environment

A virtual environment keeps your project's libraries separate from other Python projects on your machine. This prevents version conflicts between projects.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir coffee-shop-analysis
cd coffee-shop-analysis
  • Confirm the folder was created by running:
pwd

You should see a path ending in coffee-shop-analysis.

  • Create a virtual environment by running this command:

macOS

python3 -m venv venv

Windows

python -m venv venv

What is a virtual environment?

The venv module creates an isolated Python environment inside a folder called venv. When activated, any packages you install with pip go into this folder instead of your system Python.

This means you can have different library versions for different projects without them interfering with each other.

  • Activate the virtual environment:

macOS

source venv/bin/activate

You should see (venv) appear at the beginning of your terminal prompt.

Windows

venv\Scripts\activate

You should see (venv) appear at the beginning of your terminal prompt.

Don't see (venv) in your prompt?

On Windows, you may need to allow script execution first. Run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned in PowerShell, then try the activate command again.

On macOS, double-check that you typed source before the path. Without it, the activation will not work.

Help me activate my virtual environment.

Install the required libraries

With your virtual environment active, you can now install the three data analysis libraries this project depends on.

  • Install pandas, matplotlib, and seaborn by running this command:
pip install pandas==3.0.3 matplotlib==3.10.9 seaborn==0.13.2

What does this command do?

The pip install command downloads packages from PyPI (the Python Package Index). The == syntax pins each library to a specific version so your results match this project exactly.

pandas handles data loading and manipulation. matplotlib creates charts and plots. seaborn builds on matplotlib to make statistical visualizations easier to create.

The installation may take a minute. You should see progress bars as each package downloads.

  • Verify the installation by running a quick import test:
python -c "import pandas; import matplotlib; import seaborn; print('pandas', pandas.__version__); print('matplotlib', matplotlib.__version__); print('seaborn', seaborn.__version__)"

You should see the version numbers printed with no errors:

pandas 3.0.3
matplotlib 3.10.9
seaborn 0.13.2

Seeing a ModuleNotFoundError?

Make sure your virtual environment is active. You should see (venv) in your terminal prompt. If not, run the activate command from the previous substep again, then re-run the pip install command.

If pip install itself failed with a network error, check your internet connection and try again.

Help me fix my library installation.

Your Python environment is ready with all three libraries installed. Next up, you will create the sales dataset and start loading it into pandas.

Create Your Sales Dataset

Your Python environment is ready with all three libraries installed. Now it's time to create the data you'll be analyzing.

Every data analysis project starts with a dataset. In the real world, you'd pull this from a database or receive it as a file from a colleague. For this project, you'll create a CSV file that simulates 100 coffee shop transactions, then write a Python script that loads and inspects the data.

In this step, get ready to:
  • Create a CSV dataset with 100 coffee shop transactions.
  • Write a Python script that loads and inspects the data using pandas.
  • Run the script and verify the data loads correctly.
Create the sales data CSV file

Your analysis needs data to work with. This CSV file contains 100 transactions from a fictional coffee shop, with columns for the order ID, date, product name, category, price, quantity, and customer ID.

  • In VS Code, click File in the top menu bar.
  • Click New File.
  • Save the file immediately by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Name the file sales_data.csv and save it inside your project folder.
  • Paste the following CSV data into the file:
order_id,date,product,category,price,quantity,customer_id
1,2024-01-02,Latte,Coffee,4.50,1,C001
2,2024-01-02,Croissant,Pastry,3.25,2,C002
3,2024-01-02,Espresso,Coffee,3.00,1,C003
4,2024-01-02,Blueberry Muffin,Pastry,3.75,1,C001
5,2024-01-03,Cappuccino,Coffee,4.75,1,C004
6,2024-01-03,Latte,Coffee,4.50,2,C005
7,2024-01-03,Chocolate Cake,Pastry,5.50,1,C002
8,2024-01-03,Green Tea,Tea,3.50,1,C006
9,2024-01-04,Espresso,Coffee,3.00,2,C007
10,2024-01-04,Chai Latte,Tea,4.25,1,C003
11,2024-01-04,Croissant,Pastry,3.25,1,C008
12,2024-01-04,Latte,Coffee,4.50,1,C004
13,2024-01-05,Cappuccino,Coffee,4.75,2,C009
14,2024-01-05,Scone,Pastry,3.00,1,C010
15,2024-01-05,Latte,Coffee,4.50,1,C001
16,2024-01-05,Earl Grey,Tea,3.50,2,C006
17,2024-01-06,Espresso,Coffee,3.00,1,C011
18,2024-01-06,Blueberry Muffin,Pastry,3.75,2,C002
19,2024-01-06,Matcha Latte,Tea,5.00,1,C012
20,2024-01-06,Latte,Coffee,4.50,1,C005
21,2024-01-07,Cappuccino,Coffee,4.75,1,C003
22,2024-01-07,Croissant,Pastry,3.25,2,C013
23,2024-01-07,Chai Latte,Tea,4.25,1,C007
24,2024-01-07,Chocolate Cake,Pastry,5.50,1,C014
25,2024-01-08,Latte,Coffee,4.50,2,C001
26,2024-01-08,Espresso,Coffee,3.00,1,C015
27,2024-01-08,Scone,Pastry,3.00,2,C009
28,2024-01-08,Green Tea,Tea,3.50,1,C006
29,2024-01-09,Cappuccino,Coffee,4.75,1,C016
30,2024-01-09,Latte,Coffee,4.50,1,C004
31,2024-01-09,Blueberry Muffin,Pastry,3.75,1,C002
32,2024-01-09,Matcha Latte,Tea,5.00,2,C012
33,2024-01-10,Espresso,Coffee,3.00,2,C017
34,2024-01-10,Croissant,Pastry,3.25,1,C008
35,2024-01-10,Latte,Coffee,4.50,1,C005
36,2024-01-10,Earl Grey,Tea,3.50,1,C010
37,2024-01-11,Cappuccino,Coffee,4.75,1,C003
38,2024-01-11,Chocolate Cake,Pastry,5.50,2,C014
39,2024-01-11,Chai Latte,Tea,4.25,1,C018
40,2024-01-11,Latte,Coffee,4.50,2,C001
41,2024-01-12,Espresso,Coffee,3.00,1,C019
42,2024-01-12,Scone,Pastry,3.00,1,C009
43,2024-01-12,Latte,Coffee,4.50,1,C020
44,2024-01-12,Green Tea,Tea,3.50,2,C006
45,2024-01-13,Cappuccino,Coffee,4.75,2,C016
46,2024-01-13,Blueberry Muffin,Pastry,3.75,1,C002
47,2024-01-13,Matcha Latte,Tea,5.00,1,C012
48,2024-01-13,Latte,Coffee,4.50,1,C004
49,2024-01-14,Espresso,Coffee,3.00,1,C007
50,2024-01-14,Croissant,Pastry,3.25,2,C013
51,2024-01-14,Chai Latte,Tea,4.25,2,C003
52,2024-01-14,Latte,Coffee,4.50,1,C005
53,2024-01-15,Cappuccino,Coffee,4.75,1,C011
54,2024-01-15,Chocolate Cake,Pastry,5.50,1,C014
55,2024-01-15,Earl Grey,Tea,3.50,1,C010
56,2024-01-15,Latte,Coffee,4.50,2,C001
57,2024-01-16,Espresso,Coffee,3.00,2,C017
58,2024-01-16,Scone,Pastry,3.00,1,C009
59,2024-01-16,Latte,Coffee,4.50,1,C020
60,2024-01-16,Matcha Latte,Tea,5.00,1,C018
61,2024-01-17,Cappuccino,Coffee,4.75,1,C004
62,2024-01-17,Blueberry Muffin,Pastry,3.75,2,C002
63,2024-01-17,Green Tea,Tea,3.50,1,C006
64,2024-01-17,Latte,Coffee,4.50,1,C015
65,2024-01-18,Espresso,Coffee,3.00,1,C019
66,2024-01-18,Croissant,Pastry,3.25,1,C008
67,2024-01-18,Chai Latte,Tea,4.25,1,C007
68,2024-01-18,Latte,Coffee,4.50,2,C001
69,2024-01-19,Cappuccino,Coffee,4.75,2,C016
70,2024-01-19,Chocolate Cake,Pastry,5.50,1,C014
71,2024-01-19,Earl Grey,Tea,3.50,2,C010
72,2024-01-19,Scone,Pastry,3.00,1,C009
73,2024-01-20,Latte,Coffee,4.50,1,C005
74,2024-01-20,Espresso,Coffee,3.00,1,C011
75,2024-01-20,Blueberry Muffin,Pastry,3.75,1,C002
76,2024-01-20,Matcha Latte,Tea,5.00,2,C012
77,2024-01-21,Cappuccino,Coffee,4.75,1,C003
78,2024-01-21,Croissant,Pastry,3.25,2,C013
79,2024-01-21,Latte,Coffee,4.50,1,C004
80,2024-01-21,Green Tea,Tea,3.50,1,C006
81,2024-01-22,Espresso,Coffee,3.00,2,C017
82,2024-01-22,Chai Latte,Tea,4.25,1,C018
83,2024-01-22,Chocolate Cake,Pastry,5.50,2,C014
84,2024-01-22,Latte,Coffee,4.50,1,C020
85,2024-01-23,Cappuccino,Coffee,4.75,1,C009
86,2024-01-23,Scone,Pastry,3.00,2,C010
87,2024-01-23,Latte,Coffee,4.50,2,C001
88,2024-01-23,Earl Grey,Tea,3.50,1,C006
89,2024-01-24,Espresso,Coffee,3.00,1,C015
90,2024-01-24,Blueberry Muffin,Pastry,3.75,1,C002
91,2024-01-24,Matcha Latte,Tea,5.00,1,C012
92,2024-01-24,Latte,Coffee,4.50,1,C005
93,2024-01-25,Cappuccino,Coffee,4.75,2,C016
94,2024-01-25,Croissant,Pastry,3.25,1,C008
95,2024-01-25,Latte,Coffee,4.50,1,C003
96,2024-01-25,Chai Latte,Tea,4.25,2,C007
97,2024-01-26,Espresso,Coffee,3.00,1,C019
98,2024-01-26,Chocolate Cake,Pastry,5.50,1,C014
99,2024-01-26,Latte,Coffee,4.50,2,C001
100,2024-01-26,Green Tea,Tea,3.50,1,C006

What is this data?

This CSV file simulates 25 days of sales at a coffee shop. Each row is one transaction with a unique order ID, the date of purchase, the product name, its category (Coffee, Tea, or Pastry), the price per item, how many were ordered, and which customer placed the order.

The 7 columns give you enough dimensions to ask interesting business questions: which products sell best, what days are busiest, and how much revenue each category generates.

  • Save sales_data.csv by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Confirm the file exists by checking the VS Code Explorer sidebar on the left. You should see sales_data.csv listed in your project folder.

Don't see the file in the sidebar?

Make sure you saved the file inside your project folder, not somewhere else on your computer. If the Explorer sidebar is hidden, press Cmd+Shift+E (macOS) or Ctrl+Shift+E (Windows) to open it.

help me find my sales_data.csv file in VS Code

Write the analysis script imports

With your data ready, you need a Python script to read and analyze it. The script starts with importing the three libraries you installed in Step 1.

  • Create a new file in VS Code by pressing Cmd+N (macOS) or Ctrl+N (Windows).
  • Save it immediately as analysis.py in your project folder.
  • Add the library imports by pasting this code:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

What do these imports do?

pandas (imported as pd) is the core data manipulation library. It gives you DataFrames for working with tabular data like spreadsheets.

matplotlib (imported as plt) handles chart creation. seaborn (imported as sns) builds on matplotlib to make statistical visualizations easier to create.

  • Save analysis.py.
Load and inspect the data with pandas

Before analyzing data, you always want to look at it first. pandas gives you several methods to quickly understand the size, structure, and content of your dataset.

  • In analysis.py, add the following code below your imports:
# ============================================================
# STEP 2: Load and inspect the data
# ============================================================

df = pd.read_csv('sales_data.csv')

print("=== Dataset Overview ===")
print(f"Shape: {df.shape}")
print()
print(df.head())
print()
print(df.info())
print()
print(df.describe())

What does this code do?

pd.read_csv('sales_data.csv') reads your CSV file into a DataFrame called df. A DataFrame is like a spreadsheet in Python, with rows and columns you can filter, sort, and aggregate.

  • .shape tells you how many rows and columns exist (expected: 100 rows, 7 columns).
  • .head() shows the first 5 rows so you can eyeball the data.
  • .info() shows each column's name and data type.
  • .describe() calculates summary statistics (mean, min, max) for numeric columns.
  • Save analysis.py.
  • Run the script in your terminal by running this command:
python analysis.py

You should see output that starts with the dataset shape (100, 7), followed by the first 5 rows of data, column information, and numeric statistics.

Seeing a FileNotFoundError?

This means Python cannot find sales_data.csv. Make sure both analysis.py and sales_data.csv are saved in the same folder. Also check that your terminal is currently in that folder.

help me fix a FileNotFoundError when loading my CSV

Seeing 'python' is not recognized?

On macOS or Linux, the command might be python3 instead of python. Also make sure your virtual environment is activated. You should see (venv) at the start of your terminal prompt.

help me run my Python script

✔️ Awesome, I've got everything!

Great. Double check you've saved both files, then move on to the next step.

ⓧ I'd like to double check the full code

Here is the complete analysis.py at this point:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ============================================================
# STEP 2: Load and inspect the data
# ============================================================

df = pd.read_csv('sales_data.csv')

print("=== Dataset Overview ===")
print(f"Shape: {df.shape}")
print()
print(df.head())
print()
print(df.info())
print()
print(df.describe())

Your data is loaded and looking good. Next up, you'll clean and transform it to prepare for analysis.

Clean and Transform the Data

Your DataFrame loaded successfully and you've confirmed the shape of your dataset. Now it's time to prepare this raw data for meaningful analysis.

Raw data almost always needs cleanup before you can draw conclusions from it. Dates might be stored as plain text, key metrics like revenue might not exist yet, and hidden gaps in the data can skew your results. In this step, you'll fix these issues so the data is ready for the analysis ahead.

In this step, get ready to:
  • Convert the date column to proper datetime format and calculate revenue.
  • Check for and remove missing values and duplicate rows.
  • Add a day of week column for time-based analysis.
Convert date to datetime and calculate revenue

Pandas loaded the date column as plain text strings. To do any time-based analysis (like finding the busiest day), pandas needs to understand these values as actual dates.

  • In analysis.py, add this code at the very bottom of your file (below the last print(df.describe()) line):
# ============================================================
# STEP 3: Clean and transform the data
# ============================================================

# Convert date column to datetime
df['date'] = pd.to_datetime(df['date'])

# Calculate revenue
df['revenue'] = df['price'] * df['quantity']

What does this code do?

pd.to_datetime() converts the text strings like "2024-01-02" into proper datetime objects. This unlocks time-based operations like extracting the day of the week or grouping by date.

The revenue column multiplies each row's price by its quantity. A single Latte at $4.50 is $4.50 revenue, but two Lattes at $4.50 is $9.00.

  • Save analysis.py.

You won't see a change yet. The next chunk adds print statements that confirm your cleaning is working.

Check for and handle missing values

Missing values (empty cells) and duplicate rows can silently corrupt your analysis. Before calculating any metrics, you need to inspect the data for these issues and remove them.

  • Add this code below the revenue line you just wrote:
# Check for missing values
print("\n=== Missing Values ===")
print(df.isnull().sum())

# Remove any missing values and duplicates
df = df.dropna()
df = df.drop_duplicates()

What does this code do?

  • df.isnull().sum() counts how many missing values exist in each column. If every count is 0, your data has no gaps.
  • .dropna() removes any rows with missing values. .drop_duplicates() removes exact duplicate rows that could inflate your totals.
  • Save analysis.py and run your script by entering this command in the terminal:
python analysis.py
  • Scroll through the output. After the dataset overview from Step 2, you should see a === Missing Values === section showing 0 for every column.

Seeing a NameError or SyntaxError?

  • Make sure the new code is below the Step 2 code, not inside it. It should come after print(df.describe()).
  • Check that the quotes around column names match exactly: df['date'] uses single quotes.

Help me debug my data cleaning code in analysis.py

Add a day of week column

Knowing which day of the week each sale happened on opens up useful questions. Are Mondays slow? Do weekends drive more revenue? Extracting this from the date column takes one line of code.

  • Add this code at the bottom of analysis.py, below the df = df.drop_duplicates() line:
# Add day of week
df['day_of_week'] = df['date'].dt.day_name()

print(f"\nCleaned dataset shape: {df.shape}")

What does this code do?

.dt.day_name() extracts the weekday name (like "Monday" or "Friday") from each date. This only works because you converted the column to datetime in the previous chunk.

The final print confirms the DataFrame's shape after cleaning. If no rows were dropped, you'll still see 100 rows, now with 9 columns (the original 7 plus revenue and day_of_week).

  • Save analysis.py and run the script again:
python analysis.py
  • At the bottom of the output, you should see Cleaned dataset shape: (100, 9). This confirms your 100 rows are intact and you now have 9 columns.

Seeing fewer than 100 rows?

  • If .dropna() removed rows, your CSV may have empty cells. Open sales_data.csv and check for blank fields or extra commas at the end of lines.
  • If you see 9 columns but the count is lower than 100, check that your CSV has exactly 100 data rows (plus the header row).

Help me figure out why my cleaned DataFrame has fewer rows than expected

✔️ Awesome, I've got everything!

Great. Double check you've saved your file.

ⓧ I'd like to double check the full code

Here's the complete analysis.py so far. Make sure your file matches this exactly:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ============================================================
# STEP 2: Load and inspect the data
# ============================================================

df = pd.read_csv('sales_data.csv')

print("=== Dataset Overview ===")
print(f"Shape: {df.shape}")
print()
print(df.head())
print()
print(df.info())
print()
print(df.describe())

# ============================================================
# STEP 3: Clean and transform the data
# ============================================================

# Convert date column to datetime
df['date'] = pd.to_datetime(df['date'])

# Calculate revenue
df['revenue'] = df['price'] * df['quantity']

# Check for missing values
print("\n=== Missing Values ===")
print(df.isnull().sum())

# Remove any missing values and duplicates
df = df.dropna()
df = df.drop_duplicates()

# Add day of week
df['day_of_week'] = df['date'].dt.day_name()

print(f"\nCleaned dataset shape: {df.shape}")

Your data is clean, enriched with a revenue column, and ready for deeper analysis. Next up, you'll use groupby to calculate business metrics like total revenue by category and the top-selling products.

Analyze the Data with GroupBy

Your data is clean, properly typed, and enriched with a revenue column. Now it is time to extract actual business insights from it.

The pandas groupby method lets you split your data into groups, perform a calculation on each group, and combine the results. This is how analysts answer questions like "which product category brings in the most money?" and "what day had the highest sales?"

In this step, get ready to:
  • Calculate total revenue by product category and find the top-selling products.
  • Calculate daily revenue totals to identify sales trends over time.
  • Print a summary report with key business metrics.
Calculate revenue by category and find top products

A groupby operation works in three stages: split the DataFrame into groups based on a column value, apply an aggregation function (like sum()) to each group, then combine the results back into a single Series.

  • In analysis.py, find the line that says print(f"\nCleaned dataset shape: {df.shape}") at the bottom of your file.
  • Add the following code below that line to calculate revenue by category and identify the top-selling products:
# ============================================================
# STEP 4: Analyze the data with GroupBy
# ============================================================

# Revenue by category
category_revenue = df.groupby('category')['revenue'].sum().sort_values(ascending=False)
print("\n=== Revenue by Category ===")
print(category_revenue)

# Top 5 products by quantity sold
top_products = df.groupby('product')['quantity'].sum().sort_values(ascending=False).head(5)
print("\n=== Top 5 Products by Quantity ===")
print(top_products)

What does this code do?

df.groupby('category')['revenue'].sum() splits all rows by their category value (Coffee, Pastry, Tea), then adds up the revenue for each group.

.sort_values(ascending=False) puts the highest-revenue category first. The second groupby does the same thing but groups by individual product name, sums quantity instead of revenue, and .head(5) keeps only the top 5.

  • Save analysis.py.
  • Run the script in your terminal by running:
python analysis.py
  • Scroll through the output. Below the earlier dataset overview, you should see a "Revenue by Category" section showing three categories with their total revenue, and a "Top 5 Products by Quantity" section listing product names with quantities.

Seeing a KeyError?

If you see KeyError: 'revenue', make sure the Step 3 code that calculates df['revenue'] = df['price'] * df['quantity'] is still in your file above this new code.

If you see KeyError: 'category', check that the column name in your groupby matches the CSV header exactly (lowercase category).

Help me debug a KeyError in my pandas groupby code

Calculate daily revenue and build a summary report

Grouping by date reveals how sales change day to day. You can use this daily total to spot the busiest day and track revenue trends over time.

  • Add the following code below the print(top_products) line to calculate daily revenue and print a summary of your key findings:
# Daily revenue
daily_revenue = df.groupby('date')['revenue'].sum()

# Summary report
total_revenue = df['revenue'].sum()
avg_order_value = df['revenue'].mean()
busiest_day = daily_revenue.idxmax().strftime('%A, %B %d')
top_category = category_revenue.index[0]

print("\n=== Summary Report ===")
print(f"Total Revenue: ${total_revenue:.2f}")
print(f"Average Order Value: ${avg_order_value:.2f}")
print(f"Busiest Day: {busiest_day}")
print(f"Top Category: {top_category}")

What does this code do?

  • daily_revenue groups all transactions by date and sums their revenue, giving one total per day.
  • total_revenue sums every transaction's revenue across the entire dataset.
  • avg_order_value calculates the mean revenue per order.
  • .idxmax() finds the date with the highest revenue, then .strftime('%A, %B %d') formats it into a readable day name like "Monday, January 08".
  • category_revenue.index[0] grabs the name of the highest-revenue category (the first item, since the Series is sorted descending).
  • Save analysis.py and run the script again:
python analysis.py
  • At the bottom of the output, you should see a "Summary Report" section showing total revenue, average order value, the busiest day name, and the top category.

Seeing a NameError for category_revenue?

The summary report uses category_revenue and daily_revenue which were defined earlier in this step. Make sure the code from substep 1 (the category and top products groupby) is still above this new code in your file.

If the formatting looks strange in the printed output, check that your f-string curly braces {} match exactly what is shown in the code above.

Help me fix a NameError in my pandas summary report

✔️ Awesome, I've got everything!

Great. Double check you have saved analysis.py and that your terminal output shows the summary report at the bottom.

ⓧ I'd like to double check the full code

Here is the complete analysis.py file up to this point. Compare it against yours:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ============================================================
# STEP 2: Load and inspect the data
# ============================================================

df = pd.read_csv('sales_data.csv')

print("=== Dataset Overview ===")
print(f"Shape: {df.shape}")
print()
print(df.head())
print()
print(df.info())
print()
print(df.describe())

# ============================================================
# STEP 3: Clean and transform the data
# ============================================================

# Convert date column to datetime
df['date'] = pd.to_datetime(df['date'])

# Calculate revenue
df['revenue'] = df['price'] * df['quantity']

# Check for missing values
print("\n=== Missing Values ===")
print(df.isnull().sum())

# Remove any missing values and duplicates
df = df.dropna()
df = df.drop_duplicates()

# Add day of week
df['day_of_week'] = df['date'].dt.day_name()

print(f"\nCleaned dataset shape: {df.shape}")

# ============================================================
# STEP 4: Analyze the data with GroupBy
# ============================================================

# Revenue by category
category_revenue = df.groupby('category')['revenue'].sum().sort_values(ascending=False)
print("\n=== Revenue by Category ===")
print(category_revenue)

# Top 5 products by quantity sold
top_products = df.groupby('product')['quantity'].sum().sort_values(ascending=False).head(5)
print("\n=== Top 5 Products by Quantity ===")
print(top_products)

# Daily revenue
daily_revenue = df.groupby('date')['revenue'].sum()

# Summary report
total_revenue = df['revenue'].sum()
avg_order_value = df['revenue'].mean()
busiest_day = daily_revenue.idxmax().strftime('%A, %B %d')
top_category = category_revenue.index[0]

print("\n=== Summary Report ===")
print(f"Total Revenue: ${total_revenue:.2f}")
print(f"Average Order Value: ${avg_order_value:.2f}")
print(f"Busiest Day: {busiest_day}")
print(f"Top Category: {top_category}")

Your script now crunches the raw data into real business answers. Next up, you will turn these numbers into professional visualizations that make the insights immediately obvious.

Visualize Your Findings

Your analysis script now crunches numbers and prints a summary report. Numbers in a terminal are useful, but charts tell a story that anyone can understand at a glance.

In this step, you will turn your aggregated data into four publication-quality visualizations using matplotlib and seaborn. Each chart highlights a different insight from the coffee shop data.

In this step, get ready to:
  • Create a bar chart showing revenue by product category.
  • Create a line chart showing daily revenue trends over time.
  • Create a histogram and a horizontal bar chart to visualize order distribution and top products.
Create a bar chart of revenue by category

A bar chart is the clearest way to compare totals across categories. You will use seaborn's barplot() function to visualize which product category earns the most revenue.

  • In analysis.py, scroll to the very bottom of the file (after the Summary Report print statements).
  • Add the following code to set a chart theme and create your first visualization:
# ============================================================
# STEP 5: Visualize the findings
# ============================================================

sns.set_theme(style="whitegrid")

# Chart 1: Revenue by category (bar chart)
fig, ax = plt.subplots(figsize=(8, 5))
sns.barplot(x=category_revenue.index, y=category_revenue.values, ax=ax)
ax.set_title('Total Revenue by Product Category', fontsize=14, fontweight='bold')
ax.set_xlabel('Category')
ax.set_ylabel('Revenue ($)')
plt.tight_layout()
plt.savefig('chart_revenue_by_category.png', dpi=150)
plt.close()

What does this code do?

sns.set_theme(style="whitegrid") applies a clean visual style to all charts that follow. The white grid background makes values easy to read.

plt.subplots(figsize=(8, 5)) creates a figure and axes object. The figure is 8 inches wide and 5 inches tall. plt.savefig() writes the chart to a PNG file instead of opening a window. plt.close() frees the memory so the next chart starts fresh.

  • Save analysis.py.
  • Run your script in the terminal by running:
python analysis.py
  • Confirm that chart_revenue_by_category.png appeared in your project folder by running:
ls

You should see chart_revenue_by_category.png listed alongside analysis.py and sales_data.csv.

Don't see the PNG file?

  • Make sure the new code is below the Step 4 print statements, not inside them.
  • Check that you have import matplotlib.pyplot as plt at the top of your file.
  • If you see an indentation error, confirm none of the new lines are accidentally indented.

Help me debug my matplotlib chart

Create a line chart of daily revenue trends

Line charts are ideal for showing how a value changes over time. This chart will reveal whether the coffee shop's revenue is growing, shrinking, or fluctuating day to day.

  • In analysis.py, add the following code directly below plt.close() from Chart 1:
# Chart 2: Daily revenue trend (line chart)
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(daily_revenue.index, daily_revenue.values, marker='o', linewidth=2, color='steelblue')
ax.set_title('Daily Revenue Trend', fontsize=14, fontweight='bold')
ax.set_xlabel('Date')
ax.set_ylabel('Revenue ($)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('chart_daily_revenue.png', dpi=150)
plt.close()

What does this code do?

ax.plot() draws a line connecting each data point. The marker='o' argument adds a circle at each day so you can see individual values.

plt.xticks(rotation=45) tilts the date labels at 45 degrees so they do not overlap. plt.tight_layout() adjusts spacing so the rotated labels are not clipped.

  • Save analysis.py and run the script again:
python analysis.py
  • Verify that chart_daily_revenue.png now appears in your project folder by running ls.

Script errors out before creating Chart 2?

  • Make sure Chart 1 ends with its own separate plt.close() line before Chart 2 begins.
  • If you see a NameError for daily_revenue, confirm the Step 4 groupby code is still intact above.

Help me fix my line chart code

Create a histogram and a top-products bar chart

The histogram shows how order values are distributed. Are most orders clustered around the same price, or is there a wide spread? The horizontal bar chart answers a different question: which specific products sell the most units?

  • In analysis.py, add the histogram code directly below the Chart 2 plt.close():
# Chart 3: Distribution of order values (histogram)
fig, ax = plt.subplots(figsize=(8, 5))
sns.histplot(df['revenue'], bins=10, kde=True, ax=ax)
ax.set_title('Distribution of Order Values', fontsize=14, fontweight='bold')
ax.set_xlabel('Order Value ($)')
ax.set_ylabel('Count')
plt.tight_layout()
plt.savefig('chart_order_distribution.png', dpi=150)
plt.close()

What does this code do?

sns.histplot() bins continuous values into ranges and counts how many fall into each bin. The kde=True argument overlays a smooth curve that estimates the probability density, making the distribution shape easier to see.

  • Now add the horizontal bar chart for top products directly below:
# Chart 4: Top 5 products (horizontal bar chart)
fig, ax = plt.subplots(figsize=(8, 5))
sns.barplot(x=top_products.values, y=top_products.index, ax=ax, orient='h')
ax.set_title('Top 5 Products by Quantity Sold', fontsize=14, fontweight='bold')
ax.set_xlabel('Quantity Sold')
ax.set_ylabel('Product')
plt.tight_layout()
plt.savefig('chart_top_products.png', dpi=150)
plt.close()

print("\n=== Visualizations saved! ===")
print("- chart_revenue_by_category.png")
print("- chart_daily_revenue.png")
print("- chart_order_distribution.png")
print("- chart_top_products.png")

What does this code do?

The orient='h' parameter flips the bar chart sideways. Horizontal bars work better for product names because the labels are easier to read without rotation.

The print statements at the end confirm which files were saved, giving you a clear checklist of outputs.

  • Save analysis.py and run the full script one final time:
python analysis.py
  • Confirm all four chart files exist by running:
ls

You should see chart_revenue_by_category.png, chart_daily_revenue.png, chart_order_distribution.png, and chart_top_products.png in the listing.

View your charts

Double-click any PNG file in the VS Code Explorer sidebar to preview it directly in the editor. You can also open them with your system image viewer.

Only seeing some of the PNG files?

  • Check the terminal output for error messages. The script stops at the first error, so only charts above the failing line are created.
  • Each chart block must end with its own plt.close() before the next one starts.
  • If you see a TypeError about orient, make sure Chart 4 uses orient='h' as a keyword argument.

Help me debug my visualization code

✔️ Awesome, I've got everything!

Great. Double check you have saved analysis.py and all four PNG files are in your folder.

ⓧ I'd like to double check the full code

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# ============================================================
# STEP 2: Load and inspect the data
# ============================================================

df = pd.read_csv('sales_data.csv')

print("=== Dataset Overview ===")
print(f"Shape: {df.shape}")
print()
print(df.head())
print()
print(df.info())
print()
print(df.describe())

# ============================================================
# STEP 3: Clean and transform the data
# ============================================================

# Convert date column to datetime
df['date'] = pd.to_datetime(df['date'])

# Calculate revenue
df['revenue'] = df['price'] * df['quantity']

# Check for missing values
print("\n=== Missing Values ===")
print(df.isnull().sum())

# Remove any missing values and duplicates
df = df.dropna()
df = df.drop_duplicates()

# Add day of week
df['day_of_week'] = df['date'].dt.day_name()

print(f"\nCleaned dataset shape: {df.shape}")

# ============================================================
# STEP 4: Analyze the data with GroupBy
# ============================================================

# Revenue by category
category_revenue = df.groupby('category')['revenue'].sum().sort_values(ascending=False)
print("\n=== Revenue by Category ===")
print(category_revenue)

# Top 5 products by quantity sold
top_products = df.groupby('product')['quantity'].sum().sort_values(ascending=False).head(5)
print("\n=== Top 5 Products by Quantity ===")
print(top_products)

# Daily revenue
daily_revenue = df.groupby('date')['revenue'].sum()

# Summary report
total_revenue = df['revenue'].sum()
avg_order_value = df['revenue'].mean()
busiest_day = daily_revenue.idxmax().strftime('%A, %B %d')
top_category = category_revenue.index[0]

print("\n=== Summary Report ===")
print(f"Total Revenue: ${total_revenue:.2f}")
print(f"Average Order Value: ${avg_order_value:.2f}")
print(f"Busiest Day: {busiest_day}")
print(f"Top Category: {top_category}")

# ============================================================
# STEP 5: Visualize the findings
# ============================================================

sns.set_theme(style="whitegrid")

# Chart 1: Revenue by category (bar chart)
fig, ax = plt.subplots(figsize=(8, 5))
sns.barplot(x=category_revenue.index, y=category_revenue.values, ax=ax)
ax.set_title('Total Revenue by Product Category', fontsize=14, fontweight='bold')
ax.set_xlabel('Category')
ax.set_ylabel('Revenue ($)')
plt.tight_layout()
plt.savefig('chart_revenue_by_category.png', dpi=150)
plt.close()

# Chart 2: Daily revenue trend (line chart)
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(daily_revenue.index, daily_revenue.values, marker='o', linewidth=2, color='steelblue')
ax.set_title('Daily Revenue Trend', fontsize=14, fontweight='bold')
ax.set_xlabel('Date')
ax.set_ylabel('Revenue ($)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('chart_daily_revenue.png', dpi=150)
plt.close()

# Chart 3: Distribution of order values (histogram)
fig, ax = plt.subplots(figsize=(8, 5))
sns.histplot(df['revenue'], bins=10, kde=True, ax=ax)
ax.set_title('Distribution of Order Values', fontsize=14, fontweight='bold')
ax.set_xlabel('Order Value ($)')
ax.set_ylabel('Count')
plt.tight_layout()
plt.savefig('chart_order_distribution.png', dpi=150)
plt.close()

# Chart 4: Top 5 products (horizontal bar chart)
fig, ax = plt.subplots(figsize=(8, 5))
sns.barplot(x=top_products.values, y=top_products.index, ax=ax, orient='h')
ax.set_title('Top 5 Products by Quantity Sold', fontsize=14, fontweight='bold')
ax.set_xlabel('Quantity Sold')
ax.set_ylabel('Product')
plt.tight_layout()
plt.savefig('chart_top_products.png', dpi=150)
plt.close()

print("\n=== Visualizations saved! ===")
print("- chart_revenue_by_category.png")
print("- chart_daily_revenue.png")
print("- chart_order_distribution.png")
print("- chart_top_products.png")

Secret mission

Customer Spending Analysis

Your analysis so far looks at products and categories. But what about the customers themselves? In this bonus challenge, you'll segment customers into spending tiers and visualize how their behavior differs.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your files for future reference or delete them to free up disk space.

Resources you used:

  • Project folder on the Desktop (contains analysis.py, sales_data.csv, 6 chart PNG files, and the venv virtual environment).

Keep everything running

No action needed. Choose this if you plan to extend your analysis script or experiment with different visualizations.

  • Your project folder stays on the Desktop with all files intact.
  • The virtual environment stays ready. Reactivate it anytime to run your script again.

Pause - I'll come back to this later

Deactivate the virtual environment to free up your terminal session. Your files remain on disk for next time.

  • In your terminal, deactivate the virtual environment by running:
deactivate
  • Close VS Code when you are done. Your project folder and all files stay on disk.

Delete - I don't want to use this again

Remove the entire project folder to delete all files, charts, and the virtual environment.

  • If your virtual environment is still active, deactivate it first by running:
deactivate
  • Navigate to your Desktop and delete the project folder by running:

macOS

cd ~/Desktop
rm -rf [[PROJECT_FOLDER="your project folder name"]]

Windows

cd ~\Desktop
rmdir /s /q [[PROJECT_FOLDER="your project folder name"]]
  • Confirm the folder is gone by running:
ls

Your project folder should no longer appear in the output.

Nice Work!

Nice Work!

You just completed a real data analysis workflow from start to finish. You took raw coffee shop transaction data and transformed it into actionable business insights with professional visualizations.

You've learned how to:

  • Load, clean, and transform raw data using pandas DataFrames including handling missing values, duplicates, and type conversions.
  • Aggregate data with groupby to calculate business KPIs like total revenue, average order value, and best-selling products.
  • Create publication-quality visualizations using matplotlib and seaborn including bar charts, line charts, histograms, and horizontal bar charts.
  • Segmented customers into spending tiers using pd.cut() and visualized spending patterns with pie charts and box plots.

Ready to quiz yourself?