Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Marimo Support? #1018

Closed
jsnelgro opened this issue Feb 16, 2024 · 5 comments
Closed

Better Marimo Support? #1018

jsnelgro opened this issue Feb 16, 2024 · 5 comments
Milestone

Comments

@jsnelgro
Copy link

Let-plot is my favorite plotting library and for the past few weeks I've been happily experimenting with Marimo, a reactive Jupyter alternative. Unfortunately though, Lets-plot doesn't play very nicely with it. The big issues are:

  1. Charts keep getting appended to the cell output instead of modifying the chart in place (see screenshot)
  2. The output is awkwardly sized and not responsive, so you need to scroll within the iframe
  3. it's not clear how the imperative call LetsPlot.setup_html() works in a reactive environment like this (including it doesn't seem to make any difference)
  4. (less important) Marimo doesn't allow star imports, so chart declarations can be a bit harder to read

Here's an example screenshot: Screenshot 2024-02-15 at 16 44 28

And here's a minimal example Marimo notebook to reproduce the issue:

import marimo

__generated_with = "0.1.86"
app = marimo.App()


@app.cell
def __():
    import numpy as np
    import marimo as mo
    import lets_plot as lp

    lp.LetsPlot.setup_html()
    np.random.seed(42)
    return lp, mo, np


@app.cell
def __(mo):
    stddev = mo.ui.slider(1, 10, step=0.001, label="std dev")
    stddev
    return stddev,


@app.cell
def __(np, stddev):
    data = dict(
        cond=np.repeat(['A', 'B'], 200),
        rating=np.concatenate(
            (np.random.normal(0, 1, 200), np.random.normal(1, stddev.value, 200))
        )
    )
    return data,


@app.cell
def __(data, lp, mo):
    # necessary, otherwise charts keep getting appended to the frame.
    # However, this makes the cell output redraw everything on changes, causing flashes
    mo.output.clear()

    chart = lp.ggplot(data, lp.aes(x='rating', fill='cond')) + lp.ggsize(700, 300) + \
        lp.geom_density(color='dark_green', alpha=.7) + lp.scale_fill_brewer(type='seq') + \
        lp.theme(panel_grid_major_x='blank')
    chart
    return chart,


@app.cell
def __():
    return


if __name__ == "__main__":
    app.run()

I'm not sure where the easiest integration point would be (with Lets plot or Marimo?), but I figured I'd at least get it on your radar. Honestly, a quick and simple QoL improvement would just be make to_html return the html as a string if no filepath was provided.
I'm happy to take a stab at patching the to_html, to_svg, etc methods if it seems like a reasonable feature to add.

@alshan
Copy link
Collaborator

alshan commented Feb 16, 2024

Hi, by contract, IPython notebook calls our magic _repr_html_() method which indeed returns a string.

What happens with this string in the notebook is beyond our scope.

Does Marimo provide some kind of API alternative to IPython?

@jsnelgro
Copy link
Author

interesting, yes it looks like they provide some APIs that might do the trick (for example, rendering arbitrary html). Although, based on their interactive Altair, Plotly, and matplotlib helper methods, I'm wondering if interactive charts would be more of an integration on their end 🤔

@jsnelgro
Copy link
Author

So I think if LetsPlot.setup_html(isolated_frame=False) didn't have a hard dependency on IPython's display_html method here, it could possibly solve the issue. Right now, setting isolated_frame=False just throws an error unfortunately, due to the display_html method being None.
Calling mo.Html(_repr_html()) almost does the trick but the method returns a whole html page with javascript to attach to a newly created DOM node. And it seems isolated_frame=False is needed to change this behavior?

@alshan
Copy link
Collaborator

alshan commented Feb 22, 2024

isolated_frame=True is used when notebook wraps any output in iframe. In this case the output should be self sufficient and includes both the library script and plot data and JS that calls the library.

When isolated_frame=False the library is added to the output of LetsPlot.setup_html() and plot cells only contain JS calling the library.

If display_html is None then there is no IPython in the env and I can't say how anything is shown at all :)

Calling mo.Html(_repr_html()) almost does the trick but the method returns a whole html page with javascript to attach to a newly created DOM node. And it seems isolated_frame=False is needed to change this behavior?

If this is not what you need then absolutely try isolated_frame=False.
But in absence of display_html the LetsPlot.setup_html() will not be able to inject the library JS into the notebook :(
As a workaround you can try to call _configure_connected_script and then pass the result to mo.Html().

@alshan alshan modified the milestones: New, 2024Q2 Mar 29, 2024
@alshan alshan modified the milestones: 2024Q2, 2024Q3 Jun 30, 2024
@alshan
Copy link
Collaborator

alshan commented Aug 22, 2024

@jsnelgro , @signup2k, @VivaldoMendes
Hi guys, good new: my PR-2084 was accepted and just made it to the latest marimo 0.8.1 release.

Now Marimo knows how to display lets-plot charts. (also, LetsPlot.setup_html() is no longer required).
p.show() however won't work.

Try this simple demo:

import numpy as np
from lets_plot import ggplot, geom_point, aes

x = np.random.rand(100)
y = 2 * x + 1 + np.random.normal(0, 0.1, 100)

data = dict(x = x, y = y)

p = ggplot(data) + geom_point(aes('x', 'y'))
p

@alshan alshan closed this as completed in 340c42a Aug 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants