【问题标题】:R filter a large dataframe conditional on dates, using filter and pipesR使用过滤器和管道过滤以日期为条件的大型数据框
【发布时间】:2020-10-23 08:49:15
【问题描述】:

我有一个大数据框,我想选择满足日期列条件的行。数据框与此类似:

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) 晚两个月的行。在这个例子中,我应该选择这五个日期(第 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)

【问题讨论】:

    标签: r dataframe if-statement filter pipes-filters


    【解决方案1】:

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

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

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

    编辑
    问题更新后,我从 base-R 添加了 months 函数。也可以使用来自lubridatemonth 函数。

    【讨论】:

    • 没有解决问题。问题是处理 curdate 在 11 月或 12 月的情况。看我的例子。
    • @BertrandG 检查您的测试数据 - 似乎只有第 1、12 和 19 行应该匹配。这是我的代码生成的输出。
    • 我的问题没有很好地表述。我刚刚编辑了它。我想选择 expdate 哪个月份是 curdate 之后的两个月。您的代码确实选择了三行 1、12 和 19。除了第 1 行、第 12 行和第 19 行之外,我还想选择第 7 行和第 13 行。
    【解决方案2】:

    如果您要导入 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() 可以根据您的需要提供解决方案。

    澄清问题后的编辑。如果您正在寻找 expdate 与 curdate 相比“晚”两个月的日期,具体而言,无论年份如何,您都只比较它们的月份,那么一点模运算可能会有所帮助:

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

    【讨论】:

    • 没有解决问题。问题是处理 curdate 在 11 月或 12 月的情况。看我的例子。
    • 你能具体说明为什么这不起作用吗?请注意,我的代码与您的不同! expdate == curdate + months(2) 实际上确实比较了两个日期。 ymd("2019-11-01")+months(2) 等于 "2020-01-01" 还是我没有遇到问题?相比之下,使用month(ymd("2019-11-01")) + 2 显然会导致整数13,这不是你想要的。
    • 我的问题没有很好地表述。我刚刚编辑了它。我想选择 expdate 哪个月份是 curdate 之后的两个月。您的代码选择了 1、12 和 19 三行,但除了 1、12 和 19 之外,我还想选择第 7 和 13 行。
    • 你是对的,我没有得到正确的问题。如果需要,您可以查看我的编辑。一点模运算应该可以解决问题,对吧?
    猜你喜欢
    • 2023-03-15
    • 1970-01-01
    • 2021-09-18
    • 2014-02-28
    • 2021-11-23
    • 2014-12-21
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    相关资源
    最近更新 更多