我有大量数据,其中包含许多人的病理测试数据。我提出了一个缩小的数据集来描述案例的类型。
library(plyr)
library(tidyr)
library(dplyr)
library(lubridate)
options(stringsAsFactors = FALSE)
dat <- structure(list(PersID = c("am1", "am2", "am2", "am3", "am3", "am4", "am4", "am4", "am4", "am4", "am4"), Sex = c("M", "F","F", "M", "M", "F", "F", "F", "F", "F", "F"), DateTested = c("21/10/2015", "9/07/2010", "24/09/2010", "23/10/2013", "25/10/2013", "28/04/2010", "23/06/2010", "21/07/2010", "20/10/2010", "4/03/2011", "2/12/2011"), Res = c("NR", "R", "R", "NR", "R", "R", "R", "R", "R", "R", "R"), Status = c("Yes", "No", "No", "Yes", "Yes", "No", "No", "No", "No", "No", "No"), DateOrder = c(1L, 1L, 2L, 1L, 2L, 1L, 2L, 3L, 4L, 5L, 6L)), .Names = c("PersID", "Sex", "DateTested", "Res", "Status", "DateOrder"), class = "data.frame", row.names = c(NA, -11L))
数据描述了三种类型的人(1)仅具有单个结果的人(2)具有2个结果的人,以及(3)具有许多结果的人。
我的目标是提出一个脚本,该脚本只根据一组标准包含个人行。从技术上讲,如果个体的后续结果在指定的再感染期(30天)内,则该方法仅计算个体行数。
我已将数据转换为列表并向其传递了许多函数以开始处理数据。
dat$DateTested <- dmy(dat$DateTested)
datList <- dlply(.data=dat, .variables=c('PersID'))
到目前为止,我所做的是:
选择每个人只有一个结果的所有行
fnSingleTests <- function(y){
y <- y[length(y$DateOrder)==1,]
}
singleTests <- ldply(datList, fnSingleTests, .id = NULL)
将数据框转换为列表并传递一个函数 确定(a)30天内每人是否有两行 再感染期,然后选择第一个,和(b)如果有 每人超过两行,最后一条记录和第一条记录 记录在30天内,只保留第一个。
fnMultiTests <- function(y){
y <- y[length(y$DateOrder) > 1,]
}
multiTests <- llply(datList, fnMultiTests)
fnMultiTestsSplit <- function(y){
test <- difftime(y$DateTested[length(y$DateTested)], y$DateTested[1], units='days')
if (nrow(y) <=2){
if (test < 31){
y <- y[y$DateOrder == 1, ]
y <- y[!is.na(y$PerdID), ]
} else {
y <- y[y$DateOrder %in% 1:2, ]
y <- y[!is.na(y$PersID), ]
}
} else {
if (test < 31){
y <- y[y$DateOrder == 1, ]
y <- y[!is.na(y$PersID), ]
} else {
break()
}
}
}
finalTests <- ldply(multiTests, failwith(NULL, fnMultiTestsSplit, quiet = TRUE), .id = NULL)
然后我可以将数据框与rbind结合起来:
allFinalTests <- rbind(singleTests, finalTests)
我遇到的情况是每人有两行以上的情况,并且在连续的行中,可能会出现一段时间超过30天再感染期的情况。
任何人都可以建议我如何扩展此代码,以便仅包含两个以上PersID
的情况,然后仅包含在30天再感染期之后发生后续情况的结果。
具体而言,从最早的案例开始,如果下一个案件在30天内,则排除第二个案件,或者如果第二个案件超过上一个案件后30天,则包括两个案件。对于相同的PersID
在这个例子中,我要找的最终输出是:
PersID Sex DateTested Res Status DateOrder
am1 M 21/10/2015 NR Yes 1
am2 F 9/07/2010 R No 1
am2 F 24/09/2010 R No 2
am3 M 23/10/2013 NR Yes 1
am4 F 28/04/2010 R No 1
am4 F 23/06/2010 R No 2
am4 F 20/10/2010 R No 4
am4 F 4/03/2011 R No 5
am4 F 2/12/2011 R No 6
答案 0 :(得分:3)
在基地R中,我会按如下方式接近它:
# convert the 'DateTested' column to a date-format
dat$DateTested <- as.Date(dat$DateTested, format = "%d/%m/%Y")
# calculate the difference in days with the previous observation in the group
dat$tdiff <- unlist(tapply(dat$DateTested, INDEX = dat$PersID,
FUN = function(x) c(0, `units<-`(diff(x), "days"))))
# filter the observations that have either a timedifference of zero or more
dat[(dat[,"tdiff"]==0 | dat[,"tdiff"] > 30),]
给出:
PersID Sex DateTested Res Status DateOrder tdiff
1 am1 M 2015-10-21 NR Yes 1 0
2 am2 F 2010-07-09 R No 1 0
3 am2 F 2010-09-24 R No 2 77
4 am3 M 2013-10-23 NR Yes 1 0
6 am4 F 2010-04-28 R No 1 0
7 am4 F 2010-06-23 R No 2 56
9 am4 F 2010-10-20 R No 4 91
10 am4 F 2011-03-04 R No 5 135
11 am4 F 2011-12-02 R No 6 273
使用 data.table 包:
library(data.table)
# convert the 'data.frame' to a 'data.table'
# and convert the 'DateTested' column to a date-format
setDT(dat)[, DateTested := as.Date(DateTested, format = "%d/%m/%Y")]
# calculate the difference in days with the previous observation in the group
dat[, tdiff := c(0, `units<-`(diff(DateTested), "days")), PersID]
# filter the observations that have either a timedifference of zero or more than 30 days
dat[(tdiff==0 | tdiff > 30)]
会给你相同的结果。您也可以按如下方式将其链接在一起:
setDT(dat)[, DateTested := as.Date(DateTested, format = "%d/%m/%Y")
][, tdiff := c(0, `units<-`(diff(DateTested), "days")), by = PersID
][(tdiff==0 | tdiff > 30)]
使用 dplyr :
library(dplyr)
dat %>%
mutate(DateTested = as.Date(DateTested, format = "%d/%m/%Y")) %>%
group_by(PersID) %>%
mutate(tdiff = c(0, `units<-`(diff(DateTested), "days"))) %>%
filter(tdiff == 0 | tdiff > 30)
也会给你相同的结果。
答案 1 :(得分:0)
对于版本1.9.8(在2016年11月25日的CRAN上),data.table
包已获得inrange()
函数,该函数使用非equi连接执行范围连接
使用inrange()
或%inrange%
运算符,可以使用
library(data.table) # CRAN version 1.10.4-2 used
data.table(dat)[, DateTested := as.IDate(DateTested, "%d/%m/%Y")][
, .SD[!DateTested %inrange% list(DateTested + 1L, DateTested + 30L)], by = PersID]
PersID Sex DateTested Res Status DateOrder 1: am1 M 2015-10-21 NR Yes 1 2: am2 F 2010-07-09 R No 1 3: am2 F 2010-09-24 R No 2 4: am3 M 2013-10-23 NR Yes 1 5: am4 F 2010-04-28 R No 1 6: am4 F 2010-06-23 R No 2 7: am4 F 2010-10-20 R No 4 8: am4 F 2011-03-04 R No 5 9: am4 F 2011-12-02 R No 6
对于每个PersID
,查看日期范围内的任何其他条目[第二天,30天后]。这些被排除在结果之外。
排除的行可以通过以下方式显示:
data.table(dat)[, DateTested := as.IDate(DateTested, "%d/%m/%Y")][
, .SD[DateTested %inrange% list(DateTested + 1L, DateTested + 30L)], by = PersID]
PersID Sex DateTested Res Status DateOrder 1: am3 M 2013-10-25 R Yes 2 2: am4 F 2010-07-21 R No 3