Analyze Superstore Sales with Pandas
Load 10,000 retail records, clean messy data, and extract business insights.
Introduction
30 Second Summary
Every business decision starts with data. Whether it's a store manager deciding which products to stock or a CEO choosing which region to expand into, the ability to turn raw numbers into clear answers is one of the most valuable skills you can have.
In this project, you will load 10,000 real retail sales records into Python, clean messy data that isn't in the right format, and create three charts that reveal which products make money, how sales trend over time, and which regions underperform.
What You'll Build
By the end, you'll run a single script that prints business insights to your terminal and saves three professional charts as PNG files you could drop into a presentation tomorrow.
By the end of this project, you'll have:
- A pandas script that loads a 10,000-row dataset and instantly shows you its shape, columns, and summary statistics in the terminal.
- Concrete answers to business questions like "which category is most profitable?" extracted using groupby aggregations on real sales data.
- Three publication-ready charts saved as PNG files: a category performance bar chart, a monthly sales trend line chart, and a styled regional profit chart.
- Secret Mission: A heatmap that reveals which specific product-region combinations are losing money.
Are there any prerequisites?
You'll need a free Kaggle account to download the dataset. Step 1 walks you through creating one if you don't have it already. Basic programming knowledge is helpful, but no prior Python or data analysis experience is required.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Your Python Environment
Before you can analyze any data, you need the right tools installed. Python's data analysis ecosystem relies on three key libraries: pandas for working with tabular data, matplotlib for creating charts, and seaborn for polished statistical graphics.
In this step, you'll set up an isolated Python environment and install these libraries. This keeps your project's dependencies separate from anything else on your machine.
In this step, get ready to:
- Check your Python version and create a virtual environment.
- Install pandas, matplotlib, and seaborn.
- Download the Superstore dataset from Kaggle.
Check your Python version
This project requires Python 3.11 or higher because pandas 3.0.5 only supports Python 3.11+.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal and press Enter to open it.
- Check your Python version by running this command:
python3 --version
✔️ I see version 3.11 or higher
You're all set. Move on to creating your project folder below.
ⓧ I see an older version
Your system has a version of Python below 3.11. macOS often ships with an older Python that python3 resolves to by default.
- Go to https://www.python.org/downloads/ and download the latest Python 3.11+ installer for macOS.
- Run the installer and follow the prompts.
- After installation, close and reopen Terminal.
- Run python3 --version again to confirm it now shows 3.11 or higher.
Why might python3 still show the old version?
macOS keeps its built-in Python at the system path. The python.org installer adds the new version, but your shell may still resolve the old one first. If python3 --version still shows the old version after reinstalling, try running python3.11 --version or python3.12 --version to confirm the new install exists. Use that specific binary for the rest of the project.
ⓧ Command not found
Python is not installed on your machine yet.
- Go to https://www.python.org/downloads/ and download the latest Python 3.11+ installer for macOS.
- Run the installer and follow the prompts.
- Close and reopen Terminal.
- Run python3 --version to confirm it shows 3.11 or higher.
Create your project folder and virtual environment
A virtual environment is an isolated copy of Python where you install project-specific packages. This prevents conflicts between projects that need different library versions.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir superstore-analysis
cd superstore-analysis
- Confirm you're inside the new folder by running:
pwd
You should see a path ending in /Desktop/superstore-analysis.
- Create the virtual environment by running:
python3 -m venv venv
- Confirm the virtual environment was created by running:
ls
You should see a venv folder listed in the output.
- Activate the virtual environment by running:
source venv/bin/activate
Your terminal prompt now shows (venv) at the beginning. This means any packages you install will live inside this environment only.
Don't see (venv) in your prompt?
- Make sure you ran source venv/bin/activate (not just venv/bin/activate without source).
- Confirm you're inside the superstore-analysis folder by running pwd. The venv folder must exist in your current directory.
- help me activate my Python virtual environment
Install the data analysis libraries
- Install pandas, matplotlib, and seaborn by running:
pip install pandas==3.0.5 matplotlib==3.11.1 seaborn==0.13.2
What does this command do?
- pip install downloads and installs packages from the Python Package Index (PyPI).
- The == syntax pins each library to an exact version. This ensures everyone following this project gets identical behavior.
- pandas handles tabular data, matplotlib draws charts, and seaborn adds polished themes and statistical plots on top of matplotlib.
You'll see download progress and a success message once installation completes. This may take a minute.
Before you run the next command, what do you expect to see if all three libraries installed correctly?
- Verify the imports work by running:
python3 -c "import pandas; import matplotlib; import seaborn; print('All good!')"
You should see All good! printed in your terminal. This confirms all three libraries are installed and importable.
Seeing an ImportError or ModuleNotFoundError?
- Make sure your virtual environment is active. You should see (venv) in your prompt. If not, run source venv/bin/activate and try the install command again.
- If you see a permissions error, confirm you're inside the superstore-analysis folder and the venv is active. You should never need sudo with a virtual environment.
- help me fix my pip install error for pandas, matplotlib, or seaborn
Download the dataset from Kaggle
The dataset you'll analyze is a real-world retail sales file hosted on Kaggle. It contains 9,994 rows of order transactions from a US superstore.
- Open https://www.kaggle.com/datasets/jacopoferretti/superstore-dataset in your browser.
- Sign in with your free Kaggle account. If you don't have one, create it now using your Google or email account.
- Click the Download button near the top of the dataset page. This downloads a ZIP file to your computer.
- Open your Downloads folder and extract (unzip) the ZIP file.
- Find the file called Sample - Superstore.csv inside the extracted folder.
- Move Sample - Superstore.csv into your superstore-analysis folder on the Desktop.
- Back in Terminal, confirm the file is in place by running:
ls
You should see both Sample - Superstore.csv and venv listed in the output.
Don't see the CSV file?
- Confirm you moved the file into ~/Desktop/superstore-analysis and not into the venv subfolder.
- The file name has spaces and a hyphen: Sample - Superstore.csv. Make sure you didn't rename it.
- help me find and move my Kaggle dataset into my project folder
Your environment is ready and your data is downloaded. Next up, you'll load the dataset into Python and explore what 10,000 rows of real sales data actually look like.
Load and Explore the Data
Your Python environment is set up with all the analysis libraries installed. Now it's time to open the dataset and see what 10,000 sales transactions actually look like.
Before running any analysis, you need to understand the data's shape, column types, and potential issues. Loading the CSV and exploring it with pandas gives you an instant picture of what you're working with.
In this step, get ready to:
- Load the Superstore CSV into a pandas DataFrame.
- Explore the dataset's shape, types, and statistics.
- Run your first groupby to answer a business question.
Create the analysis script and load the CSV
Every data analysis starts with loading the raw data into a DataFrame. A DataFrame is a table-like structure that lets you filter, group, and aggregate thousands of rows with a single line of code.
- Create a new file called analysis.py in your superstore-analysis folder and add this code:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# --- Load Data ---
df = pd.read_csv("Sample - Superstore.csv")
# --- Explore ---
print("=" * 50)
print("DATASET OVERVIEW")
print("=" * 50)
print(f"\nShape: {df.shape}")
What does this code do?
- import pandas as pd loads the pandas library and gives it the shorthand pd.
- pd.read_csv("Sample - Superstore.csv") reads the entire CSV file into a DataFrame called df.
- df.shape prints the number of rows and columns as a tuple, confirming the data loaded.
- Save analysis.py.
- Run the script in your terminal:
python3 analysis.py
- You should see Shape: (9994, 21) printed, confirming 9,994 rows and 21 columns loaded successfully.
Seeing a FileNotFoundError?
- Make sure Sample - Superstore.csv is in the same folder as analysis.py. The filename has spaces and a hyphen.
- Confirm your terminal is in the superstore-analysis folder by running ls and checking both files appear.
- help me fix a FileNotFoundError when loading my CSV
Explore the data structure
You know the dataset has 9,994 rows, but you don't yet know what's in those columns. Adding a few exploration prints reveals column types, sample rows, and summary statistics in one run.
- In analysis.py, add these lines directly below the print(f"\nShape: {df.shape}") line:
print(f"\nColumn types:\n{df.dtypes}")
print(f"\nFirst 5 rows:\n{df.head()}")
print(f"\nBasic statistics:\n{df.describe()}")
print(f"\nMissing values:\n{df.isnull().sum()}")
What does this code do?
- df.dtypes shows each column's data type. This is where you'll spot problems like dates stored as strings.
- df.head() shows the first 5 rows so you can see actual values.
- df.describe() calculates count, mean, min, max, and percentiles for all numeric columns.
- df.isnull().sum() counts missing values per column, revealing data quality issues.
- Save analysis.py and run it again:
python3 analysis.py
You should see a wall of useful information scroll past. Notice three things: Sales averages around $230, Profit can be negative (some products lose money), and Order Date shows as object type instead of a datetime.
Why does the Order Date type matter?
When a date column shows as object, pandas treats it as plain text. You won't be able to group by month or calculate time differences until you fix this. Keep it in mind for later.
Seeing a UnicodeDecodeError?
- Try adding encoding="latin-1" inside your pd.read_csv() call. Some systems read the file with a different character encoding.
- help me fix a UnicodeDecodeError when loading my CSV in pandas
Try a quick groupby to see category totals
A groupby splits your data into groups based on a column, then applies a calculation to each group. With one line, you can answer real business questions like "which product category generates the most revenue?"
- In analysis.py, add this code at the bottom of the file (below the missing values print):
# --- Early win: quick category groupby ---
print("\n" + "=" * 50)
print("QUICK LOOK: Sales by Category")
print("=" * 50)
print(df.groupby("Category")["Sales"].sum())
What does this code do?
- df.groupby("Category") splits the 9,994 rows into three groups: Furniture, Office Supplies, and Technology.
- ["Sales"].sum() sums the Sales column within each group, giving you total revenue per category.
- Save analysis.py.
Before you run this, what do you think the output will show? Will all three categories have roughly equal sales, or will one dominate?
- Run the script one more time:
python3 analysis.py
Scroll to the bottom of the output. Technology leads with the highest total sales. You just answered a business question with one line of pandas.
Not seeing the category output?
- Make sure the groupby code is at the very bottom of your file, after all the exploration prints.
- Check that the column name is exactly "Category" with a capital C. Column names are case-sensitive.
- help me fix my groupby code in pandas
✔️ Awesome, I've got everything!
Great. Double check you've saved analysis.py and move on to the next step.
ⓧ I'd like to double check the full code
Here is the complete analysis.py file at this point:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# --- Load Data ---
df = pd.read_csv("Sample - Superstore.csv")
# --- Explore ---
print("=" * 50)
print("DATASET OVERVIEW")
print("=" * 50)
print(f"\nShape: {df.shape}")
print(f"\nColumn types:\n{df.dtypes}")
print(f"\nFirst 5 rows:\n{df.head()}")
print(f"\nBasic statistics:\n{df.describe()}")
print(f"\nMissing values:\n{df.isnull().sum()}")
# --- Early win: quick category groupby ---
print("\n" + "=" * 50)
print("QUICK LOOK: Sales by Category")
print("=" * 50)
print(df.groupby("Category")["Sales"].sum())
Your script already answers a real business question, and you haven't written more than 22 lines of code. Next up, you'll try to break this data down by month and hit a wall that teaches you one of the most common data cleaning tasks.
Hit the Data Type Wall
In the last step, you loaded the dataset and ran your first groupby to see category totals. The natural next question is about time: how do sales change month over month?
To answer time-based questions, you need to group rows by their month. Let's try using the Order Date column for that and see what happens.
In this step, get ready to:
- Attempt a monthly sales groupby and hit an error.
- Diagnose why the Order Date column cannot be used for time operations.
- Add error handling that catches the problem gracefully.
Attempt monthly sales analysis
You already know which categories sell the most. Now you want to see how revenue changes over time, which means grouping sales by month.
- In analysis.py, add this code at the bottom of the file (below the category groupby):
# --- Designed obstacle: attempt monthly analysis ---
print("\n" + "=" * 50)
print("ATTEMPTING MONTHLY ANALYSIS...")
print("=" * 50)
monthly_attempt = df.groupby(df["Order Date"].dt.month)["Sales"].sum()
print(monthly_attempt)
What does this code do?
This code uses the .dt.month accessor to extract the month number from each Order Date value.
It then groups all sales by that month number and sums them up, which would give you monthly revenue totals.
Before you run this, think about what you expect to happen. Remember that in the previous step, the dtypes output showed Order Date as object type.
- Save analysis.py and run the script:
python3 analysis.py
Your script crashes with a traceback ending in:
AttributeError: Can only use .dt accessor with datetimelike values
The script printed all the previous exploration output, then crashed the moment it tried to use .dt.month on the Order Date column. This is the exact wall every data analyst hits: the data is not in the shape you need.
Why did this happen?
When pandas loads a CSV, it reads date-looking text as plain strings (dtype object).
The .dt accessor only works on columns that pandas recognises as datetime. Since Order Date is just text, pandas does not know how to extract a month from it.
Diagnose the problem and handle it gracefully
A crashing script is not useful for analysis. You want the script to detect the problem, explain what went wrong, and keep running so you can fix it in the next step.
- In analysis.py, find these two lines you just added:
monthly_attempt = df.groupby(df["Order Date"].dt.month)["Sales"].sum()
print(monthly_attempt)
- Replace those two lines with this code that prints the dtype and wraps the attempt in a try/except block:
print(f"Order Date dtype: {df['Order Date'].dtype}")
try:
monthly_attempt = df.groupby(df["Order Date"].dt.month)["Sales"].sum()
print(monthly_attempt)
except AttributeError as e:
print(f"ERROR: {e}")
print("Order Date is a string, not a datetime. We need to convert it!")
What does this code do?
The print(f"Order Date dtype: ...") line confirms the column type before attempting the operation.
The try/except block catches the AttributeError instead of letting the script crash. It prints a clear diagnostic message so you know exactly what to fix.
- Save analysis.py.
- What do you expect to see now when you run the script? Will it crash again, or handle the error?
- Run the script:
python3 analysis.py
This time the script does not crash. After the exploration output, you see:
ATTEMPTING MONTHLY ANALYSIS...
==================================================
Order Date dtype: object
ERROR: Can only use .dt accessor with datetimelike values
Order Date is a string, not a datetime. We need to convert it!
The script now names the exact problem: Order Date is a string, not a datetime. In the next step, you will fix this with a single pandas function.
Still seeing a crash instead of the handled error?
- Make sure you removed the bare monthly_attempt = ... and print(monthly_attempt) lines. They must be inside the try: block now, not duplicated above it.
- Check that except AttributeError as e: is at the same indentation level as try:.
- Confirm the two print lines inside except are indented with four spaces.
Why is my try/except not catching the error?
✔️ Awesome, I've got everything!
Great. Double check you have saved analysis.py before moving on.
ⓧ I'd like to double check the full code
Your analysis.py should have these sections in order. Scroll up and confirm each one is present:
- Imports (pandas, matplotlib, seaborn).
- Load Data section with pd.read_csv().
- Explore section printing shape, dtypes, head, describe, and missing values.
- Early win section with the Category groupby.
- Designed obstacle section that prints the dtype, then uses a try/except around the monthly groupby attempt.
You have identified the core problem: your dates are text, not datetimes. Next up, you will convert them with a single pandas function and unlock the monthly analysis that just failed.
Clean, Transform, and Analyze
In the last step, you hit a wall. Your monthly analysis attempt crashed because pandas cannot perform time-based operations on plain text strings.
Now you will fix the problem by converting those strings into proper datetime values. Once the dates are real dates, you can group by month, extract years, and answer business questions that were impossible before.
In this step, get ready to:
- Fix the date columns and create time features.
- Run groupby aggregations for category, region, and monthly performance.
- Identify the most profitable category and lowest-profit region.
Fix the date columns and create time features
The function pd.to_datetime() converts a column of strings into datetime64 values. This unlocks the .dt accessor that caused the error in the previous step.
- In analysis.py, find the except AttributeError block at the bottom of your file.
- Below the entire try/except block, add this code to convert the date columns and create derived time features:
# --- Clean and Transform ---
print("\n" + "=" * 50)
print("CLEANING DATA...")
print("=" * 50)
df["Order Date"] = pd.to_datetime(df["Order Date"])
df["Ship Date"] = pd.to_datetime(df["Ship Date"])
df["Order Year"] = df["Order Date"].dt.year
df["Order Month"] = df["Order Date"].dt.to_period("M")
print(f"Order Date dtype after conversion: {df['Order Date'].dtype}")
What does this code do?
- pd.to_datetime() parses the string dates (like "1/6/2014") into proper datetime objects that pandas can do math with.
- .dt.year extracts the year as an integer, and .dt.to_period("M") converts each date into a monthly period for grouping.
- The final print confirms the conversion worked by showing the new dtype.
- Save analysis.py and run your script:
python3 analysis.py
Scroll to the CLEANING DATA section of the output. You should see:
Order Date dtype after conversion: datetime64[ns]
The same .dt accessor that crashed before now works. The date columns are real dates.
Still seeing the old object dtype?
- Make sure your new code is placed after the try/except block, not inside it.
- Check that you are using pd.to_datetime() with the exact column name "Order Date" (capital O, capital D, with a space).
Help me debug my pd.to_datetime() conversion
Run the full analysis
With clean dates in place, you can now use groupby to split the data by category, region, or month. Each groupby answers a different business question.
- Below the code you just added, add this analysis section that groups the data three different ways:
# --- Analysis ---
print("\n" + "=" * 50)
print("ANALYSIS RESULTS")
print("=" * 50)
# Category performance
category_performance = df.groupby("Category")[["Sales", "Profit"]].sum().reset_index()
print("\nSales and Profit by Category:")
print(category_performance)
# Regional performance
region_performance = df.groupby("Region")[["Sales", "Profit"]].sum().reset_index()
print("\nSales and Profit by Region:")
print(region_performance)
# Monthly sales trend
monthly_sales = df.groupby("Order Month")["Sales"].sum().reset_index()
monthly_sales["Order Month"] = monthly_sales["Order Month"].dt.to_timestamp()
print("\nMonthly Sales Trend (first 12 months):")
print(monthly_sales.head(12))
What does this code do?
- groupby("Category")[["Sales", "Profit"]].sum() totals sales and profit for each product category (Furniture, Office Supplies, Technology).
- groupby("Region") does the same split by geographic region (Central, East, South, West).
- .reset_index() turns the grouped result back into a flat DataFrame with numbered rows.
- The monthly groupby uses the Order Month period column you just created. The .dt.to_timestamp() converts periods back to timestamps for plotting later.
- Save analysis.py and run the script again:
python3 analysis.py
Scroll to ANALYSIS RESULTS. You should see three printed DataFrames showing totals by Category, by Region, and by month.
Seeing a KeyError on Order Month?
- Make sure the analysis section comes after the Clean and Transform section. The "Order Month" column only exists after you create it with .dt.to_period("M").
- Confirm you spelled "Order Month" with a capital O and capital M.
Help me fix a KeyError in my groupby analysis
Identify key findings
Aggregated DataFrames are useful, but stakeholders want headline answers. You can use .idxmax() and .idxmin() to pull the top and bottom performers programmatically.
- Below your monthly sales code, add this section to extract and print the key findings:
# Key findings
print("\n" + "=" * 50)
print("KEY FINDINGS")
print("=" * 50)
most_profitable_category = category_performance.loc[category_performance["Profit"].idxmax(), "Category"]
print(f"Most profitable category: {most_profitable_category}")
lowest_profit_region = region_performance.loc[region_performance["Profit"].idxmin(), "Region"]
print(f"Lowest profit region: {lowest_profit_region}")
What does this code do?
- .idxmax() returns the row index of the highest value in the Profit column. Wrapping it in .loc[..., "Category"] grabs the category name from that row.
- .idxmin() does the reverse, finding the lowest profit region.
- These two lines turn your DataFrames into concrete, quotable findings.
- Save analysis.py.
Before you run the script, predict: which category do you think is most profitable, and which region do you think has the lowest profit?
- Run the final version of your script:
python3 analysis.py
Scroll to KEY FINDINGS at the bottom. You should see the most profitable category and the lowest profit region printed as clean, one-line answers.
Seeing an IndexError or empty output?
- Make sure the Key findings section is placed after the analysis section. It depends on the category_performance and region_performance DataFrames existing.
- Check that .reset_index() is included on your groupby lines. Without it, the column lookup behaves differently.
Help me debug my key findings extraction
✔️ Awesome, I've got everything!
Great. Double check you have saved analysis.py and your script runs without errors from top to bottom.
ⓧ I'd like to double check the full code
Your analysis.py should now contain these sections in order. Scroll up and confirm each one is present:
- Imports (pandas, matplotlib, seaborn).
- Load Data section with pd.read_csv().
- Explore section printing shape, dtypes, head, describe, and missing values.
- Early win: quick category groupby.
- Designed obstacle: the try/except block that catches the AttributeError.
- Clean and Transform section converting dates and creating Order Year and Order Month.
- Analysis section with three groupby operations (Category, Region, Order Month).
- Key findings section printing the most profitable category and lowest profit region.
Your data is cleaned, transformed, and analyzed. You have concrete answers to three business questions. Next up, you will turn those numbers into charts that tell a visual story.
Visualize Your Insights
Your data is clean, transformed, and analyzed. You have concrete answers about which categories drive profit, which regions lag behind, and how sales trend over time.
Numbers in a terminal don't tell a story. If you were presenting these findings to stakeholders, they'd want charts. In this step, you'll turn your aggregated DataFrames into three publication-ready visualizations saved as PNG files.
In this step, get ready to:
- Create a side-by-side bar chart comparing category Sales and Profit.
- Plot a monthly sales trend line chart showing revenue over time.
- Build a styled regional profit chart with seaborn and save all charts as PNG files.
Create a side-by-side bar chart for category performance
Your first chart compares Sales and Profit across the three product categories. A side-by-side layout lets viewers spot the gap between revenue and profitability at a glance.
- In analysis.py, scroll to the very bottom of the file (below the lowest_profit_region print statement).
- Add the following code to set up the seaborn theme and create your first chart:
# --- Visualizations ---
sns.set_theme(style="whitegrid")
# Chart 1: Category Performance (side-by-side bar charts)
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
category_performance.plot(
kind="bar", x="Category", y="Sales", ax=axes[0],
color="steelblue", legend=False
)
axes[0].set_title("Total Sales by Category")
axes[0].set_ylabel("Sales ($)")
axes[0].set_xlabel("")
axes[0].tick_params(axis="x", rotation=0)
category_performance.plot(
kind="bar", x="Category", y="Profit", ax=axes[1],
color="seagreen", legend=False
)
axes[1].set_title("Total Profit by Category")
axes[1].set_ylabel("Profit ($)")
axes[1].set_xlabel("")
axes[1].tick_params(axis="x", rotation=0)
plt.tight_layout()
plt.savefig("category_performance.png", dpi=150)
plt.show()
What does this code do?
sns.set_theme(style="whitegrid") applies a clean grid background to all charts that follow.
- plt.subplots(1, 2) creates a figure with two side-by-side panels (one row, two columns).
- Each .plot(kind="bar") call draws a bar chart on one panel using the category_performance DataFrame from your analysis step.
- plt.savefig() saves the chart as a PNG file before plt.show() displays it on screen.
- Save analysis.py.
- Run the script to see your first chart:
python3 analysis.py
After the terminal output scrolls past, a chart window appears showing two bar charts side by side. Technology leads in Sales, and the Profit chart reveals which categories actually keep the most money. Close the chart window to let the script finish.
No chart window appearing?
- If you see a warning about a non-interactive backend, your matplotlib may be missing the Tkinter dependency. On macOS, reinstall Python from python.org (which bundles Tkinter) rather than using the system Python.
- Make sure you closed any previous chart windows. Each plt.show() call blocks until you close the current window.
Why isn't my matplotlib chart window showing up?
Create a monthly sales trend line chart
A line chart is the natural choice for time-series data. It shows how revenue rises and falls month-over-month, making seasonal patterns visible.
- In analysis.py, add the following code directly below the first chart's plt.show() line:
# Chart 2: Monthly Sales Trend (line chart)
plt.figure(figsize=(12, 5))
plt.plot(
monthly_sales["Order Month"], monthly_sales["Sales"],
color="steelblue", linewidth=1.5
)
plt.title("Monthly Sales Trend")
plt.xlabel("Month")
plt.ylabel("Sales ($)")
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig("monthly_sales_trend.png", dpi=150)
plt.show()
What does this code do?
- plt.figure() creates a fresh figure (separate from the previous subplot figure).
- plt.plot() draws a continuous line connecting each month's total Sales value.
- plt.xticks(rotation=45) rotates the date labels so they don't overlap on the x-axis.
- Save analysis.py and run it again:
python3 analysis.py
This time you see two chart windows in sequence. Close the category chart and the monthly trend line chart appears. You can spot spikes and dips in revenue across the dataset's time range. Close it to continue.
Chart looks empty or flat?
- Make sure the monthly_sales["Order Month"] = monthly_sales["Order Month"].dt.to_timestamp() line exists in your analysis section above. Without it, the x-axis can't plot Period objects as dates.
- Check that this code sits below the first chart's plt.show() line, not inside the subplots block.
Why is my line chart not rendering correctly?
Create a styled regional profit chart with seaborn
Your third chart uses seaborn directly for a polished bar chart. Seaborn handles color, spacing, and styling with less code than raw matplotlib.
- In analysis.py, add this code below the second chart's plt.show() line:
# Chart 3: Regional Profit (seaborn bar chart)
plt.figure(figsize=(8, 5))
sns.barplot(data=region_performance, x="Region", y="Profit", color="steelblue")
plt.title("Total Profit by Region")
plt.ylabel("Profit ($)")
plt.xlabel("")
plt.tight_layout()
plt.savefig("region_profit.png", dpi=150)
plt.show()
print("\nAnalysis complete! Charts saved as PNG files.")
What does this code do?
- sns.barplot() creates a bar chart with seaborn's built-in styling. You pass the DataFrame directly and name the x and y columns.
- The final print() statement confirms the script finished and all three charts are saved.
- Save analysis.py.
Before you run the script, think about what you expect to see. How many chart windows will appear, and in what order?
- Run the full script:
python3 analysis.py
Three chart windows appear one after another as you close each one. The category bar charts come first, then the monthly trend line, then the regional profit bars. After you close the last chart, the terminal prints:
Analysis complete! Charts saved as PNG files.
- Confirm the PNG files were saved by running:
ls *.png
You should see category_performance.png, monthly_sales_trend.png, and region_profit.png listed in the output.
Missing PNG files?
- Make sure plt.savefig() appears BEFORE plt.show() in each chart block. Calling plt.show() first clears the figure, leaving nothing to save.
- Check that you're running the script from inside your superstore-analysis folder. The PNGs save to whatever directory you run the command from.
Why aren't my PNG files being saved?
✔️ Awesome, I've got everything!
Great. Double check you've saved your file.
ⓧ I'd like to double check the full code
Scroll through your analysis.py and confirm these sections are present in order, below the KEY FINDINGS print statements:
- The sns.set_theme(style="whitegrid") line and the Chart 1 subplot block (category performance bar charts).
- The Chart 2 block (monthly sales trend line chart).
- The Chart 3 block (regional profit seaborn bar chart) followed by the final print("\nAnalysis complete! Charts saved as PNG files.") line.
Secret mission
Build a Profitability Heatmap
Your bar charts show one dimension at a time. A heatmap reveals patterns hiding in two dimensions: which specific product-region combinations are bleeding money? Time to find out.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- The superstore-analysis folder on your Desktop (contains analysis.py, Sample - Superstore.csv, four PNG chart files, and the venv virtual environment).
- Python packages installed inside the virtual environment (pandas, matplotlib, seaborn).
Keep everything running
No action needed. Choose this if you're still actively building or want to revisit your analysis later.
- Your project folder stays on your Desktop with all files intact.
- Next time you want to run the script, open Terminal, navigate to the folder, and activate the virtual environment with source venv/bin/activate.
Pause - I'll come back to this later
Shut down any running processes and deactivate the virtual environment to free up terminal resources.
- If your virtual environment is still active, deactivate it by running:
deactivate
- Your project folder and all files remain on your Desktop.
- To resume later, navigate back to superstore-analysis and run source venv/bin/activate to reactivate.
Delete - I don't want to use this again
Remove all project files from your machine. This deletes your script, dataset, charts, and the virtual environment.
- If your virtual environment is still active, deactivate it first by running:
deactivate
- Delete the entire project folder by running:
macOS
rm -rf ~/Desktop/superstore-analysis
Windows
rmdir /s /q %USERPROFILE%\Desktop\superstore-analysis
- Confirm the folder is gone by checking your Desktop.
Nice Work!
Nice Work!
Nice work! You just built a complete data analysis pipeline that takes raw retail sales data, cleans it, answers real business questions, and produces publication-ready charts.
You've learned how to:
- Load and clean a 10,000-row retail dataset with pandas, fixing data type issues (strings stored as dates) using pd.to_datetime() to unlock time-based analysis.
- Use groupby aggregations to answer business questions about categories, regions, and monthly sales trends from 10,000+ transactions.
- Create three styled visualizations (bar charts and line charts) using matplotlib and seaborn, and save them as PNG files ready for stakeholder presentations.
- Secret Mission: Built a pivot table heatmap revealing which product-region combinations lose money.
Ready to quiz yourself?