plotnine examples¶
Simple scatter plot¶
Imports
[1]:
from plotnine import *
from plotnine.data import mtcars
Scatter plot
[2]:
(
ggplot(mtcars, aes("wt", "mpg"))
+ geom_point()
)

[2]:
<Figure Size: (640 x 480)>
plotnine.aes creates aesthetic mappings with miles per gallon mpg
on the y-axis and weight of cars wt
on the x-axis. plotnine.geom_point then creates a scatter plot.
Colour differentiation of the variables
[3]:
(
ggplot(mtcars, aes("wt", "mpg", color="factor(gear)"))
+ geom_point()
)

[3]:
<Figure Size: (640 x 480)>
Smoothed linear model with confidence intervals
With plotnine.stats.stat_smooth, smoothed conditional means can be calculated, whereby
lm
is based on a linear model:
[4]:
(
ggplot(mtcars, aes("wt", "mpg", color="factor(gear)"))
+ geom_point()
+ stat_smooth(method="lm")
)

[4]:
<Figure Size: (640 x 480)>
Display in separated fields
The fields can be separated with plotnine.facet_wrap.
[5]:
(
ggplot(mtcars, aes("wt", "mpg", color="factor(gear)"))
+ geom_point()
+ stat_smooth(method="lm")
+ facet_wrap("~gear")
)

[5]:
<Figure Size: (640 x 480)>
Interactive diagrams¶
Interactive diagrams can also be created with ipywidgets.
Imports
[6]:
import itertools
from copy import copy
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotnine as p9
from IPython.display import display
from ipywidgets import widgets
from plotnine.data import mtcars
Create an interactive scatter diagram
In the following we look at horsepower on the x-axis, miles per gallon on the y-axis and differentiate the weight of the cars by colour:
[7]:
%matplotlib notebook
p = (ggplot(mtcars, aes(x="hp", y="mpg", color="wt"))
+ p9.geom_point()
+ p9.theme_linedraw()
)
p
[7]:
<Figure Size: (640 x 480)>
Now we select the cars based on the number of cylinders.
First, we prepare the list that we will use to select subsets of data based on the number of cylinders:
[8]:
cylList = np.unique(mtcars["cyl"])
We will now use this list for a drop-down menu with the number of cylinders:
[9]:
cylSelect = widgets.Dropdown(
options=list(cylList),
value=cylList[1],
description="Cylinders:",
disabled=False,
)
The widgets should be able to update the same display and not create a new plot every time a change is made.
First, we determine the maximum ranges of the relevant variables in order to keep the axes constant during updates.
With
minWt
andmaxWt
we obtain the range of the weights.With
wtOptions
we convert the NumPy array into a sorted Python list of unique values.In
cylSelect
we define the first selection for the dropdown menu of the number of cylinders.
[10]:
minWt = min(mtcars["wt"])
maxWt = max(mtcars["wt"])
wtOptions = list(
np.sort(np.unique(mtcars.loc[mtcars["cyl"] == cylList[0], "wt"]))
)
minHP = min(mtcars["hp"])
maxHP = max(mtcars["hp"])
minMPG = min(mtcars["mpg"])
maxMPG = max(mtcars["mpg"])
cylSelect = widgets.Dropdown(
options=list(cylList),
value=cylList[1],
description="Cylinders:",
disabled=False,
)
We then use
get_current_artists
to determine all the objects that are to be rendered:
[11]:
def get_current_artists():
# Return artists attached to all the matplotlib axes
axes = plt.gca()
return itertools.chain(
axes.lines, axes.collections, axes.artists, axes.patches, axes.texts
)
Now we create the
plotUpdate
function, which is called to update the plot every time we change a selection.
[12]:
fig = None
axs = None
def plotUpdate(*args):
# Use global variables for matplotlib’s figure and axis.
global fig, axs
# Get current values of the selection widget
cylValue = cylSelect.value
# Create a temporary dataset that is constrained by the user's selections.
tmpDat = mtcars.loc[(mtcars["cyl"] == cylValue), :]
# Create plotnine's plot
# Using the maximum and minimum values we gatehred before, we can keep the plot axis from
# changing with the cyinder selection
p = (
ggplot(tmpDat, aes(x="hp", y="mpg", color="wt"))
+ p9.geom_point()
+ p9.theme_linedraw()
)
if fig is None:
# If this is the first time a plot is made in the notebook, we let plotnine create a new
# matplotlib figure and axis.
fig = p.draw()
axs = fig.axes
else:
# p = copy(p)
# This helps keeping old selected data from being visualized after a new selection is made.
# We delete all previously reated artists from the matplotlib axis.
for artist in get_current_artists():
artist.remove()
# If a plot is being updated, we re-use the figure an axis created before.
p._draw_using_figure(fig, axs)
Now we observe whether the value in our drop-down menu for the number of cylinders changes:
[13]:
cylSelect.observe(plotUpdate, "value")
The widget is displayed with
display
:
[14]:
display(cylSelect)
Now we plot the first image with initial values.
[15]:
plotUpdate()
Mit der Matplotlib-Funktion
tight_layout()
passen wir die Figur an die Abmessungen des Plots an:
[16]:
plt.tight_layout()
/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_23701/2925123646.py:2: UserWarning: The figure layout has changed to tight
Trick, damit das erste gerenderte Bild dem
tight_layout
entspricht. Ohne diesen Befehl würde die Figur erst nach der ersten Aktualisierung in ihre Abmessungen passen.
[17]:
cylSelect.value = cylList[0]
Bereichsregler hinzufügen
Nun schränken wir mit einem Bereichsregler die Daten basierend auf dem Fahrzeuggewicht ein:
[18]:
# The first selection is a drop-down menu for number of cylinders
cylSelect = widgets.Dropdown(
options=list(cylList),
value=cylList[1],
description="Cylinders:",
disabled=False,
)
# The second selection is a range of weights
wtSelect = widgets.SelectionRangeSlider(
options=wtOptions,
index=(0, len(wtOptions) - 1),
description="Weight",
disabled=False,
)
widgetsCtl = widgets.HBox([cylSelect, wtSelect])
# The range of weights needs to always be dependent on the cylinder selection.
def updateRange(*args):
"""Updates the selection range from the slider depending on the cylinder selection."""
cylValue = cylSelect.value
wtOptions = list(
np.sort(np.unique(mtcars.loc[mtcars["cyl"] == cylValue, "wt"]))
)
wtSelect.options = wtOptions
wtSelect.index = (0, len(wtOptions) - 1)
cylSelect.observe(updateRange, "value")
# For the widgets to update the same plot, instead of creating one new image every time
# a selection changes. We keep track of the matplotlib image and axis, so we create only one
# figure and set of axis, for the first plot, and then just re-use the figure and axis
# with plotnine's "_draw_using_figure" function.
fig = None
axs = None
# This is the main function that is called to update the plot every time we chage a selection.
def plotUpdate(*args):
# Use global variables for matplotlib's figure and axis.
global fig, axs
# Get current values of the selection widgets
cylValue = cylSelect.value
wrRange = wtSelect.value
# Create a temporary dataset that is constrained by the user's selections.
tmpDat = mtcars.loc[
(mtcars["cyl"] == cylValue)
& (mtcars["wt"] >= wrRange[0])
& (mtcars["wt"] <= wrRange[1]),
:,
]
# Create plotnine's plot
p = (
ggplot(tmpDat, aes(x="hp", y="mpg", color="wt"))
+ p9.geom_point()
+ p9.theme_linedraw()
+ p9.lims(x=[minHP, maxHP], y=[minMPG, maxMPG])
+ p9.scale_color_continuous(limits=(minWt, maxWt))
)
if fig is None:
fig = p.draw()
axs = fig.axes
else:
for artist in get_current_artists():
artist.remove()
p._draw_using_figure(fig, axs)
cylSelect.observe(plotUpdate, "value")
wtSelect.observe(plotUpdate, "value")
# Display the widgets
display(widgetsCtl)
# Plots the first image, with inintial values.
plotUpdate()
# Matplotlib function to make the image fit within the plot dimensions.
plt.tight_layout()
# Trick to get the first rendered image to follow the previous "tight_layout" command.
# without this, only after the first update would the figure be fit inside its dimensions.
cylSelect.value = cylList[0]
/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_23701/1448775928.py:89: UserWarning: The figure layout has changed to tight
Diagramm optimieren
Schließlich ändern wir noch einige Diagrammeigenschaften, um eine verständlichere Abbildung zu erhalten:
[19]:
# The first selection is a drop-down menu for number of cylinders
cylSelect = widgets.Dropdown(
options=list(cylList),
value=cylList[1],
description="Cylinders:",
disabled=False,
)
# The second selection is a range of weights
wtSelect = widgets.SelectionRangeSlider(
options=wtOptions,
index=(0, len(wtOptions) - 1),
description="Weight",
disabled=False,
)
widgetsCtl = widgets.HBox([cylSelect, wtSelect])
# The range of weights needs to always be dependent on the cylinder selection.
def updateRange(*args):
"""Updates the selection range from the slider depending on the cylinder selection."""
cylValue = cylSelect.value
wtOptions = list(
np.sort(np.unique(mtcars.loc[mtcars["cyl"] == cylValue, "wt"]))
)
wtSelect.options = wtOptions
wtSelect.index = (0, len(wtOptions) - 1)
cylSelect.observe(updateRange, "value")
fig = None
axs = None
# This is the main function that is called to update the plot every time we chage a selection.
def plotUpdate(*args):
# Use global variables for matplotlib's figure and axis.
global fig, axs
# Get current values of the selection widgets
cylValue = cylSelect.value
wrRange = wtSelect.value
# Create a temporary dataset that is constrained by the user's selections of
# number of cylinders and weight.
tmpDat = mtcars.loc[
(mtcars["cyl"] == cylValue)
& (mtcars["wt"] >= wrRange[0])
& (mtcars["wt"] <= wrRange[1]),
:,
]
# Create plotnine's plot showing all data ins smaller grey points, and
# the selected data with coloured points.
p = (
ggplot(tmpDat, aes(x="hp", y="mpg", color="wt"))
+ p9.geom_point(mtcars, color="grey")
+ p9.geom_point(size=3)
+ p9.theme_linedraw()
+ p9.xlim([minHP, maxHP])
+ p9.ylim([minMPG, maxMPG])
+ p9.scale_color_continuous(
name="spring", limits=(np.floor(minWt), np.ceil(maxWt))
)
+ p9.labs(x="Horse-Power", y="Miles Per Gallon", color="Weight")
)
if fig is None:
fig = p.draw()
axs = fig.axes
else:
for artist in get_current_artists():
artist.remove()
p._draw_using_figure(fig, axs)
cylSelect.observe(plotUpdate, "value")
wtSelect.observe(plotUpdate, "value")
# Display the widgets
display(widgetsCtl)
# Plots the first image, with inintial values.
plotUpdate()
# Matplotlib function to make the image fit within the plot dimensions.
plt.tight_layout()
# Trick to get the first rendered image to follow the previous "tight_layout" command.
# without this, only after the first update would the figure be fit inside its dimensions.
cylSelect.value = cylList[0]
/var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_23701/207462609.py:91: UserWarning: The figure layout has changed to tight