Mathew K Analytics

Lesson 6 · Data visualisation in python

Master Data Cleaning and Preparation with Pandas in Python for Effective Bible Study Analysis

In this lesson, we will explore how to use the pandas library to clean and prepare data for analysis. You will learn fundamental techniques: loading data,…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Welcome to Data Cleaning and Preparation with Pandas!#

In this lesson, we will explore how to use the pandas library to clean and prepare data for analysis.

You will learn fundamental techniques: loading data, handling missing values, fixing errors, and getting your data ready for real-world tasks.

No prior experience needed!

Let us get started and see how powerful pandas can be.

import warnings
from statsmodels.tools.sm_exceptions import ConvergenceWarning, ValueWarning
warnings.filterwarnings("ignore", category=ValueWarning)
warnings.filterwarnings("ignore", category=ConvergenceWarning)
warnings.filterwarnings("ignore", category=RuntimeWarning)
# First we import pandas and matplotlib. These tools help us work with data easily.
import pandas as pd
import matplotlib.pyplot as plt

Dataset Introduction: Shampoo Sales Data#

We will practice with a real-world time series dataset.

The Shampoo Sales dataset contains monthly sales totals for a shampoo company.

It is a common example for learning data preparation and cleaning.

Let us load this data and explore what is inside.

# Data setup
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/shampoo.csv"
df = pd.read_csv(url)
# Create a sequence of monthly dates starting at January 1901 (one entry per row).
df['Month'] = pd.date_range(start='1901-01', periods=df.shape[0], freq='MS')
df['Month'] = pd.to_datetime(df['Month'])

print("Shape:", df.shape)
df.head()
Shape: (36, 2)
Month Sales
0 1901-01-01 266.0
1 1901-02-01 145.9
2 1901-03-01 183.1
3 1901-04-01 119.3
4 1901-05-01 180.3
# Let us see a simple line plot of the sales data.
plt.figure(figsize=(8, 4))
plt.plot(df["Sales"])
plt.title("Monthly shampoo sales over time")
plt.xlabel("Month (row number)")
plt.ylabel("Sales")
plt.show()
No description has been provided for this image

Checking Data Types#

Data can come in many forms: numbers, text, or dates.

It is important to check these types, so nothing surprises us later.

Every column in pandas has a data type.

Let us see what types our shampoo data has.

# Check column types
print(df.dtypes)
Month    datetime64[ns]
Sales           float64
dtype: object
# Convert the 'Month' column to datetime if needed.
df["Month"] = pd.to_datetime(df["Month"], errors="coerce")
df.dtypes
Month    datetime64[ns]
Sales           float64
dtype: object

Finding Missing Data#

Sometimes there are blanks, holes, or weird values in our data.

Cleaning means finding and handling these.

Let us check for missing values.

# Show missing values in each column.
print(df.isnull().sum())
Month    0
Sales    0
dtype: int64
# Fill missing sales values with the average of the column.
mean_sales = df["Sales"].mean()
df["Sales"] = df["Sales"].fillna(mean_sales)
print(df.isnull().sum())
Month    0
Sales    0
dtype: int64
# Remove rows where 'Month' could not be converted to a date.
before = df.shape[0]
df = df.dropna(subset=["Month"])
after = df.shape[0]
print(f"Rows before: {before}, Rows after: {after}")
Rows before: 36, Rows after: 36
# Create a new column with sales in thousands.
df["Sales_K"] = df["Sales"] / 1000
df.head()
Month Sales Sales_K
0 1901-01-01 266.0 0.2660
1 1901-02-01 145.9 0.1459
2 1901-03-01 183.1 0.1831
3 1901-04-01 119.3 0.1193
4 1901-05-01 180.3 0.1803
# Remove outliers above 10,000 in 'Sales'.
outliers = df[df["Sales"] > 10000]
df = df[df["Sales"] <= 10000]
print(f"Removed {outliers.shape[0]} outliers.")
Removed 0 outliers.
# Reset index so numbers run smoothly after any row changes.
df = df.reset_index(drop=True)
df.head()
Month Sales Sales_K
0 1901-01-01 266.0 0.2660
1 1901-02-01 145.9 0.1459
2 1901-03-01 183.1 0.1831
3 1901-04-01 119.3 0.1193
4 1901-05-01 180.3 0.1803
# Sort by date just to be safe.
df = df.sort_values("Month").reset_index(drop=True)
df.head()
Month Sales Sales_K
0 1901-01-01 266.0 0.2660
1 1901-02-01 145.9 0.1459
2 1901-03-01 183.1 0.1831
3 1901-04-01 119.3 0.1193
4 1901-05-01 180.3 0.1803
# Use describe to quickly see summary statistics.
df.describe()
Month Sales Sales_K
count 36 36.000000 36.000000
mean 1902-06-16 12:00:00 312.600000 0.312600
min 1901-01-01 00:00:00 119.300000 0.119300
25% 1901-09-23 12:00:00 192.450000 0.192450
50% 1902-06-16 00:00:00 280.150000 0.280150
75% 1903-03-08 18:00:00 411.100000 0.411100
max 1903-12-01 00:00:00 682.000000 0.682000
std NaN 148.937164 0.148937
# Find months with above average sales.
above_avg = df[df["Sales"] > df["Sales"].mean()]
print(f"Months with above average sales: {above_avg.shape[0]}")
above_avg.head()
Months with above average sales: 15
Month Sales Sales_K
10 1901-11-01 336.5 0.3365
21 1902-10-01 421.6 0.4216
23 1902-12-01 342.3 0.3423
24 1903-01-01 339.7 0.3397
25 1903-02-01 440.4 0.4404
# Group by year and add up sales.
df["Year"] = df["Month"].dt.year
yearly_sales = df.groupby("Year")["Sales"].sum()
print(yearly_sales)
Year
1901    2357.5
1902    3153.5
1903    5742.6
Name: Sales, dtype: float64
# Save the clean data to a new file.
df.to_csv("shampoo_cleaned.csv", index=False)
print("Cleaned data saved!")
Cleaned data saved!
# HANDS-ON: Practice reading user data and fixing missing sales
try:
    user_month = input("Enter a month (2001-01): ")
    user_sales = input("Enter sales for this month (number): ")
    if user_sales == "":
        user_sales = df["Sales"].mean()
        print(f"No sales entered. Using average: {user_sales}")
    else:
        user_sales = float(user_sales)
    new_row = {"Month": pd.to_datetime(user_month, errors="coerce"), "Sales": user_sales}
    import numpy as np
    # Fill missing columns if needed
    for col in df.columns:
        if col not in new_row:
            new_row[col] = np.nan
    new_df = pd.DataFrame([new_row])
    df = pd.concat([df, new_df], ignore_index=True)
    df.tail()
except Exception as e:
    print("An error occurred. Please try again. Error:", str(e))
    

Mini-Project Part 1: Spotting Mistakes#

Take a copy of your data and manually introduce a few unusual values (like -100 or 99999).

Can you use pandas to find and remove them?

Try using filters and conditions as you practiced.

Give it a try before moving on to the next part.

# Mini-Project Part 2: Find missing months and fill them
all_months = pd.date_range(df["Month"].min(), df["Month"].max(), freq="MS")
df_full = df.set_index("Month").reindex(all_months).rename_axis("Month").reset_index()
missing = df_full[df_full["Sales"].isnull()]
print(f"Missing months: {missing.shape[0]}")
df_full["Sales"] = df_full["Sales"].fillna(df_full["Sales"].mean())
df_full.head()
Missing months: 1194
Month Sales Sales_K Year
0 1901-01-01 266.0 0.2660 1901.0
1 1901-02-01 145.9 0.1459 1901.0
2 1901-03-01 183.1 0.1831 1901.0
3 1901-04-01 119.3 0.1193 1901.0
4 1901-05-01 180.3 0.1803 1901.0
# Best Practice: Always keep a copy of raw data.
df_raw = pd.read_csv(url)
print("Raw data shape:", df_raw.shape)
Raw data shape: (36, 2)
# Troubleshooting: What if .csv uses a different separator?
test_df = pd.read_csv(url, sep=",")
print(test_df.head())
  Month  Sales
0  1-01  266.0
1  1-02  145.9
2  1-03  183.1
3  1-04  119.3
4  1-05  180.3
# Extra tip: Quickly see unique values with .unique()
print(df["Year"].unique())
[1901. 1902. 1903.   nan]

Challenge Time: Try these exercises!#

  1. Create a column for sales as a percent of the year's total.
  2. Plot only months with below average sales.
  3. Save a copy where sales are at least 2500.

Use what you have learned: filters, calculations, and saving files.

Pause the lesson, and try now!

Recap: Key Skills#

  • Loading and exploring data
  • Finding and fixing missing values
  • Removing outliers
  • Making new columns and sorting/pivoting
  • Saving and exporting cleaned data

You now have a strong foundation in using pandas for real-world data cleaning!

Final Thoughts and Next Steps#

Well done! Keep practicing on your own data.

Want more lessons like this? Like and subscribe to the channel.

Share your mini-projects and questions in the comments below!

See you next time!

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.