What’s one thing you learned? What’s still confusing?
Python for Machine Learning: sklearn & Beyond
The sklearn workflow: load, split, train, predict, evaluate. Pipelines and model comparison.
Databases with Python: SQLite, SQLAlchemy & Data Access
Connect to databases, write SQL, use SQLAlchemy ORM, integrate Pandas with SQL.
Type Hints, Testing & Code Quality
Type hints, mypy, pytest basics, unittest.mock, PEP 8, and good docstrings.
Interactive Labs for This Track
Loop Visualizer
You're a factory robot repeating the same task on an assembly line — watch how loops automate repetitive work
List Slicing
You have a playlist of 50 songs — grab just tracks 10 through 20 with a single slice expression
Sorting Algorithms
You're organizing a library of 10,000 books — which sorting method is fastest?
Ask questions, share insights
fig, ax = plt.subplots() pattern unlocks Seaborn, Plotly, and every other library built on top.pyplot module provides a MATLAB-like interface:import matplotlib.pyplot as plt
import numpy as np
# Generate data
x = np.linspace(0, 10, 100) # 100 points from 0 to 10
y = np.sin(x)
# Create a line plot
plt.figure(figsize=(8, 4)) # width x height in inches
plt.plot(x, y)
plt.title("A Simple Sine Wave")
plt.xlabel("x")
plt.ylabel("sin(x)")
plt.grid(True)
plt.show()x = np.linspace(0, 2 * np.pi, 100)
plt.figure(figsize=(10, 5))
plt.plot(x, np.sin(x), label="sin(x)", color="blue", linewidth=2)
plt.plot(x, np.cos(x), label="cos(x)", color="red", linewidth=2, linestyle="--")
plt.plot(x, np.sin(x) + np.cos(x), label="sin(x) + cos(x)", color="purple", alpha=0.7)
plt.title("Trigonometric Functions", fontsize=16, fontweight="bold")
plt.xlabel("x (radians)", fontsize=12)
plt.ylabel("y", fontsize=12)
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.axhline(y=0, color="black", linewidth=0.5) # horizontal line at y=0
plt.tight_layout()
plt.show()Key parameters:
label -- text for the legendcolor -- line color (names, hex codes, or RGB tuples)linewidth -- line thicknesslinestyle -- "-" solid, "--" dashed, ":" dotted, "-." dash-dotalpha -- transparency (0.0 = invisible, 1.0 = opaque)Scatter plots show the relationship between two numerical variables:
import numpy as np
import matplotlib.pyplot as plt
# Generate correlated data
np.random.seed(42)
hours_studied = np.random.uniform(1, 10, 50)
scores = 40 + 5 * hours_studied + np.random.normal(0, 5, 50)
scores = np.clip(scores, 0, 100)
plt.figure(figsize=(8, 6))
plt.scatter(hours_studied, scores, c="steelblue", alpha=0.7, edgecolors="white", s=80)
# Add a trend line
z = np.polyfit(hours_studied, scores, 1)
p = np.poly1d(z)
x_line = np.linspace(1, 10, 100)
plt.plot(x_line, p(x_line), "r--", linewidth=2, label=f"Trend: y = {z[0]:.1f}x + {z[1]:.1f}")
plt.title("Study Hours vs Test Scores", fontsize=14)
plt.xlabel("Hours Studied", fontsize=12)
plt.ylabel("Test Score", fontsize=12)
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()# Color by a third variable
np.random.seed(42)
n = 100
math_scores = np.random.normal(75, 12, n)
science_scores = np.random.normal(78, 10, n)
attendance = np.random.normal(85, 10, n).clip(50, 100)
plt.figure(figsize=(8, 6))
scatter = plt.scatter(
math_scores, science_scores,
c=attendance, # color by attendance
cmap="RdYlGn", # red-yellow-green colormap
s=60, alpha=0.8, edgecolors="white"
)
plt.colorbar(scatter, label="Attendance %")
plt.title("Math vs Science Scores (colored by Attendance)")
plt.xlabel("Math Score")
plt.ylabel("Science Score")
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()subjects = ["Math", "Science", "English", "History", "Art"]
averages = [78.5, 82.3, 80.1, 75.8, 88.2]
colors = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#a855f7"]
plt.figure(figsize=(8, 5))
bars = plt.bar(subjects, averages, color=colors, edgecolor="white", width=0.6)
# Add value labels on top of each bar
for bar, val in zip(bars, averages):
plt.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.5,
f"{val}", ha="center", fontsize=11, fontweight="bold")
plt.title("Average Scores by Subject", fontsize=14)
plt.ylabel("Average Score", fontsize=12)
plt.ylim(0, 100)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()classes = ["9A", "9B", "9C"]
math_avgs = [82, 76, 79]
sci_avgs = [85, 80, 83]
eng_avgs = [78, 81, 77]
x = np.arange(len(classes))
width = 0.25
plt.figure(figsize=(8, 5))
plt.bar(x - width, math_avgs, width, label="Math", color="#3b82f6")
plt.bar(x, sci_avgs, width, label="Science", color="#22c55e")
plt.bar(x + width, eng_avgs, width, label="English", color="#f59e0b")
plt.title("Subject Averages by Class", fontsize=14)
plt.xlabel("Class")
plt.ylabel("Average Score")
plt.xticks(x, classes)
plt.legend()
plt.ylim(0, 100)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()np.random.seed(42)
scores = np.random.normal(75, 12, 500) # 500 students
plt.figure(figsize=(8, 5))
plt.hist(scores, bins=25, color="steelblue", edgecolor="white", alpha=0.8)
# Add mean and median lines
mean_score = np.mean(scores)
median_score = np.median(scores)
plt.axvline(mean_score, color="red", linestyle="--", linewidth=2, label=f"Mean: {mean_score:.1f}")
plt.axvline(median_score, color="orange", linestyle="-.", linewidth=2, label=f"Median: {median_score:.1f}")
plt.title("Distribution of Student Scores (n=500)", fontsize=14)
plt.xlabel("Score", fontsize=12)
plt.ylabel("Frequency", fontsize=12)
plt.legend(fontsize=11)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()When should you use a histogram instead of a bar chart?
Real analysis often requires multiple plots side by side:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
n = 200
math = np.random.normal(75, 12, n).clip(0, 100)
science = np.random.normal(78, 10, n).clip(0, 100)
english = np.random.normal(80, 11, n).clip(0, 100)
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle("Student Performance Dashboard", fontsize=16, fontweight="bold")
# Plot 1: Score distributions (overlapping histograms)
ax1 = axes[0, 0]
ax1.hist(math, bins=20, alpha=0.6, label="Math", color="#3b82f6")
ax1.hist(science, bins=20, alpha=0.6, label="Science", color="#22c55e")
ax1.hist(english, bins=20, alpha=0.6, label="English", color="#f59e0b")
ax1.set_title("Score Distributions")
ax1.set_xlabel("Score")
ax1.set_ylabel("Frequency")
ax1.legend()
ax1.grid(axis="y", alpha=0.3)
# Plot 2: Math vs Science scatter
ax2 = axes[0, 1]
ax2.scatter(math, science, alpha=0.5, c="steelblue", edgecolors="white", s=40)
ax2.set_title("Math vs Science Scores")
ax2.set_xlabel("Math Score")
ax2.set_ylabel("Science Score")
ax2.grid(True, alpha=0.3)
# Plot 3: Average by subject (bar chart)
ax3 = axes[1, 0]
subjects = ["Math", "Science", "English"]
means = [np.mean(math), np.mean(science), np.mean(english)]
stds = [np.std(math), np.std(science), np.std(english)]
bars = ax3.bar(subjects, means, color=["#3b82f6", "#22c55e", "#f59e0b"],
edgecolor="white", yerr=stds, capsize=5)
ax3.set_title("Average Scores (with Std Dev)")
ax3.set_ylabel("Score")
ax3.set_ylim(0, 100)
ax3.grid(axis="y", alpha=0.3)
# Plot 4: Box plots for comparison
ax4 = axes[1, 1]
bp = ax4.boxplot([math, science, english], tick_labels=subjects,
patch_artist=True, medianprops={"color": "black", "linewidth": 2})
# (Matplotlib 3.9+: use `tick_labels=`. The older `labels=` is deprecated.)
colors = ["#3b82f6", "#22c55e", "#f59e0b"]
for patch, color in zip(bp["boxes"], colors):
patch.set_facecolor(color)
patch.set_alpha(0.7)
ax4.set_title("Score Distribution (Box Plots)")
ax4.set_ylabel("Score")
ax4.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.show()# Method 1: fig, axes = plt.subplots(rows, cols)
fig, axes = plt.subplots(2, 3, figsize=(15, 8)) # 2 rows, 3 columns
axes[0, 0].plot(x, y) # access by row, col index
axes[1, 2].bar(...) # bottom-right plot
# Method 2: Single row or column
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 4))
ax1.plot(x, y)
ax2.scatter(x, y)
ax3.hist(y)
# Always call tight_layout() to prevent overlapping
plt.tight_layout()| Chart Type | Best For | Example |
|---|---|---|
| Line plot | Trends over time or continuous sequences | Training loss over epochs |
| Scatter plot | Relationship between two numerical variables | Height vs weight |
| Bar chart | Comparing categories | Average score per subject |
| Histogram | Distribution of a single numerical variable | Score distribution |
| Box plot | Comparing distributions across groups | Scores by class section |
| Pie chart | Part-to-whole relationships (use sparingly) | Market share |
| Heatmap | Correlation matrices or 2D data |
# Save to file (always save BEFORE plt.show())
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot([1, 2, 3], [4, 5, 6])
ax.set_title("My Plot")
fig.savefig("my_plot.png", dpi=150, bbox_inches="tight") # PNG
fig.savefig("my_plot.pdf", bbox_inches="tight") # PDF (vector)
fig.savefig("my_plot.svg", bbox_inches="tight") # SVG (vector)
plt.show()# Use a built-in style
plt.style.use("seaborn-v0_8-whitegrid") # clean, modern look
# Other popular styles:
# plt.style.use("ggplot") # R's ggplot2 look
# plt.style.use("dark_background") # dark theme
# plt.style.use("fivethirtyeight") # FiveThirtyEight blog styleimport seaborn as sns
import pandas as pd
# Seaborn works beautifully with Pandas DataFrames
df = pd.DataFrame({
"student": [f"S{i}" for i in range(100)],
"hours_studied": np.random.uniform(1, 10, 100),
"score": np.random.normal(75, 12, 100).clip(0, 100).round(),
"class": np.random.choice(["9A", "9B", "9C"], 100),
})
# Scatter with regression line -- one line of code!
sns.lmplot(data=df, x="hours_studied", y="score", hue="class",
height=5, aspect=1.3)
plt.title("Study Hours vs Score by Class")
plt.show()
# Distribution plot
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.histplot(data=df, x="score", hue="class", kde=True, ax=axes[0])
axes[0].set_title("Score Distribution by Class")
sns.boxplot(data=df, x="class", y="score", hue="class", palette="Set2", legend=False, ax=axes[1])
axes[1].set_title("Score Box Plots by Class")
plt.tight_layout()
plt.show()
# Heatmap -- great for correlation matrices
numeric_cols = df[["hours_studied", "score"]].copy()
numeric_cols["attendance"] = np.random.normal(85, 10, 100)
corr = numeric_cols.corr()
plt.figure(figsize=(6, 4))
sns.heatmap(corr, annot=True, cmap="RdBu_r", center=0, fmt=".2f",
square=True, linewidths=1)
plt.title("Feature Correlation Matrix")
plt.tight_layout()
plt.show()Seaborn is not a replacement for Matplotlib -- it is built on top of it. You often use both together: Seaborn for high-level statistical plots, Matplotlib for customization.
Tests · Prepare data for visualization, calculate statistics, and explore trends!
import matplotlib.pyplot as plt
import numpy as np
# 1. Training loss curve — the most important ML plot
epochs = range(1, 21)
train_loss = [1.0 * (0.85 ** e) + np.random.normal(0, 0.02) for e in epochs]
val_loss = [1.0 * (0.87 ** e) + np.random.normal(0, 0.03) for e in epochs]
plt.figure(figsize=(8, 4))
plt.plot(epochs, train_loss, 'b-o', label='Training Loss', markersize=4)
plt.plot(epochs, val_loss, 'r-o', label='Validation Loss', markersize=4)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Progress — watch for validation loss rising (overfitting!)')
plt.legend()
plt.grid(alpha=0.3)
plt.show()
# 2. Feature importance bar chart
features = ['Credit Score', 'Income', 'Loan Amount', 'Employment Yrs']
importance = [0.38, 0.27, 0.22, 0.13]
colors = ['#818cf8', '#22d3ee', '#34d399', '#fbbf24']
plt.barh(features, importance, color=colors)
plt.xlabel('Importance Score')
plt.title('Which features does the model rely on?')
plt.tight_layout()
plt.show()plt.plot() for lines, plt.scatter() for relationships, plt.bar() for categories, plt.hist() for distributions -- choosing the right chart type is the most important visualization decisionfig, axes = plt.subplots(rows, cols) creates a grid of plots. Use this to show multiple perspectives of the same data side by sidelmplot), distributions (histplot), correlations (heatmap), and box plots (boxplot), Seaborn produces beautiful results in one linefig.savefig("plot.png", dpi=150) must come before plt.show(). After show(), the figure object is cleared and saving produces a blank imageWhich chart type is best for showing the distribution of a single numerical variable?
# OO style — the "explicit" API (always prefer this)
fig, ax = plt.subplots() # fig = canvas, ax = one plot on it
ax.plot(x, y) # unambiguous: we are plotting on THIS ax
ax.set_title("My Plot") # same ax — no global state involved
fig.savefig("out.png") # save THIS figurefig, axes = plt.subplots(2, 2) gives you a 2×2 array of Axes. Each ax is independent — setting the title on axes[0, 0] does not affect axes[1, 1]. This is why every professional Matplotlib code you will read in the wild uses fig, ax = plt.subplots() rather than plt.plot().| Feature correlations |