0

I'm trying to create a Jupyter notebook that plots some data using matplotlib. I've been told that when using pcolormesh, as I am, instead of creating a plot from scratch, one can instead use the set_array function on the plot object and this greatly speeds up the plotting. The person who doing this was using matplotlib for batch processing images, so was using set_ray then calling save_fig, and that worked OK. I would like to use matplotlib to plot data in a jupyter notebook.

Here is an example Jupyter Notebook which contains an example of plotting 2 blobs using pcolormesh, and a Jupyter Widget slider to change the position of one of the blobs:

Link to notebook: mpl_interactive_plot.ipynb

In the example, I set up a class for plotting. In the next cell I call create_plot(), which creates and shows the plot. In the last cell, I create a Jupiter Widgets slider, which has a callback that should update the plot created in the previous cell when the slider changes position.

I create the plot using a call to pcolormesh:

self.plot_arr[0] = self.axarr[0].pcolormesh(self.xarr,self.yarr,self.blob1_arr,cmap='viridis')

I recalculate the value of the data array in the callback function passed to the slider, and call the following code to update the plot:

self.plot_arr[0].set_array(self.blob1_arr)

to update the array. This seems to have no effect on the plot. It seems likely I need to call some sort of update/refresh function to trigger a redraw of the plot using the new array, but all the obvious candidate just seem to cause the plot to disappear. Is what I'm trying to do sensible, and what is the right way to do it?

Here is the code extracted from the notebook:

import matplotlib
import matplotlib.pyplot
import numpy
import ipywidgets

get_ipython().magic('matplotlib notebook')
def gaussian_blob(xarr, yarr, blob_params):
    blob_dist = (xarr - blob_params[0]) **2 + (yarr - blob_params[1])**2
    gaussian_func = numpy.exp(-blob_dist / blob_params[2])
    return gaussian_func

class MyPlot(object):
    def __init__(self):
        self.blob1_params = [2.0,5.0,5.0]
        self.blob2_params = [8.0,12.0,5.0]
        self.plot_arr = None
        self.fig_name = 'blob_plots'
        self.f1 = None
        self.axarr = None
    def calc_data(self):
        self.xarr, self.yarr = numpy.meshgrid(range(10),range(15))
        self.blob1_arr = gaussian_blob(self.xarr, self.yarr, self.blob1_params)
        self.blob2_arr = gaussian_blob(self.xarr, self.yarr, self.blob2_params)
    def create_plot(self):
        self.calc_data()
        #print(self.blob1_arr[2,:])
        #matplotlib.pyplot.figure(self.fig_name).clear()
        self.f1, self.axarr = matplotlib.pyplot.subplots(num=self.fig_name,
                                                         ncols=2,
                                                         nrows=1,
                                                         figsize=matplotlib.pyplot.figaspect(0.5))
        self.plot_arr = [None] * 2
        self.plot_arr[0] = self.axarr[0].pcolormesh(self.xarr,self.yarr,self.blob1_arr,cmap='viridis')
        self.plot_arr[1] = self.axarr[1].pcolormesh(self.xarr,self.yarr,self.blob2_arr,cmap='viridis')
        matplotlib.pyplot.show()        
    def update_plot(self):
        self.calc_data()
        self.plot_arr[0].set_array(self.blob1_arr)
        self.plot_arr[1].set_array(self.blob2_arr)
        # probably need some sort of update/refresh call, but what?
    def on_x_centre_change(self, xc_event):
        self.blob1_params[0] = xc_event['new']
        self.update_plot()

plot_obj1 = MyPlot()
plot_obj1.create_plot()
pslider = ipywidgets.IntSlider(value=2, 
                            min=0, 
                            max=10,
                            description='left x centre',
                              continuous_update=False)
pslider.observe(plot_obj1.on_x_centre_change, names='value')
pslider
user824600
  • 25
  • 4
  • First, please provide a [mcve]. It also depends on what you do; update the figure and save it, or update the figure and show it, or both? In case you want to show it, would you like to replace the figure or show one after the other? – ImportanceOfBeingErnest Dec 05 '17 at 17:02
  • The google drive link in my post is a link to the example Jupyter Notebook which contains the minimal, complete verifiable example. Is there a better way for me to share it that you could suggest? I would the figure which is displayed in the cell with the call to create_plot() to be updated when I move the slider. Thanks! – user824600 Dec 06 '17 at 09:05
  • I would expect a [mcve] of the issue to be maximum 20 to 30 lines of code. This can be easily posted *inside* the question. – ImportanceOfBeingErnest Dec 06 '17 at 10:31
  • since the problem only occurs in a jupyter notebook, i thought that a jupyter notebook would be the minimal example that would be complete and verifiable. Even a blank notebook is 30 lines. The file I have posted is the smallest I know how to make to example such that it demonstrates my problem. I'll post the python code extracted from notebook, though I doesn't run outside a notebook. – user824600 Dec 07 '17 at 11:33
  • Maybe you haven't thought about this, but there are a couple of reasons to include the code in the question: Links can break in the future; for this to be useful in half a year for others, the problem needs be clear from the question alone. The content of links are not searchable, people with the same problem would not be able to find it. Last, I do not want to download some file from an unknown source to my computer. – ImportanceOfBeingErnest Dec 07 '17 at 11:40
  • You create a new plot for each slider move. That is surely inefficient. But the code is running fine for me, see [image](https://i.stack.imgur.com/8eVbb.gif). – ImportanceOfBeingErnest Dec 07 '17 at 11:46
  • Thanks so much for taking a look at the code! IMy apologies, the code I posted was wrong, with the on_x_centre_change() function calling create_plot() rather than update_plot(). I get the correct result but it is slow. I have corrected the code so that update_plot() is called. This function updates the array of the existing plot rather than creating the plot from scratch, which should more efficient. Unfortunately, the plot is not updated after the call, which is the problem I'm having. Any advice on how to force the plot to update after calling set_array() would be greatly appreciated. – user824600 Dec 08 '17 at 09:32
  • See [this answer](https://stackoverflow.com/a/31490420/4124317). To update the drawing, use `fig.canvas.draw_idle()` – ImportanceOfBeingErnest Dec 08 '17 at 09:54

0 Answers0