R 按组选择具有最小值的行
我有一个困扰我一段时间的问题…希望这里的任何人都能帮助我 我得到了以下数据帧R 按组选择具有最小值的行,r,dataframe,R,Dataframe,我有一个困扰我一段时间的问题…希望这里的任何人都能帮助我 我得到了以下数据帧 f <- c('a','a','b','b','b','c','d','d','d','d') v1 <- c(1.3,10,2,10,10,1.1,10,3.1,10,10) v2 <- c(1:10) df <- data.frame(f,v1,v2) 我尝试了各种各样的东西,包括聚合、ddply、by、tapply……但似乎没有任何效果。如果有任何建议,我将非常感谢 对于plyr,我会使
f <- c('a','a','b','b','b','c','d','d','d','d')
v1 <- c(1.3,10,2,10,10,1.1,10,3.1,10,10)
v2 <- c(1:10)
df <- data.frame(f,v1,v2)
我尝试了各种各样的东西,包括聚合、ddply、by、tapply……但似乎没有任何效果。如果有任何建议,我将非常感谢 对于
plyr
,我会使用:
ddply(df, .var = "f", .fun = function(x) {
return(subset(x, v1 %in% min(v1)))
}
)
试一试,看看它是否能返回您想要的结果。对不起,我的思维能力已经耗尽,而我几乎凌晨1点就只能想出这个丑陋的解决方案
lapply(split(df, df$f), FUN = function(x) {
vec <- which(x[3] == min(x[3]))
return(x[vec, ])
})
lappy(拆分(df,df$f),FUN=function(x){
vec这里有一个简单的解决方案
> df[ df$v1 %in% tapply(df$v1, df$f, min), ]
f v1 v2
1 a 1.3 1
3 b 2.0 3
6 c 1.1 6
8 d 3.1 8
在您的示例中,它仅为每组选择一个,但如果有联系,此方法将显示所有联系。(我怀疑Parker和Luštrik的方法也是如此。)另一个tapply
解决方案,无需使用%In%
对向量进行不必要的扫描:
df[tapply(1:nrow(df),df$f,function(i) i[which.min(df$v1[i])]),]
编辑:如果打成平局,将只剩下第一行
EDIT2:ave给我留下了深刻的印象,我做了额外的改进:
df[sapply(split(1:nrow(df),df$f),function(x) x[which.min(df$v1[x])]),]
在我的机器上(使用Joris的基准数据):
使用DWin的解决方案,可以使用ave
避免使用tapply
df[ df$v1 == ave(df$v1, df$f, FUN=min), ]
这会带来另一种加速,如下所示。请注意,这也取决于级别的数量。我注意到,ave
经常被遗忘,尽管它是R中更强大的功能之一
f <- rep(letters[1:20],10000)
v1 <- rnorm(20*10000)
v2 <- 1:(20*10000)
df <- data.frame(f,v1,v2)
> system.time(df[ df$v1 == ave(df$v1, df$f, FUN=min), ])
user system elapsed
0.05 0.00 0.05
> system.time(df[ df$v1 %in% tapply(df$v1, df$f, min), ])
user system elapsed
0.25 0.03 0.29
> system.time(lapply(split(df, df$f), FUN = function(x) {
+ vec <- which(x[3] == min(x[3]))
+ return(x[vec, ])
+ })
+ .... [TRUNCATED]
user system elapsed
0.56 0.00 0.58
> system.time(df[tapply(1:nrow(df),df$f,function(i) i[which.min(df$v1[i])]),]
+ )
user system elapsed
0.17 0.00 0.19
> system.time( ddply(df, .var = "f", .fun = function(x) {
+ return(subset(x, v1 %in% min(v1)))
+ }
+ )
+ )
user system elapsed
0.28 0.00 0.28
f系统时间(ddply(df,.var=“f”,.fun=函数(x){
+返回(子集(x,v1%在%min(v1))中)
+ }
+ )
+ )
用户系统运行时间
0.28 0.00 0.28
另一种方法是使用订单
和!复制的
,但您只能在领带上拿到第一张
df2 <- df[order(df$f,df$v1),]
df2[!duplicated(df2$f),]
f v1 v2
1 a 1.3 1
3 b 2.0 3
6 c 1.1 6
8 d 3.1 8
df2Adata.table
solution
library(data.table)
DT <- as.data.table(df)
DT[,.SD[which.min(v1)], by = f]
## f v1 v2
## 1: a 1.3 1
## 2: b 2.0 3
## 3: c 1.1 6
## 4: d 3.1 8
一些基准测试
f这是一个使用by
do.call(rbind, unname(by(df, df$f, function(x) x[x$v1 == min(x$v1),])))
## f v1 v2
## 1 a 1.3 1
## 3 b 2.0 3
## 6 c 1.1 6
## 8 d 3.1 8
这是dplyr按f
组筛选最小v1
值的方法:
require(dplyr)
df %>%
group_by(f) %>%
filter(v1 == min(v1))
#Source: local data frame [4 x 3]
#Groups: f
#
# f v1 v2
#1 a 1.3 1
#2 b 2.0 3
#3 c 1.1 6
#4 d 3.1 8
如果在v1
中出现连接,这将导致每组f
中出现多行。如果要避免这种情况,可以使用:
df %>%
group_by(f) %>%
filter(rank(v1, ties.method= "first") == 1)
这样,如果是ties,您将只获得第一行。您也可以使用ties.method=“random”
或帮助文件中描述的其他内容。关于领带的观点非常好。功能也很好-我读了几篇文章,看了一眼?t很快就知道发生了什么。哇,太棒了!Matt的ddply解决方案很有效,但在我的真实数据框(约10000行)中花了大约2分钟。此解决方案给出相同的结果,但不到1秒。非常优雅,谢谢!如果ddply对10000个观测值花费2分钟,则会出现问题。此外,此方法并不总是返回正确的结果-请考虑这样一种情况,其中2是一组的最低值,而另一组的第二最低值。这是错误的幸运的是,它对这个例子有效。可能是因为在10000次观察中有7700个因子水平?我用你的ddply解决方案再次尝试了它,它真的花了那么长时间…好吧,mbq,我投票支持你的。就像马特和我的一样,我必须通过你的“看”它的内部工作方式。@DWin很抱歉,我可能会高估效率:|我同意所有的解决方案都非常复杂;by
解决方案在我看来非常可读,但结果很糟糕(-;或者更简单地说:ddply(df,“f”,subset,v1==min(v1))
好的,解决方案很不错,但是由于很多级别,每个级别只有很少的值,速度非常慢…这显然是最好的答案!很好,你也添加了计时。感谢你的答案和计时。我如何使其适应多个因子?假设我将f1和f2作为因子,并且我希望每个组合的最小值。。。我试过c()和list(),但都不起作用。@donodarazao:see?ave
:ave(x,factor1,factor2,factor3,factor4,…,FUN=min)@哈德利:thx,我甚至没有意识到我纠正了一个棘手的错误。订购v1
就足够了。很好的解决方案,时间安排如何?@Marek谢谢,没有考虑到不需要在f
上订购。这似乎可以将速度提高约2倍,但仍然比Joris Meys的ave
解决方案慢一点。
DT[DT[,.I[which.min(v1)],by=f][['V1']]]
f <- rep(letters[1:20],100000)
v1 <- rnorm(20*100000)
v2 <- 1:(20*100000)
df <- data.frame(f,v1,v2)
DT <- as.data.table(df)
f1<-function(){df2<-df[order(df$f,df$v1),]
df2[!duplicated(df2$f),]}
f2<-function(){df2<-df[order(df$v1),]
df2[!duplicated(df2$f),]}
f3<-function(){df[ df$v1 == ave(df$v1, df$f, FUN=min), ]}
f4 <- function(){DT[,.SD[which.min(v1)], by = f]}
f5 <- function(){DT[DT[,.I[which.min(v1)],by=f][['V1']]]}
library(microbenchmark)
microbenchmark(f1(),f2(),f3(),f4(), f5(),times = 5)
# Unit: milliseconds
# expr min lq median uq max neval
# f1() 3254.6620 3265.4760 3286.5440 3411.4054 3475.4198 5
# f2() 1630.8572 1639.3472 1651.5422 1721.4670 1738.6684 5
# f3() 172.2639 174.0448 177.4985 179.9604 184.7365 5
# f4() 206.1837 209.8161 209.8584 210.4896 210.7893 5
# f5() 105.5960 106.5006 107.9486 109.7216 111.1286 5
do.call(rbind, unname(by(df, df$f, function(x) x[x$v1 == min(x$v1),])))
## f v1 v2
## 1 a 1.3 1
## 3 b 2.0 3
## 6 c 1.1 6
## 8 d 3.1 8
require(dplyr)
df %>%
group_by(f) %>%
filter(v1 == min(v1))
#Source: local data frame [4 x 3]
#Groups: f
#
# f v1 v2
#1 a 1.3 1
#2 b 2.0 3
#3 c 1.1 6
#4 d 3.1 8
df %>%
group_by(f) %>%
filter(rank(v1, ties.method= "first") == 1)