【问题标题】:How do I pick only one row of data per ID based on the minimum date difference in R?如何根据 R 中的最小日期差异为每个 ID 仅选择一行数据?
【发布时间】:2020-06-27 12:01:46
【问题描述】:

我有一个带有一些 ID 的数据框和每个 ID 的可变行数,如下所示:

ID    Date_start    Date
1     2016-11-02    2020-2-22
1     2016-11-02    2015-1-18
2     2019-12-22    2017-3-2
2     2019-12-22    2019-2-9
2     2019-12-22    2017-12-1

而且,对于每个 ID,我只想保留一行,即日期最接近 Date_start 的那一行。 然后我想将 abs(Date 和 Date_start) 之间的日期差小于 100 天的所有行设置为 NA。

有简单的方法吗?

在此先感谢

【问题讨论】:

  • 把它分成几块。您尝试过什么来计算日期差异?

标签: r formatting selection


【解决方案1】:

使用dplyr 的一种方法是group_by ID 并获得与Date 差异最小的行。

library(dplyr)

df %>%
  mutate_at(-1, lubridate::ymd) %>%
  group_by(ID) %>%
  slice(which.min(abs(Date_start - Date)))

#    ID Date_start Date      
#  <int> <date>     <date>    
#1     1 2016-11-02 2015-01-18
#2     2 2019-12-22 2019-02-09

如果您想将日期设置为NA,您可以这样做。

df %>%
  mutate_at(-1, lubridate::ymd) %>%
  group_by(ID) %>%
  mutate(diff = as.numeric(abs(Date_start - Date))) %>%
  slice(which.min(abs(Date_start - Date))) %>%
  mutate(diff = replace(diff, diff < 100, NA))

数据

df <- structure(list(ID = c(1L, 1L, 2L, 2L, 2L), Date_start = structure(c(1L, 
1L, 2L, 2L, 2L), .Label = c("2016-11-02", "2019-12-22"), class = "factor"), 
Date = structure(c(5L, 1L, 3L, 4L, 2L), .Label = c("2015-1-18", 
"2017-12-1", "2017-3-2", "2019-2-9", "2020-2-22"), class = "factor")),
class = "data.frame", row.names = c(NA, -5L))

【讨论】:

    【解决方案2】:

    使用dplyr,我们可以得到以下信息。我添加了一个新 ID 来展示如何创建所有值 NA。使用 Ronak 的 replace 而不是 ifelse 更好的想法,我们可以保留类。

    library(dplyr)
    
    df %>%
      mutate(Date_diff = abs(difftime(Date, Date_start))) %>%
      group_by(ID) %>%
      filter(Date_diff == min(Date_diff)) %>%
      mutate_all(~replace(., Date_diff < 100, NA))
    #> # A tibble: 3 x 4
    #> # Groups:   ID [3]
    #>      ID Date_start Date       Date_diff
    #>   <dbl> <date>     <date>     <drtn>   
    #> 1     1 2016-11-02 2015-01-18 654 days 
    #> 2     2 2019-12-22 2019-02-09 316 days 
    #> 3     3 NA         NA          NA days
    

    数据

    df <- structure(list(ID = c(1, 1, 2, 2, 2, 3), Date_start = structure(c(17107, 
    17107, 18252, 18252, 18252, 18250), class = "Date"), Date = structure(c(18314, 
    16453, 17227, 17936, 17501, 18202), class = "Date")), class = c("spec_tbl_df", 
    "tbl_df", "tbl", "data.frame"), row.names = c(NA, -6L))
    

    【讨论】:

      【解决方案3】:

      基础 R 解决方案:

      # Convert factors to dates: 
      
      cleaned_df <- within(df, {
                          Date_start <- as.Date(sapply(Date_start, as.character), "%Y-%m-%d")
                          Date <- as.Date(sapply(Date, as.character), "%Y-%m-%d")
                          }
                        )
      # Aggregate to find the min Date per id: 
      
      data.frame(do.call("rbind", lapply(split(cleaned_df, cleaned_df$ID), 
             function(x){
               data.frame(ID = unique(x$ID), Date = x$Date[which.min(x$Date_start - x$Date)])
              }
             )
            ),
       row.names = NULL
      )
      

      Tidyverse 解决方案:

      library(tidyverse)
      df %>% 
        mutate_if(str_detect(tolower(names(.)), "date"), funs(as.Date(., "%Y-%m-%d"))) %>%
        group_by(ID) %>% 
        summarise(Date = Date[which.min(Date - Date_start)]) %>% 
        ungroup()
      

      数据感谢@Ronak Shah:

      df <-
        structure(
          list(
            ID = c(1L, 1L, 2L, 2L, 2L),
            Date_start = structure(
              c(1L,
                1L, 2L, 2L, 2L),
              .Label = c("2016-11-02", "2019-12-22"),
              class = "factor"
            ),
            Date = structure(
              c(5L, 1L, 3L, 4L, 2L),
              .Label = c("2015-1-18",
                         "2017-12-1", "2017-3-2", "2019-2-9", "2020-2-22"),
              class = "factor"
            )
          ),
          class = "data.frame",
          row.names = c(NA,-5L)
        )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-20
        • 1970-01-01
        • 1970-01-01
        • 2019-12-07
        • 1970-01-01
        • 2020-02-10
        • 1970-01-01
        • 2012-04-11
        相关资源
        最近更新 更多