【问题标题】:(dplyr) Sum of N values most recent to a date(dplyr) 最近的 N 个值的总和
【发布时间】:2017-10-07 18:16:09
【问题描述】:

我正在尝试创建一个函数,将最接近的 n 值与给定日期相加。因此,如果我有 5 周的数据,并且 n=2,则第 1 周的值将是第 2 周和第 3 周的总和,第 2 周的值将是第 1 周和第 3 周的总和,等等。示例:

library(dplyr)
library(data.table)

Week <- 1:5
Sales <- c(1, 3, 5, 7, 9)

frame <- data.table(Week, Sales)
frame
   Week Sales  Recent
1:    1     1    8
2:    2     3    6
3:    3     5    10
4:    4     7    14
5:    5     9    12

我想创建一个函数来为我执行此操作,并输入最近的 n(不仅仅是 2),但现在我想正确设置 2。这是我使用滞后/领先的函数:

RecentSum = function(Variable, Lags){
  Sum = 0
  for(i in 1:(Lags/2)){ #Lags/2 because I want half values before and half after
    #Check to see if you can go backwards. If not, go foward (i.e. use lead).
    if(is.na(lag(Variable, i))){
      LoopSum = lead(Variable, i)
    }
    else{
      LoopSum = lag(Variable, i)
    }
    Sum = Sum + LoopSum
  }
  for(i in 1:(Lags/2)){
    if(is.na(lead(Variable, i))){ #Check to see if you can go forward. If not, go backwards (i.e. use lag).
      LoopSum = lag(Variable, i)
    }
    else{
      LoopSum = lead(Variable, i)
    }
    Sum = Sum + LoopSum
  }
  Sum
}

当我执行RecentSum(frame$Sale,2) 时,我得到[1] 6 10 14 18 NA,这是错误的,原因有很多:

  1. 我的 if 声明仅在第一周出现,因此对于滞后始终为 NA,对于领先始终为非 NA。
  2. 我需要有一种方法来查看它是否第一次使用滞后/超前。第一个值是6 而不是8,因为第一个for 循环将它发送到lead(_,1),但随后第二个for 循环执行相同操作。我想不出如何让我的第二个 for 循环识别这一点。

是否有一个函数或库 (Zoo?) 可以使这项任务变得简单?为了练习/理解,我想让我自己的函数工作,但在这一点上,我宁愿完成它。

谢谢!

【问题讨论】:

  • 在尝试回答您的问题之前,您当前的设置有两点需要注意。首先,您正在调用一个只循环一次的 for 循环,因为 i in 1:(2/1) 与 i in 1:1 相同。所以这就解释了为什么它只在第一周做。其次,如果 lead 和 lag 是来自 dplyr 函数的函数,那么我不相信你正在正确地实现它们。

标签: r dplyr


【解决方案1】:

为了详细说明我的评论,lead 和 lag 是用于矢量化函数(例如 dplyr)的函数。这是在dplyr 内不使用函数的方法:

df <- tibble(week = Week, sales = Sales)

df %>%
  mutate(recent = case_when(is.na(lag(sales)) ~ lead(sales, n = 1) + lead(sales, n = 2),
                            is.na(lead(sales)) ~ lag(sales, n = 1) + lag(sales, n = 2),
                            TRUE ~ lag(sales) + lead(sales)))

这给了你这个:

# A tibble: 5 x 3
   week sales recent
   <int> <dbl>  <dbl>
1     1     1      8
2     2     3      6
3     3     5     10
4     4     7     14
5     5     9     12

【讨论】:

  • 感谢您的评论!快速提问 - dplyr 中的 ~ 基本上是 if 运算符?如果我真的只想要最近的两个日期,这正是我会做的事情,但实际上我有超过 24 周,我想尝试组合最多 +/- 5 周(所以 10 个总和)来测试我的回归。根据我选择的 N 嵌套一堆 if 语句是“最简单”的方法吗?
  • 是的,~ 本质上是说,如果左侧的语句为真,则将~ 的右侧分配给该行的变量recent。如果为假,则进入下一个选项。如果定义的逻辑都不为真,则将TRUE ~ 语句的右侧分配给变量
【解决方案2】:

1) 假设 k 甚至将 to 定义为索引向量,这样对于 to 的每个元素,我们将 Sales 的 k+1 个元素相加,以索引并从中减去Sales:

k <- 2  # number of elements to sum
n <- nrow(frame)
to <- pmax(k+1, pmin(1:n + k/2, n))
Sum <- function(to, Sales) sum(Sales[seq(to = to, length = k+1)])
frame %>% mutate(recent = sapply(to, Sum, Sales) - Sales)

给予:

  Week Sales recent
1    1     1      8
2    2     3      6
3    3     5     10
4    4     7     14
5    5     9     12    

请注意,通过将上面的最后一行代码替换为以下行,可以完全在基础 R 中完成解决方案:

transform(frame, recent = sapply(to, Sum, Sales) - Sales)

2) 这会在 Sales 系列之前和之后连接适当的元素,以便普通的滚动总和给出结果。

library(zoo)

ix <- c(seq(to = k+1, length = k/2), 1:n, seq(to = n-k, length = k/2))
frame %>% mutate(recent = rollsum(Sales[ix], k+1) - Sales)

请注意,如果 k=2 则将其简化为单线:

frame %>% mutate(recent = rollsum(Sales[c(3, 1:n(), n()-2)], 3) - Sales)

给予:

  Week Sales recent
1    1     1      8
2    2     3      6
3    3     5     10
4    4     7     14
5    5     9     12

更新:已修复 k > 2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-10
    • 2020-07-21
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 2018-09-23
    • 1970-01-01
    相关资源
    最近更新 更多