Programming

Filter Plotly scatter3d by pandas MultiIndex dropdowns

Create one dropdown per pandas MultiIndex level to filter Plotly scatter3d traces. Step-by-step code for updatemenus, JS visibility masks, transforms, and standalone HTML export without Dash.

1 answer 1 view

How can I create one dropdown menu per level of a pandas.MultiIndex DataFrame to filter traces in a Plotly scatter3d figure and preserve interactivity in a standalone HTML file (no Dash or IPython.widgets)?

Details:

  • I have a DataFrame with a MultiIndex on columns (dates × categories), and each (date, category) column pair is plotted as a separate scatter3d trace.
  • Currently I loop over each (date, category) tuple and create a single dropdown with a button for every tuple; this is not viable when a level contains many values.
  • I want one dropdown for each MultiIndex level (for example, one dropdown for dates and one for categories) so users can pick a value from each level and the plot updates accordingly.
  • The interactivity must work in a standalone HTML export (so Dash and IPython.widgets are not options).

Example code:

python
import numpy as np
import pandas as pd
import plotly.graph_objects as go

## data looks something like this
categories = pd.Index(["A", "B"])
dates = pd.date_range(start="2026-01-01", end="2026-01-10", freq="D")
columns = pd.MultiIndex.from_product([dates, categories])
index = pd.Index(range(6), name="Observations")
x, y, z = (pd.DataFrame(
 np.random.standard_normal(size=(len(index), len(columns))),
 columns=columns,
 index=index
) for _ in range(3))

## scatter plot
fig = go.Figure()
for date, cat in columns:
 fig.add_scatter3d(
 x=x[date, cat],
 y=y[date, cat],
 z=z[date, cat],
 mode="markers",
 )
fig.update_layout(updatemenus=[
 dict(
 x=0.85, y=1.00, xanchor="center", showactive=True,
 buttons=[
 dict(label=f"({date:%Y-%m-%d}, {cat})", method="restyle", visible=True, args=[{
 "visible": np.arange(len(columns)) == c,
 }])
 for c, (date, cat) in enumerate(columns)
 ],
 ),
])
fig.show()

Question: What is the best way to implement one dropdown per MultiIndex level (e.g., date and category) that efficiently updates trace visibility or otherwise filters the plotted traces in Plotly, supports large numbers of values per level, and works in a standalone HTML file without using Dash or IPython.widgets? Are there recommended approaches using Plotly’s updatemenus, custom JavaScript callbacks, grouping/structuring traces, or another technique to achieve this?

Use one of two practical patterns: either (A) keep one trace per (date,category), precompute a mapping from each MultiIndex level value to the trace indices in Python and embed a small Plotly.js script in the exported HTML that reads one