import mpl_toolkits.axisartist as axisartist from scipy.optimize import curve_fit from pandas import DataFrame import matplotlib.pyplot as plt import numpy as np import pylab as mpl from tkinter import * import pandas as pd import tkinter import sys import tkinter as tk from tkinter import filedialog import re import function import data as gl_data from X1 import * from X2 import * mpl.rcParams['font.sans-serif'] = ['SimHei'] # 解决matplotlib中文不显示问题 plt.rcParams['axes.unicode_minus'] = False # 解决matplotlib负数坐标显示问题 # 创建一个二维数组sampleData,用于存储样本数据 sampleData = None global Q_root,F_root global root_window global label1,label2,label3 # 编程4.6 ----------------------------------------- # 定义一个处理文件的相关函数 def askfile(): # 选择需要加载的数据文件,并返回文件的路径 filename = tkinter.filedialog.askopenfilename() if filename != '': # 若选中了一个文件,则对文件进行读取 label3.config(text=filename) # 显示文件路径,其中label3为对应的按钮 read_sample_data(filename) # 将文件读取并存到sampleData中 # print_sample_data(filename)# 将文件读取并逐行输出 selfdata_show(gl_data.SampleData[:,0], gl_data.SampleData[:,1], gl_data.LOW, gl_data.HIGH) else: # 若未选择文件,则显示为空 label3.config(text='') def print_sample_data(file_path):#打开对应文件 with open(file_path, 'r') as file: for line in file: # 逐行读取文件 line = line.strip('\n') # 移除换行符 sx, sy = line.split(' ') # 以空格分割x,y print(f'sx: {float(sx)}, sy: {float(sy)}') # 将sx、sy转换为浮点数并打印 def read_sample_data(file_path): x, y = [], [] # 初始化x,y with open(file_path, 'r') as file: for line in file: # 逐行读文件 line = line.strip('\n') # 移除换行符 sx, sy = line.split(' ') # 以空格分割x,y x.append(float(sx)) # 将sx转换为浮点数并加入数组 y.append(float(sy)) # 将sy转换为浮点数并加入数组 # x,y数组转为array并赋值给全局变量sampleData x = np.array(x) # 列表转数组 y = np.array(y) gl_data.SampleData = np.array(list(zip(x, y))) # 编程4.6 END----------------------------------------- # 编程4.7 ----------------------------------------- def generate_and_plot_sample_data(sampleData, Low, High): sx,sy = sampleData[:, 0],sampleData[:, 1] draw_axis(Low, High) # 绘制坐标轴 plt.scatter(sx, sy, color='red') # 绘制样本数据点 plt.savefig(r"dot.png", facecolor='w', bbox_inches='tight') # 保存到本地 plt.close() # 清除内存 set_phtot(1) # 显示到主界面 4.7示例时没有编程该函数 # if __name__ == '__main__': # num_samples = 25 #设置样本点数量 # sampleData = random_points(num_samples, -1000, 1000) # 生成随机样本数据25个 # generate_and_plot_sample_data(sampleData, gl_data.LOW, gl_data.HIGH) # 编程4.7 END----------------------------------------- # 编程4.8 ----------------------------------------- def draw_dots_and_line(curveData, sampleData, low, high): x_fit, y_fit = curveData[:, 0], curveData[:, 1] x, y = sampleData[:, 0], sampleData[:, 1] draw_axis(low, high) # 画xy坐标轴 positive_mask = y >= 0.0 # 给样本点分类 negative_mask = y < 0.0 # 给样本点分类 positive_colors = ['red' if xx >= 0.0 else 'blue' for xx in x] # 给样本点分类 negative_colors = ['green' if xx >= 0.0 else 'purple' for xx in x] # 给样本点分类 # 创建图形对象并绘制拟合曲线和样本点 ax = draw_axis(low,high,step=250) # 根据样本点类型决定样本点颜色 ax.scatter(x[positive_mask], y[positive_mask], color=np.array(positive_colors)[positive_mask], lw=1) ax.scatter(x[negative_mask], y[negative_mask], color=np.array(negative_colors)[negative_mask], lw=1) plt.plot(x_fit, y_fit, color='blue', label='Fitted Curve')# 绘制拟合曲线 # plt.savefig(r"dot5.png", facecolor='w') # 保存到本地 # plt.legend() # plt.show() # X5修改后展示到主界面中 plt.legend(loc='center left', bbox_to_anchor=(1, 0.9), fontsize=9) plt.savefig(r"line.png", facecolor='w', bbox_inches='tight')# 将图片保存到本地 plt.close()# 清除内存 set_phtot(2)# 将图片显示到程序中 # if __name__ == '__main__': # gl_data.SampleData = random_points(25,-1000, 1000) # # print("gl_data.SampleData",gl_data.SampleData) # draw_dots_and_line(gl_data.LOW, gl_data.HIGH, gl_data.SampleData) # if __name__ == '__main__': # LimitNum = 1000 # # sampleData = random_points(20,0, 1000) # # x, y = sampleData[:, 0], sampleData[:, 1] # # 这里为了便于观察使用设计好的数据 # x = np.array([0, 100, 200, 300, 400, 500, 600, 700, 800, 900]) # y = np.array([10, 20, 10, 50, 80, 130, 210, 340, 550, 890]) # sampleData = np.array(list(zip(x, y))) # 将两个一维数组拼接成二维数组 # m = 3 # 使用三次函数拟合 # theta, covariance_matrix = least_square_method(m, sampleData) # curveData = compute_curveData2(0,1000,1,theta,m, x.mean(), x.std()) # draw_dots_and_line(curveData,sampleData,0,1000) # 编程4.8 END----------------------------------------- # 编程4.9 ----------------------------------------- def input_num(root_tk): global top top = tk.Toplevel(root_tk) top.geometry("300x50") top.title('坐标点个数') label1 = Label(top, text="坐标点个数") label1.grid(row=0) # 这里的side可以赋值为LEFT RTGHT TOP BOTTOM num1 = IntVar() entry1 = Entry(top, textvariable=num1) num1.set(0) entry1.grid(row=0, column=1) Label(top, text=" ").grid(row=0, column=3) Button(top, text="确定", command=lambda: input_data(root_tk, int(entry1.get()))).grid(row=0, column=3) top.mainloop() def add_sample_data(): global sample_x, sample_y, sample_data global entry_x,entry_y,label_status, numx try: x = float(entry_x.get()) y = float(entry_y.get()) except: label_status.config(text="输入不合法") return entry_x.delete(0, tk.END) entry_y.delete(0, tk.END) if min(x, y) < gl_data.LOW or max(x, y) > gl_data.HIGH: label_status.config(text="输入超过范围") return elif len(sample_data) < numx: label_status.config(text="点对已添加") sample_data.append((x, y)) sample_x.append(x) sample_y.append(y) else: label_status.config(text="已达到最大数量") def check_sample_data(): global label_status,numx,sample_x,sample_y,sample_data if len(sample_data) == numx: label_status.config(text="已达到最大数量") gl_data.X = np.array(sample_x) gl_data.Y = np.array(sample_y) print('已添加', sample_data) sys.exit() else: label_status.config(text="还需输入{}个点对".format(numx - len(sample_data))) print(sample_data) def input_data(root_tk, num): global top global sample_x,sample_y,sample_data global numx numx = num sample_x = [] sample_y = [] sample_data = [] top.destroy() top = tk.Toplevel(root_tk) top.geometry("300x200") top.title('坐标') global entry_x, entry_y, label_status label_x = tk.Label(top, text="X 值:") label_x.pack() entry_x = tk.Entry(top) entry_x.pack() label_y = tk.Label(top, text="Y 值:") label_y.pack() entry_y = tk.Entry(top) entry_y.pack() button_add = tk.Button(top, text="添加", command=add_sample_data) button_add.pack() button_check = tk.Button(top, text="检查", command=check_sample_data) button_check.pack() label_status = tk.Label(top, text="") label_status.pack() top.mainloop() # 编程4.9 END----------------------------------------- # 编程4.10 ----------------------------------------- def error_func(func, sampleData, theta): # 定义误差函数 sx, sy = sampleData[:, 0], sampleData[:, 1] y_pred = func(theta, sx) # 使用func函数和参数theta计算预测值 error = np.sum((sy - y_pred) ** 2) # 计算误差平方和 ss_tot = np.sum((sy - np.mean(sy)) ** 2) # 计算总平方和 r_squared = 1 - (error / ss_tot) # 计算决定系数(R^2) return error, r_squared # 这里是处理了标准化的error_func def error_func2(func, sampleData, theta): # 定义误差函数 sx, sy = sampleData[:, 0], sampleData[:, 1] x_normalized = (sx - sx.mean()) / sx.std() # 使用func函数和参数theta计算预测值 y_pred = func(theta, x_normalized) # 计算误差平方和 error = np.sum((sy - y_pred) ** 2) # 计算总平方和 ss_tot = np.sum((sy - np.mean(sy)) ** 2) # 计算决定系数(R^2) r_squared = 1 - (error / ss_tot) return error, r_squared # fit函数中修改使用方法为梯度下降法,并输出拟合结果 def fit(sampleData, m): theta, covariance_matrix = least_square_method(m, sampleData) # 计算拟合结果 # 计算拟合误差 if m == 1: func = linear_function if m == 2: func = quadratic_function # 此处以m=2为例,省略其他函数 if m == 3: func = qubic_function if m == 4: func = quartic_function error,r_squared = error_func2(func, sampleData, theta) fit_function_name = func.__name__ # 打印拟合函数的形式、系数、误差和优度 print("拟合函数形式:{}".format(fit_function_name)) print("标准化拟合系数:{}".format(theta)) print("误差:{:.4f}".format(error)) print("拟合优度(R^2):{:.4f}".format(r_squared)) return theta, covariance_matrix # if __name__ == '__main__': # # sampleData = random_points(20,0, 1000) # # x = sampleData[:,0] # # y = sampleData[:,1] # # 这里为了便于观察使用设计好的数据 # x = np.array([0, 100, 200, 300, 400, 500, 600, 700, 800, 900]) # y = np.array([10, 20, 10, 50, 80, 130, 210, 340, 550, 890]) # sampleData = np.array(list(zip(x, y))) # 将两个一维数组拼接成二维数组 # m = 2 # 假设选择使用二次函数拟合 # theta, covariance_matrix = fit(sampleData, m) # ------fit_X4前提函数------- # 为了适应扩展的函数,需要根据用户输入的函数类型构造函数 def fitting(letters,result): code_str = ''' def func({}): return {} '''.format(letters,result) return code_str # 构造用户自定义的函数 def create_func(code_str): # 创建一个空的命名空间 namespace = {} # 使用exec函数执行字符串代码,并指定命名空间为locals exec(code_str, globals(), namespace) # 返回命名空间中的函数对象 return namespace['func'] # 简化版本的goodness_of_fit def goodness_of_fit_easy(y_fitting, y_no_fitting): """ 计算拟合优度R^2 :param y_fitting: List[int] or array[int] 拟合好的y值 :param y_no_fitting: List[int] or array[int] 待拟合y值 :return: 拟合优度R^2 """ y_mean = sum(y_no_fitting) / len(y_no_fitting) # 计算SST sst = sum((y - y_mean) ** 2 for y in y_no_fitting) # 计算SSE sse = sum((y_fitting[i] - y_no_fitting[i]) ** 2 for i in range(len(y_fitting))) # 计算R^2 r_squared = 1 - sse / sst return r_squared # ------fit_X4前提函数------- # 这里是为了后续的fit能够直接用到按钮中,而不是BF_Fit写一大堆 def fit_X4(xian_index, sampleData): sx, sy = sampleData[:, 0], sampleData[:, 1] cur = function.Fun[xian_index] # 装载正在选择的函数 func = cur.get_fun() # 获取当前函数func popt, pcov = gradient_descent_method_X5(func, sx, sy) # 用curve_fit来对数据进行拟合 x_normalized = (sx - np.mean(sx)) / np.std(sx) # 标准化处理的x y_pred = func(x_normalized, *popt) # 获取y的预测值 gl_data.CurveData = compute_curveData_X5_2(func, popt, sampleData) # 计算拟合曲线的数据 rr = goodness_of_fit_easy(y_pred, sy) # 计算本次拟合的R*R值,用于表示拟合优度 # 输出拟合后的各个参数值 ans = '\n函数标准化系数:F(x) = ' for i in range(cur.variable): if i == 4: ans += '\n' if i != 0: ans += ', ' ans += chr(ord('a') + i) + '=' + '{:.2e}'.format(popt[i]) # str(round(gl_data.popt[i], 3)) gl_data.Out = '函数形式:' + cur.name + ' ' gl_data.Out += cur.demo gl_data.Out += ans gl_data.Out += '\n拟合优度(R\u00b2):' + str(round(rr, 5)) # 编程4.17----------------------------------------- # 选择 def fit_X4(xian_index, sampleData): sx, sy = sampleData[:, 0], sampleData[:, 1] cur = function.Fun[xian_index] # 装载正在选择的函数 func = cur.get_fun() # 获取当前函数func # params_count = func.__code__.co_argcount - 2 # 计算参数数量 # if params_count <= 3 and len(sampleData) <= 20 : # 当参数和样本点数量较少时 # popt, pcov = least_square_method(func, sx, sy) # 用最小二乘法来对数据进行拟合 # else: # 当参数和样本点数量较多时 # popt, pcov = gradient_descent_method_X5(func, sx, sy) # 用梯度下降法来对数据进行拟合 popt, pcov = gradient_descent_method_X5(func, sx, sy) x_normalized = (sx - np.mean(sx)) / np.std(sx) # 标准化处理的x y_pred = func(x_normalized, *popt) # 获取y的预测值 gl_data.CurveData = compute_curveData_X5_2(func, popt, sampleData) # 计算拟合曲线的数据 rr = goodness_of_fit_easy(y_pred, sy) # 计算本次拟合的R*R值,用于表示拟合优度 # 输出拟合后的各个参数值 ans = '\n函数标准化系数:F(x) = ' for i in range(cur.variable): if i == 4: ans += '\n' if i != 0: ans += ', ' ans += chr(ord('a') + i) + '=' + '{:.2e}'.format(popt[i]) gl_data.Out = '函数形式:' + cur.name + ' ' gl_data.Out += cur.demo gl_data.Out += ans gl_data.Out += '\n拟合优度(R\u00b2):' + str(round(rr, 5)) # 编程4.17 END----------------------------------------- # 这里是为了后续的fit能够直接用到按钮中,而不是BF_Fit写一大堆 def fit_X5(xian_index, sampleData): sx, sy = sampleData[:, 0], sampleData[:, 1] letters, result = gl_data.FITT_SAVE['variable'][xian_index], gl_data.FITT_SAVE['function'][xian_index] code_str = fitting(letters, result) # 获取当前选择函数的表达式 func = create_func(code_str) # 构造当前函数的func方法 popt, pcov = gradient_descent_method_X5(func, sx, sy) # 用curve_fit来对数据进行拟合 x_normalized = (sx - np.mean(sx)) / np.std(sx) y_pred = func(x_normalized, *popt) # 获取y的预测值 gl_data.CurveData = compute_curveData_X5_2(func, popt, sampleData) # 计算拟合曲线的数据 rr = goodness_of_fit_easy(y_pred, sy) # 计算本次拟合的R*R值,用于表示拟合优度 # 输出拟合后的各个参数值 ans = '\n函数系数:F(x) = ' letter = str(letters[2:]).split(',') pattern = re.compile('|'.join(letter)) s = pattern.sub('{:.2g}', gl_data.FITT_SAVE['demo'][xian_index]) print(pattern) print(gl_data.FITT_SAVE['demo'][xian_index]) print("s:", s) ans += str(s.format(*popt)) gl_data.Out = '函数形式:' + gl_data.FITT_SAVE['name'][xian_index] + ' ' gl_data.Out += gl_data.FITT_SAVE['demo'][xian_index] gl_data.Out += ans gl_data.Out += '\n拟合优度(R\u00b2):' + str(round(rr, 5)) # 这里是使用curve_fit实现的,实际使用的方式 def fit_XX(xian_index, sampleData): sx, sy = sampleData[:, 0], sampleData[:, 1] letters, result = gl_data.FITT_SAVE['variable'][xian_index], gl_data.FITT_SAVE['function'][xian_index] code_str = fitting(letters, result) # 获取当前选择函数的表达式 func = create_func(code_str) # 构造当前函数的func方法 popt, pcov = curve_fit(func, sx, sy) # 用curve_fit来对数据进行拟合 y_pred = func(sx, *popt) # 获取y的预测值 gl_data.CurveData = compute_curveData_X5(func, popt) # 计算拟合曲线的数据 rr = goodness_of_fit_easy(y_pred, sy) # 计算本次拟合的R*R值,用于表示拟合优度 # 输出拟合后的各个参数值 ans = '\n函数系数:F(x) = ' letter = str(letters[2:]).split(',') pattern = re.compile('|'.join(letter)) s = pattern.sub('{:.2g}', gl_data.FITT_SAVE['demo'][xian_index]) print(pattern) print(gl_data.FITT_SAVE['demo'][xian_index]) print("s:", s) ans += str(s.format(*popt)) gl_data.Out = '函数形式:' + gl_data.FITT_SAVE['name'][xian_index] + ' ' gl_data.Out += gl_data.FITT_SAVE['demo'][xian_index] gl_data.Out += ans gl_data.Out += '\n拟合优度(R\u00b2):' + str(round(rr, 5)) # 编程4.10 END----------------------------------------- # 编程4.11 ----------------------------------------- def change_Q(no): gl_data.Quadrant = no #更改全局变量的象限显示 if no: #若为一象限,则修改显示下限为0 gl_data.LOW = 0 else: #若为四象限,则修改显示下限为-gl_data.MAXV gl_data.LOW = -gl_data.MAXV q_button_X3() #更新象限显示面板 def q_button_X3(): r = 7.5 rr = 2.5 for widget in Q_root.winfo_children(): widget.destroy() q_cv = tk.Canvas(Q_root, width=450, height=500) q_cv.place(x=0, y=0) l = tk.Label(Q_root, text='坐标轴', bd=0, font=("微软雅黑", 16) , anchor=W) l.place(x=20, y=0, width=80, height=50,) # 四象限按钮 b1 = tk.Button(Q_root, text='四象限', bd=0, font=("微软雅黑", 16) , command=lambda: change_Q(0), anchor=W) b1.place(x=170, y=0, width=80, height=50,) # 一象限按钮 b2 = tk.Button(Q_root, text='一象限', bd=0, font=("微软雅黑", 16) , command=lambda: change_Q(1), anchor=W) b2.place(x=320, y=0, width=80, height=50,) # 绘制标记框 q_cv.create_oval(140 - r, 25 - r, 140 + r, 25 + r, fill="white", width=1, outline="black") q_cv.create_oval(290 - r, 25 - r, 290 + r, 25 + r , fill="white", width=1, outline="black") # 根据当前的象限选择值来填充标记框 if gl_data.Quadrant == 0: q_cv.create_oval(140 - rr, 25 - rr, 140 + rr, 25 + rr, fill="black", width=1, outline="black")# 绘制象限 else: q_cv.create_oval(290 - rr, 25 - rr, 290 + rr, 25 + rr, fill="black", width=1, outline="black") if __name__ == '__main__': # 象限选择相关界面 Q_root = tk.Tk() Q_root.geometry("500x550") # 设置窗口的大小和位置 q_button_X3() Q_root.mainloop() # 编程4.11 END-----------------------------------------