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

如何用dplyr/tidyverse高效实现分组时间序列递归指数平滑?

用dplyr/tidyverse实现分组时间序列的递推指数平滑

我来帮你搞定这个分组时间序列的指数平滑问题,既要满足递推依赖(下一年用上一年的结果),又要兼顾大数据量的运行速度。

首先,咱们得明确核心需求:每个组有固定初始值,第一年的平滑值基于初始值计算,之后每一年都用上一年的平滑结果+当前年份的观测值来计算,而且数据量大,必须用高效的实现方式。

步骤1:准备数据与定义平滑函数

先假设你的数据结构包含分组ID、年份、观测值、每个组的初始值(比如group_id、year、value、initial_value)。先定义指数平滑的单步计算函数——这里以简单指数平滑为例,你可以根据自己的需求修改这个函数:

library(tidyverse)

# 定义单步指数平滑函数:输入上一期平滑值、当前观测值、平滑系数α
exp_smooth_step <- function(prev_smooth, current_value, alpha) {
  alpha * current_value + (1 - alpha) * prev_smooth
}

步骤2:分组递推计算

关键是用purrr::accumulate()来实现递推——这个函数是底层优化过的,比手动写for循环快得多,非常适合大数据量场景。结合dplyr::group_by()就能实现分组处理:

# 示例数据(你可以替换成自己的真实数据)
set.seed(123)
df <- tibble(
  group_id = rep(1:3, each = 5),
  year = rep(2018:2022, 3),
  value = rnorm(15, 100, 5),
  initial_value = rep(c(98, 102, 99), each = 5)
)

# 设置平滑系数α
alpha <- 0.3

# 核心计算逻辑
df_smoothed <- df %>%
  # 第一步:务必按组和年份排序,递推必须保证时间顺序正确
  arrange(group_id, year) %>%
  # 按分组处理
  group_by(group_id) %>%
  mutate(
    # 用accumulate实现递推:
    # .x是当前组的观测值向量,.f是单步平滑函数,.init是组的初始值
    smoothed = accumulate(
      .x = value,
      .f = exp_smooth_step,
      alpha = alpha,
      .init = first(initial_value)  # 取每个组的第一个初始值(因为每个组的初始值是重复的)
    ) %>% tail(-1)  # 去掉初始值对应的S0,只保留逐年的平滑结果S1~Sn
  ) %>%
  ungroup()

关键细节解释

  • 排序的重要性:必须先按group_id和year排序,否则递推的时间顺序会混乱,结果完全错误。
  • accumulate的作用:它会从初始值开始,依次把上一步的输出作为下一步的输入,完美实现“下一年依赖上一年结果”的需求,而且底层是C实现的,速度远快于手动循环。
  • 自定义函数扩展:如果你的指数平滑是更复杂的类型(比如Holt-Winters带趋势/季节项),只需要修改exp_smooth_step函数,让它能接收并返回多状态值(比如用列表存储水平、趋势、季节项),accumulate同样能处理这种情况。

验证结果

你可以查看第一个组的计算结果,比如初始值98,第一年观测值97.46,计算出的第一年平滑值是0.3*97.46 + 0.7*98 = 97.84,和df_smoothed里的结果完全一致,后续年份也都是基于前一年的平滑值递推的,符合你的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:06:18