Fastest/Simplest Algorithm/Function 确定三个值中的最大值
The Fastest/Simplest Algorithm/Function to Determine Largest of Three Values
这是一个 非常 的基本编程问题,但为了将来,我只想知道哪种方法是处理这种常见情况的最佳方法。
我有 0 到 10 之间不同级别的三列,我想确定其中哪一个具有最高值,并显示列的名称(在变异列中或以其他方式创建 'Largest'列。)如果有任何关系,我更喜欢 c 而不是 b 而不是列,因为此开关将用于从其他列中提取值,这些值可能与这些列不同。
下面的代码可以解决问题,但是有更短更简单的方法吗?
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()
函数,但我只得到 return 最高值,而不是具有最高值的列名。此外,我显然没有比较所有三列的值,因为结果只是来自 a 和 c 的最佳结果,而且永远不会b。此外,我似乎不喜欢大写字母,这没关系,也许没有那个附加功能我也可以生活。
matBestOf <-
data.frame(mat) %>%
rowwise %>%
mutate(Largest = max(a:c))
matBestOf
# Source: local data frame [5 x 4]
# Groups: <by row>
#
# a b c Largest
# (int) (int) (int) (int)
# 1 9 7 1 9
# 2 3 3 2 3
# 3 1 9 7 7
# 4 0 1 0 0
# 5 2 4 4 4
这里有一个选项 max.col
:
mat %>%
data.frame() %>%
mutate(Largest = names(.)[max.col(., ties.method = "last")])
# c b a Largest
#1 1 7 9 a
#2 2 3 3 b
#3 7 9 1 b
#4 0 1 0 b
#5 4 4 2 c
我使用 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
的选项
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
这是一个 非常 的基本编程问题,但为了将来,我只想知道哪种方法是处理这种常见情况的最佳方法。
我有 0 到 10 之间不同级别的三列,我想确定其中哪一个具有最高值,并显示列的名称(在变异列中或以其他方式创建 'Largest'列。)如果有任何关系,我更喜欢 c 而不是 b 而不是列,因为此开关将用于从其他列中提取值,这些值可能与这些列不同。
下面的代码可以解决问题,但是有更短更简单的方法吗?
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()
函数,但我只得到 return 最高值,而不是具有最高值的列名。此外,我显然没有比较所有三列的值,因为结果只是来自 a 和 c 的最佳结果,而且永远不会b。此外,我似乎不喜欢大写字母,这没关系,也许没有那个附加功能我也可以生活。
matBestOf <-
data.frame(mat) %>%
rowwise %>%
mutate(Largest = max(a:c))
matBestOf
# Source: local data frame [5 x 4]
# Groups: <by row>
#
# a b c Largest
# (int) (int) (int) (int)
# 1 9 7 1 9
# 2 3 3 2 3
# 3 1 9 7 7
# 4 0 1 0 0
# 5 2 4 4 4
这里有一个选项 max.col
:
mat %>%
data.frame() %>%
mutate(Largest = names(.)[max.col(., ties.method = "last")])
# c b a Largest
#1 1 7 9 a
#2 2 3 3 b
#3 7 9 1 b
#4 0 1 0 b
#5 4 4 2 c
我使用 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
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