按时间间隔合并记录

问题描述:

让我先说这个问题与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和你将如何开始?如果没有,你可以建议一个替代工具吗?

设置数据

首先设置输入数据帧。我们创建一个数据帧的两个版本:AB只使用字符列的时间和AtBt使用克隆氏病包"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与次类

现在我们可以使用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"列会自动设置为"times" class by sqldf class assignment heuristic:

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的所有时间计算出使用的SQLite的strftime函数的字符串使用"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声明。

+0

哇。谢谢你这样彻底的回答。 – bnjmn

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

这里是一个例子:

# 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看起来是在范围内。是否有意义?

+0

oops。我的意思是obs 2超出范围。你是对的。 – bnjmn