You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合facet_wrap与geom_smooth正确使用geom_label_repel?

解决分面后geom_smooth曲线末端标签异常问题

问题核心是直接用stat="smooth"的after_stat获取范围时,未考虑分面的分组逻辑,导致标签匹配错误。解决思路是提前计算每个分面+曲线分组的平滑末端点,再用该数据绘制标签。

修正后的代码

library(ggrepel)
library(tidyverse)

df<-structure(list(month_day = structure(c(19124, 19124, 19132, 19132,19145, 19146, 19171, 19172, 19160, 19185, 19201, 19214, 19229,19244, 19110, 19259, 19273, 19104, 19116, 19130, 19131, 19144, 19166, 19179, 19193, 19208, 19229, 19243, 19256, 19271, 19131, 19145, 19145, 19171, 19171, 19157, 19160, 19185, 19185, 19201, 19201, 19214, 19214, 19229, 19229, 19244, 19244, 19110, 19110, 19259, 19259, 19273, 19273, 19104, 19104, 19116, 19144, 19130, 19166, 19179, 19208, 19229, 19243, 19256, 19256, 19271, 19271,19131, 19145, 19145, 19171, 19171, 19157, 19185, 19185, 19201, 19201, 19214, 19214, 19214, 19229, 19229, 19244, 19244, 19110,  19110, 19259, 19259, 19273, 19273, 19104, 19104, 19116, 19116,19130, 19144, 19166, 19179, 19179, 19193), class = "Date"), 
               Sensibilite = c(4,1, 0, 1, 0, 2, 3, 3, 2, 3, 4, 5, 5, 2, 0, 4, 4, 0, 1, 2, 3, 1,2, 2, 3, 3, 2, 2, 5, 4, 0, 2, 1, 3, 0, 2, 1, 5, 4, 5, 5, 5, 1, 4, 1, 3, 0, 0, 0, 5, 4, 5, 5, 0, 1, 1, 1, 1, 2, 1, 2, 2, 0, 4,3, 5, 4, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 1, 0, 0,0, 1, 0, 1, 1, 1, 0, 1, 0, 2, 0, 0, 0, 1, 1),
               Nom = c("Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom1","Nom1", "Nom1", "Nom1", "Nom1", "Nom1", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2","Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2","Nom2", "Nom2", "Nom2", "Nom2","Nom2", "Nom2", "Nom2", "Nom2","Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom2", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3",  "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3",  "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3", "Nom3","Nom3", "Nom3"), 
               Annee = c("2021","2021","2021", "2021", "2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2022", "2022", "2022","2022", "2022","2022", "2022", "2022", "2022", "2022","2022", "2022","2022","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021","2021", "2021","2021","2021","2021","2021", "2021","2021","2021","2021","2022","2022","2022","2022","2022","2022","2022","2022","2022","2022","2022","2022", "2022","2022","2021","2021","2021","2021","2021","2021", "2021","2021","2021","2021","2021","2021","2021","2021","2021","2021", "2021","2021","2021","2021","2021","2021", "2021","2022","2022","2022", "2022","2022", "2022","2022","2022","2022", "2022"), 
               Lieu = c("Lieu1","Lieu1","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu1","Lieu1","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu1","Lieu1","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2","Lieu2" )), 
              row.names = c(NA, -100L), class = c("tbl_df", "tbl", "data.frame"))                                                                                    

# 计算每个分面+Nom对应的平滑曲线末端点
smooth_endpoints <- df %>%
  group_by(Lieu, Annee, Nom) %>%
  nest() %>%
  mutate(smooth_data = map(data, ~{
    # 用loess拟合,和geom_smooth默认方法一致
    fit <- loess(Sensibilite ~ as.numeric(month_day), data = .x)
    # 获取该分组下最大的日期(曲线末端)
    max_date <- max(.x$month_day)
    # 预测该日期对应的平滑值
    pred_y <- predict(fit, newdata = data.frame(month_day = as.numeric(max_date)))
    tibble(month_day = max_date, Sensibilite = pred_y)
  })) %>%
  unnest(smooth_data) %>%
  ungroup()

# 绘图
ggplot(df, aes(month_day, Sensibilite, color = Nom)) +
  geom_smooth(se = FALSE) +
  # 使用预计算的末端点添加标签
  geom_label_repel(data = smooth_endpoints,
                   aes(label = Nom),
                   max.overlaps = Inf, size = 2) +
  facet_wrap(Lieu~Annee, ncol = 2) +
  scale_y_continuous(limits = c(0,5)) +
  scale_x_date(expand = expansion(mult = 0.25), name = "Mois",
               date_breaks = "1 month", date_labels = "%b")

关键说明

  • 原代码中after_stat(ifelse(x %in% range(x), color, NA_character_))基于全局x范围判断,分面后每个子图的独立范围被忽略,导致标签位置混乱。
  • 提前按Lieu、Annee、Nom分组拟合平滑曲线,提取每个分组的末端点,确保标签精准对应每个分面内的曲线末端。
  • 如果你的geom_smooth使用了其他拟合方法(如method = "lm"),只需修改loess为对应函数即可。

内容的提问来源于stack exchange,提问作者Mmerose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 06:25:18