PyQtGraphの時系列グラフに時間軸を使う
自動売買ソフトにチャート表示を付けるため、PyQtGraphを使いました。リアルタイムグラフはPyQtGraph のほうが、Matplotlabより使い勝手がいいのですが、横軸を時間軸にするのに少してこずったので紹介しておきます。
時間軸データの変換
まず時間軸のデータは、UNIX時間(エポック秒)に変換します。 以下にサンプルです。PandasのDataFrameにデータを入れて、時間データ部分を変換するのですが、最終的にはmapを使う方法を採用しました。astypeでintにしたあとに割り算する方法ですが、最初、//演算でやってたら(当然ですが)、秒以下が切り捨てられてしまってしばらく悩みました。
# time を index にする df.set_index('time', inplace=True) # UNIX時間(エポック秒)にする ''' 他の書き方 以下の書き方のサンプルだと、秒単位でns単位が切り捨てられる self.dt = df.index.astype(np.int64)//10**9 以下のやり方だと、ns単位 self.dt = df.index.astype(np.int64)/10**9 ''' self.dt = df.index.map(pd.Timestamp.timestamp)
時間軸の表示
AxisItemから継承して時間軸の表示フォーマットを指定します。拡大、縮小すれば自動的に変わります。
# ----- 時間軸の表示フォーマットを指定 ----- class TimeAxisItem(pg.AxisItem): def __init__(self, *args, **kwargs): super(TimeAxisItem, self).__init__(*args, **kwargs) def tickStrings(self, values, scale, spacing): return [datetime.datetime.fromtimestamp(v).strftime('%m-%d %H:%M:%S') for v in values]
PlotWidgetで時間軸を指定してグラフを作成
PlotWidgetのaxisItemに、先ほどの時間軸表示のクラスを指定してあげます。
# dockarea を準備 dockarea = pgda.DockArea() self.setCentralWidget(dockarea) # Chart を準備 setprop = lambda x: (x.showGrid(x=True, y=True), x.showAxis('right'), x.hideAxis('left')) plt_chart = pg.PlotWidget(axisItems={'bottom': TimeAxisItem(orientation='bottom')}) setprop(plt_chart) # dockaarea へ追加 dockarea.addDock(pgda.Dock('chart', widget=plt_chart))
出来栄え
サンプルコード全体
# coding: utf-8 import sys import datetime import pandas as pd from PyQt6.QtWidgets import * import pyqtgraph as pg import pyqtgraph.dockarea as pgda # ----- Sample Data ----- data =[ {'time': datetime.datetime(2024, 2, 2, 0, 25, 0, 105000), 'val_A': 36059.0, 'val_B': 36063.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 0, 364000), 'val_A': 36060.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 0, 364000), 'val_A': 36060.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 0, 670000), 'val_A': 36061.0, 'val_B': 36066.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 1, 17000), 'val_A': 36061.0, 'val_B': 36066.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 1, 270000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 1, 624000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 1, 927000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 2, 226000), 'val_A': 36061.0, 'val_B': 36066.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 2, 530000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 2, 530000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 3, 71000), 'val_A': 36061.0, 'val_B': 36066.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 3, 370000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 3, 370000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 3, 370000), 'val_A': 36061.0, 'val_B': 36065.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 3, 873000), 'val_A': 36064.0, 'val_B': 36068.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 4, 166000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 4, 166000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 4, 467000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 4, 467000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 4, 467000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 5, 76000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 5, 374000), 'val_A': 36063.0, 'val_B': 36068.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 5, 374000), 'val_A': 36063.0, 'val_B': 36068.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 5, 668000), 'val_A': 36062.0, 'val_B': 36067.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 5, 974000), 'val_A': 36063.0, 'val_B': 36068.0} , {'time': datetime.datetime(2024, 2, 2, 0, 25, 6, 120000), 'val_A': 36065.0, 'val_B': 36069.0} ] # ----- 時間軸の表示フォーマットを指定 ----- class TimeAxisItem(pg.AxisItem): def __init__(self, *args, **kwargs): super(TimeAxisItem, self).__init__(*args, **kwargs) def tickStrings(self, values, scale, spacing): return [datetime.datetime.fromtimestamp(v).strftime('%m-%d %H:%M:%S') for v in values] # ----- TestChart ----- class TestChart(QMainWindow): """ DockAreaにPlotWidgetを表示する """ def __init__(self): super(TestChart, self).__init__() # DataFrameにしておく df = pd.DataFrame(data) # time を index にする df.set_index('time', inplace=True) # UNIX時間(エポック秒)にする ''' 他の書き方 以下の書き方のサンプルだと、秒単位でns単位が切り捨てられる self.dt = df.index.astype(np.int64)//10**9 以下のやり方だと、ns単位 self.dt = df.index.astype(np.int64)/10**9 ''' self.dt = df.index.map(pd.Timestamp.timestamp) # dockarea を準備 dockarea = pgda.DockArea() self.setCentralWidget(dockarea) # Chart を準備 setprop = lambda x: (x.showGrid(x=True, y=True), x.showAxis('right'), x.hideAxis('left')) plt_chart = pg.PlotWidget(axisItems={'bottom': TimeAxisItem(orientation='bottom')}) setprop(plt_chart) # dockaarea へ追加 dockarea.addDock(pgda.Dock('chart', widget=plt_chart)) # データを描画 plt_chart.addItem(pg.PlotDataItem(self.dt, df['val_A'], pen='w')) plt_chart.addItem(pg.PlotDataItem(self.dt, df['val_B'], pen='r')) # ----- MainWindow ----- class TestMainWindow(QMainWindow): """ QMainWindowにチャートを表示する """ def __init__(self): super(TestMainWindow, self).__init__() self.setGeometry(200, 200, 800, 600) self.setCentralWidget(TestChart()) self.show() def main(): app = QApplication(sys.argv) test=TestMainWindow() sys.exit(app.exec()) if __name__ == '__main__': main()
コメント
0 件のコメント :
コメントを投稿