You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
13 KiB

5 months ago
import inspect
from tkinter import *
import tkinter as tk
import mpl_toolkits.axisartist as axisartist
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.optimize import approx_fprime
import data as gl_data
import numpy as np
from X1 import *
#------------------借用setphoto
set_img1 = None
set_img2 = None
# 这里有个将图像输出到主界面的函数
def set_phtot(index1):
global set_img1,set_img2
# 显示图像
Canvas2 = gl_data.Canvas2
if index1 == 1:
if set_img2 != None:
Canvas2.delete(set_img2)
gl_data.Img1=PhotoImage(file=r"dot.png")
# print(gl_data.Img1)
gl_data.Img1 = gl_data.Img1.zoom(2, 2)
set_img1=Canvas2.create_image(2, 10, image=gl_data.Img1, anchor=NW)
# Canvas2.itemconfigure(set_img1,int(420 * gl_data.MAGNIDICATION), int(300 * gl_data.MAGNIDICATION))
Canvas2.update()
print("已输出数据点")
elif index1 == 2:
if set_img1 != None:
Canvas2.delete(set_img1)
gl_data.Img2=PhotoImage(file=r"line.png")
# print(gl_data.Img2)
gl_data.Img2 = gl_data.Img2.zoom(2, 2)
set_img2=Canvas2.create_image(2, 10, image=gl_data.Img2, anchor=NW)
# Canvas2.itemconfigure(set_img2, 0,0,int(420*gl_data.MAGNIDICATION), int(300*gl_data.MAGNIDICATION))
Canvas2.update()
print("已输出数据点和曲线")
def window():
root_window = Tk()
root_window.title('函数拟合')
root_window.geometry('900x600') # 设置窗口大小:宽x高,注,此处不能为 "*",必须使用 "x"
# 设置主窗口的背景颜色,颜色值可以是英文单词或者颜色值的16进制数,除此之外还可以使用Tk内置的颜色常量
root_window["background"] = "white"
root_window.resizable(0, 0) # 防止用户调整尺寸
label1 = tk.Label(root_window, text="样本数据\n集文件", font=('Times', 8), bg="white",
width=13, height=3, # 设置标签内容区大小
padx=0, pady=0, borderwidth=0, )
label1.place(x=10, y=4) # 设置填充区距离、边框宽度和其样式(凹陷式)
label3 = tk.Label(root_window, text="", font=('Times', 8), bg="white", fg="black",
width=39, height=2, padx=0, pady=0, borderwidth=0, relief="ridge", highlightcolor="blue")
label3.place(x=122, y=10)
return root_window
# if __name__ == '__main__':
# root_window = window()
# root_window.mainloop()
def draw_axis(low, high, step=250):
fig = plt.figure(figsize=(4.4, 3.2)) # 设置显示大小
ax = axisartist.Subplot(fig, 111) # 使用axisartist.Subplot方法创建一个绘图区对象ax
fig.add_axes(ax) # 将绘图区对象添加到画布中
ax.axis[:].set_visible(False)# 通过set_visible方法设置绘图区所有坐标轴隐藏
ax.axis["x"] = ax.new_floating_axis(0, 0) # 添加新的x坐标轴
ax.axis["x"].set_axisline_style("-|>", size=1.0) # 给x坐标轴加上箭头
ax.axis["y"] = ax.new_floating_axis(1, 0) # 添加新的y坐标轴
ax.axis["y"].set_axisline_style("-|>", size=1.0) # y坐标轴加上箭头
ax.axis["x"].set_axis_direction("bottom") # 设置x、y轴上刻度显示方向
ax.axis["y"].set_axis_direction("left") # 设置x、y轴上刻度显示方向
# plt.xlim(low, high) # 把x轴的刻度范围设置
# plt.ylim(low, high) # 把y轴的刻度范围设置
# 把x轴和y轴的刻度范围设置
ax.set_xlim(low, high)
ax.set_ylim(low, high)
ax.set_xticks(np.arange(low, high + 5, step)) # 把x轴的刻度间隔设置
ax.set_yticks(np.arange(low, high + 5, step)) # 把y轴的刻度间隔设置
# plt.show()
return ax
# if __name__ == '__main__':
# draw_axis(gl_data.LOW, gl_data.HIGH)
def selfdata_show(sampleData, low, high):
x = sampleData[:,0]
y = sampleData[:,1]
ax = draw_axis(low, high) # 绘制x、y轴
ax.scatter(x, y, c='red') # 绘制样本数据点
# plt.show() #显示图片
plt.savefig(r"dot.png", facecolor='w', bbox_inches='tight') # 保存为png文件
plt.clf()
set_phtot(1) # 后续函数将图像显示在主界面中
# if __name__ == '__main__':
# sampleData = random_points(20, 0, 1000) # 生成样本点
# selfdata_show(sampleData, 0, 1000) # 绘制样本数据点
# 编程4.5-----------------------------------------
# m次多项式的最小二乘法
# 这里是没有对数据进行了标准化处理的
def least_square_method_UnNormalized(m, sampleData):
x = sampleData[:, 0]
y = sampleData[:, 1]
# 使用标准化的x构造A矩阵
A = np.vander(x, m + 1, increasing=True)
# 计算拟合系数theta使用最小二乘法的正规方程
theta = np.linalg.inv(A.T @ A) @ A.T @ y
# 计算残差平方和和其他统计量
residuals = y - A @ theta
residuals_squared_sum = residuals.T @ residuals
degrees_of_freedom = len(y) - (m + 1)
mean_squared_error = residuals_squared_sum / degrees_of_freedom
covariance_matrix = np.linalg.inv(A.T @ A) * mean_squared_error
return theta, covariance_matrix
# 这里是对数据进行了标准化处理的
def least_square_method(m, sampleData):
x = sampleData[:, 0]
y = sampleData[:, 1]
# 标准化x
x_mean = x.mean()
x_std = x.std()
x_normalized = (x - x_mean) / x_std
# 使用标准化的x构造A矩阵
A = np.vander(x_normalized, m + 1, increasing=True)
# 计算拟合系数theta使用最小二乘法的正规方程
theta = np.linalg.inv(A.T @ A) @ A.T @ y
# 计算残差平方和和其他统计量
residuals = y - A @ theta
residuals_squared_sum = residuals.T @ residuals
degrees_of_freedom = len(y) - (m + 1)
mean_squared_error = residuals_squared_sum / degrees_of_freedom
covariance_matrix = np.linalg.inv(A.T @ A) * mean_squared_error
return theta, covariance_matrix
# def curve_draw_line(curveData,sampleData):
# x_fit = curveData[:,0]
# y_fit = curveData[:,1]
# x = sampleData[:,0]
# y = sampleData[:,1]
# # 创建图形对象并绘制拟合曲线和样本点
# ax = draw_axis(0, 1000,step=250)
# ax.plot(x_fit, y_fit, label=f'FitCurve')
# ax.scatter(x, y, color='red', label='SampleData')
# # ax.set_xlabel('X')
# # ax.set_ylabel('Y')
# plt.legend()
# plt.show()
def draw_dots_and_line(curveData, sampleData, low,high):
x_fit, y_fit = curveData[:, 0], curveData[:, 1]
x, y = sampleData[:, 0], sampleData[:, 1]
# 创建图形对象并绘制拟合曲线和样本点
ax = draw_axis(low,high,step=250)
ax.plot(x_fit, y_fit, label=f'FitCurve')
ax.scatter(x, y, color='red', label='SampleData')
# ax.set_xlabel('X')
# ax.set_ylabel('Y')
plt.legend()
plt.show()
# 重写compute_curveData以适应标准化的需要
def compute_curveData2(low, high, step, theta, m, x_mean, x_std):
x_fit = np.arange(low, high, step)
# 将x_fit标准化
x_fit_normalized = (x_fit - x_mean) / x_std
A_fit = np.vander(x_fit_normalized, m + 1, increasing=True)
y_fit = A_fit @ theta
return np.column_stack((x_fit, y_fit))
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])
sy = np.array([10, 20, 10, 50, 80, 130, 210, 340, 550, 890])
sampleData = np.array(list(zip(x, sy)))# 将两个一维数组拼接成二维数组
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.5 END-----------------------------------------
# 编程 4.12 -----------------------------------------
def gradient_descent_method(sampleData, degree, learning_rate=0.001, iterations=50000):
def error_function(y_pred, y): # 定义误差函数error_function()
return y_pred - y
def gradient(degree,error,x_normalized,sampleNum,learning_rate): # 定义梯度函数gradient()
# 对每个参数计算梯度
for i in range(degree + 1):
# 计算损失函数对参数的偏导数(梯度)
gradient = np.dot(error, x_normalized**i) * 2 / sampleNum
# 梯度下降更新参数
theta[i] -= learning_rate * gradient
return theta
sx, sy = sampleData[:, 0], sampleData[:, 1]
# 初始化参数多项式系数为0
theta = np.zeros(degree + 1)
sampleNum = len(sx) # 样本数量
# 归一化x以改善数值稳定性
x_normalized = (sx - sx.mean()) / sx.std()
# 迭代梯度下降
for _ in range(iterations):
# 通过当前参数计算多项式的值
y_pred = np.polyval(theta[::-1], x_normalized)
# 计算预测值与真实值之间的误差
error = error_function(y_pred, sy)
# 计算梯度并更新theta
theta = gradient(degree, error, x_normalized, sampleNum, learning_rate)
# --------------------
# 设计矩阵
A = np.vander(x_normalized, N=degree + 1, increasing=True)
# 计算残差方差
residuals = sy - np.polyval(theta[::-1], x_normalized)
residuals_squared_sum = np.sum(residuals ** 2)
variance = residuals_squared_sum / (sampleNum - (degree + 1))
# 估计协方差矩阵
# 这里使用A的伪逆来近似最小二乘法中的(A^T A)^-1项
covariance_matrix = np.linalg.pinv(A.T @ A) * variance
# --------------------
return theta, covariance_matrix
def compute_curveData2(low, high, step, theta, m, x_mean, x_std):
x_fit = np.arange(low, high, step)
# 将x_fit标准化
x_fit_normalized = (x_fit - x_mean) / x_std
A_fit = np.vander(x_fit_normalized, m + 1, increasing=True)
y_fit = A_fit @ theta
return np.column_stack((x_fit, y_fit))
if __name__ == '__main__':
# sampleData = random_points(20,0, 1000)
# sx, sy = sampleData[:, 0], sampleData[:, 1]
# 这里为了便于观察使用设计好的数据
sx = np.array([0, 100, 200, 300, 400, 500, 600, 700, 800, 900])
sy = np.array([10, 20, 10, 50, 80, 130, 210, 340, 550, 890])
sampleData = np.array(list(zip(sx, sy)))
# 设置一个较小的学习率和较多的迭代次数
learning_rate = 0.001
iterations = 50000
degree = 4
# 梯度下降算法求解
theta, covariance_matrix = gradient_descent_method(sampleData, degree, learning_rate, iterations)
# 计算画图所需要的拟合数据
curveData = compute_curveData2(0, 1000, 1, theta, degree, sx.mean(), sx.std())
# 画拟合结果
draw_dots_and_line(curveData, sampleData, 0, 1000)
# 这里是为了后续fit函数中调用梯度下降法
# 这里将梯度下降法的输入变为了与curve_fit相同由于协方差矩阵只对于多项式函数适用因此这里返回的第二个值为False
# 这里搞好了就是函数的梯度不能够确定因此调用了包approx_fprime
def gradient_descent_method_X5(func, sx, sy, learning_rate=0.001, iterations=5000):
def error_function(y_pred, y): # 定义误差函数error_function()
return (y_pred - y) ** 2
# 定义损失函数和梯度的计算
def loss_grad(theta):
y_pred = func(sx_normalized, *theta)
# 计算预测值与真实值之间的误差
error = error_function(y_pred, sy)
# 调用包来实现对任意函数计算梯度
grad = approx_fprime(theta, lambda t: np.mean((func(sx_normalized, *t) - sy) ** 2), epsilon=1e-6)
return error, grad
# 因为X4和X5中的函数方式不一样X5中不是function类没有self参数因此要处理不同的参数要求
sig = inspect.signature(func) # 获取所有参数的名称
params = [param.name for param in sig.parameters.values()]
# print(params)
# 动态确定func需要的参数数量减去self, sx或者只有sx
if 'self' in params:
params_count = func.__code__.co_argcount - 2
else:
params_count = func.__code__.co_argcount - 1
# print(params_count)
# 基于参数数量初始化theta
theta = np.random.randn(params_count) * 0.01
# 标准化sx
mean_sx = np.mean(sx)
std_sx = np.std(sx)
sx_normalized = (sx - mean_sx) / std_sx
# 梯度下降循环
for _ in range(iterations):
err, grad = loss_grad(theta)
theta -= learning_rate * grad
# 由于不再特定于多项式函数,以下部分相关于残差方差和协方差矩阵的计算需要根据实际情况调整
# 但是实际没有使用到这个矩阵因此返回False
return theta, False