I have asked this before and maybe I was not clear so, asking again. I am taking 4 different sensor values from arduino and plotting them real time using matplotlib of python. The real time plotting of multiple sensors is working, but I need them in a GUI with a start and a stop button. How do I modify my following code such that I get 4 real time plots in a GUI. This code is working, I just need to see my graphs in a GUI. Thanks in advance and help me the additional modification as I am new to python and still learning.
import copy
from threading import Thread
from tkinter import Tk, Frame
import serial
import time
import collections
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import struct
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
A = 0
B = 0
C = 0
D = 0
Sum = 0
Az = 0
El = 0
class serialPlot:
def __init__(self, serialPort='com5', serialBaud=38400, plotLength=100, dataNumBytes=2, numPlots=1):
self.port = serialPort
self.baud = serialBaud
self.plotMaxLength = plotLength
self.dataNumBytes = dataNumBytes
self.numPlots = numPlots
self.rawData = bytearray(numPlots * dataNumBytes)
self.dataType = None
if dataNumBytes == 2:
self.dataType = 'h' # 2 byte integer
elif dataNumBytes == 4:
self.dataType = 'f' # 4 byte float
self.data = []
self.privateData = None # for storing a copy of the data so all plots are synchronized
for i in range(numPlots): # give an array for each type of data and store them in a list
self.data.append(collections.deque([0] * plotLength, maxlen=plotLength))
self.isRun = True
self.isReceiving = False
self.thread = None
self.plotTimer = 0
self.previousTimer = 0
print('Trying to connect to: ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
try:
self.serialConnection = serial.Serial(serialPort, serialBaud, timeout=4)
print('Connected to ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
except:
print("Failed to connect with " + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
def readSerialStart(self):
if self.thread == None:
self.thread = Thread(target=self.backgroundThread)
self.thread.start()
# Block till we start receiving values
while self.isReceiving != True:
time.sleep(0.1)
def getSerialData(self, frame, lines, lineValueText, lineLabel, timeText, pltNumber):
if pltNumber == 0: # in order to make all the clocks show the same reading
currentTimer = time.perf_counter()
self.plotTimer = int((currentTimer - self.previousTimer) * 1000) # the first reading will be erroneous
self.previousTimer = currentTimer
self.privateData = copy.deepcopy(
self.rawData) # so that the 4 values in our plots will be synchronized to the same sample time
timeText.set_text('Plot Interval = ' + str(self.plotTimer) + 'ms')
data = self.privateData[(pltNumber * self.dataNumBytes):(self.dataNumBytes + pltNumber * self.dataNumBytes)]
value, = struct.unpack(self.dataType, data)
self.data[pltNumber].append(value) # we get the latest data point and append it to our array
lines.set_data(range(self.plotMaxLength), self.data[pltNumber])
lineValueText.set_text('[' + lineLabel + '] = ' + str(value))
if lineLabel == 'Detector A':
global A
A = float(value)
if lineLabel == 'Detector B':
global B
B = float(value)
if lineLabel == 'Detector C':
global C
C = float(value)
if lineLabel == 'Detector D':
global D
D = float(value)
Sum = (A + B + C + D)
Az = (A + D - C - B)
El = (A + B - C - D)
def backgroundThread(self): # retrieve data
time.sleep(1.0) # give some buffer time for retrieving data
self.serialConnection.reset_input_buffer()
while (self.isRun):
self.serialConnection.readinto(self.rawData)
self.isReceiving = True
def close(self):
self.isRun = False
self.thread.join()
self.serialConnection.close()
print('Disconnected...')
def makeFigure(xLimit, yLimit, title):
xmin, xmax = xLimit
ymin, ymax = yLimit
fig = plt.figure()
ax = plt.axes(xlim=(xmin, xmax), ylim=(int(ymin - (ymax - ymin) / 10), int(ymax + (ymax - ymin) / 10)))
ax.set_title(title)
ax.set_xlabel("Time")
ax.set_ylabel("Detector Output")
ax.grid(True)
return fig, ax
class Window(Frame):
def __init__(self, figure, master, SerialReference):
Frame.__init__(self, master)
self.entry = None
self.setPoint = None
self.master = master # a reference to the master window
self.serialReference = SerialReference # keep a reference to our serial connection so that we can use it for bi-directional communicate from this class
self.initWindow(figure) # initialize the window with our settings
def initWindow(self, figure):
self.master.title("Real Time Plot")
canvas = FigureCanvasTkAgg(figure, master=self.master)
#toolbar = NavigationToolbar2TkAgg(canvas, self.master)
#canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def main():
portName = 'COM5'
# portName = '/dev/ttyUSB0'
baudRate = 38400
maxPlotLength = 100 # number of points in x-axis of real time plot
dataNumBytes = 4 # number of bytes of 1 data point
numPlots = 4 # number of plots in 1 graph
s = serialPlot(portName, baudRate, maxPlotLength, dataNumBytes, numPlots) # initializes all required variables
s.readSerialStart() # starts background thread
# plotting starts below
pltInterval = 50 # Period at which the plot animation updates [ms]
lineLabelText = ['Detector A', 'Detector B', 'Detector C', 'Detector D']
title = ['Detector A', 'Detector B', 'Detector C', 'Detector D']
xLimit = [(0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength), (0, maxPlotLength)]
yLimit = [(-1, 1), (-1, 1), (-1, 1), (-1, 1)]
style = ['r-', 'g-', 'b-', 'y-'] # linestyles for the different plots
anim = []
for i in range(numPlots):
fig, ax = makeFigure(xLimit[i], yLimit[i], title[i])
lines = ax.plot([], [], style[i], label=lineLabelText[i])[0]
timeText = ax.text(0.50, 0.95, '', transform=ax.transAxes)
lineValueText = ax.text(0.50, 0.90, '', transform=ax.transAxes)
anim.append(
animation.FuncAnimation(fig, s.getSerialData, fargs=(lines, lineValueText, lineLabelText[i], timeText, i),
interval=pltInterval)) # fargs has to be a tuple
plt.legend(loc="upper left")
plt.show()
s.close()
if __name__ == '__main__':
main()
Related
I'm using the matplotlib backend 'notebook', because I am making some interactive figures, and this works well with the notebook backend (in particular, I serve them via Jupyter Notebooks). I use ipywidgets to design the GUI and interactivity.
However, using this backend, there are all sorts of buttons that can interfere with my interactive figure. Especially, resizing, zooming, panning, or the power button, will lead to much confusion for my students...
I want to disable them. See this illustration on what I want to disable.
Can anyone point me to the relevant API pages or does anyone know how to disable/remove these buttons? I tried some other backends, but these typically will not work so well for interactive figures in Jupyter notebooks, so I want to stick to the notebook backend if possible.
This is the contents of svm_helper:
from matplotlib import pyplot as plt
from matplotlib.backend_bases import MouseButton as mb
import ipywidgets as widgets
import sklearn.linear_model
import sklearn.metrics
import sklearn.svm
import numpy as np
def plot_decision_boundary_margin(X, y, model):
Xmin = np.min(X[:,:],axis=0)
Xmax = np.max(X[:,:],axis=0)
Xmin = np.array([-3, -3])
Xmax = np.array([3, 3])
x0, x1 = np.meshgrid(
np.linspace(Xmin[0], Xmax[0], 500).reshape(-1, 1),
np.linspace(Xmin[1], Xmax[1], 200).reshape(-1, 1),
)
X_new = np.c_[x0.ravel(), x1.ravel()]
y_new = model.decision_function(X_new)
#plot_dataset(X,y)
zz = y_new.reshape(x0.shape)
C1 = plt.contour(x0, x1, zz, levels=np.array([0]),colors='k')
C2 = plt.contour(x0, x1, zz, levels=np.array([-1,1]),colors='k',linestyles='dashed')
return (C1, C2)
class LineBuilder2:
def __init__(self, lineR, lineB, widgetcolor, widgetC, my_out, need_seperable):
self.lineR = lineR
self.xsR = list(lineR.get_xdata())
self.ysR = list(lineR.get_ydata())
self.lineB = lineB
self.xsB = list(lineB.get_xdata())
self.ysB = list(lineB.get_ydata())
self.mywidgetcolor = widgetcolor
self.cid = lineR.figure.canvas.mpl_connect('button_press_event', self)
self.cid = lineR.figure.canvas.mpl_connect('motion_notify_event', self)
self.widgetC = widgetC
self.my_out = my_out
self.dragging_timer = 0
self.trained = False
self.model = None
self.C1 = None
self.C2 = None
self.need_seperable = need_seperable
def remove_decision_boundary(self):
if (self.C1 == None) or (self.C2 == None):
return
for coll in self.C1.collections:
plt.gca().collections.remove(coll)
for coll in self.C2.collections:
plt.gca().collections.remove(coll)
def __call__(self, event):
#print('click', event)
currently_dragging = False
if event.name == 'motion_notify_event':
currently_dragging = True
self.dragging_timer = self.dragging_timer+1
if self.dragging_timer > 5:
self.dragging_timer = 0
if not (event.button == mb.LEFT or event.button == mb.MIDDLE or event.button == mb.RIGHT):
return
if event.inaxes != self.lineB.axes:
return
#print(widgetcolor.value)
if self.mywidgetcolor.value == 'green':
self.xsR.append(event.xdata)
self.ysR.append(event.ydata)
if (not currently_dragging) or (currently_dragging and self.dragging_timer == 0):
self.lineR.set_data(self.xsR, self.ysR)
#self.lineR.figure.canvas.draw()
if self.mywidgetcolor.value == 'blue':
self.xsB.append(event.xdata)
self.ysB.append(event.ydata)
if (not currently_dragging) or (currently_dragging and self.dragging_timer == 0):
self.lineB.set_data(self.xsB, self.ysB)
#self.lineB.figure.canvas.draw()
#if self.dragging_timer == 0:
# self.lineR.figure.canvas.draw()
def clear(self, button):
if self.trained == False:
with self.my_out:
print('can only reset if trained')
return
with self.my_out:
print('resetted the widget')
self.trained = False
self.remove_decision_boundary()
self.C1 = None
self.C2 = None
self.model = None
self.xsR = []
self.ysR = []
self.xsB = []
self.ysB = []
self.lineR.set_data(self.xsR, self.ysR)
self.lineB.set_data(self.xsB, self.ysB)
self.lineB.figure.canvas.draw()
self.lineR.figure.canvas.draw()
def export(self):
dataR = np.array([self.xsR,self.ysR]).transpose()
dataB = np.array([self.xsB,self.ysB]).transpose()
yR = np.ones((dataR.shape[0], 1))
yB = -np.ones((dataB.shape[0], 1))
X = np.concatenate((dataR,dataB))
y = np.concatenate((yR,yB))
y = np.reshape(y,y.shape[0])
return (X,y)
def train(self, button):
self.my_out.clear_output()
if len(self.xsR) < 1 or len(self.xsB) < 1:
with self.my_out:
print('need at least one object in both classes to train')
return
(X,y) = self.export()
if self.need_seperable:
C = float('inf')
else:
C = self.widgetC.value
model = sklearn.svm.LinearSVC(loss='hinge',C=C)
model.fit(X,y)
if self.need_seperable:
acc = model.score(X,y)
if acc < 0.99999:
with self.my_out:
print('this dataset is not seperable')
return
self.remove_decision_boundary()
train_error = model.score(X,y)
(C1, C2) = plot_decision_boundary_margin(X,y,model)
self.C1 = C1
self.C2 = C2
self.model = model
self.trained = True
with self.my_out:
if self.need_seperable:
print('trained hard margin SVM')
else:
print('trained soft margin SVM with C %f' % C)
def init(need_seperable = True):
# Turn off interactivity, for now
plt.ioff()
fig = plt.figure(figsize = (4,4))
ax = fig.add_subplot(111)
# Make some nice axes
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.set_title('click to add points')
ax.set_xlabel('Feature 1')
ax.set_ylabel('Feature 2')
# Remove some stuff from the backend
#fig.canvas.toolbar_visible = False # Hide toolbar
#fig.canvas.header_visible = False # Hide the Figure name at the top of the figure
#fig.canvas.footer_visible = False
#fig.canvas.resizable = False
# These items will contain the objects
lineR, = ax.plot([], [], linestyle="none", marker="s", color="g", markersize=10)
lineB, = ax.plot([], [], linestyle="none", marker="^", color="b", markersize=10)
# Make the GUI
w_clear = widgets.Button(
description='Clear all',
disabled=False,
button_style='danger', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Remove all data and start from scratch',
icon='check' # (FontAwesome names without the `fa-` prefix)
)
w_color = widgets.ToggleButtons(
options=['green', 'blue'],
description='Class:',
disabled=False,
button_style='', # 'success', 'info', 'warning', 'danger' or ''
tooltips=['Description of slow', 'Description of regular'],
# icons=['check'] * 3
)
if not need_seperable:
w_C = widgets.FloatLogSlider(
value=1,
base=10,
min=-10, # max exponent of base
max=10, # min exponent of base
step=0.2, # exponent step
#description='Log Slider',
description='C:',
continuous_update=False,
orientation='horizontal',
readout=True,
#readout_format='.2f',
)
else:
w_C = None
w_train = widgets.Button(
description='Train SVM',
disabled=False,
button_style='warning', # 'success', 'info', 'warning', 'danger' or ''
tooltip='...',
icon='check' # (FontAwesome names without the `fa-` prefix)
)
out = widgets.Output(layout={'border': '1px solid black'})
out.layout.height = '40px'
out.layout.width = '600px'
if need_seperable:
b1 = widgets.HBox([w_color,w_train])
bar = widgets.VBox([b1, out])
else:
b1 = widgets.HBox([w_color,w_C,w_train])
#b2 = widgets.HBox([w_train,w_C])
bar = widgets.VBox([b1, out])
linebuilder = LineBuilder2(lineR, lineB, w_color, w_C, out, need_seperable)
w_clear.on_click(linebuilder.clear)
w_train.on_click(linebuilder.train)
# Turn interactivity back on
plt.ion()
out = fig
ui = bar
return display(ui, out)
To start the interactivity, I use the following in a Jupyter notebook:
%matplotlib notebook
from svm_helper import init
init()
So far, I've found adding the following code (from here) in a cell above the cell you have beginning with %matplotlib notebook works:
%%html
<style>
.output_wrapper button.btn.btn-default,
.output_wrapper .ui-dialog-titlebar {
display: none;
}
</style>
Maybe not ideal since instead of explaining to your students to just ignore the buttons, you have to explain why they have to run this, but it's something.
When I run this code I get this error: ax = plt.subplot("311")
MatplotlibDeprecationWarning: Passing non-integers as three-element position specification is deprecated since 3.3 and will be removed two minor releases later. I made those changes that was suggested now I get errors in other parts of the code.
I have provided a small snippet of the code
Any suggestions on how I can fix this
import socket, traceback
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from scipy.signal import butter, lfilter,iirfilter,savgol_filter
import math
import pylab
from pylab import *
import time
import numpy as np
host = ''
port = 5555
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind((host, port))
# lists for plotting
Ax = [0.0] * 50
Ay = [0.0] * 50
Az = [0.0] * 50
G = [0.0] * 50
x = [i for i in range(len(Ax))]
#used for debugging
fig = plt.figure(figsize=(16,10))
# raw data
ax = plt.subplot(311)
ax.set_xlim(0, 50)
ax.set_ylim(-2, 2)
ax.set_title("Raw acceleration data")
ax.set_ylabel("g$/m^2$",fontsize=18)
line = ax.plot(Ax,label='Acc x')[0]
line2 = ax.plot(Ay,label='Acc y')[0]
line3 = ax.plot(Az,label='Acc z')[0]
# filtered data
ax2 = plt.subplot(312)
ax2.set_xlim(0, 50)
ax2.set_ylim(-2, 2)
ax2.set_title(" acceleration data")
ax2.set_ylabel("g$/m^2$",fontsize=18)
f_line = ax2.plot(Ax,label='Acc x')[0]
f_line2 = ax2.plot(Ay,label='Acc y')[0]
f_line3 = ax2.plot(Az,label='Acc z')[0]
# tilt angle plot
ax3 = plt.subplot(313)
ax3.set_ylim([-180,180])
ax3.set_title("Tilt Angles")
ax3.set_ylabel("degrees",fontsize=18)
t_line = ax3.plot(G)[0]
fig.suptitle('Three-axis accelerometer streamed from Sensorstream',fontsize=18)
plt.show(False)
plt.draw()
# cache the background
background = fig.canvas.copy_from_bbox(fig.bbox)
count = 0
print("Success binding")
while 1:
# time it
tstart = time.time()
message, address = s.recvfrom(8192)
messageString = message.decode("utf-8")
Acc = messageString.split(',')[2:5]
Acc = [float(Acc[i])/10.0 for i in range(3)]
# appending and deleting is order 10e-5 sec
Ax.append(Acc[0])
del Ax[0]
Ay.append(Acc[1])
del Ay[0]
Az.append(Acc[2])
del Az[0]
G.append(np.sqrt(Ax[-1]**2 + Ay[-1]**2 + Az[-1]**2))
del G[0]
# filter
acc_x_savgol = savgol_filter(Ax, window_length=5, polyorder=3)
acc_y_savgol = savgol_filter(Ay, window_length=5, polyorder=3)
acc_z_savgol = savgol_filter(Az, window_length=5, polyorder=3)
tilt_angles = []
for i,val in enumerate(G):
angle = math.atan2(Ax[i], -1*Ay[i]) * (180 / math.pi)
if (math.isnan(angle)):
tilt_angles.append(0)
else:
tilt_angles.append(angle)
print(Ax[0],Ay[1],Az[2])
line.set_xdata(x)
line.set_ydata(Ax)
line2.set_xdata(x)
line2.set_ydata(Ay)
line3.set_xdata(x)
line3.set_ydata(Az)
ax.set_xlim(count, count+50)
f_line.set_xdata(x)
f_line.set_ydata(acc_x_savgol)
f_line2.set_xdata(x)
f_line2.set_ydata(acc_y_savgol)
f_line3.set_xdata(x)
f_line3.set_ydata(acc_z_savgol)
ax2.set_xlim(count, count+50)
t_line.set_xdata(x)
t_line.set_ydata(tilt_angles)
ax3.set_xlim(count, count+50)
# restore background
fig.canvas.restore_region(background)
# redraw just the points
ax.draw_artist(line)
ax.draw_artist(line2)
ax.draw_artist(line3)
ax2.draw_artist(f_line)
ax2.draw_artist(f_line2)
ax2.draw_artist(f_line3)
ax3.draw_artist(t_line)
# fill in the axes rectangle
fig.canvas.blit(fig.bbox)
count+=1
x = np.arange(count,count+50,1)
# tops out at about 25 fps :|
print ("Total time for 1 plot is: ",(time.time() - tstart))
The following code executes without warnings on my laptop.
ax = plt.subplot(311)
ax.set_xlim(0, 50)
ax.set_ylim(-2, 2)
ax.set_title("Raw acceleration data")
ax.set_ylabel("g$/m^2$",fontsize=18)
I removed ax.hold(True) and changed "311" to 311.
I am currently working on a Yee Solver script for uni, but when I try to animate my 3D graph, the graph is not what is expected. It works for a 2D plot, but I can't seem to translate that into 3D. From my understanding, set_data and set_3d_properties need a 1D array to work, which I am inputting.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.widgets import Slider
# Program Variables
wv_lgth_num = 10
graph_type = '3d'
t = 0
# Physical Constants
c = 3e8
mu_r = 1
eps_r = 1
# Source Constants
f = 2e9
omega = 2*f*(np.pi)
amp = 1.0
wv_lgth = c/f
period = 1/f
# Step size
dz = wv_lgth/20
dt = ((c/f)/20)/c
#dt = ((1/f)/20)/1
# Axis Grids
z_grid = np.arange(0,wv_lgth_num*wv_lgth,dz)
t_grid = np.arange(0,10*period,dt)
# Number of steps
num_z = z_grid.size
num_t = t_grid.size
# Coefficients
coe_E = c*dt/(eps_r*dz)
coe_H = c*dt/(mu_r*dz)
# E and H Matricies
E_mat = np.zeros((num_z,num_t))
H_mat = np.zeros((num_z,num_t))
# Generating Values for E and H
for time in range(0,num_t-1):
for pos in range(0,num_z-1):
# Source Wave
if pos == 0:
H_mat[0,time] = amp*np.sin(omega*t_grid[time])
# All cases of Yee Solver
if pos == 1:
if time == 0:
H_mat[1,0] = 0
E_mat[0,0] = 0
else:
H_mat[1,time] = H_mat[1,time-1] + coe_H*(E_mat[1,time-1] - E_mat[0,time-1])
E_mat[0,time] = E_mat[0,time-1] + coe_E*(H_mat[1,time] - H_mat[0,time])
if pos > 1 and pos != num_z-1:
if time == 0:
H_mat[pos,0] = 0
E_mat[pos-1,0] = 0
if time > 0:
H_mat[pos,time] = H_mat[pos,time-1] + coe_H*(E_mat[pos,time-1] - E_mat[pos-1,time-1])
E_mat[pos-1,time] = E_mat[pos-1,time-1] + coe_E*(H_mat[pos,time] - H_mat[pos-1,time])
if pos == num_z-1:
if time == 0:
H_mat[num_z-1,0] = 0
E_mat[num_z-2,0] = 0
E_mat[num_z-1,0] = 0
if time > 0:
H_mat[num_z-1,time] = H_mat[num_z-1,time-1] + coe_H*(E_mat[num_z-1,time-1] - E_mat[num_z-2,time-1])
E_mat[num_z-2,time] = E_mat[num_z-2,time-1] + coe_E*(H_mat[num_z-1,time] - H_mat[num_z-2,time])
E_mat[num_z-1,time] = E_mat[num_z-2,time]
def update(val):
t = slider_time.val
if graph_type == '2d':
a.set_ydata(E_mat[:,t])
b.set_ydata(H_mat[:,t])
if graph_type == '3d':
a.set_3d_properties(E_mat[:,t])
a.set_data(z_grid,np.zeros((num_z,num_t))[:,t])
b.set_3d_properties(np.zeros((num_z,num_t))[:,t])
b.set_data(z_grid,H_mat[:t])
fig.canvas.draw_idle()
print(H_mat)
print(H_mat[:,t].size)
print(z_grid)
print(np.zeros((num_z,num_t))[:,t].size)
# Creating plot
if graph_type == '3d':
fig, ax = plt.subplots()
ax = plt.axes(projection='3d')
b, = ax.plot3D(z_grid,H_mat[:,t],np.zeros((num_z,num_t))[:,t], label='H')
a, = ax.plot3D(z_grid,np.zeros((num_z,num_t))[:,t],E_mat[:,t], label='E')
plt.title('Light Wave')
ax.set_xlabel('z')
ax.set_ylabel('x')
ax.set_zlabel('y')
plt.legend()
ax_time = plt.axes([0.25,0.1,0.65,0.03])
slider_time = Slider(ax_time,'Time',0,num_t-2,valinit=0,valstep=1)
slider_time.on_changed(update)
plt.show()
if graph_type == '2d':
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
a, = plt.plot(z_grid,E_mat[:,t], label='E (yz plane)')
b, = plt.plot(z_grid,H_mat[:,t], label='H (xz plane)')
plt.title('Light Wave')
plt.xlabel('z')
plt.ylabel('x')
plt.legend()
ax_time = plt.axes([0.25,0.1,0.65,0.03])
slider_time = Slider(ax_time,'Time',0,num_t-2,valinit=0,valstep=1)
slider_time.on_changed(update)
plt.show()
Any help would be appreciated. The middle for loop is just generating my functions, using the Yee Method.
I'd like to create a barplot in matplotlib:
fig, ax = plt.subplots()
oldbar = ax.bar(x=ind, height=y, width=width)
I'd then like to pickle this barplot to file (either the dictionary or the axes - I'm not sure which is correct):
pickle.dump(oldbar, file('oldbar.pkl', 'w'))
I'd then like to reload this file, and then plot the old bar onto alongside a new bar plot, so I can compare them on a single axes:
fig, ax = plt.subplots()
newbar = ax.bar(x=ind, height=y, width=width)
oldbar = pickle.load(file('oldbar.pkl'))
# I realise the line below doesn't work
ax.bar(oldbar)
plt.show()
Ideally, I'd then like to present them as below. Any suggestions of how I might go about this?
You would pickle the figure instead the artists in it.
import matplotlib.pyplot as plt
import numpy as np
import pickle
ind = np.linspace(1,5,5)
y = np.linspace(9,1,5)
width = 0.3
fig, ax = plt.subplots()
ax.bar(x=ind, height=y, width=width)
ax.set_xlabel("x label")
pickle.dump(fig, file('oldbar.pkl', 'w'))
plt.close("all")
ind2 = np.linspace(1,5,5)
y2 = np.linspace(8,2,5)
width2 = 0.3
fig2 = pickle.load(file('oldbar.pkl'))
ax2 = plt.gca()
ax2.bar(x=ind2+width, height=y2, width=width2, color="C1")
plt.show()
However pickling the data itself may make more sense here.
import matplotlib.pyplot as plt
import numpy as np
import pickle
ind = np.linspace(1,5,5)
y = np.linspace(9,1,5)
width = 0.3
dic = {"ind":ind, "y":y, "width":width}
pickle.dump(dic, file('olddata.pkl', 'w'))
### new data
ind2 = np.linspace(1,5,5)
y2 = np.linspace(8,2,5)
width2 = 0.3
olddic = pickle.load(file('olddata.pkl'))
fig, ax = plt.subplots()
ax.bar(x=olddic["ind"], height=olddic["y"], width=olddic["width"])
ax.bar(x=ind2+olddic["width"], height=y2, width=width2)
ax.set_xlabel("x label")
plt.show()
Maybe this will help:
import pickle as pkl
import matplotlib.pyplot as plt
import numpy as np
class Data_set(object):
def __init__(self, x=[], y=[], name='data', pklfile=None,
figure=None, axes=None):
"""
"""
if pklfile is None:
self.x = np.asarray(x)
self.y = np.asarray(y)
self.name = str(name)
else:
self.unpickle(pklfile)
self.fig = figure
self.ax = axes
self.bar = None
def plot(self, width=0, offset=0, figure=None, axes=None):
if self.fig is None:
if figure is None:
self.fig = plt.figure()
self.ax = self.fig.subplots(1, 1)
else:
self.fig = figure
if axes is None:
self.ax = self.fig.subplots(1, 1)
else:
self.ax = axes
# maybe there's no need to keep track of self.fig, .ax and .bar,
# but just in case...
if figure is not None:
fig_to_use = figure
if axes is not None:
ax_to_use = axes
else:
ax_to_use = fig_to_use.subplots(1, 1)
else:
fig_to_use = self.fig
ax_to_use = self.ax
if not width:
width = (self.x[1]-self.x[0]) / 2.
self.bar = ax_to_use.bar(x=self.x+offset, height=self.y, width=width)
return fig_to_use, ax_to_use, self.bar
def pickle(self, filename='', ext='.pkl'):
if filename == '':
filename = self.name
with open(filename+ext, 'w') as output_file:
pkl.dump((self.name, self.x, self.y), output_file)
def unpickle(self, filename='', ext='.pkl'):
if filename == '':
filename = self.name
with open(filename + ext, 'r') as input_file:
# the name should really come from the filename, but then the
# above would be confusing?
self.name, self.x, self.y = pkl.load(input_file)
class Data_set_manager(object):
def __init__(self, datasets={}):
self.datasets = datasets
def add_dataset(self, data_set):
self.datasets[data_set.name] = data_set
def add_dataset_from_file(self, filename, ext='.pkl'):
self.datasets[filename] = Data_set(name=filename)
self.datasets[filename].unpickle(filename=filename, ext=ext)
def compare(self, width=0, offset=0, *args):
self.fig = plt.figure()
self.ax = self.fig.subplots(1, 1)
if len(args) == 0:
args = self.datasets.keys()
args.sort()
n = len(args)
if n == 0:
return None, None
if width == 0:
min_dx = None
for dataset in self.datasets.values():
sorted_x = dataset.x.copy()
sorted_x.sort()
try:
new_min_dx = np.min(dataset.x[1:] - dataset.x[:-1])
except ValueError:
# zero-size array to reduction operation minimum which
# has no identity (empty array)
new_min_dx = None
if new_min_dx < min_dx or min_dx is None:
min_dx = new_min_dx
if min_dx is None:
min_dx = 1.
width = float(min_dx) / (n + 1)
offset = float(min_dx) / (n + 1)
offsets = offset*np.arange(n)
if n % 2 == 0:
offsets -= offsets[n/2] - offset/2.
else:
offsets -= offsets[n/2]
i = 0
for name in args:
self.datasets.get(name, Data_set()).plot(width=width,
offset=offsets[i],
figure=self.fig,
axes=self.ax)
i += 1
self.ax.legend(args)
return self.fig, self.ax
if __name__ == "__main__":
# test saving/loading
name = 'test'
to_pickle = Data_set(x=np.arange(10),
y=np.random.rand(10),
name=name)
to_pickle.pickle()
unpickled = Data_set(pklfile=name)
print unpickled.name == to_pickle.name
# test comparison
blorg = Data_set_manager({})
x_step = 1.
n_bars = 4 # also try an odd number
for n in range(n_bars):
blorg.add_dataset(Data_set(x=x_step * np.arange(n_bars),
y=np.random.rand(n_bars),
name='teste' + str(n)))
fig, ax = blorg.compare()
fig.show()
It should work with both even and odd number of bars:
And as long as you keep a record of the names you've used (tip:look in the folder where you are saving them) you can reload the data and compare it with the new one.
More checks could be made (to make sure the file exists, that the x axis is something that can be subtracted before trying to do so, etc.), and it could also use some documentation and proper testing - but this should do in a hurry.
i am new to python. I am writing a data logging program and want to open a serial port, then read from it.
My problem is that calling this in my main():
# Handle serial
ser = serial.Serial(strPort, 9600)
doesnt allow me to invoke methods on the ser handle in other functions. Should i make a class or whats the best approach?
Here is the error message:
line 164, in updateData
line = ser.readline().rstrip()
NameError: name 'ser' is not defined
Here is the code:
# Uncomment the next two lines if you want to save the animation
import matplotlib
# matplotlib.use("Agg")
import sys
import serial
import argparse
import csv
import numpy
from matplotlib.pylab import *
from mpl_toolkits.axes_grid1 import host_subplot
import matplotlib.animation as animation
'''
ax03.legend([p031,p032], [p031.get_label(),p032.get_label()])
'''
def main():
# Sent for figure
font = {'size': 9}
matplotlib.rc('font', **font)
# Setup figure and subplots
f0 = figure(num=0, figsize=(12, 10)) # , dpi = 100)
f0.suptitle("Sensor Data", fontsize=12)
ax01 = subplot2grid((3, 2), (1, 0))
#ax02 = subplot2grid((4, 2), (1, 1))
ax02 = ax01.twinx()
ax03 = subplot2grid((3, 2), (0, 0), colspan=2, rowspan=1)
ax04 = subplot2grid((3, 2), (1, 1))
ax05 = subplot2grid((3, 2), (2, 0))
ax06 = subplot2grid((3, 2), (2, 1))
subplots_adjust(left=None, bottom=None, right=None,
top=None, wspace=0.3, hspace=0.3)
# Set titles of subplots
ax01.set_title('Heart Rate Quality and Sample Frequency')
ax03.set_title('Heart Rate')
ax04.set_title('Step Rate')
ax05.set_title('Perfusion Index')
ax06.set_title('Raw PPG')
# set y-limits
ax01.set_ylim(-1, 5)
ax02.set_ylim(100, 500)
ax03.set_ylim(30, 140)
ax04.set_ylim(-50, 250)
ax05.set_ylim(0, 500)
ax06.set_ylim(0, 50000)
# sex x-limits
ax01.set_xlim(0, 50.0)
# ax02.set_xlim(0,50.0)
ax03.set_xlim(0, 50.0)
ax04.set_xlim(0, 50.0)
ax05.set_xlim(0, 50.0)
ax06.set_xlim(0, 50.0)
# Turn on grids
ax01.grid(True)
# ax02.grid(True)
ax03.grid(True)
ax04.grid(True)
ax05.grid(True)
ax06.grid(True)
# set label names
ax01.set_xlabel("t[s]")
ax01.set_ylabel("HRQ")
ax02.set_xlabel("t[s]")
ax02.set_ylabel("samples[Hz]")
ax03.set_xlabel("t[s]")
ax03.set_ylabel("bpm")
ax04.set_xlabel("t[s]")
ax04.set_ylabel("Steps/min")
ax05.set_xlabel("t[s]")
ax05.set_ylabel("AC/DC ratio")
ax06.set_xlabel("t[s]")
ax06.set_ylabel("raw PS sample")
# Data Placeholders
t = zeros(0)
hr = zeros(0)
HRQ = zeros(0)
Fs = zeros(0)
stepRate = zeros(0)
ADCGain = zeros(0)
pI = zeros(0)
rawPPG = zeros(0)
ser = zeros(0)
# set plots
p011, = ax01.plot(t, HRQ, 'c-', label="HRQ", linewidth=2)
p021, = ax02.plot(t, ADCGain, 'r-', label="Sample Frequency", linewidth=2)
p031, = ax03.plot(t, hr, 'b-', label="Heart Rate", linewidth=2)
p041, = ax04.plot(t, stepRate, 'b-', label="Step Rate", linewidth=2)
p051, = ax05.plot(t, pI, 'y-', label="Perfusion Index", linewidth=2)
p061, = ax06.plot(t, rawPPG, 'g-', label="Raw PPG", linewidth=2)
# set lagends
ax01.legend([p011, p021], [p011.get_label(), p021.get_label()], loc=2)
#ax02.legend([p021], [p021.get_label()])
ax03.legend([p031], [p031.get_label()], loc=2)
ax04.legend([p041], [p041.get_label()], loc=2)
ax05.legend([p051], [p051.get_label()], loc=2)
ax06.legend([p061], [p061.get_label()], loc=2)
# Data Update
xmin = 0.0
xmax = 50.0
x = 0.0
# create parser
parser = argparse.ArgumentParser(description="LDR serial")
# add expected arguments
parser.add_argument('--port', dest='port', required=True)
# parse args
args = parser.parse_args()
#strPort = '/dev/tty.usbserial-A7006Yqh'
strPort = args.port
print('reading from serial port %s...' % strPort)
# Handle serial
ser = serial.Serial(strPort, 9600)
print('plotting data...')
# Logfile writer
# open('test.csv','w') as csvfile
#logwriter = csv.writer(csvfile, delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL)
# interval: draw new frame every 'interval' ms
# frames: number of frames to draw
simulation = animation.FuncAnimation(
f0, updateData, blit=False, frames=1000, interval=100, repeat=True)
# Uncomment the next line if you want to save the animation
# simulation.save(filename='sim.mp4',fps=1,dpi=300)
plt.show()
def updateData(self):
global x
global t
global ser
global hr
global HRQ
global Fs
global ADCGain
global stepRate
global pI
global rawPPG
try:
line = ser.readline().rstrip()
data = [float(val) for val in line.split()]
# print data
print(data)
if(len(data) == 9):
# log data
for i in range(len(data)):
out_string = ""
out_string += str(data[i])
# logwriter.writerow(out_string)
# update variables
tmpT = data[0]
tmpFs = data[1]
tmpStepRate = data[2]
tmpHr = data[3]
tmpPI = data[4]
tmpHRQ = data[5]
tmpRawPPG = data[6]
except KeyboardInterrupt:
print('exiting')
hr = append(hr, tmpHr)
HRQ = append(HRQ, tmpHRQ)
Fs = append(Fs, tmpFs)
stepRate = append(stepRate, tmpStepRate)
pI = append(pI, tmpPI)
rawPPG = append(rawPPG, tmpRawPPG)
t = append(t, x)
x += 1
p011.set_data(t, HRQ)
p021.set_data(t, Fs)
p031.set_data(t, hr)
p041.set_data(t, stepRate)
p051.set_data(t, pI)
p061.set_data(t, rawPPG)
if x >= xmax - 10.00:
p011.axes.set_xlim(x - xmax + 10.0, x + 10.0)
p021.axes.set_xlim(x - xmax + 10.0, x + 10.0)
p031.axes.set_xlim(x - xmax + 10.0, x + 10.0)
p041.axes.set_xlim(x - xmax + 10.0, x + 10.0)
p051.axes.set_xlim(x - xmax + 10.0, x + 10.0)
p061.axes.set_xlim(x - xmax + 10.0, x + 10.0)
return p011, p021, p031, p041, p051, p061
# Call main
if __name__ == '__main__':
main()