Reputation: 604
I am creating a Plotly box plot that has 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
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")
Upvotes: 4