R 包 DT - 如何突出显示每行最大值

R package DT - how to highlight maximum per row

在 R 中使用 DT 包,我想 突出显示 max/min 每行 的数字数据框。

假设我们用 6 个聚类构建了分割,然后我们想用描述性变量来描述这些聚类:

library(DT)
library(tidyverse)
df <- iris[1:6, 1:4] %>% t()
(df)

#                1   2   3   4   5   6
# Sepal.Length 5.1 4.9 4.7 4.6 5.0 5.4
# Sepal.Width  3.5 3.0 3.2 3.1 3.6 3.9
# Petal.Length 1.4 1.4 1.3 1.5 1.4 1.7
# Petal.Width  0.2 0.2 0.2 0.2 0.2 0.4

我们想可视化哪些聚类具有最高 Sepal.Length,哪些聚类具有最高 Sepal.Width,等等。一个解决方案是对每一行的最大值进行着色。

如果我使用 DT 包的 formatStyle() 函数,我可以按列(突出显示最大值,颜色值根据特定的时间间隔,...),但我不能每行都这样做。即使我使用参数 target = 'row',整个行都会被着色,而不仅仅是最大值或我想要的单元格。

df %>% datatable() %>% 
   formatStyle('1', background = styleEqual(max(df[,1]), 'green'))

另一种方法是翻译我的数据框的行和列,但我更喜欢有一个有 50 行和 5 列的数据框,而不是一个有 5 行和 50 列的名字很长的数据框。

提前感谢您的帮助和建议。

为此您需要自定义 rowCallback 函数。这是一个例子:

iris[1:6, 1:4] %>% datatable(options=list(rowCallback = JS(
  'function(row, data) {
    var num_data = data.slice(1,data.length)
    var row_max = Math.max.apply(Math,num_data);
    var row_min = Math.min.apply(Math,num_data);
    for(i=1;i < data.length; i++) {
      if(data[i]==row_max) {
        $("td:eq("+i+")", row).css("background-color", "green")
      } else if(data[i]==row_min) {
        $("td:eq("+i+")", row).css("background-color", "yellow")
      }
    }
  }')))

请注意,您需要从 num_data 中过滤掉任何文本列,包括第一个具有行名的列。

如果你想高亮显示第二高的值,你可以排序num_data,并在JS代码中调整if/else颜色。一旦 num_data 被排序,num_data[num_data.length-1] 是最大值,num_data[0] 是最小值,num_data[num_data.length-2]` 是第二个最大值等

iris[1:6, 1:4] %>% datatable(options=list(rowCallback = JS(
  'function(row, data) {
    var num_data = data.slice(1,data.length)
    num_data.sort(function (a, b) {  return a - b;  });
    for(i=1;i < data.length; i++) {
    if(data[i]==num_data[num_data.length-1]) {
      $("td:eq("+i+")", row).css("background-color", "green")
    } else if(data[i]==num_data[0]) {
      $("td:eq("+i+")", row).css("background-color", "yellow")
    } else if(data[i]==num_data[num_data.length-2]) {
      $("td:eq("+i+")", row).css("background-color", "orange")
    }
    }
  }')))

@NicE 的回答很正确,很好。他提到需要从数据中过滤掉任何文本列,在我的用例中,我确实将文本与数字混合在一起,所以我想提供一个基于他的解决方案,但也增加了额外的步骤,以防有人以后需要它:

library(shiny)

ui <- fluidPage(
  actionButton("reset", "New data"),
  DT::DTOutput("table")
)

server <- function(input, output, session) {
  output$table <- DT::renderDT({
    input$reset

    df <- data.frame(
      a = sample(letters, 5),
      b = runif(5),
      c = runif(5),
      d = sample(letters, 5),
      e = runif(5),
      f = runif(5)
    )

    DT::datatable(df, options = list(rowCallback = DT::JS(
      'function(row, data) {
        function filterNumbersFromArray(arr) {
          arr = arr.filter((item) => {
            return (typeof item == "number")
          })
          return arr;
        }
        var num_data = filterNumbersFromArray(data);
        var row_max = Math.max.apply(Math, num_data);
        var row_min = Math.min.apply(Math, num_data);
        for(i = 1; i < data.length; i++) {
          if(data[i] == row_max) {
            $("td:eq(" + i + ")", row).css("background-color", "lightgreen");
          } else if(data[i] == row_min) {
            $("td:eq(" + i + ")", row).css("background-color", "lightblue");
          }
        }
      }')))
  })
}

shinyApp(ui, server)