Skip to content

Comments

feat: Implement raw=True mode for high-performance figure construction#5515

Open
AlonSpivack wants to merge 2 commits intoplotly:mainfrom
AlonSpivack:feat/as-dict-mode
Open

feat: Implement raw=True mode for high-performance figure construction#5515
AlonSpivack wants to merge 2 commits intoplotly:mainfrom
AlonSpivack:feat/as-dict-mode

Conversation

@AlonSpivack
Copy link

@AlonSpivack AlonSpivack commented Feb 18, 2026

  • I have read through the contributing notes and understand the structure of the package.
  • I have added tests or modified existing tests.
  • For a new feature, I have added documentation examples (benchmarks included).
  • I have added a CHANGELOG entry if changing anything substantial.
  • For a new feature or a change in behavior, I have updated the relevant docstrings in the code.

Description

This PR implements the raw=True parameter for go.Figure, trace constructors, and validation-heavy methods like add_traces, update_layout, and shape additions. It also introduces a global configuration flag plotly.config.raw to enable this mode by default.

When raw=True is active (either via parameter or global config):

  • Trace Constructors: Bypass all validation and object creation, returning a plain dictionary with the correct type.
  • Figure Constructor: Bypasses BaseFigure initialization logic, storing data and layout as plain lists/dicts.
  • Methods: add_traces, update_layout, add_annotation, add_shape, add_vline, etc., have fast paths to directly modify the internal list/dict structures without overhead.

Usage

1. Global Configuration (Recommended)

Ideally suited for production environments where performance is critical:

import plotly
import plotly.graph_objects as go

# Enable globally for maximum performance
plotly.config.raw = True

# All subsequent figures created are roughly 26x-100x faster
fig = go.Figure()
fig.add_scatter(x=[1, 2, 3], y=[4, 5, 6])

2. Per-Object Configuration

You can also enable it for specific objects without changing global application state:

# Pass raw=True to constructors
trace = go.Scatter(x=[1, 2], y=[3, 4], raw=True)  # Returns a plain dict
fig = go.Figure(data=[trace], raw=True)           # Skips figure validation

Benchmarks

Run the following script to reproduce:

Benchmark Script (bench_raw.py)
import time
import timeit
import plotly.graph_objects as go
import plotly.colors as colors
import plotly.io as pio

# Disable any global rendering
pio.renderers.default = None

N = 200  # Number of iterations for micro-benchmarks
TRACE_COUNT = 200  # Number of traces for pipeline test

def bench_trace_creation():
    t0 = timeit.timeit(lambda: go.Scatter(x=[1, 2], y=[3, 4], mode='lines'), number=N)
    t1 = timeit.timeit(lambda: go.Scatter(x=[1, 2], y=[3, 4], mode='lines', raw=True), number=N)
    print(f"Prop: go.Scatter creation: {t0/N*1000:.4f}ms vs {t1/N*1000:.4f}ms (Speedup: {t0/t1:.1f}x)")

def bench_figure_creation():
    t0 = timeit.timeit(lambda: go.Figure(), number=N)
    t1 = timeit.timeit(lambda: go.Figure(raw=True), number=N)
    print(f"Prop: go.Figure creation: {t0/N*1000:.4f}ms vs {t1/N*1000:.4f}ms (Speedup: {t0/t1:.1f}x)")

def bench_add_traces():
    fig_std = go.Figure()
    fig_raw = go.Figure(raw=True)
    traces_std = [go.Scatter(x=[i], y=[i]) for i in range(TRACE_COUNT)]
    traces_raw = [go.Scatter(x=[i], y=[i], raw=True) for i in range(TRACE_COUNT)]
    
    def add_std():
        fig = go.Figure()
        fig.add_traces(traces_std)
        
    def add_raw():
        fig = go.Figure(raw=True)
        fig.add_traces(traces_raw)

    t0 = timeit.timeit(add_std, number=100)
    t1 = timeit.timeit(add_raw, number=100)
    print(f"Prop: Add {TRACE_COUNT} traces: {t0/100*1000:.4f}ms vs {t1/100*1000:.4f}ms (Speedup: {t0/t1:.1f}x)")

def bench_add_vline():
    fig_std = go.Figure()
    fig_raw = go.Figure(raw=True)
    
    def vline_std():
        fig_std.add_vline(x=1)
        
    def vline_raw():
        fig_raw.add_vline(x=1)

    t0 = timeit.timeit(vline_std, number=N)
    t1 = timeit.timeit(vline_raw, number=N)
    print(f"Prop: add_vline: {t0/N*1000:.4f}ms vs {t1/N*1000:.4f}ms (Speedup: {t0/t1:.1f}x)")

if __name__ == "__main__":
    print("Running benchmarks...")
    bench_trace_creation()
    bench_figure_creation()
    bench_add_traces()
    bench_add_vline()
  • Trace Creation: ~30x faster
  • Figure Creation: ~42x faster
  • Add Traces (200 traces): ~365x faster
  • Add VLine: ~11,500x faster (O(1) vs O(N²))

This enables extremely high-performance generation of Plotly figures for large datasets or high-throughput applications.

Limitations (Trade-offs for Performance)

In raw mode, the following features are unavailable because they rely on the validation engine:

  1. Magic Underscore Expansion: Keys like xaxis_title="X" are not expanded to nested dicts. Use explicit nesting: xaxis={"title": {"text": "X"}}.
  2. Subplot Row/Col Arguments: add_trace(..., row=1, col=1) is ignored. Manually specify axes (e.g., xaxis="x2", yaxis="y2").
  3. String-to-Object Auto-Conversion: Setting layout.title = "X" works (JS support), but setting layout.xaxis.title = "X" fails (JS expects object). Use layout.xaxis.title = {"text": "X"}.

Issues

Closes #5514

Add _as_dict=True parameter to trace constructors and go.Figure for
~4-6x faster figure construction when validation is not needed.

Fast paths added for:
- BasePlotlyType.__new__ / BaseTraceType.__new__
- BaseFigure.__init__, .data, .layout, .add_traces, .update_layout
- BaseFigure._add_annotation_like, ._process_multiple_axis_spanning_shapes
@AlonSpivack
Copy link
Author

Implementation for #5514.
Discussion about the API naming (_as_dict vs as_dict)
is happening on the issue.

@AlonSpivack AlonSpivack changed the title feat: _as_dict=True mode for high-performance figure construction feat: Implement raw=True mode for high-performance figure construction Feb 19, 2026
@AlonSpivack
Copy link
Author

Update: Renamed PR title and description to finalize raw=True API (formerly _as_dict).
Added comprehensive usage examples (Global & Per-Object), verified benchmarks (inline script), and limitations.

- Implements  in BaseFigure and trace constructors
- Adds global config
- Optimizes , ,
- Adds comprehensive tests in
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

Successfully merging this pull request may close these issues.

[FEATURE]: Add _as_dict=True to graph_objects and go.Figure for 26x-11,300x faster figure construction

2 participants