【问题标题】:Conditional sum in rolling window滚动窗口中的条件和
【发布时间】:2017-07-12 19:05:03
【问题描述】:

我对 R 很陌生,所以如果我说错了,请提前道歉:)

我有一个由 395 行和 4973 列组成的数据框,按月份排序,许多公司每月出现的次数(范围从 0 到例如 25)。从按月和年分组的每日数据中总结了发生次数。 我的数据框 df 看起来像这样(只有几个月和 3 家公司):

Date     FirmA FirmB FirmC
01-2015  20    NA    20
02-2015  21    2     1
03-2015  22    3     2
04-2015  24    7     5
05-2015  10    10    10
06-2015  9     20    2
07-2015  13    22    1
08-2015  20    19    1

我现在的任务是通过从 t-3 个月到 t-1 个月(前 3 个月)的三个月滚动窗口来总结每家公司的发生情况。但是,该总和应具有以下条件。它应该在三个月窗口内至少出现 10 次,在 t-1 月至少出现 3 次。 NA 是否在 t-3 和/或 t-2 无关紧要,只要满足这两个条件即可。

应该是这样的。

Date     FirmA FirmB FirmC
01-2015  NA    NA    NA
02-2015  20    NA    20
03-2015  41    NA    NA
04-2015  63    NA    NA
05-2015  67    12    NA
06-2015  56    20    17
07-2015  43    37    NA
08-2015  32    52    NA

我不知道如何解决这个问题,尤其是滚动窗口/总和的组合(可能有滞后)以及关于使用哪些数字和不使用哪些数字的条件。

【问题讨论】:

  • dplyr可以定义窗口函数
  • 我真的很想在dplyr 中看到一个高效的滚动窗口实现;虽然我相信它是可行的,但它的窗口并不是为滚动而设计的。

标签: r conditional-statements rolling-sum


【解决方案1】:

另一种方法,在概念上类似于 r2evans',是通过 cumsum 计算滚动总和(在将 NAs 替换为 0s 之后)并在不满足条件时插入 NAs:

ff = function(x, w = 3, ntot = 10, nlast = 3)
{
    x[is.na(x)] = 0L
    x = c(0L, x[-length(x)])

    cs = cumsum(x)
    wcs = cs - c(numeric(w), cs[1:(length(x) - w)])

    wcs[!((wcs >= ntot) & (x >= nlast))] = NA
    return(wcs)

}

sapply(df[-1], ff)  # 'df' borrowed from r2evans' answer
#     FirmA FirmB FirmC
#[1,]    NA    NA    NA
#[2,]    20    NA    20
#[3,]    41    NA    NA
#[4,]    63    NA    NA
#[5,]    67    12    NA
#[6,]    56    20    17
#[7,]    43    37    NA
#[8,]    32    52    NA

【讨论】:

  • 也感谢您的解决方案。我只是选择 r2evans 的解决方案,因为我先尝试过它并且它有效。我现在必须解决下一个障碍;)
  • @Henky 我认为这个可能要快得多。还要注意有frollapply 来到data.table,但这个使用cumsum 的可能是最快的。
【解决方案2】:

这是一个使用zoo::rollapply的方法:

df <- structure(list(Date = c("01-2015", "02-2015", "03-2015", "04-2015", 
"05-2015", "06-2015", "07-2015", "08-2015"), FirmA = c(20L, 21L, 
22L, 24L, 10L, 9L, 13L, 20L), FirmB = c(NA, 2L, 3L, 7L, 10L, 
20L, 22L, 19L), FirmC = c(20L, 1L, 2L, 5L, 10L, 2L, 1L, 1L)), .Names = c("Date", 
"FirmA", "FirmB", "FirmC"), class = "data.frame", row.names = c(NA, 
-8L))

library(zoo)

mysum <- function(x, minprev = 3) {
  l <- length(x)
  if (l==1 || (! is.na(x[l-1]) && x[l-1] >= minprev)) sum(x[-l], na.rm = TRUE) else NA
}

winsize <- 3
# conditionally-sum
df[-1] <- lapply(df[-1], function(z) rollapply(z, winsize + 1, mysum, partial = TRUE, align = "right"))
# remove those that are insufficient in total
df[-1] <- lapply(df[-1], function(z) ifelse(z <= 10, NA, z))
df
#      Date FirmA FirmB FirmC
# 1 01-2015    NA    NA    NA
# 2 02-2015    20    NA    20
# 3 03-2015    41    NA    NA
# 4 04-2015    63    NA    NA
# 5 05-2015    67    12    NA
# 6 06-2015    56    20    17
# 7 07-2015    43    37    NA
# 8 08-2015    32    52    NA

可能有一种方法不需要mysum,但有两点让它有点棘手:(1) 结果总和进入 next 字段(如果窗口更容易绕过总是长度 3),和 (2) 最后一个值的条件。尝试平滑它当然是可行的,但这已经足够了。

【讨论】:

  • 太棒了,帮助很大。太感谢了!与我的实际数据框完美结合。
猜你喜欢
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-26
  • 2010-11-12
  • 1970-01-01
相关资源
最近更新 更多