Analyze Real Business Data with Python
Turn 9,994 retail transactions into actionable business insights using pandas.
Introduction
30 Second Summary
Every retail business sits on thousands of transactions. The companies that turn that raw data into decisions are the ones that grow.
In this project, you will analyze 9,994 real retail transactions using Python to uncover which products make money, which regions are thriving, and where the business is bleeding profit.
What You'll Build
A complete exploratory data analysis of a real retail Superstore dataset using Python, producing professional visualizations and written business recommendations.
By the end of this project, you'll have:
- A working Python analysis script that loads, cleans, and explores 9,994 real retail transactions.
- A 4-panel business dashboard saved as a PNG image showing sales trends, category performance, and regional profitability.
- A set of data-backed business recommendations derived from statistical patterns in the data.
- Secret Mission: Uncover "The Discount Trap" by analyzing the hidden relationship between discount levels and profit destruction.
Are there any prerequisites?
Basic Excel familiarity is helpful. You should also have Python installed on your Windows machine. If you do not have Python yet, Step 1 will guide you through verifying your installation.
Before We Start
Every data analyst starts the same way: gather the tools, get the data, and set up a clean workspace.
In this step, you will confirm your Python installation, install the analysis libraries, download a real retail dataset, and create the file where all your analysis code will live.
In this step, get ready to:
- Verify Python is installed and install pandas, matplotlib, and seaborn.
- Download the Superstore dataset from Kaggle.
- Create your project folder and analysis file.
Verify Python installation and install libraries
Python is the engine that powers your entire analysis. Before writing any code, you need to confirm it is installed and at the right version.
- Press the Windows key to open your search bar.
- Type VS Code and press Enter to open it.
- Open a terminal inside VS Code by clicking Terminal in the top menu bar, then clicking New Terminal.
- Check your Python version by running this command:
python --version
Look at the version number that appears in your terminal.
✔️ I see version 3.11 or higher
Your Python version is compatible. You are ready to install the analysis libraries.
- Install pandas, matplotlib, and seaborn by running this command:
pip install pandas matplotlib seaborn
What does this command do?
`pip` is Python's package installer. This single command downloads and installs three libraries: pandas for data manipulation, matplotlib for creating charts, and seaborn for statistical visualizations.
You should see output ending with a success message confirming the packages were installed.
ⓧ I see a version lower than 3.11
Your Python version works, but the latest pandas requires 3.11+. You can still complete this project by pinning pandas to an older compatible version.
- Install the libraries with a compatible pandas version by running this command:
pip install pandas==2.2.3 matplotlib seaborn
Why pin pandas to 2.2.3?
The latest pandas (3.0.3) requires Python 3.11 or higher. Version 2.2.3 supports Python 3.9+ and has the same features you need for this project.
You should see output ending with a success message confirming the packages were installed.
ⓧ Command not found
Python is not installed or not added to your system PATH. You need to install it before continuing.
- Go to python.org/downloads in your browser.
- Click the large Download Python button at the top of the page.
- Run the downloaded installer.
- On the first installer screen, check the box that says Add python.exe to PATH. This is critical.
- Click Install Now.
- After installation completes, close and reopen VS Code.
- Open a new terminal (Terminal > New Terminal) and run python --version again to confirm it works.
Once you see a version number, install the analysis libraries by running this command:
pip install pandas matplotlib seaborn
You should see output ending with a success message confirming the packages were installed.
Seeing a permission error during install?
Try running the install command with the --user flag instead. This installs the packages to your user directory, which avoids permission issues.
pip install --user pandas matplotlib seaborn
If you are still stuck, help me fix my pip install error.
Download the dataset
You will analyze a real retail dataset with 9,994 transactions from a US Superstore. The dataset lives on Kaggle, a free platform where data scientists share datasets.
- Go to the Superstore dataset page on Kaggle.
- If you do not have a Kaggle account, click Register to create a free account. You can sign up with Google.
- Once signed in, click the Download button near the top of the page.
- A ZIP file will download. Extract it and locate the file named Sample - Superstore.csv.
What is this dataset?
The Superstore dataset contains 9,994 retail transactions across 21 columns including Sales, Profit, Region, Category, and Customer Segment. It is one of the most widely-used datasets for learning data analysis because it has real business complexity at a manageable size.
Now create your project folder and move the dataset into it.
- In your VS Code terminal, navigate to your Desktop by running this command:
cd ~/Desktop
- Create your project folder by running this command:
mkdir superstore-analysis
- Open File Explorer and navigate to the superstore-analysis folder on your Desktop.
- Move the Sample - Superstore.csv file from your Downloads folder into the superstore-analysis folder.
- Right-click the file, select Rename, and change the name to superstore.csv.
Why rename the file?
The original filename has spaces, which can cause path issues in code. Renaming to superstore.csv keeps your code clean and avoids quoting headaches.
- Confirm the file is in place by running this command:
ls superstore-analysis
You should see superstore.csv in the output.
Don't see the file listed?
Make sure you moved and renamed the file before running the ls command. Double-check that you are on the Desktop in your terminal by running cd ~/Desktop first.
If you are still stuck, help me find my downloaded CSV file.
Create your analysis file
Now you will open your project folder in VS Code and create the Python script that will hold all your analysis code.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the superstore-analysis folder, and click Select Folder.
- If VS Code asks whether you trust the authors of the files in this folder, click Yes, I trust the authors.
You should see superstore.csv listed in the Explorer panel on the left side of VS Code.
- Create a new file by clicking the New File icon at the top of the Explorer panel (it looks like a page with a plus sign).
- Name the file analysis.py and press Enter.
Your new file opens in the editor. Now add your library imports.
- Add the following imports to analysis.py:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
What do these imports do?
- `pandas as pd` gives you DataFrames for loading and manipulating tabular data (think of it as a programmable spreadsheet).
- `matplotlib.pyplot as plt` provides the low-level charting engine for creating plots and saving them as images.
- `seaborn as sns` adds statistical visualization styles and chart types on top of matplotlib.
- Save the file by pressing Ctrl+S.
- Open a new terminal (Terminal > New Terminal) and verify your imports work by running this command:
python analysis.py
The script should run silently with no errors and no output. If you see nothing happen and return to the prompt, everything is working correctly.
Seeing a ModuleNotFoundError?
This means one of the libraries did not install correctly. Run the install command again from your project terminal. Make sure you are using the same python that pip installed to. Try running:
pip install pandas matplotlib seaborn
If you are still stuck, help me fix my ModuleNotFoundError.
✔️ Awesome, I've got everything!
Great. Double check you have saved analysis.py and that it ran without errors.
ⓧ 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
Your workspace is ready. Next up, you will load the dataset and start exploring what 9,994 transactions can tell you about this business.
Load and Inspect Your Data
You have a CSV file with nearly 10,000 rows of retail data sitting in your project folder. Before you can answer any business questions, you need to understand what you're working with.
In this step, you'll load the dataset into pandas and run a series of inspection commands. This is the equivalent of opening a spreadsheet and scanning the columns, rows, and data types.
In this step, get ready to:
- Load the Superstore CSV into a pandas DataFrame.
- Inspect the shape, data types, and basic statistics.
- Check for missing values, duplicates, and column cardinality.
Load the CSV and explore basic statistics
A DataFrame is the core data structure in pandas. Think of it as a spreadsheet that lives inside your Python script, with rows and columns you can filter, sort, and aggregate programmatically.
- In analysis.py, add the following code below your import statements to load the dataset and print its basic properties:
# --- Step 2: Load and Inspect ---
df = pd.read_csv("superstore.csv", encoding="latin-1")
print("Dataset Shape:", df.shape)
print("\nFirst 5 Rows:")
print(df.head())
print("\nData Types:")
print(df.dtypes)
print("\nBasic Statistics:")
print(df.describe())
What does this code do?
pd.read_csv("superstore.csv", encoding="latin-1") reads your CSV file into a DataFrame called df. The encoding="latin-1" parameter tells pandas how to decode the file's text characters. Without it, you might see a UnicodeDecodeError because this CSV was created on Windows.
.shape tells you how many rows and columns exist. .head() shows the first 5 rows so you can see what the data looks like. .dtypes reveals the data type of each column. .describe() gives you summary statistics (mean, min, max) for every numeric column.
- Save analysis.py.
- Run the script in your terminal by running:
python analysis.py
- You should see Dataset Shape: (9994, 21) printed first, followed by the first 5 rows of data, column types, and statistics for Sales, Quantity, Discount, and Profit.
Seeing a UnicodeDecodeError?
If you see an error mentioning that a byte cannot be decoded, double check that you included encoding="latin-1" inside your pd.read_csv() call. This parameter is required because the Superstore CSV contains characters that the default UTF-8 decoder cannot handle.
If you see FileNotFoundError, make sure superstore.csv is in the same folder as analysis.py. help me fix my CSV loading error
Check data quality
Before you trust any dataset, you need to check for holes and duplicates. Missing values can silently distort your calculations, and duplicate rows can inflate your totals.
- Add the following code below your print(df.describe()) line to check for data quality issues:
print("\nMissing Values:")
print(df.isnull().sum())
print("\nDuplicate Rows:", df.duplicated().sum())
What does this code do?
.isnull().sum() counts how many missing (blank) values exist in each column. If any column has missing data, you would need to decide how to handle it before running calculations.
.duplicated().sum() counts how many rows are exact copies of another row. Duplicates would artificially inflate sales totals and profit numbers.
- Save analysis.py and run it again:
python analysis.py
- Scroll to the bottom of the output. You should see all zeros for missing values and Duplicate Rows: 0. This dataset is clean.
Output is too long to read?
Your terminal might not show all the output at once. Scroll up in your terminal to see everything from the beginning, or widen your terminal window so columns display cleanly. help me read my pandas output in the terminal
Explore column cardinality
Cardinality tells you how many unique values each column contains. This reveals the key business dimensions you can group and filter by in your analysis.
- Add this code below your df.duplicated().sum() line to print the unique value counts:
print("\nUnique Values per Column:")
print(df.nunique())
What does this tell you?
.nunique() counts the distinct values in every column. Look for the columns with small numbers of unique values. These are your key business dimensions: 3 Categories, 17 Sub-Categories, 4 Regions, 3 Segments, and 793 unique Customers.
Columns with low cardinality are perfect for grouping and comparing ("show me profit by Region"). Columns with high cardinality (like Product Name with 1,862 values) are better for filtering to specific items.
- Save analysis.py and run it one more time:
python analysis.py
- At the very bottom of the output, you should see the unique value count for each of the 21 columns. Confirm you see Category with 3 unique values, Region with 4, and Segment with 3.
✔️ Awesome, I've got everything!
Great. Double check you've saved analysis.py before moving on.
ⓧ 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
# --- Step 2: Load and Inspect ---
df = pd.read_csv("superstore.csv", encoding="latin-1")
print("Dataset Shape:", df.shape)
print("\nFirst 5 Rows:")
print(df.head())
print("\nData Types:")
print(df.dtypes)
print("\nBasic Statistics:")
print(df.describe())
print("\nMissing Values:")
print(df.isnull().sum())
print("\nDuplicate Rows:", df.duplicated().sum())
print("\nUnique Values per Column:")
print(df.nunique())
Your data is loaded, verified clean, and you understand its structure. Next up, you'll engineer new features from the raw columns to unlock deeper analysis.
Clean and Engineer Features
In the previous step, you loaded the Superstore dataset and inspected its structure. You discovered 9,994 rows across 21 columns, with key fields like Sales, Profit, and Discount ready for analysis.
Raw data rarely arrives in a form you can analyze directly. The Order Date and Ship Date columns are stored as plain text strings, which means pandas cannot perform time-based calculations on them. In this step, you will convert those strings into proper dates and create new columns that make the data easier to analyze.
In this step, get ready to:
- Convert date columns from text to datetime objects.
- Engineer four new columns for time and profitability analysis.
- Verify your transformations with summary statistics.
Convert date columns to datetime
Date columns stored as text cannot be used for time-based math like calculating shipping speed or grouping by year. Converting them to datetime objects unlocks these calculations.
- In analysis.py, scroll to the bottom of the file (below the last print(df.nunique()) line).
- Add the following code to convert the date columns and create three new time-based columns:
# --- Step 3: Clean and Engineer Features ---
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.month
df["Shipping Days"] = (df["Ship Date"] - df["Order Date"]).dt.days
What does this code do?
pd.to_datetime() converts the text strings in Order Date and Ship Date into proper date objects that pandas can do math with.
- .dt.year extracts just the year (e.g. 2014, 2015) from each order date.
- .dt.month extracts the month number (1 through 12) so you can spot seasonal patterns.
- Subtracting Order Date from Ship Date produces a time difference. Accessing .dt.days converts that into a plain integer (number of days to ship).
- Save analysis.py and run it in your terminal:
python analysis.py
- You should see the same Step 2 output as before, with no new errors appearing. The date conversions and new columns are created silently.
Seeing a ParserError or ValueError?
- Make sure you renamed the original Kaggle file to superstore.csv (no spaces in the filename). A file named Sample - Superstore.csv would require a different path in your code.
- Check that you spelled the column names exactly as they appear in the dataset: Order Date and Ship Date (with a space, capital letters).
Why is my date conversion failing?
Create the profit margin column
Raw profit numbers are hard to compare across products with different price points. A profit margin percentage lets you compare profitability on equal footing.
- Add this code below the Shipping Days line to create the margin column and preview all your new features:
df["Profit Margin"] = (df["Profit"] / df["Sales"]) * 100
print("\nNew Columns Preview:")
print(df[["Order Date", "Order Year", "Order Month", "Shipping Days", "Profit Margin"]].head())
What does this code do?
The formula (Profit / Sales) * 100 converts the raw profit value into a percentage of the sale price. A product that sells for $100 with $20 profit has a 20% margin.
The .head() call prints the first 5 rows of just your new columns, so you can quickly confirm the values look reasonable.
- Save analysis.py and run python analysis.py again.
- After the Step 2 output, you should see a table showing Order Date, Order Year, Order Month, Shipping Days, and Profit Margin for the first 5 rows.
Seeing NaN or inf values in Profit Margin?
- If any row has a Sales value of 0, dividing by it produces infinity. This dataset should not have zero-sales rows, but if you see inf values, your CSV file may be corrupted. Re-download it from Kaggle.
- Make sure the column names Profit and Sales are capitalized exactly as shown.
Why am I seeing unexpected values in my Profit Margin column?
Print a summary of your new features
- Add these print statements below the .head() line to confirm your transformations with concrete numbers:
print(f"\nAverage Shipping Days: {df['Shipping Days'].mean():.1f}")
print(f"Average Profit Margin: {df['Profit Margin'].mean():.1f}%")
print(f"Data covers: {df['Order Year'].min()} to {df['Order Year'].max()}")
What does this code do?
These f-strings calculate and display three key metrics from your new columns. The :.1f format rounds each number to one decimal place for readability.
- Average Shipping Days tells you how fast orders typically arrive.
- Average Profit Margin tells you the overall business health as a percentage.
- The year range confirms your data spans multiple years, which means you can analyze trends over time.
- Save analysis.py and run python analysis.py one more time.
- At the bottom of your output, you should see the average shipping days (around 4 days), an average profit margin percentage, and a data range from 2014 to 2017.
Not seeing the summary stats?
- Make sure these three print lines are at the very bottom of your file, below the .head() print statement.
- Check for mismatched quotes. Python f-strings use single quotes inside when the outer string uses double quotes (or vice versa).
Why aren't my summary statistics printing?
✔️ Awesome, I've got everything!
Great. Double check you have saved analysis.py and confirmed the summary output looks correct.
ⓧ I'd like to double check the full code
Here is the complete analysis.py file up to this point. Compare your file against it:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# --- Step 2: Load and Inspect ---
df = pd.read_csv("superstore.csv", encoding="latin-1")
print("Dataset Shape:", df.shape)
print("\nFirst 5 Rows:")
print(df.head())
print("\nData Types:")
print(df.dtypes)
print("\nBasic Statistics:")
print(df.describe())
print("\nMissing Values:")
print(df.isnull().sum())
print("\nDuplicate Rows:", df.duplicated().sum())
print("\nUnique Values per Column:")
print(df.nunique())
# --- Step 3: Clean and Engineer Features ---
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.month
df["Shipping Days"] = (df["Ship Date"] - df["Order Date"]).dt.days
df["Profit Margin"] = (df["Profit"] / df["Sales"]) * 100
print("\nNew Columns Preview:")
print(df[["Order Date", "Order Year", "Order Month", "Shipping Days", "Profit Margin"]].head())
print(f"\nAverage Shipping Days: {df['Shipping Days'].mean():.1f}")
print(f"Average Profit Margin: {df['Profit Margin'].mean():.1f}%")
print(f"Data covers: {df['Order Year'].min()} to {df['Order Year'].max()}")
Your data is now clean and enriched with four new analysis-ready columns. Next up, you will use these features to answer real business questions about category performance, regional profitability, and growth trends.
Answer Business Questions with Data
Your data is cleaned, enriched, and ready for analysis. You have new columns like Profit Margin and Shipping Days that transform raw numbers into meaningful metrics.
Now comes the core of any data analyst's job: asking specific business questions and extracting answers from the data. In this step, you'll use pandas groupby aggregation to slice the Superstore dataset by category, region, product line, and customer segment.
In this step, get ready to:
- Find which product categories generate the most sales and profit.
- Identify the most profitable and money-losing sub-categories.
- Compare customer segments and track monthly sales trends over time.
Analyze category and region performance
The first question any business asks is: which product categories make the most money? The second is: which regions are most profitable?
- In analysis.py, scroll to the bottom of the file (after the feature engineering print statements).
- Add the section header and first business question by copying this code:
# --- Step 4: Answer Business Questions ---
print("\n" + "=" * 50)
print("BUSINESS ANALYSIS")
print("=" * 50)
# Question 1: Sales and Profit by Category
print("\n--- Sales and Profit by Category ---")
category_performance = df.groupby("Category")[["Sales", "Profit"]].sum().sort_values("Sales", ascending=False)
print(category_performance)
What does this code do?
The separator lines (= * 50) create a visual break in your terminal output so you can quickly find where the business analysis starts.
The .groupby("Category") splits your 9,994 rows into groups based on each unique Category value. Then .sum() adds up the Sales and Profit columns within each group, and .sort_values("Sales", ascending=False) ranks them from highest to lowest.
- You won't run the script yet. The next chunk adds Question 2, then you'll run both together.
- Directly below the Question 1 code, add the region profit analysis:
# Question 2: Profit by Region
print("\n--- Profit by Region ---")
region_profit = df.groupby("Region")["Profit"].sum().sort_values(ascending=False)
print(region_profit)
What does this code do?
This groups all transactions by Region and sums up the Profit column only. Sorting in descending order reveals which geographic region generates the most profit for the business.
- Save analysis.py and run your script:
python analysis.py
- Scroll to the bottom of your terminal output. You should see a BUSINESS ANALYSIS header followed by a table showing 3 categories (Technology, Furniture, Office Supplies) with their Sales and Profit totals, then 4 regions ranked by profit.
Don't see the business analysis output?
- Make sure your new code is placed after the Step 3 code, not inside it. The section header (# --- Step 4: Answer Business Questions ---) should be at the very bottom of the file.
- Check that column names match exactly: "Category", "Sales", "Profit", "Region" with capital letters. These must match the original CSV column headers.
- If you see a KeyError, double-check the spelling of your column names against what .dtypes printed in Step 2.
My groupby code is throwing an error. What's wrong?
Find the most and least profitable products
Knowing which categories perform well is useful, but a business needs to drill deeper. Which specific product lines should they invest in, and which ones are actively losing money?
- Below the Question 2 code, add the sub-category profit analysis:
# Question 3: Top and Bottom Sub-Categories
print("\n--- Top 5 Sub-Categories by Profit ---")
subcat_profit = df.groupby("Sub-Category")["Profit"].sum().sort_values(ascending=False)
print(subcat_profit.head())
print("\n--- Bottom 5 Sub-Categories by Profit (Money Losers!) ---")
print(subcat_profit.tail(5))
What does this code do?
This groups by Sub-Category (17 unique product lines) and sums profit for each one. Sorting descending means .head() gives you the top 5 most profitable, while .tail(5) reveals the 5 biggest money losers.
This is the kind of insight that directly drives business decisions. A product line that consistently loses money might need a pricing change or discontinuation.
- Save analysis.py and run python analysis.py again.
- Scroll to the bottom of the output. You should see a list of the Top 5 sub-categories (like Copiers, Phones, Accessories) and the Bottom 5 (like Tables, Bookcases, Supplies) with negative profit values.
Seeing unexpected sub-category names?
The exact ranking depends on the dataset version, but you should see some sub-categories with negative profit totals in the bottom 5. If all values are positive, check that you used "Profit" (not "Sales") in the groupby.
My sub-category analysis isn't showing money-losing products. What did I do wrong?
Compare customer segments and track sales over time
The final business question combines two angles: which customer type is most valuable, and is the business growing over time?
- Below the Question 3 code, add the segment and trend analysis:
# Question 4: Segment Performance and Sales Trend
print("\n--- Performance by Customer Segment ---")
segment_performance = df.groupby("Segment")[["Sales", "Profit"]].sum().sort_values("Profit", ascending=False)
print(segment_performance)
monthly_sales = df.groupby(df["Order Date"].dt.to_period("M"))["Sales"].sum()
print(f"\nSales Trend: {monthly_sales.iloc[0]:.0f} (first month) -> {monthly_sales.iloc[-1]:.0f} (last month)")
What does this code do?
The first part groups by Segment (Consumer, Corporate, Home Office) and sums Sales and Profit for each. This reveals which customer type generates the most value.
The second part uses .dt.to_period("M") to convert each Order Date into a monthly period, then sums Sales per month. Comparing the first and last month tells you whether the business is growing.
- Save analysis.py and run python analysis.py one more time.
- At the bottom of the output, you should see the three customer segments ranked by profit, followed by a Sales Trend line showing the first month's sales vs the last month's sales.
Getting a TypeError on the monthly sales line?
- This usually means Order Date was not converted to datetime in Step 3. Check that your pd.to_datetime() line is present and runs before this code.
- Make sure you're using df["Order Date"].dt.to_period("M") with a capital M for monthly period.
My monthly sales trend is throwing an error. Help me debug it.
✔️ Awesome, I've got everything!
Great. Double check you've saved analysis.py and your terminal is showing all 4 business questions with formatted output.
ⓧ I'd like to double check the full code
Here is the full analysis.py up to this point:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# --- Step 2: Load and Inspect ---
df = pd.read_csv("superstore.csv", encoding="latin-1")
print("Dataset Shape:", df.shape)
print("\nFirst 5 Rows:")
print(df.head())
print("\nData Types:")
print(df.dtypes)
print("\nBasic Statistics:")
print(df.describe())
print("\nMissing Values:")
print(df.isnull().sum())
print("\nDuplicate Rows:", df.duplicated().sum())
print("\nUnique Values per Column:")
print(df.nunique())
# --- Step 3: Clean and Engineer Features ---
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.month
df["Shipping Days"] = (df["Ship Date"] - df["Order Date"]).dt.days
df["Profit Margin"] = (df["Profit"] / df["Sales"]) * 100
print("\nNew Columns Preview:")
print(df[["Order Date", "Order Year", "Order Month", "Shipping Days", "Profit Margin"]].head())
print(f"\nAverage Shipping Days: {df['Shipping Days'].mean():.1f}")
print(f"Average Profit Margin: {df['Profit Margin'].mean():.1f}%")
print(f"Data covers: {df['Order Year'].min()} to {df['Order Year'].max()}")
# --- Step 4: Answer Business Questions ---
print("\n" + "=" * 50)
print("BUSINESS ANALYSIS")
print("=" * 50)
# Question 1: Sales and Profit by Category
print("\n--- Sales and Profit by Category ---")
category_performance = df.groupby("Category")[["Sales", "Profit"]].sum().sort_values("Sales", ascending=False)
print(category_performance)
# Question 2: Profit by Region
print("\n--- Profit by Region ---")
region_profit = df.groupby("Region")["Profit"].sum().sort_values(ascending=False)
print(region_profit)
# Question 3: Top and Bottom Sub-Categories
print("\n--- Top 5 Sub-Categories by Profit ---")
subcat_profit = df.groupby("Sub-Category")["Profit"].sum().sort_values(ascending=False)
print(subcat_profit.head())
print("\n--- Bottom 5 Sub-Categories by Profit (Money Losers!) ---")
print(subcat_profit.tail(5))
# Question 4: Segment Performance and Sales Trend
print("\n--- Performance by Customer Segment ---")
segment_performance = df.groupby("Segment")[["Sales", "Profit"]].sum().sort_values("Profit", ascending=False)
print(segment_performance)
monthly_sales = df.groupby(df["Order Date"].dt.to_period("M"))["Sales"].sum()
print(f"\nSales Trend: {monthly_sales.iloc[0]:.0f} (first month) -> {monthly_sales.iloc[-1]:.0f} (last month)")
You now have hard data answering 4 real business questions. Next up, you'll turn these numbers into a professional 4-panel dashboard that communicates your findings visually.
Visualize Patterns and Present Findings
Your analysis has already uncovered which categories, regions, and segments drive profit. Numbers in a table are powerful, but a visual dashboard makes patterns instantly obvious to stakeholders who don't want to read raw data.
In this step, you'll turn your findings into a professional 4-panel dashboard using matplotlib and seaborn. You'll also write formatted business recommendations that translate data patterns into actions.
In this step, get ready to:
- Build a 2x2 dashboard with bar charts and a trend line.
- Save the dashboard as a PNG image.
- Write data-backed business recommendations.
Create your dashboard figure and first two charts
A subplot grid lets you arrange multiple charts in one image. You'll create a 2x2 grid and fill the top row with a category comparison and a sales trend line.
- In analysis.py, scroll to the very bottom of your file.
- Add the following code below the last print statement from Step 4:
# --- Step 5: Visualize Patterns ---
sns.set_style("whitegrid")
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle("Superstore Business Performance Dashboard", fontsize=16, fontweight="bold")
# Chart 1: Sales and Profit by Category
category_performance.plot(kind="bar", ax=axes[0, 0], color=["#3498db", "#2ecc71"])
axes[0, 0].set_title("Sales & Profit by Category")
axes[0, 0].set_ylabel("Amount ($)")
axes[0, 0].tick_params(axis="x", rotation=0)
axes[0, 0].legend(["Sales", "Profit"])
# Chart 2: Monthly Sales Trend
monthly_sales_ts = monthly_sales.copy()
monthly_sales_ts.index = monthly_sales_ts.index.to_timestamp()
monthly_sales_ts.plot(ax=axes[0, 1], color="#3498db", linewidth=1.5)
axes[0, 1].set_title("Monthly Sales Trend")
axes[0, 1].set_ylabel("Sales ($)")
axes[0, 1].set_xlabel("")
What does this code do?
This sets up the visual foundation for your entire dashboard.
- sns.set_style("whitegrid") applies a clean grid background to every chart, making values easier to read.
- plt.subplots(2, 2, figsize=(14, 10)) creates a figure with a 2x2 grid of subplot areas. You access each panel using axes[row, col].
- Chart 1 uses the category_performance DataFrame you created in Step 4 to plot a grouped bar chart comparing Sales and Profit.
- Chart 2 converts the monthly period index to timestamps (required for smooth line plotting), then draws the sales trend over time.
- Save analysis.py.
- You won't see a visual change yet. The next chunk saves and displays the dashboard.
Complete the dashboard and save it as a PNG
The bottom row of your dashboard highlights specific product lines and regional differences. Once all four panels are filled, you'll save the entire figure as an image file.
- Add this code directly below the Chart 2 section you just wrote:
# Chart 3: Top 10 Sub-Categories by Profit
top_10_subcat = subcat_profit.head(10).sort_values()
colors = ["#2ecc71" if x > 0 else "#e74c3c" for x in top_10_subcat.values]
top_10_subcat.plot(kind="barh", ax=axes[1, 0], color=colors)
axes[1, 0].set_title("Top 10 Sub-Categories by Profit")
axes[1, 0].set_xlabel("Profit ($)")
# Chart 4: Profit by Region
region_colors = ["#2ecc71", "#3498db", "#f39c12", "#e74c3c"]
region_profit.plot(kind="bar", ax=axes[1, 1], color=region_colors[:len(region_profit)])
axes[1, 1].set_title("Profit by Region")
axes[1, 1].set_ylabel("Profit ($)")
axes[1, 1].tick_params(axis="x", rotation=0)
plt.tight_layout()
plt.savefig("superstore_dashboard.png", dpi=150, bbox_inches="tight")
plt.show()
What does this code do?
- Chart 3 takes the top 10 sub-categories by profit and plots them horizontally. The color list comprehension assigns green (#2ecc71) to profitable items and red (#e74c3c) to money-losing ones.
- Chart 4 gives each region a distinct color so you can spot the strongest performer at a glance.
- plt.tight_layout() prevents labels from overlapping between panels.
- plt.savefig() writes the figure to disk at 150 DPI before plt.show() opens the interactive viewer. Saving first ensures you always get the file, even if you close the window quickly.
- Save analysis.py.
- Run your script by entering this command in your terminal:
python analysis.py
- A matplotlib window opens showing your 4-panel dashboard. You should see a grouped bar chart (top-left), a line chart (top-right), a horizontal bar chart with green and red bars (bottom-left), and a bar chart (bottom-right).
- Close the matplotlib window.
- Confirm that superstore_dashboard.png appeared in your superstore-analysis folder by running:
ls
- You should see superstore_dashboard.png listed alongside analysis.py and superstore.csv.
Dashboard window not appearing?
- If the script runs but no window opens, matplotlib may be using a non-interactive backend. Try adding import matplotlib; matplotlib.use('TkAgg') before import matplotlib.pyplot as plt at the top of your file.
- If you see a FileNotFoundError about superstore.csv, make sure your terminal is inside the superstore-analysis folder.
- Even if the window doesn't appear, the PNG file is still saved. Check your folder for superstore_dashboard.png and open it manually to verify the charts.
Why isn't my matplotlib dashboard appearing when I run the script?
Print business recommendations
Data analysis only creates value when it leads to action. The final piece of your script translates the patterns you found into concrete recommendations a business stakeholder could act on immediately.
- In analysis.py, add this code below the plt.show() line:
# --- Business Recommendations ---
print("\n" + "=" * 50)
print("BUSINESS RECOMMENDATIONS")
print("=" * 50)
print("""
1. DOUBLE DOWN ON TECHNOLOGY: Highest profit margin category.
Action: Increase marketing spend on Copiers and Phones.
2. FIX THE FURNITURE PROBLEM: Tables and Bookcases are losing money.
Action: Review pricing strategy or discontinue loss-making products.
3. EXPAND IN THE WEST: West region generates the most profit.
Action: Study what makes West successful and replicate elsewhere.
4. TARGET CONSUMER SEGMENT: Largest customer base with highest total sales.
Action: Build loyalty programs for repeat Consumer buyers.
""")
What makes these recommendations effective?
Each recommendation follows a professional format: a finding backed by data, followed by a specific action the business can take.
- They reference specific product lines (Copiers, Phones, Tables, Bookcases) and regions (West) from your groupby results.
- They propose concrete next steps, not vague suggestions. This is how analysts communicate to decision-makers.
- Save analysis.py.
- Run the script again:
python analysis.py
- Close the dashboard window when it appears. In the terminal output, scroll to the bottom. You should see the BUSINESS RECOMMENDATIONS header followed by 4 numbered recommendations.
Recommendations not printing?
- The recommendations print to the terminal AFTER you close the matplotlib window. If you leave the window open, the script pauses at plt.show().
- Make sure the recommendations code is placed after plt.show(), not before it.
Help me debug why my print statements aren't appearing after plt.show().
✔️ Awesome, I've got everything!
Great. Double check you've saved your file.
ⓧ I'd like to double check the full code
Here is the complete analysis.py file up to this point:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# --- Step 2: Load and Inspect ---
df = pd.read_csv("superstore.csv", encoding="latin-1")
print("Dataset Shape:", df.shape)
print("\nFirst 5 Rows:")
print(df.head())
print("\nData Types:")
print(df.dtypes)
print("\nBasic Statistics:")
print(df.describe())
print("\nMissing Values:")
print(df.isnull().sum())
print("\nDuplicate Rows:", df.duplicated().sum())
print("\nUnique Values per Column:")
print(df.nunique())
# --- Step 3: Clean and Engineer Features ---
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.month
df["Shipping Days"] = (df["Ship Date"] - df["Order Date"]).dt.days
df["Profit Margin"] = (df["Profit"] / df["Sales"]) * 100
print("\nNew Columns Preview:")
print(df[["Order Date", "Order Year", "Order Month", "Shipping Days", "Profit Margin"]].head())
print(f"\nAverage Shipping Days: {df['Shipping Days'].mean():.1f}")
print(f"Average Profit Margin: {df['Profit Margin'].mean():.1f}%")
print(f"Data covers: {df['Order Year'].min()} to {df['Order Year'].max()}")
# --- Step 4: Answer Business Questions ---
print("\n" + "=" * 50)
print("BUSINESS ANALYSIS")
print("=" * 50)
# Question 1: Sales and Profit by Category
print("\n--- Sales and Profit by Category ---")
category_performance = df.groupby("Category")[["Sales", "Profit"]].sum().sort_values("Sales", ascending=False)
print(category_performance)
# Question 2: Profit by Region
print("\n--- Profit by Region ---")
region_profit = df.groupby("Region")["Profit"].sum().sort_values(ascending=False)
print(region_profit)
# Question 3: Top and Bottom Sub-Categories
print("\n--- Top 5 Sub-Categories by Profit ---")
subcat_profit = df.groupby("Sub-Category")["Profit"].sum().sort_values(ascending=False)
print(subcat_profit.head())
print("\n--- Bottom 5 Sub-Categories by Profit (Money Losers!) ---")
print(subcat_profit.tail(5))
# Question 4: Segment Performance and Sales Trend
print("\n--- Performance by Customer Segment ---")
segment_performance = df.groupby("Segment")[["Sales", "Profit"]].sum().sort_values("Profit", ascending=False)
print(segment_performance)
monthly_sales = df.groupby(df["Order Date"].dt.to_period("M"))["Sales"].sum()
print(f"\nSales Trend: {monthly_sales.iloc[0]:.0f} (first month) -> {monthly_sales.iloc[-1]:.0f} (last month)")
# --- Step 5: Visualize Patterns ---
sns.set_style("whitegrid")
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle("Superstore Business Performance Dashboard", fontsize=16, fontweight="bold")
# Chart 1: Sales and Profit by Category
category_performance.plot(kind="bar", ax=axes[0, 0], color=["#3498db", "#2ecc71"])
axes[0, 0].set_title("Sales & Profit by Category")
axes[0, 0].set_ylabel("Amount ($)")
axes[0, 0].tick_params(axis="x", rotation=0)
axes[0, 0].legend(["Sales", "Profit"])
# Chart 2: Monthly Sales Trend
monthly_sales_ts = monthly_sales.copy()
monthly_sales_ts.index = monthly_sales_ts.index.to_timestamp()
monthly_sales_ts.plot(ax=axes[0, 1], color="#3498db", linewidth=1.5)
axes[0, 1].set_title("Monthly Sales Trend")
axes[0, 1].set_ylabel("Sales ($)")
axes[0, 1].set_xlabel("")
# Chart 3: Top 10 Sub-Categories by Profit
top_10_subcat = subcat_profit.head(10).sort_values()
colors = ["#2ecc71" if x > 0 else "#e74c3c" for x in top_10_subcat.values]
top_10_subcat.plot(kind="barh", ax=axes[1, 0], color=colors)
axes[1, 0].set_title("Top 10 Sub-Categories by Profit")
axes[1, 0].set_xlabel("Profit ($)")
# Chart 4: Profit by Region
region_colors = ["#2ecc71", "#3498db", "#f39c12", "#e74c3c"]
region_profit.plot(kind="bar", ax=axes[1, 1], color=region_colors[:len(region_profit)])
axes[1, 1].set_title("Profit by Region")
axes[1, 1].set_ylabel("Profit ($)")
axes[1, 1].tick_params(axis="x", rotation=0)
plt.tight_layout()
plt.savefig("superstore_dashboard.png", dpi=150, bbox_inches="tight")
plt.show()
# --- Business Recommendations ---
print("\n" + "=" * 50)
print("BUSINESS RECOMMENDATIONS")
print("=" * 50)
print("""
1. DOUBLE DOWN ON TECHNOLOGY: Highest profit margin category.
Action: Increase marketing spend on Copiers and Phones.
2. FIX THE FURNITURE PROBLEM: Tables and Bookcases are losing money.
Action: Review pricing strategy or discontinue loss-making products.
3. EXPAND IN THE WEST: West region generates the most profit.
Action: Study what makes West successful and replicate elsewhere.
4. TARGET CONSUMER SEGMENT: Largest customer base with highest total sales.
Action: Build loyalty programs for repeat Consumer buyers.
""")
Your analysis is complete. You've gone from raw CSV data to a professional dashboard with written recommendations. Next up, the Secret Mission challenges you to uncover a hidden relationship between discounts and profit destruction.
Secret mission
Uncover The Discount Trap
Your dashboard shows which categories and regions are profitable. But there's a hidden pattern lurking in the data that most analysts miss. Discounts are supposed to drive sales, but at what point do they start destroying profit instead?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources on your machine, close things down for now, or delete them entirely.
Resources you used:
- The superstore-analysis folder containing analysis.py, superstore.csv, superstore_dashboard.png, and discount_analysis.png.
- Python libraries: pandas, matplotlib, and seaborn.
Keep everything running
No action needed. Choose this if you want to extend your analysis or use this project as a template for other datasets.
- Your analysis files stay on your machine with no ongoing costs.
- You can re-run analysis.py any time to regenerate your dashboard.
- Your free Kaggle account remains active for downloading other datasets.
Pause - I'll come back to this later
Close VS Code to free up memory. Your files remain on disk and you can reopen the project any time.
- Close the VS Code window with the superstore-analysis folder.
- Your project folder, dataset, and generated images remain on your Desktop.
- When you return, open VS Code and use File > Open Folder to reopen superstore-analysis.
Delete - I don't want to use this again
Remove all project files and uninstall the Python libraries you added.
- Delete the project folder by running this command in your terminal:
macOS
cd ~/Desktop
rm -rf superstore-analysis
Windows
cd ~\Desktop
rmdir /s /q superstore-analysis
- Uninstall the Python libraries by running:
pip uninstall pandas matplotlib seaborn -y
- This removes the libraries from your Python environment. You can reinstall them any time with pip install.
Nice Work!
Nice Work!
Nice work! You just completed a full exploratory data analysis of real retail data using Python. You transformed 9,994 raw transactions into actionable business insights, professional visualizations, and written recommendations.
You've learned how to:
- Load, inspect, and clean real-world data using pandas, then engineer new features like Profit Margin and Shipping Days to unlock deeper analysis.
- Answer business questions using groupby aggregation to surface patterns across categories, regions, sub-categories, and customer segments.
- Build a 4-panel professional dashboard with matplotlib and seaborn, and deliver written business recommendations backed by data evidence.
- Secret Mission: Discovered The Discount Trap and identified the exact threshold where discounts destroy profit.
Ready to quiz yourself?