Figure data lingering between plotting calls?

Hello,

First I’d like to say I’ve been using plotly for some time now and have really enjoyed it. You all are doing a phenomenal job! This question represents just a small frustration.

I’ve been developing a codebase that has a large number of plotting functions using plotly. Our plots are used in a lot of different ways - as one off plots from CLI drivers, in the dashboard, in custom scripts, nested within other plots, etc. As a result they all include fig, row, and col as arguments and return a figure as output. This allows them to be used for a variety of different purposes. For example:

from plotly.subplots import make_subplots
import plotly.graph_objects as go

def plot(x, fig=make_subplots(1,1), row=1, col=1):
    fig.add_trace(go.Scatter(x=x, y=x), row=row, col=col)
    return fig

This has proved extremely useful but fails in one area. If you just quickly want to plot a few things, it’s often easiest just ot do something like this:

plot([1]).show()
plot([2]).show() # <--- why does this contain the data of the previous plot?

When doing so, however, the data from the first plot is included in the second even though it’s a completely new figure! i.e. the second plot shows:

Obviously, this is not the desired behavior.

I saw this post Remove all traces, and have just been doing that for a while, but it just adds an unnecessary amount of code. e.g.

fig = plot([1])
fig.show()
fig.data = []
fig.layout = {}

fig = plot([2])
fig.show()
fig.data = []
fig.layout = {}

I suppose my question is twofold:

  1. Why does the data in the first plot call linger outside of its scope?
  2. It would seem that the data is stored in a global scope, so is there anyway to access that and delete it? If so then at the very least I could create a clear() function that I could call after each show call.

Thanks so much!

Isaac

This is actually due to the way you’re initializing fig in the defaults of your function… your code is actually roughly equivalent to the following, with a global default:

from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig_default=make_subplots(1,1)
def plot(x, fig=fig_default, row=1, col=1):
    fig.add_trace(go.Scatter(x=x, y=x), row=row, col=col)
    return fig

This will work better for you:

def plot(x, fig=None, row=1, col=1):
    fig = fig or make_subplots(1,1)
    fig.add_trace(go.Scatter(x=x, y=x), row=row, col=col)
    return fig
1 Like

Hmm interesting, is there a particular reason why that’s the case? I actually had tried the second solution but prefer to keep it in the arguments just for cleanliness sake. Should make_subplots not be called each time? Creating a brand new instance of a go.Figure?

This is basically just the way Python default arguments work :man_shrugging: … Check out some other examples here: Common Gotchas — The Hitchhiker's Guide to Python

1 Like

Wow I had no idea! Thank you for your time Nicolas, I really appreciate the help!

It’s definitely a common and annoying “gotcha” that caught me by surprise the first time too! Happy plotting :slight_smile: