R 确定三个值中最大值的最快/最简单算法/函数
这是一个非常基本的编程问题,但为了将来,我只想知道哪种方法是处理这种常见情况的最佳方法 我有三列,级别在0和10之间,我希望确定其中哪一列的值最高,并在变异列或以其他方式创建的“最大”列中显示列的名称。在任何情况下,我更喜欢c而不是b而不是a列,因为此开关将用于从其他列中提取值,这些列可能与这些列不同 下面的代码可以做到这一点,但是有没有一种更简短的方法呢R 确定三个值中最大值的最快/最简单算法/函数,r,algorithm,conditional,dplyr,R,Algorithm,Conditional,Dplyr,这是一个非常基本的编程问题,但为了将来,我只想知道哪种方法是处理这种常见情况的最佳方法 我有三列,级别在0和10之间,我希望确定其中哪一列的值最高,并在变异列或以其他方式创建的“最大”列中显示列的名称。在任何情况下,我更喜欢c而不是b而不是a列,因为此开关将用于从其他列中提取值,这些列可能与这些列不同 下面的代码可以做到这一点,但是有没有一种更简短的方法呢 set.seed(7) mat <- matrix(as.integer(runif(15, 0, 10)), nrow = 5, n
set.seed(7)
mat <- matrix(as.integer(runif(15, 0, 10)), nrow = 5, ncol = 3)
colnames(mat) <- letters[1:3]
(mat)
matBestOf <-
data.frame(mat) %>%
mutate(Largest = ifelse(c >= b & c >= a, "c",
ifelse(b >= c & b >= a, "b",
"a"))
)
matBestOf
# a b c Largest
# 1 9 7 1 a
# 2 3 3 2 b
# 3 1 9 7 b
# 4 0 1 0 b
# 5 2 4 4 c
这里有一个max.col选项:
我使用select将列按您指定的顺序排列,这样我们就可以简单地使用ties.method=first。everything可确保其他列(如果存在)也会被选中,但会出现在前三列之后。使用apply和rev将c优先于b,而不是a:
cbind.data.frame(mat,
Largest = apply(mat, 1,
function(i)rev(colnames(mat))[rev(i) == max(i)][1]))
# a b c Largest
# 1 9 7 1 a
# 2 3 3 2 b
# 3 1 9 7 b
# 4 0 1 0 b
# 5 2 4 4 c
编辑:基准测试
将rev置于apply之外会使代码在更大数据上的速度提高3-4倍,但仍然不如dplyr解决方案快
library(dplyr)
# bigger dummy data
bigmat <- matrix(rep(mat, 10000), ncol = 20)
colnames(bigmat) <- letters[1:ncol(bigmat)]
microbenchmark::microbenchmark(
dplyr = {bigmat %>%
data.frame() %>%
select(c,b,a, everything()) %>%
mutate(Largest = names(.)[max.col(., ties.method = "first")])},
base_apply_v1 = {
cbind.data.frame(bigmat,
Largest = apply(bigmat, 1,
function(i)rev(colnames(bigmat))[rev(i) == max(i)][1]))
},
base_apply_v2 = {
myFlip <- bigmat[nrow(bigmat):1, ncol(bigmat):1]
myNames <- colnames(myFlip)
cbind.data.frame(bigmat,
Largest = apply(myFlip, 1,
function(i)myNames[i == max(i)][1]))
}
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# dplyr 3.271673 3.52583 4.665696 3.730951 5.915583 8.405259 100 a
# base_apply_v1 86.191320 91.94412 99.370839 93.709812 96.214598 196.007909 100 c
# base_apply_v2 23.121803 26.70536 30.906054 28.042854 29.065466 134.257780 100 b
下面是一个使用data.table的选项
也许这对你有帮助。如果您在解决方案中进行了一些不必要的测试,那么代码中只需要进行3次比较,并且只需要执行3次比较中的2次。请参见示例2:非常好!谢谢你的反馈和有用的链接:我到处都找过这样的东西。[以上代码已更新。]@leerssej mat需要转换为data.frame,请参见编辑。2新函数:everything和max.col,并重新学习如何将索引转换为其相关值。谢谢大家!@里尔塞吉,欢迎你!顺便说一句,在c对b对a的情况下,您也可以省略select语句并更改ties.method=last。@Docendiscimus用last更新了您的帖子,希望您不介意,它提供了0.3分钟更好的性能:在bigmat数据上。@zx8754,没问题,谢谢您的更新和基准测试
cbind.data.frame(mat,
Largest = apply(mat, 1,
function(i)rev(colnames(mat))[rev(i) == max(i)][1]))
# a b c Largest
# 1 9 7 1 a
# 2 3 3 2 b
# 3 1 9 7 b
# 4 0 1 0 b
# 5 2 4 4 c
library(dplyr)
# bigger dummy data
bigmat <- matrix(rep(mat, 10000), ncol = 20)
colnames(bigmat) <- letters[1:ncol(bigmat)]
microbenchmark::microbenchmark(
dplyr = {bigmat %>%
data.frame() %>%
select(c,b,a, everything()) %>%
mutate(Largest = names(.)[max.col(., ties.method = "first")])},
base_apply_v1 = {
cbind.data.frame(bigmat,
Largest = apply(bigmat, 1,
function(i)rev(colnames(bigmat))[rev(i) == max(i)][1]))
},
base_apply_v2 = {
myFlip <- bigmat[nrow(bigmat):1, ncol(bigmat):1]
myNames <- colnames(myFlip)
cbind.data.frame(bigmat,
Largest = apply(myFlip, 1,
function(i)myNames[i == max(i)][1]))
}
)
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# dplyr 3.271673 3.52583 4.665696 3.730951 5.915583 8.405259 100 a
# base_apply_v1 86.191320 91.94412 99.370839 93.709812 96.214598 196.007909 100 c
# base_apply_v2 23.121803 26.70536 30.906054 28.042854 29.065466 134.257780 100 b
library(data.table)
as.data.table(mat)[, Largest := rev(colnames(mat))[which.max(rev(unlist(.SD)))] , 1:nrow(mat)][]
# a b c Largest
#1: 9 7 1 a
#2: 3 3 2 b
#3: 1 9 7 b
#4: 0 1 0 b
#5: 2 4 4 c