9

I am trying to plot figures in real time using a for loop. I have the following simple code:

import matplotlib.pyplot as plt

plt.ion()
plt.figure()
for i in range(100):
    plt.plot([i], [i], 'o')
    plt.draw()
    plt.pause(0.0001)

This code does not show the figure until it has finished computing, which I don't want. I want it to draw the figure after every loop. If I replace plt.draw() with plt.show, multiple figures are output in real time, but I want them all to appear in the same figure. Any ideas?

EDIT:
I downloaded PyCharm with Anaconda and everything works fine. I guess it's a problem with Spyder since I tried a few different versions of it without success. If anyone has any clue what is causing this problem in Spyder, let me know!

Alex
  • 93
  • 1
  • 1
  • 4
  • try a putting `plt.show()` after `plt.figure()`. – Ben Feb 05 '16 at 04:46
  • Doing this, I get the output ``, and the code evaluates as before. – Alex Feb 06 '16 at 01:30
  • How are you executing the script? Calling `python stript.py` from the bash terminal? Or calling `%run` from inside an Ipython terminal? – Ben Feb 06 '16 at 01:45
  • I'm using Spyder and am just clicking the run button to run the script. – Alex Feb 06 '16 at 01:57
  • That's not typical behavior. What you have should work. Try running it through Ipython (https://pythonhosted.org/spyder/ipythonconsole.html). If that doesn't work It might be a problem with the graphics backend (http://stackoverflow.com/questions/3285193/how-to-switch-backends-in-matplotlib-python). – Ben Feb 06 '16 at 02:02
  • I was already running it in an IPython console in Spyder. I tried changing matplotlib backend using matplotlib.use('TkAgg') and a few others, but every time I get the error `matplotlib.use() has no effect because the backend has already been chosen; matplotlib.use() must be called *before* pylab, matplotlib.pyplot, or matplotlib.backends is imported for the first time.`. I also tried changing the graphics backend in IPython console preferences, without any success. I also reinstalled Spyder, still nothing... – Alex Feb 06 '16 at 02:55
  • I've seen comments saying to change the backend in Preferences > Console > External modules > Matplotlib GUI backend, but I don't see Matplotlib under External modules. Am I missing something here? – Alex Feb 06 '16 at 03:07
  • I don't use spyder. So, unfortunately, I can't provide much help on that. I have run into similar problems using other packages and I now run IPython through the command line to avoid some of these issues. You might have luck customizing the matplotlibrc file (http://matplotlib.org/1.4.0/users/customizing.html) so that the backend is set before being loaded into spyder. – Ben Feb 06 '16 at 19:17

4 Answers4

7

Adapted for your case from : Python realtime plotting

import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()
ax = fig.add_subplot(111)

# some X and Y data
x = [0]
y = [0]

li, = ax.plot(x, y,'o')

# draw and show it
fig.canvas.draw()
plt.show(block=False)

# loop to update the data
for i in range(100):
    try:
        x.append(i)
        y.append(i)

        # set the new data
        li.set_xdata(x)
        li.set_ydata(y)

        ax.relim() 
        ax.autoscale_view(True,True,True) 

        fig.canvas.draw()

        time.sleep(0.01)
    except KeyboardInterrupt:
        plt.close('all')
        break
Community
  • 1
  • 1
CoMartel
  • 3,521
  • 4
  • 25
  • 48
  • Thanks for the reply. I tried running your code, but I get an error saying `show() got an unexpected argument`. I read that the block argument was experimental, but I'm not sure why it doesn't work on my Spyder platform. – Alex Feb 06 '16 at 01:29
  • Does it works without the `block`keyword? Apparently it is not working if you already are in an interactive mode. – CoMartel Feb 06 '16 at 09:26
  • It does work without the block statement, but doing this outputs many different figures. Is there a way to put them all together? – Alex Feb 06 '16 at 23:23
  • This looks like a spyder-specific issue, as it works perfectly fine on my computer. I'm not familiar enough with Spyder to help you – CoMartel Feb 07 '16 at 11:55
  • time.sleep() does not work for me, but using plt.pause() fixes it – d.popov Dec 25 '20 at 17:04
  • 1
    Thank you! This is example that finally got it working for me. Inital try was not a success, but I did not think lines ax.relim() & ax.autoscale_view(True,True,True) were important. After adding them, it worked. – Matiss Zuravlevs Aug 06 '21 at 10:39
4

This solution example has worked for me on multiple machines. Try adjusting plt.pause(...)

import matplotlib.pyplot as plt
import numpy as np

F = lambda x: np.sin(2*x)

plt.ion()    
x = np.linspace(0, 1, 200)
plt.plot(x, F(x))


for i in range(100):
    if 'ax' in globals(): ax.remove()
    newx = np.random.choice(x, size = 10)
    ax = plt.scatter(newx, F(newx))
    plt.pause(0.05)

plt.ioff()
plt.show()
Jon
  • 2,373
  • 1
  • 26
  • 34
3

Hey I was having the same problem, I checked other questions and my issue was solved when I plugged a pause into my solution. Here's some example code that worked for me.

import matplotlib.pyplot as plt
import numpy as np
plt.ion()
x = np.arange(0, 4*np.pi, 0.1)
y = [np.sin(i) for i in x]
plt.plot(x, y, 'g-', linewidth=1.5, markersize=4)
plt.pause(0.0001)         
plt.plot(x, [i**2 for i in y], 'g-', linewidth=1.5, markersize=4)
plt.pause(0.0001)
plt.plot(x, [i**2*i+0.25 for i in y], 'r-', linewidth=1.5, markersize=4) 
plt.pause(0.0001)

The solution was posted here: Matplotlib ion() and subprocesses

Community
  • 1
  • 1
1

The problem - and the solution - is highly dependent on the plot.draw() function within the Python environment and back end, and may even vary in different product releases. It manifests itself in different ways depending on the environment. The problem shows up in many places on stackoverflow with some solutions working for some people and not for others.

The gold standard on my Windows laptop is running the Python from the command line - no IDE, just plain vanilla Python3. draw() as shown in the example always works fine there.

If I try it in Jupyter notebook on the same machine, no amount of draw(), plot.pause(), plot.show(), or any other suggestion works. I tried %matplotlib with notebook, widget and ipympl. Nothing gets drawn until complete end of cell code execution.

Some other sources on stackoverflow suggested using figure.canvas.flush_events(). I had some success with that and investigated further.

The best solution turned out to be to run the draw() at the figure.canvas level instead of the axes or plot level.

You can get the figure by creating your plot with command:

fig, graph, = plt.subplots()

or, if you've already created the plot, as in the code at the top of the ticket, put the following outside the loop:

fig = plt.gcf() #get current figure

Inside the loop, instead of plt.draw(), use

fig.canvas.draw()

It's proven reliable in my Jupyter Notebook environment even when running multiple axes/plots across multiple figures. I can drop in sleep() statements and everything appears when expected.

Your mileage may vary.

ylabrj
  • 11
  • 3