I, Oliver Bonham-Carter 👋

Associate Professor in The Department of Computer and Information Science, Allegheny College

I, Oliver Bonham-Carter, In June

I, Oliver Bonham-Carter 👋

Associate Professor in The Department of Computer and Information Science, Allegheny College

  1. Plotting and Distributions

A Futuristic Classroom

Welcome to Data Visualization! 🎨📊

Have you ever tried to tell someone about a huge list of numbers and watched their eyes glaze over? Numbers alone can be boring and hard to understand. But turn those numbers into a colorful chart or graph, and suddenly the story becomes clear and exciting!

In this tutorial, you’ll learn how to transform raw data into beautiful, meaningful visualizations using Python. We’ll explore different types of plots and discover when to use each one. Think of it like learning different artistic styles - each has its own purpose and tells a different story.

What You’ll Learn:

Before We Start:

Open up a Jupyter notebook so you can run the code examples alongside this tutorial. Try modifying the code - breaking things and fixing them is one of the best ways to learn!


Part 1: Your First Plot - The Histogram

A histogram is like a bar chart that shows how often different values appear in your data. Imagine you surveyed your class about their favorite numbers between 1 and 10 - a histogram would show you which numbers were most popular!

Understanding the Parts of a Plot:

Let’s Build Our First Histogram!

# libraries to help the code to perform
# new functions
import matplotlib.pyplot as plt
import numpy as np

# define the x axis - our data points
x = [10, 20, 30]
# x-axis label (what we're measuring)
plt.xlabel('x-axis')
# y-axis label (how many we counted)
plt.ylabel('y-axis')
# plot title (what's this chart about?)
plt.title('My Cool Histogram')
# plot the histogram in memory
plt.hist(x)
# Draw the histogram to the screen
plt.show()

What’s happening in this code?

  1. We import libraries (like toolboxes with pre-built functions)
  2. We create simple data: just three numbers [10, 20, 30]
  3. We label our axes so people know what they’re looking at
  4. plt.hist(x) creates the histogram bars
  5. plt.show() displays it on your screen

Try it yourself! Change the numbers in the list to [5, 10, 10, 15, 15, 15, 20] and see how the histogram changes. What do you notice?

A Real-World Example: Fruit Inventory

Let’s say you run a fruit stand and want to visualize your inventory. This bar chart (a type of histogram) makes it easy to see which fruits you have the most of!

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

fruits = ['apple', 'blueberry', 'cherry', 'orange']
counts = [40, 100, 30, 55]
bar_labels = ['red', 'blue', '_red', 'orange']
bar_colors = ['tab:red', 'tab:blue', 'tab:red', 'tab:orange']

ax.bar(fruits, counts, label=bar_labels, color=bar_colors)

ax.set_ylabel('fruit supply')
ax.set_title('Fruit supply by kind and color')
ax.legend(title='Fruit color')

plt.show()

Reading this chart:

Challenge: Can you modify the code to add “banana” with a count of 75 and make it yellow?

Read more about this type of code at RealPython


Part 2: Understanding Data Distributions

Now for the exciting part! Not all data looks the same when you plot it. Just like people can be tall, short, or average height, data can be distributed in different patterns. Understanding these patterns is like being a data detective! 🔍

What is a Distribution?

A distribution shows us which values are common and which are rare in our dataset. Think about test scores in your class:

When we plot this, we see a pattern or “distribution” that tells a story about the data.

Reference: Learn more about distributions


Part 3: The Normal Distribution (The Bell Curve)

The Normal Distribution is the most famous distribution in statistics! It’s also called the “bell curve” because it looks like a bell. 🔔

Why it matters:

Key Features:

Visualizing the Normal Distribution

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import statistics

# Plot between -20 and 20 with .001 steps (creates smooth curve)
x_axis = np.arange(-20, 20, 0.01)
# Calculating mean and standard deviation
mean = statistics.mean(x_axis)
sd = statistics.stdev(x_axis)
age = norm.pdf(x_axis, mean, sd)
# x-axis label
plt.xlabel('age')
# frequency label
plt.ylabel('No. of people')
# plot title
plt.title('A Normal Distribution')
plt.plot(x_axis, age)
plt.show()

Code Breakdown:

What you see: A beautiful bell curve! The highest point is in the middle (most common values), and it slopes down on both sides (rare values).

Histogram Version of Normal Distribution

Sometimes we want to see the data as bars instead of a smooth curve. This makes it easier to count how many values fall in each range!

import numpy
import matplotlib.pyplot as plt

# Generate 100,000 random values with mean=5.0 and std deviation=1.0
x = numpy.random.normal(5.0, 1.0, 100000)
# Split the data into 100 bins (bars)
plt.hist(x, 100)
plt.show()

What’s different here?

Try this: Change 5.0 to 10.0 and see how the curve shifts. Change 1.0 to 3.0 and watch it get wider!

Scatter Plot Version (Chronological)

This plot shows how randomly generated normal data looks when plotted point-by-point over time:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import statistics
from numpy import random

# Plot between -10 and 10 with .001 steps
x_axis = np.arange(-10, 10, 0.01)
# Generate 2000 random normal values
ages = random.normal(size=(2000, 1))
# x-axis label
plt.xlabel('age')
# frequency label
plt.ylabel('No. of people')
# plot title
plt.title('A Normal Distribution')

plt.plot(x_axis, ages)
plt.show()

What this shows: Each point is a randomly generated value. Notice how most points cluster in the middle and fewer appear at the edges? That’s the normal distribution at work!


Part 4: The Laplace Distribution (Double Peak)

The Laplace Distribution is like two back-to-back exponential curves. It has a sharp peak in the center and drops off quickly on both sides - steeper than the normal distribution!

When you see this:

Key Feature:

import matplotlib.pyplot as plt
import numpy as np

# Generate 500 random values from Laplace distribution
# loc=15 is the center, scale=3 controls the spread
d = np.random.laplace(loc=15, scale=3, size=500)
n, bins, patches = plt.hist(x=d,
bins='auto',
color='#0504aa',
alpha=0.7, rwidth=0.85)
plt.grid(axis='y', alpha=0.75)
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.title('Laplace Distribution')
plt.text(23, 45, r'$\mu=15, b=3$')
maxfreq = n.max()
# Set a clean upper y-axis limit
plt.ylim(ymax=np.ceil(maxfreq / 10) * 10
if maxfreq % 10 else maxfreq + 10)
plt.show()

Code Highlights:

Notice: The peak is sharper than the normal distribution we saw earlier!


Part 5: The Binomial Distribution (Coin Flips!)

This is one of the most fun distributions because it models things like coin flips, yes/no surveys, or success/failure experiments!

Real-World Example:

If you flip a coin 10 times, how many heads will you get? The binomial distribution tells you the probability of getting 0, 1, 2, … up to 10 heads.

Key Parameters:

Let’s Generate Some Values!

from numpy import random

# n=10 trials (coin flips), p=0.5 probability, generate 10 experiments
x = random.binomial(n=10, p=0.5, size=10)
print(x)

Understanding the output: Each number represents how many “successes” (heads) you got in 10 coin flips. You’ll see numbers between 0 and 10. Run it multiple times and watch how the results change!

Visualizing Binomial Distribution

from numpy import random
import matplotlib.pyplot as plt

# Generate 1000 experiments of 10 coin flips each
ages = list(random.binomial(n=10, p=0.5, size=1000))
# setting the ranges and no. of intervals
range = (0, 10)
bins = 10
# plotting a histogram
plt.hist(ages, bins, range, 
color = 'blue', 
histtype = 'bar',
rwidth = 0.9)
# x-axis label
plt.xlabel('Number of Successes')
# frequency label
plt.ylabel('Frequency')
# plot title
plt.title('A Binomial Distribution')
# function to show the plot
plt.show()

Reading this chart:

Challenge: Change p=0.5 to p=0.7 to simulate an unfair coin that lands on heads 70% of the time. How does the distribution shift?


Part 6: The Poisson Distribution (Counting Events)

The Poisson Distribution answers questions like:

It’s perfect for counting events that happen randomly over time!

Key Parameter:

Generating Poisson Values

from numpy import random

# lam=2 means we expect an average of 2 events, generate 10 samples
x = random.poisson(lam=2, size=10)
print(x)

Understanding the output: Each number represents how many events occurred. Most will be close to 2, but some might be 0, 1, 3, or even higher!


Part 7: The Uniform Distribution (Everything is Equal!)

The Uniform Distribution is the simplest - every value has an equal chance of occurring. Think of it like:

Key Feature:

Flat! No peak in the middle - all values are equally common.

Uniform Distribution Histogram

import numpy
import matplotlib.pyplot as plt

# Generate 250 random values uniformly between 0.0 and 5.0
x = numpy.random.uniform(0.0, 5.0, 250)
# Split into 5 bins
plt.hist(x, 5)
plt.show()

What you see: All the bars should be roughly the same height! That’s because every value between 0 and 5 is equally likely.

More Detailed Uniform Distribution

import numpy as np 
import matplotlib.pyplot as plt

# Generate 1000 uniform random values between 0.01 and 0.99
values = np.random.uniform(0.01, 0.99, 1000) 
count, bins, ignored = plt.hist(values, 20, density=True)
# The red line shows the theoretical uniform distribution
plt.plot(bins, np.ones_like(bins), color='r')
# add title
plt.title('Uniform Distribution')
# label the axes
plt.ylabel('Density')
plt.xlabel('Values')
# function to show the plot
plt.show()

Key Observation: The red line shows the “perfect” uniform distribution. The bars might not be exactly at the red line due to randomness, but with more data points, they’d get closer!

Scatter Plot Version of Uniform Distribution

import numpy as np
import matplotlib.pyplot as plt
from numpy import random

# Plot between -10 and 10 with .001 steps
x_axis = np.arange(-10, 10, 0.01)
# Generate 2000 uniform random values
ages = random.uniform(size=(2000, 1))
# x-axis label
plt.xlabel('Position')
# frequency label
plt.ylabel('Random Values')
# plot title
plt.title('A Uniform Distribution (Scatter)')
plt.plot(x_axis, ages)
plt.show()

What this shows: Random uniform values scattered evenly across the range. No clustering anywhere - true randomness!


Part 8: The Logistic Distribution (S-Curves!)

The Logistic Distribution looks similar to the normal distribution but has heavier tails. It’s used in:

Generating Logistic Values

from numpy import random
import numpy as np

# loc=1 is center, scale=2 controls spread, generate 2x3 array
x = np.random.logistic(loc=1, scale=2, size=(2, 3))
print(x)

Parameters explained:

Visualizing the Logistic Distribution

import matplotlib.pyplot as plt
import numpy as np

# Generate 200 logistic random values
values = np.random.logistic(loc=1, scale=.5, size=(200, 1))
count, bins, ignored = plt.hist(values, 20, density=True)
plt.plot(bins, np.ones_like(bins), color='r')
plt.title('Logistic Distribution')
plt.ylabel('Density')
plt.xlabel('Values')
# function to show the plot
plt.show()

Compare to Normal: The logistic distribution looks similar to normal but has slightly thicker tails (more extreme values show up).

Scatter Plot Version

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import statistics

# Plot between -10 and 10 with .001 steps
x_axis = np.arange(-10, 10, 0.01)
# Generate 2000 logistic random values
ages = np.random.logistic(loc=1, scale=.5, size=(2000, 1))
# x-axis label
plt.xlabel('Position')
# frequency label
plt.ylabel('Random Values')
# plot title
plt.title('A Logistic Distribution (Scatter)')
plt.plot(x_axis, ages)
plt.show()

Part 9: Advanced Visualization Types

Now let’s explore some exciting plot types beyond histograms! These are powerful ways to visualize relationships and patterns in data.

Bubble Plots (Size Matters!)

A bubble plot is like a scatter plot, but the size of each dot represents a third variable. It’s perfect for showing three dimensions of data on a 2D screen!

from matplotlib import pyplot as plt
import numpy as np

# Generate 100 random data points along 3 dimensions
x, y, scale = np.random.randn(3, 100)
fig, ax = plt.subplots()
# Map each onto a scatterplot - size is determined by 'scale' value
ax.scatter(x=x, y=y, c=scale, s=np.abs(scale)*500)
ax.set(title="Some random data, plotted as bubbles!")
plt.show()

How to read this:

Real-world use: Stock market data (x=time, y=price, size=trading volume), population data (x=longitude, y=latitude, size=population), scientific measurements

Heatmaps (Seeing Patterns in Grids)

A heatmap uses colors to represent values in a grid. Hot colors (red, orange) = high values, cold colors (blue, purple) = low values. Perfect for seeing patterns at a glance!

import matplotlib.pyplot as plt
import numpy as np

# Create a 16x16 grid of random values
a = np.random.random((16, 16))
# Display as heatmap with 'hot' colormap
plt.imshow(a, cmap='hot', interpolation='nearest')
plt.show()

How to read this:

Real-world uses:

Reference

Advanced Mathematical Heatmap

This creates a more complex heatmap using a mathematical function:

import matplotlib.pyplot as plt
import numpy as np

# Generate 2D grids for the x & y bounds
y, x = np.meshgrid(
    np.linspace(-3, 3, 100), np.linspace(-3, 3, 100))

# Create a complex mathematical function
tmp = np.exp(-x ** 2 - y ** 2)
z = (1 - x / 2. + x ** 5 + y ** 3) * tmp
# x and y are bounds, so z should be the value *inside* those bounds
# Therefore, remove the last value from the z array
z = z[:-1, :-1]
z_min, z_max = -np.abs(z).max(), np.abs(z).max()

fig, ax = plt.subplots()

c = ax.pcolormesh(x, y, z, 
cmap='RdBu',  # Red-Blue colormap
vmin=z_min, 
vmax=z_max)
ax.set_title('pcolormesh')
# set the limits of the plot to the limits of the data
ax.axis([x.min(), x.max(), y.min(), y.max()])
fig.colorbar(c, ax=ax)  # Adds color scale bar

plt.show()

What’s happening:

Try this: Change cmap='RdBu' to cmap='viridis' or cmap='plasma' for different color schemes!

Reference: Stackoverflow

Scatter Plots (Finding Relationships)

Scatter plots are perfect for finding relationships between two variables. Each dot represents one data point with two measurements.

Using the Famous Iris Dataset

The Iris dataset contains measurements of 150 flowers. Let’s visualize the relationship between sepal length and petal length!

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
 
from sklearn.datasets import load_iris
iris = load_iris()
 
df = pd.DataFrame(
    data=np.c_[iris['data'], iris['target']],
    columns=iris['feature_names'] + ['target'])
 
# select setosa and versicolor (two flower species)
y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', 0, 1)
 
# extract sepal length and petal length
X = df.iloc[0:100, [0, 2]].values
 
# plot data with different colors and shapes for each species
plt.scatter(X[:50, 0], X[:50, 1],
            color='blue', marker='o', label='Setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
            color='green', marker='s', label='Versicolor')
 
plt.xlabel('Sepal length [cm]')
plt.ylabel('Petal length [cm]')
plt.legend(loc='upper left')
plt.show()

Reading this scatter plot:

What you can learn:

Reference: Vitalflux


Part 10: Challenge Plot - What’s This?

Can you figure out what this advanced plot shows? Study the code and run it to find out!

import matplotlib.pyplot as plt
import numpy as np

fig, (ax1, ax2) = plt.subplots(2, 1, layout='constrained')

dt = 0.01
t = np.arange(0, 30, dt)

# Fixing random state for reproducibility
np.random.seed(19680801)

nse1 = np.random.randn(len(t))                 # white noise 1
nse2 = np.random.randn(len(t))                 # white noise 2
r = np.exp(-t / 0.05)

cnse1 = np.convolve(nse1, r, mode='same') * dt   # colored noise 1
cnse2 = np.convolve(nse2, r, mode='same') * dt   # colored noise 2

# two signals with a coherent part and a random part
s1 = 0.01 * np.sin(2 * np.pi * 10 * t) + cnse1
s2 = 0.01 * np.sin(2 * np.pi * 10 * t) + cnse2

ax1.plot(t, s1, t, s2)
ax1.set_xlim(0, 5)
ax1.set_xlabel('Time (s)')
ax1.set_ylabel('s1 and s2')
ax1.grid(True)

cxy, f = ax2.csd(s1, s2, 256, 1. / dt)
ax2.set_ylabel('CSD (dB)')

plt.show()

What does this show? This is a Cross-Spectral Density (CSD) plot! It’s used in signal processing to analyze two signals over time:

Real-world applications:


More Data Visualization with Python???

Here are more advanced (but compelling!) visualizations using popular Python libraries.

Required Libraries

# Install required packages for data analytics
import piplite
await piplite.install(['seaborn', 'matplotlib', 'pandas', 'numpy', 'scipy', 'plotly'])
print("Packages installed successfully!")
print("You can now import and use: seaborn, matplotlib, pandas, numpy, scipy, plotly")


# Test imports for data science libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

print("✓ All libraries imported successfully!")
print(f"Pandas version: {pd.__version__}")
print(f"NumPy version: {np.__version__}")
print(f"Seaborn version: {sns.__version__}")

The plotting code

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# Set style for better-looking plots
plt.style.use('seaborn-v0_8')
sns.set_palette("husl")

Basic Plotting with Matplotlib

Line Plots

# Generate sample data
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# Create the plot
plt.figure(figsize=(10, 6))
plt.plot(x, y1, label='sin(x)', linewidth=2)
plt.plot(x, y2, label='cos(x)', linewidth=2)
plt.title('Trigonometric Functions', fontsize=16, fontweight='bold')
plt.xlabel('x values')
plt.ylabel('Function values')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

Bar Charts for Grade Distribution

# Student grade data
grades = ['A', 'B', 'C', 'D', 'F']
counts = [25, 30, 20, 15, 10]
colors = ['#2E8B57', '#4169E1', '#FFD700', '#FF8C00', '#DC143C']

plt.figure(figsize=(8, 6))
bars = plt.bar(grades, counts, color=colors, alpha=0.8, edgecolor='black')
plt.title('Student Grade Distribution', fontsize=16, fontweight='bold')
plt.xlabel('Grade')
plt.ylabel('Number of Students')

# Add value labels on bars
for bar, count in zip(bars, counts):
    plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5, 
             str(count), ha='center', va='bottom', fontweight='bold')

plt.show()

Advanced Visualizations with Seaborn

Scatter Plot with Regression Line

# Generate sample student performance data
np.random.seed(42)
n_students = 100

study_hours = np.random.normal(5, 2, n_students)
study_hours = np.clip(study_hours, 0, 10)  # Limit to 0-10 hours

# Create correlation between study hours and test scores
test_scores = 60 + 3 * study_hours + np.random.normal(0, 5, n_students)
test_scores = np.clip(test_scores, 0, 100)  # Limit to 0-100

# Create DataFrame
df = pd.DataFrame({
    'Study Hours': study_hours,
    'Test Score': test_scores
})

# Create scatter plot with regression line
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='Study Hours', y='Test Score', alpha=0.7, s=60)
sns.regplot(data=df, x='Study Hours', y='Test Score', scatter=False, color='red')
plt.title('Study Hours vs Test Scores', fontsize=16, fontweight='bold')
plt.xlabel('Study Hours per Week')
plt.ylabel('Test Score (%)')
plt.show()

Heatmap for Correlation Matrix

# Create a more comprehensive dataset
np.random.seed(42)
n = 200

data = {
    'Study Hours': np.random.normal(5, 2, n),
    'Class Attendance': np.random.beta(2, 1, n) * 100,
    'Previous GPA': np.random.normal(3.0, 0.5, n),
    'Assignment Score': np.random.normal(80, 15, n),
    'Final Exam': np.random.normal(75, 20, n)
}

# Add some correlations
data['Assignment Score'] += data['Study Hours'] * 2
data['Final Exam'] += data['Study Hours'] * 3 + data['Class Attendance'] * 0.2
data['Final Exam'] += data['Previous GPA'] * 5

df_comprehensive = pd.DataFrame(data)

# Create correlation heatmap
plt.figure(figsize=(10, 8))
correlation_matrix = df_comprehensive.corr()
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0,
            square=True, linewidths=0.5, cbar_kws={"shrink": .8})
plt.title('Correlation Matrix of Academic Performance Factors', 
          fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()

Interactive Plotting Concepts

Subplots for Multiple Visualizations

# Create a figure with multiple subplots
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Programming Concept Performance Dashboard', fontsize=16, fontweight='bold')

# Subplot 1: Algorithm Complexity Understanding
concepts = ['Arrays', 'Sorting', 'Searching', 'Trees', 'Graphs']
scores = [85, 78, 82, 65, 58]
axes[0, 0].bar(concepts, scores, color='lightblue', edgecolor='navy')
axes[0, 0].set_title('Algorithm Concept Scores')
axes[0, 0].set_ylabel('Average Score (%)')
axes[0, 0].tick_params(axis='x', rotation=45)

# Subplot 2: Programming Language Preference
languages = ['Python', 'Java', 'JavaScript', 'C++', 'Go']
popularity = [35, 25, 20, 15, 5]
axes[0, 1].pie(popularity, labels=languages, autopct='%1.1f%%', startangle=90)
axes[0, 1].set_title('Programming Language Preferences')

# Subplot 3: Learning Progress Over Time
weeks = range(1, 16)
python_progress = [20, 35, 50, 62, 70, 75, 80, 82, 85, 87, 89, 90, 92, 94, 95]
java_progress = [10, 20, 30, 45, 55, 65, 70, 75, 78, 80, 82, 84, 85, 87, 88]

axes[1, 0].plot(weeks, python_progress, marker='o', label='Python', linewidth=2)
axes[1, 0].plot(weeks, java_progress, marker='s', label='Java', linewidth=2)
axes[1, 0].set_title('Learning Progress Over Semester')
axes[1, 0].set_xlabel('Week')
axes[1, 0].set_ylabel('Proficiency (%)')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)

# Subplot 4: Error Distribution by Type
error_types = ['Syntax', 'Logic', 'Runtime', 'Type', 'Import']
error_counts = [45, 30, 20, 15, 10]
axes[1, 1].barh(error_types, error_counts, color='salmon')
axes[1, 1].set_title('Common Error Types')
axes[1, 1].set_xlabel('Frequency')

plt.tight_layout()
plt.show()

Mathematical Visualizations

Function Plotting for Algorithm Analysis

# Visualize Big O notation complexities
n = np.arange(1, 101)

# Different time complexities
constant = np.ones_like(n)
logarithmic = np.log2(n)
linear = n
n_log_n = n * np.log2(n)
quadratic = n ** 2
cubic = n ** 3

plt.figure(figsize=(12, 8))
plt.plot(n, constant, label='O(1) - Constant', linewidth=2)
plt.plot(n, logarithmic, label='O(log n) - Logarithmic', linewidth=2)
plt.plot(n, linear, label='O(n) - Linear', linewidth=2)
plt.plot(n, n_log_n, label='O(n log n) - Linearithmic', linewidth=2)
plt.plot(n, quadratic, label='O(n²) - Quadratic', linewidth=2)
plt.plot(n[n<=20], cubic[n<=20], label='O(n³) - Cubic', linewidth=2)

plt.title('Algorithm Time Complexity Comparison', fontsize=16, fontweight='bold')
plt.xlabel('Input Size (n)')
plt.ylabel('Time Units')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xlim(1, 100)
plt.ylim(0, 1000)
plt.show()

Visualization Best Practices for Education

1. Clear and Descriptive Titles

Always use titles that clearly explain what the visualization shows.

2. Proper Axis Labels

Label your axes with units when applicable.

3. Color Considerations

4. Interactive Elements

Consider adding interactivity for engagement:

5. Annotations

Add text annotations to highlight key insights or learning points.

Code Example: Creating Educational Plots

def create_educational_plot(data, title, learning_objective):
    """
    Create a standardized educational visualization.
    
    Parameters:
    - data: Dictionary with x and y values
    - title: Plot title
    - learning_objective: What students should learn
    """
    plt.figure(figsize=(10, 6))
    plt.plot(data['x'], data['y'], marker='o', linewidth=2, markersize=6)
    plt.title(f"{title}\nLearning Objective: {learning_objective}", 
              fontsize=14, fontweight='bold')
    plt.xlabel('Input')
    plt.ylabel('Output')
    plt.grid(True, alpha=0.3)
    
    # Add annotation for key insight
    max_idx = np.argmax(data['y'])
    plt.annotate(f'Peak: ({data["x"][max_idx]}, {data["y"][max_idx]:.1f})',
                xy=(data['x'][max_idx], data['y'][max_idx]),
                xytext=(10, 10), textcoords='offset points',
                bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.7),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
    
    plt.tight_layout()
    plt.show()

# Example usage
sample_data = {
    'x': np.linspace(0, 2*np.pi, 50),
    'y': np.sin(np.linspace(0, 2*np.pi, 50))
}

create_educational_plot(
    sample_data, 
    "Sine Wave Function", 
    "Understand periodic behavior in trigonometric functions"
)

Congratulations! 🎉

You’ve learned how to:

Next Steps

Experiment with the above code!

  1. Practice: Try creating plots with your own data
  2. Explore: Test different colors, sizes, and styles
  3. Combine: Mix different plot types to tell richer stories
  4. Learn more: Check out the resources below

More Resources

Check out more plotting and Python code at the following URLs. Copy and paste code into your Jupyter notebook and experiment!

Remember:

“A picture is worth a thousand words, but a well-made plot is worth a thousand numbers!”

Keep plotting, keep learning, and most importantly - have fun with data! 🚀📊