我有一个数据框架,结构如下
输入
set.seed(5)
df <- data.frame(A= round(runif(6,0,100)),B= round(runif(6,0,100)),C= round(runif(6,0,100)),D= round(runif(6,0,100)))
A B C D
1 20 53 32 55
2 69 81 56 84
3 92 96 26 89
4 28 11 20 72
5 10 27 39 21
6 70 49 89 23
现在,我想添加两列额外的列,每列分别是一行的第二和第三大元素。
輸出
A B C D thirdLarge secLarge
1 20 53 32 55 32 53
2 69 81 56 84 69 81
3 92 96 26 89 89 92
4 28 11 20 72 20 28
5 10 27 39 21 21 27
6 70 49 89 23 49 70
我试着用一个简单的 "for循环 "来做,但这样做效率不够高,而且对于700000行来说,要花很长时间。
我们可以用 apply
:
df[c('thirdLarge', 'secLarge')] <- t(apply(df, 1, function(x)
sort(x)[c(length(x)-2, length(x) - 1)]))
#This is shorter
df[c('thirdLarge', 'secLarge')] <- t(apply(df, 1, function(x)
sort(x, decreasing = TRUE)[3:2]))
df
# A B C D thirdLarge secLarge
#1 20 53 32 55 32 53
#2 69 81 56 84 69 81
#3 92 96 26 89 89 92
#4 28 11 20 72 20 28
#5 10 27 39 21 21 27
#6 70 49 89 23 49 70
使用 rank
正如 @Chris Ruehlemann 所建议的那样,但如果你连续两个相同的值,这将会失败。
df[c('secLarge', 'thirdLarge')] <- t(apply(df, 1, function(x)
x[rank(-x) %in% 2:3]))
如果速度是个问题,请查看 Rfast 包。
library(Rfast)
library(dplyr)
mutate(df,
lrg.2 = rownth(as.matrix(df), elems=rep(2, nrow(df)), descending=TRUE),
lrg.3 = rownth(as.matrix(df), elems=rep(3, nrow(df)), descending=TRUE))
A B C D lrg.2 lrg.3
1 20 53 32 55 53 32
2 69 81 56 84 81 69
3 92 96 26 89 92 89
4 28 11 20 72 28 20
5 10 27 39 21 27 21
6 70 49 89 23 70 49
对于一个包含700,000行的数据框架,这需要<1秒。使用apply需要>30秒。
看看我是否能做出一个更快的基础版本,不需要用 apply
:
我基本上是按一次订购 row
和递减值,然后将其反馈到一个。matrix
并取所需的列。
udf <- unlist(df)
mat <- matrix(udf[order(row(df), -udf)], ncol=ncol(df), byrow=TRUE)
df[c('thirdLarge','secLarge')] <- mat[,3:2]
## A B C D thirdLarge secLarge
##1 20 53 32 55 32 53
##2 69 81 56 84 69 81
##3 92 96 26 89 89 92
##4 28 11 20 72 20 28
##5 10 27 39 21 21 27
##6 70 49 89 23 49 70
df <- df[rep(1:6, 116667),]
## 700,002 rows
system.time({
udf <- unlist(df)
mat <- matrix(udf[order(row(df), -udf)], ncol=ncol(df), byrow=TRUE)
df[c('thirdLarge','secLarge')] <- mat[,3:2]
})
## user system elapsed
## 0.971 0.000 0.972
比较在行上循环与 apply
:
system.time({
df[c('thirdLarge', 'secLarge')] <- t(apply(df, 1, function(x)
sort(x, decreasing = TRUE)[3:2]))
})
## user system elapsed
## 24.362 0.003 24.365