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

如何为facet_wrap后的ggplot2分面图匹配对应月份的二次拟合函数

问题描述

我有两个数据框:

  • 一个记录1880-2023年各年份每月的全球平均气温;
  • 另一个是tidy格式的二次模型参数,包含月份列、截距列,以及beta(一次项)、beta平方(二次项)两个模型估计列。

我需要在按月份使用facet_wrap分面的ggplot2图表中,为每个月份的气温数据叠加对应月份的二次函数曲线。目前已完成数据加载、清洗和二次模型拟合,但geom_function无法为每个分面匹配对应月份的模型参数,求解决办法。

相关代码如下:

加载数据

library(tidytuesdayR)
library(tidyverse)

tuesdata <- tidytuesdayR::tt_load(2023, week = 28)
global_temps <- tuesdata$global_temps

数据清洗

# 清洗数据
global_temps <- global_temps |> 
  janitor::clean_names()

# 重塑数据
global_temps <- global_temps |> 
    pivot_longer(
        c(jan:dec),
        names_to = "month",
        values_to = "temperature"
    ) |> 
    select(year, month, temperature) |> 
  mutate(month = str_to_title(month))

# 排序月份
month_order <- c(
  "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)

global_temps <- global_temps |> 
  mutate(month = factor(month, levels = month_order))

二次模型拟合

# 为温度增长拟合二次项模型
global_temp_quad <- global_temps |> 
  group_by(month) |> 
  nest() |> 
  mutate(model = map(data, ~ lm(temperature ~ poly(year, 2, raw = TRUE), data = .x)))

global_temp_quad <- global_temp_quad |> 
  mutate(coef = map(model, broom::tidy)) |> 
  unnest(coef) |> 
  select(term, estimate)

# 宽表转换
global_temp_quad <- global_temp_quad |> 
  pivot_wider(
    names_from = term,
    values_from = estimate
  ) |> 
  rename(
    intercept = "(Intercept)",
    year = "poly(year, 2, raw = TRUE)1",
    year_2 = "poly(year, 2, raw = TRUE)2"
  ) |> 
  ungroup()

原绘图代码(存在匹配问题)

ggplot() +
  geom_point(
    data = global_temps,
    aes(x = year, y = temperature), 
    show.legend = FALSE,
    size = 1.5,
    alpha = 1,
    shape = 1
  ) +
  theme_light() +
  labs(
    title = "Global surface temperatures (1890-2023)",
    subtitle = "Temperature is recorded as deviation from the mean temperature for 1890-1950",
    source = "Data from NASA/GISS",
    x = NULL,
    y = "Deviation from mean temperature (Celsius)"
  ) +
  facet_wrap(~ month) +
  geom_function(
    data = global_temp_quad,
    aes(group = interaction(month)),
    color = "red",
    alpha = 0.7,
    show.legend = FALSE,
    fun = function(x) {
      global_temp_quad$intercept +
      global_temp_quad$year * x +
      global_temp_quad$year_2 * x^2
    }
  ) +
  theme(
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.major.x = element_blank(),
    axis.ticks = element_blank()
  ) +
  scale_x_continuous(breaks = seq(1890, 2020, 40)) +
  scale_y_continuous(limits = c(-0.5, 1.5))
解决方法

方法1:生成预测数据并用geom_line绘制(推荐)

geom_function的fun参数无法直接关联分面的分组参数,最可靠的方式是先基于模型参数生成每个月份的预测数据,再用geom_line绘制曲线,这样能确保每个分面匹配对应月份的函数。

步骤1:生成预测数据

# 生成每个月份的预测数据(覆盖数据中的年份范围)
pred_data <- global_temp_quad |>
  rowwise() |>
  mutate(
    # 生成密集的年份序列,让曲线更平滑
    year_pred = list(seq(min(global_temps$year), max(global_temps$year), length.out = 100)),
    # 根据二次模型计算预测温度
    temp_pred = list(intercept + year * year_pred + year_2 * year_pred^2)
  ) |>
  unnest(c(year_pred, temp_pred))

步骤2:修改绘图代码

替换原代码中的geom_function部分为geom_line,加载预测数据:

ggplot() +
  geom_point(
    data = global_temps,
    aes(x = year, y = temperature), 
    show.legend = FALSE,
    size = 1.5,
    alpha = 1,
    shape = 1
  ) +
  # 添加对应月份的二次拟合曲线
  geom_line(
    data = pred_data,
    aes(x = year_pred, y = temp_pred),
    color = "red",
    alpha = 0.7
  ) +
  theme_light() +
  labs(
    title = "Global surface temperatures (1890-2023)",
    subtitle = "Temperature is recorded as deviation from the mean temperature for 1890-1950",
    source = "Data from NASA/GISS",
    x = NULL,
    y = "Deviation from mean temperature (Celsius)"
  ) +
  facet_wrap(~ month) +
  theme(
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.major.x = element_blank(),
    axis.ticks = element_blank()
  ) +
  scale_x_continuous(breaks = seq(1890, 2020, 40)) +
  scale_y_continuous(limits = c(-0.5, 1.5))

方法2:使用stat_function传递分组参数

如果坚持使用函数类图层,可以用stat_function结合args传递每个月份的模型参数,通过group关联分面:

ggplot() +
  geom_point(
    data = global_temps,
    aes(x = year, y = temperature), 
    show.legend = FALSE,
    size = 1.5,
    alpha = 1,
    shape = 1
  ) +
  theme_light() +
  labs(
    title = "Global surface temperatures (1890-2023)",
    subtitle = "Temperature is recorded as deviation from the mean temperature for 1890-1950",
    source = "Data from NASA/GISS",
    x = NULL,
    y = "Deviation from mean temperature (Celsius)"
  ) +
  facet_wrap(~ month) +
  stat_function(
    data = global_temp_quad,
    aes(group = month),
    color = "red",
    alpha = 0.7,
    # 定义带参数的二次函数
    fun = function(x, a, b, c) {
      a + b*x + c*x^2
    },
    # 传递每个月份的模型参数
    args = list(
      a = global_temp_quad$intercept,
      b = global_temp_quad$year,
      c = global_temp_quad$year_2
    )
  ) +
  theme(
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.major.x = element_blank(),
    axis.ticks = element_blank()
  ) +
  scale_x_continuous(breaks = seq(1890, 2020, 40)) +
  scale_y_continuous(limits = c(-0.5, 1.5))

内容的提问来源于stack exchange,提问作者Simen Løkken

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:02:33