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

df2A
data.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)