R使用过滤器和管道过滤以日期为条件的大型数据框

R filter a large dataframe conditional on dates, using filter and pipes

我有一个大数据框,我想要 select 满足日期列条件的行。数据框类似于:

library(tidyverse)
library(lubridate)

curdate <- seq(as.Date("2000/1/1"), by = "month", length.out = 24)
expdate <- rep(seq(as.Date("2000/3/1"), by = "quarter", length.out = 12),2)
afactor <- rep(c("C","P"),12)
anumber <- runif(24)
df<-data.frame(curdate, expdate, afactor, anumber)
df$expdate[12]<-as.Date("2001-02-01")

我想获取到期日期 (expdate) 的月份比当前日期的月份 (curdate) 晚两个月的行。在这个例子中,我应该 select 这五个日期(第 1、7、12、13 和 19 行):

curdate     expdate     afactor     anumber
2000-01-01  2000-03-01     C        0.6832251
2000-07-01  2001-09-01     C        0.2671076
2001-01-01  2000-03-01     C        0.2097065
2001-07-01  2001-09-01     C        0.9258450
2000-12-01  2001-02-01     P        0.4903951

首先,我为此使用了以下行:

df_select1 <- df %>% group_by(curdate, afactor) %>% 
  filter(month(expdate) == month(curdate)+2)

但它漏掉了月份是 11 月或 12 月的情况。例如这里,当 curdate 为 2000-12-01 时,它会忽略这种情况。所以我想添加一个条件,来处理这些情况。我写道:

 df_select2 <- df %>% group_by(curdate, afactor) %>% 
  if_else(month(curdate)<11,
    filter(month(expdate) == month(curdate)+2),
    filter(month(expdate) == month(curdate)-10))

但我收到以下错误:condition 必须是逻辑向量,而不是 grouped_df/tbl_df/tbl/data.frame 对象

我找到了以下解决方案,但当然还有更短的方法:

df_select1 <- df %>% group_by(curdate, afactor) %>% 
  filter(month(curdate)<11) %>%
  filter(month(expdate) == month(curdate)+2)

df_select2 <- df %>% group_by(curdate, afactor) %>% 
  filter(month(curdate)>10) %>%
  filter(month(expdate) == month(curdate)-10)

df_select <- full_join(df_select1, df_select2)

您可以使用 lubridate 中的 %m+% 运算符向 curdate 添加 2 个月:

df %>% 
  filter(months(expdate) == months(curdate %m+% months(2)))

这将考虑日历月的天数变化。

编辑
问题更新后,我从 base-R 添加了 months 函数。也可以使用 lubridate 中的 month 函数。

如果您要导入 lubridate,您可能还应该使用它的函数来计算月份。这些显然有点棘手,因为它们的长度不相等,例如为什么基本函数 difftime 不提供每月单位。

如果没有 if_else 函数,这将是您问题的解决方案:

df_select1 <- df %>% group_by(curdate, afactor) %>% 
  filter(expdate == curdate + months(2))

顺便说一下,只要您的数据始终是相应月份的第一天,您就不会 运行 遇到问题。不过,您必须决定在以下情况下应该发生什么:

ymd("2019-08-31")+months(1)
ymd("2019-01-29")+months(1)

由于显而易见的原因,这导致了 NA。如果发生这种情况,lubridate::add_with_rollback() 可以根据您的需要提供解决方案。

澄清问题后的编辑。如果您正在寻找那些与 curdate 相比,expdate 是“晚”两个月的日期,在特定意义上,您只比较它们的月份而不考虑年份,一个小的模运算可能会有所帮助:

df %>% 
  filter(lubridate::month(expdate) == (lubridate::month(curdate)+2) %% 12)