效果:
双Y轴,左轴对应曲线总市值;右轴对应柱状图总营收。
市值数据为月数据,营收数据为季度数据,两者时间区间一致。
代码:
# -*- coding: utf-8-*-
import pandas as pd
import numpy as np
from PyQt6.QtWidgets import (
QApplication
)
import pyqtgraph as pg
class StrAxisItem(pg.AxisItem):
def __init__(self,ticks,*args,**kwargs):
pg.AxisItem.__init__(self,*args,**kwargs)
self.x_values = [x[0] for x in ticks]
self.x_strings = [x[1] for x in ticks]
pass
def tickStrings(self, values, scale, spacing):
strings = []
for v in values:
vs = v*scale
if vs in self.x_values:
vstr = self.x_strings[self.x_values.index(vs)]
else:
vstr = ''
strings.append(vstr)
return strings
class Graph2YBarAndLineWidget(pg.PlotWidget):
def __init__(self):
super().__init__()
self.init_data()
def init_data(self):
self.color_bar = (0, 0, 255)
self.color_line = (178, 34, 34)
self.x = None
self.xTicks:list = []
self.p2 = pg.ViewBox()
self.whole_left_df = pd.DataFrame() # lien
self.whole_right_df = pd.DataFrame() # bar
self.whole_left_name:str = ''
self.whole_right_name:str = ''
pass
def set_data(self,left_df:pd.DataFrame,right_df:pd.DataFrame,left_name:str,right_name:str,xTicks:list):
'''
dateStr 字符型
timestamp 长整型
a1 line 总市值 a2 bar 总营收
:return:
'''
self.clear()
if self.p2:
self.p2.clear()
pass
if left_df.empty or right_df.empty:
return
self.x = range(len(xTicks))
self.whole_right_df = right_df
self.whole_left_df = left_df
self.whole_left_name = left_name
self.whole_right_name = right_name
self.xTicks = xTicks
self.addLegend()
self.setLabel('left',left_name)
y_left = left_df['a1'].to_list()
x_left = left_df['x'].to_list()
right_df = right_df.fillna(0)
y_right = right_df['a2'].to_list()
x_right = right_df['x'].to_list()
curve_left = pg.PlotCurveItem(x=x_left,y=y_left,pen=pg.mkPen({'color': self.color_line, 'width': 4}), connect='finite',name=left_name)
self.addItem(curve_left)
self.setLabel('right',right_name)
self.p2.enableAutoRange()
self.scene().addItem(self.p2)
self.getAxis('right').linkToView(self.p2)
self.p2.setXLink(self)
bars = pg.BarGraphItem(x=x_right,height=y_right,width=0.8,brush=pg.mkBrush(color=self.color_bar))
self.p2.addItem(bars)
horAxis = StrAxisItem(ticks=xTicks, orientation='bottom')
self.setAxisItems({'bottom': horAxis})
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.addItem(self.vLine, ignoreBounds=True)
self.addItem(self.hLine, ignoreBounds=True)
self.label = pg.TextItem()
self.addItem(self.label)
self.vb = self.getViewBox()
self.vb.sigResized.connect(self.updateViews)
self.scene().sigMouseMoved.connect(self.mouseMoved)
self.enableAutoRange()
pass
def updateViews(self):
self.p2.setGeometry(self.getViewBox().sceneBoundingRect())
self.p2.linkedViewChanged(self.getViewBox(), self.p2.XAxis)
pass
def mouseMoved(self,evt):
pos = evt
if self.sceneBoundingRect().contains(pos):
mousePoint = self.vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index > 0 and index < len(self.x):
l_df = self.whole_left_df.loc[self.whole_left_df['x']==index]
r_df = self.whole_right_df.loc[self.whole_right_df['x']==index]
dateStr = self.xTicks[index][1]
html_str = f"<span style='font-size:20pt;color:#ffff00;'>日期:{dateStr} <br/>"
if not l_df.empty:
html_str += f"{self.whole_left_name}:{trans_float_to_money(l_df.iloc[0]['a1'])} <br/>"
pass
if not r_df.empty:
html_str += f"{self.whole_right_name}:{trans_float_to_money(r_df.iloc[0]['a2'])} "
pass
html_str += '</span>'
self.label.setHtml(html_str)
self.label.setPos(mousePoint.x(),mousePoint.y())
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
pass
pass
pass
pass
def trans_float_to_money(val):
try:
val = float(val)
if abs(val) // 1000000000000 >= 1:
return f"{val / 1000000000000:,.2f}万亿"
elif abs(val) // 100000000 >= 1:
return f"{val / 100000000:,.2f}亿"
elif abs(val) // 10000 >= 1:
return f"{val / 10000:,.2f}万"
else:
return f"{val:,.2f}"
except:
return f"{val}"
if __name__ == '__main__':
dateStr = ['2021-01-31', '2021-02-28', '2021-03-31', '2021-04-30', '2021-05-31', '2021-06-30', '2021-07-31', '2021-08-31', '2021-09-30', '2021-10-31', '2021-11-30', '2021-12-31', '2022-01-31', '2022-02-28', '2022-03-31', '2022-04-30', '2022-05-31', '2022-06-30', '2022-07-31', '2022-08-31', '2022-09-30', '2022-10-31', '2022-11-30', '2022-12-31', '2023-01-31', '2023-02-28', '2023-03-31', '2023-04-30', '2023-05-31', '2023-06-30', '2023-07-31', '2023-08-31', '2023-09-30', '2023-10-31', '2023-11-30', '2023-12-31', '2024-01-31', '2024-02-29', '2024-03-31', '2024-04-30', '2024-05-31', '2024-06-30', '2024-07-31', '2024-08-31', '2024-09-30', '2024-10-31', '2024-11-30', '2024-12-31', '2025-01-31', '2025-02-28', '2025-03-31', '2025-04-30', '2025-05-31', '2025-06-30', '2025-07-31']
a1 = [3366454261851.7603, 3085759494953.95, 2915166338295.36, 3062518461207.14, 3282010044186.26, 3704755450707.67, 3819034032030.23, 4151137992229.7, 3731726058531.34, 4220140547358.65, 4395348959207.6, 4134718310549.7695, 3622885627493.38, 3638523139149.04, 3188729955826.0195, 2949100387509.55, 3533200939973.0, 4028306230193.0703, 4230437911317.54, 3777311881572.21, 3371730187564.07, 4775148842141.6, 5147063106507.54, 4839222735880.72, 3713661759474.92, 3709103595200.17, 3508159000487.1, 3319952875737.66, 3281394558675.9004, 3555277221999.0703, 3770568023843.5, 3482966320754.82, 3491462068127.8105, 3586476527336.13, 3647520125533.5303, 3424088400362.96, 2886662947012.88, 3285235429188.3203, 3438771995769.73, 3545558269681.92, 3450330288454.79, 3375797478167.6, 3421568335798.04, 3232236836706.6396, 3977065566968.01, 4013214016479.33, 4059133933833.29, 4178900418689.1104, 4157633137555.46, 4543013367676.15, 4703947321683.3, 4571493143958.17, 4632381147891.07, 4589724848135.59, 4680715665617.9]
a2 = [np.nan, np.nan, 3032052010652.64, np.nan, np.nan, 3122744032107.75, np.nan, np.nan, 3095421730935.44, np.nan, np.nan, 3070683024038.89, np.nan, np.nan, 3039483894149.17, np.nan, np.nan, 2915294618426.64, np.nan, np.nan, 3093223235167.2803, np.nan, np.nan, 3201963125816.03, np.nan, np.nan, 3253459877515.63, np.nan, np.nan, 3494836097005.8496, np.nan, np.nan, 3536010599250.05, np.nan, np.nan, 3635214337405.83, np.nan, np.nan, 3665836883287.5, np.nan, np.nan, 3674413007760.9, np.nan, np.nan, 3617732527099.52, np.nan, np.nan, 3721610095247.51, np.nan, np.nan, 3766992158253.9995, np.nan, np.nan, np.nan, np.nan]
df = pd.DataFrame(data={
'dateStr':dateStr,
'a1':a1,
'a2':a2
})
df['x'] = range(len(df))
df_l = df.loc[:, ['x', 'dateStr', 'a1']]
df_r = df.loc[:, ['x', 'dateStr', 'a2']]
df_l.dropna(subset=['a1'], inplace=True)
df_r.dropna(subset=['a2'], inplace=True)
xTicks = df.loc[:, ['x', 'dateStr']].values
app = QApplication([])
mv = Graph2YBarAndLineWidget()
# mv.set_data(df)
mv.set_data(df_l, df_r, '总市值', '总营收', xTicks)
mv.show()
app.exec()
pass