【问题标题】:Using dplyr's mutate and case_when to test multiple columns (date intervals)使用 dplyr 的 mutate 和 case_when 来测试多列(日期间隔)
【发布时间】:2020-07-02 21:30:28
【问题描述】:

我有一个数据集,其中有大约 250 列对应于日期。我使用interval() 命令创建了一个时间间隔,我想测试250 个日期列中的任何一个是否是%within% 我的间隔变量,并创建一个新列“Y”或“N”。

我尝试过的(没有奏效)是这样的:

date_cols <- names(df[,1:250]) 

df_new <- df %>% 
  mutate(test=case_when(date_cols %within% interval ~ "Y",
                        TRUE ~ "N"))

如果我这样做的话,我希望它的运作方式与此类似

df_new <- df %>%
mutate(test=case_when(date_col_1 %within% interval ~ "Y",
                     date_col_2 %within% interval ~ "Y",
                     ...
                     date_col_250 %within% interval ~ "Y",
                     TRUE ~ "N"))

显然,我不想为所有 250 列输入 case_when() 行,但我还没有找到或 tidyverse(或实际上任何)解决方案,用于尝试查找日期是否在区间内.

有什么想法吗?

【问题讨论】:

  • 马特,当我们有您的数据样本时,它真的很有帮助,如果该样本显示出一点可变性会更好。您能否包含来自dput(head(df)) 的明确数据样本?

标签: r tidyverse intervals dplyr


【解决方案1】:

我认为在 dplyr 中执行此操作可能更难,因为它实际上是基本 R 中的单线。让我们采用您的设置的玩具版本,其中包含一个间隔和一个只有两个日期列的迷你数据框:

library(lubridate)

interval <- interval(as.Date("2020-07-01"), as.Date("2020-07-04"))

df <- data.frame(id   = 1:10,
                 col1 = seq(as.Date("2020-06-30"), by = "1 day", length.out = 10), 
                 col2 = seq(as.Date("2020-06-25"), by = "1 day", length.out = 10))

df
#>    id       col1       col2
#> 1   1 2020-06-30 2020-06-25
#> 2   2 2020-07-01 2020-06-26
#> 3   3 2020-07-02 2020-06-27
#> 4   4 2020-07-03 2020-06-28
#> 5   5 2020-07-04 2020-06-29
#> 6   6 2020-07-05 2020-06-30
#> 7   7 2020-07-06 2020-07-01
#> 8   8 2020-07-07 2020-07-02
#> 9   9 2020-07-08 2020-07-03
#> 10 10 2020-07-09 2020-07-04

现在听起来好像您希望每个现有日期列都有一个逻辑列,指示其日期是否在时间间隔内。您可以像这样将这些列创建为新的数据框:

data.frame(t(apply(df[date_cols], 1, function(x) as.Date(x) %within% interval)))
#>       X1    X2
#> 1  FALSE FALSE
#> 2   TRUE FALSE
#> 3   TRUE FALSE
#> 4   TRUE FALSE
#> 5   TRUE FALSE
#> 6  FALSE FALSE
#> 7  FALSE  TRUE
#> 8  FALSE  TRUE
#> 9  FALSE  TRUE
#> 10 FALSE  TRUE

因此,如果您想将列添加到数据框中,您可以执行以下操作:

df2 <- data.frame(t(apply(df[date_cols], 1, function(x) as.Date(x) %within% interval)))
df2 <- setNames(df2, paste0(date_cols, "_in_interval"))

cbind(df, df2)
#>    id       col1       col2 col1_in_interval col2_in_interval
#> 1   1 2020-06-30 2020-06-25            FALSE            FALSE
#> 2   2 2020-07-01 2020-06-26             TRUE            FALSE
#> 3   3 2020-07-02 2020-06-27             TRUE            FALSE
#> 4   4 2020-07-03 2020-06-28             TRUE            FALSE
#> 5   5 2020-07-04 2020-06-29             TRUE            FALSE
#> 6   6 2020-07-05 2020-06-30            FALSE            FALSE
#> 7   7 2020-07-06 2020-07-01            FALSE             TRUE
#> 8   8 2020-07-07 2020-07-02            FALSE             TRUE
#> 9   9 2020-07-08 2020-07-03            FALSE             TRUE
#> 10 10 2020-07-09 2020-07-04            FALSE             TRUE

或者,使用管道,您的解决方案将如下所示:

date_cols <- names(df[1:250])

df[date_cols] %>%
  apply(1, function(x) as.Date(x) %within% interval) %>%
  t() %>%
  data.frame() %>%
  setNames(paste0(date_cols, "_within_interval")) %>%
  cbind(df, .)

reprex package (v0.3.0) 于 2020 年 7 月 2 日创建

【讨论】:

  • 非常感谢!有没有一种方法,而不是为每个日期列创建多个逻辑列来创建单个列,如果任何逻辑列为 TRUE,则为 TRUE,否则为 FALSE?
【解决方案2】:

我们可以在date_cols 上使用sapply 来获取interval 内日期的TRUE/FALSE 值。我们使用rowSums 来找出是否有任何日期位于`间隔内。

使用@Allan Cameron 的数据:

library(lubridate)
date_cols <- 2:3
df$test <- rowSums(sapply(df[date_cols], `%within%`, interval)) > 0
df

#   id       col1       col2  test
#1   1 2020-06-30 2020-06-25 FALSE
#2   2 2020-07-01 2020-06-26  TRUE
#3   3 2020-07-02 2020-06-27  TRUE
#4   4 2020-07-03 2020-06-28  TRUE
#5   5 2020-07-04 2020-06-29  TRUE
#6   6 2020-07-05 2020-06-30 FALSE
#7   7 2020-07-06 2020-07-01  TRUE
#8   8 2020-07-07 2020-07-02  TRUE
#9   9 2020-07-08 2020-07-03  TRUE
#10 10 2020-07-09 2020-07-04  TRUE

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-08
    相关资源
    最近更新 更多