我有一些简单的python代码,它可以生成实时监视器,用于查看每秒钟发出的数据。它使用matplotlib并且工作得很好,除了存在内存泄漏。脚本的内存使用量在一天中缓慢上升,似乎没有约束。我当然是编程python的新手,所以想知道是否有人能看到我正在做的事情显然很糟糕。提前感谢您的帮助。
import time
import numpy as np
import matplotlib
from matplotlib import figure
import matplotlib.pyplot as plt
import pylab as p
import os
import subprocess as sp
from subprocess import Popen, PIPE
def main():
#####Initialize the plot#####
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1,axisbg='black') #Set up basic plot attributes
ax1.set_title('Blip Current vs. Time',color='blue')
ax1.set_xlabel('Time (hrs)',color='blue')
ax1.set_ylabel('Blip Current',color='blue')
for t in ax1.xaxis.get_ticklines(): t.set_color('yellow')
for t in ax1.xaxis.get_ticklabels(): t.set_color('yellow')
for t in ax1.yaxis.get_ticklines(): t.set_color('white')
for t in ax1.yaxis.get_ticklabels(): t.set_color('purple')
plt.ion() #Set interactive mode
plt.show(False) #Set to false so that the code doesn't stop here
i=0 #initialize counter variable (this will help me to limit the number of points displayed on graph
###Update the plot continuously###
while True: #This is a cheap trick to keep updating the plot, i.e. create a real time data monitor
blip=Popen('adoIf -vo -6 lxf.blip_b3 dataBarM', shell=True, stdout=PIPE).communicate()[0] #Get data to plot
hr=float(time.strftime('%H'))
mins=time.strftime('%M')
secs=time.strftime('%S')
secadj=float(secs)/3600
minadj=float(mins)/60
currenttime=float(hr+minadj+secadj) #Put time into format for easier plotting, i.e. 21.50 for 9:30 pm
if currenttime >= 0 and currenttime < 0.22: #Set x range properly when rolling over to midnight
xmin=0
xmax=currenttime+.01
else:
xmin=currenttime-.22 #Limit data to be displayed, only care about recent past
xmax=currenttime+.01
try:
blip =float(blip) #This throws an error if for some reason the data wasn't received at the top of the while statement
except ValueError:
blip=0.0
if i>300: #Limit displayed points to save memory (hopefully...)
del ax1.lines[0] #After 300 points, start deleting the first point each time
else:
i +=1
if blip > 6: #Plot green points if current is above threshold
ax1.plot(currenttime,blip,marker='o', linestyle='--',c='g')
else: #Plot red points if current has fallen off
ax1.plot(currenttime,blip,marker='o', linestyle='--',c='r')
plt.axis([xmin,xmax,None,None]) #Set xmin/xmax to limit displayed data to a reasonable window
plt.draw()
time.sleep(2) #Update every 2 seconds
if __name__=='__main__':
print 'Starting Monitor'
main()
答案 0 :(得分:2)
我很确定你每次都需要清除这个数字,否则matplotlib将继续创建一大堆新对象,并且不会收集垃圾。尝试类似:
fig.clf()
作为while循环中的第一件事。
答案 1 :(得分:2)
尤里卡!我想通了(好吧,至少是一种解决方法)。我从while循环中取出了ax1.plot命令,而是使用'set_xdata'和'set_ydata'命令以及fig.canvas.draw()命令。感谢大家的帮助,特别是reptilicus指出ax.plot命令每次调用它时都会创建一个新对象。
要绘制的x和y值现在存储在数组中,每个数组中的第一个元素在while循环的每次迭代中都被删除(在绘制一定数量的点之后,其数量在代码使用简单的索引号i)。内存使用率持平,CPU使用率较低。代码如下:
def main():
#####Initialize the plot attributes#####
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1, axisbg='black')#Set up basic plot attributes
ax1.set_title('Blip Current vs. Time',color='blue')
ax1.set_xlabel('Time (hrs)',color='blue')
ax1.set_ylabel('Blip Current',color='blue')
for t in ax1.xaxis.get_ticklines(): t.set_color('yellow')
for t in ax1.xaxis.get_ticklabels(): t.set_color('yellow')
for t in ax1.yaxis.get_ticklines(): t.set_color('white')
for t in ax1.yaxis.get_ticklabels(): t.set_color('purple')
plt.ion() #Set interactive mode
plt.show(False) #Set to false so that the code doesn't stop here
i=0 #initialize counter variable (this will help me to limit the number of points displayed on graph
###Initialize x values####
times=[] #Create blank array to hold x values
hr=float(time.strftime('%H')) #Hours
mins=time.strftime('%M') #Minutes
secs=time.strftime('%S') #Seconds
secadj=float(secs)/3600
minadj=float(mins)/60
currenttime=float(hr+minadj+secadj) #Put time into format for easier plotting, i.e. 21.50 for 9:30 pm
times.append(currenttime) #Add first x value to x value array
if currenttime >= 0 and currenttime < 0.22: #Set x range properly when rolling over to midnight
xmin=0
xmax=currenttime+.01
else:
xmin=currenttime-.22 #Limit data to be displayed, only care about recent past
xmax=currenttime+.01
###Initialize y values###
blipcur=[] #Create blank array to hold y values
blip=Popen('adoIf -vo -6 lxf.blip_b3 dataBarM', shell=True, stdout=PIPE).communicate()[0] #Get first datapoint for plot
try:
blip =float(blip) #This throws an error if for some reason the data wasn't received at the top of the while statement
except ValueError:
blip=0.0
blipcur.append(blip) #Add first y value to y value array
###Initialize plot###
line1, = ax1.plot(times, blipcur, 'g-', marker='o')
###Update the plot continuously###
while True: #This is a cheap trick to keep updating the plot, i.e. create a real time data monitor
hr=float(time.strftime('%H')) #Get new x data for plotting (get current time)
mins=time.strftime('%M')
secs=time.strftime('%S')
secadj=float(secs)/3600
minadj=float(mins)/60
currenttime=float(hr+minadj+secadj) #Put time into format for easier plotting, i.e. 21.50 for 9:30 pm
times.append(currenttime) #Add latest point to x value array
if currenttime >= 0 and currenttime < 0.22: #Set x range properly when rolling over to midnight
xmin=0
xmax=currenttime+.01
else:
xmin=currenttime-.22 #Limit data to be displayed, only care about recent past
xmax=currenttime+.01
blip=Popen('adoIf -vo -6 lxf.blip_b3 dataBarM', shell=True, stdout=PIPE).communicate()[0] #Get new y data for plotting
try:
blip =float(blip) #This throws an error if for some reason the data wasn't received from previous line of code
except ValueError: #Just set to zero if there was some temporary error
blip=0.0
blipcur.append(blip) #Add latest point to y value array
if i>285: #Limit data points shown on graph. Saves memory.
del blipcur[0] #Delete first element in y value array (oldest/first plotted point)
del times[0] #Delete first element in x value array
else:
i +=1 #Only want to keep making number bigger until I get over the threshold for # points I want, then why bother
line1.set_xdata(times) #Set x data for plot from x value array
plt.axis([xmin,xmax,-2,50]) #Set xmin/xmax to limit displayed data to a reasonable window
line1.set_ydata(blipcur) #Set y data for plot from y value array
fig.canvas.draw() #Update plot
time.sleep(2.6) #Update every 2.6 seconds
if __name__=='__main__':
print 'Starting Monitor'
main()
答案 2 :(得分:0)
您的原始代码显示您需要不同颜色的点,具体取决于blip
的值。使用set_data
新解决方案,每种颜色都需要新的Line2D
。另一种方法是使用散点图而不是线图。散点图可以为图中的每个点指定不同的颜色。
如果你想要一个固定大小的列表,比如285个元素,而不是 这样做:
if i>285: #Limit data points shown on graph. Saves memory.
del blipcur[0] #Delete first element in y value array (oldest/first plotted point)
del times[0] #Delete first element in x value array
您可以将collection.deques与maxlen=285
一起使用。使用deques
,您可以满怀希望,deque
会在最旧元素过满时丢弃它。这将使您的代码更简单,因为您不必自己管理它的大小。
deques
也可以在O(1)时间内关闭第一个元素,而lists
在O(n)时间内关闭第一个元素,因此这里也有theoretical performance个增益,虽然只有大约300个元素,但它不会产生显着的差异。
如果你有Matplotlib 1.2或更新版本,你可以使用它的FuncAnimation
类。这将为您处理大部分样板代码。此外,它允许您避免调用plt.ion()
。 (对于复杂的脚本,不建议使用plt.ion()
。)
import numpy as np
import matplotlib.pyplot as plt
import collections
import datetime as DT
import matplotlib.animation as animation
def currenttime():
now = DT.datetime.now()
hr = now.hour
mins = now.minute / 60
secs = now.second / 3600
return float(hr + mins + secs)
def main():
def animate(data, pathcol):
xvals, yvals, colors = data
assert len(xvals)<=300
if len(xvals) > 1:
ax.set_xlim(xvals.min(), xvals.max())
pathcol.set_array(colors)
pathcol.set_offsets(np.column_stack([xvals, yvals]))
def step():
xvals = collections.deque([], maxlen=N)
yvals = collections.deque([], maxlen=N)
colors = collections.deque([], maxlen=N)
fudge = 0
while True:
blip = np.random.random() * 10
xvals.append(currenttime() + fudge)
yvals.append(blip)
colors.append(1 if blip > 6 else 0)
yield np.asarray(xvals), np.asarray(yvals), np.asarray(colors)
# make time go faster
fudge += 0.0001
# Initialize the plot#####
N = 300
fig = plt.figure()
ax = fig.add_subplot(
1, 1, 1, axisbg='black') # Set up basic plot attributes
ax.set_title('Blip Current vs. Time', color='blue')
ax.set_xlabel('Time (hrs)', color='blue')
ax.set_ylabel('Blip Current', color='blue')
for t in ax.xaxis.get_ticklines():
t.set_color('yellow')
for t in ax.xaxis.get_ticklabels():
t.set_color('yellow')
for t in ax.yaxis.get_ticklines():
t.set_color('white')
for t in ax.yaxis.get_ticklabels():
t.set_color('purple')
pathcol = ax.scatter([0,24], [0,10],
c=[0,1], s=100,
cmap=plt.get_cmap('RdYlGn'), vmin=0, vmax=1)
ani = animation.FuncAnimation(
fig, animate, step, interval=20, fargs=(pathcol,))
plt.show()
if __name__ == '__main__':
print 'Starting Monitor'
main()