Python Foundations: Complete Visual Guide

UT Austin | Great Learning PGP in AI & ML
Every concept explained from scratch. No assumptions. Real-life examples for everything.
Notes prepared by: Vamshi Krishan Reddy Bandaru

⚑ Python Foundations Cheat Sheet

Big Picture

This module covers Python from zero to data analysis. Four weeks of building blocks:

  • Week 1: Python basics β€” variables, data structures, loops, functions
  • Week 2: Data manipulation β€” NumPy arrays and Pandas DataFrames
  • Week 3: Exploratory Data Analysis β€” visualization and statistical insights
  • Week 4: Text analysis β€” string processing, regex, basic NLP

Vocabulary Quick Reference

TermDefinitionExample
VariableNamed container storing a valuex = 42
ListMutable ordered collection[1, 2, 3]
TupleImmutable ordered collection(1, 2, 3)
DictionaryKey-value pair mapping{"a": 1}
DataFrame2D labeled data table (Pandas)pd.DataFrame(data)
ndarrayN-dimensional array (NumPy)np.array([1,2,3])
EDAExploratory Data AnalysisHistograms, box plots, heatmaps
NaNNot a Number (missing value)np.nan
Boolean IndexingFilter rows by conditiondf[df['age'] > 30]
LambdaAnonymous one-line functionlambda x: x*2
List ComprehensionCompact list creation[x**2 for x in range(5)]
RegexRegular expression for pattern matchingre.findall(r'\d+', text)

Data Types at a Glance

Numeric Types
int β€” whole numbers: 42, -7
float β€” decimals: 3.14, -0.5
bool β€” True or False
complex β€” 3+4j (rarely used)
Text & Sequence
str β€” "Hello World"
list β€” [1, "a", True]
tuple β€” (1, "a", True)
range β€” range(0, 10, 2)
Mapping & Set
dict β€” {"key": "value"}
set β€” {1, 2, 3} (unique)
frozenset β€” immutable set
Bracket Guide
() β€” tuples, function calls
[] β€” lists, indexing
{} β€” dicts, sets
{k:v} β€” dict with key-value

Essential Operations

Python Basics (Week 1)
  • type(x) β€” check data type
  • int("5") β€” convert to integer
  • len(my_list) β€” count elements
  • my_list.append(x) β€” add to list
  • for i in range(n): β€” loop n times
  • if/elif/else β€” conditionals
  • def func(x): return x*2 β€” function
Data Tools (Weeks 2-3)
  • pd.read_csv("file.csv") β€” load data
  • df.head() β€” first 5 rows
  • df.describe() β€” summary stats
  • df.groupby("col").mean() β€” aggregate
  • df.isnull().sum() β€” count missing
  • plt.hist(data) β€” histogram
  • sns.heatmap(corr) β€” correlation map

Common Syntax Patterns

List Comprehension: [expression for item in iterable if condition]
squares = [x**2 for x in range(10) if x % 2 == 0] β†’ [0, 4, 16, 36, 64]
Lambda Function: lambda arguments: expression
double = lambda x: x * 2 β†’ double(5) = 10
String Formatting: f"Hello {name}, you are {age} years old"
f-strings (Python 3.6+) embed variables directly in strings
Floor Division: a // b (integer result, no decimal)
9 // 5 = 1 | 17 // 3 = 5 | -7 // 2 = -4 (rounds toward negative infinity)

πŸ—ΊοΈ Concept Map: Python Foundations Journey

Learning Path: From Zero to Data Analysis
Week 1: Python Basics
↓
Google Colab Setup
Variables & Types
Data Structures
↓
Control Flow
Functions
List Comprehension
↓
Week 2: Data Manipulation
↓
NumPy Arrays
Pandas DataFrames
Data Cleaning
↓
Week 3: EDA & Visualization
↓
Matplotlib
Seaborn
Statistical Analysis
↓
Week 4: Text Analysis
↓
String Methods
Regex
NLP Basics
↓
🎯 FoodHub Project

Week 1: Python Basics (15 Videos, ~2.75 hours)

Build the foundation β€” learn to think in Python. Every data science workflow starts here.

🎬
Videos 1.1-1.3: Google Colab setup β€” your free cloud coding environment. Markdown cells for documentation, code cells for execution.
🎬
Videos 1.4-1.6: Variables and type conversion β€” the building blocks. Python is dynamically typed, so x = 5 then x = "hello" is perfectly valid.
🎬
Videos 1.7-1.9: Data structures β€” lists (mutable, ordered), tuples (immutable, ordered), dictionaries (key-value pairs). The trio you will use in every project.
🎬
Videos 1.10-1.12: Control flow β€” conditionals (if/elif/else) and loops (for, while). The logic engine of every program.
🎬
Videos 1.13-1.15: List comprehension and functions β€” writing clean, reusable, Pythonic code. Lambda functions for quick one-liners.

Week 2: Data Manipulation (18 Videos)

Move from basic Python to data science tools. NumPy for numerical computing, Pandas for structured data.

πŸ“Š
NumPy: Fast numerical operations on arrays. Broadcasting, vectorized operations, and mathematical functions that work on entire arrays at once.
🐼
Pandas: The Swiss Army knife of data manipulation. Read CSV files, explore with head()/describe(), filter with boolean indexing, aggregate with groupby().
🧹
Data Cleaning: Handle missing values (isnull, dropna, fillna), fix data types, merge datasets, and prepare data for analysis.

Week 3: Exploratory Data Analysis (16 Videos)

Turn raw data into visual insights. Learn to see patterns, detect outliers, and tell stories with data.

πŸ“ˆ
Matplotlib: The foundation of Python plotting. Figures, axes, subplots, and full customization control.
🎨
Seaborn: Beautiful statistical visualizations built on Matplotlib. Heatmaps, pair plots, distribution plots with minimal code.
πŸ“
Statistical measures: mean, median, mode, variance, standard deviation, IQR for outlier detection, correlation for relationships.

Week 4: Analyzing Text Data (5 Videos)

Text is data too. Learn to process, clean, and extract insights from unstructured text.

πŸ“
String methods, regular expressions, tokenization, and word frequency analysis β€” the basics of Natural Language Processing.

πŸ’» 1.1 Google Colab & Python Setup

Week 1
Google Colab (Colaboratory)
A free, cloud-based Jupyter notebook environment by Google. Write and execute Python code in your browser β€” no installation required. Comes with free GPU/TPU access for machine learning.
Like Google Docs, but for code. Multiple people can edit the same notebook simultaneously, and everything saves to Google Drive.
Jupyter Notebook (.ipynb)
An interactive document format that combines code, text, and visualizations in a single file. The .ipynb extension stands for "IPython Notebook." Colab uses this format natively.
A scientist's lab notebook β€” you write observations (Markdown), run experiments (code cells), and see results (outputs) all in one place.
Markdown Cell
A cell in Colab for writing formatted text using Markdown syntax. Use # for headings, **bold** for bold, *italic* for italic, and - for bullet points.
The table of contents and chapter headers in your lab notebook β€” they organize and explain your work.
Think of Google Colab as a free cooking show kitchen. The stove (Python engine) is already set up, the ingredients (libraries like NumPy, Pandas) are pre-stocked, and you just need to follow the recipe (write code). You do not need to buy any equipment or install anything β€” just show up and start cooking.
Colab Notebook Structure
Notebook (.ipynb)
↓
Markdown Cells (Text + Formatting)
Code Cells (Python Execution)
↓
Output (Results, Charts, Tables)

Getting Started Steps

1
Go to colab.research.google.com and sign in with your Google account.
2
Click "New Notebook" β€” a fresh .ipynb file opens connected to a cloud Python runtime.
3
Type Python code in a code cell and press Shift + Enter to execute. The output appears below the cell.
4
Add a text cell (Insert β†’ Text cell) to write notes using Markdown formatting.
5
Save your work: it auto-saves to Google Drive. Download via File β†’ Download β†’ Download .ipynb.

Essential Keyboard Shortcuts

ShortcutActionWhen to Use
Shift + EnterRun cell and move to nextExecute code and continue
Ctrl + EnterRun cell, stay in placeTest code without moving
Ctrl + M, BInsert cell belowAdd new code/text cell
Ctrl + M, DDelete current cellRemove unwanted cells
Ctrl + M, MConvert to MarkdownSwitch cell type to text
Ctrl + M, YConvert to CodeSwitch cell type to code
Ctrl + /Toggle commentComment/uncomment lines
# Your very first Python code in Colab
print("Hello, World!")  # Output: Hello, World!

# Python is case-sensitive!
Name = "Alice"
name = "Bob"
print(Name)  # Alice
print(name)  # Bob  β€” these are DIFFERENT variables

# Comments: # for single-line, triple quotes for multi-line
'''
This is a multi-line comment.
It can span several lines.
Useful for documentation.
'''
⚠️
Python is case-sensitive! HelloWorld and helloworld are completely different. This is one of the most common beginner mistakes (FAQ #2 from the course).
Test Your Understanding
Q: How do you download a Colab notebook to your computer?
Q: What happens if you write Print("Hello") instead of print("Hello")?

πŸ”€ 1.2 Variables & Data Types

Week 1
Variable
A named container that stores a value in memory. In Python, you create a variable simply by assigning a value: x = 10. No need to declare the type β€” Python figures it out automatically (dynamic typing).
A labeled jar in your kitchen. The label says "sugar" (variable name), and inside is the actual sugar (the value). You can empty it and fill it with salt later β€” the jar does not care what is inside.
Dynamic Typing
Python determines the type of a variable at runtime based on the value assigned. You can reassign a variable to a completely different type without errors. Use type(x) to check the current type.
A parking spot that accepts any vehicle β€” car, motorcycle, or truck. The spot does not need a sign saying "cars only."
Type Conversion (Casting)
Explicitly changing a value from one type to another using functions like int(), float(), str(), bool(). Essential when combining different types β€” you cannot concatenate a string and an integer directly.
Converting currency β€” changing US dollars to Euros. The value stays the same in principle, but the format changes so it can be used in a different context.
Variables are like phone contacts. The contact name ("Mom") is the variable name, and the phone number (555-1234) is the value. You can update the number anytime, and the name still works. You can even change "Mom" to store an email address instead of a phone number β€” Python does not mind.

Python Data Types

TypeDescriptionExampleCheck With
intWhole numbers (no decimal)x = 42type(42) β†’ <class 'int'>
floatDecimal numberspi = 3.14type(3.14) β†’ <class 'float'>
strText (string of characters)name = "Alice"type("hi") β†’ <class 'str'>
boolTrue or Falseis_valid = Truetype(True) β†’ <class 'bool'>
NoneTypeAbsence of valuex = Nonetype(None) β†’ <class 'NoneType'>

int32 vs int64 (FAQ #8)

When working with NumPy and Pandas, you will encounter int32 and int64:

  • int32: Uses 4 bytes (32 bits) of memory. Range: -2,147,483,648 to 2,147,483,647
  • int64: Uses 8 bytes (64 bits) of memory. Range: approximately -9.2 Γ— 10¹⁸ to 9.2 Γ— 10¹⁸

For most data science work, int64 is the default and perfectly fine. Only optimize to int32 when memory is a constraint with large datasets.

Type Conversion Flow
str("42") β†’ "42"
int("42") β†’ 42
float("42") β†’ 42.0
↕ Convert Between Types
str(42) β†’ "42"
int(42.9) β†’ 42
float(42) β†’ 42.0
⚠️ Watch Out!
int("hello") β†’ ValueError!
int("42.5") β†’ ValueError!
# Variable assignment β€” no type declaration needed
age = 25             # int
height = 5.9          # float
name = "Vamshi"       # str
is_student = True     # bool

# Check types
print(type(age))       # <class 'int'>
print(type(height))    # <class 'float'>

# Type conversion
age_str = str(age)     # "25" β€” now a string
price = float("19.99") # 19.99 β€” string to float

# Common mistake: concatenating str + int (FAQ #14)
count = 5
# print("I have " + count + " apples")  # TypeError!
print("I have " + str(count) + " apples")  # Fixed!
print(f"I have {count} apples")  # Better: f-string

# Comparison operators (FAQ #4)
print(10 == 10)   # True  (equal to)
print(10 != 5)    # True  (not equal)
print(10 > 5)     # True  (greater than)
print(10 >= 10)   # True  (greater or equal)
print(10 < 20)    # True  (less than)

# Floor division (FAQ #15)
print(9 // 5)     # 1 (not 1.8)
print(9 / 5)      # 1.8 (regular division)
print(9 % 5)      # 4 (modulo β€” remainder)
πŸ’‘
TypeError: can only concatenate str to str. This is one of the most common Python errors (FAQ #14). Fix it by converting the number to a string with str() or by using f-strings: f"value is {x}".
Test Your Understanding
Q: What does type(42) return in Python?
Q: What is the result of 9 // 5?

πŸ“¦ 1.3 Data Structures: Lists, Tuples, Dictionaries

Week 1
List
A mutable, ordered collection of items enclosed in square brackets []. Lists can hold mixed types (integers, strings, booleans, even other lists). Created with list() or [].
A shopping list on paper β€” you can add items, cross them off, rearrange them, and they stay in order.
Tuple
An immutable, ordered collection enclosed in parentheses (). Once created, you cannot add, remove, or modify elements. Created with tuple() or ().
A sealed time capsule β€” once you put items in and seal it, you can look at the contents but cannot change them.
Dictionary
A mutable, unordered collection of key-value pairs enclosed in curly braces {}. Access values by their unique key, not by position. Created with dict() or {}.
A real dictionary book β€” you look up a word (key) to find its definition (value). You do not search by page number.
A list is a shelf with numbered slots β€” items stay in order, you can swap them. A tuple is a display case with a glass cover β€” look but do not touch. A dictionary is a filing cabinet β€” each drawer has a label (key), inside is the content (value).

Comparison: List vs Tuple vs Dictionary (FAQ #5)

FeatureListTupleDictionary
Syntax[1, 2, 3](1, 2, 3){"a": 1}
Mutable?Yes β€” can add/remove/changeNo β€” immutableYes β€” can add/remove/change
Ordered?Yes β€” maintains insertion orderYes β€” maintains insertion orderYes (Python 3.7+)
Access ByIndex: lst[0]Index: tpl[0]Key: dct["key"]
Duplicates?AllowedAllowedKeys must be unique
Use CaseDynamic collectionsFixed data (coordinates, constants)Lookups, mappings
Constructorlist()tuple()dict()

Bracket Guide (FAQ #6)

One of the most confusing things for beginners β€” which bracket for what?

( ) Parentheses
Tuples: (1, 2, 3)
Function calls: print("hi")
Grouping: (2 + 3) * 4
[ ] Square Brackets
Lists: [1, 2, 3]
Indexing: my_list[0]
Slicing: my_list[1:3]
{ } Curly Braces
Dicts: {"a": 1}
Sets: {1, 2, 3}
f-strings: f"{var}"
# ============ LISTS ============
fruits = ["apple", "banana", "cherry"]
print(fruits[0])      # "apple" β€” indexing starts at 0
print(fruits[-1])     # "cherry" β€” negative index = from end

fruits.append("date")   # Add to end: ["apple","banana","cherry","date"]
fruits.remove("banana") # Remove by value
fruits.insert(1, "fig") # Insert at index 1
print(len(fruits))     # 3 β€” number of items

# Slicing: list[start:stop:step]
nums = [0, 1, 2, 3, 4, 5]
print(nums[1:4])     # [1, 2, 3] β€” start inclusive, stop exclusive
print(nums[:3])      # [0, 1, 2] β€” from beginning
print(nums[3:])      # [3, 4, 5] β€” to end
print(nums[::2])     # [0, 2, 4] β€” every 2nd element

# ============ TUPLES ============
coords = (10.5, 20.3)
print(coords[0])     # 10.5
# coords[0] = 99      # TypeError! Tuples are immutable

# ============ DICTIONARIES ============
student = {
    "name": "Vamshi",
    "age": 25,
    "courses": ["Python", "ML"]
}
print(student["name"])     # "Vamshi" β€” access by key (FAQ #7)
student["grade"] = "A"    # Add new key-value pair
print(student.keys())     # dict_keys(['name', 'age', 'courses', 'grade'])
print(student.values())   # dict_values(['Vamshi', 25, ['Python', 'ML'], 'A'])
Indexing Visual β€” How Python Counts
0
1
2
3
4
["a", "b", "c", "d", "e"] β€” Forward index (0 to n-1)
-5
-4
-3
-2
-1
Negative index β€” counts from the end

Common List Methods

MethodWhat It DoesExample
.append(x)Add x to end[1,2].append(3) β†’ [1,2,3]
.insert(i, x)Insert x at index i[1,3].insert(1,2) β†’ [1,2,3]
.remove(x)Remove first occurrence of x[1,2,2].remove(2) β†’ [1,2]
.pop(i)Remove & return item at index i[1,2,3].pop(0) β†’ 1
.sort()Sort in place (ascending)[3,1,2].sort() β†’ [1,2,3]
.reverse()Reverse in place[1,2,3].reverse() β†’ [3,2,1]
.index(x)Find index of first x[10,20,30].index(20) β†’ 1
.count(x)Count occurrences of x[1,1,2].count(1) β†’ 2
.extend(lst)Add all items from another list[1].extend([2,3]) β†’ [1,2,3]
Test Your Understanding
Q: Which data structure uses curly braces {} and stores key-value pairs?
Q: What happens when you try my_tuple[0] = "new" on an existing tuple?

πŸ”€ 1.4 Control Flow: Conditionals & Loops

Week 1
Conditional Statement (if/elif/else)
A control structure that executes different blocks of code based on whether a condition is True or False. Python uses indentation (4 spaces) to define code blocks β€” no curly braces like other languages.
A traffic light β€” if green, go. Elif (else if) yellow, slow down. Else (red), stop. Only one action happens based on the current state.
For Loop
Iterates over a sequence (list, tuple, string, range) executing a block of code for each element. The variable i (or any name you choose) takes on each value in the sequence, one at a time (FAQ #13).
A postal worker delivering mail β€” they visit each house on the street in order, drop off the mail, then move to the next house.
While Loop
Repeats a block of code as long as a condition remains True. Be careful β€” if the condition never becomes False, you get an infinite loop! Always make sure the condition will eventually change (FAQ #12).
Filling a bathtub β€” keep the water running WHILE the tub is not full. Once it is full, stop. If you forget to check, the water overflows (infinite loop).
Control flow is like a choose-your-own-adventure book. At each decision point (if), you pick a path. For loops know how many times to repeat; while loops keep going until a condition changes.
Control Flow Decision Tree
Start
↓
Evaluate Condition
↓
True β†’ Execute Block
False β†’ Skip / Try elif / else
↓
Continue to Next Statement
# ============ CONDITIONALS ============
score = 85

if score >= 90:
    print("Grade: A")
elif score >= 80:
    print("Grade: B")    # This runs! score=85 >= 80
elif score >= 70:
    print("Grade: C")
else:
    print("Grade: F")

# Logical operators: and, or, not
age = 25
has_license = True
if age >= 18 and has_license:
    print("Can drive")

# ============ FOR LOOP ============
# Iterate over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(f"I like {fruit}")

# Using range(): range(start, stop, step) β€” FAQ #13
for i in range(0, 10, 2):   # 0, 2, 4, 6, 8
    print(i, end=" ")

# Nested loop with enumerate
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")   # 0: apple, 1: banana, 2: cherry

# ============ WHILE LOOP ============
count = 0
while count < 5:       # FAQ #12: runs while condition is True
    print(count)
    count += 1         # Don't forget this! Otherwise infinite loop

# break and continue
for i in range(10):
    if i == 3: continue   # Skip 3
    if i == 7: break      # Stop at 7
    print(i, end=" ")       # Output: 0 1 2 4 5 6

For Loop vs While Loop

For Loop β€” Use When:
  • You know how many iterations
  • Iterating over a sequence
  • Processing each item in a collection
  • Using range() for counted loops
While Loop β€” Use When:
  • Unknown number of iterations
  • Waiting for a condition to change
  • User input validation
  • Running until a flag is set
Test Your Understanding
Q: What does range(1, 10, 3) produce?
Q: What keyword skips the current iteration and moves to the next one?

βš™οΈ 1.5 Functions & List Comprehension

Week 1
Function
A reusable block of code that performs a specific task. Defined with the def keyword, it can accept inputs (parameters) and return outputs. Functions eliminate code duplication and make programs modular.
A coffee machine β€” you put in water and beans (inputs), press a button (call the function), and get coffee (output). You do not need to know how the machine works internally to use it.
Return Statement
Sends a value back from a function to the caller. Without return, the function returns None by default. A function can return multiple values as a tuple: return a, b.
A vending machine β€” you insert money (arguments), the machine processes your selection, and returns the snack to you. If you just press buttons without inserting money, you get nothing (None).
Lambda Function
An anonymous, one-line function defined with the lambda keyword. Syntax: lambda arguments: expression. Best for simple operations where defining a full function would be overkill.
A sticky note with a quick formula β€” instead of writing a full instruction manual, you jot down "multiply by 2" on a Post-it and stick it where needed.
List Comprehension
A concise, Pythonic way to create lists in a single line. Syntax: [expression for item in iterable if condition]. It replaces multi-line for loops with a compact, readable expression.
A factory assembly line β€” raw materials (iterable) come in, each gets processed (expression), and defective ones are filtered out (condition). The output is a list of finished products.
Functions are recipes in a cookbook β€” define once, call by name whenever needed. Lambda functions are quick recipes on a napkin. List comprehensions are speed-cooking: make 10 dishes at once in one instruction.
# ============ DEFINING FUNCTIONS ============
def greet(name):
    """Greet a person by name."""
    return f"Hello, {name}!"

print(greet("Vamshi"))   # "Hello, Vamshi!"

# Default parameters
def power(base, exp=2):
    return base ** exp

print(power(3))       # 9 (uses default exp=2)
print(power(3, 3))    # 27 (exp=3)

# Multiple return values
def min_max(numbers):
    return min(numbers), max(numbers)

lo, hi = min_max([4, 2, 9, 1])
print(f"Min: {lo}, Max: {hi}")  # Min: 1, Max: 9

# ============ LAMBDA FUNCTIONS ============
double = lambda x: x * 2
print(double(5))     # 10

# Lambda with sorted
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
by_score = sorted(students, key=lambda s: s[1], reverse=True)
# [('Bob', 92), ('Alice', 85), ('Charlie', 78)]

# ============ LIST COMPREHENSION ============
# Instead of:
squares = []
for x in range(10):
    squares.append(x**2)

# Write this:
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# With condition: only even squares
even_sq = [x**2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]

# Nested: flatten 2D list
flat = [n for row in [[1,2],[3,4]] for n in row]  # [1,2,3,4]
Function Anatomy
def function_name(params):
↓
"""Docstring: what it does"""
↓
Function body (indented)
↓
return result
πŸ’‘
Pythonic Code: List comprehensions are considered more "Pythonic" than equivalent for loops. They are also slightly faster because the loop happens internally in C. Use them for simple transformations. For complex logic, stick with regular for loops for readability.
Test Your Understanding
Q: What does [x*2 for x in [1,2,3] if x > 1] return?

πŸ”’ 2.1 NumPy: Numerical Computing

Week 2
NumPy (Numerical Python)
A fundamental Python library for numerical computing. Its core object is the ndarray (N-dimensional array), which enables fast mathematical operations on large datasets. NumPy is the foundation upon which Pandas, scikit-learn, and most data science libraries are built.
A calculator that can handle millions of numbers at once. Instead of adding numbers one by one in a loop, NumPy adds entire arrays in a single operation β€” like having a million calculators working simultaneously.
Vectorized Operations
Operations that apply to entire arrays at once, without explicit loops. NumPy executes these in optimized C code, making them 10-100x faster than Python loops. Examples: arr * 2, arr1 + arr2, np.sqrt(arr).
A factory production line that stamps all parts at once, vs. a craftsman filing each part individually.
Broadcasting
NumPy's ability to perform operations on arrays of different shapes by automatically expanding the smaller array to match. For example, adding a scalar to an array or adding a 1D array to each row of a 2D array.
Giving a $5 raise to every employee in a company. You state the raise once, and it "broadcasts" to everyone's salary β€” you do not need to write each person's new salary individually.
Think of Python lists as a regular backpack β€” flexible, can hold anything, but slow to search through. NumPy arrays are like an organized toolbox β€” everything has a fixed type and position, so finding and using tools is lightning fast. The trade-off: the toolbox only holds one type of item (all integers, all floats, etc.), but it is extremely efficient.

NumPy vs Python Lists

FeaturePython ListNumPy Array
SpeedSlow (interpreted loop)Fast (C-compiled vectorized)
TypesMixed types allowedHomogeneous (one type only)
OperationsElement-by-element manualVectorized (whole-array at once)
MemoryMore (each element is an object)Less (contiguous memory block)
Methods.append(), .sort().mean(), .std(), .reshape()
Math[1,2]+[3,4]=[1,2,3,4] (concat)arr([1,2])+arr([3,4])=arr([4,6])
import numpy as np

# Creating arrays
arr = np.array([1, 2, 3, 4, 5])
zeros = np.zeros((3, 4))        # 3x4 matrix of zeros
ones = np.ones((2, 3))          # 2x3 matrix of ones
rng = np.arange(0, 10, 2)      # [0, 2, 4, 6, 8]
lin = np.linspace(0, 1, 5)     # [0, 0.25, 0.5, 0.75, 1.0]

# Array properties
print(arr.shape)     # (5,) β€” dimensions
print(arr.dtype)     # int64 β€” data type
print(arr.ndim)      # 1 β€” number of dimensions

# Vectorized operations β€” no loops needed!
print(arr * 2)       # [2, 4, 6, 8, 10]
print(arr ** 2)      # [1, 4, 9, 16, 25]
print(np.sqrt(arr))  # [1.0, 1.41, 1.73, 2.0, 2.24]

# Statistical functions
print(arr.mean())    # 3.0
print(arr.std())     # 1.414
print(arr.sum())     # 15
print(arr.min())     # 1
print(arr.max())     # 5

# Indexing and slicing (same as lists)
matrix = np.array([[1,2,3], [4,5,6], [7,8,9]])
print(matrix[0, 1])    # 2 (row 0, col 1)
print(matrix[:2, 1:])  # [[2,3],[5,6]] β€” first 2 rows, cols 1+

# Boolean indexing
print(arr[arr > 3])    # [4, 5] β€” filter elements

# Reshape
reshaped = np.arange(12).reshape(3, 4)  # 3 rows, 4 cols
Test Your Understanding
Q: What is the main advantage of NumPy arrays over Python lists for numerical data?

🐼 2.2 Pandas: Data Manipulation

Week 2
Pandas
The most popular Python library for data manipulation and analysis. It provides two primary data structures: Series (1D labeled array) and DataFrame (2D labeled table with rows and columns). Think of a DataFrame as a supercharged Excel spreadsheet that you can program.
Excel on steroids β€” you can load a million-row CSV file, filter it, group it, merge it with another file, and compute statistics, all in a few lines of code.
DataFrame
A 2D labeled data structure with named columns and an index. Each column is a Pandas Series. Created from dictionaries, CSV files, Excel files, or SQL queries. The workhorse of data science in Python.
A database table or Excel spreadsheet β€” rows are individual records (customers, transactions), columns are attributes (name, age, purchase amount).
Boolean Indexing
Filtering rows in a DataFrame using a condition that returns True/False for each row. Syntax: df[df['column'] > value]. You can combine conditions with & (and) and | (or).
Telling a librarian "Show me only the books published after 2020 that are science fiction" β€” the librarian checks each book and hands you only the matching ones.
Pandas is like hiring a personal assistant for your data. You say "read this CSV file" (pd.read_csv), "show me the first few rows" (.head()), "what are the stats?" (.describe()), "group by department and give me averages" (.groupby().mean()). The assistant does all the tedious work instantly.
import pandas as pd

# Creating a DataFrame from a dictionary
data = {
    "Name": ["Alice", "Bob", "Charlie", "Diana"],
    "Age": [25, 30, 35, 28],
    "Salary": [50000, 60000, 75000, 55000]
}
df = pd.DataFrame(data)

# Reading from a CSV file
df = pd.read_csv("employees.csv")

# First look at data β€” the essential 5
print(df.head())        # First 5 rows
print(df.tail())        # Last 5 rows
print(df.shape)         # (rows, columns)
print(df.info())        # Column types, non-null counts
print(df.describe())    # Summary statistics

# Selecting columns
print(df["Name"])       # Single column β†’ Series
print(df[["Name", "Age"]])  # Multiple columns β†’ DataFrame

# Selecting rows: loc (label-based) vs iloc (integer-based)
print(df.loc[0])          # First row by label
print(df.iloc[0:3])      # First 3 rows by position

# Boolean indexing β€” filtering
seniors = df[df["Age"] > 30]
high_earners = df[(df["Salary"] > 55000) & (df["Age"] < 35)]

# Sorting
df_sorted = df.sort_values("Salary", ascending=False)

# Groupby β€” split-apply-combine
dept_avg = df.groupby("Department")["Salary"].mean()

# Adding new columns
df["Bonus"] = df["Salary"] * 0.1

# Apply custom function
df["Category"] = df["Age"].apply(lambda x: "Senior" if x > 30 else "Junior")

Essential Pandas Methods

MethodPurposeExample
.head(n)First n rowsdf.head(10)
.describe()Summary statisticsdf.describe()
.value_counts()Frequency of valuesdf["City"].value_counts()
.groupby()Group and aggregatedf.groupby("Dept").mean()
.sort_values()Sort by columndf.sort_values("Age")
.merge()Join two DataFramespd.merge(df1, df2, on="ID")
.concat()Stack DataFramespd.concat([df1, df2])
.apply()Apply function to columndf["Col"].apply(func)
.unique()Unique valuesdf["City"].unique()
.nunique()Count of unique valuesdf["City"].nunique()
GroupBy: Split β†’ Apply β†’ Combine
Original DataFrame
↓ .groupby("Department")
Group: Sales
Group: Engineering
Group: Marketing
↓ .mean() / .sum() / .count()
Aggregated Result per Group
Test Your Understanding
Q: How do you select rows where the "Age" column is greater than 30?

🧹 2.3 Data Cleaning & Preparation

Week 2
Missing Values (NaN)
Entries in your dataset that have no value. Represented as NaN (Not a Number) in Pandas. Can occur due to data entry errors, sensor failures, or optional fields. Must be handled before analysis because they propagate through calculations.
Blank cells in a spreadsheet β€” some customers did not fill in their phone number. You cannot compute an "average phone number," so you must decide: fill in a default, remove those rows, or ignore that column.
Data Type Conversion
Changing the data type of a column to the appropriate type. Common conversions: string to datetime, object to category, string to numeric. Use df['col'].astype() or pd.to_datetime(), pd.to_numeric().
Converting currency strings like "$1,500.00" to the number 1500.0 so you can do math with them.
Data cleaning is like preparing ingredients before cooking. You wash vegetables (remove duplicates), peel and chop them (type conversion), and check for any rotten pieces (missing values). No chef starts cooking with dirty, unprepped ingredients β€” and no data scientist starts analyzing dirty data.
import pandas as pd
import numpy as np

# ============ MISSING VALUES ============
# Detect missing values
print(df.isnull().sum())         # Count NaN per column
print(df.isnull().sum() / len(df) * 100)  # Percentage missing

# Strategy 1: Drop rows with any NaN
df_clean = df.dropna()

# Strategy 2: Drop columns with too many NaN
df_clean = df.dropna(axis=1, thresh=len(df)*0.5)  # Keep cols with >50% data

# Strategy 3: Fill missing values
df["Age"].fillna(df["Age"].mean(), inplace=True)   # Fill with mean
df["City"].fillna("Unknown", inplace=True)     # Fill with constant
df["Score"].fillna(df["Score"].median(), inplace=True)  # Median for skewed data

# ============ DUPLICATES ============
print(df.duplicated().sum())     # Count duplicate rows
df_clean = df.drop_duplicates()   # Remove duplicates

# ============ TYPE CONVERSION ============
df["Date"] = pd.to_datetime(df["Date"])
df["Price"] = pd.to_numeric(df["Price"], errors="coerce")

# ============ STRING CLEANING ============
df["Name"] = df["Name"].str.strip()     # Remove whitespace
df["Name"] = df["Name"].str.lower()    # Lowercase
df["Email"] = df["Email"].str.replace("@old.com", "@new.com")

# ============ MERGING DATASETS ============
merged = pd.merge(df1, df2, on="CustomerID", how="left")
stacked = pd.concat([df_2023, df_2024], ignore_index=True)

Missing Value Strategy Decision Guide

ScenarioStrategyCode
Very few missing (<5%)Drop rowsdf.dropna()
Numerical, normally distributedFill with meandf["col"].fillna(df["col"].mean())
Numerical, skewedFill with mediandf["col"].fillna(df["col"].median())
CategoricalFill with modedf["col"].fillna(df["col"].mode()[0])
Column >50% missingDrop columndf.drop("col", axis=1)
Time seriesForward/backward filldf["col"].fillna(method="ffill")
Test Your Understanding
Q: Which method counts the number of missing values per column?

πŸ“Š 3.1 Data Visualization

Week 3
Matplotlib
The foundational Python plotting library. Provides complete control over every element of a figure: axes, labels, colors, sizes, legends. Most other Python visualization libraries (including Seaborn) are built on top of Matplotlib.
A blank canvas with all the paint colors β€” you can create any visualization, but you must specify every detail yourself.
Seaborn
A statistical visualization library built on Matplotlib. Provides beautiful, informative plots with minimal code. Especially good for showing distributions, relationships, and categorical comparisons.
A paint-by-numbers kit β€” you get professional-looking results with much less effort. Perfect for statistical plots.
Matplotlib is like cooking from scratch β€” full control, but you do everything yourself. Seaborn is like using a meal kit β€” pre-measured ingredients, beautiful presentation, and it still tastes great. Most data scientists use both: Seaborn for quick statistical plots, Matplotlib for custom fine-tuning.

Chart Selection Guide

QuestionChart TypeCode
Distribution of one variable?Histogram / KDEsns.histplot(df["Age"])
Relationship between two numeric?Scatter plotplt.scatter(x, y)
Comparison across categories?Bar chartsns.countplot(x="City", data=df)
Spread and outliers?Box plotsns.boxplot(x="Dept", y="Salary")
Correlation between all pairs?Heatmapsns.heatmap(df.corr())
Trend over time?Line chartplt.plot(dates, values)
All pairwise relationships?Pair plotsns.pairplot(df)
Proportions of a whole?Pie chartplt.pie(sizes, labels=labels)
import matplotlib.pyplot as plt
import seaborn as sns

# ============ MATPLOTLIB BASICS ============
# Simple line plot
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot([1,2,3,4], [10,20,25,30], marker='o')
ax.set_xlabel("Quarter")
ax.set_ylabel("Revenue ($K)")
ax.set_title("Quarterly Revenue")
plt.show()

# Multiple subplots
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].hist(df["Age"], bins=20)
axes[1].scatter(df["Age"], df["Salary"])
axes[2].boxplot(df["Salary"])
plt.tight_layout()
plt.show()

# ============ SEABORN ============
# Distribution plot
sns.histplot(df["Age"], kde=True, bins=20)

# Correlation heatmap
plt.figure(figsize=(10, 8))
sns.heatmap(df.corr(), annot=True, cmap="coolwarm", vmin=-1, vmax=1)

# Box plot for comparing groups
sns.boxplot(x="Department", y="Salary", data=df)

# Pair plot β€” all relationships at once
sns.pairplot(df, hue="Category")
Test Your Understanding
Q: Which plot is best for showing the correlation between all numerical variables at once?

πŸ” 3.2 EDA Techniques & Statistics

Week 3
Exploratory Data Analysis (EDA)
A systematic approach to investigating datasets using summary statistics and visualizations before formal modeling. The goal is to discover patterns, spot anomalies, test hypotheses, and check assumptions. John Tukey coined the term in 1977.
A detective examining a crime scene β€” before forming theories, you observe everything: what is there, what is missing, what looks unusual, what patterns emerge.
Outlier
A data point that lies significantly outside the overall pattern of the data. Can be detected using the IQR method (below Q1-1.5Γ—IQR or above Q3+1.5Γ—IQR) or Z-score (more than 3 standard deviations from the mean). May indicate errors, fraud, or genuinely extreme values.
In a classroom of 30 students with ages 18-22, one "student" listed as age 97 is clearly an outlier β€” likely a data entry error.
Correlation
A statistical measure (-1 to +1) of the linear relationship between two variables. +1 means perfect positive correlation, -1 means perfect negative, 0 means no linear relationship. Calculated with df.corr(). Remember: correlation does not imply causation.
Ice cream sales and drowning deaths are positively correlated β€” not because ice cream causes drowning, but because both increase in summer (confounding variable: hot weather).
EDA is like a doctor doing a checkup β€” take vital signs (summary stats), look at X-rays (visualizations), check for symptoms (outliers), and run blood tests (correlations) before diagnosing (modeling).

EDA Checklist

1
Shape & Structure: df.shape, df.info(), df.dtypes β€” How big is the data? What types are the columns?
2
Summary Statistics: df.describe() β€” Mean, median, min, max, quartiles for every numerical column.
3
Missing Values: df.isnull().sum() β€” Which columns have gaps? How much data is missing?
4
Univariate Analysis: Histograms and box plots for each variable individually β€” What does each variable look like?
5
Bivariate Analysis: Scatter plots, grouped bar charts β€” How do variables relate to each other?
6
Correlation Analysis: sns.heatmap(df.corr()) β€” Which variables move together?
7
Outlier Detection: IQR method or Z-scores β€” Are there extreme values that need attention?

Statistical Measures Quick Reference

Central Tendency
Mean: df["col"].mean() β€” average, sensitive to outliers
Median: df["col"].median() β€” middle value, robust to outliers
Mode: df["col"].mode() β€” most frequent value
Spread / Dispersion
Std Dev: df["col"].std() β€” average distance from mean
Variance: df["col"].var() β€” std dev squared
IQR: Q3 - Q1 β€” spread of middle 50%
Range: max - min β€” total spread
Shape
Skewness: df["col"].skew()
0 = symmetric, >0 = right-skewed, <0 = left-skewed
Kurtosis: df["col"].kurtosis()
Measures tail heaviness (outlier-proneness)
Outlier Detection: IQR
Q1 = 25th percentile
Q3 = 75th percentile
IQR = Q3 - Q1
Lower fence = Q1 - 1.5 Γ— IQR
Upper fence = Q3 + 1.5 Γ— IQR
# Outlier detection using IQR method
Q1 = df["Salary"].quantile(0.25)
Q3 = df["Salary"].quantile(0.75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# Filter outliers
outliers = df[(df["Salary"] < lower_bound) | (df["Salary"] > upper_bound)]
print(f"Found {len(outliers)} outliers")

# Correlation matrix
corr_matrix = df.corr()
print(corr_matrix)
Test Your Understanding
Q: If a dataset has a few extreme high values, which measure of central tendency is more reliable?

πŸ“ 4.1 Analyzing Text Data

Week 4
Regular Expression (Regex)
A powerful pattern-matching language for searching, extracting, and manipulating text. Python provides the re module. Common patterns: \d (digit), \w (word character), \s (whitespace), . (any character), * (0 or more), + (1 or more).
A metal detector on a beach β€” you define what you are looking for (pattern), and the regex engine scans through the entire text to find all matches.
Tokenization
The process of splitting text into individual units (tokens), typically words or sentences. The first step in most text analysis pipelines. Can be done with .split() for simple cases or with NLP libraries for complex text.
Breaking a sentence into individual words β€” like taking apart a LEGO structure into its individual bricks before sorting and counting them.
Stop Words
Common words (the, is, at, which, on) that carry little meaningful information and are typically removed during text preprocessing. Removing them reduces noise and improves analysis quality.
In a book review, words like "the," "is," and "was" appear everywhere but tell you nothing about whether the reviewer liked the book. The meaningful words are "excellent," "boring," "recommended."
Text analysis is like being a librarian organizing documents: break into words (tokenization), ignore filler words (stop word removal), count important words (frequency analysis), and use patterns to find specific info like phone numbers (regex).
import re

# ============ STRING METHODS ============
text = "  Hello, World! Welcome to Python.  "

print(text.strip())       # Remove leading/trailing whitespace
print(text.lower())       # "  hello, world! welcome to python.  "
print(text.upper())       # "  HELLO, WORLD! WELCOME TO PYTHON.  "
print(text.replace("World", "Data Science"))
print(text.split())        # Split by whitespace β†’ list of words
print(", ".join(["a", "b", "c"]))  # "a, b, c"

# ============ REGULAR EXPRESSIONS ============
text = "Contact us at [email protected] or call 555-123-4567"

# Find all email addresses
emails = re.findall(r'[\w.]+@[\w.]+', text)
print(emails)  # ['[email protected]']

# Find all phone numbers
phones = re.findall(r'\d{3}-\d{3}-\d{4}', text)
print(phones)  # ['555-123-4567']

# Search for a pattern
match = re.search(r'\d+', "Order #12345")
print(match.group())  # "12345"

# ============ WORD FREQUENCY ============
from collections import Counter

text = "the cat sat on the mat the cat likes the mat"
words = text.lower().split()
freq = Counter(words)
print(freq.most_common(3))
# [('the', 4), ('cat', 2), ('mat', 2)]

# Remove stop words
stop_words = {"the", "on", "a", "an", "is", "in"}
filtered = [w for w in words if w not in stop_words]
print(Counter(filtered).most_common(3))
# [('cat', 2), ('mat', 2), ('sat', 1)]

Common Regex Patterns

PatternMatchesExample
\dAny digit (0-9)\d{3} matches "123"
\wWord character (a-z, A-Z, 0-9, _)\w+ matches "hello_world"
\sWhitespace (space, tab, newline)\s+ matches any spaces
.Any character except newlinea.c matches "abc", "a1c"
+1 or more of previous\d+ matches "123"
*0 or more of previousab*c matches "ac", "abc"
^Start of string^Hello matches "Hello world"
$End of stringworld$ matches "Hello world"
[abc]Any of a, b, or c[aeiou] matches vowels
Test Your Understanding
Q: What does re.findall(r'\d+', "I have 3 cats and 12 fish") return?

⚠️ Common Mistakes & Misconceptions

Week 1: Python Basics

1. "Python is not case-sensitive"
Writing Print("hello") instead of print("hello"), or using True as true. Python IS case-sensitive β€” every character matters.
βœ“ Always use lowercase for built-in functions: print(), len(), type(). Boolean values are capitalized: True, False, None.
2. Concatenating strings and numbers directly
"I am " + 25 + " years old" β†’ TypeError: can only concatenate str to str (FAQ #14)
βœ“ Convert with str(): "I am " + str(25) or use f-strings: f"I am {25} years old". F-strings handle conversion automatically.
3. Confusing = and ==
Using x = 5 when you mean to check equality x == 5. One assigns, the other compares.
βœ“ = is assignment (x = 5 stores 5 in x). == is comparison (x == 5 returns True/False). Use == in if statements.
4. Modifying a list while iterating over it
Removing items from a list inside a for loop causes skipped elements or IndexError.
βœ“ Create a new list with list comprehension: filtered = [x for x in items if x != "remove"]. Never modify a collection while looping through it.
5. Forgetting that range() excludes the stop value
range(1, 5) produces [1, 2, 3, 4], NOT [1, 2, 3, 4, 5]. Beginners expect 5 to be included.
βœ“ range(start, stop) goes from start to stop-1. To include 5: range(1, 6). Think of it as "up to but not including."

Week 2: Data Manipulation

6. Using df["col"] = df["col"].fillna(value) without inplace
Writing df["Age"].fillna(0) but forgetting to assign it back. fillna() returns a new Series by default.
βœ“ Either df["Age"] = df["Age"].fillna(0) or df["Age"].fillna(0, inplace=True). Always assign the result!
7. Using loc when you mean iloc (or vice versa)
df.loc[0:3] returns 4 rows (label-based, inclusive), but df.iloc[0:3] returns 3 rows (position-based, exclusive stop).
βœ“ loc = label-based (inclusive both ends). iloc = integer position-based (exclusive stop, like range()). When in doubt, use iloc for predictable slicing.

Week 3: EDA & Visualization

8. "Correlation means causation"
Seeing a high correlation between two variables and concluding one causes the other.
βœ“ Correlation only measures how variables move together. Always look for confounding variables. Ice cream sales and drownings correlate because both increase in summer β€” not because one causes the other.
9. Using mean for skewed data
Reporting mean salary as $80K when most employees earn $50K and a few executives earn $500K.
βœ“ For skewed distributions, use median (robust to outliers). Always visualize first β€” a histogram reveals whether mean or median is more representative.
10. Forgetting the raw string prefix in regex
re.findall("\bword\b", text) fails because \b is a backspace character in normal strings.
βœ“ Always use raw strings with regex: r"\bword\b". The r prefix tells Python to treat backslashes literally.