Есть ли способ сделать цвета столбцов зависимыми от столбца?

Я хотел бы сделать цвета столбцов в столбце «Процентиль» зависящими от значения, а это означает, что если значение находится в диапазоне 100–90, оно имеет один цвет, если оно находится в диапазоне 90–80, то это другой цвет и так далее. ..

Это таблица, о которой идет речь:

player_percentiles %>%
    gt() %>%
    gt_plt_bar_pct(Percentile, labels = F, fill = "black") %>%
    cols_width(Percentile ~ px(200)) %>%
    opt_row_striping() %>%
    cols_align(align = "left", columns = Stat) %>%
    cols_align(align = "center", columns = Percentile) %>%
    opt_stylize(style = 1, color = "gray") %>%
    cols_label(perc_value = "") %>%
    tab_style(
        style = cell_text(color = "darkred"),
        locations = cells_body(columns = perc_value)
    )

И на всякий случай небольшой тиббл, использованный для таблицы:

# A tibble: 23 × 4
   Stat                         `Per 90` perc_value Percentile
   <fct>                        <chr>         <dbl>      <dbl>
 1 Passes Completed             64.3             94         94
 2 Passes Attempted             75.9             94         94
 3 Pass Completion %            84.1%            65         65
 4 Total Passing Distance       1177.9           96         96
 5 Progressive Passing Distance 345.5            95         95
 6 Progressive Distance %       29.4%            61         61
 7 Short Passes Completed       27.5             86         86
 8 Short Passes Attempted       30.2             86         86
 9 Short Pass Completion %      90.8%            73         73
10 Medium Passes Completed      26.9             94         94
# ℹ 13 more rows

И dput() тиббла

dput(player_percentiles)

structure(list(Stat = structure(1:23, levels = c("Passes Completed", 
"Passes Attempted", "Pass Completion %", "Total Passing Distance", 
"Progressive Passing Distance", "Progressive Distance %", "Short Passes Completed", 
"Short Passes Attempted", "Short Pass Completion %", "Medium Passes Completed", 
"Medium Passes Attempted", "Medium Pass Completion %", "Long Passes Completed", 
"Long Passes Attempted", "Long Pass Completion %", "Assists", 
"xAG: Exp. Assisted Goals", "xA: Expected Assists", "Key Passes", 
"Passes into Final 1/3", "Passes into Penalty Area", "Crosses into Penalty Area", 
"Progressive Passes"), class = "factor"), `Per 90` = c("64.3", 
"75.9", "84.1%", "1177.9", "345.5", "29.4%", "27.5", "30.2", 
"90.8%", "26.9", "30.2", "88.6%", "8.1", "11.3", "70.8%", "0.08", 
"0.13", "0.14", "1.32", "7.31", "1.78", "0.17", "8.64"), perc_value = c(94, 
94, 65, 96, 95, 61, 86, 86, 73, 94, 96, 68, 98, 95, 80, 46, 69, 
73, 66, 95, 92, 63, 96), Percentile = c(94, 94, 65, 96, 95, 61, 
86, 86, 73, 94, 96, 68, 98, 95, 80, 46, 69, 73, 66, 95, 92, 63, 
96)), row.names = c(NA, -23L), class = c("tbl_df", "tbl", "data.frame"
))

Любая помощь высоко ценится!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
67
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Предпочтительным решением было бы зарегистрировать обратный вызов отрисовки таблицы, но я не мог понять, как это сделать с помощью {gt}.

Вместо этого аналогичным подходом было бы позаимствовать ответ на этот вопрос и начать интервал, когда таблица генерируется с реактивным значением. Этот интервал выполняется каждые 10 мс, пока не будет найдена таблица tds, после чего он отменяется и переходит к установке цветов полос.

Блестящий пример кода

library(gt)
library(gtExtras)
library(shiny)
library(shinyjs)

player_percentiles <- structure(
  list(
    Stat = structure(
      1:23,
      levels = c(
        "Passes Completed",
        "Passes Attempted",
        "Pass Completion %",
        "Total Passing Distance",
        "Progressive Passing Distance",
        "Progressive Distance %",
        "Short Passes Completed",
        "Short Passes Attempted",
        "Short Pass Completion %",
        "Medium Passes Completed",
        "Medium Passes Attempted",
        "Medium Pass Completion %",
        "Long Passes Completed",
        "Long Passes Attempted",
        "Long Pass Completion %",
        "Assists",
        "xAG: Exp. Assisted Goals",
        "xA: Expected Assists",
        "Key Passes",
        "Passes into Final 1/3",
        "Passes into Penalty Area",
        "Crosses into Penalty Area",
        "Progressive Passes"
      ),
      class = "factor"
    ),
    `Per 90` = c(
      "64.3",
      "75.9",
      "84.1%",
      "1177.9",
      "345.5",
      "29.4%",
      "27.5",
      "30.2",
      "90.8%",
      "26.9",
      "30.2",
      "88.6%",
      "8.1",
      "11.3",
      "70.8%",
      "0.08",
      "0.13",
      "0.14",
      "1.32",
      "7.31",
      "1.78",
      "0.17",
      "8.64"
    ),
    perc_value = c(
      94,
      94,
      65,
      96,
      95,
      61,
      86,
      86,
      73,
      94,
      96,
      68,
      98,
      95,
      80,
      46,
      69,
      73,
      66,
      95,
      92,
      63,
      96
    ),
    Percentile = c(
      94,
      94,
      65,
      96,
      95,
      61,
      86,
      86,
      73,
      94,
      96,
      68,
      98,
      95,
      80,
      46,
      69,
      73,
      66,
      95,
      92,
      63,
      96
    )
  ),
  row.names = c(NA, -23L),
  class = c("tbl_df", "tbl", "data.frame")
)

ui <- fluidPage(useShinyjs(), gt_output("gtout"))

server <- function(input, output) {
  gtrv <- reactiveVal({
    player_percentiles %>%
      gt() %>%
      gt_plt_bar_pct(Percentile, labels = F, fill = "black") %>%
      cols_width(Percentile ~ px(200)) %>%
      opt_row_striping() %>%
      cols_align(align = "left", columns = Stat) %>%
      cols_align(align = "center", columns = Percentile) %>%
      opt_stylize(style = 1, color = "gray") %>%
      cols_label(perc_value = "") %>%
      tab_style(style = cell_text(color = "darkred"),
                locations = cells_body(columns = perc_value))
  })
  
  output$gtout <- render_gt({ gtrv() })
  
  observeEvent(gtrv, { 
    runjs('
var watch_for_gt_draw = setInterval(function() {
  var tds = $( "td[headers=\'Percentile\'] > div > div" );
  if (tds) {
    clearInterval(watch_for_gt_draw);
    
    tds.each(function() {
      var p = $( this ).width() / $( this ).parent().width();
      
      var b = "#FFFFFF";
      
      if (p <= 0.1) {
        b = "#000000";
      } else if (p <= 0.2) {
        b = "#1C0000";
      } else if (p <= 0.3) {
        b = "#380000";
      } else if (p <= 0.4) {
        b = "#550000";
      } else if (p <= 0.5) {
        b = "#710000";
      } else if (p <= 0.6) {
        b = "#8D0000";
      } else if (p <= 0.7) {
        b = "#AA0000";
      } else if (p <= 0.8) {
        b = "#C60000";
      } else if (p <= 0.9) {
        b = "#E20000";
      } else if (p <= 1.0) {
        b = "#FF0000";
      }
      
      $( this ).css("background", b);
    });
  } 
}, 10);
'
    )
  })
}

shinyApp(ui = ui, server = server)

Выход

Это отличное решение, но проблема в том, что я не планирую вязать в html.

Lucas Varela 24.06.2024 16:07

какой у вас целевой формат?

the-mad-statter 24.06.2024 16:09

Что ж, я планирую использовать таблицу в блестящем приложении, проблема в том, что я не знаю, как применить ваш js-код в этом контексте.

Lucas Varela 24.06.2024 17:40

@LucasVarela обновлен до блеска

the-mad-statter 24.06.2024 20:00

Вот лучший пример, в котором используется цветовой градиент и прослушивается событие вывода блестящего значения.

Содержимое javascript-color-gradient.js

// https://www.npmjs.com/package/javascript-color-gradient

class GradientColor {
  constructor(startColor = "", endColor = "", minNum = 0, maxNum = 10) {
    this.setColorGradient = (colorStart, colorEnd) => {
      startColor = getHexColor(colorStart);
      endColor = getHexColor(colorEnd);
    };

    this.setMidpoint = (minNumber, maxNumber) => {
      minNum = minNumber;
      maxNum = maxNumber;
    };

    this.getColor = (numberValue) => {
      if (numberValue) {
        return (
          "#" +
          generateHex(
            numberValue,
            startColor.substring(0, 2),
            endColor.substring(0, 2)
          ) +
          generateHex(
            numberValue,
            startColor.substring(2, 4),
            endColor.substring(2, 4)
          ) +
          generateHex(
            numberValue,
            startColor.substring(4, 6),
            endColor.substring(4, 6)
          )
        );
      }
    };

    const generateHex = (number, start, end) => {
      if (number < minNum) {
        number = minNum;
      } else if (number > maxNum) {
        number = maxNum;
      }

      const midPoint = maxNum - minNum;
      const startBase = parseInt(start, 16);
      const endBase = parseInt(end, 16);
      const average = (endBase - startBase) / midPoint;
      const finalBase = Math.round(average * (number - minNum) + startBase);
      const balancedFinalBase =
        finalBase < 16 ? "0" + finalBase.toString(16) : finalBase.toString(16);
      return balancedFinalBase;
    };

    const getHexColor = (color) => {
      return color.substring(color.length - 6, color.length);
    };
  }
}

class Gradient {
  constructor(
    colorGradients = "",
    maxNum = 10,
    colors = ["", ""],
    intervals = []
  ) {
    const setColorGradient = (gradientColors) => {
      if (gradientColors.length < 2) {
        throw new Error(
          `setColorGradient should have more than ${gradientColors.length} color`
        );
      } else {
        const increment = maxNum / (gradientColors.length - 1);
        const firstColorGradient = new GradientColor();
        const lower = 0;
        const upper = 0 + increment;
        firstColorGradient.setColorGradient(
          gradientColors[0],
          gradientColors[1]
        );
        firstColorGradient.setMidpoint(lower, upper);
        colorGradients = [firstColorGradient];
        intervals = [
          {
            lower,
            upper,
          },
        ];

        for (let i = 1; i < gradientColors.length - 1; i++) {
          const gradientColor = new GradientColor();
          const lower = 0 + increment * i;
          const upper = 0 + increment * (i + 1);
          gradientColor.setColorGradient(
            gradientColors[i],
            gradientColors[i + 1]
          );
          gradientColor.setMidpoint(lower, upper);
          colorGradients[i] = gradientColor;
          intervals[i] = {
            lower,
            upper,
          };
        }
        colors = gradientColors;
      }
    };

    this.setColorGradient = (...gradientColors) => {
      setColorGradient(gradientColors);
      return this;
    };

    this.getColors = () => {
      const gradientColorsArray = [];
      for (let j = 0; j < intervals.length; j++) {
        const interval = intervals[j];
        const start = interval.lower === 0 ? 1 : Math.ceil(interval.lower);
        const end =
          interval.upper === maxNum
            ? interval.upper + 1
            : Math.ceil(interval.upper);
        for (let i = start; i < end; i++) {
          gradientColorsArray.push(colorGradients[j].getColor(i));
        }
      }
      return gradientColorsArray;
    };

    this.getColor = (numberValue) => {
      if (isNaN(numberValue)) {
        throw new TypeError(`getColor should be a number`);
      } else if (numberValue <= 0) {
        throw new TypeError(`getColor should be greater than ${numberValue}`);
      } else {
        const toInsert = numberValue + 1;
        const segment = (maxNum - 0) / colorGradients.length;
        const index = Math.min(
          Math.floor((Math.max(numberValue, 0) - 0) / segment),
          colorGradients.length - 1
        );
        return colorGradients[index].getColor(toInsert);
      }
    };

    this.setMidpoint = (maxNumber) => {
      if (!isNaN(maxNumber) && maxNumber >= 0) {
        maxNum = maxNumber;
        setColorGradient(colors);
      } else if (maxNumber <= 0) {
        throw new RangeError(`midPoint should be greater than ${maxNumber}`);
      } else {
        throw new RangeError("midPoint should be a number");
      }
      return this;
    };
  }
}

Содержимое Shiny_value_listener.js

// listen to shiny output events
$(document).on('shiny:value', function(event) {  
  // for gt_out events
  if (event.target.id === 'gt_out') {
    const id = 'gt_out';
    
    // generate a 100 point color gradient
    const gradientArray = new Gradient()
      .setColorGradient("#000000", "00FF00")
      .setMidpoint(100);
    
    // temporarily place the freshly rendered table into the #gtout 
    // div to be replaced later with the modified version
    $( '#' + id ).html(event.value.html);
    
    // for each bar element
    $( "#" + id + " > div > table > tbody > tr > td[headers='x'] > div > div" )
      .each(function() {
        // calculate rendered width as a percent
        var p = $( this ).width() / $( this ).parent().width();
          
        // retrive the corresponding color
        var i = Math.round(100 * p);
        var b = gradientArray.getColor(i);
        
        // set bar color
        $( this ).css('background', b);
      });
    
    // copy modified table back to event data to allow 
    // default processing to use our modified table
    event.value.html = $( '#' + id ).html();
  }
});

Содержимое приложения.R

library(gtExtras)
library(shiny)

ui <- fluidPage(
  includeScript("javascript-color-gradient.js"),
  includeScript("shiny_value_listener.js"),
  gt_output("gt_out")
)

server <- function(input, output) {
  output$gt_out <- render_gt({
    data.frame(x = seq(10, 100, 10)) |> 
      gt() |>
      gt_plt_bar_pct(column = x, labels = TRUE)
  })
}

shinyApp(ui = ui, server = server)

Выход

Reprex files hosted with on GitHub

Простой подход полностью на базе R:

# make a colour scheme from black to red with 10 steps
black_to_red=colorRampPalette( c("black","red") )(10)

# set the plot area to make room for the long labels
par( plt=c(0.5,0.9,0.1,0.9) )

# plot a bar graph, specifying the colours for each bar
barplot( data$Percentile, col=black_to_red[ data$Percentile/10 ],
    names.arg=data$Stat, las=2, horiz=T)

Другие вопросы по теме

Автоматическая нумерация таблиц с помощью пакета gt в R при рендеринге Quarto в документ Word
В R при представлении данных (RCT) в таблице (желательно gtsummary) как включить тесты значимости как внутри группы, так и между группами? [картинка]
Как я могу разместить в центре страницы PDF таблицу GT в R из Rmarkdown?
Использование списка таблиц и списка рисунков в Quarto для ссылки на вывод фрагментов кода
Gt() из gt по-разному раскрашивает ячейки с одинаковым содержимым?
Добавьте второй `groupname_col` в таблицу `gt` без объединения значений столбца
Есть ли функция R для связывания двух таблиц рядом друг с другом, но имеющих заголовок над всем этим?
Как включить имена столбцов и метки столбцов для наборов данных sas в верхнюю часть таблицы в таблицах gt в r?
Gt: Можно ли программно исключить строки из итоговых функций?
Добавьте изображения таблицы gt и ggplot в электронную почту