Create and update charts in notebooks

GenomeSpy charts display through a notebook widget in JupyterLab, Jupyter Notebook, VS Code notebooks, and Marimo. The browser needs network access when it first loads the pinned GenomeSpy JavaScript bundle.

Install for notebooks

Install the package with Arrow support when using it in a notebook. This also installs PyArrow for dataframe transport:

pip install "genome-spy-python[arrow]"

Display a chart

Build a chart normally:

initial_rows = [
    {"sample": "A", "value": 2.1, "group": "control"},
    {"sample": "B", "value": 3.4, "group": "control"},
    {"sample": "C", "value": 4.2, "group": "treated"},
]

chart = (
    gs.Chart(
        data={"name": "measurements"},
        datasets={"measurements": initial_rows},
    )
    .mark_point(filled=True, size=120)
    .encode(
        x=gs.X("sample:N").title("Sample"),
        y=gs.Y("value:Q").title("Measurement"),
        color=gs.Color("group:N"),
    )
    .properties(height=180, title="Notebook measurements")
)

Leave the chart as the final expression in a notebook cell to display it:

# Leave the chart as the final expression in a notebook cell.
chart

This form is enough when later Python cells do not need to update the displayed data.

Display a dataframe

Chart accepts pandas and Polars dataframes, plus PyArrow Table and RecordBatch objects:

import genome_spy as gs
import pandas as pd

frame = pd.DataFrame({"sample": ["A", "B"], "value": [2.1, 3.4]})

chart = gs.Chart(frame).mark_point().encode(x="sample:N", y="value:Q")
chart

Notebook rendering transfers supported tables with Arrow automatically. This changes only how data reaches the widget: to_dict() and to_json() still produce ordinary JSON-compatible specifications. A pandas index is not a chart field, so use frame.reset_index() first when the index contains values the chart needs.

Keep a widget for updates

Call widget() when the displayed chart must receive new data:

view = chart.widget()

# Display this object once in the notebook.
view

Display view once and keep the same object alive. Updating it preserves the mounted GenomeSpy instance, including its current zoom and selections.

The chart uses a named dataset:

data={"name": "measurements"},
datasets={"measurements": initial_rows},

data.name tells the chart which dataset to read. The root datasets mapping provides its initial rows and gives later updates a stable target. The GenomeSpy documentation describes this indirection in named data.

Equal unnamed tables are shared automatically. Updating a shared dataset changes all charts that read it. Use different explicit names when charts need independent updates, even if they start with the same rows. See Reuse a table across charts.

Replace the named dataset

Use set_dataset() to replace its records:

updated_rows = [
    {"sample": "A", "value": 2.8, "group": "control"},
    {"sample": "B", "value": 3.1, "group": "control"},
    {"sample": "C", "value": 4.7, "group": "treated"},
]

view.set_dataset("measurements", updated_rows, format="records")

New rows should retain the fields and value types expected by the chart. For a widget with exactly one named dataset, view.set_data(updated_rows, format="records") is a shorter equivalent.

Dataframes and PyArrow tables can be passed directly:

view.set_dataset("measurements", updated_frame)

Arrow is the default transport for these updates. Keep column names and value types compatible with the chart fields. Most users do not need to call to_arrow_ipc() directly.

In a reactive notebook, create and display the widget in a stable cell. Let dependent cells prepare new rows and call set_dataset() on that same object. Create a new chart only when its fields, marks, encodings, or composition need to change.

Use Marimo

Marimo displays the same widget through its anywidget support. Create and wrap the widget once in a stable cell:

import marimo as mo

view = chart.widget()
chart_widget = mo.ui.anywidget(view)
chart_widget

Dependent cells can prepare a new dataframe and update the existing widget:

view.set_dataset("measurements", updated_frame)

Keeping the widget in its original cell avoids rebuilding the chart whenever a Marimo control changes.

See the genome_spy.api.JupyterChart reference for multiple datasets, transport options, and method signatures.

If a chart does not appear

Make sure the package is installed in the Python environment your notebook uses. After installing or upgrading it, restart the notebook’s Python session and rerun the cells. The chart also needs internet access to load its display code. As an alternative, use save() to save an HTML file and open it in a browser.