2016-10-04 53 views
1

我只是將腳趾浸入Python中,並且在這裏獲得了一個實時更新Matplotlib圖的幫助。該方案採用動畫從一個動態增長的CSV文件中提取數據,看起來像下面的數據:Python數據顯示圖

TimeStamp, ReadCount, Antenna, Protocol, RSSI, EPC, Sensor 
09/28/2016 17:34:28.967, 5686, 2, GEN2, -25, E036115348A908CB, 23.16,0.00,0.00, (Infinity%), 
09/28/2016 17:34:29.716, 5687, 2, GEN2, -32, E036115348A908CB, (Infinity%), 
09/28/2016 17:34:31.155, 5689, 2, GEN2, -27, E036115348A908CB, 22.74,3.38, (Infinity%), 
09/28/2016 17:34:32.351, 5692, 2, GEN2, -25, E036115348A908CB, 22.95,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:32.895, 5695, 2, GEN2, -23, E036115348A908CB, 22.95,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:33.397, 5698, 2, GEN2, -21, E036115348A908CB, 23.78,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:33.946, 5699, 2, GEN2, -23, E036115348A908CB, 23.57,0.00,3.38, (Infinity%), 
09/28/2016 17:34:34.912, 5702, 2, GEN2, -27, E036115348A908CB, 23.36,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:35.394, 5705, 2, GEN2, -25, E036115348A908CB, 23.36,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:35.988, 5707, 2, GEN2, -23, E036115348A908CB, 23.78,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:36.489, 5710, 2, GEN2, -21, E036115348A908CB, 23.99,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:37.269, 5712, 2, GEN2, -23, E036115348A908CB, 23.78,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:37.796, 5715, 2, GEN2, -18, E036115348A908CB, 23.78,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:38.296, 5718, 2, GEN2, -7, E036115348A908CB, 22.32,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:38.826, 5721, 2, GEN2, -7, E036115348A908CB, 23.57,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:39.320, 5724, 2, GEN2, -30, E036115348A908CB, 23.36,0.00,0.00,3.38, (Infinity%), 
09/28/2016 17:34:39.870, 5727, 2, GEN2, -9, E036115348A908CB, 23.36,0.00,0.00,3.38, (Infinity%), 

這個數據是從傳感器進來,我想能夠拉/顯示一些此數據與此圖形一起顯示在其他值中。對於情節的代碼如下所示:

import matplotlib 
import matplotlib.pyplot as plt 
import matplotlib.animation as animation 

from datetime import datetime, timedelta 
import collections 
import csv 

offset = 16 
slope = -.2081 

def plot(ax, data, colour, width): 
    if data: 
     last_dt = data[0][0] 
     gap = timedelta(seconds=10) 

     x = [] 
     y = [] 

     # Plot groups of data not more than 60 seconds apart 
     for dt, ten in data: 
      if dt <= last_dt + gap: 
       x.append(dt) 
       y.append(ten) 
      else: 
       ax.plot(matplotlib.dates.date2num(x), y, colour, linewidth=width) 
       x = [dt] 
       y = [ten] 

      last_dt = dt 

     ax.plot(matplotlib.dates.date2num(x), y, colour, linewidth=width) 


def animate(i, fig, ax): 
    # Read in the CSV file 
    data = collections.defaultdict(list) 
    fields = ["TimeStamp", "ReadCount", "Antenna", "Protocol", "RSSI", "EPC", "Temp", "Ten", "Powr", "Unpowr", "Inf"] 

    with open('SensorLogFile.csv') as f_input: 
     csv_input = csv.DictReader(f_input, skipinitialspace=True, fieldnames=fields) 
     header = next(csv_input) 

     # Separate the rows based on the Antenna field 
     for row in csv_input: 
      try: 
       data[row['Antenna']].append(
        [datetime.strptime(row['TimeStamp'], '%m/%d/%Y %H:%M:%S.%f'), 
        int(float(row['Ten']) * float(slope) + float(offset))]) 
      except: 
       pass 

    # Drop any data points more than 1.5 mins older than the last entry 

    latest_dt = data[row['Antenna']][-1][0]  # Last entry 
    not_before = latest_dt - timedelta(minutes=.25) 

    for antenna, entries in data.items(): 
     data[antenna] = [[dt, count] for dt, count in entries if dt >= not_before] 

    # Redraw existing axis 
    ax.clear() 

    ax.spines['bottom'].set_color("#5998ff") 
    ax.spines['top'].set_color("#5998ff") 
    ax.spines['left'].set_color("#5998ff") 
    ax.spines['right'].set_color("#5998ff") 

    hfmt = matplotlib.dates.DateFormatter('%m/%d/%Y\n%I:%M:%S %p') 
    ax.xaxis.set_major_formatter(hfmt) 
    fig.autofmt_xdate() 

    plot(ax, data['1'], 'c', 6)  # Antenna 1 
    plot(ax, data['2'], 'r', 6)  # Antenna 2 
    plot(ax, data['3'], 'y', 6)  # Antenna 3 

    ax.grid(True, color='w') 
    plt.ylabel('Tension (lb)', color='w', fontsize=20) 
    plt.title('Spiral 1 Tension', color='w', fontsize=26) 

    ax.tick_params(axis='y', colors='w') 
    ax.tick_params(axis='x', colors='w') 

# ax.set_ylim([21,28]) 

fig = plt.figure(facecolor='#07000d') 
ax = fig.add_subplot(111, axisbg='#07000d') 

ani = animation.FuncAnimation(fig, animate, fargs=(fig, ax), interval=250) 
plt.show() 

我希望能有在上面的圖表和圖形下方的空白區域(圖中的全寬),在那裏我可以把以下(可能有一個框,每一個這些數據的):

  • 「TEMP」(第7每行CSV,只需要轉換爲華氏度)
  • 「時代」(當前時間)
  • 「Overdrive」(未顯示任何與此數據有關的信息,但僅爲將來的佔位符)
  • 「循環」(將是天線1的微小間隙之後先讀的次數)
  • 盒與右下方的圖像 - 爲對例如可以說這個圖像this image here

我已經研究過使用Tkinter並試圖遵循sentdex的video here

但一直沒有能夠得到它的工作。

我處於時間緊縮狀態,無法再單獨對此進行鬥爭 - 需要專業人士的幫助。任何人都可以給我一些方向/指導如何使顯示像我在找什麼?

回答

1

爲了幫助您入門,可能需要先回答一些問題。

  1. 生活是如何生活的?它應該實時更新嗎?在這種情況下,使用FuncAnimation可能是一個壞主意。 答案:是的,實時
  2. 它有多快,也就是說,每秒多少次更新? 答案:每秒更新4次
  3. 是否有更新的觸發器?你知道新數據何時到達嗎? 答:沒有真正的觸發器,但請參閱2.
  4. 您需要多少交互性?它只是應該打開並自行運行的情節,還是需要交互式按鈕/滑塊,以便您選擇部分數據?即使有了一些基本的交互性,我也會建議留在matplotlib中,並使用交互式後端,而不要過多地使用tkinter - 因爲你似乎對此一無所知,並且時間有限。 答案:無需互動
  5. 當涉及到您的數據時,我看到一個問題。看看這些行
    E036115348A908CB, 22.74,3.38, (Infinity%),
    E036115348A908CB, 22.95,0.00,0.00,3.38, (Infinity%),
    似乎有數據不完整的情況?!這是可以避免的嗎?

  6. 情節的時間軸如何?首先,你#Drop any data points more than 1.5 mins older than the last entry然後你#Plot groups of data not more than 60 seconds apart,然後你丟棄任何大於gap = timedelta(seconds=10)的數據。你能告訴我們什麼「數據組」意味着時間軸應該是多長時間?

  7. 你會如何計算number of cycles


結果(到目前爲止): 在這裏,我們走了,這是你可以輕鬆地與matplotlib得到。

enter image description here

import matplotlib 
import matplotlib.pyplot as plt 
import matplotlib.image as mpimg 

from datetime import datetime, timedelta 
import collections 
import csv 

class Anim(): 
    """ 
    This class provides a "live" plot of the contents of a log file in csv format. 
    The class structure makes it easy to separate the plot generation from the 
     frequent updating of the plot. 
    The code is based on a question at stackoverflow 
     http://stackoverflow.com/questions/39858501/python-data-display-with-graph 
    """ 
    def __init__(self): 

     self.offset = 16. 
     self.slope = -.2081 

     self.i = 0 

     self.axisbg = '#07000d' 

     self.fig = plt.figure(figsize=(15,8), facecolor=self.axisbg) 
     self.ax = self.fig.add_subplot(111, axisbg=self.axisbg) 

     [self.ax.spines[wh].set_color("#5998ff") for wh in ['bottom', 'top', 'left', 'right']] 

     self.hfmt = matplotlib.dates.DateFormatter('%m/%d/%Y\n%I:%M:%S %p') 
     self.ax.xaxis.set_major_formatter(self.hfmt) 
     self.fig.autofmt_xdate() 

     #self.framenumber = plt.figtext(0.9, .9, "0", color='w') 
     self.ax.grid(True, color='w') 
     plt.ylabel('Tension (lb)', color='w', fontsize=20) 
     plt.title('Spiral 1 Tension', color='w', fontsize=26) 

     self.ax.tick_params(axis='y', colors='w') 
     self.ax.tick_params(axis='x', colors='w') 

     initialx = [self.stime("09/28/2016 17:34:28.967"),self.stime("09/28/2016 17:34:29.716") ] 
     initialy = [0,0 ] 

     self.line1, = self.ax.plot(matplotlib.dates.date2num(initialx), initialy, color="c", linewidth=6) 
     self.line2, = self.ax.plot(matplotlib.dates.date2num(initialx), initialy, color="r", linewidth=6) 
     self.line3, = self.ax.plot(matplotlib.dates.date2num(initialx), initialy, color="y", linewidth=6) 

     plt.subplots_adjust(left=0.1, bottom=0.28, right=0.9, top=0.9, wspace=0, hspace=0) 

     self.ax_temp =  plt.axes([0.1, 0.08, 0.2, 0.06], axisbg=self.axisbg) 
     self.ax_time =  plt.axes([0.2, 0.08, 0.2, 0.06], axisbg=self.axisbg) 
     self.ax_overdrive = plt.axes([0.4, 0.08, 0.2, 0.06], axisbg=self.axisbg) 
     self.ax_cycles = plt.axes([0.5, 0.08, 0.2, 0.06], axisbg=self.axisbg) 
     self.ax_image = plt.axes([0.75, 0.03, 0.3, 0.2], axisbg=self.axisbg) 

     self.tx_temp = self.ax_temp.text(0,0, "Temp", color="w", transform=self.ax_temp.transAxes, bbox={"pad" : 10, "ec" : "w", "fc" : self.axisbg}) 
     self.tx_time = self.ax_time.text(0,0, "Time", color="w", transform=self.ax_time.transAxes, bbox={"pad" : 10, "ec" : "w", "fc" : self.axisbg}) 
     self.tx_overdrive = self.ax_overdrive.text(0,0, "Overdrive", color="w", transform=self.ax_overdrive.transAxes, bbox={"pad" : 10, "ec" : "w", "fc" : self.axisbg}) 
     self.tx_cycles = self.ax_cycles.text(0,0, "Cyles", color="w", transform=self.ax_cycles.transAxes, bbox={"pad" : 10, "ec" : "w", "fc" : self.axisbg}) 

     self.ax_image.imshow(mpimg.imread('mmbRy.jpg')) 
     self.ax_image.tick_params(axis='x',which='both',bottom='off', top='off',labelbottom='off') 
     self.ax_image.tick_params(axis='y',which='both',left='off', right='off',labelleft='off') 
     [self.ax_image.spines[wh].set_color("#5998ff") for wh in ['bottom', 'top', 'left', 'right']] 

     self.timer = self.fig.canvas.new_timer(interval=250, callbacks=[(self.animate, [], {})]) 
     self.timer.start() 
     plt.show() 


    def plot(self, data, line): 
     if data: 
      last_dt = data[0][0] 
      gap = timedelta(seconds=10) 
      x = [] 
      y = [] 
      # Plot groups of data not more than 60 seconds apart 
      for dt, ten in data: 
       if dt <= last_dt + gap: 
        x.append(dt) 
        y.append(ten) 
       else: 
        line.set_data(matplotlib.dates.date2num(x), y) 
        #ax.plot(, colour, linewidth=width) 
        x = [dt] 
        y = [ten] 
       last_dt = dt 
      line.set_data(matplotlib.dates.date2num(x), y) 


    def animate(self): 
     self.i +=1 #counting the number of frames 
     # Read in the CSV file 
     data = collections.defaultdict(list) 
     fields = ["TimeStamp", "ReadCount", "Antenna", "Protocol", "RSSI", "EPC", "Temp", "Ten", "Powr", "Unpowr", "Inf"] 
     temp = "" 
     # the complete file is read in, which might be a problem once the file gets very large 
     with open('SensorLogFile.csv') as f_input: 
      csv_input = csv.DictReader(f_input, skipinitialspace=True, fieldnames=fields) 
      header = next(csv_input) 
      # Separate the rows based on the Antenna field 
      for row in csv_input: 
       try: 
        data[row['Antenna']].append([self.stime(row['TimeStamp']), self.rten(row['Ten']) ]) 
        temp= row['Temp'] 
       except: 
        pass 

     # Drop any data points more than 1.5 mins older than the last entry 
     latest_dt = data[row['Antenna']][-1][0]  # Last entry 
     not_before = latest_dt - timedelta(minutes=.25) 

     for antenna, entries in data.items(): 
      data[antenna] = [[dt, count] for dt, count in entries if dt >= not_before] 

     self.plot(data['1'], self.line1)  # Antenna 1 
     self.plot(data['2'], self.line2)  # Antenna 2 
     self.plot(data['3'], self.line3)  # Antenna 3 

     #Filling the text boxes 
     self.tx_temp.set_text(u"Temperature\n{temp:.2f} °F".format(temp=self.deg2F(temp))) 
     self.tx_time.set_text("Time\n{time}".format(time=datetime.now().time())) 
     self.tx_overdrive.set_text("Overdrive\nfill later") 
     #Todo: how do you calculate this? 
     self.tx_cycles.set_text("Cyles\n{cyles}".format(cyles=self.i)) 
     #Todo: setting the limits correctly, depending on the user's need 
     self.ax.set_ylim([0,16])  
     self.ax.set_xlim([matplotlib.dates.date2num(not_before), matplotlib.dates.date2num(latest_dt)]) 
     #Update the canvas 
     self.fig.canvas.draw() 


    def deg2F(self,deg): 
     return float(deg) * 9./5. + 32. 

    def stime(self, timestamp): 
     return datetime.strptime(timestamp, '%m/%d/%Y %H:%M:%S.%f') 

    def rten(self, ten): 
     return int(float(ten) * self.slope + self.offset) 


if __name__ == "__main__": 
    Anim() 
+0

1)理想的情況下,是的,實時的將是很好。我現在每隔250毫秒就會運行一次,這與現實時間非常接近。 2)當數據被寫入csv(並非全部時間)時,每250ms會有新值出現。 3)不需要任何交互性,只需要顯示頂部的圖形和底部的單元格以顯示我提到的實時數據並放置照片。哦,所以我只用Matplotlib就可以做到這一切?劇情創作代碼看起來像拉取數據(即溫度),通過公式運行並在單元格中顯示實時輸出數字? – Jeffrey

+0

所以你想要數字格式的「Temp」「Time」「Overdrive」「Cycles」,而不是圖形? Matplotlib當然可以顯示簡單的文本,使用'ax.text(x_coord,y_coord,「E036115348A908CB」)'繪圖都不是問題。問題是每250ms讀取一次文件。 – ImportanceOfBeingErnest

+0

這與我正在尋找的東西非常接近!我已經測試過了,它看起來像X軸沒有像以前那樣顯示最後的'.25'分鐘的數據,現在它似乎被卡住了,實際上並沒有顯示新的數據。溫度效果很好,但是對於時間顯示,我不想從時間數據中提取時間,只是使用計算機時鐘或其他東西(否則當傳感器超出範圍時,時間會凍結)。 – Jeffrey