按组选择最近日期的行

20
我在R中有一个数据框,其中行代表事件,一个列是事件的日期。每个ID都有多个条目来描述它正在发生的事件。如何过滤数据框以保留每个ID最近的事件? IDs是整数,日期的格式为mm/dd/yyyy。
5个回答

32

你可以尝试

library(dplyr)
df %>% 
  group_by(ID) %>%
  slice(which.max(as.Date(date, '%m/%d/%Y')))

数据

df <- data.frame(ID= rep(1:3, each=3), date=c('02/20/1989',
'03/14/2001', '02/25/1990',  '04/20/2002', '02/04/2005', '02/01/2008',
'08/22/2011','08/20/2009', '08/25/2010' ), stringsAsFactors=FALSE)

如何考虑小时、分钟和秒数进行操作?我有一些寄存器,它们的月份、日期、年份、小时和分钟相同,但秒数不同,我无法获得最近的时间。提前致谢。 - kikusanchez
1
@kikusanchez 你可能需要使用 as.POSIXct 将其转换为 POSIXct,并按照指定的格式进行排序。 - akrun
1
需要注意的是,由于which.max返回组中遇到的第一个最大值,如果您有重复的日期,可能会出现问题。如果要保留重复的日期,请查看dplyr::slice_max(date, with_ties=TRUE) - nstjhp
@nstjhp 是的,但是使用 slice_max 还有另一个问题。假设您只有一个值,而行数为 10000,则 with_ties 将打印所有这 10000 行。 - akrun
@akrun 是的,我只是想提出这个问题,以防对任何人有所帮助。 - nstjhp

12

对于任何解决方案,你最好先更正你的日期变量,就像@akrun所示:

df$date <- as.Date(df$date, '%m/%d/%Y')

基本 R

df[
  tapply(1:nrow(df),df$ID,function(ii) ii[which.max(df$date[ii])])
,]

这使用一组行号来子集化数据。您可以通过单独运行中间行(在[]之间)来查看选择。

Data.table

类似于@rawr的:

require(data.table)
DT <- data.table(df)

unique(DT[order(date)], by="ID", fromLast=TRUE)
# or
unique(DT[order(-date)], by="ID")

1
这是一个非常有趣的基本R tapply解决方案。我不确定以前是否注意到过它。 - IRTFM

4

或者您可以对日期进行排序

df <- data.frame(ID= rep(1:3, each=3), date=c('02/20/1989',
                                              '03/14/2001', '02/25/1990',  '04/20/2002', '02/04/2005', '02/01/2008',
                                              '08/22/2011','08/20/2009', '08/25/2010' ), stringsAsFactors=FALSE)

df$date <- as.Date(df$date, '%m/%d/%Y')

## make sure to order by both `ID` and `date` as Frank mentions in comments
## since the dates may be overlapping among IDs

df <- df[with(df, order(ID, date)), ]

1) 选择最后一个

df[cumsum(table(df$ID)), ]

#   ID       date
# 2  1 2001-03-14
# 6  2 2008-02-01
# 7  3 2011-08-22

2) 或者删除重复项

df[!duplicated(df$ID, fromLast = TRUE), ]

#   ID       date
# 2  1 2001-03-14
# 6  2 2008-02-01
# 7  3 2011-08-22

这些数据由@akrun提供。

最好使用order(df $ ID,df $ date),因为“ID”可能不会将日期划分为非重叠间隔。 - Frank
@Frank,我本来以为我已经做到了,你说得好。如果不是因为这个东西还能运行,我可能就没发现它了。 - rawr

2
我有时候会因为个人缺陷而抵制使用新的程序包。"Base R"函数通常可以完成工作。但在这种情况下,我认为 dplyr 程序包的价值得到了体现,因为我在使用 ave 函数时遇到了问题,它返回了一个逻辑测试的字符值,我仍然不理解。所以我认为 dplyr 是一个真正的宝石。如果可以的话,我想强调任何赞成票都应该先给 akrun 的答案点赞。(很难相信这个问题还没有在 SO 上被问过和回答过。)
无论如何:
> df[ as.logical(
        ave(df$date, df$ID, FUN=function(d) as.Date(d , '%m/%d/%Y') == 
                                             max(as.Date(d, '%m/%d/%Y'))))
      , ]
  ID       date
2  1 03/14/2001
6  2 02/01/2008
7  3 08/22/2011

我认为这应该可以工作(失败):

> df[ ave(df$date, df$ID, FUN=function(d) as.Date(d , '%m/%d/%Y') ==max(as.Date(d, '%m/%d/%Y'))) , ]
     ID date
NA   NA <NA>
NA.1 NA <NA>
NA.2 NA <NA>
NA.3 NA <NA>
NA.4 NA <NA>
NA.5 NA <NA>
NA.6 NA <NA>
NA.7 NA <NA>
NA.8 NA <NA>

这里有另一个基于R语言的解决方案,第一次使用就能成功,没有任何意外:

> do.call( rbind, by(df, df$ID, function(d) d[ which.max(as.Date(d$date, '%m/%d/%Y')), ] ) )
  ID       date
1  1 03/14/2001
2  2 02/01/2008
3  3 08/22/2011

这是受 @rawr 的想法启发的一个例子,它从有序子集中取最后一个元素:
> do.call( rbind, by(df, df$ID, function(d) tail( d[ order(as.Date(d$date, '%m/%d/%Y')), ] ,1)) )
  ID       date
1  1 03/14/2001
2  2 02/01/2008
3  3 08/22/2011

奇怪。ave 不喜欢以正确的模式报告结果。这两个都不是逻辑的:ave(c("a","b"),1,FUN=function(x)x==x[1])ave(c(1,2),1,FUN=function(x)x==x[1]) - Frank

0

我从来没有在 R 中处理过任何数据而不使用 plyr

library(plyr)
ddply(df, .(ID), summarize, most_recent = max(as.Date(date, '%m/%d/%Y')))

   ID most_recent
1  1  2001-03-14
2  2  2008-02-01
3  3  2011-08-22

这将返回两列:“ID”和“most-recent”。我如何保留其余的列? - PM0087

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