How to update python plot with new data? - python

I would like to update python plot once new data arrive. Below is my example using timer to simulate data change.
index = 0
class RepeatTimer(Timer):
def run(self):
while not self.finished.wait(self.interval):
self.function(*self.args, **self.kwargs)
def LoopArray():
global index
if index >= 4: index =0
ArrayList = [
[85.0805, 85.0625, 85.094, 85.043, 84.9655, 84.9725, 84.93],
[0.896385, 0.89645, 0.896315, 0.89622, 0.89579, 0.896005, 0.896535],
[1.19046, 1.191145, 1.19125, 1.19071, 1.19086, 1.19122, 1.190185],
[162.1485, 162.2275, 162.285, 162.305, 162.3965, 162.562, 162.7135],
]
Array = np.array(ArrayList[index],float)
fig = plt.figure()
ax = fig.subplots()
ax.plot(Array)
ax.set_title('Symbol' )
ax.set_ylabel('Price')
ax.legend()
ax.grid()
plt.show()
index = index +1
timer_main = RepeatTimer(3, LoopArray)
timer_main.start()
But I got only first array is displayed. Any advice or guidance on this would be greatly appreciated, Thanks.

Related

Animating heatmap with python seaborn

This is driving me crazy. I'm trying to create an animation of a heatmap where every frame shows a different time stamp. I have created the data structures, imported ffmpeg, and I tried another package too (celluloid that seems very good). I tried an example with a simple line plot and it does animate. The result is always the same: I get the last frame, not an animation. Here is my code in case anyone can spot the error... Thank you in advance.
------------- Code --------------
from matplotlib import animation, rc
from IPython.display import HTML
data2 = df[['ProducerName', 'MemOccupied']]
data2 = data2.sort_index().sort_values('ProducerName', kind='mergesort')
data2 = data2[['MemOccupied']]
fig = plt.figure()
camera = Camera(fig)
df_heatmap = pd.DataFrame(data2)
df_heatmap = df_heatmap.dropna()
df_heatmap = df_heatmap.resample('1T').mean()
count = df_heatmap.count()
index_series = df_heatmap.index
def init():
#plt.clf()
ax = sns.heatmap(data2[data2.index == index_series[1]], cbar_kws={'label': 'Memory Occupied (GB)'}, cmap='Blues_r')
ax.set(ylabel=None)
#return (ax,)
def animate(i):
plt.clf()
ax = sns.heatmap(data2[data2.index == index_series[i]], cbar_kws={'label': 'Memory Occupied (GB)'}, cmap='Blues_r')
ax.set(ylabel=None)
#return (ax,)
anim = animation.FuncAnimation(fig = fig, func = animate, init_func=init, frames=count-1,repeat = True, interval = 200)
HTML(anim.to_html5_video())

Going from 2 Axes to 1?

How can I go down to one plot (axes) without getting the error "'AxesSubplot' object is not subscriptable" I tried changing this line <self.fig, axes = plt.subplots(2, 1, figsize=(11,7))> to <self.fig, axes = plt.subplots(1, 1, figsize=(11,7))>.....but I got the error.
Any help would be truly appreciated.
Just some background, this is a live EEG (Biosensor) Stream.
#############3 Create matplotlib plots
#self.figure = Figure() ???
self.fig, axes = plt.subplots(2, 1, figsize=(11,7)) #2 or 1?
prop_cycle = plt.rcParams['axes.prop_cycle'] #CHANGED
colors = prop_cycle.by_key()['color']
# Set the Axes Instance
# Add brainflow waveform and FFT
self.wave_ax = axes[0] #Location 1
# Set titles
self.wave_ax.set_title("Cyton Waveform") #Title
# Create line objects whose data we update in the animation
self.lines = [x[0] for x in
[ax.plot(self.data, self.data, color=colors[i]) for i,ax in enumerate(axes)]]
# Start animation
self.fig.tight_layout() #Creates spaces between titles :)
self.ani = matplotlib.animation.FuncAnimation(self.fig, self.updateFig,
interval=5, blit=True) #Animation
# Create list of objects that get animated
self.ani_objects = [self.wave_ax]
################################################# Animation helpers
def updateVars(self): #THIS IS WHERE YOU PICK THE FIRST CHANNEL
### What's the latest?? # Using the first channel
all_data = self.board.get_current_board_data(self.display_window)
self.curData = all_data[self.board_channel, :]
if (len(self.curData) < self.display_window):
self.data = self.curData
return
################################## Animation function
def updateFig(self, *args):
now = time.time()
# Update data from the brainflow
self.updateVars()
if (len(self.data) == self.display_window):
### Update plots with new data
self.lines[0].set_data(self.waveX, self.data)
else: # Set filler waveform data if the buffer hasn't filled yet
self.lines[0].set_data(list(range(len(self.data))), self.data)
## Reset limits of the waveform plot so it looks nice
self.wave_ax.relim()
self.wave_ax.autoscale_view(tight=True)
self.propagateChanges()
return self.ani_objects
def propagateChanges(self):
self.fig.stale = True #'stale' and needs to be re-drawn for the output to match the internal state
self.fig.canvas.draw() # Redraw the current figure. This is used to update a figure that has been altered, but not automatically re-drawn
self.fig.canvas.flush_events() # Flush the GUI events for the figure.
global stream
'''
i = 0
while i < 5:
print("starting loop")
i += 1
'''
try:
stream = CytonStream()
plt.show()
#except KeyboardInterrupt: # Dont understand incentive behind this???
# print('\nKeyboard interrupted, ending program')
# stream.board.release_session()
except Exception as e:
print("other exception,", e)
time.sleep(1)
When you can self.fig, axes = plt.subplots(2, 1, figsize=(11,7)), axes will be a numpy array of subplots that you can index.
When you call self.fig, axes = plt.subplots(1, 1, figsize=(11,7)), axes will be a single subplot that you can not index.
f1, a1 = plt.subplots(1, 1, figsize=(11,7))
f2, a2 = plt.subplots(2, 1, figsize=(11,7))
print(type(a1)) # <class 'matplotlib.axes._subplots.AxesSubplot'>
print(type(a2)) # <class 'numpy.ndarray'>

Updating a matplotlib figure during simulation

I try to implement a matplotlib figure that updates during the simulation of my environment.
The following Classes works fine in my test but doesn't update the figure when I use it in my environment. During the simulation of the environment, the graph is shown, but no lines are plotted.
My guess is that .draw() is not working how I think it does.
Can anyone figure out the issue here?
class Visualisation:
def __init__(self, graphs):
self.graphs_dict = {}
for graph in graphs:
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(graph.x, graph.y, 'r-')
self.graphs_dict[graph.title] = {"fig": fig, "ax": ax, "line": line, "graph": graph}
self.graphs_dict[graph.title]["fig"].canvas.draw()
plt.ion()
plt.show()
def update(self, graph):
graph = self.graphs_dict[graph.title]["graph"]
self.graphs_dict[graph.title]["line"].set_xdata(graph.x)
self.graphs_dict[graph.title]["line"].set_ydata(graph.y)
self.graphs_dict[graph.title]["fig"].canvas.flush_events()
x_lim, y_lim = self.get_lim(graph)
self.graphs_dict[graph.title]["ax"].set_xlim(x_lim)
self.graphs_dict[graph.title]["ax"].set_ylim(y_lim)
self.graphs_dict[graph.title]["fig"].canvas.draw()
#staticmethod
def get_lim(graph):
if graph.x_lim is None:
x = np.array(graph.x)
y = np.array(graph.y)
x_lim = [x.min(), x.max()]
y_lim = [y.min(), y.max()]
else:
x_lim = graph.x_lim
y_lim = graph.y_lim
return x_lim, y_lim
class Graph:
def __init__(self, title, x, y, x_label="", y_label=""):
"""
Sets up a graph for Matplotlib
Parameters
----------
title : String
Title of the plot
x : float
y : float
x_label : String
x Label
y_label : String
y Label
"""
self.title = title
self.x = x
self.y = y
self.x_label = x_label
self.y_label = y_label
self.x_lim, self.y_lim = None, None
def set_lim(self, x_lim, y_lim):
self.x_lim = x_lim
self.y_lim = y_lim
class Environment:
def __init__(self, [..], verbose=0):
"""verbose : int
0 - No Visualisation
1 - Visualisation
2 - Visualisation and Logging"""
self.vis = None
self.verbose = verbose
[......]
def simulate(self):
for _ in range(self.n_steps):
[...]
self.visualize()
def visualize(self):
if self.verbose == 1 or self.verbose == 2:
if self.vis is None:
graphs = [Graph(title="VariableY", x=[], y=[])]
graphs[0].set_lim(x_lim=[0, 100], y_lim=[0, 300])
self.vis = Visualisation(graphs=graphs)
else:
self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
self.vis.graphs_dict["VariableY"]["graph"].y.append(150)
self.vis.update(self.vis.graphs_dict["VariableY"]["graph"])
When I run the code I more or less just write: env.simulate().
The code runs fine here:
class TestSingularVisualisation(unittest.TestCase):
def setUp(self):
self.graph = Graph(title="Test", x=[0], y=[0])
self.vis = Visualisation(graphs=[self.graph])
class TestSingleUpdate(TestSingularVisualisation):
def test_repeated_update(self):
for i in range(5):
self.graph.x.append(i)
self.graph.y.append(np.sin(i))
self.vis.update(self.graph)
time.sleep(1)
Turns out your code works the way it is set up. Here is the sole problem with the code you provided:
self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
self.vis.graphs_dict["VariableY"]["graph"].y.append(150)
You are plotting a line and correctly updating the canvas, however, you keep appending the exact same (x, y) coordinate. So the simulation does update the line, but the line simplifies to a point. Your test case does not do this. You can run a dummy example with your code by simply adding a line like this:
self.internal_step += 5
before adding the new x point, and you will produce a horizontal line.
Let me know if this solves your problem.
Probably not the most elegant, but I use plt.pause(0.1) when I want to update plots during execution. It pauses for 0.1s and forces all plots to be actually displayed. (It work in %debug in ipython as a bonus)

Update Pyplot Subplots with Images in a Loop

I think I'm doing something really dumb, but I can't quite figure it out. I want to create a class to display a set of images as subplots; the display should be updated manually from within a loop. Here's the class I've created to try to do this:
import matplotlib.pyplot as plt
import numpy as np
class tensor_plot:
def __init__(self, tensor_shape, nrows=1):
self.img_height, self.img_width, self.num_imgs = tensor_shape
self.nrows = nrows
self.ncols = self.num_imgs // nrows
assert(self.ncols*self.nrows == self.num_imgs)
self.fig, self.a = plt.subplots(self.nrows, self.ncols, sharex='col', sharey='row')
for (row, col) in zip(range(self.nrows), range(self.ncols)):
self.a[row, col] = plt.imshow(np.zeros([self.img_height, self.img_width]))
def update(self, tensor):
n=0
for row in range(self.nrows):
for col in range(self.ncols):
self.a[row,col].set_data(tensor[:,:,n].squeeze())
n += 1
plt.show()
When I try to pass a tensor in to update, it says no set_data attribute. But using dir there is such an attribute.
In [322]: tp = tensor_plot(l10.shape, 4)
In [323]: tp.update(l10)
AttributeError: 'AxesSubplot' object has no attribute 'set_data'
In [324]: dir(tp.a[0,0])
Out[324]:
['_A',
...
'set_data',
...
'update_from',
'write_png',
'zorder']
If I add a line print(dir(self.a[row,col])) in the loop, it is true that set_data isn't there though! Same comment applies to imshow.
Any ideas?
With many thanks to #ImportanceOfBeingEarnest, here's the final code that works for me (in case it's useful to others).
class tensor_plot:
def __init__(self, tensor_shape, nrows=1):
self.img_height, self.img_width, self.num_imgs = tensor_shape
self.nrows = nrows
self.ncols = self.num_imgs // nrows
assert(self.ncols*self.nrows == self.num_imgs)
self.fig, self.a = plt.subplots(self.nrows, self.ncols, sharex='col', sharey='row')
self.imgs = np.array( [ [ self.a[row, col].imshow(np.zeros([self.img_height, self.img_width])) for col in range(self.ncols) ] for row in range(self.nrows)])
plt.pause(0.1)
def update(self, tensor):
n=0
for row in range(self.nrows):
for col in range(self.ncols):
self.imgs[row,col].set_data(tensor[:,:,n].squeeze())
self.imgs[row,col].set_clim(vmin=0, vmax=255)
n += 1
self.fig.canvas.draw_idle()
plt.pause(0.01)
plt.draw_all()

updating a Slider min - max range in runtime in matplotlib [duplicate]

I am trying to write a small bit of code that interactively deletes selected slices in an image series using matplotlib. I have created a button 'delete' which stores a number of indices to be deleted when the button 'update' is selected. However, I am currently unable to reset the range of my slider widget, i.e. removing the number of deleted slices from valmax. What is the pythonic solution to this problem?
Here is my code:
import dicom
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
frame = 0
#store indices of slices to be deleted
delete_list = []
def main():
data = np.random.rand(16,256,256)
nframes = data.shape[0]
raw_dicom_stack = []
for x in range (nframes):
raw_dicom_stack.append(data[x,:,:])
#yframe = 0
# Visualize it
viewer = VolumeViewer(raw_dicom_stack, nframes)
viewer.show()
class VolumeViewer(object):
def __init__(self, raw_dicom_stack, nframes):
global delete_list
self.raw_dicom_stack = raw_dicom_stack
self.nframes = nframes
self.delete_list = delete_list
# Setup the axes.
self.fig, self.ax = plt.subplots()
self.slider_ax = self.fig.add_axes([0.2, 0.03, 0.65, 0.03])
self.delete_ax = self.fig.add_axes([0.85,0.84,0.1,0.04])
self.update_ax = self.fig.add_axes([0.85,0.78,0.1,0.04])
self.register_ax = self.fig.add_axes([0.85,0.72,0.1,0.04])
self.add_ax = self.fig.add_axes([0.85,0.66,0.1,0.04])
# Make the slider
self.slider = Slider(self.slider_ax, 'Frame', 1, self.nframes,
valinit=1, valfmt='%1d/{}'.format(self.nframes))
self.slider.on_changed(self.update)
#Make the buttons
self.del_button = Button(self.delete_ax, 'Delete')
self.del_button.on_clicked(self.delete)
self.upd_button = Button(self.update_ax, 'Update')
self.upd_button.on_clicked(self.img_update)
self.reg_button = Button(self.register_ax, 'Register')
self.add_button = Button(self.add_ax, "Add")
# Plot the first slice of the image
self.im = self.ax.imshow(np.array(raw_dicom_stack[0]))
def update(self, value):
global frame
frame = int(np.round(value - 1))
# Update the image data
dat = np.array(self.raw_dicom_stack[frame])
self.im.set_data(dat)
# Reset the image scaling bounds (this may not be necessary for you)
self.im.set_clim([dat.min(), dat.max()])
# Redraw the plot
self.fig.canvas.draw()
def delete(self,event):
global frame
global delete_list
delete_list.append(frame)
print 'Frame %s has been added to list of slices to be deleted' %str(frame+1)
print 'Please click update to delete these slices and show updated image series \n'
#Remove duplicates from delete list
def img_update(self,event):
#function deletes image stacks and updates viewer
global delete_list
#Remove duplicates from list and sort into numerical order
delete_list = list(set(delete_list))
delete_list.sort()
#Make sure delete_list is not empty
if not delete_list:
print "Delete list is empty, no slices to delete"
#Loop through delete list in reverse numerical order and remove slices from series
else:
for i in reversed(delete_list):
self.raw_dicom_stack.pop(i)
print 'Slice %i removed from dicom series \n' %(i+1)
#Can now remove contents from delete_list
del delete_list[:]
#Update slider range
self.nframes = len(self.raw_dicom_stack)
def show(self):
plt.show()
if __name__ == '__main__':
main()
In order to update a slider range you may set the min and max value of it directly,
slider.valmin = 3
slider.valmax = 7
In order to reflect this change in the slider axes you need to set the limits of the axes,
slider.ax.set_xlim(slider.valmin,slider.valmax)
A complete example, where typing in any digit changes the valmin of the slider to that value.
import matplotlib.pyplot as plt
import matplotlib.widgets
fig, (ax,sliderax) = plt.subplots(nrows=2,gridspec_kw=dict(height_ratios=[1,.05]))
ax.plot(range(11))
ax.set_xlim(5,None)
ax.set_title("Type number to set minimum slider value")
def update_range(val):
ax.set_xlim(val,None)
def update_slider(evt):
print(evt.key)
try:
val = int(evt.key)
slider.valmin = val
slider.ax.set_xlim(slider.valmin,None)
if val > slider.val:
slider.val=val
update_range(val)
fig.canvas.draw_idle()
except:
pass
slider=matplotlib.widgets.Slider(sliderax,"xlim",0,10,5)
slider.on_changed(update_range)
fig.canvas.mpl_connect('key_press_event', update_slider)
plt.show()
It looks like the slider does not have a way to update the range (api). I would suggest setting the range of the slider to be [0,1] and doing
frame = int(self.nframes * value)
On a somewhat related note, I would have made frame an instance variable a data attribute instead of a global variable (tutorial).

Categories

Resources