使用 mutate ifelse 和 rollappy 根据连续变量的变化创建条件因子

Using mutate ifelse and rollappy to create a conditional factor based on changes in continuous variable

tl;dr 我需要根据价格随时间的下降(或不下降)来确定是否进行促销。我对其他方法持开放态度。

我有一个价格数据框,其中价格随着时间的推移分为几个分组因素。我的目标是让 'EACH' 商店中的每个 'ITEM' 检查过去 7 个日期(如果存在)的 'PRICE' 的模式。如果观察值小于价格模式的 10%,则在 'Promotion' 列中填充 1,如果不是 0。

示例数据

dat <- data.frame(Date = sample(seq(as.Date('1999/01/01'), as.Date('2000/01/01'), by="day"), 10),
              Item = rep(LETTERS[1:4], times = 10),
              Store =  as.factor(sample(rep(c("NY","SYD","LON","PAR"), each = 10))),
              Price = rnorm(n = 40, mean = 2.5, sd = 1))

到目前为止,我已经使用 dplyr 的 group_split 来分解项目并将分组存储到单独的数据帧中以捕获所有条件。我认为我现在需要做的是 mutate 使用带有 rollapplyifelse 语句的新专栏。到目前为止,我已经尝试使用以下代码行...

data %>% mutate(Promotion = ifelse(rollapply(Price, 7, Mode <= Price*0.91,1,0)))

这个returns一个错误陈述...

Error: Problem with `mutate()` input `PRMT_IND2`.
x comparison (5) is possible only for atomic and list types
i Input `PRMT_IND2` is `ifelse(...)`.

我不太确定从这里到哪里去。如果您有时间,请告诉我如何将此应用到 group_split 创建的所有组,以及如何将其重新组合在一起,我将不胜感激。

note. 观察结果 (dates/rows) 在商店之间是不均匀的,有些填充时间少于 7 天。如果滚动应用没有它就无法工作,我可以删除它们。但这会丢失大量数据。

我正在为模式使用此功能...

  Mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

正如 Ronah Shak 所指出的,函数似乎不是最合适的选择。 另外请注意,使用 tabulate 会将值转换为整数,这对于您拥有的值可能会有问题。

关于错误,正如您猜对的那样,问题是您的拆分数据并不总是有 7 个日期,因此带有 width=7rollapply 函数返回了一个错误。 允许您的函数使用 Date 向量的长度或 7(如果可用)可以解决问题。 此外,您可以使用 group_by 应用函数,不需要拆分数据。

dat %>%
  group_by(Store,Item)%>%
  mutate(price_check = Price*0.91, 
         Promotion = ifelse(rollapply(Price, width = min(length(Date),7), Mode)>=price_check,1,0))

或许你可以使用滚动均值代替众数。

library(dplyr)
library(zoo)

dat %>%
   group_by(Item, Store) %>%
   mutate(Promotion = as.integer(abs((Price - 
                         rollmeanr(Price, 7, fill = NA))/Price) > 0.1))

这将为前 6 个值提供 NA,如果 Price 比前 7 天的值变化超过 10%,则为 1,否则为 0。另请注意,我们在这里采用绝对值,因此如果价格上涨 10% 或下跌,它将给出 1。