确定三个值中最大的最快/简单的算法/函数是什么?

3

这里是一个非常基础的编程问题,但为了将来的发展,我想知道处理这种常见情况的最佳方式。我有三列变化在0到10之间的不同级别,并希望确定它们中哪一列具有最高的值,并显示该列的名称(在突变的列或另外创建的“最大”列中)。如果存在任何平局,我更喜欢选择c列而不是b或a列,因为此开关将用于从其他列中提取值,这些值可能与这些等效。

下面的代码可以完成任务,但是否有更短、更简单的方法呢?

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()函数,但它只返回最高的值而不是具有最高值的列名。此外,显然我没有比较所有三列的值,因为结果仅来自ac中的最佳值,从未来自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

也许这会对你有所帮助。https://dev59.com/0Gkv5IYBdhLWcg3w4kl2 - Pankaj Kaundal
3
你的解决方案中进行了多次不必要的测试,实际上在代码中只需要进行3次比较,其中只有2次是需要执行的。可以参考这个例子2:http://www.programiz.com/c-programming/examples/largest-number-three - Jackson
太好了!感谢您的反馈和有用的链接:我已经到处寻找类似的东西。[上面的代码已更新。] - leerssej
3个回答

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() 确保其他列(如果存在)也会被选择,但会出现在前三列之后。


1
@leerssej mat需要转换为data.frame,请参见编辑。 - zx8754
2个新函数:everything()和max.col(),并重新学习了如何将索引转换为其相关值。谢谢! - leerssej
2
@leerssej,欢迎!顺便说一下,在您的情况下(c over b over a),您也可以省略select语句并更改tied.method =“last” - talat
@docendodiscimus 使用 last 更新了您的帖子,希望您不介意,这将在 bigmat 数据上提高 0.3 分钟的性能 :)。 - zx8754
@zx8754,没问题,感谢您的更新和基准测试。 - talat

3
使用applyrev将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 

2

这里有一个使用 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接