Charles Plager
Charles Plager

Reputation: 604

Plotly box plots with overlay very slow - is there a faster/better method?

I am creating a Plotly box plot that has text overlays.
Plotly box plot with text overlays

The issue is that while what I have works, it is slow. When I do a 30 by 16 boxes, it takes more than 30 minutes to create the figure.

import random
import plotly.graph_objects as go    

def addSquare(fig, x, y, size, which):
    x0 = x - size
    x1 = x + size
    y0 = y - size
    y1 = y + size
    if which > 1:
        fill = 'LightSkyBlue'
        lineCol = 'Blue'
    elif which < -1:
        fill = 'pink'
        lineCol = 'red'
    else:
        fill = 'Lightgrey'
        lineCol = 'grey'
    fig.add_shape(
        type="rect",
        x0=x0, y0=y0,
        x1=x1, y1=y1,
        fillcolor=fill,
        line_color=lineCol,
        name=f'Shape_{x}_{y}'
    )
    # Adding a trace with a fill, setting opacity to 0
    fig.add_trace(
        go.Scatter(
            x=[x0,x0,x1,x1,x0], 
            y=[y0,y1,y1,y0,y0], 
            fill="toself",
            fillcolor=fill,
            mode='lines',
            name='',
            text=f'shape {x} {y}<br>size {size:.2f}<br>cost {which:.1f}',
            opacity=0
        )
    )


# Draw shape (you won't be able to add a hover text for it)
fig = go.Figure(layout=go.Layout(
        title=go.layout.Title(text="A Figure Specified By A Graph Object")
    ))
for xVal in range(1,12+1):
    for yVal in range(1, 15+1):
        size = random.uniform(0.1, 0.4)   # max 0.4
        which = random.uniform(-1.5, 1.5) # > 1 expensive, < 1 cheap
        addSquare(fig, xVal, yVal, size, which)

fig.show()

Any suggestions on how to speed this up while keeping the same functionality?

Upvotes: 2

Views: 1850

Answers (1)

Derek O
Derek O

Reputation: 19610

Your code has some redundancy in it: you probably don't need to use both add_shape and a filled go.Scatter trace. You can achieve the same visualization using go.Scatter alone, by specifying the same line color and fill color you passed to add_shape.

For example, in the following code, I created 30x16 boxes. Using the time module, the following code (including the fig.add_shape block) takes 30.38765597343445 seconds to render, while commenting that block out and using go.Scatter only speeds up the rendering significantly to 1.4587550163269043 seconds

import random
import plotly.graph_objects as go    
import time

start_time = time.time()

def addSquare(fig, x, y, size, which):
    x0 = x - size
    x1 = x + size
    y0 = y - size
    y1 = y + size
    if which > 1:
        fill = 'LightSkyBlue'
        lineCol = 'Blue'
    elif which < -1:
        fill = 'pink'
        lineCol = 'red'
    else:
        fill = 'Lightgrey'
        lineCol = 'grey'
    # fig.add_shape(
    #     type="rect",
    #     x0=x0, y0=y0,
    #     x1=x1, y1=y1,
    #     fillcolor=fill,
    #     line_color=lineCol,
    #     name=f'Shape_{x}_{y}'
    # )
    # Adding a trace with a fill, setting opacity to 0

    fig.add_trace(
        go.Scatter(
            x=[x0,x0,x1,x1,x0], 
            y=[y0,y1,y1,y0,y0], 
            fill="toself",
            fillcolor=fill,
            mode='lines',
            name='',
            text=f'shape {x} {y}<br>size {size:.2f}<br>cost {which:.1f}',
            line_color=lineCol,
            opacity=1,
            showlegend=False
        )
    )

# Draw shape (you won't be able to add a hover text for it)
fig = go.Figure(layout=go.Layout(
        title=go.layout.Title(text="A Figure Specified By A Graph Object")
    ))
for xVal in range(1,30+1):
    for yVal in range(1, 15+1):
        size = random.uniform(0.1, 0.4)   # max 0.4
        which = random.uniform(-1.5, 1.5) # > 1 expensive, < 1 cheap
        addSquare(fig, xVal, yVal, size, which)

fig.show()

# print(f"{(time.time() - start_time)} seconds to render")

enter image description here

Upvotes: 4

Related Questions