我有一个数据集,每个参与者都有几个时间评估。我想为每个参与者选择最后一次评估。我的数据集如下所示:
ID week outcome
1 2 14
1 4 28
1 6 42
4 2 14
4 6 46
4 9 64
4 9 71
4 12 85
9 2 14
9 4 28
9 6 51
9 9 66
9 12 84
我想只为每个参与者选择最后一次观察/评估,但我只有周数作为每个参与者的指标。如何在R(或excel?)
中做到这一点提前感谢,
尼基
答案 0 :(得分:11)
这是一种基础R方法:
do.call("rbind",
by(df, INDICES=df$ID, FUN=function(DF) DF[which.max(DF$week), ]))
ID week outcome
1 1 6 42
4 4 12 85
9 9 12 84
或者,data.table
包提供了一种简洁而富有表现力的语言,用于执行此类数据框操作:
library(data.table)
dt <- data.table(df, key="ID")
dt[, .SD[which.max(outcome), ], by=ID]
# ID week outcome
# [1,] 1 6 42
# [2,] 4 12 85
# [3,] 9 12 84
# Same but much faster.
# (Actually, only the same as long as there are no ties for max(outcome)..)
dt[ dt[,outcome==max(outcome),by=ID][[2]] ] # same, but much faster.
# If there are ties for max(outcome), the following will still produce
# the same results as the method using .SD, but will be faster
i1 <- dt[,which.max(outcome), by=ID][[2]]
i2 <- dt[,.N, by=ID][[2]]
dt[i1 + cumsum(i2) - i2,]
最后,这是一个基于plyr
的解决方案
library(plyr)
ddply(df, .(ID), function(X) X[which.max(X$week), ])
# ID week outcome
# 1 1 6 42
# 2 4 12 85
# 3 9 12 84
答案 1 :(得分:9)
如果您只是在寻找每人ID的最后一次观察,那么应该使用简单的两行代码。在可能的情况下,我总是寻求简单的基础解决方案,而拥有多种方法解决问题总是很棒。
dat[order(dat$ID,dat$Week),] # Sort by ID and week
dat[!duplicated(dat$ID, fromLast=T),] # Keep last observation per ID
ID Week Outcome
3 1 6 42
8 4 12 85
13 9 12 84
答案 2 :(得分:2)
基地的另一个选项:df[rev(rownames(df)),][match(unique(df$ID), rev(df$ID)), ]
答案 3 :(得分:2)
我可以玩这个游戏。我在 lapply , sapply 和 之间的差异上运行了一些基准测试。在我看来,你对数据类型的控制越多,操作越基本,它就越快(例如,lapply通常比sapply更快,而as.numeric(lapply(...))正在进行更快,也)。考虑到这一点,这产生了与上述相同的结果,可能比其他结果更快。
df[cumsum(as.numeric(lapply(split(df$week, df$id), which.max))), ]
说明:我们只想在每个id的周上使用which.max。它处理 lapply 的内容。我们只需要这些相对点的向量,所以将它设为数字。结果是向量(3,5,5)。我们需要添加先前最大值的位置。这是通过 cumsum 完成的。
应该注意,当我使用 cumsum 时,这个解决方案并不常用。它可能要求在执行之前我们在id和week上对帧进行排序。我希望你理解为什么(并且知道如何在行索引中使用 with(df,order(id,week))来实现这一点。在任何情况下,如果我们没有唯一的最大值,它可能仍然会失败,因为which.max只接受第一个。因此,我的解决方案有点问题,但不言而喻。我们试图为一个非常具体的例子提取非常具体的信息。我们的解决方案不可能是一般性的(尽管这些方法通常很重要)。
我会留下来更新他的比较!
答案 4 :(得分:2)
这个答案使用data.table包。即使数据集较大,它也应该非常快。
setkey(DT, ID, week) # Ensure it's sorted.
DT[DT[, .I[.N], by = ID][, V1]]
说明:.I
是一个整数向量,包含组的行位置(在本例中为组ID
)。 .N
是一个长度为一的整数向量,包含组中的行数。所以我们在这里做的是使用“内部”DT[.]
提取每个组的最后一行的位置,使用数据根据ID
和{{1 }}。之后我们使用它来“外部”week
。
为了进行比较(因为它未在其他地方发布),以下是如何生成原始数据以便您可以运行代码:
DT[.]
答案 5 :(得分:1)
我一直在尝试使用split和tapply,以便更熟悉它们。我知道这个问题已经得到了回答,但我想我会用拆分添加另一个solotuion(原谅丑陋;我不仅仅对改进提出反馈意见;想想也许有一个用来减轻代码的用法):< / p>
sdf <-with(df, split(df, ID))
max.week <- sapply(seq_along(sdf), function(x) which.max(sdf[[x]][, 'week']))
data.frame(t(mapply(function(x, y) y[x, ], max.week, sdf)))
我也想到为什么我们在这里有7个答案它已经成熟了基准。结果可能会让您感到惊讶(在Win 7机器上使用rbenchmark和R2.14.1):
# library(rbenchmark)
# benchmark(
# DATA.TABLE= {dt <- data.table(df, key="ID")
# dt[, .SD[which.max(outcome),], by=ID]},
# DO.CALL={do.call("rbind",
# by(df, INDICES=df$ID, FUN=function(DF) DF[which.max(DF$week),]))},
# PLYR=ddply(df, .(ID), function(X) X[which.max(X$week), ]),
# SPLIT={sdf <-with(df, split(df, ID))
# max.week <- sapply(seq_along(sdf), function(x) which.max(sdf[[x]][, 'week']))
# data.frame(t(mapply(function(x, y) y[x, ], max.week, sdf)))},
# MATCH.INDEX=df[rev(rownames(df)),][match(unique(df$ID), rev(df$ID)), ],
# AGGREGATE=df[cumsum(aggregate(week ~ ID, df, which.max)$week), ],
# #WHICH.MAX.INDEX=df[sapply(unique(df$ID), function(x) which.max(x==df$ID)), ],
# BRYANS.INDEX = df[cumsum(as.numeric(lapply(split(df$week, df$ID),
# which.max))), ],
# SPLIT2={sdf <-with(df, split(df, ID))
# df[cumsum(sapply(seq_along(sdf), function(x) which.max(sdf[[x]][, 'week']))),
# ]},
# TAPPLY=df[tapply(seq_along(df$ID), df$ID, function(x){tail(x,1)}),],
# columns = c( "test", "replications", "elapsed", "relative", "user.self","sys.self"),
# order = "test", replications = 1000, environment = parent.frame())
test replications elapsed relative user.self sys.self
6 AGGREGATE 1000 4.49 7.610169 2.84 0.05
7 BRYANS.INDEX 1000 0.59 1.000000 0.20 0.00
1 DATA.TABLE 1000 20.28 34.372881 11.98 0.00
2 DO.CALL 1000 4.67 7.915254 2.95 0.03
5 MATCH.INDEX 1000 1.07 1.813559 0.51 0.00
3 PLYR 1000 10.61 17.983051 5.07 0.00
4 SPLIT 1000 3.12 5.288136 1.81 0.00
8 SPLIT2 1000 1.56 2.644068 1.28 0.00
9 TAPPLY 1000 1.08 1.830508 0.88 0.00
编辑1:我省略了WHICH MAX解决方案,因为它没有返回正确的结果并返回了我想要使用的AGGREGATE解决方案(Bryan Goodrich的赞美)和更新版本的拆分,SPLIT2,使用cumsum(我喜欢那个动作)。
编辑2: Dason还加入了一个tapply解决方案,我投入了相当不错的测试。