Plotly Dash和OmniSciDB用於實時數據可視化

在本文中,我們討論瞭如何使用Plotly Dash和OmniSciDB創建具有實時數據可視化的儀表板。

在過去的一年中,OmniSci F1 Demo遍歷了美國各地的會議和聚會。通過允許參與者在F1視頻遊戲中行駛幾圈並查看流向遙測結果的流媒體,OmniSciDB,社區和活動團隊可以以高度參與的方式演示OmniSci平臺。

在本文中,我將逐步介紹如何使用Plotly Dash創建實時儀表板,並概述將OmniSciDB用作自定義應用程序的數據源時要記住的一些注意事項。

F1 Demo GitHub存儲庫中提供了此博客文章中解釋的所有代碼。

Flask + Bootstrap + React.js =破折號

Python社區並沒有像R社區和Shiny那樣將單個開源項目合併在一起,但是Dash在設計和目標上感到相似。通過Dash Bootstrap組件可以使用諸如模板化和網格佈局選項之類的用戶友好功能,並且由於Dash是基於React.js構建的,因此默認情況下可以進行交互。對我而言,選擇Dash的最大優勢是它基於我已經有經驗的Python Web框架Flask構建。

佈置F1儀表板僅需幾行代碼,類似於普通的Flask應用程序:


Plotly Dash和OmniSciDB用於實時數據可視化

F1儀表板可以認為是兩個部分:

  1. 導航欄,其中包含應用程序標題和公司品牌以及
  2. 主體由兩行組成,每行都有兩列以保存可視化內容。

我選擇對導航欄和正文使用佔位符變量,而不是將實際的小部件構建到佈局中。這使我可以分別處理這四個部分,而不是將所有Dash代碼嵌套在同一app.layout作業中。


Plotly Dash和OmniSciDB用於實時數據可視化

調用OmniSciDB填充可視化

Dash提供了用於在儀表板上創建和佈置可視化效果的所有功能,而該應用程序的計算密集型部分則由OmniSciDB處理。為儀表板提供動力的主要查詢有四個,每個小組件一個(不包括Metric 靜態填充的下拉小組件):

  • 最快10圈進入排行榜。
  • 獲取軌道位置(散點圖)。
  • 獲取車輛遙測(折線圖)。
  • 填充參考單圈下拉列表(最快的50個單圈)。

這些查詢中的每一個都被編寫為Python函數,使用pymapd與OmniSciDB後端進行通信。讓我們探索get_telemetry_data():


Plotly Dash和OmniSciDB用於實時數據可視化

該功能有幾個輸入,有助於從特定圈中唯一地識別和檢索遙測數據。在函數主體中,您會注意到我將pymapd.connect()調用嵌入到函數內部,而不是將Connection 對象作為參數傳遞給函數。雖然每隔幾秒鐘對數據庫進行一次新連接錘擊並不是最佳的開發實踐,但它確實可以確保每次提交新查詢時都保持“新鮮”連接。

在後面的文章中,我將更多地討論整個應用程序的性能注意事項,但是對於將應用程序作為活動展臺中的單個顯示器的預期用途,在每個查詢上創建新的連接都不夠高效。

使用Dash回調進行反應式編程

F1儀表板的交互功能是使用Dash中的回調創建的,這些回調被實現為Python裝飾器。裝飾器允許使用其他行為來修改Python函數,而無需實際更改底層Python函數。Dash回調裝飾器具有輸入和輸出;更改輸入(通常是菜單元素)將重新運行Python函數,從而修改頁面上的特定元素(也定義為回調的一部分)。

按需回調

在儀表板上使用回調的第一種方式是下拉菜單,用於設置Reference Lap 與當前行駛性能進行比較的,以及Metric 選擇要監視的遙測指標。您可以在build_telemetry_chart()函數中看到兩種輸入的工作方式。為了避免過多的說明,我們在這裡僅查看裝飾器和函數簽名:


Plotly Dash和OmniSciDB用於實時數據可視化


Plotly Dash和OmniSciDB用於實時數據可視化

上面的代碼段的6至28行定義了下拉菜單。有關這些行的重要注意事項是id字段。這些標籤定義了整個Dash應用程序將觀察狀態變化的內容。當這些下拉菜單中的任何一個更改其value字段時(在用戶與界面進行交互時發生),它們都會發送一個信號,build_telemetry_chart()提示需要重新運行。

當build_telemetry_chart()完成運行,它返回每回調(這對於本實施例中是id定義的輸出位置它的輸出telemetry-graph 在telgraph Python對象)。瞭解Dash中的回調的一個重要概念是,該id 值與HTML中的id概念相同,HTML是Web頁面中的唯一標識符。儘管Dash是通過Python運行的,並且telgraph 是我們的Python對象,但id的回調引用是對React.js的傳遞,以告訴它要更新網頁的哪一部分。

基於時間的回調

按需回調增加了儀表板的交互性,但是要製作實時更新的儀表板,我們需要定期刷新支持圖表的數據,而無需用戶干預。使用Dash Core Components庫中的Interval類,我們可以設置選擇觸發回調的任何時間段:


Plotly Dash和OmniSciDB用於實時數據可視化

在trackgraph Python對象中,我添加了一個Interval ,每7,000毫秒更新一次,以更新track-interval ID。您會在上面的回調嵌入代碼段中注意到,Input數組中的第一個值是Input('track-interval', 'n_intervals');。每7秒鐘,該dcc.Interval() 代碼就會觸發一次,它告訴build_telemetry_chart() 函數它需要運行,然後更新telemetry-graph ID。

用這種方式控制UI元素可能會造成混亂,因為JavaScript比Python多得多!但是,回調使我們能夠在應用程序中構建以下邏輯:“如果1)更改了參考間隔和/或2)更改了度量標準和/或3)7秒鐘,則更新遙測圖。” 因此,儘管Dash中的回調可能令人困惑,但回調非常強大,值得花時間學習。

控制Dash CSS的加載順序

有了儀表板的佈局並增加了交互性之後,儀表板小部件中實際顯示的內容就是您要講述的故事和設計敏感性的問題。我不會介紹我所做的每個設計決策,也不會介紹如何修改Darkly主題以具有OmniSci品牌色彩,但是Dash文檔和用戶論壇應該提供您所需要的所有答案。

當我嘗試為儀表盤設置樣式時,令我震驚的一件事是,我的CSS更改一直被Dash加載的React組件中的基本CSS樣式覆蓋。我克服這個問題的方法是用來app.index_string 控制文件的加載順序:


Plotly Dash和OmniSciDB用於實時數據可視化

通過將{%css%} 標籤放在頁腳的最後,我能夠確保CSS更改不會被Dash的任何默認CSS類覆蓋。我的自定義CSS(作為asset / external.css文件的一部分)現在是頁面上加載的最後一件事,因此,這些CSS定義就是應用程序中顯示的內容。

從Flask Dev Server遷移到生產服務器

在完成Dash應用程序後,我決定留意Flask警告,不要將開發服務器用於生產工作。我使用了gunicorn和8個線程,但是由於該應用程序一次僅在一個位置(即活動展位)運行,因此我使用的次數可能更少。

如何運行Dask / Flask應用程序的安裝步驟超出了本文的範圍,但是Flask的官方文檔以及DigitalOcean Flask指南使該過程非常輕鬆。

對於該特定的Dash應用程序而言,至關重要的一項決定是將該應用程序與OmniSciDB在同一服務器上運行,並使用多個GPU。通過使用更大的GPU服務器(Microsoft Azure上的ND24s實例,448GB CPU RAM / 96GB GPU RAM)並從同一服務器運行Dash應用程序,它既消除了網絡延遲,又為OmniSciDB提供了可觀的GPU內存緩衝區以保留數據“熱”。

無論用戶探索了參考圈和遙測指標的哪種組合,該應用程序使用的最大GPU RAM都約為30GB,隨著額外的圈擴展了遙測數據集,剩下了足夠的服務器容量。

讓我們來看看您的自定義儀表盤!

在我的兩個OmniSci F1 Demo帖子中,我已經展示了可以使用StreamSets將實時數據流插入OmniSciDB,還可以將OmniSciDB與Dash一起使用以創建實時儀表板。由於OmniSciDB的體系結構,因此數據工程和實時儀表板用例都是可能的,該體系結構不需要創建索引即可獲得高性能的查詢。一旦將數據提取到OmniSciDB中,並且可以將數據從CPU RAM傳輸到GPU RAM,就可以使用該數據。

對於OmniSciDB開源用戶,像Dash這樣的框架可以提供接近Immerse的分析體驗。您唯一可能會錯過的是Immerse中的一些高級功能,例如圖表與後端渲染之間的自動交叉過濾(當然還有構建儀表板的無代碼性質)。


分享到:


相關文章: