Stop rebuilding the same report by hand.
Every module below runs on the same Northside Bike Works repair-jobs dataset โ the same one from the Data Analyst course, if you've taken it. Work through them in order: load and inspect the data, clean it, group and summarise it, understand it statistically, chart it, then automate the whole routine.
Your 3-month path
Read and trust the data
pandas foundations and cleaning โ the two skills every later step depends on.
Summarise and measure
groupby, aggregation, and enough statistics to know whether a number is meaningful.
Show it, then automate it
Charts worth sharing, then a script that runs the whole routine on its own.
Pandas Foundations
Reading data into Python and actually looking at it first.
1 Series vs. DataFrame
A Series is one labeled column of data. A DataFrame is a full table โ many Series sharing the same row index. jobs["Price"] hands you back a Series; jobs itself is the DataFrame.
2 Reading data in
import pandas as pd
jobs = pd.read_csv("jobs.csv")
One line replaces "open the file, find the delimiter, guess the header row" โ pandas handles all of it and hands you a DataFrame.
3 First look: head(), info(), dtypes
jobs.head() # first 5 rows โ a quick sanity check
jobs.info() # row count, columns, non-null counts, dtypes
jobs.dtypes # just the column types
Three commands, thirty seconds, and you already know how many rows you have, which columns might have missing values, and whether anything got loaded as the wrong type.
In [1]: jobs.head()
In [2]: jobs.info()
Exercises
- Load the Jobs export into pandas with pd.read_csv().
- Run jobs.head() and jobs.tail() and compare.
- Run jobs.info() and identify every column's dtype.
- Check jobs.shape โ how many rows and columns is that?
- List one thing you'd want to fix before analysing this data further.
A short written profile of the Jobs dataset: row/column count, each column's dtype, and a one-line note on anything that looks off before you clean it.
Recap quiz
Data Cleaning & Wrangling
Fixing missing values, types, and messy text before you trust a single number.
1 Finding and handling missing values
jobs["JobType"].isna() # True/False mask of missing cells
jobs.dropna(subset=["JobType"]) # remove rows with a missing JobType
jobs.fillna({"Notes": "None"}) # or fill missing values instead
isna() never changes your data โ it just tells you where the gaps are, so you can decide: drop the row, or fill it with something sensible.
2 Fixing types
jobs["Price"] = jobs["Price"].str.replace("$", "").astype(float)
A stray "$" in a CSV export turns a numeric column into text. Strip it, then astype() converts it back into something you can actually do math on.
3 Cleaning text columns
jobs["JobType"] = jobs["JobType"].str.strip().str.title()
jobs = jobs[jobs["JobType"] != ""]
.str.strip() removes stray whitespace, .str.title() normalises capitalisation, and a boolean filter drops blank rows โ the pandas equivalent of Power Query's Applied Steps.
| JobType | Cost | Price |
|---|---|---|
| " flat repair " | "$8" | "$25" |
| "FULL TUNE-UP" | "35" | "90" |
| "Puncture Repair" | "5.0" | "20" |
| "" | "10" | "30" |
| "Brake Adjustment" | "$10" | "$30" |
| JobType | Cost | Price |
|---|---|---|
| Flat Repair | 8 | 25 |
| Full Tune-Up | 35 | 90 |
| Puncture Repair | 5 | 20 |
| Brake Adjustment | 10 | 30 |
- โ jobs["JobType"].str.strip()
- โ jobs["JobType"].str.title()
- โ jobs[["Cost","Price"]].str.replace("$","").astype(float)
- โ jobs.dropna(subset=["JobType"])
Exercises
- Check every column for missing values with .isna().sum().
- Strip and title-case the JobType column.
- Convert Cost and Price to float, stripping any "$".
- Drop rows with a blank JobType and note how many were removed.
- Re-run jobs.info() and confirm the dtypes are now correct.
A function clean_jobs(df) that takes a raw DataFrame and returns a cleaned one โ every fix from this module, in one callable place.
Recap quiz
GroupBy & Aggregation
pandas' answer to the Excel PivotTable and the SQL GROUP BY.
1 The basic pattern
jobs.groupby("JobType")["Price"].sum()
Split the table into buckets by JobType, then sum Price within each bucket. Swap .sum() for .mean(), .count(), or .median() for a different summary of the same groups.
2 Multiple aggregations at once
jobs.groupby("JobType").agg(
TotalRevenue=("Price", "sum"),
AvgMargin=("Margin", "mean"),
JobCount=("JobID", "count"),
)
.agg() with named arguments computes several summaries in one pass and hands back clearly labeled columns โ the exact shape of a report table.
3 Combining tables with merge()
jobs_with_city = pd.merge(jobs, customers, on="CustomerID")
jobs_with_city.groupby("City")["Price"].sum()
merge() is pandas' join โ line up rows from two tables on a shared key, same idea as a SQL JOIN or a Power BI relationship, so you can group Jobs by a Customer attribute like City.
Exercises
- Group Jobs by JobType and sum Price.
- Group Jobs by Status and count rows in each group.
- Merge Jobs with Customers and group the result by City.
- Write one .agg() call that returns TotalRevenue, AvgMargin, and JobCount together.
One .agg() call producing a report-ready summary table: revenue, average margin, and job count, grouped by JobType and sorted by revenue descending.
Recap quiz
NumPy & Statistics
Knowing whether a number is meaningful, not just calculating it.
1 Vectorized operations
import numpy as np
prices = jobs["Price"].to_numpy()
prices_with_tax = prices * 1.08 # applied to every element at once
NumPy applies math to a whole array in one step instead of a manual loop โ the engine pandas itself is built on, and dramatically faster on large data.
2 describe() โ the five-second summary
jobs["Price"].describe()
One call returns count, mean, standard deviation, min, the quartiles, and max โ the fastest way to understand a column's spread before you chart it.
3 Correlation
jobs["Price"].corr(jobs["Cost"])
A single number from -1 to 1 summarising how strongly two numeric columns move together. Useful for spotting relationships โ but correlation never proves one causes the other.
Exercises
- Run .describe() on Price, Cost, and Margin.
- Compute the standard deviation of Margin using NumPy directly.
- Compute the correlation between Price and Cost and interpret it in one sentence.
- Identify any job whose Price sits more than 2 standard deviations from the mean.
A one-page stats summary of Price, Cost, and Margin โ mean, median, std, and one sentence interpreting the Price/Cost correlation for a non-technical reader.
Recap quiz
Data Visualization
matplotlib and seaborn โ turning a DataFrame into something someone else can read.
1 Choosing the right chart
Comparing separate categories โ bar chart. A trend over time โ line chart. The relationship between two numeric columns โ scatter plot. Matching the chart to the question is the actual skill โ matplotlib syntax is just the mechanics.
2 matplotlib basics
import matplotlib.pyplot as plt
plt.bar(jobs_by_type.index, jobs_by_type["Price"])
plt.title("Revenue by Job Type")
plt.xlabel("Job Type"); plt.ylabel("Revenue")
plt.show()
Build the chart, label the axes, give it a title, then show it. Skipping the labels is how a perfectly good chart becomes unreadable to anyone but you.
3 seaborn: matplotlib with better defaults
import seaborn as sns
sns.scatterplot(data=jobs, x="Cost", y="Price", hue="Status")
seaborn is built directly on top of matplotlib โ same engine, nicer default styling, and higher-level statistical chart types like this one, which colours points by an extra category for free.
Exercises
- Build a bar chart of revenue by JobType, with axis labels and a title.
- Build a line chart of revenue by month.
- Build a scatter plot of Cost vs. Price and describe the relationship in one sentence.
- Recreate one of the three using seaborn instead of plain matplotlib.
Three finished, labeled charts โ one bar, one line, one scatter โ each choosing the right chart type for its question, ready to paste into a report.
Recap quiz
Automation & Real-World Workflows
Write the routine once, run it every month for free.
1 Packaging the routine into a function
def summarise_jobs(csv_path):
jobs = pd.read_csv(csv_path)
jobs = clean_jobs(jobs)
jobs["Margin"] = jobs["Price"] - jobs["Cost"]
return jobs.groupby("JobType").agg(
TotalRevenue=("Price", "sum"),
AvgMargin=("Margin", "mean"),
JobCount=("JobID", "count"),
).sort_values("TotalRevenue", ascending=False)
Every step from Modules 1-3, in one callable function. Run it on any month's export and get the same trustworthy summary, instantly.
2 Exporting the result
summary = summarise_jobs("march.csv")
summary.to_excel("march_report.xlsx")
.to_excel() writes the DataFrame straight to a real file โ the step that turns a script's output into something you can actually email to someone.
3 Running it as a script
if __name__ == "__main__":
summarise_jobs("jobs.csv").to_excel("report.xlsx")
print("Report generated.")
This guard keeps reusable functions reusable โ the file can be imported elsewhere without re-running itself, or run directly to generate a report on demand (or on a schedule, via cron or Task Scheduler).
Exercises
- Combine your Module 2 cleaning function and Module 3 groupby into one summarise_jobs() function.
- Export its result to an .xlsx file.
- Add the if __name__ == "__main__": guard so the file can be safely imported elsewhere.
- Write one sentence on how you'd schedule this to run automatically every month.
A single Python file that reads a raw export, cleans it, summarises it, and writes a finished .xlsx report โ runnable with one command.
Recap quiz
Ship one full Python analysis script
Every module, tied into one real deliverable.
Take a dataset โ the Northside Bike Works set you've used throughout, or your own โ and carry it through all six stages, end to end, in a single script:
| Stage | What "done" looks like |
|---|---|
| 1. Pandas Foundations | Data loaded with pd.read_csv(), inspected with head()/info() |
| 2. Data Cleaning | A clean_jobs(df) function fixing types, text, and missing values |
| 3. GroupBy & Aggregation | A .agg() summary table: revenue, avg margin, job count by JobType |
| 4. NumPy & Statistics | describe() output plus one interpreted correlation |
| 5. Data Visualization | At least one bar, one line, and one scatter chart, all labeled |
| 6. Automation | One function that runs the whole pipeline and exports report.xlsx |
- All six stages are complete for the same dataset, in one script.
- You can explain, out loud, one design decision you made at each stage and why.
- Someone with no Python experience could open report.xlsx and understand the numbers without you in the room.