【问题标题】:Using lubridate to create factors based on date使用 lubridate 根据日期创建因子
【发布时间】:2020-05-25 13:17:58
【问题描述】:

我试图找到合适的答案,但所有案例都比我所拥有的简单得多。 我需要根据我拥有的数据框中的日期信息创建一个 4 级(nov、end_feb、end_apr、其他)因子,然后将其添加为列。此外,我需要代码快速运行,因为我拥有的真实 df 超过 80 万行

这是我目前所拥有的 lubridate%within%。它确实有效,但由于效率低下而非常缓慢,因为我不得不求助于使用sapply(df, sub_period_gen(date)) 创建一个新列。 理想情况下,我需要一种方法来确保解决方案是矢量化的,因为我有一些其他因子生成器可以在同一数据帧上工作并且速度也很慢

sub_period_gen <- function(x){
  i_1 <- ymd("2019-11-01")%--% ymd("2019-11-30")
  i_2 <- ymd("2020-02-24")%--% ymd("2020-02-29")
  i_3 <- ymd("2020-04-24")%--% ymd("2020-04-30")
  if (x %within% i_1){
    return("nov")  # return case one
  } else if (x %within% i_2){
    return("end_feb")  # return case two
  } else if (x %within% i_3){
    return("end_apr")  # return case three
  } else{
    return("other")  # return case four
  }
}

提前致谢!

编辑:我对解决方案进行了一些优化,但看起来仍然不是最理想的 而且很难修改。另外,我将区间移到了全局环境中

sub_period_gen <- function(x){
  return(ifelse(x %within% i_1,"nov",ifelse(x %within% i_2,"end_feb",ifelse(x %within% i_3,"end_apr","other"))))
  }

我的问题与this one 不同,因为我的约会确实没有规律,而休息时间是针对特定分析的。

编辑 2: 示例输入:

library(lubridate)
toy <- tibble(date = ymd("2019-11-12","2020-03-11","2020-01-31","2019-12-19","2019-12-04","2020-01-21","2020-01-31","2020-02-16",
              "2020-02-28","2020-03-20","2020-02-08","2020-03-23","2020-01-22","2020-02-18","2020-03-19","2019-11-22",
              "2020-01-14","2020-03-04","2019-12-02","2019-11-03","2020-02-27","2020-02-13","2019-11-17","2020-03-17",
              "2020-04-14","2019-12-19","2019-11-05","2020-01-11","2020-04-25","2019-11-24"))

想要的输出:

>  date         sub_period
>   <date>     <chr>     
> 1 2019-11-12 nov       
> 2 2020-03-11 other
> 3 2020-01-31 other   
> 4 2019-12-19 other   
> 5 2019-12-04 other   
> 6 2020-01-21 other   
> 7 2020-02-29 end_feb   
> 8 2020-02-16 other   
> 9 2020-04-28 end_apr 

【问题讨论】:

  • 嗨@zeebrah,你能提供一个小玩具数据集来测试你的功能吗?这将使您更容易理解您想要实现的目标
  • 这能回答你的问题吗? Using R cut function on dates
  • @milanmft 添加了输入和输出示例。谢谢!
  • @machine,有点回答,但部分是,他们发布在那里的代码工作起来非常麻烦,而且仍然很慢

标签: r datetime dplyr lubridate


【解决方案1】:

这是case_when 来自dplyr 的一种方法:

library(dplyr)
library(lubridate)
toy %>%
  mutate(sub_period = 
         case_when(date >= ymd("2019-11-01") & date < ymd("2019-11-30") ~ "nov",
                   date >= ymd("2020-02-24") & date < ymd("2020-02-29") ~ "end_feb",
                   date >= ymd("2020-04-24") & date < ymd("2020-04-30") ~ "end_apr",
                   TRUE ~ "other"))
# A tibble: 30 x 2
   date       sub_period
   <date>     <chr>     
 1 2019-11-12 nov       
 2 2020-03-11 other     
 3 2020-01-31 other     
 4 2019-12-19 other     
 5 2019-12-04 other     
 6 2020-01-21 other     
 7 2020-01-31 other     
 8 2020-02-16 other     
 9 2020-02-28 end_feb   
10 2020-03-20 other     
# … with 20 more rows

如果您需要更快的速度,您可以使用data.tableIDate 类进行非等连接。首先,您需要设置一个单独的表来加入:

library(data.table)
setDT(toy)
toy[,date:=as.IDate(date)]

date.table <- data.table(Start = c(as.IDate("2019-11-01"),as.IDate("2020-02-24"),as.IDate("2020-04-24")),
                         End = c(as.IDate("2019-11-30"),as.IDate("2020-02-29"),as.IDate("2020-04-30")),
                         sub_period = c("nov","end_feb","end_apr"))

date.table
        Start        End sub_period
1: 2019-11-01 2019-11-30        nov
2: 2020-02-24 2020-02-29    end_feb
3: 2020-04-24 2020-04-30    end_apr

然后执行join:

date.table[toy, on = .(Start<=date, End>date)][is.na(sub_period),sub_period := "other"][]
         Start        End sub_period
 1: 2019-11-12 2019-11-12        nov
 2: 2020-03-11 2020-03-11      other
 3: 2020-01-31 2020-01-31      other
 4: 2019-12-19 2019-12-19      other
 5: 2019-12-04 2019-12-04      other
 6: 2020-01-21 2020-01-21      other
 7: 2020-01-31 2020-01-31      other
 8: 2020-02-16 2020-02-16      other
 9: 2020-02-28 2020-02-28    end_feb
10: 2020-03-20 2020-03-20      other
...

【讨论】:

  • 你的基本方法效果很好,而且比我修改后的解决方案要快一些,我还没有尝试 IDate 类,但会记住它,以防我需要更快的速度。谢谢!
【解决方案2】:

在基础 R 中,您可以像这样使用嵌套的 ifelse 函数:

sub_period_gen <- function(x){
ifelse(x >= ymd("2019-11-01") & x <= ymd("2019-11-30"), "nov",
ifelse(x >= ymd("2020-02-24") & x <= ymd("2020-02-29"), "end_feb",
ifelse(x >= ymd("2020-04-24") & x <= ymd("2020-04-30"), "end_apr",
"other")))
}

要获得所需的输出,您可以像这样cbind.data.frame(toy,sub_period= sub_period_gen(toy$date)) 绑定输入和输出。

【讨论】:

  • 看起来很整洁。与我在 edit 1 中修改后的解决方案非常相似,只是定义了间隔。您认为将间隔移动到全局环境并将它们添加为函数参数会影响性能吗?问原因,我希望生成器在未来的间隔边界方面更加灵活,手动调整是不可取的
  • @zeebrah 抱歉没有看到编辑。无论如何,我不认为这会改变性能。目前有几种不同的方法可以解决相同的任务,因此您可以test 哪种方式在速度方面最好。
猜你喜欢
  • 1970-01-01
  • 2016-06-05
  • 2015-12-19
  • 2018-02-03
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-23
相关资源
最近更新 更多