【问题标题】:The Fastest/Simplest Algorithm/Function to Determine Largest of Three Values确定三个值中最大值的最快/最简单算法/函数
【发布时间】:2016-10-16 04:35:32
【问题描述】:

这里是一个非常基本的编程问题,但为了将来,我只想知道哪种方式是处理这种常见情况的最佳方式。

我有 0 到 10 之间不同级别的三列,我希望确定其中哪一个具有最高的值,并显示列的名称(在变异列中或以其他方式创建的“最大”列中。)如果出现任何关系,我更喜欢 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() 函数,但我只是让它返回最大值而不是具有最大值的列名。此外,我显然没有比较所有三列的值,因为结果仅来自 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

【问题讨论】:

标签: r algorithm conditional dplyr


【解决方案1】:

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

【讨论】:

  • @leerssej mat 需要转换成data.frame,见编辑。
  • 2 个新函数:everything() 和 max.col(),并重新学习了如何将索引转换为其相关值。谢谢!
  • @leerssej,欢迎!顺便说一句,在你的情况下(c 超过 b 超过 a)你也可以省略 select 语句并更改 ties.method = "last"
  • @docendodiscimus 用last 更新了您的帖子,希望您不介意,它在bigmat 数据上提供了0.3 分钟的更好性能:)。
  • @zx8754,没问题,感谢更新和基准测试
【解决方案2】:

使用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 

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2011-12-29
      • 2019-06-08
      • 2019-05-21
      • 2021-11-26
      • 2015-10-29
      • 2010-12-27
      • 2012-02-10
      • 2022-12-09
      • 2011-01-15
      相关资源
      最近更新 更多