Your first app


title: “Your first Shiny application”

Plan out your application data

  • Start off with the data you have an some kind of data artifact without the interactivity
  • Use a placeholder variable to apply the interactivity manually

Visualizing penguin data

import altair as alt
from palmerpenguins import load_penguins

dat = load_penguins().dropna()

species = "Adelie"  # selected species
sel = dat.loc[dat.species == species]  # selected data

# base histogram for all data
base = (
    alt.Chart(dat)
    .mark_bar(color="#C2C2C4", binSpacing=0)
    .encode(
        alt.X("bill_length_mm:Q", bin=alt.Bin(step=1), title="bill_length_mm"),
        alt.Y("count()", title="count"),
    )
)

# overlay histogram for selected species
overlay = (
    alt.Chart(sel)
    .mark_bar(color="#447099", binSpacing=0)
    .encode(alt.X("bill_length_mm:Q", bin=alt.Bin(step=1)), alt.Y("count()"))
)

# layer the charts
base + overlay

Change the species

import altair as alt
from palmerpenguins import load_penguins

dat = load_penguins().dropna()

species = "Gentoo"  # selected species
sel = dat.loc[dat.species == species]  # selected data

# base histogram for all data
base = (
    alt.Chart(dat)
    .mark_bar(color="#C2C2C4", binSpacing=0)
    .encode(
        alt.X("bill_length_mm:Q", bin=alt.Bin(step=1), title="bill_length_mm"),
        alt.Y("count()", title="count"),
    )
)

# overlay histogram for selected species
overlay = (
    alt.Chart(sel)
    .mark_bar(color="#447099", binSpacing=0)
    .encode(alt.X("bill_length_mm:Q", bin=alt.Bin(step=1)), alt.Y("count()"))
)

# layer the charts
base + overlay

The user interface

Radio Buttons

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: horizontal

from shiny import App, ui

app_ui = ui.page_fluid(
    ui.input_radio_buttons(
        id="species",
        label="Species",
        choices=["Adelie", "Gentoo", "Chinstrap"],
    )
)


def server(input, output, session):
    pass


app = App(app_ui, server)

Run your application

  1. Positron/VS Code + Shiny Extension

Tip

Begin your application file names with app-* so you can use the play button.

  1. Command line:
shiny run --reload app.py

Radio Buttons: options

https://shiny.posit.co/py/api/core/ui.input_radio_buttons.html

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: horizontal

from shiny import App, ui

app_ui = ui.page_fluid(
    ui.input_radio_buttons(
        id="species",
        label="Species",
        choices=["Adelie", "Gentoo", "Chinstrap"],
        inline=True,
    )
)


def server(input, output, session):
    pass


app = App(app_ui, server)


Add in the figure

Now let’s add all that data and plotting code from earlier into our application.

If we just dump in our code, the application errors because it does not know what to do with the figure that’s trying to be printed.

Output will error

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: horizontal

from shiny import ui, render, App
import altair as alt
from palmerpenguins import load_penguins

dat = load_penguins().dropna()

app_ui = ui.page_fluid(
    ui.input_radio_buttons(
        id="species",
        label="Species",
        choices=["Adelie", "Gentoo", "Chinstrap"],
        inline=True,
    )
)


def server(input, output, session):
    species = "Gentoo"  # selected species
    sel = dat.loc[dat.species == species]  # selected data

    # Base histogram for all data
    base = (
        alt.Chart(dat)
        .mark_bar(color="#C2C2C4", binSpacing=0)
        .encode(
            alt.X("bill_length_mm:Q", bin=alt.Bin(step=1), title="bill_length_mm"),
            alt.Y("count()", title="count"),
        )
    )

    # Overlay histogram for selected species
    overlay = (
        alt.Chart(sel)
        .mark_bar(color="#447099", binSpacing=0)
        .encode(alt.X("bill_length_mm:Q", bin=alt.Bin(step=1)), alt.Y("count()"))
    )

    return base + overlay


app = App(app_ui, server)

Outputs (python core syntax + R)

  • Each output component has 2 parts:

  • We now need to use one of the built-in Shiny output components,

  • plotnine figure (which is based on matplotlib), plot output component.

  • For example, we want to return a plot, so we will need to wrap our plotnine code, and decorate it with the @render.plot decorator.

  • For altair (and jupyter widgets) we need to use the shinywidgets package

Render plot output

...
from shinywidgets import render_altair, output_widget
...

app_ui = ui.page_fluid(
    ...

    output_widget("plot"),


def server(input, output, session):
    @render_altair
    def plot():
        dat = load_penguins().dropna()
        ...
ImportantReturn the output

Don’t forget to return the object you want displayed in the function! Otherwise the output will not render.

Render plot output

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: horizontal

from shiny import ui, render, App
import altair as alt
from palmerpenguins import load_penguins
from shinywidgets import render_altair, output_widget

dat = load_penguins().dropna()

app_ui = ui.page_fluid(
    ui.input_radio_buttons(
        id="species",
        label="Species",
        choices=["Adelie", "Gentoo", "Chinstrap"],
        inline=True,
    ),
    output_widget("plot"),
)


def server(input, output, session):
    @render_altair
    def plot():
        dat = load_penguins().dropna()

        species = "Gentoo"  # selected species
        sel = dat.loc[dat.species == species]  # selected data

        # Base histogram for all data
        base = (
            alt.Chart(dat)
            .mark_bar(color="#C2C2C4", binSpacing=0)
            .encode(
                alt.X("bill_length_mm:Q", bin=alt.Bin(step=1), title="bill_length_mm"),
                alt.Y("count()", title="count"),
            )
        )

        # Overlay histogram for selected species
        overlay = (
            alt.Chart(sel)
            .mark_bar(color="#447099", binSpacing=0)
            .encode(alt.X("bill_length_mm:Q", bin=alt.Bin(step=1)), alt.Y("count()"))
        )

        return base + overlay


app = App(app_ui, server)

Reactivity

  • But the radio buttons don’t change anything

  • We didn’t connect the input component to the output component

  • Reactivity is what makes Shiny unique

    • More about reactivity next lecture
    • tl;dr: use input.ID() to read the input component value

The data reacts to the input

...
    ui.input_radio_buttons(
        id="species",
        label="Species",
        choices=["Adelie", "Gentoo", "Chinstrap"],
        inline=True,
    ),
...

        species = input.species()
...

Your first application: altair

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
#| layout: horizontal
#| viewerHeight: 500

from shiny import ui, render, App
import altair as alt
from palmerpenguins import load_penguins
from shinywidgets import render_altair, output_widget

dat = load_penguins().dropna()

app_ui = ui.page_fluid(
    ui.input_radio_buttons(
        id="species",
        label="Species",
        choices=["Adelie", "Gentoo", "Chinstrap"],
        inline=True,
    ),
    output_widget("plot"),
)


def server(input, output, session):
    @render_altair
    def plot():

        species = input.species()  # selected species
        sel = dat.loc[dat.species == species]  # selected data

        # Base histogram for all data
        base = (
            alt.Chart(dat)
            .mark_bar(color="#C2C2C4", binSpacing=0)
            .encode(
                alt.X("bill_length_mm:Q", bin=alt.Bin(step=1), title="bill_length_mm"),
                alt.Y("count()", title="count"),
            )
        )

        # Overlay histogram for selected species
        overlay = (
            alt.Chart(sel)
            .mark_bar(color="#447099", binSpacing=0)
            .encode(alt.X("bill_length_mm:Q", bin=alt.Bin(step=1)), alt.Y("count()"))
        )

        return base + overlay


app = App(app_ui, server)

Shiny + Altair

Components gallery docs are in a PR deployment preview (this is all very new docs!)

https://pr-326--pyshiny.netlify.app/components/outputs/plot-altair/

These docs will eventually be on the official components page

https://shiny.posit.co/py/components/