2013-07-08 31 views
0

我有一些簡單的python代碼,可以爲每秒查看數據而生成實時監視器。它使用matplotlib,工作得很好,除了有內存泄漏。腳本的內存使用情況在一天內緩慢上升,似乎沒有限制。我承認編程python是新手,所以想知道是否有人能看到我正在做的事情,顯然是很糟糕的。預先感謝您的幫助。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() 
+1

你爲什麼要使用一個字符串(使用plt.ion()不推薦用於複雜的腳本。)''的true''而不是布爾TRUE;? – user2357112

+0

因爲我是新手。謝謝,我改變了它。但我懷疑這與我的記憶問題無關。 – crownjbl

+0

你在Linux,Windows,Mac上運行嗎?子進程處理對每個處理都有點不同。 – ixe013

回答

1

我敢肯定,你需要每次都清除圖形,否則matplotlib將繼續創建一大堆新對象,並且事物不會被垃圾收集。嘗試是這樣的:

fig.clf() 

爲while循環中的第一件事情。

+0

謝謝,但我認爲這不會起作用,因爲要保持實時圖顯示過去〜20分鐘的數據可見。如果我添加,那麼我只是得到一個空白圖。 – crownjbl

+0

我試圖在使用del ax1.lines [0]繪製300點後刪除點,該點用於擺脫點(即從圖中消失),但不知何故數據似乎仍然堆積如山。 – crownjbl

+0

那麼如果你不清除這個圖形,那麼matplotlib每次你做一個ax.plot()就會創建一個新的對象。隨着過程的繼續,這將導致內存使用量的增加。 – reptilicus

1

尤里卡!我想通了(至少,一個解決方法)。我從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() 
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.dequesmaxlen=285deques,你可以追加到你的心臟的喜悅和deque將放棄最舊的元素,當它太滿。這會讓你的代碼更簡單一些,因爲你不必自己管理它的大小。 deques也可以在彈出的第一個元素在關O(1)時間,而在lists O(n)的時間彈出的第一個元素了,所以有theoretical performance增益也在這裏,雖然只有約300個元素是不會做出有顯着差異。


如果你有Matplotlib 1.2版或更新版本,您可以使用它FuncAnimation類。這將爲您處理大部分樣板代碼。此外,它可以讓你避免調用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() 
+0

謝謝!很多好的建議。不幸的是,我無法訪問我們工作系統上的動畫。但願我做了,可惜我沒有;你給我的例子代碼絕對是更清潔。我將結合您建議的其他更改,感謝提示。在我的編程方法中,我承認有點野蠻(不是很有經驗),並且感謝有人向我展示了一種更好的方法來完成某件事。 – crownjbl