10

I have a long running Python loop (used for machine learning), which periodically prints output and displays figures (using matplotlib). When run in Jupyter Notebook, all the text (stdout) is displayed in real-time, but the figures are all queued and not displayed until the entire loop is done.

I'd like to see the figures in real-time, on each iteration of the loop. During cell execution, not when the entire cell execution is done.

For example, if my code is:

for i in range(10):
  print(i)
  show_figure(FIG_i)
  do_a_10_second_calculation()

I currently see:

0
1
2
...
9
FIG_0
FIG_1
...
FIG_9

What I'd like is:

0
FIG_0
1
FIG_1
2
FIG_2
...

Most importantly, I'd like to see the figures as they are calculated, as opposed to not seeing any figures on the screen until the entire loop is done.

SRobertJames
  • 8,210
  • 14
  • 60
  • 107

2 Answers2

5

The display function from IPython.display can be used to immediately flush a figure to cell output. Assuming that FIG_i in your code is an actual Matplotlib figure object, you can just replace show_figure(FIG_i) with display(FIG_i) and the figures will output in real time.

Here's a complete example of display in action:

from matplotlib import pyplot as plt
import numpy as np
from IPython.display import display
from time import sleep

for eps in range(0, 11, 5):
    data = np.random.randint(eps, eps+10, size=(2,10))

    fig = plt.figure()
    ax = fig.gca()

    ax.plot(*data)

    print('eps %f' % eps)
    display(fig)
    plt.close()    # .close prevents the normal figure display at end of cell execution

    sleep(2)
    print('slept 2 sec')

Here's a screenshot of the output:

enter image description here

tel
  • 13,005
  • 2
  • 44
  • 62
3

I suppose the problem lies in the part of the code you do not show here. Because it should work as expected. Making it runnable,

%matplotlib inline

import matplotlib.pyplot as plt

def do_a_1_second_calculation():
    plt.pause(1)

def show_figure(i):
    plt.figure(i)
    plt.plot([1,i,3])
    plt.show()

for i in range(10):
    print(i)
    show_figure(i)
    do_a_1_second_calculation()

results in the desired outcome

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Aha! I didn't realize you need to explicitly do `plt.show()`. I had omitted that, and all the figures still displayed at the end, when the cell completed execution. Adding that in the loop indeed solves the problem. – SRobertJames Nov 23 '18 at 17:38
  • Does not work as claimed anymore. If you remove the `do_a_1_second_calculation()` line the output is out of order again. Also, the use of `show` raises a warning for me, since `inline` is not an interactive matplotlib backend. – LudvigH Dec 18 '19 at 12:13
  • @LudvigH This still works fine. You could, instead of claiming something here, ask a new question on why it might not work in your case, giving all details necessary for reproduction. – ImportanceOfBeingErnest Dec 18 '19 at 12:41
  • Didn't want to create duplicate questions, but now that you ask me, I'll try to reproduce and ask a new question about it. Thank you for swift feedback btw! – LudvigH Dec 18 '19 at 12:48
  • My follow up question is here: https://stackoverflow.com/questions/59392951/specify-where-in-output-matplotlib-figures-are-rendered-in-jupyter-using-figure – LudvigH Dec 18 '19 at 13:20
  • I mistook the `plt.show` for `Figure.show`. Your solution indeed works with `plt.show`. I'd be happy to change downvote to upvote, but the vote is locked until any edit to the answer. – LudvigH Dec 18 '19 at 13:22
  • 1
    @LudvigH The other answer using `display` seems more suited for your problem. (It's actually pretty much the same as [this earlier answer](https://stackoverflow.com/a/49366556/4124317)) – ImportanceOfBeingErnest Dec 18 '19 at 13:26
  • There will be an issue if you s/10/25/: matplotlib will complain about too many figures open (default limit 20). And calling `plt.close(fig)` after `plt.show(fig)` results in output with the inline backend, the jupyter default. No idea why. Only `IPython.display.display(fig)` followed by `plt.close(fig)` does the trick for me. If you have just a few figures, should not be a problem. Or just ignore the warning, unless you print 500 figures. :) – kkm inactive - support strike Feb 07 '23 at 08:14