如何将离散点转换为连续函数?寻求可调用的连续移动平均函数
解决方案:从离散点生成连续平滑函数的替代方法
我明白你之前用三次多项式GLM拟合时遇到了泛化性差的问题——这类全局多项式模型很容易在数据端点或稀疏区域出现过拟合的波动。下面给你几个实用的替代方案,都可以封装成类似moving_average(x)的可调用函数,适合从离散点生成连续平滑的预测曲线:
1. 局部加权回归(LOESS)—— 简单鲁棒的平滑首选
LOESS是R内置的局部加权线性回归工具,会针对每个预测点附近的样本分配权重(距离越近权重越高),完全避免全局多项式的过拟合问题,泛化性极强。
# 先拟合LOESS模型(span控制平滑程度,0-1之间,越大越平滑) loess_model <- loess(y ~ x, data = df_demand, span = 0.7) # 封装成可直接调用的预测函数 predict_loess <- function(x_new) { predict(loess_model, newdata = data.frame(x = x_new)) } # 测试x=0.3的预测值 predict_loess(0.3) # 可视化对比原多项式模型 data.frame(x = seq(0,1,length = 1000)) %>% mutate(pred_loess = predict_loess(x), pred_poly = predict(model, .)) %>% ggplot()+ geom_line(aes(x = x, y = pred_loess), color = "blue", size = 1, label = "LOESS平滑")+ geom_line(aes(x = x, y = pred_poly), color = "green", linetype = "dashed", label = "三次多项式")+ geom_point(data = df_demand, aes(x = x, y = y), color = "red", size = 2)+ labs(title = "LOESS vs 三次多项式拟合对比", y = "预测值")+ theme_minimal()
2. 高斯核移动平均—— 贴合你需求的"连续移动平均"实现
这个自定义函数完全符合你想要的连续移动平均逻辑:用高斯核函数给每个预测点附近的样本分配权重,距离越近权重越高,最终输出加权平均后的y值。
# 自定义高斯核移动平均函数 moving_average <- function(x_new, data = df_demand, bandwidth = 0.2) { # 定义高斯核权重计算函数 kernel_weights <- function(x, x0, h) { exp(-((x - x0)^2)/(2*h^2)) / (h*sqrt(2*pi)) } # 对每个输入x_new计算加权平均y值 sapply(x_new, function(x0) { weights <- kernel_weights(data$x, x0, bandwidth) weighted.mean(data$y, weights = weights) }) } # 测试x=0.3的预测值 moving_average(0.3) # 可视化效果 data.frame(x = seq(0,1,length = 1000)) %>% mutate(pred_ma = moving_average(x)) %>% ggplot()+ geom_line(aes(x = x, y = pred_ma), color = "purple", size = 1)+ geom_point(data = df_demand, aes(x = x, y = y), color = "red", size = 2)+ labs(title = "高斯核移动平均拟合", y = "预测值")+ theme_minimal()
参数说明:bandwidth控制"移动窗口"的宽窄,越大平滑程度越高,越小越贴合原始数据,你可以根据需求调整。
3. 分段滑动窗口平均—— 传统移动平均的连续化版本
如果你更偏向传统滑动窗口的逻辑,这个函数会筛选预测点周围固定范围内的样本,按距离倒数加权平均,窗口外的点不参与计算:
# 分段滑动窗口平均函数 window_moving_average <- function(x_new, data = df_demand, window_width = 0.3) { sapply(x_new, function(x0) { # 筛选窗口内的样本点 in_window <- data$x >= x0 - window_width/2 & data$x <= x0 + window_width/2 if(sum(in_window) == 0) { # 窗口内无数据时,取最近点的y值 nearest_idx <- which.min(abs(data$x - x0)) return(data$y[nearest_idx]) } # 按距离倒数加权平均(避免除以0加极小值) weights <- 1 / (abs(data$x[in_window] - x0) + 1e-6) weighted.mean(data$y[in_window], weights = weights) }) } # 测试x=0.3的预测值 window_moving_average(0.3)
这些方法均基于局部数据做平滑,相比全局多项式模型泛化性更好,不会在数据稀疏区域出现离谱的波动。你可以根据对平滑程度的需求,调整各函数的参数找到最适合的曲线。
内容的提问来源于stack exchange,提问作者cyrilb38




