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 件のコメント :
コメントを投稿