Section 1 of 16

1 Before you start

Before you begin, take a few minutes to check what you already know and how confident you feel. You will see the same questions again at the end of the module — this helps both you and us measure what you have learned. Click an option for every question and confidence rating, then click Next to continue.

Pre-test

You write a Python script that ends with plt.plot(ages) and run it. The script finishes without errors but no figure window appears on screen. What is the most likely cause?

Pre-test

You write fig, ax = plt.subplots() and want to draw a line on that panel using the object-oriented style. Which call is correct?

Pre-test

You plot age (x) against BMI (y) for 200 unrelated patients and join the points with plt.plot. Why is the result misleading?

Pre-test

You have one row per patient in a DataFrame and want to show the relationship between age and cholesterol. Which matplotlib call is the right one?

Pre-test

You want to compare two patients' weight trajectories on the same axes. What is the simplest correct approach?

Pre-test

You want to see the distribution of patient ages across your study. Which call produces the right plot?

Pre-test

You have the mean BMI for each of three clinics (A, B, C) and want to compare them. Which call is the right one?

Pre-test

You draw plt.hist(values, bins=3) on several hundred measurements and the distribution looks like a featureless block. What is the most sensible next step?

Pre-confidence

I can decide whether a particular plot should be a line plot, a scatter plot, a bar plot, or a histogram, and explain why.

Not at all confident
Fully confident
Pre-confidence

I can set up a matplotlib figure in either the pyplot or the object-oriented style, follow the import-build-show skeleton, and plot one or more series on the same axes.

Not at all confident
Fully confident
Section 2 of 16

2 Introduction

Numbers don't become insights until you visualize them. A column of ages or a list of expression values tells you very little on its own. However, a histogram instantly reveals demographic trends, and a bar chart highlights exactly which gene is firing hardest. Visualization translates raw data into clear pictures, sparking the questions that drive your research.

Matplotlib is the foundational plotting library of the Python data stack. It’s the engine powering pandas' df.plot() and seaborn graphics under the hood. Because it easily produces high-quality outputs accepted by most scientific journals, learning just a small fraction of Matplotlib is enough to generate publication-ready figures for your reports, slides, and manuscripts.

This is the first of two parts on Matplotlib. It covers the foundations and the core plot types — everything you need to turn a raw column of numbers into the right kind of chart:

  • Introduction to Matplotlib — the import convention, the simplest possible figure, and the figure-and-axes pair that everything else is built on.
  • Line plots and scatter plots — when to draw a connecting line, when to draw a cloud of dots, and how to put more than one series on the same axes.
  • Bar plots and histograms — comparing one number per category, and showing the distribution of values inside a single column.

Part II then builds on these to make a figure presentation-ready: customizing labels, colours, and legends, arranging several panels with subplots, and saving the finished result to a file.

  • We will run through some of the common plots that you will encounter in Matplotlib.
Introduction to the eight plot types you can create with matplotlib.
Introduction to the eight plot types you can create with matplotlib.

Try every snippet in the Python Scratchpad on the right. By the end of this part you will be able to set up a figure in either coding style, follow the three-step skeleton, and reach for the right basic plot — line, scatter, bar, or histogram — including plotting several series at once.

Section 3 of 16

3 Introduction to Matplotlib

Matplotlib is a third-party package, so the first step is to make it available to your script. The convention is to import the part of it called pyplot under the alias plt. From that line onwards, every plotting tool is reached as plt.something.

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

ages = [54, 61, 47, 72, 38, 65, 49]
plt.plot(ages)
plt.show()

This creates a complete, working figure in just four lines. plt.plot draws a line through your data, and plt.show() opens the figure window.

NOTE: In Jupyter notebooks, figures usually appear inline automatically. In a plain Python script, nothing appears until you explicitly call show()

Section 4 of 16

4 The Canvas vs The Plot

Behind the scenes, Matplotlib manages two related objects:

  • ‘Figure’ – The entire canvas is the window that holds everything
  • ‘Axes’ – The actual plotting area inside the figure i.e. the box containing your data, tick marks, and labels.

A single Figure can hold multiple Axes (subplots). A simple plt.plot call automatically creates one of each, which is all you need to worry about for a one-panel figure.

A Figure is the outer container; an Axes is a plotting region inside it, and one Figure can hold many Axes as subplots.
A Figure is the outer container; an Axes is a plotting region inside it, and one Figure can hold many Axes as subplots.

You can try the code here:

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.suptitle("One Figure (the whole window)")
ax1.set_title("Axes 1 (a panel inside)")
ax2.set_title("Axes 2 (another panel)")
ax1.plot([1, 2, 3], [1, 4, 9])
ax2.plot([1, 2, 3], [3, 2, 1])
plt.show()
Section 5 of 16

5 Two Ways to Code

You will encounter two different styles of writing Matplotlib code:

  • The ‘pyplot’ style – A sequence of plt.something calls that quietly modify the active figure. It is shorter and great for quick, one-off plots.
  • The Object-oriented style – Explicitly creates the figure and axes upfront, then calls methods directly on the axes object. It is cleaner and highly recommended for complex or multi-panel figures.

Here are the two different styles

(a) pyplot style

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

ages = [54, 61, 47, 72, 38, 65, 49]
plt.plot(ages)
plt.show()

(b) Object-oriented style

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([54, 61, 47, 72, 38])
plt.show()
A side-by-side comparison of matplotlib's pyplot style, where plt holds the figure state internally, and the object-oriented style, where fig and ax are named variables the programmer can reference directly.
A side-by-side comparison of matplotlib's pyplot style, where plt holds the figure state internally, and the object-oriented style, where fig and ax are named variables the programmer can reference directly.

NOTE: Calling plt.subplots() with no arguments returns one Figure and one Axes. Storing the axes in a variable named ax is standard practice, and it pays off immediately when you need to draw more than one panel.

Object-oriented style scales to many subplots: pyplot tracks one implicit active subplot at a time, while named ax[i] variables let the programmer target any panel directly.
Object-oriented style scales to many subplots: pyplot tracks one implicit active subplot at a time, while named ax[i] variables let the programmer target any panel directly.
Section 6 of 16

6 The Three-Step Skeleton

From here on, every plot in this module follows the same rhythm:

  • Import Matplotlib
  • Build the plot with pyplot or object-oriented calls
  • Show (or savefig) the final output

Once that rhythm is in your fingers, the rest of the module is just about what you put between the import and the show

# The three-step skeleton every plot follows.
import matplotlib.pyplot as plt # 1. import

plt.plot([54, 61, 47, 72, 38]) # 2. build (one or more pyplot calls)
plt.xlabel("Patient index")
plt.ylabel("Age")

plt.show() # 3. show (or .savefig for a file)
Three-step matplotlib rhythm, showing how plotting calls accumulate before being rendered when plt.show() runs.
Three-step matplotlib rhythm, showing how plotting calls accumulate before being rendered when plt.show() runs.
Section 7 of 16

7 Choosing your plot

Four of the most common plots answer fundamentally different questions about your data. Choosing the right one comes down to identifying exactly what you want the viewer to see:

  • Line plots ask: How does this change over an ordered sequence (like time)?
  • Scatter plots ask: Is there a relationship between these two variables?
  • Bar plots ask: How does a summary metric compare across distinct categories?
  • Histograms ask: How are the values distributed within a single dataset?

Choosing the right plot comes down to identifying which question you are trying to answer.

Four common plot types and the question each one is designed to answer.
Four common plot types and the question each one is designed to answer.
Section 7.1 of 16

7.1 plt.plot for line plot

plt.plot is the line tool. It takes a list of x values and a list of y values, drawing a line that connects the points in order. (If you only provide one list, Matplotlib defaults the x-axis to index numbers 0, 1, 2...).

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

months = [0, 1, 2, 3, 6, 12]
weight_kg = [82.0, 80.5, 79.1, 77.8, 75.4, 73.2]

plt.plot(months, weight_kg)
plt.show()

This shows a single patient's weight over a year. The connecting line makes the trajectory obvious, i.e. a steady decline followed by a plateau. Line plots shine when the x-axis has a natural order, such as time, dose, or position along a chromosome.

plt.plot pairs x and y values by index and connects the points in order, with a side-by-side terminal and matplotlib-style chart.
plt.plot pairs x and y values by index and connects the points in order, with a side-by-side terminal and matplotlib-style chart.
Section 7.2 of 16

7.2 plt.scatter for scatter plots

plt.scatter is the dot tool. It takes x and y arrays and draws one dot per pair without connecting them. Use this when consecutive points have no logical link—for example, plotting age versus BMI across different patients. A line here would falsely imply a trajectory.

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

ages = [54, 61, 47, 72, 38, 65, 49, 58, 71, 44]
bmi = [27.3, 24.1, 31.2, 22.8, 29.5, 26.0, 30.1, 25.4, 23.7, 28.2]

plt.scatter(ages, bmi)
plt.show()

Each dot represents one patient. The overall "cloud" instantly reveals whether age and BMI move together, highlighting loose patterns far faster than staring at raw CSV columns.

How plt.scatter places one dot per (x, y) pair, contrasting the clean cloud with the chaotic line plt.plot would draw through unsorted data, then highlighting the underlying trend.
How plt.scatter places one dot per (x, y) pair, contrasting the clean cloud with the chaotic line plt.plot would draw through unsorted data, then highlighting the underlying trend.
Section 7.3 of 16

7.3 Plotting multiple series

You can plot more than one series on the same axes simply by calling plt.plot or plt.scatter twice. Matplotlib will automatically assign different colors to each series so you can tell them apart.

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

months = [0, 3, 6, 12]
patient_a = [82, 79, 75, 73]
patient_b = [78, 76, 75, 74]

plt.plot(months, patient_a)
plt.plot(months, patient_b)
plt.show()

Plotting multiple patients on the same axes tells a richer story. The reader's eye can directly compare the lines on the exact same scale, without mentally juggling two separate figures side-by-side.

Calling plt.plot twice draws two auto-colored lines on the same axes, allowing direct comparison of two series at a glance.
Calling plt.plot twice draws two auto-colored lines on the same axes, allowing direct comparison of two series at a glance.
Section 7.4 of 16

7.4 plt.bar for bar plots

plt.bar takes a list of categories for the x-axis and a list of heights for the y-axis. The heights are usually a summary metric, like a mean or percentage.

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

clinics = ["A", "B", "C"]
mean_bmi = [27.4, 25.1, 29.0]

plt.bar(clinics, mean_bmi)
plt.show()

If your category names are long and overlap on the x-axis, use plt.barh instead to draw horizontal bars (the categories will run down the y-axis).

How plt.bar pairs each category with its summary height and raises a bar for each.
How plt.bar pairs each category with its summary height and raises a bar for each.
Section 7.5 of 16

7.5 plt.hist for historgrams

A histogram buckets a single list of numbers for you. plt.hist divides your data range into equal-width buckets and draws one bar per bucket to show how many values landed inside.

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

ages = [54, 61, 47, 72, 38, 65, 49, 58, 71, 44, 60, 55, 67, 41, 50]

plt.hist(ages, bins=8)
plt.show()

The bins= argument is the main knob you need to turn. Too few bins smooth out the shape of your data; too many create visual static. Between 5 and 20 bins is usually the sweet spot—adjust it until the picture clearly shows what you want to see. If you omit it, Matplotlib defaults to 10.

plt.hist showing fifteen values flying into equal-width buckets to form a histogram, then tuning the bins argument through several values to compare how the shape changes.
plt.hist showing fifteen values flying into equal-width buckets to form a histogram, then tuning the bins argument through several values to compare how the shape changes.
Section 7.6 of 16

7.6 plt.errorbar for adding uncertainty

A standard bar plot of group means hides how spread out the underlying data is. plt.errorbar solves this by adding a vertical line through each point to show standard deviation, standard error, or a 95% confidence interval. You can pass the yerr argument either a single number (for a uniform error) or a list (one error value per point).

Try it out

Try this snippet in the Python Scratchpad on the right.

Try this snippet
import matplotlib.pyplot as plt

clinics = ["A", "B", "C"]
mean_bmi = [27.4, 31.1, 24.8]
sem = [0.8, 1.1, 0.6]
plt.errorbar(clinics, mean_bmi, yerr=sem, fmt="o", capsize=5)
plt.ylabel("Mean BMI (with SEM)")
plt.title("BMI by clinic")
plt.show()
plt.errorbar adds vertical whiskers to mean values, then compares how the choice of yerr (SEM, a scalar, SD, or 95% CI) changes what the bars communicate.
plt.errorbar adds vertical whiskers to mean values, then compares how the choice of yerr (SEM, a scalar, SD, or 95% CI) changes what the bars communicate.
Section 8 of 16

8 Wrapping up Part I

You can now set up a matplotlib figure two ways, follow the import—build—show skeleton, and choose the right basic plot — line, scatter, bar, or histogram — for the question you are asking, including plotting several series and adding error bars for uncertainty. Part II picks up from here: making those plots clear and presentation-ready with labels and legends, arranging multiple panels with subplots, and saving figures to disk.

Every matplotlib figure follows the same import, build, show skeleton, and the build step means choosing among four basic plots matched to your question, a line for trends and multiple series, a scatter for two variables, a bar with error bars for comparing groups under uncertainty, and a histogram for the distribution of one variable.
Every matplotlib figure follows the same import, build, show skeleton, and the build step means choosing among four basic plots matched to your question, a line for trends and multiple series, a scatter for two variables, a bar with error bars for comparing groups under uncertainty, and a histogram for the distribution of one variable.
Section 9 of 16

9 Check your understanding

You have reached the end of the module. Try the same questions again — your answers here, paired with your pre-test answers, are how we measure what the module taught you. Answer every question and confidence rating, then click Submit and see results to view your score.

Post-test

You write a Python script that ends with plt.plot(ages) and run it. The script finishes without errors but no figure window appears on screen. What is the most likely cause?

Post-test

You write fig, ax = plt.subplots() and want to draw a line on that panel using the object-oriented style. Which call is correct?

Post-test

You plot age (x) against BMI (y) for 200 unrelated patients and join the points with plt.plot. Why is the result misleading?

Post-test

You have one row per patient in a DataFrame and want to show the relationship between age and cholesterol. Which matplotlib call is the right one?

Post-test

You want to compare two patients' weight trajectories on the same axes. What is the simplest correct approach?

Post-test

You want to see the distribution of patient ages across your study. Which call produces the right plot?

Post-test

You have the mean BMI for each of three clinics (A, B, C) and want to compare them. Which call is the right one?

Post-test

You draw plt.hist(values, bins=3) on several hundred measurements and the distribution looks like a featureless block. What is the most sensible next step?

Post-confidence

I can decide whether a particular plot should be a line plot, a scatter plot, a bar plot, or a histogram, and explain why.

Not at all confident
Fully confident
Post-confidence

I can set up a matplotlib figure in either the pyplot or the object-oriented style, follow the import-build-show skeleton, and plot one or more series on the same axes.

Not at all confident
Fully confident
Section 10 of 16

10 Your results

Here is how your post-test answers compare with your pre-test answers. The pre/post pairing is the most reliable way to see what this module actually taught you.

Your score

Submit the post-test to see your results.

Muddiest point

What is the one thing from this module that is still unclear to you?

Rate this module

Overall, how would you rate this module?

How likely are you to recommend this module to a peer? (0 = not at all, 10 = extremely likely)