通过将值除以 data.table 中周边区域观测值的平均值来创建摘要 table

Creating summary table by dividing values by the average of the observations in the surrounding area in data.table

我的数据的最小样本是;

library(data.table)

set.seed(1)

tbl <- data.table(store = c('A','A','A','A','A','A','A','B','B','B','B','B','B','C','C','C','C'),
                  year = c(2017,2017,2017,2017,2018,2018,2018,2017,2017,2017,2017,2017,2017,2017,2017,2017,2018),
                  week = c(12,13,15,16,2,3,4,18,19,20,22,24,25,1,2,3,2),
                  insert = sample(0:1,size = 17,replace = T),
                  demand = sample(200:250,size = 17,replace=T))

我想创建一个以这种方式计算的摘要table;

要计算插入列为1的效果,我必须将插入列的需求值除以一年内最近几周的需求值的平均值,取值那个星期左右的插入列为零

比如我需要计算A店2017年第3周的数据,则必须是:

rate <- 209 / mean(c(206,245))

但如果这一年有多个插页,例如 2017 年的商店 B,它应该是费率的平均值(第 19 周和第 20 周):

rate <- mean(224 / mean(c(240,236)), 245 / mean(c(240,236)))

如果我找不到它周围的两个值,我必须用它除以我找到的唯一值来计算一个比率。例如 2017 年的商店 C:

rate <- 243 / 224

如果我在插入的行周围找不到值,我需要传递 1。

最终摘要 table 应该是这样的;

desired_tbl <- data.table(store = c('A','A','B','C','C'),
                          year = c(2017,2018,2017,2017,2018),
                          rate = c(0.927,0.97,0.941,1.08,1))

desired_tbl

  store  year  rate
  <chr> <dbl> <dbl>
1 A      2017 0.927
2 A      2018 0.97 
3 B      2017 0.941
4 C      2017 1.08 
5 C      2018 1    

我可以通过编写 for 循环和大量条件来完成所有这些操作,但我正在寻找一种矢量化方法来完成。也欢迎 dplyr 解决方案。

提前致谢。

这是一种可能的方式

  • 首先,确定 store-years,其中 insert=1 行是连续的,然后折叠它们(即取它们的平均值)。在您的示例中,只有一个这样的(2017-B,第 19/20 周)。获取这些折叠的行,并将它们绑定回 insert=0 行,重新排序为 storeyearweek
tbl[, `:=`(demand=as.double(demand), insert_id=rleid(insert))]
tbl <- rbind(
  tbl[insert==0],
  tbl[insert==1][, lapply(.SD, mean, na.rm=T), by=.(store,year,insert_id)]
)[order(store,year,week)]
  • 接下来,获取insert=1行前后的值,按行生成这些值的平均值(m),估计demand与[=19的比率=],并保留 insert=1 行。
tbl[, c("v1","v2"):=shift(demand,c(-1,1)), by=.(store, year)]
tbl[, m:=mean(c(v1,v2), na.rm=T), by=1:nrow(tbl)]
tbl[, rate:=demand/m][insert==1,.(rate=mean(fifelse(is.na(rate),1,rate))), by=.(store,year)]

输出:

    store  year      rate
   <char> <num>     <num>
1:      A  2017 0.9268293
2:      A  2018 0.9727273
3:      B  2017 0.9852941
4:      C  2017 1.0848214
5:      C  2018 1.0000000

它与你想要的输出不一样,因为我相信你计算错了 2017-B。

将具有 insert 值 1 的 demand 值替换为 NA 给出 d 然后按组向前和向后填充 d 取两个方向的平均值并分成 demand 给出 rate。然后仅保留 insert 1 行,并使用 meanstore/year 组进行聚合 rate 。如果任何 rate 不是有限使用 1.

library(dplyr)
library(zoo)

tbl %>%
  mutate(d = ifelse(insert == 1, NA, demand)) %>%
  group_by(store, year) %>%
  mutate(rate = demand / 
    rowMeans(cbind(na.locf0(d), na.locf0(d, fromLast = TRUE)), na.rm = TRUE)) %>%
  filter(insert == 1) %>%
  summarize(rate = mean(rate, na.rm = TRUE), .groups = "drop") %>%
  mutate(rate = ifelse(is.finite(rate), rate, 1))

给予:

# A tibble: 5 x 3
  store  year  rate
  <chr> <dbl> <dbl>
1 A      2017 0.927
2 A      2018 0.973
3 B      2017 0.985
4 C      2017 1.08 
5 C      2018 1