Python+Matplotlib+PyQt4做的简单数据预测工具

来源:互联网 发布:亚投行结局是笑话知乎 编辑:程序博客网 时间:2024/06/08 20:57

需求:

      在软件测试的bug管理过程中,有时需要知道某一个版本在未来的bug走势(当然不是准确的,只是大概一个估计),如果我们已经有了最近一段时间内的bug统计情况(比如近一个月每天的bug数),可以通过曲线拟合的方式来进行简单的预测。根据这个需要,做了一个方便界面操作的小工具。

代码环境:

代码环境:python2.7 + numpy + scipy + matplotlib + PyQt4
(注意,相关库的版本和位数,要和所安装的python版本和位数保持一致,否则可能无法安装成功或安装之后无法正常使用)
如果python没有自带py2exe, 则还要另外下载py2exe进行安装,以便后面打包用。

代码(DefectPredict.py):

# -*-coding:UTF-8 -*-__author__ = 'GraceDD'from PyQt4 import QtGuifrom matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvasfrom matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbarimport matplotlib.pyplot as pltimport pylab as plfrom scipy.optimize import leastsqfrom scipy.integrate import *import warningsimport matplotlib.backends.backend_qt5aggimport mathpl.mpl.rcParams['font.sans-serif'] = ['SimHei']#指定默认字体  pl.mpl.rcParams['axes.unicode_minus'] = False#解决保存图像是负号'-'显示为方块的问题 class Window(QtGui.QWidget):    param=[0,0,0]    def __init__(self):        super(Window, self).__init__()        # a figure instance to plot on        self.figure = plt.figure()        # this is the Canvas Widget that displays the `figure`        # it takes the `figure` instance as a parameter to __init__        self.canvas = FigureCanvas(self.figure)        # this is the Navigation widget        # it takes the Canvas widget and a parent        self.toolbar = NavigationToolbar(self.canvas, self)        # Just some button connected to `plot` method        self.button = QtGui.QPushButton('Plot')        self.button.clicked.connect(self.plot)        self.inLbl=QtGui.QLabel(u"输入",self)        self.inEdit=QtGui.QLineEdit(self)        self.modelLbl=QtGui.QLabel(u"函数模型:a*b^c^x",self)        self.blankLbll=QtGui.QLabel("",self)        self.oriALbl=QtGui.QLabel(u"初始参数a:",self)        self.oriAEdit=QtGui.QLineEdit(self)        self.oriBLbl=QtGui.QLabel(u"初始参数b:",self)        self.oriBEdit=QtGui.QLineEdit(self)        self.oriCLbl=QtGui.QLabel(u"初始参数c:",self)        self.oriCEdit=QtGui.QLineEdit(self)        self.resALbl=QtGui.QLabel(u"预测参数a:",self)        self.resAEdit=QtGui.QLineEdit(self)        self.resBLbl=QtGui.QLabel(u"预测参数b:",self)        self.resBEdit=QtGui.QLineEdit(self)        self.resCLbl=QtGui.QLabel(u"预测参数c:",self)        self.resCEdit=QtGui.QLineEdit(self)        self.perLbl=QtGui.QLabel(u"问题数百分比:",self)        self.perEdit=QtGui.QLineEdit(self)        self.perEdit.setValidator(QtGui.QDoubleValidator(0,100,2,self))        self.perUnitLbl=QtGui.QLabel(u"%",self)        self.dayLbl=QtGui.QLabel(u"天数:",self)        self.dayEdit=QtGui.QLineEdit(self)        self.dayUnitLbl=QtGui.QLabel(u"天",self)        self.getDayBtn=QtGui.QPushButton(u"计算天数")        self.getDayBtn.clicked.connect(self.getDay)        self.dayLbl2=QtGui.QLabel(u"天数:",self)        self.dayEdit2=QtGui.QLineEdit(self)        self.dayUnitLbl2=QtGui.QLabel(u"天",self)        self.numLbl=QtGui.QLabel(u"问题数:",self)        self.numEdit=QtGui.QLineEdit(self)        self.numUnitLbl=QtGui.QLabel(u"个",self)        self.getNumBtn=QtGui.QPushButton(u"计算问题数")        self.getNumBtn.clicked.connect(self.getNum)        # set the layout        gridLayout=QtGui.QGridLayout()        # gridLayout.setSpacing(10)        gridLayout.addWidget(self.inLbl,0,0,1,2)        gridLayout.addWidget(self.inEdit,0,1,1,8)        gridLayout.addWidget(self.modelLbl,1,0,2,2)        gridLayout.addWidget(self.blankLbll,1,2,2,1)        gridLayout.addWidget(self.oriALbl,1,3,1,1)        gridLayout.addWidget(self.oriAEdit,1,4,1,1)        gridLayout.addWidget(self.oriBLbl,1,5,1,1)        gridLayout.addWidget(self.oriBEdit,1,6,1,1)        gridLayout.addWidget(self.oriCLbl,1,7,1,1)        gridLayout.addWidget(self.oriCEdit,1,8,1,1)        gridLayout.addWidget(self.resALbl,2,3,1,1)        gridLayout.addWidget(self.resAEdit,2,4,1,1)        gridLayout.addWidget(self.resBLbl,2,5,1,1)        gridLayout.addWidget(self.resBEdit,2,6,1,1)        gridLayout.addWidget(self.resCLbl,2,7,1,1)        gridLayout.addWidget(self.resCEdit,2,8,1,1)        gridLayout.addWidget(self.perLbl,3,0,1,1)        gridLayout.addWidget(self.perEdit,3,1,1,1)        gridLayout.addWidget(self.perUnitLbl,3,2,1,1)        gridLayout.addWidget(self.dayLbl,3,3,1,1)        gridLayout.addWidget(self.dayEdit,3,4,1,1)        gridLayout.addWidget(self.dayUnitLbl,3,5,1,1)        gridLayout.addWidget(self.getDayBtn,3,6,1,1)        gridLayout.addWidget(self.dayLbl2,4,0,1,1)        gridLayout.addWidget(self.dayEdit2,4,1,1,1)        gridLayout.addWidget(self.dayUnitLbl2,4,2,1,1)        gridLayout.addWidget(self.numLbl,4,3,1,1)        gridLayout.addWidget(self.numEdit,4,4,1,1)        gridLayout.addWidget(self.numUnitLbl,4,5,1,1)        gridLayout.addWidget(self.getNumBtn,4,6,1,1)        gridLayout.addWidget(self.toolbar,5, 1, 1, 8)        gridLayout.addWidget(self.canvas,6, 1, 1, 8)        gridLayout.addWidget(self.button,7,0,1,1)        self.setLayout(gridLayout)        self.setWindowTitle(u"缺陷预测")        self.resize(900, 700)        self.oriAEdit.setText('5.0')        self.oriBEdit.setText('0.1')        self.oriCEdit.setText('1.0')        self.perEdit.setText("95")    def plot(self):        ''' plot some random stuff '''        # random data        #data = [random.random() for i in range(10)]        # create an axis        x_new,y_new=self.predict()        ax = self.figure.add_subplot(111)        ax.clear()        self.clearDate()        # discards the old graph        ax.hold(True)        # plot data        y_src=self.getData()        x_src=np.arange(1,len(y_src)+1,1)        ax.plot(x_src,y_src,"*",label=u"真实数据")        ax.plot(x_new,y_new, label=u"拟合数据")        plt.legend()        # refresh canvas        self.canvas.draw()        self.resAEdit.setText(str(self.param[0]))        self.resBEdit.setText(str(self.param[1]))        self.resCEdit.setText(str(self.param[2]))        self.dayEdit2.setText("")        ax.hold(False)    #从输入文本框中获取文本并返回数字列表    def getData(self):          textData_str=self.inEdit.text()          data=str(textData_str).split(',')          data_f=[]          for i in range(len(data)):               data_f.append(float(data[i]))          #print data_f          return data_f    def clearDate(self):        self.numEdit.setText("")        self.dayEdit.setText("")    def func(self,x, p):        """        数据拟合所用的函数: a*b^c^x        """        a,b,c = p        return a*b**c**x   #两个**表示指数运算    def getNum(self):        # 根据给定的x,求y        x_str = str(self.dayEdit2.text()).replace("\n","").split(",")        try:            if(len(x_str)==0):                self.numEdit.setText(str(0))            else:                x=float(x_str[0])                y = self.func(float(x),self.param)                self.numEdit.setText(str(round(y)))        except ValueError,e:            print "error",e,x_str            self.numEdit.setText(str(0))    def getDay(self):        # 根据给定的y,求x        y_str=str(self.perEdit.text()).replace("\n","").split(",")        try:            y=float(y_str[0])*0.01*self.param[0]            #求x            a=self.param[0]            b=self.param[1]            c=self.param[2]            tmp=(math.log(y)-math.log(a))/math.log(b)            x=math.log(tmp)/math.log(c)            self.dayEdit.setText(str(round(x)))        except ValueError,e:            print "error:",e,y_str    def residuals(self,p, y, x):        """        实验数据x, y和拟合函数之间的差,p为拟合需要找到的系数        """        return y - self.func(x, p)    def predict(self,):        #x=np.arange(1,22,1)  #1~21周        #y=np.array([24,27,22,20,20,15,9,18,19,16,37,32,29,15,11,10,12,11,7,9,11])  #第1周的缺陷数        y=self.getData()        x=np.arange(1,len(y)+1,1)        #y_accu=np.add.accumulate(y)    #累积分布        y_accu=y;        num=len(y)        #p=[0.5,0.1,1.2]   #指定a、b、c的初始值        #p=self.getParam()        p=[float(self.oriAEdit.text()),float(self.oriBEdit.text()),float(self.oriCEdit.text())]        # 调用leastsq进行数据拟合        # residuals为计算误差的函数        # p0为拟合参数的初始值        # args为需要拟合的实验数据        plsq = leastsq(self.residuals, p, args=(y_accu, x))        x_new=np.arange(1,num+200,1)        #print u"拟合参数", plsq[0] # 实验数据拟合后的参数        predict_res=self.func(x_new,plsq[0])        self.param=plsq[0]   #保存拟合后的参数        return x_new,predict_resif __name__ == '__main__':    warnings.filterwarnings("ignore",category=matplotlib.backends.backend_qt5agg.mplDeprecation)    app = QtGui.QApplication(sys.argv)    m = Window()    m.show()    app.exec_()    #del m    sys.exit()

打包脚本:

如果想将上面工具打包成exe,则还需要下面的脚本(DPTool.py):

from distutils.core import setupimport sysimport numpyimport scipy.sparse.csgraph._validationimport globincludes = ["encodings", "encodings.*"]sys.argv.append("py2exe")# We need to exclude matplotlib backends not being used by this executable.  You may find# that you need different excludes to create a working executable with your chosen backend.# We also need to include include various numerix libraries that the other functions call.opts = {    'py2exe': { "includes" : ["sip","PyQt4.QtGui", "matplotlib.backends",  "matplotlib.backends.backend_qt4agg",                               "matplotlib.figure","pylab", "numpy","matplotlib.pyplot","matplotlib.backends.backend_tkagg","scipy.optimize","scipy.sparse.csgraph._validation","scipy.special._ufuncs_cxx","scipy.integrate"],                'dll_excludes': ['libgdk-win32-2.0-0.dll',                                 'libgobject-2.0-0.dll','MSVCP90.dll'],                'packages': ['FileDialog'],              }       }# Save matplotlib-data to mpl-data ( It is located in the matplotlib\mpl-data# folder and the compiled programs will look for it in \mpl-data# note: using matplotlib.get_mpldata_infodata_files = [(r'mpl-data', glob.glob(r'D:\Program Files\Python27\Lib\site-packages\matplotlib\mpl-data\*.*')),                    # Because matplotlibrc does not have an extension, glob does not find it (at least I think that's why)                    # So add it manually here:                  (r'mpl-data', [r'D:\Program Files\Python27\Lib\site-packages\matplotlib\mpl-data\matplotlibrc']),                  (r'mpl-data\images',glob.glob(r'D:\Program Files\Python27\Lib\site-packages\matplotlib\mpl-data\images\*.*')),                  (r'mpl-data\stylelib',glob.glob(r'D:\Program Files\Python27\Lib\site-packages\matplotlib\mpl-data\stylelib\*.*')),                  (r'mpl-data\fonts',glob.glob(r'D:\Program Files\Python27\Lib\site-packages\matplotlib\mpl-data\fonts\*.*'))]# for console program use 'console = [{"script" : "scriptname.py"}]setup(windows=[{"script" : "DefectPredict.py"}], options=opts, data_files=data_files)

打包方法:

在windows cmd窗口中,去到脚本所在目录,执行:python DPsetup.py py2exe
即可自动生成dist和build两个目录(build目录可以不用管),在dist目录下有我们需要的DefectPredict.exe


操作界面如下:


界面操作使用说明:

1、输入:数据为数字,以英文逗号分隔,数据代表的是累积缺陷数量分布。比如,第一天10个bug,第二天8个bug,第三个6个bug, 则输入数据为:10,18,24
2、函数模型:目前默认只有一个模型
3、参数:初始参数需要自己设定,界面上已给出默认初始参数值,实际使用时可依据这个进行调整。预测参数是由程序自动计算之后给出的。
4、Plot按钮:给定输入数据,并设置好初始参数后,点击Plot按钮,即可绘制预测曲线,同时显示预测参数值
5、问题百分比:输入一个100以内的数字,点击“计算天数”按钮,即可得到该问题数对应的天数
6、天数:输入一个数字,点击“计算问题数”的按钮,即可得到该天对应的问题数


0 0
原创粉丝点击