【问题标题】:Merge Records Over Time Interval合并时间间隔内的记录
【发布时间】:2011-11-18 15:58:32
【问题描述】:

首先让我说这个问题与 R(stat 编程语言)有关,但我对其他环境持开放的直截了当的建议。

目标是将数据框 (df) A 中的结果合并到 df B 中的子元素。这是一对多的关系,但是,一旦记录通过键匹配,这就是转折它们还必须在开始时间和持续时间给定的特定时间范围内匹配。

例如df A中的几条记录:

    OBS ID StartTime Duration Outcome 
    1   01 10:12:06  00:00:10 Normal
    2   02 10:12:30  00:00:30 Weird
    3   01 10:15:12  00:01:15 Normal
    4   02 10:45:00  00:00:02 Normal

来自 df B:

    OBS ID Time       
    1   01 10:12:10  
    2   01 10:12:17  
    3   02 10:12:45  
    4   01 10:13:00  

合并的期望结果是:

    OBS ID Time     Outcome  
    1   01 10:12:10 Normal 
    3   02 10:12:45 Weird 

期望的结果:数据框 B 与从 A 合并的结果。请注意,观察 2 和 4 已被删除,因为尽管它们与 A 中的记录上的 ID 匹配,但它们并未落在给定的任何时间间隔内。

问题

是否可以在 R 中执行此类操作,您将如何开始?如果没有,您能推荐一个替代工具吗?

【问题讨论】:

    标签: r time intervals


    【解决方案1】:

    设置数据

    首先设置输入数据帧。我们创建两个版本的数据帧:AB 只使用字符列作为时间,AtBt 使用 chron 包 "times" 类作为时间(比 @987654328 有优势@class 可以加减):

    LinesA <- "OBS ID StartTime Duration Outcome 
        1   01 10:12:06  00:00:10 Normal
        2   02 10:12:30  00:00:30 Weird
        3   01 10:15:12  00:01:15 Normal
        4   02 10:45:00  00:00:02 Normal"
    
    LinesB <- "OBS ID Time       
        1   01 10:12:10  
        2   01 10:12:17  
        3   02 10:12:45  
        4   01 10:13:00"
    
    A <- At <- read.table(textConnection(LinesA), header = TRUE, 
                   colClasses = c("numeric", rep("character", 4)))
    B <- Bt <- read.table(textConnection(LinesB), header = TRUE, 
                   colClasses = c("numeric", rep("character", 2)))
    
    # in At and Bt convert times columns to "times" class
    
    library(chron) 
    
    At$StartTime <- times(At$StartTime)
    At$Duration <- times(At$Duration)
    Bt$Time <- times(Bt$Time)
    

    带有时间类的sqldf

    现在我们可以使用sqldf 包执行计算。我们使用method="raw"(它不会为输出分配类),所以我们必须自己将"times" 类分配给输出"Time" 列:

    library(sqldf)
    
    out <- sqldf("select Bt.OBS, ID, Time, Outcome from At join Bt using(ID)
       where Time between StartTime and StartTime + Duration",
       method = "raw")
    
    out$Time <- times(as.numeric(out$Time))
    

    结果是:

    > out
          OBS ID     Time Outcome
    1   1 01 10:12:10  Normal
    2   3 02 10:12:45   Weird
    

    使用 sqldf 的开发版本,这可以在不使用 method="raw" 的情况下完成,并且 "Time" 列将通过 sqldf 类分配启发式自动设置为 "times" 类:

    library(sqldf)
    source("http://sqldf.googlecode.com/svn/trunk/R/sqldf.R") # grab devel ver 
    sqldf("select Bt.OBS, ID, Time, Outcome from At join Bt using(ID)
        where Time between StartTime and StartTime + Duration")
    

    带有字符类的sqldf

    实际上可以使用"times" 类,方法是使用sqlite 的strftime 函数在sqlite 中使用字符串执行所有时间计算。不幸的是,SQL 语句涉及更多:

    sqldf("select B.OBS, ID, Time, Outcome from A join B using(ID)
        where strftime('%s', Time) - strftime('%s', StartTime)
           between 0 and strftime('%s', Duration) - strftime('%s', '00:00:00')")
    

    编辑:

    一系列修正语法、添加额外方法和修正/改进read.table 语句的编辑。

    编辑:

    简化/改进的最终 sqldf 语句。

    【讨论】:

      【解决方案2】:

      这是一个例子:

      # first, merge by ID
      z <- merge(A[, -1], B, by = "ID")
      
      # convert string to POSIX time
      z <- transform(z,
        s_t = as.numeric(strptime(as.character(z$StartTime), "%H:%M:%S")),
        dur = as.numeric(strptime(as.character(z$Duration), "%H:%M:%S")) - 
          as.numeric(strptime("00:00:00", "%H:%M:%S")),
        tim = as.numeric(strptime(as.character(z$Time), "%H:%M:%S")))
      
      # subset by time range
      subset(z, s_t < tim & tim < s_t + dur)
      

      输出:

        ID StartTime Duration Outcome OBS     Time        s_t dur        tim
      1  1  10:12:06 00:00:10  Normal   1 10:12:10 1321665126  10 1321665130
      2  1  10:12:06 00:00:10  Normal   2 10:12:15 1321665126  10 1321665135
      7  2  10:12:30 00:00:30   Weird   3 10:12:45 1321665150  30 1321665165
      

      OBS #2 看起来在范围内。有意义吗?

      【讨论】:

        【解决方案3】:

        将两个data.frames与merge()合并在一起。然后subset() 生成的data.frame 条件为time &gt;= startTime &amp; time &lt;= startTime + Duration 或任何对您有意义的规则。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多