Select row with most recent date by group

You can try

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

data

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)

For any solution, you might as well correct your date variable first, as shown by @akrun:

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

Base R

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

This uses a selection of row numbers to subset the data. You can see the selection by running the middle line (between the []s) on its own.

Data.table

Similar to @rawr's:

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

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

Or you can order the dates and

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) select the last

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

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

2) or remove the duplicates

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

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

this data brought to you by @akrun


It's probably a character flaw but I sometimes resist picking up new packages. The "base R" functions can often do the job. In this case I think the alue of the dplyr package shows through since I stumbled in creating a good solution since the ave function returned a character value for a logical test, which I still don't understand. So I think dplyr is a real gem. And if I could I'd like to insist that any upvotes be preceded by an upvote to akrun's answer. (It's hard to believe this hasn't already been asked and answered on SO.)

Anyway:

> 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

I thought this should work (fail) :

> 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>

Here's another base R solution that worked the first time with no surprises:

> 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

Here's one inspired by @rawr's notion of taking the last one from an ordered subset:

> 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