我有以下数据框DF描述在某些日期参与项目的人员:
ID ProjectName StartDate
1 Health 3/1/06 18:20
2 Education 2/1/07 15:30
1 Education 5/3/09 9:00
3 Wellness 4/1/10 12:00
2 Health 6/1/11 14:20
目标是找到与每个ID相对应的第一个项目。例如,预期输出如下:
ID ProjectName StartDate
1 Health 3/1/06 18:20
2 Education 2/1/07 15:30
3 Wellness 4/1/10 12:00
到目前为止,我已完成以下操作以获取每个ID的第一个StartDate:
sub <- ddply(DF, .(ID), summarise, st = min(as.POSIXct(StartDate)));
在此之后,我需要将sub中的每一行与原始DF匹配,并提取与该ID和StartDate对应的项目。这可以在sub中的每一行的循环中完成。但是,我的数据集非常大,我想知道是否有一种有效的方法来进行匹配并从DF中提取这个子集。
答案 0 :(得分:7)
这是一个data.table
解决方案,应该非常有效。
DF <- data.frame(ID=c(1,2,1,3,2,1), ProjectName=c('Health', 'Education', 'Education', 'Wellness', 'Health', 'Health'),
StartDate=c('3/1/06 18:20', '2/1/07 15:30', '5/3/09 9:00', '4/1/10 12:00', '6/1/11 14:20', '1/1/06 11:10'))
请注意,我已经修改了您的数据,最后添加了另一个元素,因此不再对日期进行排序。因此输出不同。
d <- as.data.table(DF)
# Order by StartDate and take the first ID.
# Assumes that your dates are month/day/year.
d[order(as.POSIXct(StartDate, format="%m/%d/%y %H:%M"))][,.SD[1,],by=ID]
## ID ProjectName StartDate
## 1: 1 Health 1/1/06 11:10
## 2: 2 Education 2/1/07 15:30
## 3: 3 Wellness 4/1/10 12:00
如果您的日期已经排序(如您的示例所示),则足以满足:
d[,.SD[1,],by=ID]
答案 1 :(得分:7)
使用match
这非常简单,因为match
会返回:
第一个参数的第一个匹配位置的向量 它的第二个
所以你需要做的就是按日期排序,然后使用unique
获取每个ID的一个实例,match
找到第一个位置。感谢@MatthewLunberg提供可重现的数据示例:
DF <- DF[ order(as.POSIXct(DF$StartDate, format="%m/%d/%y %H:%M")) , ]
DF[ match( unique( DF$ID ) , DF$ID ) , ]
# ID ProjectName StartDate
#6 1 Health 1/1/06 11:10
#2 2 Education 2/1/07 15:30
#4 3 Wellness 4/1/10 12:00
一个优点是它在使用之前保留了原始数据帧的rownumbers。我不知道这对你有用。
答案 2 :(得分:2)
这是基础R解决方案
dat <- data.frame(
ID=c(1,2,1,3,2),
PRJ=c("H","E","E", "W", "H"),
START=strptime(
c(
"3/1/06 18:20", "2/1/07 15:30", "5/3/09 9:00",
"4/1/10 12:00","6/1/11 14:20"),
"%d/%m/%y %H:%M")
)
min_date <- function(x) {x[which.min(x$START), ]}
s <- split(dat, dat$ID) # split
a <- lapply(s, FUN=min_date) # apply
do.call("rbind", a) # combine
导致
ID PRJ START
1 1 H 2006-01-03 18:20:00
2 2 E 2007-01-02 15:30:00
3 3 W 2010-01-04 12:00:00
然而,来自@ SimonO101的订单匹配解决方案比这快得多。
只是为了它的乐趣,这是使用sqldf
的另一种解决方案:
sqldf("select * from dat group by ID having START=min(START)")
答案 3 :(得分:1)
为了完善它,这是一个基于plyr
包的解决方案。我添加了一个额外的列,以便textConnection
更容易读取数据。
dfProjects = as.data.frame(read.table(textConnection("ID ProjectName Date Time
1 Health 3/1/06 18:20
2 Education 2/1/07 15:30
1 Education 5/3/09 9:00
3 Wellness 4/1/10 12:00
2 Health 6/1/11 14:20"), header = TRUE))
ddply(within(dfProjects, dfProjects[order(
as.POSIXct(paste(Date, Time), format = "%m/%d/%y %H:%M")), ]),
.(ID), function(dataFrame) dataFrame[1, ])