Programmatically creating plots in a loop

I’ve been vexed with the Models must be owned by only a single document" error.

The use case: create a loop in python to generate 10 plots, each of which is saved to a separate .html file. I just need each plot in its own html file, and so I expect to output 10 basic html files.

This prior discussion addresses the issue:

Models must be owned by only a single document" error appears when splitting files #8265

And this line from @Bryan is helpful: “Every session must get its own unique set of Boken objects, so the app code always must generate new instances (this is to ensure that different users are completely isolated).”

We had been working with standalone html, it appears we must make this a server app? (Even though we don’t need callbacks or any other server functions)

My questions:

  1. Is there an example in any of the tutorials that address this need for loop-created plots saved to separate html files?
  2. If we need to force a new server session, how exactly do we do that?
  3. Do we need to also force a new Document each iteration of the loop, and if so, how?

We’ve had a great time working with Bokeh and appreciate the support. Thanks tons!

If I understand your situation correctly (it’s a bit vague without actual code to focus a discussion), you can call bokeh.io.reset_output() before/after each loop iteration.

That said, I don’t see why it should be necessary, either. This code, e.g works fine without any call to reset_output:

In [7]: from bokeh.plotting import figure

In [8]: from bokeh.io import show

In [9]: for i in range(10):
   ...:     p = figure(title="%d"%i, x_range=(0,1), y_range=(0,1))
   ...:     show(p)

So what would really help the discussion is a minimal reproducing example so that we see what you are actually doing, instead of having to speculate.

Thanks @Bryan, the issue seems to be in generating multiple .html files from the common excel data file. (We can generate the plots in the notebook, and in fact can put all of the plots into a single html file, but not output 1 plot per separate html file, or in this situation generate a total of 10 html files.)

Representative code:

from bokeh.plotting import figure, show, save, output_file, output_notebook, reset_output
from bokeh.io.state import curstate
from bokeh.models.widgets import Panel, Tabs
from bokeh.resources import Resources
from bokeh.models import ColumnDataSource, Label, Span, Title, Range1d, FixedTicker, LinearAxis, Whisker, Legend
from bokeh.models import LinearColorMapper, BasicTicker, PrintfTickFormatter, ColorBar, HoverTool, TableColumn, DataTable
from bokeh.io import curdoc
from bokeh.embed import file_html   
from bokeh.resources import CDN, INLINE
from bokeh.transform import linear_cmap
from bokeh.palettes import brewer
from bokeh.layouts import column, row, gridplot

def filename(name):

    return name + '.html'  #dynamically creating name for each html file

#Create the plots and save to html in a loop
#Needs to 1)read values from a single excel data file, 2) apply plot definition & formatting 3) output each plot to separate html file*  

unique_vals = df['val'].unique()    

for val in unique_vals:
    bokeh.io.reset_output()
    df = pd.read_excel('data.xlsx')
    val_df = df[df['val']==val]
    plot = create_plot(val, val_df, ColumnDataSource(val_df))
    output_file(plot, CDN, filename(val))              --> IGNORED: call to reset_output() ignored when running scripts with the 'bokeh' command.
    html = file_html(plot, CDN, filename(val))     -->RuntimeError: **Models must be owned by only a single document**, Span(id='1234', ...) is already in a doc.
    #bokeh.io.reset_output()

After extensive trial and error in standalone Bokeh, we wonder if this cannot be accomplished without Bokeh server? Our other hypothesis is that there is something we need to do to reset or separately reference a different ColumnDataSource for each plot

Do you have any code examples that conclude with generating and saving multiple html files, as opposed to show()?

Thanks again!

@SJR I don’t understand this comment at all:

what bokeh command are you running, exactly? By far the most common is bokeh serve but that in that case you are not generating standalone output. Standalone output is typically generated by regular Python scripts run normally as python script.py

Apologies for the confusion.

we’ve used both

bokeh.io.reset_output()

as well as

reset_output()

in monitoring the execution of the .py script, it appeared that the reset was being ignored/didn’t do anything

@SJR If I pare your code down to this:

from bokeh.plotting import figure, output_file
from bokeh.embed import file_html
from bokeh.resources import CDN
from bokeh.io import reset_output

def filename(name):
    return name + '.html'

for val in [str(x) for x in range(10)]:
    reset_output()
    plot = figure()
    output_file(filename(val), 'cdn')
    html = file_html(plot, CDN, filename(val))

It runs fine. The only two things that come to mind are:

  • you are running an ancient version of Bokeh (you did not state what version you are using)
  • you are reusing the plot (or any Bokeh object) that you return in create_plot (which you have not shown).

The only way I can help you with this is if you provide a complete, minimal reproducer, i.e. a script, that I can take and run, unchanged, without any modifications whatsoever, that reproduces what you are seeing.

Otherwise, all I can do is re-iterate what both runnable examples above demonstrate: it is 100% possible to create multiple standalone outputs in a loop, without a bokeh server, and people do this all the time.

@Bryan, thanks for your help! The problem was that we had a plot element getting defined outside of the loop:

hline = Span(location=0, dimension=‘width’, line_color=‘dimgray’, line_width=1.4) # defines a horizontal line at y = 0

That was the object being shared by multiple documents, and throwing the error. Once we identified that and moved it under the loop, your solution worked just fine. Thanks for the assistance!

1 Like