☰

Data visualization

Data Visualization in Python Using Matplotlib: Complete Tutorial

Imagine you have a spreadsheet with ten thousand rows of sales, marks or sensor readings. You could scroll through it for hours and still miss the one pattern that matters. Draw a chart and you see it in seconds. Data visualization is the habit of turning numbers into pictures, and in Python the library that does most of the work is Matplotlib.

Matplotlib is the oldest and most widely used plotting library in Python. It draws 2D and 3D charts, it works with NumPy arrays, and many other tools, such as Seaborn and the plotting methods in pandas, are built on top of it. If you learn how Matplotlib thinks, with its figures, axes and plot functions, every other Python charting tool becomes easier.

This tutorial is a complete working reference. We list the main plot types and functions, explain the Figure and Axes classes, and then go through each plot one by one: line, multiplot, subplot2grid, grid, limits, ticks, bar, histogram, pie, scatter, contour, quiver, box, violin, and the 3D line, contour, wireframe and surface plots. Every program has been run. We also add a guide to picking the right chart, the two ways of writing Matplotlib code, and the mistakes beginners make most often.

Introduction to Data Visualization in Python Using Matplotlib

Here is what Matplotlib gives you.

  • 2D and 3D plots. Line, bar, pie, scatter, contour and many more, plus 3D surfaces through the mplot3d toolkit.
  • NumPy friendly. It draws NumPy arrays directly.
  • Pyplot. A module of simple commands that works like MATLAB, where each command changes the current figure.
  • Many output formats. Save to PNG, JPG, PDF, SVG and more.
  • Works in many places. Jupyter Notebook, plain Python scripts, and desktop windows built with Tkinter, PyQt or wxPython.

Install Matplotlib

Python 2 reached its end of life in 2020, so use Python 3. Current Matplotlib needs Python 3.11 or newer, along with these packages, which pip installs for you: contourpy, cycler, fonttools, kiwisolver, numpy, packaging, pillow, pyparsing and python-dateutil.

terminal
pip install matplotlib numpy

This tutorial was tested on Python 3.13 with Matplotlib 3.11.2 and NumPy 2.5.3.

Two Ways to Write Matplotlib Code

This is the idea that confuses most beginners, so it is worth a minute. Matplotlib has two styles, and the examples below use both.

Pyplot style and object oriented style
Style How it looks When to use it
Pyplot style plt.plot(x, y) then plt.title(“Sales”) Quick, single plots and learning
Object oriented style fig, ax = plt.subplots() then ax.plot(x, y) and ax.set_title(“Sales”) Several plots, full control, real projects

In the object oriented style, a Figure is the whole window or page, and an Axes is one plot inside it. You will see the methods on an Axes start with set_, such as set_title and set_xlabel.

Types of Plots in Matplotlib

These are the main plot functions.

Main plot functions
Function What it draws
plot Lines or markers, the default choice for trends
bar Vertical bar chart
barh Horizontal bar chart
boxplot Box and whisker plot
hist Histogram
hist2d 2D histogram, counting points in a grid of cells
pie Pie chart
polar Plot on polar coordinates
scatter Scatter plot of two variables
stackplot Stacked area plot
stem Stem plot, lines from a baseline to each value
step Step plot
quiver Arrows showing direction and size

Image Functions in Matplotlib

These three functions read, save and show images. Their names are all lowercase.

Image functions
Function What it does
imread Reads an image file into an array
imsave Saves an array as an image file
imshow Shows an array as an image
python
import matplotlib.pyplot as plt
import numpy as np

pixels = np.array([[0.0, 0.5, 1.0], [1.0, 0.5, 0.0], [0.0, 1.0, 0.0]])
plt.imsave("tiny.png", pixels, cmap="gray")
img = plt.imread("tiny.png")
print(img.shape)
plt.imshow(img)
plt.show()
output
(3, 3, 4)

The shape means 3 rows, 3 columns and 4 values per pixel, which are red, green, blue and transparency.

Axes Functions in Matplotlib

Axes functions
Function What it does
axes Adds an axes to the figure
text Adds text at a position on the axes
title Sets the title of the axes
xlabel, ylabel Sets the label of the x axis and the y axis
xlim, ylim Gets or sets the range of the x axis and the y axis
xticks, yticks Gets or sets the tick positions and labels of the x axis and the y axis

Do not mix up the last two rows. The functions xlim and ylim set the range. The functions xticks and yticks control where the marks sit and what they say.

Figure Functions in Matplotlib

Figure functions
Function What it does
figtext Adds text to the figure
figure Creates a new figure
show Shows the figure
savefig Saves the figure to a file
close Closes a figure window

Figure Class in Matplotlib

The Figure is the top level container. Everything you draw sits inside one. You create it with plt.figure.

python
g1 = plt.figure()

You can pass these optional parameters:

Figure parameters
Parameter What it sets
figsize Width and height in inches, as a pair such as (6, 3)
dpi Dots per inch, which is the sharpness of the image
facecolor The background colour of the figure
edgecolor The colour of the border around the figure
linewidth The width of that border
python
import matplotlib.pyplot as plt
import numpy as np

g1 = plt.figure(figsize=(6, 3), dpi=100, facecolor="lightgrey", edgecolor="navy", linewidth=2)
print(g1.get_size_inches(), g1.dpi)
output
[6. 3.] 100

Axes Class in Matplotlib

An Axes is the actual plot area, with its own x axis, y axis, title and data. A figure can hold many. You add one by calling add_axes with four numbers, which are the left, bottom, width and height as fractions of the figure. So [0.1, 0.1, 0.8, 0.8] means a plot that starts 10 percent in from the left and bottom, and takes 80 percent of the width and height.

python
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])

The legend method labels the lines or shapes, and its loc argument says where to put it. Here are the location codes.

Legend location codes
Location Code Location Code
best 0 center left 6
upper right 1 center right 7
upper left 2 lower center 8
lower left 3 upper center 9
lower right 4 center 10
right 5

axes.plot() in Matplotlib

The plot method draws lines or markers. You can add a format string as the third argument, which packs the colour, marker and line style into a short code. For example, rs- means red, square markers, solid line. Here is a working example.

python
import matplotlib.pyplot as plt
import numpy as np

y = [1, 5, 10, 17, 25, 36, 49, 64]
x1 = [1, 16, 30, 42, 55, 68, 77, 88]
x2 = [1, 6, 12, 18, 28, 40, 52, 65]
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
l1 = ax.plot(x1, y, "rs-")    # red, square markers, solid line
l2 = ax.plot(x2, y, "mo--")   # magenta, circle markers, dashed line
ax.legend(labels=("Hadoop", "Data science"), loc="lower right")
ax.set_title("Learners enrolled over time")
ax.set_xlabel("Learners enrolled")
ax.set_ylabel("Weeks since launch")
plt.show()

Line plot with two lines, one red with square markers and one magenta dashed with circle markers, with a legend in the lower right

You will see two rising lines and a legend in the lower right corner. These are the colour codes:

Colour codes
Code Colour Code Colour
b Blue m Magenta
g Green y Yellow
r Red k Black
c Cyan w White

Marker Codes in Matplotlib

Markers are the shapes drawn on each data point. Note that the D marker is a diamond.

Marker codes
Code Marker
“.” Point
“o” Circle
“x” X mark
“D” Diamond
“H” Hexagon
“s” Square
“+” Plus

Line Styles in Matplotlib

The dashed style is two hyphens typed together.

Line styles
Code Line style
“-“ Solid
“–“ Dashed
“-.” Dash dot
“:” Dotted

Multiplot in Matplotlib

To draw several plots in one figure, use plt.subplots with the number of rows and columns. It returns the figure and an array of axes, and you pick each axes by its row and column.

python
plt.subplots(nrows, ncols)
python
import matplotlib.pyplot as plt
import numpy as np

fig, a = plt.subplots(2, 2)
x = np.arange(2, 8)
a[0][0].plot(x, x ** 2)
a[0][0].set_title("square")
a[0][1].plot(x, np.sqrt(x))
a[0][1].set_title("square root")
a[1][0].plot(x, np.exp(x))
a[1][0].set_title("exp")
a[1][1].plot(x, np.log10(x))
a[1][1].set_title("log")
fig.tight_layout()
plt.show()

Multiplot output, a 2 by 2 grid of four small plots titled square, square root, exp and log

You will see four small plots, with square and exp rising fast, and square root and log rising slowly. The call tight_layout stops the titles from overlapping.

The subplot2grid Function

With subplots, every cell is the same size. The function subplot2grid lets you place axes at chosen positions in a grid, and let one axes span several rows or columns. The call is plt.subplot2grid(shape, location, rowspan, colspan).

python
import matplotlib.pyplot as plt
import numpy as np

a1 = plt.subplot2grid((4, 4), (0, 0), colspan=2)
a2 = plt.subplot2grid((4, 4), (0, 2), rowspan=3)
a3 = plt.subplot2grid((4, 4), (1, 0), rowspan=2, colspan=2)
x = np.arange(1, 10)
a2.plot(x, x ** 3)
a2.set_title("cube")
a1.plot(x, np.exp(x))
a1.set_title("exp")
a3.plot(x, np.log(x))
a3.set_title("log")
plt.tight_layout()
plt.show()

subplot2grid output with three plots of different sizes titled exp, cube and log

The grid is 4 by 4. The first plot takes the top two cells, the second takes three rows on the right, and the third takes a 2 by 2 block on the left.

Grid in Matplotlib

The grid method turns gridlines on or off, and lets you change their colour, line style and width. The short names are ls for linestyle and lw for linewidth.

python
import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(1, 3, figsize=(12, 4))
x = np.arange(2, 22)
axes[0].plot(x, x ** 4, "g", lw=2)
axes[0].grid(True)
axes[0].set_title("default grid")
axes[1].plot(x, np.exp(x), "r")
axes[1].grid(color="b", ls="-.", lw=0.25)
axes[1].set_title("custom grid")
axes[2].plot(x, x)
axes[2].set_title("no grid")
fig.tight_layout()
plt.show()

Grid output, three plots side by side with a default grid, a custom dash dot grid and no grid

Setting Limits in Matplotlib

By default Matplotlib chooses the range of each axis from your data. The methods set_xlim and set_ylim let you fix it, which is useful when a few huge values squash everything else. Note that these are two separate methods.

python
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
a1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
x = np.arange(1, 90)
a1.plot(x, np.exp(x), "r")
a1.set_title("exp")
a1.set_ylim(0, 80000)
a1.set_xlim(0, 10)
plt.show()

Setting limits output, a red exponential curve cut to the x range 0 to 10

Without the limits, the curve would reach a number with 39 digits and the early part would look flat. With the limits, you see the rise clearly.

Setting Ticks and Tick Labels

Ticks are the small marks along an axis. Tick labels are the text next to them. Use set_xticks and set_yticks to choose the positions, and set_xticklabels and set_yticklabels to choose the text.

python
import matplotlib.pyplot as plt
import numpy as np
import math

x = np.arange(0, math.pi * 4, 0.04)
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.plot(x, np.sin(x))
ax.set_xlabel("angle")
ax.set_title("sine")
ax.set_xticks([0, 3, 6, 9])
ax.set_xticklabels(["a", "b", "c", "d"])
ax.set_yticks([-1, 0, 1])
plt.show()

Setting ticks output, a sine wave with custom x tick labels a, b, c and d

The x axis now shows the letters a to d at positions 0, 3, 6 and 9, and the y axis shows only minus 1, 0 and 1.

Bar Plot in Matplotlib

A bar plot compares values across categories. Use bar for vertical bars and barh for horizontal ones. These are its main parameters.

Bar plot parameters
Parameter What it sets
x The positions of the bars on the x axis
height The height of each bar
width The width of each bar, 0.8 by default
bottom Where each bar starts on the y axis, used for stacking
align Whether bars are centred on the position or start at it

The first example stacks the girls on top of the boys using bottom. The stacked totals reach about 267, so the y axis here goes to 300. If you stopped it at 80, the bars would run off the chart.

python
import matplotlib.pyplot as plt
import numpy as np

N = 5
boysMeans = (120, 135, 130, 135, 127)
girlsMeans = (125, 132, 134, 120, 125)
ind = np.arange(N)  # the x locations for the groups
width = 0.35
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.bar(ind, boysMeans, width, color="r")
ax.bar(ind, girlsMeans, width, bottom=boysMeans, color="b")
ax.set_ylabel("Scores")
ax.set_title("Scores by group and gender (stacked)")
ax.set_xticks(ind, ("G1", "G2", "G3", "G4", "G5"))
ax.set_yticks(np.arange(0, 301, 50))
ax.legend(labels=["boys", "girls"])
plt.show()

Stacked bar plot of boys and girls scores for five groups, G1 to G5

Stacked bars hide the comparison, because only the bottom bar starts at zero. To compare the two means, put the bars side by side. Shift the boys bars left by half the width, and the girls bars right by half the width.

python
import matplotlib.pyplot as plt
import numpy as np

ind = np.arange(5)
width = 0.35
fig, ax = plt.subplots()
ax.bar(ind - width / 2, (120, 135, 130, 135, 127), width, color="r", label="boys")
ax.bar(ind + width / 2, (125, 132, 134, 120, 125), width, color="b", label="girls")
ax.set_ylabel("Scores")
ax.set_title("Scores by group and gender (side by side)")
ax.set_xticks(ind, ("G1", "G2", "G3", "G4", "G5"))
ax.legend()
plt.show()

Now the two bars in each group start at zero, and you can see at a glance that the girls beat the boys only in G1 and G3. The call to set_xticks with two arguments needs Matplotlib 3.5 or newer.

Histogram in Matplotlib

A histogram groups numbers into ranges, called bins, and counts how many values fall in each. It shows the shape of a distribution, for example how marks are spread across a class. It is an estimate of the distribution, because the picture changes with the bin choice.

Histogram parameters
Parameter What it sets
x The data, as an array or a list
bins The number of bins, or a list of bin edges
range The lowest and highest values to include
density If True, shows the share of values and not the count
python
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(1, 1)
a = np.array([22, 87, 5, 43, 56, 73, 55, 54, 11, 20, 51, 5, 79, 31, 27])
ax.hist(a, bins=[0, 25, 50, 75, 100])
ax.set_title("histogram of result")
ax.set_xticks([0, 25, 50, 75, 100])
ax.set_xlabel("marks")
ax.set_ylabel("no. of students")
plt.show()

The four bars have heights 5, 3, 5 and 2. That means 5 students scored below 25, 3 scored from 25 to 50, 5 from 50 to 75, and 2 from 75 up.

Pie Chart in Matplotlib

A pie chart shows each value as a share of the whole. It works best with five or fewer slices. These are its main parameters.

Pie chart parameters
Parameter What it sets
x The values, one per slice
labels The text for each slice
colors The colour of each slice
autopct A format string that prints the percentage on each slice
python
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
langs = ["Statistics", "Python", "Machine Learning", "SQL", "Big Data"]
students = [53, 27, 25, 19, 12]
ax.pie(students, labels=langs, autopct="%1.2f%%")
plt.show()

Pie chart of students per subject: Statistics, Python, Machine Learning, SQL and Big Data

Statistics gets 38.97 percent, Python 19.85, Machine Learning 18.38, SQL 13.97 and Big Data 8.82. The pie function already draws a circle, so you do not need an extra axis equal line.

Scatter Plot in Matplotlib

A scatter plot draws one dot for each pair of values. It shows whether two variables move together, and where the odd points sit. A third variable can set the colour or the size of the dots. Keep in mind that a pattern shows a link, not a cause.

python
import matplotlib.pyplot as plt
import numpy as np

girls_marks = [19, 30, 10, 69, 80, 60, 14, 30, 80, 34]
boys_marks = [90, 29, 49, 48, 100, 48, 38, 45, 20, 30]
grades_range = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.scatter(grades_range, girls_marks, color="r", label="girls")
ax.scatter(grades_range, boys_marks, color="b", label="boys")
ax.set_xlabel("Grades Range")
ax.set_ylabel("Grades Scored")
ax.set_title("scatter plot")
ax.legend()
plt.show()

Scatter plot of girls marks in red and boys marks in blue against grade range

The label and legend lines tell the reader which colour is which. Without them, red and blue would be a guess.

Contour Plot in Matplotlib

A contour plot shows a surface on a flat page. It is sometimes called a level plot. You give it a grid of X and Y values, and a height Z for each point. X goes on the x axis and Y on the y axis, and Z is shown by lines or colours. The function contour draws lines, and contourf fills the bands with colour.

python
import matplotlib.pyplot as plt
import numpy as np

xlist = np.linspace(-8.0, 10.0, 100)
ylist = np.linspace(-8.0, 10.0, 100)
X, Y = np.meshgrid(xlist, ylist)
Z = np.sqrt(X ** 4 + Y ** 4)
fig, ax = plt.subplots(1, 1)
cp = ax.contourf(X, Y, Z)
fig.colorbar(cp)  # add a colorbar to the plot
ax.set_title("Filled Contours Plot")
ax.set_xlabel("x (cm)")
ax.set_ylabel("y (cm)")
plt.show()

Filled contour plot with a colour bar, titled Filled Contours Plot

Quiver Plot in Matplotlib

A quiver plot draws arrows. Each arrow sits at a point and has a direction and a length. It is used for wind, water flow, forces and slopes. These are its parameters.

Quiver plot parameters
Parameter What it sets
x, y The position of each arrow
u, v The horizontal and vertical size of each arrow
c An optional colour for each arrow

In the code below, np.gradient gives the slope of the surface. It takes the spacing along y first, then x. The y grid steps by 0.25 and the x grid by 0.2, so the call is np.gradient(z, .25, .2).

python
import matplotlib.pyplot as plt
import numpy as np

x, y = np.meshgrid(np.arange(-2, 2, .2), np.arange(-2, 2, .25))
z = x * np.exp(-x ** 2 - y ** 2)
v, u = np.gradient(z, .25, .2)  # spacing along y first, then x
fig, ax = plt.subplots()
q = ax.quiver(x, y, u, v)
plt.show()

Quiver plot of arrows pointing uphill toward the peak of a surface

The arrows point uphill, toward the peak on the right and away from the dip on the left. They are longest where the slope is steepest.

Box Plot in Matplotlib

A box plot summarises a set of numbers with five values: the lowest value in range, the first quartile, the median, the third quartile and the highest value in range. The box runs from the first to the third quartile, and the line inside it is the median. By default the whiskers reach 1.5 times the interquartile range, and any point beyond that is drawn as a separate circle and called an outlier.

python
import matplotlib.pyplot as plt
import numpy as np

value1 = [82, 76, 24, 40, 67, 62, 71, 79, 81, 22, 98, 89, 78, 67, 72, 82, 87, 66, 56, 52]
value2 = [12, 25, 11, 35, 36, 32, 96, 95, 3, 90, 95, 32, 27, 55, 100, 12, 1, 451, 37, 21]
value3 = [23, 89, 12, 78, 72, 89, 25, 69, 68, 86, 19, 49, 15, 16, 16, 75, 65, 31, 25, 52]
value4 = [99, 33, 75, 66, 83, 61, 82, 98, 10, 87, 29, 72, 26, 23, 72, 88, 78, 99, 75, 30]
box_plot_data = [value1, value2, value3, value4]
plt.boxplot(box_plot_data)
plt.show()

Box plot of four data sets, with one far outlier above the second box

You will see four boxes. The second data set has a circle far above the others at 451. It is an outlier, and it also squashes the other boxes into the bottom of the chart. The first data set shows two outliers near 22 and 24, drawn so close that they look like one.

Violin Plot in Matplotlib

A violin plot has the same summary idea as a box plot, and adds the shape of the density on each side. The wide parts of the shape show where most values sit. It shows more than a box plot, but it is also harder to read for a new audience, so the choice depends on who reads it. Remember that a box plot shows the median, and not the mean.

python
import matplotlib.pyplot as plt
import numpy as np

np.random.seed(10)
collectn_1 = np.random.normal(300, 30, 300)
collectn_2 = np.random.normal(60, 10, 300)
collectn_3 = np.random.normal(60, 40, 300)
collectn_4 = np.random.normal(20, 25, 300)
# combine these different collections into a list
data_to_plot = [collectn_1, collectn_2, collectn_3, collectn_4]
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
vp = ax.violinplot(data_to_plot, showmedians=True)
plt.show()

Violin plot of four data sets with a median line in each

The first violin sits high, around 300. The second is short, because its values vary the least. The third is the tallest, because its values vary the most. The showmedians argument adds a line at the median of each one.

Three Dimensional Plotting in Matplotlib

Although Matplotlib began as a 2D library, it can draw 3D plots through the mplot3d toolkit. To create a 3D axes, set projection to the text "3d". In recent versions the import of mplot3d is no longer needed, but it does no harm. The first 3D example draws a line that spirals upward.

python
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = plt.axes(projection="3d")
z = np.linspace(0, 2, 200)
x = z * np.sin(40 * z)
y = z * np.cos(40 * z)
ax.plot3D(x, y, z, "red")
ax.set_title("3D line plot")
plt.show()

3D line plot, a red spiral rising upward

3D Contour Plot

The contour3D method draws contour lines on a 3D surface, given a grid of X and Y values and the height Z. The number 50 is how many levels to draw, and cmap sets the colours.

python
import matplotlib.pyplot as plt
import numpy as np

def f(x, y):
    return np.sin(np.sqrt(x ** 2 + y ** 2))

x = np.linspace(-4, 4, 40)
y = np.linspace(-4, 4, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.contour3D(X, Y, Z, 50, cmap="binary")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_title("3D contour")
plt.show()

3D contour plot of ripples drawn in grey

3D Wireframe Plot

The plot_wireframe method places a mesh of lines over the surface, so you see the shape without a solid fill. It uses the same X, Y and Z grids as the contour above. They are repeated here so the code runs on its own, and only the last calls change.

python
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-4, 4, 40)
y = np.linspace(-4, 4, 40)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X ** 2 + Y ** 2))
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.plot_wireframe(X, Y, Z, color="green")
ax.set_title("wireframe")
plt.show()

3D wireframe plot, a green net over a rippled surface

3D Surface Plot in Matplotlib

The plot_surface method draws a filled 3D surface. It takes the X, Y and Z grids, and the surface is coloured by height when you pass a colour map. In this example Z is the dependent value, calculated from X and Y.

python
import matplotlib.pyplot as plt
import numpy as np

x = np.outer(np.linspace(-4, 4, 40), np.ones(40))
y = x.copy().T  # transpose
z = np.cos(x ** 2 + y ** 2)
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.plot_surface(x, y, z, cmap="viridis", edgecolor="none")
ax.set_title("Surface plot")
plt.show()

3D surface plot coloured by height with the viridis colour map

The np.outer call builds a grid where every row is the same, and the transpose makes a matching grid in the other direction. Together they work like meshgrid.

Which Plot Should You Use?

Pick the plot by the question you want the chart to answer.

Choosing a plot
If you want to show Use Function
A trend over time Line plot plot
A comparison across categories Bar plot bar, barh
How one set of numbers is spread Histogram, box plot or violin plot hist, boxplot, violinplot
Parts of a whole, five or fewer Pie chart pie
The link between two numbers Scatter plot scatter
A surface on a flat page Contour plot contour, contourf
Direction and size at many points Quiver plot quiver
A surface in three dimensions Wireframe or surface plot plot_wireframe, plot_surface

Common Mistakes With Matplotlib

  • Curly quotes in code. Text copied from a web page or a Word file can turn straight quotes into curly ones, and Python rejects them.
  • A stray character before import. One minus sign at the start of a line stops the whole program.
  • Misspelt method names. The correct names are set_xticklabels and not set_xlabels, and xlabel and not xlable.
  • An axis that cuts off the data. Check the axis range after you set ticks, as in the stacked bar example.
  • Overlapping titles. Call tight_layout when you use more than one plot.
  • No labels or legend. If a chart has two colours and no legend, the reader has to guess.
  • Reading too much into a scatter plot. A pattern shows a link, not a cause.

Complete Script to Run Yourself

Here is the full script used to test every plot on this page. It saves each plot as an image in a folder called viz_out, so create that folder first. The line matplotlib.use("Agg") lets it run without opening windows. Remove it if you want to see the plots on screen.

python
import math
import matplotlib
matplotlib.use("Agg")  # lets the script run without a window; remove this line in Jupyter
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d  # not needed in recent versions, kept for older ones
 
print(matplotlib.__version__, np.__version__)
 
# Image functions: imsave, imread, imshow
pixels = np.array([[0.0, 0.5, 1.0], [1.0, 0.5, 0.0], [0.0, 1.0, 0.0]])
plt.imsave("viz_out/tiny.png", pixels, cmap="gray")
img = plt.imread("viz_out/tiny.png")
print(img.shape)
fig, ax = plt.subplots()
ax.imshow(img)
fig.savefig("viz_out/00_imshow.png")
plt.close(fig)
 
# Figure class
g1 = plt.figure(figsize=(6, 3), dpi=100, facecolor="lightgrey", edgecolor="navy", linewidth=2)
print(g1.get_size_inches(), g1.dpi)
plt.close(g1)
 
# axes.plot with format strings
y = [1, 5, 10, 17, 25, 36, 49, 64]
x1 = [1, 16, 30, 42, 55, 68, 77, 88]
x2 = [1, 6, 12, 18, 28, 40, 52, 65]
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
l1 = ax.plot(x1, y, "rs-")    # red, square markers, solid line
l2 = ax.plot(x2, y, "mo--")   # magenta, circle markers, dashed line
ax.legend(labels=("Hadoop", "Data science"), loc="lower right")
ax.set_title("Learners enrolled over time")
ax.set_xlabel("Learners enrolled")
ax.set_ylabel("Weeks since launch")
fig.savefig("viz_out/01_plot.png")
plt.close(fig)
 
# Multiplot: subplots(2, 2)
fig, a = plt.subplots(2, 2)
x = np.arange(2, 8)
a[0][0].plot(x, x ** 2)
a[0][0].set_title("square")
a[0][1].plot(x, np.sqrt(x))
a[0][1].set_title("square root")
a[1][0].plot(x, np.exp(x))
a[1][0].set_title("exp")
a[1][1].plot(x, np.log10(x))
a[1][1].set_title("log")
fig.tight_layout()
fig.savefig("viz_out/02_subplots.png")
plt.close(fig)
 
# subplot2grid
a1 = plt.subplot2grid((4, 4), (0, 0), colspan=2)
a2 = plt.subplot2grid((4, 4), (0, 2), rowspan=3)
a3 = plt.subplot2grid((4, 4), (1, 0), rowspan=2, colspan=2)
x = np.arange(1, 10)
a2.plot(x, x ** 3)
a2.set_title("cube")
a1.plot(x, np.exp(x))
a1.set_title("exp")
a3.plot(x, np.log(x))
a3.set_title("log")
plt.tight_layout()
plt.savefig("viz_out/03_grid2.png")
plt.close()
 
# grid()
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
x = np.arange(2, 22)
axes[0].plot(x, x ** 4, "g", lw=2)
axes[0].grid(True)
axes[0].set_title("default grid")
axes[1].plot(x, np.exp(x), "r")
axes[1].grid(color="b", ls="-.", lw=0.25)
axes[1].set_title("custom grid")
axes[2].plot(x, x)
axes[2].set_title("no grid")
fig.tight_layout()
fig.savefig("viz_out/04_grid.png")
plt.close(fig)
 
# Limits
fig = plt.figure()
a1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
x = np.arange(1, 90)
a1.plot(x, np.exp(x), "r")
a1.set_title("exp")
a1.set_ylim(0, 80000)
a1.set_xlim(0, 10)
fig.savefig("viz_out/05_limits.png")
plt.close(fig)
 
# Ticks and tick labels
x = np.arange(0, math.pi * 4, 0.04)
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.plot(x, np.sin(x))
ax.set_xlabel("angle")
ax.set_title("sine")
ax.set_xticks([0, 3, 6, 9])
ax.set_xticklabels(["a", "b", "c", "d"])
ax.set_yticks([-1, 0, 1])
fig.savefig("viz_out/06_ticks.png")
plt.close(fig)
 
# Bar plot, stacked and side by side
N = 5
boysMeans = (120, 135, 130, 135, 127)
girlsMeans = (125, 132, 134, 120, 125)
ind = np.arange(N)
width = 0.35
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.bar(ind, boysMeans, width, color="r")
ax.bar(ind, girlsMeans, width, bottom=boysMeans, color="b")
ax.set_ylabel("Scores")
ax.set_title("Scores by group and gender (stacked)")
ax.set_xticks(ind, ("G1", "G2", "G3", "G4", "G5"))
ax.set_yticks(np.arange(0, 301, 50))
ax.legend(labels=["boys", "girls"])
fig.savefig("viz_out/07_bar_stacked.png")
plt.close(fig)
 
fig, ax = plt.subplots()
ax.bar(ind - width / 2, boysMeans, width, color="r", label="boys")
ax.bar(ind + width / 2, girlsMeans, width, color="b", label="girls")
ax.set_ylabel("Scores")
ax.set_title("Scores by group and gender (side by side)")
ax.set_xticks(ind, ("G1", "G2", "G3", "G4", "G5"))
ax.legend()
fig.savefig("viz_out/07_bar_side.png")
plt.close(fig)
 
# Histogram
fig, ax = plt.subplots(1, 1)
a = np.array([22, 87, 5, 43, 56, 73, 55, 54, 11, 20, 51, 5, 79, 31, 27])
counts, edges, patches = ax.hist(a, bins=[0, 25, 50, 75, 100])
print(counts, edges)
ax.set_title("histogram of result")
ax.set_xticks([0, 25, 50, 75, 100])
ax.set_xlabel("marks")
ax.set_ylabel("no. of students")
fig.savefig("viz_out/08_hist.png")
plt.close(fig)
 
# Pie chart
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
langs = ["Statistics", "Python", "Machine Learning", "SQL", "Big Data"]
students = [53, 27, 25, 19, 12]
ax.pie(students, labels=langs, autopct="%1.2f%%")
fig.savefig("viz_out/09_pie.png")
plt.close(fig)
 
# Scatter plot
girls_marks = [19, 30, 10, 69, 80, 60, 14, 30, 80, 34]
boys_marks = [90, 29, 49, 48, 100, 48, 38, 45, 20, 30]
grades_range = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.scatter(grades_range, girls_marks, color="r", label="girls")
ax.scatter(grades_range, boys_marks, color="b", label="boys")
ax.set_xlabel("Grades Range")
ax.set_ylabel("Grades Scored")
ax.set_title("scatter plot")
ax.legend()
fig.savefig("viz_out/10_scatter.png")
plt.close(fig)
 
# Contour plot
xlist = np.linspace(-8.0, 10.0, 100)
ylist = np.linspace(-8.0, 10.0, 100)
X, Y = np.meshgrid(xlist, ylist)
Z = np.sqrt(X ** 4 + Y ** 4)
fig, ax = plt.subplots(1, 1)
cp = ax.contourf(X, Y, Z)
fig.colorbar(cp)  # add a colorbar to the plot
ax.set_title("Filled Contours Plot")
ax.set_xlabel("x (cm)")
ax.set_ylabel("y (cm)")
fig.savefig("viz_out/11_contour.png")
plt.close(fig)
 
# Quiver plot
x, y = np.meshgrid(np.arange(-2, 2, .2), np.arange(-2, 2, .25))
z = x * np.exp(-x ** 2 - y ** 2)
v, u = np.gradient(z, .25, .2)  # spacing along y first, then x
fig, ax = plt.subplots()
q = ax.quiver(x, y, u, v)
fig.savefig("viz_out/12_quiver.png")
plt.close(fig)
 
# Box plot
value1 = [82, 76, 24, 40, 67, 62, 71, 79, 81, 22, 98, 89, 78, 67, 72, 82, 87, 66, 56, 52]
value2 = [12, 25, 11, 35, 36, 32, 96, 95, 3, 90, 95, 32, 27, 55, 100, 12, 1, 451, 37, 21]
value3 = [23, 89, 12, 78, 72, 89, 25, 69, 68, 86, 19, 49, 15, 16, 16, 75, 65, 31, 25, 52]
value4 = [99, 33, 75, 66, 83, 61, 82, 98, 10, 87, 29, 72, 26, 23, 72, 88, 78, 99, 75, 30]
box_plot_data = [value1, value2, value3, value4]
fig, ax = plt.subplots()
bp = ax.boxplot(box_plot_data)
print("outliers per box:", [len(f.get_ydata()) for f in bp["fliers"]])
fig.savefig("viz_out/13_box.png")
plt.close(fig)
 
# Violin plot
np.random.seed(10)
collectn_1 = np.random.normal(300, 30, 300)
collectn_2 = np.random.normal(60, 10, 300)
collectn_3 = np.random.normal(60, 40, 300)
collectn_4 = np.random.normal(20, 25, 300)
data_to_plot = [collectn_1, collectn_2, collectn_3, collectn_4]
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
vp = ax.violinplot(data_to_plot, showmedians=True)
fig.savefig("viz_out/14_violin.png")
plt.close(fig)
 
# 3D line plot
fig = plt.figure()
ax = plt.axes(projection="3d")
z = np.linspace(0, 2, 200)
x = z * np.sin(40 * z)
y = z * np.cos(40 * z)
ax.plot3D(x, y, z, "red")
ax.set_title("3D line plot")
fig.savefig("viz_out/15_3dline.png")
plt.close(fig)
 
# 3D contour, wireframe and surface share one function
def f(x, y):
    return np.sin(np.sqrt(x ** 2 + y ** 2))
 
x = np.linspace(-4, 4, 40)
y = np.linspace(-4, 4, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
 
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.contour3D(X, Y, Z, 50, cmap="binary")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.set_title("3D contour")
fig.savefig("viz_out/16_contour3d.png")
plt.close(fig)
 
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.plot_wireframe(X, Y, Z, color="green")
ax.set_title("wireframe")
fig.savefig("viz_out/17_wireframe.png")
plt.close(fig)
 
x = np.outer(np.linspace(-4, 4, 40), np.ones(40))
y = x.copy().T  # transpose
z = np.cos(x ** 2 + y ** 2)
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.plot_surface(x, y, z, cmap="viridis", edgecolor="none")
ax.set_title("Surface plot")
fig.savefig("viz_out/18_surface.png")
plt.close(fig)
print("all plots saved")

Matplotlib has a lot of functions, but the ideas repeat. You make a figure, you add axes, you call one plot method, and you label it. Once you see that pattern, the 3D plots and the unusual charts become variations on the same steps. Run the script above, change the data, and break things on purpose. Then try a real dataset with pandas, and look at Seaborn when you want statistical charts with less code.

Popular Tags:

Data visualization in Python using MatPlotLib Tutorial Data Visualization With MatPlotLib online python course for beginners online python course with certificate online python programming course online python training course