我遇到了一些困扰我的问题...希望这里的任何人都可以帮助我.
我得到了以下数据框
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)
Run Code Online (Sandbox Code Playgroud)
f是一个因素; v1和v2是值.对于f的每个级别,我只想要保留一行:在此因子级别中具有最低值v1的行.
f v1 v2
a 1.3 1
b 2 3
c 1.1 6
d 3.1 8
Run Code Online (Sandbox Code Playgroud)
我用聚合,ddply,by,tapply尝试了各种各样的东西......但似乎没有任何效果.对于任何建议,我将非常感激.
Jor*_*eys 31
使用DWin的解决方案,tapply
可以避免使用ave
.
df[ df$v1 == ave(df$v1, df$f, FUN=min), ]
Run Code Online (Sandbox Code Playgroud)
这提供了另一种加速,如下所示.请注意,这也取决于级别的数量.我注意到它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
Run Code Online (Sandbox Code Playgroud)
mne*_*nel 14
一个data.table
解决方案.
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
Run Code Online (Sandbox Code Playgroud)
或者,更有效率
DT[DT[,.I[which.min(v1)],by=f][['V1']]]
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这种.I
方法是胜利者(FR#2330有望.SD
在实施时同样快速地呈现方法的优雅).
有了plyr
,我会用:
ddply(df, .var = "f", .fun = function(x) {
return(subset(x, v1 %in% min(v1)))
}
)
Run Code Online (Sandbox Code Playgroud)
尝试一下,看看它是否返回你想要的东西.
另一个tapply
解决方案,没有不必要的矢量扫描%in%
:
df[tapply(1:nrow(df),df$f,function(i) i[which.min(df$v1[i])]),]
Run Code Online (Sandbox Code Playgroud)
编辑:如果出现平局,这将只留下第一行.
编辑2:印象深刻ave
,我做了进一步的改进:
df[sapply(split(1:nrow(df),df$f),function(x) x[which.min(df$v1[x])]),]
Run Code Online (Sandbox Code Playgroud)
在我的机器上(使用Joris的基准数据):
> system.time(df[ df$v1 == ave(df$v1, df$f, FUN=min), ])
user system elapsed
0.022 0.000 0.021
> system.time(df[sapply(split(1:nrow(df),df$f),function(x) x[which.min(df$v1[x])]),])
user system elapsed
0.006 0.000 0.007
Run Code Online (Sandbox Code Playgroud)
v1
这是按组过滤最小值的 dplyr 方式f
:
library(dplyr)
df |>
group_by(f) |>
slice_min(v1)
Run Code Online (Sandbox Code Playgroud)
请参阅?slice_min
帮助页面以了解选项,例如是否包含平局(默认包含平局),或保留 1 个以上最低值的选项(例如,倒数 5 个或倒数 10%)。
您还可以更明确地执行此操作:
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
Run Code Online (Sandbox Code Playgroud)
如果 中出现平局v1
,这将导致每组 中有多个行f
。如果你想避免这种情况,你可以使用:
df %>%
group_by(f) %>%
filter(rank(v1, ties.method= "first") == 1)
Run Code Online (Sandbox Code Playgroud)
这样,如果出现平局,您只会获得第一行。您可以选择使用ties.method = "random"
或 帮助文件中描述的其他内容。