R子集唯一观察保持最后一个条目

时间:2014-02-11 14:10:59

标签: r

我有一个看起来像这样的数据框(有更多的观察结果)

df <- structure(list(session_user_id = c("1803f6c3625c397afb4619804861f75268dfc567", 
"1924cb2ebdf29f052187b9a2d21673e4d314199b", "1924cb2ebdf29f052187b9a2d21673e4d314199b", 
"1924cb2ebdf29f052187b9a2d21673e4d314199b", "1924cb2ebdf29f052187b9a2d21673e4d314199b", 
"198b83b365fef0ed637576fe1bde786fc09817b2", "19fd8069c094fb0697508cc9646513596bea30c4", 
"19fd8069c094fb0697508cc9646513596bea30c4", "19fd8069c094fb0697508cc9646513596bea30c4", 
"19fd8069c094fb0697508cc9646513596bea30c4", "1a3d33c9cbb2aa41515e6ef76f123b2ea8ee2f13", 
"1b64c142b1540c43e3f813ccec09cb2dd7907c14", "1b7346d13f714c97725ba2e1c21b600535164291"
), raw_score = c(1, 1, 1, 1, 1, 0.2, NA, 1, 1, 1, 1, 0.2, 1), 
    submission_time = c(1389707078L, 1389694184L, 1389694188L, 
    1389694189L, 1389694194L, 1390115495L, 1389696939L, 1389696971L, 
    1389741306L, 1389985033L, 1389983862L, 1389854836L, 1389692240L
    )), .Names = c("session_user_id", "raw_score", "submission_time"
), row.names = 28:40, class = "data.frame")

我想创建一个新的数据框,每个&#34; session_ user_id&#34;保持最新的&#34; submission_time。&#34;

我想到的唯一想法是创建一个唯一用户列表。编写一个循环来查找每个用户的submission_time的最大值,然后编写一个循环,在该用户和时间之前得到原始分数。

有人能告诉我在R中做到这一点的更好方法吗?

谢谢!

6 个答案:

答案 0 :(得分:8)

您可以先按data.frame订购submission_time,然后删除所有重复的session_user_id条目:

## order by submission_time
df <- df[order(df$submission_time, decreasing=TRUE),]

## remove duplicated user_id
df <- df[!duplicated(df$session_user_id),]

#                            session_user_id raw_score submission_time
#33 198b83b365fef0ed637576fe1bde786fc09817b2       0.2      1390115495
#37 19fd8069c094fb0697508cc9646513596bea30c4       1.0      1389985033
#38 1a3d33c9cbb2aa41515e6ef76f123b2ea8ee2f13       1.0      1389983862
#39 1b64c142b1540c43e3f813ccec09cb2dd7907c14       0.2      1389854836
#28 1803f6c3625c397afb4619804861f75268dfc567       1.0      1389707078
#32 1924cb2ebdf29f052187b9a2d21673e4d314199b       1.0      1389694194
#40 1b7346d13f714c97725ba2e1c21b600535164291       1.0      1389692240

答案 1 :(得分:5)

使用dplyr表达这一点很简单:首先按会话ID分组,然后过滤,选择每个组中最长时间的行:

library(dplyr)
df %.%
  group_by(session_user_id) %.%
  filter(submission_time == max(submission_time))

或者,如果您不想保留所有最长时间(如果重复),您可以执行以下操作:

library(dplyr)
df %.%
  group_by(session_user_id) %.%
  filter(row_number(desc(submission_time)) == 1)

答案 2 :(得分:3)

我还会添加一个data.table解决方案,并且针对更大的数据提出针对dplyr的好奇心基准:

require(data.table)
DT <- as.data.table(df)
DT[DT[, .I[which.max(submission_time)], by=list(session_user_id)]$V1]

这里我假设OP只需一个观察,即使是多个相同的“最大”值。如果没有,请查看下面的f2函数。


更大数据与dplyr的基准:

针对@ hadley dplyr更大数据的解决方案进行基准测试。我假设有大约50e3个用户ID,总共有1e7行。

require(data.table)  # 1.8.11 commit 1142
require(dplyr)       # latest commit from github
set.seed(45L)
DT <- data.table(session_user_id = sample(paste0("id", 1:5e4), 1e7, TRUE), 
                 raw_score = sample(10, 1e7, TRUE), 
                 submission_time = sample(1e5:5e5, 1e7, TRUE))

DF <- tbl_df(as.data.frame(DT))

f1 <- function(DT) {
    DT[DT[, .I[which.max(submission_time)], by=list(session_user_id)]$V1]
}

f2 <- function(DT) {
    DT[DT[, .I[submission_time == max(submission_time)], 
            by=list(session_user_id)]$V1]
}

f3 <- function(DF) {
    DF %.%
        group_by(session_user_id) %.%
        filter(submission_time == max(submission_time))
}

f4 <- function(DF) {
    DF %.%
      group_by(session_user_id) %.%
      filter(row_number(desc(submission_time)) == 1)
}

以下是时间安排。所有这些都至少有三次运行:

system.time(a1 <- f1(DT)) 
#   user  system elapsed
#  1.044   0.056   1.101

system.time(a2 <- f2(DT)) 
#   user  system elapsed
#  1.384   0.080   1.475

system.time(a3 <- f3(DF)) 
#   user  system elapsed
#  4.513   0.044   4.555

system.time(a4 <- f4(DF)) 
#   user  system elapsed
#  6.312   0.004   6.314

正如预期的那样f4是最慢的,因为它使用desc(我猜测它在某种程度上涉及到每个组的排序或排序 - 比计算成本更高的操作只是获得maxwhich.max)。

此处,a1a4(即使存在多个最大值,也只有一个观察点)会得到相同的结果,a2a3(所有最大值)也是如此

data.table此处至少快3倍(比较a2a3),比f1f4的速度提高约5.7倍。

答案 3 :(得分:2)

您可以使用&#34; plyr&#39;包以总结数据。这样的事情应该有效

max_subs<-ddply(df,"session_user_id",summarize,max_sub=max(submission_time))

ddply获取数据框并返回一个数据框,这将为您提供所需的用户和提交时间。

要返回与您可以执行的原始数据框行相对应的

df2<-df[df$session_user_id %in% max_subs$session_user_id & df$submission_time %in% max_subs$max_sub,]

答案 4 :(得分:2)

首先通过session_user_id查找最长提交时间。此表将由session_user_id唯一。

然后将(sql-speak:inner join)合并回原来的表格,加入submission_time&amp; session_user_id(R自动在两个数据帧中选取通用名称。)

maxSessions<-aggregate(submission_time~session_user_id , df, max)
mySubset<-merge(df, maxSessions)
mySubset #this table has the data your are looking for

如果您正在寻找速度且数据集很大,请查看此How to summarize data by group in R? data.table&amp; plyr是不错的选择。

答案 5 :(得分:1)

这只是一个扩展评论,因为我对每个解决方案的速度感兴趣

library(microbenchmark)
library(plyr)
library(dplyr)
library(data.table)

df <- df[sample(1:nrow(df),10000,replace=TRUE),] # 10k records

fun.test1 <- function(df) {
  df <- df[order(df$submission_time, decreasing = TRUE),]
  df <- df[!duplicated(df$session_user_id),]
  return(df)
}

fun.test2 <- function(df) { 
  max_subs<-ddply(df,"session_user_id",summarize,max_sub=max(submission_time))
  df2<-df[df$session_user_id %in% max_subs$session_user_id & 
          df$submission_time %in% max_subs$max_sub,]
  return(df2)
}

fun.test3 <- function(df) {
  df <- df %.%
    group_by(session_user_id) %.%
    filter(submission_time == max(submission_time))
  return(df)
}

fun.test4 <- function(df) {
  maxSessions<-aggregate(submission_time~session_user_id , df, max)
  mySubset<-merge(df, maxSessions)
  return(mySubset)
}

fun.test5 <- function(df) { 
  df <- df[df$submission_time %in% by(df, df$session_user_id,
           function(x) max(x$submission_time)),]
  return(df)
}

dt <- as.data.table(df) # Assuming you're working with data.table to begin with
# Don't know a lot about data.table so I'm sure there's a faster solution
fun.test6 <- function(dt) { 
  dt <- unique(
    dt[,
       list(raw_score,submission_time=max(submission_time)),
       by=session_user_id]
    )
  return(dt)
}

看起来!duplicated()的最基本解决方案在小数据(低于1k)的情况下获胜很大,其次是dplyrdplyr获得大样本(超过1k)。

microbenchmark(
 fun.test1(df),
 fun.test2(df),
 fun.test3(df),
 fun.test4(df),
 fun.test5(df),
 fun.test6(dt)
)

         expr        min          lq     median         uq        max neval
 fun.test1(df)   2476.712   2660.0805   2740.083   2832.588   9162.339   100
 fun.test2(df)   5847.393   6215.1420   6335.932   6477.745  12499.775   100
 fun.test3(df)    815.886    924.1405   1003.585   1050.169   1128.915   100
 fun.test4(df) 161822.674 167238.5165 172712.746 173254.052 225317.480   100
 fun.test5(df)   5611.329   5899.8085   6000.555   6120.123  57572.615   100
 fun.test6(dt) 511481.105 541534.7175 553155.852 578643.172 627739.674   100