I am trying to plot real time data. I managed to plot the data but I would like for the bar graph to go up and down on a single x-value rather than produce new x-values for every new datapoint. I believe I have to replace the function x.append(i) with something like a replace, any ideas? Thank you!!
So far this is what I came up with:
import time
import psutil
import matplotlib.pyplot as plt
%matplotlib notebook
fig = plt.figure()
ax = fig.add_subplot(111)
fig.show()
plt.axis('off')
i = 0
x, y = [], []
while True:
x.append(i)
y.append(psutil.cpu_percent())
ax.bar(x, y, color='b')
fig.canvas.draw()
ax.set_xlim(left=max(0, i-50), right=i+50)
time.sleep(0.1)
i += 1
For the bar graph you can create a list inside the while loop, and instantly update it there. First you need to import a random in order get random value for y axis, or you can use cpu_percent.
import psutil
import random
These two should work.
And then:
while True:
x_axis = [str(_) for _ in range(100, 200)]
y_axis = [8 * random.random() for _ in range(100, 200)]
ax.bar(x, y, color='b')
fig.canvas.draw()
time.sleep(0.1)
However, matplotlib is not convenient for real data plotting, I strongly recommend you to use bokeh. You can find bokeh documentation here. It is really cool for creating any kind of real time plot. And at the same time, you can integrate it with your web browser. Hope this will help you)
If you just want to display the latest value, you can consider doing something like:
plt.ion()
graph = plt.bar(["Now"], [0])[0]
plt.axis('off')
i = 0
data = {}
while True:
cpu_percent = psutil.cpu_percent()
graph.set_ydata(cpu_percent)
plt.draw()
plt.pause(0.1)
data[i] = cpu_percent
i += 1
This way, you still have a record of all the datapoints to play with later (x, y) but you will only display 1 x value at a time on the graph.
Further reading
Related
I want to plot a time series in a while loop as a rolling window: The graph should always show the 10 most recent observations.
My idea was to use a deque object with maxlen=10 and plot it in every step.
To my great surprise the plot appends new values to the old plot; apparently it remembers values that are no longer inside the deque! Why is that and how can I switch it off?
This is a minimal example of what I am trying to do. The plotting part is based on this post (although plt.ion() did not change anything for me, so I left it out):
from collections import deque
import matplotlib.pyplot as plt
import numpy as np
x = 0
data = deque(maxlen=10)
while True:
x += np.abs(np.random.randn())
y = np.random.randn()
data.append((x, y))
plt.plot(*zip(*data), c='black')
plt.pause(0.1)
I also tried to use Matplotlib's animation functions instead, but could not figure out how to do that in an infinite while loop...
Nowadays, it's much easier (and offers much better performance) to use the animation module than to use multiple calls to plt.plot:
from collections import deque
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
def animate(i):
global x
x += np.abs(np.random.randn())
y = np.random.randn()
data.append((x, y))
ax.relim()
ax.autoscale_view()
line.set_data(*zip(*data))
fig, ax = plt.subplots()
x = 0
y = np.random.randn()
data = deque([(x, y)], maxlen=10)
line, = plt.plot(*zip(*data), c='black')
ani = animation.FuncAnimation(fig, animate, interval=100)
plt.show()
I am new to Matplotlib and that's why there might be a more efficient way to run my program.
It is plotting a bunch of points with different colours (depending on some factors). It is constantly producing new pictures in a loop of the current colour state.
Basically it looks like this:
import matplotlib.pyplot as plt
def getColour():
#calculate some stuff with x and y and the changing factors
while True:
fig = plt.figure(figsize=(17,10))
plt.scatter(x, y , c=getColour())
plt.show()
plt.close(fig)
I was trying out clf() as well. However, it didn't change the pace at all. Does anyone have ideas? What am I doing wrong?
Thank you!
Edit:
The target is to produce a picture each time it goes through the loop. Since my program is doing this quite slowly, my question is whether there is a way to make it run faster.
I am working with python 2.7
Something like an animation:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
ms_between_frames = 100
n_points = 100
x = np.arange(n_points, dtype=float) #EDIT
y = np.random.random(n_points)
z = np.random.random(n_points)
def getColour(x, y, z):
c = np.empty((len(x),3))
for i in range(len(x)):
c[i] = [x[i]/n_points, z[i], 1.-z[i]]
return c
def update(frame_number):
global x, y
z = np.random.random(n_points)
c = getColour(x, y, z)
graph.set_color(c)
fig = plt.figure(figsize=(17,10))
ax = fig.add_subplot(111)
graph = ax.scatter(x, y , c=getColour(x, y, z))
animation = FuncAnimation(fig, update, interval=ms_between_frames)
plt.show()
EDIT: made x hold floats so the division inside getColour would not return 0 (could also have made /float(n_points))
By the way, it should be possible to define only one function to update the colours, depending on the arguments you require to do so, to avoid the call overhead.
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)
I am plotting iteratively using matplotlib in python. I am setting the axis of the plot, so as to display e.g. only 50 lines at a time. A pseudo code is given below as an example:
x = 0
y = 1
line_plot = 50
axis.set_ylim(0 , line_plot)
while True:
plot(x,y)
y = y+1
if y > line_plot :
axis.set_ylim(y , y+line_plot)
This code will run indefinitely, and eventually the memory required for the plot will get quite large, even if only 50 lines are present on the graph (since all data points are kept in memory). I would like to know if there is a command in python to delete all data that is out of axis limits, freeing some memory space.
Thank you,
Gaelle
This will depend a little bit on how exactly your script looks like. You need some method to determine the y-coordinates of every line, and based on some criteria remove them or not. But if you do something like:
x = np.arange(1)
y = np.ones(1)
pl.figure()
l1 = pl.plot(x,y)[0]
y[:] += 1
l2 = pl.plot(x,y)[0]
and call get_ydata() on both lines, they will have the same y-values, so get_ydata() seems to return the original array, not necessarily the values drawn in the plot (which apparently is a bug, see: this matplotlib issue). If, instead of y[:] += 1 you make an actual copy of the array (y = y.copy()+1), you can use get_ydata(). If this is the case in your real-world problem, such a solution might work:
import matplotlib
import matplotlib.pylab as pl
import numpy as np
pl.close('all')
x = np.arange(100000)
y = np.ones(x.size)
pl.figure()
ax = pl.gca()
line_plot = 50
ax.set_ylim(0, line_plot)
for i in range(200):
pl.plot(x, y)
y = y.copy() + 1
if y[0] > line_plot:
ax.set_ylim(y[0]-line_plot, y[0])
for l in ax.get_lines():
yval = l.get_ydata()[0]
if(yval < ax.get_ylim()[0]):
l.remove()
If I remove the for l in ax.get_lines part, the memory usage scales with i, with this part included the memory usage stays constant, even for very large values of i
You want look at the animation examples
# make a figure and axes object
fig, ax = plt.subplots()
# make a Line2D artist
ln, = ax.plot([], [], linestyle='', marker='o')
# local version of the data
xdata, ydata = [], []
for j in range(200):
# update your copy of the data
xdata.append(j)
ydata.append(j*j)
xdata = xdata[-50:]
ydata = ydata[-50:]
# update the Line2D objects copy of the data
ln.set_data(xdata, ydata)
# autoscale limits to new data
ax.relim()
ax.autoscale()
# needed in non-interactive mode and/or mpl < 1.5
# fig.canvas.draw_idle()
# sleep, but run the GUI event loop
plt.pause(.1)
I make a loop over two cases and for each case I try to make a plot.
for col_name in ['col2','col3']:
x_min = min(df['col1'].min(), df[col_name].min())
x_max = max(df['col1'].max(), df[col_name].max())
plt.xlim([x_min,x_max])
plt.ylim([x_min,x_max])
plt.axes().set_aspect('equal')
plt.scatter(df['col1'], df[col_name])
As a result I get one plot in my IPython notebook. Does anyone know how to overcome this problem?
You need to call figure() more than once.
for col_name in ['col2','col3']:
plt = figure() #This gives you a new figure to plot in
x_min = min(df['col1'].min(), df[col_name].min())
x_max = max(df['col1'].max(), df[col_name].max())
plt.xlim([x_min,x_max])
plt.ylim([x_min,x_max])
plt.axes().set_aspect('equal')
plt.scatter(df['col1'], df[col_name])
I would just use two figures if I want them on different windows.
Something like this ought to work.
>>> for i in range(3):
xAxis = [randint(1, 5) for _ in range(10)]
plt.figure(1)
plt.plot(xAxis)
plt.show()
xAxis2 = [randint(1, 5) for _ in range(10)]
plt.figure(2)
plt.plot(xAxis2)
plt.show()
It gave me six consecutive figures.
Since, you need a new figure for every iteration, do.
for index, col_name in ['col2','col3']:
plt.figure(index)
# Do the plotting.