Blank figure in while loop - python

I'm having trouble plotting graphs in a while loop (I get a blank figure that doesn't show any graph), what am I missing here? I provided an MWE that I run on Windows 10, Python 3. But I don't think the configuration is the problem:
import matplotlib.pyplot as plt
import time
def refresh(x,y1,y2,y3):
plt.close('all')
plt.figure()
plt.subplot(3,1,1)
plt.plot(x, y1)
plt.subplot(3,1,2)
plt.plot(x, y2)
plt.subplot(3,1,3)
plt.plot(x, y3)
plt.show
return
plt.ion
A = [1,2,3,4]
B = [15,16,8,2]
C = [8,6,4,7]
D = [5,4,3,1]
while True:
time.sleep(5)
refresh(A,B,C,D)
Another problem is it doesn't create a figure window, but displays the data after execution in the console. I would like it to create a figure window displaying the curves "refreshed" every 5 seconds.

If that is indeed a copy of your code, you need to actually call
plt.show()
Without the parentheses indicating a call of the function,
plt.show
just returns it being a function without actually being executed.

Related

Dynamically update plot Matplotlib Python (for unsteady heat diffusion)

I am new to python and trying to do what have been doing in MATLAB for so long. My current challenge is to dynamically update a plot without drawing a new figure in a for or while loop. I am aware there are similar questions and answers but most of them are too complicated and I believe it should be easier.
I got the example from here
https://pythonspot.com/matplotlib-update-plot/
But I can't see the figure, no error, no nothing. I added two lines just to see if I can see the static plot and I can.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10*np.pi, 100)
y = np.sin(x)
# This is just a test just to see if I can see the plot window
plt.plot(x, y)
plt.show()
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'b-')
for phase in np.linspace(0, 10*np.pi, 100):
line1.set_ydata(np.sin(0.5 * x + phase))
fig.canvas.draw()
Any idea why I can't see the dynamic plot?
Thank you
Erdem
try to add plt.pause(0.0001) inside the loop after plt.show(block=False), and a final plt.show() outside the loop. This should work fine with plt.ion(); ref to some older answers Plot one figure at a time without closing old figure (matplotlib)

Overplot trends in matplotlib: every loop gives additional trend

With the code below I get three different plots, and I would like to know how to combine them so that I have three lines on one plot. I thought there is something simple as overplot instead of plot, but somehow I could't find it.
Somehow I also need to adjust the x to the "longest" dataset.
import matplotlib.pyplot as plt
big_array = [[4,5,4,5],[6,4,1],[1,2,3,4]]
for i in big_array:
x = range(len(i))
y = i
plt.plot(x, y)
plt.show()
When you call plt.show() this displays all the current figures that have been drawn and blocks the rest of the code until the figure window has been closed.
As you are in a loop of 3 iterations you code will display and block the figure at each call to show. Then when you close the window your loop will continue, creating another figure when you call plt.plot() and then displays it again when you call show.
To fix you should only call plt.show() at the end of your script:
big_array = [[4,5,4,5],[6,4,1],[1,2,3,4]]
for i in big_array:
x = range(len(i))
y = i
plt.plot(x, y)
plt.show()
Which will produce the following figure:

Plotting a continuous stream of data with MatPlotLib

I want to use MatPlotLib to plot a graph, where the plot changes over time. At every time step, an additional data point will be added to the plot. However, there should only be one graph displayed, whose appearance evolves over time.
In my test example, the plot is a simple linear plot (y = x). Here is what I have tried:
for i in range(100):
x = range(i)
y = range(i)
plt.plot(x, y)
plt.ion()
plt.show()
time.sleep(1)
However, what happens here is that multiple windows are created, so that by the end of the loop I have 100 windows. Also, I have noticed that for the most recent window, it is just a white window, and the plot only appears on the next step.
So, my two questions are:
1) How can I change my code so that only a single window is displayed, whose contents changes over time?
2) How can I change my code so that for the most recent timestep, the plot is actually displayed on the window, rather than it only displaying a white window?
Thanks!
(1)
You can set plt.ion() at the beginning and plot all graphs to the same window. Within the loop use plt.draw() to show the graph and plt.pause(t) to make a pause. Note that t can be very small, but the command needs to be there for the animation to work on most backends.
You might want to clear the axes before plotting new content using plt.gca().cla().
import matplotlib.pyplot as plt
plt.ion()
for i in range(100):
x = range(i)
y = range(i)
# plt.gca().cla() # optionally clear axes
plt.plot(x, y)
plt.title(str(i))
plt.draw()
plt.pause(0.1)
plt.show(block=True) # block=True lets the window stay open at the end of the animation.
Alternatively to this very simple approach, use any of the examples for animations provided in http://matplotlib.org/examples/animation/index.html
(2)
In order to get each plot in a new window, use plt.figure() and remove plt.ion(). Also only show the windows at the end:
import matplotlib.pyplot as plt
for i in range(100):
x = range(i)
y = range(i)
plt.figure()
plt.plot(x, y)
plt.title(str(i))
plt.show()
Note that you might find that in both cases the first plot is empty simply because for i=0, range(i) == [] is an empty list without any points. Even for i=1 there is only one point being plotted, but of course no line can connect a single point with itself.
I think the best way is to create one line plot and then update data in it. Then you will have single window and single graph that will continuously update.
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure(figsize=(16,8))
axes = fig.add_subplot(111)
data_plot=plt.plot(0,0)
line, = axes.plot([],[])
for i in range(100):
x = range(i)
y = range(i)
line.set_ydata(y)
line.set_xdata(x)
if len(y)>0:
axes.set_ylim(min(y),max(y)+1) # +1 to avoid singular transformation warning
axes.set_xlim(min(x),max(x)+1)
plt.title(str(i))
plt.draw()
plt.pause(0.1)
plt.show(block=True)

How to dynamically update a plot in a loop in IPython notebook (within one cell)

Environment: Python 2.7, Matplotlib 1.3, IPython notebook 1.1, Linux, and Chrome. The code is in one single input cell, using --pylab=inline.
I want to use IPython notebook and Pandas to consume a stream and dynamically update a plot every five seconds.
When I just use a print statement to print the data in text format, it works perfectly fine: the output cell just keeps printing data and adding new rows. But when I try to plot the data (and then update it in a loop), the plot never shows up in the output cell. But if I remove the loop, and just plot it once, it works fine.
Then I did some simple test:
i = pd.date_range('2013-1-1',periods=100,freq='s')
while True:
plot(pd.Series(data=np.random.randn(100), index=i))
#pd.Series(data=np.random.randn(100), index=i).plot() also tried this one
time.sleep(5)
The output will not show anything until I manually interrupt the process (Ctrl + M + I). And after I interrupt it, the plot shows correctly as multiple overlapped lines. But what I really want is a plot that shows up and gets updated every five seconds (or whenever the plot() function gets called, just like what print statement outputs I mentioned above, which works well). Only showing the final chart after the cell is completely done is not what I want.
I even tried to explicitly add the draw() function after each plot(), etc. None of them works. How can I dynamically update a plot by a for/while loop within one cell in IPython notebook?
Use the IPython.display module:
%matplotlib inline
import time
import pylab as pl
from IPython import display
for i in range(10):
pl.plot(pl.randn(100))
display.clear_output(wait=True)
display.display(pl.gcf())
time.sleep(1.0)
A couple of improvement's on HYRY's answer:
call display before clear_output so that you end up with one plot, rather than two, when the cell is interrupted.
catch the KeyboardInterrupt, so that the cell output isn't littered with the traceback.
import matplotlib.pylab as plt
import pandas as pd
import numpy as np
import time
from IPython import display
%matplotlib inline
i = pd.date_range('2013-1-1',periods=100,freq='s')
while True:
try:
plt.plot(pd.Series(data=np.random.randn(100), index=i))
display.display(plt.gcf())
display.clear_output(wait=True)
time.sleep(1)
except KeyboardInterrupt:
break
You can further improve this by adding wait=True to clear_output:
display.clear_output(wait=True)
display.display(pl.gcf())
I tried many methods, but I found this as the simplest and the easiest way -> to add clear_output(wait=True), for example,
from IPython.display import clear_output
for i in range(n_iterations):
clear_output(wait=True)
x = some value
y = some value
plt.plot(x, y, '-r')
plt.show()
This overwrites on the same plot, and gives an illusion of plot animation
Adding a label to the other solutions posted here will keep adding new labels in every loop. To deal with that, clear the plot using clf.
For example:
for t in range(100):
if t % refresh_rate == 0:
plt.clf()
plt.plot(history['val_loss'], 'r-', lw=2, label='val')
plt.plot(history['training_loss'], 'b-', lw=1, label='training')
plt.legend()
display.clear_output(wait=True)
display.display(plt.gcf())
Try to add show() or gcf().show() after the plot() function. These will force the current figure to update (gcf() returns a reference for the current figure).
You can do it like this. It accepts x,y as list and output a scatter plot plus a linear trend on the same plot.
from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
def live_plot(x, y, figsize=(7,5), title=''):
clear_output(wait=True)
plt.figure(figsize=figsize)
plt.xlim(0, training_steps)
plt.ylim(0, 100)
x = [float(i) for i in x]
y = [float(i) for i in y]
if len(x) > 1:
plt.scatter(x,y, label='axis y', color='k')
m, b = np.polyfit(x, y, 1)
plt.plot(x, [x * m for x in x] + b)
plt.title(title)
plt.grid(True)
plt.xlabel('axis x')
plt.ylabel('axis y')
plt.show();
You just need to call live_plot(x, y) inside a loop. Here's how it looks:

How can I dynamically update my matplotlib figure as the data file changes?

I have a python script that reads in a data file and displays one figure with four plots using the matplotlib library. The data file is being updated every few seconds since it is an output file for a different piece of software that is running concurrently. I would like the four plots in my matplotlib figure to refresh themselves using the updated data file every 20 seconds. The way I've implemented this is as follows:
import pylab as pl
import time
pl.ion()
fig = pl.figure()
while True:
f = open('data.out', 'rb')
#code to parse data and plot four charts
ax = fig.add_subplot(2,2,1)
#...
ax = fig.add_subplot(2,2,4)
#...
pl.draw()
time.sleep(20)
This works, but I lose functionality of the zoom and pan buttons which normally work if pl.show() is called. This is not optimal. However, if pl.show() is substituted for pl.draw(), the script no longer updates the plots. Is there a way to dynamically update a plot without completely losing the zoom/pan functionality?
Your code is a little too vague to know what is going on.
I can offer this:
You should retain normal functionality if you create your subplots once, saving all the axes objects and then calling show().
Subsequent changes to those subplots could be done like this:
#inside while loop
for i in #subplotlist
ax[i].clear() #ax[i] is the axis object of the i'th subplot
ax[i].plot(#plotstuff)
ax[i].draw()
The toolbar for zooming and panning can be added by hand if you so desire.
As you are developping a sofware, I supposed you may have a multi-threaded approach.
So in this case using an infinite while loop is a bad idea, like you are holding up your main thread.
In addition when it comes to GUI it’s also a bad idea to interfere abruptly with GUI internal threads (wxPython for instance) and you should have an event driven design approach in order to not abruptly interrupt other threads (and that will cause the crash of your application).
The use of a timer will do the job.
A timer would do these actions in the following script :
1/ call a function to clear previous artist
2 / replot the data
3/ apply changes to canvas
4/ create another identical timer in the following design way : a timer who calls another identical timer after doing its job
Like I do not have access to your datas, I created a random data provider for the illustration.
The defined variable delay_repeat allows you to program in seconds the refresh.
import pylab as pl
import random
from threading import Timer
def dataprovider():
return [random.randint(0, 8) for i in range(8)]
def random_col():
return ['blue', 'red', 'green', 'orange'][random.randint(0,3)]
# .... #
fig = pl.figure()
axes = [fig.add_subplot(2,2,i) for i in range(1,5)]
paths = [ax.scatter(x=dataprovider(), y=dataprovider(), marker = '+', c=random_col()) for ax in axes]
# .... #
def clear_arts(paths, all_arts=-1):
if all_arts < 0:
all_arts = len(paths)
for path in paths[:all_arts]:
path.remove()
def refresh_arts(paths, delay_repeat):
# 1 - clear previous artists
clear_arts(paths,all_arts=-1)
# 2 - Get artists paths for cleaning
paths = [ax.scatter(x=dataprovider(), y=dataprovider(), marker = '+', c=random_col()) for ax in axes]
# 3 - Apply changes
fig.canvas.draw_idle()
# 4 - Create another timer
Timer(delay_repeat, refresh_arts, (paths, delay_repeat)).start()
# 4- Create a timer that will run function with arguments args and keyword arguments kwargs,
# after interval seconds have passed.
delay_repeat = 2
Timer(delay_repeat, refresh_arts, (paths, delay_repeat)).start()
# print("process continues here")
pl.show()
You can do it like this. It accept x,y as list and output a scatter plot plus a linear trend on the same plot.
from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
def live_plot(x, y, figsize=(7,5), title=''):
clear_output(wait=True)
plt.figure(figsize=figsize)
plt.xlim(0, training_steps)
plt.ylim(0, 100)
x= [float(i) for i in x]
y= [float(i) for i in y]
if len(x) > 1:
plt.scatter(x,y, label='axis y', color='k')
m, b = np.polyfit(x, y, 1)
plt.plot(x, [x * m for x in x] + b)
plt.title(title)
plt.grid(True)
plt.xlabel('axis x')
plt.ylabel('axis y')
plt.show();
you just need to call live_plot(x, y) inside a loop. here's how it looks:

Categories

Resources