
Start static, then add interactivity.
from palmerpenguins import load_penguins
from plotnine import aes, geom_point, ggplot, theme_minimal
1from shiny.express import input, render, ui
dat = load_penguins().dropna()input, render, and ui from shiny.express
from palmerpenguins import load_penguins
from plotnine import aes, geom_point, ggplot, theme_minimal
from shiny.express import input, render, ui
dat = load_penguins().dropna()
1num_cols = dat.select_dtypes("float64").columns.tolist()You can still use your normal Python skills!
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
from palmerpenguins import load_penguins
from plotnine import aes, geom_point, ggplot, theme_minimal
from shiny.express import input, render, ui
dat = load_penguins().dropna()
num_cols = dat.select_dtypes("float64").columns.tolist()
ui.input_select("x", "", num_cols, selected="bill_depth_mm")
ui.input_select("y", "", num_cols, selected="body_mass_g")"x""y"#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
from palmerpenguins import load_penguins
from plotnine import aes, geom_point, ggplot, theme_minimal
from shiny.express import input, render, ui
dat = load_penguins().dropna()
num_cols = dat.select_dtypes("float64").columns.tolist()
ui.input_select("x", "", num_cols, selected="bill_depth_mm")
ui.input_select("y", "", num_cols, selected="body_mass_g")
@render.plot
def plot():
return (
ggplot(dat, aes(x=input.x(), y=input.y(), color="species"))
+ geom_point(alpha=0.7)
+ theme_minimal()
)@render.plot tells Shiny this function produces a plotinput.x() and input.y() read the current dropdown selectionsinput.x() and input.y() are reactive values — they change when the user picks
input.*() - signals reading from an input component@render.plot creates a reactive context — it re-runs whenever its inputs change
@render* - signals an output componentShiny tracked both dependencies automatically.
Important
Reactive behaviors must stay within a reactive context.
@reactive.*() values for reactive intermediates#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
from palmerpenguins import load_penguins
from plotnine import aes, geom_point, ggplot, theme_minimal
from shiny.express import input, render, ui
dat = load_penguins().dropna()
num_cols = dat.select_dtypes("float64").columns.tolist()
ui.input_select("x", "", num_cols, selected="bill_depth_mm")
ui.input_select("y", "", num_cols, selected="body_mass_g")
(
ggplot(dat, aes(x=input.x(), y=input.y(), color="species"))
+ geom_point(alpha=0.7)
+ theme_minimal()
)#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
from palmerpenguins import load_penguins
from plotnine import aes, geom_point, ggplot, theme_minimal
from shiny.express import input, render, ui
dat = load_penguins().dropna()
num_cols = dat.select_dtypes("float64").columns.tolist()
ui.input_select("x", "", num_cols, selected="bill_depth_mm")
ui.input_select("y", "", num_cols, selected="body_mass_g")
@render.plot
def plot():
return (ggplot(dat, aes(x=input.x(), y=input.y(), color="species")) +
geom_point(alpha=0.7) +
theme_minimal()
)PyCon US 2026. chendaniely/pycon-2026-shiny