quarto-dev / quarto-cli

Open-source scientific and technical publishing system built on Pandoc.
https://quarto.org
Other
3.8k stars 310 forks source link

plotly plots don't work with shiny ui inputs for Quarto dashboards #8608

Closed lostmygithubaccount closed 7 months ago

lostmygithubaccount commented 7 months ago

Bug description

I'm trying to have a UI input and use the value(s) of the input to adjust plotly charts. The inputs documentation for Quarto dashboards is all empty Python code blocks currently: https://quarto.org/docs/dashboards/inputs.html

I was able to find some examples:

While this seems to work fine with other plotting libraries, plotly charts don't seem to work and I'm not sure where to go from here.

Steps to reproduce

first example -- no Ibis:

---
title: "Penguin Bills"
format: dashboard
server: shiny
---

```{python}
import seaborn as sns
import plotly.express as px

import plotly.io as pio
#pio.renderers.default = "notebook"
pio.renderers.default = "iframe"
#pio.renderers.default = "html"

penguins = sns.load_dataset("penguins")

{.sidebar}

from shiny import render, ui
ui.input_select("x", "Variable:",
                choices=["bill_length_mm", "bill_depth_mm"])
ui.input_select("dist", "Distribution:", choices=["hist", "kde"])
ui.input_checkbox("rug", "Show rug marks", value = False)

Column

@render.plot
def displot():
    #sns.displot(
    #    data=penguins, hue="species", multiple="stack",
    #    x=input.x(), rug=input.rug(), kind=input.dist())

    px.histogram(penguins, x=input.x(), y="species")

the seaborn plot works fine, the plotly plot is just blank on the dashboard

<img width="670" alt="image" src="https://github.com/quarto-dev/quarto-cli/assets/54814569/b33aea22-032b-49ae-868c-3198d2db4041">

second example with Ibis (same issue):

````qmd
--- 
title: "repro"
format:
  dashboard:
    theme: darkly
    scrolling: true
server: shiny
--- 

```{python}
#| echo: false
#| output: false
import ibis

import plotly.express as px

from shiny import ui, render
from itables import show

# set plotly express to dark mode
px.defaults.template = "plotly_dark"

ibis.options.interactive = True

t = ibis.examples.penguins.fetch()

{height=25%}

show(t.limit(10).to_pandas())
show(
  t.group_by(ibis._.species)
  .agg(
    count=ibis._.count()
  )
  .order_by(ibis._["count"].desc())
  .to_pandas()
)

{.toolbar}

#| panel: input
ui.input_text("species", "Species:", value="Gentoo")

plot

@render.plot
def bar_chart():

    species = input.species()
    #species = "Adelie"

    px.bar(
        t.filter(t.species == species)
        .group_by(ibis._.island)
        .agg(
            count=ibis._.count()
        ),
        x="island",
        y="count",
    )

### Expected behavior

plotly plots "just work" as in other dashboarding frameworks

### Actual behavior

blank space where the plot should be 

### Your environment

- IDE: VSCode + Quarto extension
- OS: MacOS

### Quarto check output

Quarto 1.4.549 [✓] Checking versions of quarto binary dependencies... Pandoc version 3.1.11: OK Dart Sass version 1.69.5: OK Deno version 1.37.2: OK [✓] Checking versions of quarto dependencies......OK [✓] Checking Quarto installation......OK Version: 1.4.549 Path: /Applications/quarto/bin

[✓] Checking tools....................OK TinyTeX: (not installed) Chromium: (not installed)

[✓] Checking LaTeX....................OK Tex: (not detected)

[✓] Checking basic markdown render....OK

[✓] Checking Python 3 installation....OK Version: 3.11.3 Path: /Users/cody/repos/pypi-analytics/venv/bin/python3 Jupyter: 5.7.1 Kernels: python3

[✓] Checking Jupyter engine render....OK

[✓] Checking R installation...........(None)

  Unable to locate an installed version of R.
  Install R from https://cloud.r-project.org/
mcanouil commented 7 months ago
lostmygithubaccount commented 7 months ago

for added context this is desired for:

nothing in the linked discussion worked

cpcloud commented 7 months ago

I am able to reproduce this:

---
title: "Penguin Bills"
format: dashboard
server: shiny
---

```{python}
import seaborn as sns
import plotly.express as px

penguins = sns.load_dataset("penguins")

{.sidebar}

from shiny import render, ui

ui.input_select("x", "Variable:",
                choices=["bill_length_mm", "bill_depth_mm"])
ui.input_select("dist", "Distribution:", choices=["hist", "kde"])
ui.input_checkbox("rug", "Show rug marks", value = False)

Column

@render.plot
def displot1():
    return sns.displot(
        data=penguins, hue="species", multiple="stack",
        x=input.x(), rug=input.rug(), kind=input.dist())
@render.plot
def displot2():
    return px.histogram(penguins, x=input.x(), color="species", marginal="rug",
                 histfunc="count", barmode="overlay")
@render.plot
def displot3():
    # note the lack of `return`
    sns.displot(
        data=penguins, hue="species", multiple="stack",
        x=input.x(), rug=input.rug(), kind=input.dist())
@render.plot
def displot4():
    # note the lack of `return`
    px.histogram(penguins, x=input.x(), color="species", marginal="rug",
                 histfunc="count", barmode="overlay")

Stick that in a file called `bills.qmd` and run

$ quarto render bills.qmd



to get 

![image](https://github.com/quarto-dev/quarto-cli/assets/417981/62a022dc-a50d-47b8-8709-d184955148ab)

I am running quarto version 1.4.549 on Linux.

Notice that when `return`ing the object, I get an error message. When I don't return anything, I see a blank plot.

Is plotly expected to work with quarto + shiny dashboards?
cwickham commented 7 months ago

Plotly plots aren't static images (like Seaborn), so you need to use @render_widget (not @render.plot) from shinywidgets:

---
title: "Penguin Bills"
format: dashboard
server: shiny
---

```{python}
import seaborn as sns
import plotly.express as px
from shinywidgets import output_widget, render_widget  

penguins = sns.load_dataset("penguins")

{.sidebar}

from shiny import render, ui

ui.input_select("x", "Variable:",
                choices=["bill_length_mm", "bill_depth_mm"])
ui.input_select("dist", "Distribution:", choices=["hist", "kde"])
ui.input_checkbox("rug", "Show rug marks", value = False)

Column

@render_widget
def displot2():
    return px.histogram(penguins, x=input.x(), color="species", marginal="rug",
                 histfunc="count", barmode="overlay")


<img width="1436" alt="Screenshot 2024-02-08 at 10 15 16 AM" src="https://github.com/quarto-dev/quarto-cli/assets/25964/03d9a8f1-77af-46d9-a7a2-d0ae411f33a0">

This is indeed a shiny question rather than a Quarto one, but I respect that our docs don't give you many pointers on how to get started with Shiny for Python and Quarto. I'll be looking at those docs soon, so I'll keep this issue in mind.
lostmygithubaccount commented 7 months ago

awesome, thanks @cwickham! that works