7

As referred in this question, I am trying to update a plot dynamically in an iPython notebook (in one cell). The difference is that I don't want to plot new lines, but that my x_data and y_data are growing at each iteration of some loop.

What I'd like to do is:

import numpy as np
import time
plt.axis([0, 10, 0, 100]) # supoose I know what the limits are going to be
plt.ion()
plt.show()
x = []
y = []
for i in range(10):
     x = np.append(x, i)
     y = np.append(y, i**2)
     # update the plot so that it shows y as a function of x
     time.sleep(0.5) 

but I want the plot to have a legend, and if I do

from IPython import display
import time
import numpy as np
plt.axis([0, 10, 0, 100]) # supoose I know what the limits are going to be
plt.ion()
plt.show()
x = []
y = []
for i in range(10):
    x = np.append(x, i)
    y = np.append(y, i**2)
    plt.plot(x, y, label="test")
    display.clear_output(wait=True)
    display.display(plt.gcf())
    time.sleep(0.3)
plt.legend()

I end up with a legend which contains 10 items. If I put the plt.legend() inside the loop, the legend grows at each iteration... Any solution?

Community
  • 1
  • 1
P. Camilleri
  • 12,664
  • 7
  • 41
  • 76

1 Answers1

7

Currently, you are creating a new Axes object for every time you plt.plot in the loop.

So, if you clear the current axis (plt.gca().cla()) before you use plt.plot, and put the legend inside the loop, it works without the legend growing each time:

import numpy as np
import time
from IPython import display

x = []
y = []
for i in range(10):
    x = np.append(x, i)
    y = np.append(y, i**2)
    plt.gca().cla() 
    plt.plot(x,y,label='test')
    plt.legend()
    display.clear_output(wait=True)
    display.display(plt.gcf()) 
    time.sleep(0.5) 

EDIT: As @tcaswell pointed out in comments, using the %matplotlib notebook magic command gives you a live figure which can update and redraw.

Jordan Lewis
  • 16,900
  • 4
  • 29
  • 46
tmdavison
  • 64,360
  • 12
  • 187
  • 165
  • Better, but if I do that, the legend is not visible until the loop has ended – P. Camilleri Aug 17 '15 at 13:26
  • really? that works for me. Where did you put the legend? It needs to be before the two `display` lines – tmdavison Aug 17 '15 at 13:27
  • I had put the legend() inside the loop, but at the last line of the loop. Now I've put it after plot() and it works fine – P. Camilleri Aug 17 '15 at 13:29
  • great, glad that helped. – tmdavison Aug 17 '15 at 13:29
  • 2
    One more thing: when I run my snippet (adding your line and changing legend() location), I end up with two images. Any idea why? – P. Camilleri Aug 17 '15 at 13:32
  • 1
    try switching the order of the `display.clear_output` and `display.display` lines. That is discussed in more detail in the answers to the question you linked to in the Q – tmdavison Aug 17 '15 at 13:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/87172/discussion-between-m-massias-and-tom). – P. Camilleri Aug 17 '15 at 13:49
  • 3
    You can do better if you use `%matplotlib notebook` which gives you a live figure that you can update and re-draw (as well as pan/zoom and have mouse+keyboard events) – tacaswell Aug 18 '15 at 00:39
  • @tcaswell: very nice, I didn't know about `%matplotlib notebook`. Updating the answer... – tmdavison Aug 18 '15 at 09:30
  • 1
    Re: @P. Camilleri's comment: I added `%matplotlib inline` at the end of my loop (or at the end of the cell, either way worked.) as a hack to remove that second plot from appearing (when the cell ends). Switching the order of `display.clear_output` and `display.display` did not work for me. I have not yet found the correct way to use `%matplotlib notebook` for my dynamic plot, so this works as a temp solution in the meantime. I use `%matplotlib inline` everywhere else in my notebook, so resetting it probably makes sense for me anyway :-) – SherylHohman Apr 13 '17 at 21:05