reference
Python Data Grid API Reference: Jupyter and Dash
The public API of the three Python packages, exactly as they ship: the notebook widget, the Dash component, and the pandas layer they share.
The three packages
There are three packages on PyPI. You install one of the two wrappers for where you work, and both pull in the third automatically.
lattice-grid-jupyteris the notebook widget.lattice-grid-dashis the Dash component.lattice-grid-pandasis the shared layer that turns a DataFrame into what the grid reads. The two wrappers both depend on it, so a column maps to the same grid type and an edit casts back the same way in either.
All three need Python 3.9 or newer and pandas 1.5 or newer.
lattice-grid-jupyter
A widget that renders a DataFrame as an editable grid in a notebook cell. Construct it with a
DataFrame, evaluate it to draw it, and drive it from the methods below. Read the live frame at any
point from the df property.
Constructor
LatticeGridWidget(df, licence='', height=360, offline=False, grid_version='1.99.1', **kwargs)
df: the DataFrame to show. Its dtypes set the grid's column types.licence: your licence key, for deploying the grid for others. Left empty, it runs free on your own machine. Note the British spelling.height: the grid's height in pixels.offline: whether to serve the grid's front-end code from inside the package rather than fetch it, so it renders with no network access.
Attributes and methods
df: a read-only property returning the live DataFrame, which reflects every committed edit.apply_edit(key, col_id, value) -> None: set one cell by its row key and column id.append_rows(rows: DataFrame | Iterable[dict]) -> list[str]: add rows and get back the new row keys.delete_rows(keys: str | Iterable[str]) -> list[str]: remove rows by key and get back the keys removed.set_data(df) -> None: replace the whole frame and repaint.
Row keys are strings, starting at "0".
import pandas as pd
from lattice_grid_jupyter import LatticeGridWidget
df = pd.DataFrame({"name": ["Ada", "Grace"], "score": [91, 88]})
grid = LatticeGridWidget(df)
grid.df # read-only property -> the live DataFrame
grid.apply_edit(key="0", col_id="score", value=100) # -> None
grid.append_rows([{"name": "Alan", "score": 77}]) # -> ['2']
grid.delete_rows(["2"]) # -> list[str] of removed keys
grid.set_data(pd.DataFrame({"name": ["X"], "score": [1]})) # -> None
Selection, options, large data, charts and the router
Beyond the basics, the widget takes these keyword arguments and exposes these attributes.
options,columns: grid options and per-column options as plain dicts.snake_casekeys are accepted. Assigningw.options = {...}updates the live grid.histograms,profile,pivot: header histograms, the Statistics panel, and a pivot on a field.selected: the selected rows as a DataFrame with the original index and dtypes. Empty when nothing is selected.view: the rows passing the grid's filters and search, in the grid's sort order. Above the large-data threshold it is a lazy handle withlen(),head(n),slice(a, b),to_pandas()andpositions().state: filter, sort, column, group and pivot state as a plain dict you can read and set.large_threshold,windowed: the row count at which the grid scrolls in windows from Python (100,000 by default), and a way to force it on or off. The attributewindowedreports the mode.LatticeChart(grid_or_df, type="bar", **spec): a chart of the grid's data that follows its filters, search and selection.update(type=..., **spec)changes it live andset_data(df)gives a DataFrame-backed chart new data.LatticeRouter(sources=, routes=): DataFrames as sources of the grid's data router.router.update(source, df)sends only the difference, androuter_statson the widget counts the rows a route re-emitted. Pass the router toLatticeGridWidget(router, route="name").
Parquet paths and Polars frames open read-only, and need pip install "lattice-grid-jupyter[large]".
See the tutorial for a worked example of each.
lattice-grid-dash
A Dash component that renders a DataFrame as a grid your callbacks can read. Edits and the current selection surface as ordinary props, so a cell change drives a callback the same way a dropdown or a slider does. The grid's front-end code ships inside the package, so it works offline with no build step and no request to a CDN at render time.
Component
LatticeGrid(id=None, data=None, columns=None, options=None, licence=None,
cellChanged=None, selectedKeys=None, licenceState=None,
style=None, className=None, **kwargs)
id: the component id your callbacks address.data: the grid's data, built from a DataFrame withdataframe_to_data.columns: an explicit column configuration, when you want to set it rather than take the one inferred from the data.options: grid options, for example{"edit": True}to allow editing.licence: your licence key, for deploying the grid for others. Left unset, it runs free on localhost.selectedKeys: the keys of the currently selected rows.style,className: standard styling props.cellChanged: an output prop carrying the last edit. Read it from a callback; it is read-only from Python.licenceState: an output prop reporting the licence state. It is read-only from Python.
Helpers
dataframe_to_data(df, keys=None) -> dict # {columns, rowKey: '__row_id__', columnar}
apply_cell_edit(df, edit, keys=None) -> pd.DataFrame
positional_keys(n)
ROW_KEY # '__row_id__'
dataframe_to_data(df, keys=None) -> dict: turn a DataFrame into the component'sdata. The dict carries the columns, the row key field'__row_id__', and the values held column by column.apply_cell_edit(df, edit, keys=None) -> pd.DataFrame: apply onecellChangededit to a DataFrame, cast to the column's dtype, and return the updated frame.positional_keys(n): build row keys by position.ROW_KEY: the row key field name,'__row_id__'.
The cellChanged payload
When a user commits an edit, cellChanged carries a dict of the change:
{"key": "0", "colId": "score", "value": 100, "old": 91, "ts": ...}
key is the row key, colId the column, value the new value,
old the previous value and ts a timestamp. Pass the whole dict to
apply_cell_edit to keep the server-side frame in step.
lattice-grid-pandas
The shared layer that turns a DataFrame into what the grid reads, and that resolves the grid's front-end code. Both wrappers use it, so you rarely call it directly, but its public API is here for the cases where you build the data yourself.
# serialization
ROW_KEY # '__row_id__'
lattice_type(dtype)
column_values(series)
build_columns(df)
build_columnar(df, keys)
data_fields(df)
cast_to_dtype(dtype, value)
# grid delivery
GRID_VERSION
CDN_BASE
cdn_urls
load_vendored
resolve_source
__version__
ROW_KEY: the row key field name,'__row_id__'.lattice_type(dtype): map a pandas dtype to the grid's column type.column_values(series): the values of a column, ready for the grid.build_columns(df): the grid's column definitions from a DataFrame.build_columnar(df, keys): the frame's values held column by column.data_fields(df): the data fields the grid reads.cast_to_dtype(dtype, value): cast one value to a column's dtype, the round-trip both wrappers rely on.
The grid-delivery names, GRID_VERSION, CDN_BASE, cdn_urls,
load_vendored, resolve_source and __version__, resolve which
copy of the grid's front-end code to serve and where from.
Datetimes are UTC end to end
A naive datetime64 column is treated as already being UTC: it serialises to the
grid as an ISO string with an explicit Z, never a bare timestamp the browser
would read in its own local zone. A timezone-aware column is converted to UTC first, so the
same instant renders identically wherever the page is opened. An edit comes back the same
way, converted to UTC and then restored to the column's own zone, or left naive for a naive
column. A missing value (NaT) round-trips as null in both
directions.
New here? Start with Hello grid in Python, or read the Python data grid overview.