marimo icon indicating copy to clipboard operation
marimo copied to clipboard

Altair reactive plots do not work if vegafusion is enabled

Open mokhin opened this issue 7 months ago • 4 comments

Describe the bug

In this example from the documentation,

import marimo as mo
import altair as alt
import vega_datasets

# Load some data
cars = vega_datasets.data.cars()

# Create an Altair chart
chart = alt.Chart(cars).mark_point().encode(
    x='Horsepower', # Encoding along the x-axis
    y='Miles_per_Gallon', # Encoding along the y-axis
    color='Origin', # Category encoding by color
)

# Make it reactive ⚡
chart = mo.ui.altair_chart(chart)

if you add the line alt.data_transformers.enable("vegafusion") the plot stops being reactive and it is not possible to select data on it.

Environment

{
  "marimo": "0.13.0",
  "OS": "Darwin",
  "OS Version": "24.1.0",
  "Processor": "arm",
  "Python Version": "3.12.9",
  "Binaries": {
    "Browser": "135.0.7049.96",
    "Node": "v23.11.0"
  },
  "Dependencies": {
    "click": "8.1.8",
    "docutils": "0.21.2",
    "itsdangerous": "2.2.0",
    "jedi": "0.19.2",
    "markdown": "3.8",
    "narwhals": "1.35.0",
    "packaging": "24.2",
    "psutil": "7.0.0",
    "pygments": "2.19.1",
    "pymdown-extensions": "10.14.3",
    "pyyaml": "6.0.2",
    "ruff": "0.11.5",
    "starlette": "0.46.2",
    "tomlkit": "0.13.2",
    "typing-extensions": "4.13.2",
    "uvicorn": "0.34.1",
    "websockets": "15.0.1"
  },
  "Optional Dependencies": {},
  "Experimental Flags": {
    "lsp": true,
    "table_charts": false,
    "inline_ai_tooltip": false
  }
}

Code to reproduce

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "altair==5.5.0",
#     "marimo==0.13.0",
#     "pyarrow==19.0.1",
#     "vega-datasets==0.9.0",
#     "vegafusion==2.0.2",
#     "vl-convert-python==1.7.0",
# ]
# ///

import marimo

__generated_with = "0.13.0"
app = marimo.App(width="medium")


@app.cell
def _():
    import marimo as mo
    import altair as alt
    import vega_datasets

    alt.data_transformers.enable("vegafusion")
    return alt, mo, vega_datasets


@app.cell
def _(alt, mo, vega_datasets):
    # Load some data
    cars = vega_datasets.data.cars()

    # Create an Altair chart
    chart = alt.Chart(cars).mark_point().encode(
        x='Horsepower', # Encoding along the x-axis
        y='Miles_per_Gallon', # Encoding along the y-axis
        color='Origin', # Category encoding by color
    )

    # Make it reactive ⚡
    chart = mo.ui.altair_chart(chart)
    return cars, chart


@app.cell
def _(chart, mo):
    mo.vstack([chart, chart.value.head()])
    return


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

mokhin avatar Apr 19 '25 22:04 mokhin

This is not yet supported. vegafusion compiles down to a vega spec, but our selection logic works on a vega-lite spec. We can look into adding support for this still

mscolnick avatar Apr 21 '25 14:04 mscolnick

This is especially useful when working with larger datasets. Altair recommends using vegafusion for any dataset with more than 5000 rows, as performance starts to drop otherwise; who are we kidding though, most datasets nowadays are larger than that :p

I'm currently using alt.data_transformers.disable_max_rows() as a temporary workaround, but it does start to chug when the datasets get too large, so it would be really nice if vegafusion was supported.

Another option that I currently use is to precompute the values in numpy or polars and then feed them to altair for much larger datasets.

AH-Merii avatar May 22 '25 12:05 AH-Merii

I often hit this limitation too! I'd be keen to work on a pair if I can pair with one the contributors and get some guidance!! @mscolnick

lucharo avatar Sep 10 '25 13:09 lucharo

You can still make reactive Altair plots with vegafusion by passing an explicit selection param. This is hand rolling the selection yourself (something that mo.ui.altair_chart otherwise does under the hood for non-vegafusion)

# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "altair>=5.5.0",
#     "marimo",
#     "vega-datasets>=0.9.0",
#     "vegafusion[embed]>=2.0.1",
#     "vl-convert-python>=1.7.0",
# ]
# ///

import marimo

__generated_with = "0.17.2"
app = marimo.App(width="medium")


@app.cell
def _():
    import altair as alt
    import anywidget
    from vega_datasets import data

    import marimo as mo

    alt.data_transformers.enable("vegafusion")

    # Load some data
    cars = data.cars()

    # Create an Altair chart
    _chart = (
        alt.Chart(cars)
        .mark_point()
        .encode(
            x="Horsepower",
            y="Miles_per_Gallon",
            color="Origin",
        )
        .properties(height=300)
        .add_params(alt.selection_interval(name="interval"))
    )

    # Make it reactive ⚡
    chart = mo.ui.anywidget(alt.JupyterChart(_chart))
    return (chart,)


@app.cell
def _(chart):
    chart
    return


@app.cell
def _(chart):
    list(chart.selections.interval.value.items())
    return

mscolnick avatar Oct 28 '25 20:10 mscolnick