• 实战PyQt5: 141-QChart图表之箱形图


    箱形图(英文:Box plot),又称为盒须图、盒式图、盒状图或箱线图,也称箱须图(Box-whisker Plot)。是一种用作显示一组数据分散情况资料的统计图,经常被使用于各种领域,因形状如箱子而得名。它主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比较。箱形图的绘制方法是:先找出一组数据的上边缘、下边缘、中位数和两个四分位数;然后, 连接两个四分位数画出箱体;再将上边缘和下边缘与箱体相连接,中位数在箱体中间。

    QChart提供QBoxPlotSeries, QBoxSet类来实现箱形图的绘制。

    QBoxPlotSeries

    QBoxPlotSeries类在箱形图中显示数据。QBoxPlotSeries充当箱形数据集的容器。在QBoxPlotSeries可添加多个箱形数据集QBoxPlotSet。

    QBoxPlotSeries 常用函数:

    • append(self, set):将由set指定的单个条目添加到箱形图序列中,并获得其所有权。如果该项为空或已在系列中,则不会附加该项。如果添加成功返回True,否则返回False。
    • insert(self, index, set):在图中指定索引index处插入set指定的条目。
    • remove(self, set):从图中删除set指定的条目。
    • clear(self):永久删除图中的所有条目。
    • take(self, set):从图中获取由set指定的单个条目。但不删除该条目。
    • setBoxOutlineVisible(self, visible):设置箱形轮廓是否可见。
    • setBoxWidth(self, width):设置条目的相对宽度,范围为0.0到1.0。
    • setBrush(self, brush):设置条目的画刷。
    • setPen(self,pen):设置条目线条的画笔。

    QBoxPlotSeries常用信号:

    • boxOutlineVisibilityChanged(self):盒子的轮廓可见性更改时,将发出此信号。
    • boxWidthChanged(self):当箱形图形状的宽度发生变化时,将发出此信号。
    • boxsetsAdded(self, sets):当sets指定的条目添加到箱形图序列中时,将发出此信号。
    • boxsetsRemoved(self, sets):当sets指定的条目移除箱形图序列时,将发出此信号。
    • countChanged(self):当箱形图中的条目数量发生改变时,将发出此信号。
    • brushChanged(self):当用于填充箱形图的画刷发生改变时,将发出此信号。
    • penChanged(self):箱形图的画笔发生更改时会发出此信号。
    • clicked(self, boxset):当用户单击由boxset指定的条目时,将发出此信号。
    • doubleClicked(self, boxset):当用户双击由boxset指定的条目时,将发出此信号。
    • hovered(self, status, boxset):当鼠标悬停在所指定的条目boxset上时,这个信号被发射。当鼠标移至该条目上方时,state变为True,而当鼠标再次移开时,state变为False。
    • pressed(self, boxset):当用户单击boxset指定的条目并按住鼠标按钮时,将发出此信号。
    • released(self, boxset):当用户释放boxset所指定的条目上的鼠标时,将发出此信号。

    BoxSet

    QBoxSet类代表箱形图中的一个条目。箱形条目是范围和由五个不同值构成的三个中值的图形表示。有两种指定值的方法。第一个是通过使用构造函数或流运算符(<<)。必须按以下顺序指定值:下极值,下四分位数,中位数,上四分位数和上限。第二种方法是创建一个空的QBoxSet实例,并使用setValue()方法指定值。

    QBoxSet常用函数:

    • append(self, value):添加由value指定值到条目的末尾处。
    • at(self, index):返回条目中由index指定位置的值。如果index超出范围,则返回0.0。
    • clear(self): 将条目中的所有值都设置为0。
    • setBrush(self, brush):设置条目的画刷。
    • setPen(self, pen):设置条目的画笔。
    • setLabel(self, label):设置条目的标签。
    • setValue(self, index, value):设置条目中由index指定位置的值为value。

    QBoxSet常用信号:

    • brushChanged(self):条目的画刷发生改变时,将发出此信号。
    • penChanged(self):条目的画笔发生改变时,将发出此信号。
    • cleared(self):当条目的所有值都设置为0时,将发出此信号。
    • clicked(self):当用户单击图中的条目时,将发出此信号。
    • doubleClicked(self):当用户双击图中的条目时,将发出此信号。
    • hovered(self,status):当鼠标悬停在条目上时,将发出此信号。
    • pressed(self):当用户单击条并按住鼠标按钮时,将发出此信号。
    • released(self):当用户释放对条目的释放鼠标时,将发出此信号。
    • valueChanged(self, index):当index指定的条目的值发生改变时,将发出此信号。
    • valuesChanged(self):当条目中的多个值发生改变时,将发出此信号。

    箱形图示例

    在QChart C++演示代码为基础上改写,示例代码显示了如何创建一个箱形图,同时也显示了如何从文件中读取数据,并对其进行排列然后查找出箱形图的中位数。最终图形显示了两个公司一年中月度股票数据偏差。

    两公司月度股票数据如下图所示:

     acme_data.txt

     boxwhisk_data.txt

    资源文件boxplotdata.qrc文件:

    1. <RCC>
    2.     <qresource prefix="/">
    3.         <file alias="acme">acme_data.txt</file>
    4.         <file alias="boxwhisk">boxwhisk_data.txt</file>
    5.     </qresource>
    6. </RCC>

    完整代码如下:

    1. import sys
    2. from PyQt5.QtCore import Qt, QIODevice, QTextStream, QFile, QDateTime
    3. from PyQt5.QtGui import QPainter
    4. from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
    5. from  PyQt5.QtChart import QChart, QChartView, QBoxPlotSeries, QBoxSet, QValueAxis
    6.  
    7. import boxplotdata_rc
    8.  
    9. #读取文本文件并从数据中查找极值和中值
    10. class BoxDataReader(QTextStream):
    11.     def __init__(selfdeviceparent = None):
    12.         super(BoxDataReaderself).__init__(parent)
    13.         self.setDevice(device)
    14.         self.sortedList = []
    15.     
    16.     def readFile(selfdevice):
    17.         self.setDevice(device)
    18.         
    19.     def readBox(self):
    20.         line = self.readLine()
    21.         if line.startswith('#'):
    22.             return None
    23.         
    24.         strList = line.split(' ')
    25.         self.sortedList.clear()
    26.         for i in range(1,len(strList)):
    27.             self.sortedList.append(float(strList[i]))
    28.         self.sortedList.sort() #从小到大排序
    29.         
    30.         count = len(self.sortedList)
    31.  
    32.         box = QBoxSet(strList[0])
    33.         box.setValue(QBoxSet.LowerExtremeself.sortedList[0])     #下边沿
    34.         box.setValue(QBoxSet.UpperExtremeself.sortedList[-1])    #上边沿 
    35.         box.setValue(QBoxSet.Medianself.findMedian(0, count))    # 中位数 
    36.         box.setValue(QBoxSet.LowerQuartileself.findMedian(0, count//2))  #下四分位数
    37.         box.setValue(QBoxSet.UpperQuartileself.findMedian(count//2 + count%2, count)) #上四分位数
    38.         
    39.         return box
    40.         
    41.     #获得中值
    42.     def findMedian(selfbeginend):
    43.         count = begin - end
    44.         if count % 2: 
    45.             return self.sortedList[count//2 + begin]
    46.         else:
    47.             right = self.sortedList[count//2 + begin]
    48.             left = self.sortedList[count//2 - 1 + begin]
    49.             return (right + left) / 2.0
    50.  
    51. class DemoChartBoxWhisk(QMainWindow):
    52.     def __init__(selfparent=None):
    53.         super(DemoChartBoxWhiskself).__init__(parent)   
    54.         
    55.          # 设置窗口标题
    56.         self.setWindowTitle('实战 Qt for PythonQChart箱须图演示')      
    57.         # 设置窗口大小
    58.         self.resize(640, 480)
    59.         
    60.         self.createChart()
    61.         
    62.     def createChart(self):
    63.         #创建box-whisk
    64.         acmeSeries = QBoxPlotSeries()
    65.         acmeSeries.setName('Acme Ltd')
    66.         
    67.         boxWhiskSeries = QBoxPlotSeries()
    68.         boxWhiskSeries.setName('BoxWhisk Inc')
    69.         
    70.         #从文件中读取数据
    71.         acmeData = QFile(':acme')
    72.         if not acmeData.open(QIODevice.ReadOnly | QIODevice.Text):
    73.             QMessageBox.information(self, '读数据', '不合法的数据文件')
    74.             return
    75.         
    76.         dataReader = BoxDataReader(acmeData)
    77.         while not dataReader.atEnd():
    78.             boxSet = dataReader.readBox()
    79.             if not boxSet is None:
    80.                 acmeSeries.append(boxSet)
    81.                 
    82.         boxwhiskData = QFile(':boxwhisk')
    83.         if not boxwhiskData.open(QIODevice.ReadOnly | QIODevice.Text):
    84.             QMessageBox.information(self, '读数据', '不合法的数据文件')
    85.             return
    86.         
    87.         dataReader.readFile(boxwhiskData)
    88.         while not dataReader.atEnd():
    89.             boxSet = dataReader.readBox()
    90.             if not boxSet is None:
    91.                 boxWhiskSeries.append(boxSet)
    92.                 
    93.         #创建图表
    94.         chart = QChart()
    95.         chart.addSeries(acmeSeries)
    96.         chart.addSeries(boxWhiskSeries)
    97.         chart.setTitle('Acme Ltd and BoxWhisk Inc share deviation in 2012')
    98.         chart.setAnimationOptions(QChart.SeriesAnimations)
    99.         
    100.         #坐标轴设置
    101.         chart.createDefaultAxes()
    102.         axisY = chart.axes(Qt.Vertical)[0]
    103.         axisY.setMin(15.0)
    104.         axisY.setMax(34.0)
    105.          
    106.         #图例
    107.         chart.legend().setVisible(True)
    108.         chart.legend().setAlignment(Qt.AlignBottom)
    109.         
    110.         #图表视图
    111.         chartView = QChartView(chart)
    112.         chartView.setRenderHint(QPainter.Antialiasing)
    113.         
    114.         self.setCentralWidget(chartView)
    115.       
    116.         
    117. if __name__ == '__main__':
    118.     app = QApplication(sys.argv)
    119.     window = DemoChartBoxWhisk()
    120.     window.show()
    121.     sys.exit(app.exec())   

    运行结果如下图:

     QChart箱形图示例

    本文知识点

    • 箱形图是什么,如何绘制获得箱形图所需要的五个数据。
    • 使用QBoxPlotSeries, QBoxSet绘制箱形图。

    前一篇:实战PyQt5: 140-QChart图表之烛台图

  • 相关阅读:
    【数据结构】模拟实现双向链表
    jquery导航图片全屏滚动、首页全屏轮播图,各式相册
    B2B企业老板和市场人必看:如何做好数字化营销
    【算法心得】minus instead of add
    Linux 软件包工具rpmbuild
    STC51单片机34——五线四相步进电机驱动(1个步进电机)
    docker0: iptables: No chain/target/match by that name
    对Mysql数据表查询出来的结果进行排序
    java计算机毕业设计医院远程诊断系统源代码+系统+数据库+lw文档
    C++中类模板的语法与使用
  • 原文地址:https://blog.csdn.net/seniorwizard/article/details/125557948