Я хотел бы сделать цвета столбцов в столбце «Процентиль» зависящими от значения, а это означает, что если значение находится в диапазоне 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"
))
Любая помощь высоко ценится!
Предпочтительным решением было бы зарегистрировать обратный вызов отрисовки таблицы, но я не мог понять, как это сделать с помощью {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)
какой у вас целевой формат?
Что ж, я планирую использовать таблицу в блестящем приложении, проблема в том, что я не знаю, как применить ваш js-код в этом контексте.
@LucasVarela обновлен до блеска
Вот лучший пример, в котором используется цветовой градиент и прослушивается событие вывода блестящего значения.
// 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;
};
}
}
// 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();
}
});
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)
Это отличное решение, но проблема в том, что я не планирую вязать в html.