Plot (Plotly)
#| standalone: true
#| components: [viewer]
#| viewerHeight: 500
import plotly.express as px
from palmerpenguins import load_penguins
from shiny import App, ui
from shinywidgets import output_widget, render_widget
penguins = load_penguins()
app_ui = ui.page_fluid(
ui.input_slider("n", "Number of bins", 1, 100, 20),
output_widget("plot"),
)
def server(input, output, session):
@render_widget
def plot():
scatterplot = px.histogram(
data_frame=penguins,
x="body_mass_g",
nbins=input.n(),
).update_layout(
title={"text": "Penguin Mass", "x": 0.5},
yaxis_title="Count",
xaxis_title="Body Mass (g)",
)
return scatterplot
app = App(app_ui, server)
import plotly.express as px
from palmerpenguins import load_penguins
from shiny import App, ui
from shinywidgets import output_widget, render_widget
penguins = load_penguins()
app_ui = ui.page_fluid(
ui.input_slider("n", "Number of bins", 1, 100, 20),
output_widget("plot"),
)
def server(input, output, session):
@render_widget
def plot():
scatterplot = px.histogram(
data_frame=penguins,
x="body_mass_g",
nbins=input.n(),
).update_layout(
title={"text": "Penguin Mass", "x": 0.5},
yaxis_title="Count",
xaxis_title="Body Mass (g)",
)
return scatterplot
app = App(app_ui, server)
import plotly.express as px
from palmerpenguins import load_penguins
from shiny.express import input, ui
from shinywidgets import render_widget
penguins = load_penguins()
ui.input_slider("n", "Number of bins", 1, 100, 20)
@render_widget
def plot():
scatterplot = px.histogram(
data_frame=penguins,
x="body_mass_g",
nbins=input.n(),
).update_layout(
title={"text": "Penguin Mass", "x": 0.5},
yaxis_title="Count",
xaxis_title="Body Mass (g)",
)
return scatterplot
Relevant Functions
-
shinywidgets.output_widget
shinywidgets.output_widget(id, width, height)
-
@shinywidgets.render_widget()
shinywidgets.render_widget(fn)
Details
Plotly is an interactive graphics plotting library.
To make an interactive plot with Plotly in Shiny for Python, we will need to use the shinywidgets
library to connect Shiny with ipywidgets
.
To make a Plotly figure, we need to do the following steps:
Import the
output_widget()
andrender_widget()
functions from theshinywidgets
library,from shinywidgets import output_widget, render_widget
Call
output_widget()
to the UI of your app to create a div in which to display the figure. Where you call this function will determine where the figure will appear within the layout of the app. Theid
parameter you provide will be used to link to other parts of the Shiny app.Define a function within the
server()
function that creates the figure.The name of the function should be the same value you passed into the
id
parameter in youroutput_widget()
function call in the UI.If your function calls reactive values, Shiny will update your figure whenever those values change, in a reactive fashion.
Decorate your plotting function with a
@render_widget()
decorator.- If your plotting function is not the same as the
id
you used in theui.output_widget()
, you can add an additional@output(id=...)
decorator. - If you use the
@output()
decorator, make sure it is above the@render_widget()
decorator.
- If your plotting function is not the same as the
Visit shiny.posit.co/py/docs/ipywidgets.html to learn more about using ipywidgets with Shiny.
Plots as Inputs
You can use a Plotly figure as an input widget, collecting the locations of user clicks, hovers, and selections.
Convert your Plotly figure to a
FigureWidget
usingplotly.graph_objects.FigureWidget()
, which extends the functionality of a standard Plotly figure and enables event handling.Use the
.data
attribute of theFigureWidget
to access its traces. The data attribute is a list that contains all the traces in the figure. Individual traces are accessible as.data[0]
,.data[1]
, etc., depending on how many traces are present in the figure.Use event handlers to listen for user interactions with the plot. These handlers include methods like
.on_click()
,.on_hover()
, and.on_selection()
, which are available for individual traces within the figure. You attach these handlers to a specific trace (e.g.,.data[0].on_click()
) to capture interactions with the data points in that trace.When you use an event handler like
.on_click()
, you need to pass it a callback function that defines what should happen when the event occurs. When defining the callback function, it should receive the parameterstrace
,points
, andstate
, which provide information about the data points interacted with. In our example app below, our callback function updates a reactive value to contain the information about the points clicked, hovered over, or selected.
Variations
Plot as input
First, convert your Plotly figure to a FigureWidget
using plotly.graph_objects.FigureWidget()
. Then, you can use .on_click()
, .on_hover()
, .on_selection()
, and other methods to control what happens when the user clicks, hover, or selects points. Capture the click, hover, and selection information as reactive variables. The app below displays the values returned, but you can also call the values from within your computations to filter tables, perform calculations, and so on.
#| standalone: true
#| components: [viewer]
#| viewerHeight: 720
import plotly.express as px
import plotly.graph_objects as go
from plotly.callbacks import Points
import plotly.express as px
from palmerpenguins import load_penguins
from shiny import App, ui, render, reactive
from shinywidgets import output_widget, render_widget
penguins = load_penguins()
app_ui = ui.page_fluid(
output_widget("plot"),
"Click info",
ui.output_text_verbatim("click_info", placeholder=True),
"Hover info",
ui.output_text_verbatim("hover_info", placeholder=True),
"Selection info (use box or lasso select)",
ui.output_text_verbatim("selection_info", placeholder=True)
)
def server(input, output, session):
click_reactive = reactive.value()
hover_reactive = reactive.value()
selection_reactive = reactive.value()
@render_widget
def plot():
fig = px.scatter(
data_frame=penguins, x="body_mass_g", y="bill_length_mm"
).update_layout(
yaxis_title="Bill Length (mm)",
xaxis_title="Body Mass (g)",
)
w = go.FigureWidget(fig.data, fig.layout)
w.data[0].on_click(on_point_click)
w.data[0].on_hover(on_point_hover)
w.data[0].on_selection(on_point_selection)
return w
def on_point_click(trace, points, state):
click_reactive.set(points)
def on_point_hover(trace, points, state):
hover_reactive.set(points)
def on_point_selection(trace, points, state):
selection_reactive.set(points)
@render.text
def click_info():
return click_reactive.get()
@render.text
def hover_info():
return hover_reactive.get()
@render.text
def selection_info():
return selection_reactive.get()
app = App(app_ui, server)
import plotly.express as px
import plotly.graph_objects as go
from palmerpenguins import load_penguins
from plotly.callbacks import Points
from shiny import reactive
from shiny.express import input, render, ui
from shiny.ui import output_code, output_plot
from shinywidgets import render_plotly
penguins = load_penguins()
@render_plotly
def plot():
fig = px.scatter(
data_frame=penguins, x="body_mass_g", y="bill_length_mm"
).update_layout(
yaxis_title="Bill Length (mm)",
xaxis_title="Body Mass (g)",
)
# Need to create a FigureWidget() for on_click to work
w = go.FigureWidget(fig.data, fig.layout)
w.data[0].on_click(on_point_click)
w.data[0].on_hover(on_point_hover)
w.data[0].on_selection(on_point_selection)
return w
# Capture the clicked point in a reactive value
click_reactive = reactive.value()
hover_reactive = reactive.value()
selection_reactive = reactive.value()
def on_point_click(trace, points, state):
click_reactive.set(points)
def on_point_hover(trace, points, state):
hover_reactive.set(points)
def on_point_selection(trace, points, state):
selection_reactive.set(points)
"Click info"
@render.code
def click_info():
return str(click_reactive.get())
"Hover info"
@render.code
def hover_info():
return str(hover_reactive.get())
"Selection info (use box or lasso select)"
@render.code
def selection_info():
return str(selection_reactive.get())
import plotly.express as px
import plotly.graph_objects as go
from plotly.callbacks import Points
import plotly.express as px
from palmerpenguins import load_penguins
from shiny import App, ui, render, reactive
from shinywidgets import output_widget, render_widget
penguins = load_penguins()
app_ui = ui.page_fluid(
output_widget("plot"),
"Click info",
ui.output_text_verbatim("click_info", placeholder=True),
"Hover info",
ui.output_text_verbatim("hover_info", placeholder=True),
"Selection info (use box or lasso select)",
ui.output_text_verbatim("selection_info", placeholder=True)
)
def server(input, output, session):
click_reactive = reactive.value()
hover_reactive = reactive.value()
selection_reactive = reactive.value()
@render_widget
def plot():
fig = px.scatter(
data_frame=penguins, x="body_mass_g", y="bill_length_mm"
).update_layout(
yaxis_title="Bill Length (mm)",
xaxis_title="Body Mass (g)",
)
w = go.FigureWidget(fig.data, fig.layout)
w.data[0].on_click(on_point_click)
w.data[0].on_hover(on_point_hover)
w.data[0].on_selection(on_point_selection)
return w
def on_point_click(trace, points, state):
click_reactive.set(points)
def on_point_hover(trace, points, state):
hover_reactive.set(points)
def on_point_selection(trace, points, state):
selection_reactive.set(points)
@render.text
def click_info():
return click_reactive.get()
@render.text
def hover_info():
return hover_reactive.get()
@render.text
def selection_info():
return selection_reactive.get()
app = App(app_ui, server)