5

I made a figure with four subplots of a histogram based on random normal, gamma, exponential, and uniform distributions respectively. I made it using matplotlib and Jupyter notebook. It is an interactive figure via ipywidgets lib. In particular, there are four slide bars that control the sample size on each histogram and update them accordingly. However, when updating the histograms, it annoyingly flickers. Is there any way to avoid this? Thx.

Now the code to be run on a jupyter notebook:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib notebook
from ipywidgets import *

n = 1000
x1 = np.random.normal(-2.5, 1, n)
x2 = np.random.gamma(2, 1.5, n)
x3 = np.random.exponential(2, n)+7
x4 = np.random.uniform(14,20, n)
x = [x1, x2, x3, x4]

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(10,7))
axs = [ax1,ax2,ax3,ax4]

titles = ['x1\nNormal', 'x2\nGamma', 'x3\nExponential', 'x4\nUniform']
subplots_axes = [[-7,2,0,250], [0,4.5,0,250], [7,25,0,250], [14,20,0,250]]

bins = [np.arange(-6, 6, 0.5),
np.arange(0, 10, 0.5),
np.arange(7, 17, 0.5),
np.arange(14, 24, 0.5)]

fig.subplots_adjust(hspace=0.5)

def plt_dist(s, sample):
    axs[s].hist(x[s][:sample], bins=bins[s], linewidth=0, color='#1F77B4')
    axs[s].axis(subplots_axes[s])
    axs[s].set_title('{}'.format(titles[s]))
    axs[s].set_ylabel('Frequency')
    axs[s].set_xlabel('Value')
    axs[s].annotate('n = {}'.format(sample), xycoords='axes fraction', xy = [0.8,0.9])
    display(fig)

for s in range(0,4):
    sld_bar = interact(plt_dist, s = fixed(s), sample = widgets.IntSlider(min=100,max=1000+45,step=1,value=100))
Antonio Serrano
  • 882
  • 2
  • 14
  • 27

2 Answers2

4

In case anyone else comes across this issue having a print statement in your interact function can also cause flickering.

fig, ax = plt.subplots()
@widgets.interact
def run(
    a = 1.2,
):
    ax.clear()
    print(1) # Comment this line to stop flickering
    ax.plot([1,2,3])
    display(fig)
sam
  • 1,005
  • 1
  • 11
  • 24
3

It's not really clear what display(fig) would do or what it's needed for.

For me, removing that line and instead clearing the axes (axs[s].clear()) at the beginning of the plt_hist function works just fine and the "flickering" is not there anymore.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib notebook
from ipywidgets import *

n = 1000
x1 = np.random.normal(-2.5, 1, n)
x2 = np.random.gamma(2, 1.5, n)
x3 = np.random.exponential(2, n)+7
x4 = np.random.uniform(14,20, n)
x = [x1, x2, x3, x4]

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(10,7))
axs = [ax1,ax2,ax3,ax4]

titles = ['x1\nNormal', 'x2\nGamma', 'x3\nExponential', 'x4\nUniform']
subplots_axes = [[-7,2,0,250], [0,4.5,0,250], [7,25,0,250], [14,20,0,250]]

bins = [np.arange(-6, 6, 0.5),
np.arange(0, 10, 0.5),
np.arange(7, 17, 0.5),
np.arange(14, 24, 0.5)]

fig.subplots_adjust(hspace=0.5)

def plt_dist(s, sample):
    axs[s].clear() # <-- clear axes
    axs[s].hist(x[s][:sample], bins=bins[s], linewidth=0, color='#1F77B4')
    axs[s].axis(subplots_axes[s])
    axs[s].set_title('{}'.format(titles[s]))
    axs[s].set_ylabel('Frequency')
    axs[s].set_xlabel('Value')
    axs[s].annotate('n = {}'.format(sample), xycoords='axes fraction', xy = [0.8,0.9])
    #display(fig)  <--- delete this

for s in range(0,4):
    sld_bar = interact(plt_dist, s = fixed(s), 
              sample = widgets.IntSlider(min=100,max=1000+45,step=1,value=100))
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • I have to use `display(fig)` because I want to update a figure with four subplots using a for loop, like in [here](http://stackoverflow.com/questions/21360361/how-to-dynamically-update-a-plot-in-a-loop-in-ipython-notebook-within-one-cell). Otherwise the figure does not show. I tried to use `plt.cla()` before but it does not work. – Antonio Serrano Apr 08 '17 at 15:21
  • So, can you run the script from my answer or not? Depending on whether that works or not, we can find a solution for the for-loops. – ImportanceOfBeingErnest Apr 08 '17 at 15:27
  • Yes, I can. No problem with that solution. – Antonio Serrano Apr 08 '17 at 15:30
  • Ok, so I'm pretty sure you will always get some flicker when using `display`. Therefore it would be desireable not to use it. At the moment I do not see how not using it should prevent you from updating four subplots instead of only one. I think you should create a [mcve] with the four subplots; you then have two options: (A) Update this question with the new example, (B) accept this answer and ask a new question about what goes wrong when updating the four subplots. – ImportanceOfBeingErnest Apr 08 '17 at 15:37
  • I am going to update it. Give a me a few minutes. Many thx :) – Antonio Serrano Apr 08 '17 at 15:39
  • Done. Have a look, please. – Antonio Serrano Apr 08 '17 at 16:12
  • Its actually the same solution as for one plot. See updated answer. – ImportanceOfBeingErnest Apr 08 '17 at 17:23
  • Is this code supposed to work still? I'm working on the same problem and this doesn't seem to update the plot at all. – AturSams Mar 30 '21 at 09:46