Plotly interaction#
Here we show some basic ways of bringing Plotly graphs to life.
# The following renders plotly graphs in Jupyter Notebook, Jupyter Lab and VS Code formats
import plotly.io as pio
pio.renderers.default = "notebook+plotly_mimetype"
Animation#
Many plot types can easily be animated.
Parameters animation_frame and animation_group control what is animated
# Gapminder dataset of health and wealth stats for different countries
import plotly.express as px
df = px.data.gapminder()
df.head()
country | continent | year | lifeExp | pop | gdpPercap | iso_alpha | iso_num | |
---|---|---|---|---|---|---|---|---|
0 | Afghanistan | Asia | 1952 | 28.801 | 8425333 | 779.445314 | AFG | 4 |
1 | Afghanistan | Asia | 1957 | 30.332 | 9240934 | 820.853030 | AFG | 4 |
2 | Afghanistan | Asia | 1962 | 31.997 | 10267083 | 853.100710 | AFG | 4 |
3 | Afghanistan | Asia | 1967 | 34.020 | 11537966 | 836.197138 | AFG | 4 |
4 | Afghanistan | Asia | 1972 | 36.088 | 13079460 | 739.981106 | AFG | 4 |
# Select a single year
df_year = px.data.gapminder().query("year == 1952")
px.scatter(df_year, x="gdpPercap", y="lifeExp",
size="pop", color="continent", hover_name="country",
log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])
# Animate all years
px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country",
size="pop", color="continent", hover_name="country",
log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])
Plotly templates#
Themes can easily be changed to one in the default set or be tuned specifically.
import plotly.io as pio
pio.templates
Templates configuration
-----------------------
Default template: 'plotly'
Available templates:
['ggplot2', 'seaborn', 'simple_white', 'plotly',
'plotly_white', 'plotly_dark', 'presentation', 'xgridoff',
'ygridoff', 'gridon', 'none']
# https://plotly.com/python/templates/
# Animate all years
px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country",
size="pop", color="continent", hover_name="country",
log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90], template='plotly_dark')
# Play with me!
Plotly callbacks#
The following code generates a random scatter.
The callback update_point changes markers based on clicking and selection.
import plotly.graph_objects as go
import numpy as np
np.random.seed(1)
x = np.random.rand(100)
y = np.random.rand(100)
# Main plot
f = go.FigureWidget([go.Scatter(x=x, y=y, mode='markers')])
# Attributes of the scatter object
scatter = f.data[0]
colors = ['#a3a7e4'] * 100
scatter.marker.color = colors
scatter.marker.size = [10] * 100
f.layout.hovermode = 'closest'
# Create our callback function
def update_point(trace, points, selector):
c = list(scatter.marker.color)
s = list(scatter.marker.size)
for i in points.point_inds:
c[i] = '#bae2be'
s[i] = 20
with f.batch_update():
scatter.marker.color = c
scatter.marker.size = s
# Assign the callback function to the scatter object
scatter.on_click(update_point)
scatter.on_selection(update_point)
f # Do not use .show() or the figure will not be interactive
Selection behaviour#
The selection in one graph affects a second graph.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from plotly.graph_objs import FigureWidget
# Random data and counting of positive and negative x values
np.random.seed(1)
x = np.random.randn(100)
y = np.random.randn(100)
df = pd.DataFrame({'x': x, 'y': y})
allU = np.unique(df['x'].values>=0, return_counts=True)
# Switch the order of counts if most positive samples are selected
if allU[0][0]:
allU[1][0], allU[1][1] = allU[1][1], allU[1][0]
# Create figure with subplots
fig = make_subplots(rows=1, cols=2,
specs=[[{"type": "xy"}, {"type": "domain"}]])
fig.add_trace(
go.Scatter(x=x, y=y, mode='markers'),
row=1, col=1
)
fig.add_trace(
go.Pie(labels=['negative x', 'positive x'], values = allU[1],
marker_colors=("#FF5050","#5050FF"), sort=False),
row=1, col=2
)
fig.update_layout(height=500, width=800, title_text="Side By Side Subplots")
# Convert Figure to FigureWidget
fig_widget = FigureWidget(fig)
# Get scatter and pie from FigureWidget
scatter2 = fig_widget.data[0]
pie = fig_widget.data[1]
# Create our callback function
def selection_handler(trace, points, selector):
df_selected = pd.DataFrame({'x': points.xs, 'y': points.ys})
sel_log, sel_num = np.unique(df_selected['x'].values>=0, return_counts=True)
# If only positive selections, add a zero count for negative x
if sel_log.shape[0] == 1 and sel_log[0]:
sel_log = np.concatenate([np.array([False]), sel_log])
sel_num = np.concatenate([np.array([0]), sel_num])
# Switch the order of counts if most positive samples are selected
if sel_log[0]:
sel_num[0], sel_num[1] = sel_num[1], sel_num[0]
with fig_widget.batch_update():
pie.values = sel_num
fig_widget.update_traces()
scatter2.on_selection(selection_handler)
fig_widget # Careful here!
Adding widgets to a Plotly graph#
As with the above interactivity, accessing the object to minpulate is important.
Using the interactive function from ipywidgets in combination with a function that sets the properties is convenient.
import plotly.graph_objects as go
from ipywidgets import interactive
import numpy as np
np.random.seed(1)
x = np.random.rand(1000)
y = np.random.rand(1000)
# Main plot
f = go.FigureWidget([go.Scatter(x=x, y=y, mode='markers')])
# Attributes of the scatter object
scatter = f.data[0]
scatter.marker.size = 10
# Add ipywidget slider to control the marker size
def set_marker_size(size):
scatter.marker.size = size
marker_slider = interactive(set_marker_size, size=(1, 20, 1))
marker_slider.children[0].layout.width = '400px'
display(f, marker_slider)