合并记录随时间间隔

时间:2011-11-18 15:58:32

标签: r time intervals

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

目标是将数据框架(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中执行此类操作,您将如何开始?如果没有,你能建议一个替代工具吗?

3 个答案:

答案 0 :(得分:4)

设置数据

首先设置输入数据帧。我们创建了两个版本的数据框:AB只使用时间字符列,AtBt使用chron包"times"类时间(优于"character"类,可以添加和减去它们):

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 with times class

现在我们可以使用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,包含字符类

通过使用sqlite的strftime函数在字符串中执行sqlite中的所有时间计算,实际上可以使用"times"类。不幸的是,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语句。

答案 1 :(得分: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看起来在范围内。它有意义吗?

答案 2 :(得分:1)

将两个data.frames与merge()合并。然后subset()生成的带有条件time >= startTime & time <= startTime + Duration的data.frame或任何规则对您有意义。