为每个患者找到最匹配的时间

4

我有两组数据:

第一组数据:

 patient<-c("A","A","B","B","C","C","C","C")
 arrival<-c("11:00","11:00","13:00","13:00","14:00","14:00","14:00","14:00")
 lastRow<-c("","Yes","","Yes","","","","Yes")

 data1<-data.frame(patient,arrival,lastRow)

另一组数据:

 patient<-c("A","A","A","A","B","B","B","C","C","C")
 availableSlot<-c("11:15","11:35","11:45","11:55","12:55","13:55","14:00","14:00","14:10","17:00")

 data2<-data.frame(patient, availableSlot)

我希望为第一个数据集添加一列,以便为每个患者的最后一行显示最接近到达时间的可用时段:
结果如下:
  patient arrival lastRow availableSlot
       A   11:00        
       A   11:00     Yes     11:15
       B   13:00        
       B   13:00     Yes     12:55
       C   14:00        
       C   14:00        
       C   14:00        
       C   14:00     Yes     14:00

希望有人能告诉我如何在R中实现这个。


你可能需要先将包含小时的列转换为日期类。 - Cath
假设你的data2按照arrival排序,那么这段代码是将data1data2[!duplicated(data2$patient),]按照patient进行合并。 - Sotos
2
@Sotos 不,那只是幸运而已。OP说“最接近”,这恰好与第一个重合。 - Frank
啊...我错过了最接近的部分。 - Sotos
2个回答

8

我会使用data.table,首先将数据转换为ITime并忽略重复行:

library(data.table)
setDT(data1)[, arrival := as.ITime(as.character(arrival))]
setDT(data2)[, availableSlot := as.ITime(as.character(availableSlot))]
DT1 = unique(data1, by="patient", fromLast=TRUE)

然后,您可以进行“滚动连接”:
res = data2[DT1, on=.(patient, availableSlot = arrival), roll="nearest", 
  .(patient, availableSlot = x.availableSlot)]

#    patient availableSlot
# 1:       A      11:15:00
# 2:       B      12:55:00
# 3:       C      14:00:00

它是如何工作的

语法为x[i, on=, roll=, j]

  • on=是合并列。
  • 这是一个连接操作:对于的每一行,我们在x中查找匹配项。
  • 如果roll="nearest",则on=中的最后一列将滚动到其最近的匹配项。
  • 原始表中on=列可以使用x.*i.*前缀引用。
  • 参数j应该给出一个列的列表,此处.()list()的别名。

请访问http://r-datatable.com/Getting-started以获取有关包的介绍材料,并输入?data.table以获取相关的滚动连接文档。


我会在res处停止,但如果您真的希望它回到原始表中...

# a very nonstandard step:
data1[lastRow == "Yes", availableSlot := res$availableSlot ]

#    patient  arrival lastRow availableSlot
# 1:       A 11:00:00                  <NA>
# 2:       A 11:00:00     Yes      11:15:00
# 3:       B 13:00:00                  <NA>
# 4:       B 13:00:00     Yes      12:55:00
# 5:       C 14:00:00                  <NA>
# 6:       C 14:00:00                  <NA>
# 7:       C 14:00:00                  <NA>
# 8:       C 14:00:00     Yes      14:00:00

现在,data1 在一个新的列中有了 availableSlot,就像你执行 data1$col <- val 时一样。

3
精彩的解释 +1! - joel.wilson
1
一如既往的好解释。 - akrun
1
@Headandtoes 当然可以。如果您输入?data.table查看文档,您会发现语法是x[i, ...],在on=中连接的列可以使用x.*i.*符号从各自的表中获取。这些符号很有用,因为在滚动连接(以及其他一些特殊情况)中,x.colnamei.colname可能不同。 - Frank
2
@Headandtoes 是的,对于这个,你会想要使用 as.ITime(as.POSIXct("02Aug2016 11:15:00", format = "%d%b%Y %H:%M:%S")) 或者只是坚持使用 POSIXct 而不提取时间。有关相关文档,请参见 ?strptime。如果你不想过多地弄日期时间格式,你也可以尝试 anytime 包,它似乎能够可靠地猜出相关的 format - Frank
@Headandtoes 阅读?strptime文档或使用anytime包将其转换为POSIX日期时间,然后使用as.ITime将其转换为ITime。 - Frank
显示剩余3条评论

1
这里有一个解决方案(基于joel.wilson的答案回答我的问题),可以在基本的R中使用。
#Convert dates to POSIXct format
data1$arrival = as.POSIXct(data1$arrival, format = "%H:%M")
data2$availableSlot = as.POSIXct(data2$availableSlot, format = "%H:%M")

#Lookup times from data2$availableSlot closest to data1$arrival
data1$availableSlot = sapply(data1$arrival, function(x)
                    data2$availableSlot[which.min(abs(x - data2$availableSlot))])

#Keep just hour and minutes
data1$availableSlot = strftime(as.POSIXct(data1$availableSlot, 
                                origin = "1970-01-01"), format = "%H:%M")
data1$arrival = strftime(as.POSIXct(data1$arrival), format = "%H:%M")

#Remove times when lastrow is empty
data1$availableSlot[which(data1$lastRow != "Yes")] = ""

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接