I'm very early in making a simple graph to plot the temperature and humidity of a given space however, the graph plots both lines but they don't intersect instead they do something like in the image below:
here's the code:
import matplotlib.pyplot as plt
from itertools import count
from matplotlib.animation import FuncAnimation
import serial
import matplotlib as mpl
import numpy as np
x = []
line1 = []
line2 = []
serialPort = serial.Serial(port = "COM3", baudrate=115200,
bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE)
serialString = ""
index = count()
def animate(i):
if serialPort.in_waiting > 0:
serialString = serialPort.readline()
string = serialString.decode('Ascii')
line1.append(string.split(' ')[0])
line2.append(string.split(' ')[1])
x.append(next(index))
print(len(line1))
if len(line1) >= 1000:
line1.pop(0)
line2.pop(0)
x.pop(0)
plt.cla()
plt.plot(x, line1, label='Temperature')
plt.plot(x, line2, label='Humidity')
ani = FuncAnimation(plt.gcf(), animate, interval=400)
plt.tight_layout()
plt.show()
I try to create a live graph with matplotlib animation. below code report error:
UnboundLocalError: local variable 'lines' referenced before assignment
lines is defined above!
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
queue = []
qSize = 20
N = 3
lines = []
xidx = list(range(0, qSize))
def init():
for i in range(qSize):
queue.append([0]*N)
for i in range(N):
xdata = []
ydata = []
line, = ax.plot(xdata, ydata,label=str(i))
lines.append([line,xdata,ydata])
ax.legend(loc=2)
plt.xticks(rotation=45, ha='right')
#plt.subplots_adjust(bottom=0.30)
plt.title('Demo')
plt.ylabel('x')
plt.ylabel('Value')
return
def tick():
arr = []
for i in range(N):
d = random.randint(i*10,i*10+5)
arr.append(d)
queue.append(arr)
lines = lines[-qSize:]
return
def animate(i):
tick()
df = pd.DataFrame(data)
ax.set_xlim(0,qSize)
ax.set_ylim(df.min().min(),df.max().max())
for i,colname in enumerate(df.columns):
line,xdata,ydata = lines[i]
xdata = xidx
ydata = df[colname]
line.set_xdata(xdata)
line.set_ydata(ydata)
plt.draw()
return
init()
ani = animation.FuncAnimation(fig, animate, fargs=(), interval=1000)
plt.show()
I'm using a Raspberry Pi to plot live data from serial, but eventually run out of memory. I'm not sure if/how I can close the figure, but still have a live data display.
Would it be possible to create and close a new figure with every animate?
My code at the moment:
import serial
import matplotlib
# Force matplotlib to not use any Xwindows backend.
matplotlib.use('TkAgg') #comment out for debugging
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import gc
# Create figure for plotting
fig = plt.figure()
xs = []
ysAC = []
ysDC = []
ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
ser.flush()
# This function is called periodically from FuncAnimation
def animate(i, xs, ysAC, ysDC):
values = getValues()
wAC = values[1]
wDC = values[2]
# Add x and y to lists
xs.append(i)
ysAC.append(wAC)
ysDC.append(wDC)
# Limit x and y lists to 10 items
xs = ['T-9','T-8','T-7','T-6','T-5','T-4','T-3','T-2','T-1','Now']
ysDC = ysDC[-10:]
ysAC = ysAC[-10:]
# Draw x and y lists
axRT1.clear()
if len(ysDC) == 10:
lineAC, = axRT1.plot(xs, ysAC, 'b:', label='Mains', linewidth = 4)
lineDC, = axRT1.plot(xs, ysDC, 'g--', label='Solar', linewidth = 4)
gc.collect()
#fig.clf()
#plt.close()
def getValues():
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
return list(line.split(","))
# Set up plot to call animate() function periodically
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ysAC, ysDC), interval=1000, blit=False)
plt.get_current_fig_manager().full_screen_toggle()
plt.ioff()
plt.show()
plt.draw()
The crude way of clearing the plots marked below fixed it for me:
import time
import serial
import datetime as dt
import matplotlib
# Force matplotlib to not use any Xwindows backend.
matplotlib.use('TkAgg') #comment out for debugging
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.animation as animation
from decimal import Decimal
import pandas as pd
import numpy as np
import os.path as path
import re
import gc
import os
count = 0
# Create figure for plotting
fig = plt.figure()
fig.patch.set_facecolor('whitesmoke')
hFont = {'fontname':'sans-serif', 'weight':'bold', 'size':'12'}
xs = ['T-9','T-8','T-7','T-6','T-5','T-4','T-3','T-2','T-1','Now']
ysTemp = []
ysAC = []
ysDC = []
axRT1 = fig.add_subplot(2, 2, 1)
axRT2 = axRT1.twinx() # instantiate a second axes that shares the same x-axis
#Draw x and y lists
axRT1.clear()
axRT2.clear()
axRT1.set_ylim([0, 4])
axRT2.set_ylim([10, 70])
axRT1.set_ylabel('Power Consumption kW', **hFont)
axRT2.set_ylabel('Temperature C', **hFont)
axRT1.set_xlabel('Seconds', **hFont)
axRT1.set_title('Power Consumption and Temperature - Real Time', **hFont)
lineTemp, = axRT2.plot([], [], 'r', label='Temp', linewidth = 4)
lineAC, = axRT1.plot([], [], 'b:', label='Mains', linewidth = 4)
lineDC, = axRT1.plot([], [], 'g--', label='Solar', linewidth = 4)
fig.legend([lineAC, lineDC,lineTemp], ['Mains', 'Solar', 'Temp'], fontsize=20)
ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
ser.flush()
# This function is called periodically from FuncAnimation
def animate(i, xs, ysTemp, ysAC, ysDC):
values = getValues()
if values != 0:
temp_c = Decimal(re.search(r'\d+',values[3]).group())
if temp_c < 0: temp_c = 0
wAC = round(Decimal(re.search(r'\d+', values[1]).group())/1000, 2)
if wAC < 0.35: wAC = 0
aDC = float(re.search(r'\d+', values[2]).group()) #remove characters
vDC = float(re.search(r'\d+', values[4][:5]).group()) #remove characters
wDC = aDC * vDC
wDC = round(abs(Decimal(wDC))/1000, 2)
# Add x and y to lists
ysTemp.append(temp_c)
ysAC.append(wAC)
ysDC.append(wDC)
# Limit x and y lists to 10 items
ysTemp = ysTemp[-10:]
ysDC = ysDC[-10:]
ysAC = ysAC[-10:]
if len(ysTemp) == 10:
axRT2.lines = [] #This crude way of clearing the plots worked
axRT1.lines =[] #This crude way of clearing the plots worked
lineTemp, = axRT2.plot(xs, ysTemp, 'r', label='Temp', linewidth = 4)
lineAC, = axRT1.plot(xs, ysAC, 'b:', label='Mains', linewidth = 4)
lineDC, = axRT1.plot(xs, ysDC, 'g--', label='Solar', linewidth = 4)
def getValues():
measureList = 0
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').rstrip()
print(line)
if line.count(',') == 4:
measureList = list(line.split(","))
return measureList
# Set up plot to call animate() function periodically
ani = animation.FuncAnimation(fig, animate, fargs=(xs, ysTemp, ysAC, ysDC), interval=1000, blit=False)
plt.get_current_fig_manager().full_screen_toggle()
plt.ioff()
plt.show()
plt.draw()
I have been trying to create a live plot by matplotlib.
My trial code is this.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import time
from PySide import QtCore
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
N=100
def animate(j):
graph_data = open('example.txt','r').read()
lines = graph_data.split('\n')
xs=[]
ys=[]
for line in lines:
if len(line) > 1:
x,y = line.split(',')
xs.append(float(x))
ys.append(float(y))
ax.clear()
ax.plot(xs, ys)
def initFile():
fid = open('example.txt','w')
fid.write('')
fid.close()
for i in range(0,N):
fid = open('example.txt', 'a')
fid.write(str(i) + ',' + str(0) + '\n')
fid.close()
def updateFile():
global wThread
wThread = writeThread()
wThread.start()
class writeThread(QtCore.QThread):
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
self.exiting = False
def run(self):
i=0
while 1:
fid = open('example.txt', 'a')
fid.write(str(N+i) + ',' + str(np.sin(2*np.pi*0.05*i)) + '\n')
time.sleep(0.1)
i=i+1
fid.close()
initFile()
updateFile()
ani = animation.FuncAnimation(fig, animate, interval = 200)
plt.show()
It works well. But, the plot points are accumulated. I want to keep up the number of points in my plot as N.
How could I do that?
Simply restrict the size of your array/list to the last N points:
def animate(j):
(...)
ax.clear()
ax.plot(xs[-N:], ys[-N:])
I want to plot different lines using a class method. In order to plot a line. Similar to Passing plots out of a class I use this class to plot a line
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
class Plotter(object):
def __init__(self, xval=None, yval=None, dim1=None, dim2=None):
self.xval = xval
self.yval = yval
self.dim1 = dim1
self.dim2 = dim2
def plotthing(self):
fig, ax = plt.subplots(figsize=(self.dim1,self.dim2))
ax.plot(self.xval, self.yval, 'o-')
return fig
app = Plotter(xval=range(0,10), yval=range(0,10), dim1=5, dim2=5)
plot = app.plotthing()
However I would like to plot different curves in the same plot and define a function inside the class to do so.
Xval = []
Yval = []
xval=range(0,10)
yval=range(0,10)
Xval.append(xval)
Yval.append(yval)
xval=range(0,10)
yval=np.sin(range(0,10))
Xval.append(xval)
Yval.append(yval)
How can I define a function to pass to plotthing?
class Plotter(object):
def __init__(self, xval=None, yval=None, dim1=None, dim2=None):
self.xval = xval
self.yval = yval
self.dim1 = dim1
self.dim2 = dim2
def function_do_plot(x, y):
do something
def plotthing(self):
fig, ax = plt.subplots(figsize=(self.dim1,self.dim2))
for i in range(0, len(self.xval)):
x = xval[i]
y = yval[i]
fig = function_do_plot(x, y)
return fig
Consider looping through your lists of inputs without needing a separate method:
class Plotter(object):
def __init__(self, xval=None, yval=None, dim1=None, dim2=None):
self.xval = xval # LIST
self.yval = yval # LIST
self.dim1 = dim1
self.dim2 = dim2
def plotthing(self):
fig, ax = plt.subplots(figsize=(self.dim1,self.dim2))
for i, j in zip(self.xval, self.yval):
ax.plot(i, j, 'o-')
return fig
# POPULATE LIST OF RANGES
Xval = []; Yval = []
xval = range(0,10)
yval = range(0,10)
Xval.append(xval)
Yval.append(yval)
xval = range(0,10)
yval = np.sin(range(0,10))
Xval.append(xval)
Yval.append(yval)
# PASS IN LIST OF RANGES
app = Plotter(xval=Xval, yval=Yval, dim1=5, dim2=5)
plot = app.plotthing()