【问题标题】:Subset dataset by time criteria by ID按时间条件按 ID 子集数据集
【发布时间】:2018-08-20 01:26:50
【问题描述】:

我正在处理如下所示的数据集

 Id           Time           Location
 754005       13:19:00       HK564
 754005       13:19:00       IE578
 980278       19:51:00       AK177
 980278       21:15:00       JB237

Id 列包含重复值,Time 可能包含也可能不包含重复值,Location 列包含唯一值。

我正在尝试过滤此数据集,其中 Id 值基于此逻辑是唯一的:

1) 如果该Id 的时间值相同,则保留该Id 的最后一行。换句话说,id 754005 重复了两次,时间相同但Location 不同,所以保留最后一行意味着保留这一行

754005       13:19:00       IE578

2) 如果该Id 的时间值不同,则保留该Id 的具有最新时间值的行。也就是说,id980278有两个不同的时间值19:51:0021:15:00,为这个id保留时间为21:15:00的行,因为这是最新的时间。

最终的数据集应该如下所示

 Id           Time           Location
 754005       13:19:00       IE578
 980278       21:15:00       JB237

【问题讨论】:

    标签: r subset


    【解决方案1】:

    这可以通过按Time 对数据集进行排序并选择每组中的最后一个观察值来实现。使用data.table,这变成了“单线”:

    library(data.table)
    setDT(DF)[order(Time), .SD[.N], by = Id]
    
           Id     Time Location
    1: 754005 13:19:00    IE578
    2: 980278 21:15:00    JB237
    

    或者,tail() 函数可用于选择每个组中的最后一个观察值:

    setDT(DF)[order(Time), tail(.SD, 1), by = Id]
    

    说明

    与目前发布的其他答案相比,此解决方案所需的代码行数更少,尤其是 digEmAlldata.table 代码。所以,我觉得这值得详细解释:

    • setDT(DF) 通过引用DF 强制转换为 data.table 对象,即在 as.data.table(DF) 创建副本时不进行复制。
    • order(Time) 按字典顺序订购 Time。无需将时间字符串转换为其他内容以进行排序。这里,Time 是一个因子,其级别在读取数据集时按字典顺序排列。
      根据帮助页面help("order")任何未解决的关系都将保留其原始顺序。因此,例如 Id == 754005 的行顺序不会改变。
    • .SD 是每个组的行子集。 .N 是每组中的行数。因此,.SD[.N] 选择每组的最后一行。

    数据

    library(data.table)
    DF <- fread("
     Id           Time           Location
     754005       13:19:00       HK564
     754005       13:19:00       IE578
     980278       19:51:00       AK177
     980278       21:15:00       JB237", 
    data.table = FALSE, stringsAsFactors = TRUE)
    

    DF 是一个 data.frame,其中字符列转换为假设“最坏情况”的因素。

    str(DF)
    
    'data.frame': 4 obs. of  3 variables:
     $ Id      : int  754005 754005 980278 980278
     $ Time    : Factor w/ 3 levels "13:19:00","19:51:00",..: 1 1 2 3
     $ Location: Factor w/ 4 levels "AK177","HK564",..: 2 3 1 4
    

    【讨论】:

    • 比我的方法聪明得多:)
    【解决方案2】:

    我们可以group_byIdarrangeTimedesc结束顺序,并使用slice从每个组中选择最后一行

    library(dplyr)
    library(lubridate)
    
    df %>%
      group_by(Id) %>%
      arrange(desc(hms(Time))) %>%
      slice(n())
    
    #     Id    Time   Location
    #   <int> <fct>    <fct>   
    #1 754005 13:19:00 IE578   
    #2 980278 21:15:00 JB237  
    

    【讨论】:

      【解决方案3】:

      使用基础 R 的可能方法:

      # recreate your input
      DF <- read.table(text=
      'Id           Time           Location
      754005       13:19:00       HK564
      754005       13:19:00       IE578
      980278       19:51:00       AK177
      980278       21:15:00       JB237',header=TRUE)
      
      # convert time strings to datetimes
      dates <- strptime(DF$Time,format='%H:%M:%S',tz='GMT')
      
      # get the desired rows
      DF2 <- 
      DF[unique(
         ave(seq_len(nrow(DF)),DF$Id,
             FUN=function(i)i[order(dates[i],i,decreasing=TRUE)][1]
            )
         )
      ,]
      
      # > DF2
      #       Id     Time Location
      # 2 754005 13:19:00    IE578
      # 4 980278 21:15:00    JB237
      

      或者使用data.table

      library(data.table)
      # convert data.frame to data.table
      DT <- as.data.table(DF)
      
      # create column with time strings converted to ITime objects
      DT[,TimeConverted := as.ITime(Time,format='%H:%M:%S')]
      
      # group by Id and choose the right row
      DT2 <- DT[,{.SD[order(TimeConverted,.I,decreasing=TRUE)[1]]},by=Id]
      
      # remove TimeConverted column
      DT2[,TimeConverted := NULL]
      
      # > DT2
      #        Id     Time Location
      # 1: 754005 13:19:00    IE578
      # 2: 980278 21:15:00    JB237
      

      【讨论】:

        猜你喜欢
        • 2020-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-10
        • 2021-07-22
        相关资源
        最近更新 更多