Mohamed A. Adel.

Python for Data Analysis

Topic 7 of 14 · In Data Analysis · September 8, 2026

⏱ 2–3 WeeksBeginner–Intermediate

From zero to pandas: the Python you actually need as an analyst — notebooks, fundamentals, the DataFrame, and cleaning + aggregating data with code.

++++FREE LEARNING PATHDATA ANALYSISDATA ANALYSIS · PHASE 3 · ADVANCEDPYTHON FOR DATAANALYSISFrom zero to pandas: the Python you actually need a…· 3 / 3BEGINNER–INTERMEDIATE2–3 WEEKSmotif / codeimport pandas as pddf = pd.read_csv('sales.csv')df = df.dropna()df.groupby('region').sum()sns.lineplot(data=df)RUN4087 · 87 · MODULE 4.1

Overview

Python is the language where your analysis stops being limited by spreadsheet rows or one window — where you can clean 200,000 rows, rerun an analysis instantly with new data, and hand a colleague a script that reproduces your whole pipeline. Even though you will reach for Excel and Power BI for many tasks, Python is what job postings mean by "programming", and it is the bridge into EDA and machine learning next.

This topic teaches the analyst's Python — not a computer-science course. You will learn just enough language fundamentals to read and write script-like code, then spend most of your time in pandas, the library that makes Python behave like a supercharged spreadsheet with a memory.

Before You Start

  • Complete the Data Cleaning topic so you can re-express its workflow in code, and know what a transformation log is before you write your clean functions.
  • Install Python 3.8 or later with pandas, numpy and jupyter, and start a Jupyter notebook before the lesson.
  • Reuse the messy CSV from the cleaning topic — you will clean it again, this time with pandas.
  • Practise running a cell with Shift+Enter and adopt the habit of Kernel, Restart and Run All before trusting any numbers.
  • Refresh the SQL grain thinking: one row in orders means one order line, and merges can duplicate rows when keys are not unique.

Learning Objectives

  • Run Python in a Jupyter notebook and understand the cell-based workflow
  • Use core syntax: variables, types, collections, loops, conditionals, functions, f-strings
  • Understand Python's zero-based indexing, slicing and methods
  • Use NumPy arrays for fast numeric operations and understand the "run on all rows" idea
  • Load data into a pandas DataFrame and inspect it (head, info, describe, shape, dtypes)
  • Select and filter rows and columns with loc/iloc, boolean masks and query
  • Clean in pandas: dropna/fillna, drop_duplicates, replace, strip, string methods, astype, to_datetime
  • Aggregate with groupby + agg, and reshape with pivot_table
  • Combine tables with merge and join, mirroring SQL JOINs
  • Export clean results and structure a reproducible notebook (markdown, functions, clear output)

Prerequisites

Basics through SQL give the concepts; Data Cleaning gives the workflow you will re-express here. No programming background needed — this starts from zero and moves at analyst speed.

Why Python, and Where It Fits in Your Path

Excel and Power BI are fantastic — until the data is huge, the task is repetitive, or you need the exact same analysis every week with zero clicking. Python's analyst strengths:

  • Scale and speed — read and transform millions of rows in seconds
  • Automation — one script does Monday's, Tuesday's and next month's job
  • Reproducibility — the script is your documented methodology
  • Ecosystem — pandas, plus plotting (matplotlib, seaborn) and, later, statistics/scikit-learn

In this path, Python supersizes your data-cleaning and prep work and becomes the workhorse for Exploratory Data Analysis. You will still use Power BI for dashboards and Excel for quick checks — professionals use all three.

Getting Started: Install and Notebooks

You need three pieces: Python (the 3.x runtime), a code environment, and pandas (plus NumPy). The analyst-standard environment is Jupyter notebooks — a document of cells mixing markdown notes and runnable Python, ideal for analysis that tells a story top to bottom.

# terminal:
python --version                 # verify Python 3.8+
pip install pandas numpy jupyter # install your analysis stack
# then:
jupyter notebook               # opens the notebook UI in your browser

In a notebook: run a cell with Shift+Enter; an In [5] counter appears. The golden cell habit: one idea per cell, run top to bottom, and re-run everything after any change — a cell run out of order is the #1 source of "but it worked a minute ago".

Python Fundamentals for Analysts

Don't learn a programming language — learn a toolbox. These few pieces carry 95% of analyst code:

# Variables & types
name      = "Cairo"            # str
price     = 49.99              # float
orders    = 1200               # int
in_stock  = True               # bool

# Collections
cities = ["Cairo", "Alex", "Giza"]   # list (ordered)
info   = {"region": "North", "target": 100000}  # dict (key → value)

# Loop + conditional
for city in cities:
    if city.startswith("C"):
        print(f"Starts with C: {city}")   # f-string

# Function
def margin(revenue, cost):
    return (revenue - cost) / revenue
  • Zero-indexing: cities[0] is the first element; slicing cities[1:3] takes positions 1–2
  • Methods — actions on values: " Hi ".strip(), cities.append("Mansoura"), name.lower()
  • Indentation is syntax — four spaces under if/for/def; inconsistent indentation is the beginner bug

You will not write a lot of loops in pandas (the library vectorizes them for you) — but you must be able to read them and debug them.

Practicetext
sales = {'monday': 1200, 'tuesday': 980, 'wednesday': 1430}
total = 0
for amount in sales.values():
    total += amount
print(f'Total week = {total:,}')

NumPy: Fast Tables of Numbers

NumPy gives you arrays — grids of numbers where every operation applies to the whole grid at once (vectorized). You rarely call NumPy directly, but it powers pandas, and understanding the "operate on everything" idea unlocks everything else.

import numpy as np

amounts = np.array([100, 250, 400, 700])   # a numeric vector
discounted = amounts * 0.9                 # applies to every element!
print(discounted)                          # [ 90. 225. 360. 630.]
print(amounts.mean())                      # 362.5
print(amounts[amounts >= 300])             # boolean filter: array([400, 700])

Notice there is no loop: the operation runs on all rows because the array knows itself as one object. That same speed and simplicity is exactly how pandas handles whole columns.

pandas: The Analyst's Power Tool

pandas gives you two objects: a Series (one labeled column) and a DataFrame (labeled rows × columns — think "a spreadsheet in code").

import pandas as pd

df = pd.read_csv("orders.csv")   # load CSV

df.head()      # first 5 rows
df.shape       # (rows, columns)
df.columns     # column names
df.info()      # types + nulls — your profile in one call
df.describe()  # count/mean/min/max/percentiles for numeric columns

df["region"].value_counts()   # categorical profile

With df.head(), df.info() and df.describe() you have just replaced your Excel/SQL profiling step — in three lines, on any size file. That is the moment the power of code becomes obvious.

Try ittext
df = pd.read_csv('your_file.csv')
# Your first exploration:
print(df.shape)
print(df.isna().sum())        # nulls per column
print(df.nunique())           # distinct values per column

Selecting, Filtering and Deriving

Two selection styles cover everything:

# By label:      df.loc[row_selector, column_selector]
# By position:   df.iloc[row_position, column_position]

df.loc[:, ["order_id", "amount"]]          # pick columns by name
df.loc[df["region"] == "North"]            # rows where region is North

# Boolean mask → the SQL WHERE
df.loc[(df["amount"] >= 500) & (df["status"] == "shipped")]

# Or the query() sugar
df.query("amount >= 500 and status == 'shipped'")

# New derived column (vectorized, like a calculated column)
df["revenue"] = df["quantity"] * df["price"]
df["order_size"] = df["revenue"].apply(lambda v: "Large" if v >= 500 else "Small")
  • & (and) and | (or) — never Python's and/or on arrays; parentheses around each condition are required
  • .apply() runs row-wise logic when a vectorized expression is not easy

This mirrors Excel's filter + XLOOKUP + IF, all evaluated for the entire table at once.

Cleaning in pandas

Every trick from your data-cleaning topic now has a one-liner:

# Missing values
clean = df.dropna(subset=["order_id"])            # drop rows missing a key column
clean = df.fillna({"region": "UNK"})             # fill a column with a default

# Duplicates — define the key first
clean = clean.drop_duplicates(subset=["order_id"], keep="last")

# Text standardization
clean["region"] = clean["region"].str.strip().str.title()

# Types
clean["order_date"] = pd.to_datetime(clean["order_date"])     # true dates
clean["price"] = clean["price"].astype(float)

# Replace / recode categories
clean["segment"] = clean["segment"].replace({"prem": "premium", "stnd": "standard"})

Notice the method chain pattern: read → drop → fill → dedupe → standardize → type, all as script steps. This is your Raw → Clean pipeline as executable, reviewable code — run it on a new file tomorrow and it recreates the exact same result.

Speedy checktext
print(clean.isna().sum())          # confirm nulls after cleaning
print(clean.duplicated(subset=['order_id']).sum())  # remaining duplicates

Grouping and Combining Tables

Your SQL muscle memory transfers directly:

# GROUP BY in pandas
df.groupby("region")["amount"].sum()          # revenue by region
df.groupby(["region", "month"]).agg(
    orders   = ("amount", "count"),
    revenue  = ("amount", "sum"),
    avg      = ("amount", "mean"),
).reset_index()

# SQL-style JOINs
merged = orders.merge(customers, on="customer_id", how="left")  # LEFT JOIN

# Pivot — like a PivotTable
df.pivot_table(index="region", columns="month",
               values="amount", aggfunc="sum")

After every merge, check the shape: merged.shape — a many-to-many join can silently duplicate rows (the same trap as SQL).

Export, Structure, Reproducibility

Finish like a professional:

clean.to_csv("orders_clean.csv", index=False)   # export, no row numbers

# save a single frame for quick inspection
summary = df.groupby("region")["amount"].sum().reset_index()
summary.to_excel("region_report.xlsx", index=False)

And follow notebook hygiene that colleagues (and future-you) will thank you for: explain each section with a markdown cell (title, source, question); keep one table → one analysis step; name DataFrames meaningfully; don't print the whole table; and when an analysis grows beyond a page, split reusable logic into functions.

The unspoken rule: your notebook is your methodology document. A reviewer should be able to re-run it end-to-end and get the same numbers — that is reproducibility, and it is the difference between "I found this" and "here is the evidence".

Hands-On Practice

Reuse the messy CSV from your data-cleaning topic — this time by code.

  1. Load it with pandas and profile it in three lines (shape, isna-sum, nunique).
  2. Clean it in a method chain: drop dupes on the key, fill nulls (sensible defaults), standardize text, convert dates and numbers.
  3. Verify the cleaning (isna().sum() and duplicated().sum() = 0 on the key).
  4. Answer 4 business questions with groupby/agg and merge (e.g. revenue by region × month, top category, salesperson rank).
  5. Export the clean table and one summary and confirm both re-run from a fresh kernel.

Set up Jupyter; do 10 small drills in a scratch notebook: variables, lists, dicts, loops, functions, f-strings. If it solves quickly, keep going — the goal is comfort reading and running, not memorizing.

Deeper Worked Examples

These examples push further than the core content — each one is a complete, step-by-step solution you can follow and then reproduce on your own.

Profile and clean a messy export in one method chain

Problem. The same finance export from your cleaning topic (1,200 rows) arrives in Python: four exact duplicates, region written several ways, twelve missing emails, a stray string inside the amount column, and order_date stored as text. Reproduce your full cleaning pipeline as code.
1

Step 1

Load and profile in three lines: df.shape shows the dimensions, df.isna().sum() reveals the twelve nulls, and df.nunique() exposes the messy distinct region count — all before any fix.
2

Step 2

Define the key and drop duplicates on it with clean = df.drop_duplicates(subset=['order_id'], keep='last') and confirm the row count falls by exactly four.
3

Step 3

Standardise the region text in one vectorised move: clean['region'] = clean['region'].str.strip().str.title(), then confirm the distinct value count collapses to one canonical label.
4

Step 4

Convert types properly: clean['order_date'] = pd.to_datetime(clean['order_date']) and clean['price'] = clean['price'].astype(float) after investigating the stray string, then re-run df.info() to verify the dtypes.
5

Step 5

Fill the twelve missing emails with a neutral code using clean = clean.fillna({'email': 'no_email'}) and verify with clean.isna().sum() that no nulls remain.
6

Step 6

Close the loop: re-run the profile on the clean frame, confirm zero duplicates and one region label, then save with clean.to_csv('orders_clean.csv', index=False) so the pipeline itself is the documented artifact.

Group, merge and answer three business questions

Problem. You are given a clean orders table (order_id, customer_id, region, month, amount) and a customers table (customer_id, customer_name). Management wants three numbers — revenue per region, order count per region and average order value — with customer names attached so the report is human-readable.
1

Step 1

Check the merge before merging: run customers['customer_id'].is_unique and print(orders['customer_id'].duplicated().sum()) so a many-to-many duplication is impossible, then merged = orders.merge(customers, on='customer_id', how='left').
2

Step 2

Verify the row count is unchanged by confirming merged.shape shows 1,200 rows before aggregating.
3

Step 3

Answer the first question with revenue_by_region = merged.groupby('region')['amount'].sum().reset_index() and print it, expecting each region's total.
4

Step 4

Answer the second with orders_by_region = merged.groupby('region')['amount'].count().reset_index(), renaming the result column to orders.
5

Step 5

Answer the third by combining the two summaries: add an average order value column as revenue divided by orders, then print it with an f-string: print(f'{row.region}: {row.revenue:,.0f} revenue, {row.orders} orders, AOV {row.aov:,.1f}').
6

Step 6

Finish with a markdown cell restating the three questions and answers plus the one-line grain caveat, then run Kernel, Restart and Run All to confirm the notebook reproduces identically.

Common Mistakes

  • Running cells out of order, then trusting stale results — restart & run all
  • Setting df = df.copy() everywhere out of fear, or never copying and mutating your input
  • Using Python 'and'/'or' on arrays instead of & and | (with parentheses)
  • Merging two tables and not checking the row count explodes
  • Forgetting pandas is zero-indexed and columns are labels, not positions
  • Doing .apply(lambda) row-wise when a vectorized string/date method exists
  • Reading but never writing a reproducible notebook + exported artifacts
  • Ignoring dtypes: a column of '9' strings quietly wrecks every sum

Real-World Usage

Python appears in analytics interviews both as takehome code and as screening questions: "load this, clean it, answer these three questions, email the script". What they grade is exactly what this topic trained: correct pandas idioms, a clean reproducible notebook, thoughtful cleaning decisions, and clearly communicated results. On the job, Python is your automation arm — scheduled ETL scripts, exploratory studies, ad-hoc analyses too big for Excel — and it is the prerequisite for the EDA topic next: filling notebooks with numbers, charts and honest conclusions before anything gets presented to a stakeholder.

Mini Project

Business problem: The growth team keeps getting different answers from different people for the same question. They ask you to produce one canonical weekly analysis, fully reproducible.

Dataset: any public retail CSV (or your cleaned data-cleaning export), 1,000+ rows, with date, region, category, salesperson and amount.

Objective: one clean, automated, reproducible pipeline + a concise weekly summary notebook.

1

Task 1

Profile the file in code (shape, nulls, duplicates, dtypes, value ranges).
2

Task 2

Write a clean() function encoding every cleaning decision (dupes, nulls, text, types).
3

Task 3

Write a summarize(df) function returning the weekly KPIs: orders, revenue, revenue per order, by region × month.
4

Task 4

Merge on any second table available (customers/products) and verify row counts.
5

Task 5

Export orders_clean.csv + region_report and add a 'how to run' markdown section.
6

Task 6

Run Kernel → Restart & Run All end-to-end and confirm the numbers reproduce.
DeliverableProves
Clean reproducible pipelinepolish: functions + Restart & Run All
Profile in codeSurvey-before-touch in pandas
Weekly KPI summarygroupby/agg + merge mastery
Exported artifactsDeliverable files, not just demo code
Documented notebookColleague-reviewable methodology

Key Takeaways

  • Python power = scale, automation, reproducibility and the data ecosystem
  • Jupyter cells mix notes and code; restart & run all when results are odd
  • Fundamentals: variables, lists, dicts, loops, functions, f-strings, zero-indexing
  • NumPy vectorizes math — operate on the whole array, never per-cell
  • pandas DataFrame = labeled spreadsheet in code; head/info/describe profile anything
  • loc/iloc, boolean masks (…&… or …|…) and query() cover all filtering
  • Cleaning is a method chain; verify with isna().sum() and duplicated().sum()
  • groupby/agg, pivot_table and merge = the SQL you already know
  • A well-structured notebook is your methodology document

Key Terms & Glossary

The vocabulary this topic uses, defined the way a working professional would.
TermMeaning
DataFramepandas' two-dimensional labelled table — a spreadsheet in code, with named columns and an index.
SeriesA single labelled column inside a DataFrame, which is also what you get when you select one column.
vectorisationApplying an operation to a whole column at once, like df['price'] times 0.9, in fast compiled code instead of looping per row.
boolean maskA Series of True and False used to filter rows — the pandas equivalent of SQL's WHERE, built with the ampersand and pipe operators and parentheses.
method chainingWriting cleaning steps one after another, each operating on the previous result, so the Raw-to-Clean pipeline reads top to bottom.
groupbyThe pandas operation that splits rows into groups and applies an aggregation such as sum, count or mean to each — SQL's GROUP BY.

Learning Resources

These curated sources go deeper than this module: official docs, books, interactive practice, and full courses. Mix them into your routine to go from comfortable to professional.

Python.org — The Python Tutorial

The official, free introduction to the language — work through part 1 for fundamentals.

Open resource ↗

Python for Everybody (py4e)

Free book and video course that takes you from zero to working code.

Open resource ↗

pandas — Getting Started

Official 10-minute- to-tutorial intro to DataFrames, selection, grouping and joining.

Open resource ↗

Kaggle — pandas Course

Short interactive lessons that pair perfectly with this module's practice.

Open resource ↗

Real Python

Extensive tutorials, from Python fundamentals to pandas, notebooks and automation.

Open resource ↗

Automate the Boring Stuff with Python

The celebrated free book for practical scripts — great follow-up after this module.

Open resource ↗

Chapter Quiz

Put this lesson to work: answer the questions below, get instant feedback and a pass/fail score. Score 70% or higher before you move on — that is your gate to the next topic.

Chapter Quiz — Python for Data Analysis

Notebooks, fundamentals, NumPy, the pandas DataFrame, cleaning, grouping and reproducibility.

0 / 5

Question 1 of 5

A Jupyter notebook produces different numbers than an hour ago. What is the fastest first fix?

Knowledge Check

Returns all rows where region is 'North' (rows selected by a boolean mask using labels). It is the pandas equivalent of a SQL WHERE — and comparison on a Series produces per-row True/False.
Vectorized methods run over the whole column in fast compiled code, are much faster at scale, and are more readable. Row-wise Python loops should be the exception, not the rule (occasionally .apply()).
The join key isn't unique on one side — many-to-many. Check for duplicates on the key in both tables before merging (df[n].duplicated().sum()), or select the grain you intend.
df['order_date'] = pd.to_datetime(df['order_date']). After that you can group by month (dt.to_period('M')) and sort chronologically correctly.
Cells were probably run out of order — use Kernel → Restart & Run All. If it persists, check for stale inputs (file changed, dtype changes) and any mutations of shared DataFrames.

End-of-Topic Exam

Hint: Parentheses around each condition in the mask; the word operators inside query.

Model answer: The boolean mask form is df.loc[(df['amount'].ge(500)) & (df['status'].eq('shipped'))], where ge and eq replace the comparison symbols and the ampersand combines conditions. The query form is df.query('amount ge 500 and status equals shipped') written in pandas' word operators, remembering that the mask uses the ampersand while query uses the word 'and'.

Hint: Row multiplication on an ununique key.

Model answer: The join key is not unique on one side, so the rows multiplied out — a many-to-many join. Check each side before merging with print(orders['customer_id'].duplicated().sum()) and print(customers['customer_id'].is_unique), then clean or aggregate the duplicates down to the intended grain and merge again.

Hint: Groupby with .agg and named tuples; verify the row count.

Model answer: The call is result = orders.groupby(['region', 'month']).agg(orders=('amount', 'count'), revenue=('amount', 'sum')).reset_index(), which names the columns orders and revenue. After merging anything, check merged.shape or merged.duplicated().sum() so a multiplication of rows never reaches your summaries silently.

Hint: Cell order first, then inputs, then mutations.

Model answer: One: cells were run out of order after an edit — fix with Kernel, Restart and Run All. Two: an input changed, such as a stale file, a changed dtype or an overwritten dataset — re-read the source and confirm the dtypes. Three: a shared DataFrame was mutated mid-analysis instead of copied — find in-place assignments and copy before transforming.

Next Step

Congratulations — you just finished Python for Data Analysis. The next topic is Exploratory Data Analysis. Press on when you are ready: it will build directly on what you practiced here.