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、天数:输入一个数字,点击“计算问题数”的按钮,即可得到该天对应的问题数
- Python+Matplotlib+PyQt4做的简单数据预测工具
- Python的GUI工具-->PyQt4入门
- 【Python-3.5】matplotlib做简单折线图
- Python使用线性回归简单预测数据
- python用最小二乘法分析数据趋势以及做数据预测
- Python--绘图工具matplotlib的使用
- Python绘图工具matplotlib的安装
- 安装Python的画图工具matplotlib
- python+PyQt4写的一个简单的计算器
- 【Python-matplotlib】subplot2grid()函数的简单示例
- 用Tkinter + Python做的简单网络配置工具
- python 数据可视化 matplotlib学习一:绘制简单的折线图
- python matplotlib简单示例
- 读书笔记-python,数据可视化之matplotlib简单实用
- python爬虫webdriver.Chrome 数据可视化简单案例matplotlib
- Python做科学计算---matplotlib
- python简单预测模型
- matplotlib+pyqt4 内容整理
- Intent与IntentFilter
- #if defined #ifdef #ifndef 的区别
- 使用axis开发webservice服务java.net.ConnectException: Connection refused: connect异常的解决办法
- MFC中静态文本框(CStatic)响应鼠标单击事件
- Map的序列的排序
- Python+Matplotlib+PyQt4做的简单数据预测工具
- spring mvc 自定义HandlerMethodArgumentResolver
- 数据挖掘入门笔记(一)--认识数据
- Parallax Mapping
- V8 之旅: 垃圾回收器
- hdu 5445 Food Problem 分组背包,二进制拆分
- hdu2196
- Spring注解
- Caffe中的特殊layer解释【慢慢填坑中】