我有一个数据集
dtf<-data.frame(id=c("A","A","A","A","B","B","B","B"), value=c(2,4,6,8,4,6,8,10))
对于每个 id,值按升序排序
我想减少 dtf 以仅包含每个id
值超过指定限制的第一行。每行只有一行id
,那应该是第一个value
超过指定限制的行。
对于这个例子和5
dtf 的限制应该减少到:
A 6
B 6
这是一个很好的方法吗?
非常感谢
可以通过以下方式完成aggregate
:
dtf<-data.frame(id=c("A","A","A","A","B","B","B","B"), value=c(2,4,6,8,4,6,8,10))
limit <- 5
aggregate(value ~ id, dtf, function(x) x[x > limit][1])
结果:
id value
1 A 6
2 B 6
更新:多列的解决方案:
一个示例数据框dtf2
:
dtf2 <- data.frame(id=c("A","A","A","A","B","B","B","B"),
value=c(2,4,6,8,4,6,8,10),
col3 = letters[1:8],
col4 = 1:8)
解决方案包括ave
:
with(dtf2, dtf2[ave(value, id, FUN = function(x) cumsum(x > limit)) == 1, ])
结果:
id value col3 col4
3 A 6 c 3
6 B 6 f 6
这是一个“不错”的选项,使用data.table
:
library(data.table)
DT <- data.table(dft, key = "id")
DT[value > 5, head(.SD, 1), by = key(DT)]
# id value
# 1: A 6
# 2: B 6
而且,本着分享的精神,sqldf
根据您是否对 SQL 感觉更舒服,使用 which 的选项可能会更好。
sqldf("select id, min(value) as value from dtf where value > 5 group by id")
# id value
# 1 A 6
# 2 B 6
data.frame
多列根据您对某些答案的评论,您的“价值”列可能不像您的示例中那样排序,并且您的data.frame
.
对于这些场景,这里有两种替代方案,一种是data.table
,我发现它最容易阅读并且很可能是最快的,另一种是这种任务通常需要的典型“拆分-应用-组合”方法。
首先,一些示例数据:
dtf2 <- data.frame(id = c("A","A","A","A","B","B","B","B"),
value = c(6,4,2,8,4,10,8,6),
col3 = letters[1:8],
col4 = 1:8)
dtf2 # Notice that the value column is not ordered
# id value col3 col4
# 1 A 6 a 1
# 2 A 4 b 2
# 3 A 2 c 3
# 4 A 8 d 4
# 5 B 4 e 5
# 6 B 10 f 6
# 7 B 8 g 7
# 8 B 6 h 8
二、data.table
做法:
library(data.table)
DT <- data.table(dtf2)
DT # Verify that the data are not ordered
# id value col3 col4
# 1: A 6 a 1
# 2: A 4 b 2
# 3: A 2 c 3
# 4: A 8 d 4
# 5: B 4 e 5
# 6: B 10 f 6
# 7: B 8 g 7
# 8: B 6 h 8
DT[order(value)][value > 5, head(.SD, 1), by = "id"]
# id value col3 col4
# 1: A 6 a 1
# 2: B 6 h 8
二、base R 常用的“split-apply-combine”方法:
do.call(rbind,
lapply(split(dtf2, dtf2$id),
function(x) x[x$value > 5, ][which.min(x$value[x$value > 5]), ]))
# id value col3 col4
# A A 6 a 1
# B B 6 h 8
另一种方法aggregate
:
> aggregate(value~id, dtf[dtf[,'value'] > 5,], min)
id value
1 A 6
2 B 6
这确实取决于被排序的元素,因为这将是返回的条目min
plyr
也可以用and替代head
:
library(plyr)
dtf<-data.frame(id=c("A","A","A","A","B","B","B","B"), value=c(2,4,6,8,4,6,8,10))
limit <- 5
result <- ddply(dtf, "id", function(x) head(x[x$value > limit ,],1) )
> result
id value
1 A 6
2 B 6
这取决于您的 data.frame 被排序:
threshold <- 5
foo <- dtf[dtf$value>=threshold,]
foo[c(1,which(diff(as.numeric(as.factor(foo$id)))>0)),]