ggplot2::geom_smooth调用gamlss::gamlss(ZAGA分布)报错及零膨胀连续分布拟合替代方案咨询
解决geom_smooth结合gamlss(ZAGA)的行数不匹配问题及替代方案
为什么会出现行数不匹配的警告?
直接在geom_smooth中使用method = gamlss::gamlss会报错,是因为stat_smooth的内部逻辑对拟合函数的返回值有特定要求——它期望模型的predict方法返回的结果长度和输入的预测网格行数完全一致,但gamlss的模型对象和stat_smooth的预期结构不兼容,导致了行数差异(比如示例中的80 vs 81),最终绘图为空。简单来说,gamlss并没有被geom_smooth官方支持,不能直接作为method参数传入。
解决方案1:手动拟合+预测绘图(支持置信区间、分组/分面)
既然直接用geom_smooth行不通,我们可以手动完成模型拟合、预测值+置信区间计算,再用ggplot2的基础图层绘图,这种方式完全可控,还能轻松支持分组和分面需求。
基础示例(单组数据)
library(gamlss) library(dplyr) library(ggplot2) # 你的示例数据 in_df <- tibble(x = c(0.1, 3.1, 3, 0.5, 2, 0.2, 2.9, 1.2, 1, 0.2), y = c(0, 2, 0, 0.2, 4, 0, 0, 1, 0, 0)) # 拟合ZAGA模型 gamlss_fit <- gamlss(y ~ x, data = in_df, family = ZAGA()) # 生成密集的x预测序列(比原始数据更平滑) pred_x <- tibble(x = seq(min(in_df$x), max(in_df$x), length.out = 100)) # 预测响应值的均值和标准误 preds <- predict(gamlss_fit, newdata = pred_x, type = "response", se.fit = TRUE) # 整理预测结果(计算95%置信区间) pred_df <- pred_x %>% mutate(fit = preds$fit, se = preds$se.fit, lower = fit - 1.96 * se, # 95%置信区间 upper = fit + 1.96 * se) # 绘图:原始点+拟合线+置信区间 ggplot(in_df, aes(x = x, y = y)) + geom_point(size = 2, alpha = 0.7) + geom_line(data = pred_df, aes(y = fit), color = "#2c3e50", linewidth = 1) + geom_ribbon(data = pred_df, aes(y = fit, ymin = lower, ymax = upper), fill = "#2c3e50", alpha = 0.2)
分组/分面示例
如果你的数据有分组变量,我们可以用dplyr的分组嵌套功能批量拟合模型并生成预测:
set.seed(123) # 保证结果可复现 # 构造带分组的模拟数据 grouped_df <- bind_rows( tibble(x = runif(20, 0, 3), y = rZAGA(20, mu = 1 + 0.5*x, sigma = 0.3, nu = 0.7), group = "A"), tibble(x = runif(20, 0, 3), y = rZAGA(20, mu = 2 + 0.8*x, sigma = 0.4, nu = 0.6), group = "B") ) # 分组拟合模型并生成预测数据 group_preds <- grouped_df %>% group_by(group) %>% nest() %>% mutate( # 为每个分组拟合ZAGA模型 model = map(data, ~ gamlss(y ~ x, data = ., family = ZAGA())), # 为每个分组生成预测序列和置信区间 pred_data = map2(model, data, function(mod, df) { pred_x <- tibble(x = seq(min(df$x), max(df$x), length.out = 100)) preds <- predict(mod, newdata = pred_x, type = "response", se.fit = TRUE) pred_x %>% mutate( fit = preds$fit, se = preds$se.fit, lower = fit - 1.96*se, upper = fit + 1.96*se, group = df$group[1] ) }) ) %>% unnest(pred_data) # 分组绘图(分面或叠加均可) ggplot(grouped_df, aes(x = x, y = y, color = group, fill = group)) + geom_point(alpha = 0.6) + geom_line(data = group_preds, aes(y = fit), linewidth = 1) + geom_ribbon(data = group_preds, aes(y = fit, ymin = lower, ymax = upper), alpha = 0.2, color = NA) + facet_wrap(~group) # 去掉这行就是叠加显示
解决方案2:适配geom_smooth的自定义方法(可选)
如果你一定要用geom_smooth的语法,可以自定义一个适配gamlss的方法函数,但这种方式相对复杂,且灵活性不如手动拟合。核心是让函数返回stat_smooth期望的结构(包含fit和se.fit的列表),示例框架如下:
# 自定义适配gamlss的方法函数 gamlss_smooth <- function(formula, data, family = ZAGA(), ...) { mod <- gamlss(formula, data = data, family = family, ...) pred <- predict(mod, newdata = data, type = "response", se.fit = TRUE) list(fit = pred$fit, se.fit = pred$se.fit) } # 使用自定义方法 ggplot(in_df) + geom_point(aes(x = x, y = y)) + geom_smooth(aes(x = x, y = y), method = gamlss_smooth, formula = y ~ x, se = TRUE)
不过这种方法的预测是基于原始数据点,而不是平滑的网格线,视觉效果不如手动生成密集预测序列好。
关于零调整连续分布的替代方案
如果你希望用method = "gam"(即mgcv包的gam函数),mgcv本身没有直接支持ZAGA分布,但可以考虑以下两种思路:
- 两阶段模型:先拟合二项模型预测是否为0,再对非零数据拟合gamma模型。但这种方式是分开的两个模型,不如ZAGA的单模型拟合效率高,且无法直接整合出统一的置信区间。
- 自定义族函数:为mgcv编写ZAGA的族函数,但这需要一定的统计编程基础,门槛较高。
对于你的日降雨量数据,ZAGA仍然是最适配的模型,因此推荐使用手动拟合+预测的绘图方式,既能保留模型的拟合效果,又能满足可视化需求。
内容的提问来源于stack exchange,提问作者climatestudent
相关产品推荐
相关产品推荐

