How to Have Conditional Formatting of Data Frames in R Shiny

How to have conditional formatting of data frames in R Shiny?

You can conditionnal formatting your table using jQuery.

For example :

library(shiny)
library(datasets)

script <- "$('tbody tr td:nth-child(5)').each(function() {

var cellValue = $(this).text();

if (cellValue > 50) {
$(this).css('background-color', '#0c0');
}
else if (cellValue <= 50) {
$(this).css('background-color', '#f00');
}
})"

runApp(list(
ui = basicPage(
tags$head(tags$script(HTML('Shiny.addCustomMessageHandler("jsCode", function(message) { eval(message.value); });'))),
tableOutput("view")
),
server = function(input, output, session) {

session$onFlushed(function() {
session$sendCustomMessage(type='jsCode', list(value = script))
})

output$view <- renderTable({
head(rock, n = 20)
})
}
))

In tbody tr td:nth-child(5) I precise nth-child(5) To loop on each td of the 5th column only (perms).

We need session$onFlushed(function() {
session$sendCustomMessage(type='jsCode', list(value = script))
})
because if you put the script in the head, it will be executed before the table output rendered and then nothing will be formatting.

If you want more formatting I suggest you to create css classes and use addClass :

### In the UI :
tags$head(tags$style(
".greenCell {
background-color: #0c0;
}

.redCell {
background-color: #f00;
}"))

### In th script
### use .addClass instead of .css(...)

$(this).addClass('greenCell')

How can I style format some rows in a data frame depending on the input of a checkbox in a shiny app?

Here is a solution which only partially works. I don't understand the issue. (Edit: issue solved, see at the end)

Firstly, I have removed your file upload, in order not to have to upload a file. This has nothing to do with the issue. I call the dataframe DF.

The issue is here: in the code below, I do renderDT(DT, ....... This works, as you can see. But when I do renderDT(filtered_df(), ....), this doesn't work, and I don't understand why.

DF <- df <- data.frame(Consequence = c(rep("x",4),rep("y",4),rep("z",4)),
CANONICAL = rep(c("YES","NO"),6),
x_LOT = c(rep("False", 3), rep("True", 5), rep("False",2), "True","False"),
y_LOT = c(rep("False", 8), rep("True",2), rep("False",2)),
x3=c(12,43,64,34,93,16,32,74,84,89,45,67))
#write.csv(df, "df.csv")

# MY APP
library(shiny)
library(DT) # for data tables
library(dplyr)
library(shinyWidgets)
# library(lazyeval)
# library(data.table)

callback <- function(rows){
c(
sprintf("var rows = [%s];", toString(rows)),
"$('#LOT').on('click', function(){",
" if($(this).prop('checked')){",
" for(var i=0; i<rows.length; ++i){",
" var row = table.row(rows[i]);",
" row.node().style.backgroundColor = 'yellow';",
" }",
" }else{",
" for(var i=0; i<rows.length; ++i){",
" var row = table.row(rows[i]);",
" row.node().style.backgroundColor = '';",
" }",
" }",
"})"
)
}

ui <- function(request) {
fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Upload your File",multiple = FALSE,
accept = c("text/csv", "text/comma-separated-values,text/plain",".csv")),
pickerInput("Consequence", "Consequence:", choices = NULL, options = list(`actions-box` = TRUE),
selected = NULL, multiple = TRUE ),
prettyCheckbox(inputId = "CANONICAL", label = "CANONICAL", value = FALSE,
outline = TRUE, fill = TRUE, bigger = TRUE, status = 'success',width = NULL),
prettyCheckbox(inputId="LOT", label = "LOT", value = FALSE,
outline= TRUE, fill = TRUE, status = 'success', width = NULL)),

mainPanel(
DTOutput("contents")
)))}

server <- function(input, output, session) {

df <- reactive({
# req(input$file1)
# df <- read.csv(input$file1$datapath)
DF
})

yellowRows <- reactive({
req(df())
which(df()$x_LOT == "True" | df()$y_LOT == "True") - 1L
})

observeEvent(df(), {
req(df())
updatePickerInput(session, inputId = "Consequence",
choices = levels(df()$Consequence),
selected = levels(df()$Consequence))
})

filtered_df <- reactive({
df() %>%
filter( Consequence %in% input$Consequence ) %>%
filter(if (input$CANONICAL == TRUE) CANONICAL == "YES" else !is.na(CANONICAL))
})

output$contents <- renderDT({
req(filtered_df())
datatable(
DF,
class = "display nowrap compact",
filter = "top",
callback = JS(callback(yellowRows())),
options = list(
pageLength = 12)
)},
server = FALSE
)

}

shinyApp(ui, server)

Sample Image

EDIT: issue solved

Just replace yellowRows with:

  yellowRows <- reactive({
req(filtered_DAT())
which(filtered_DAT()$x_LOT == "True" | filtered_DAT()$y_LOT == "True") - 1L
})

output$contents <- renderDT({
req(filtered_DAT())
datatable(
filtered_DAT(),
class = "display nowrap compact",
filter = "top",
callback = JS(callback(yellowRows())),
options = list(
pageLength = 12)
)},
server = FALSE
)

EDIT: version which works with several pages

DF <- df <- data.frame(Consequence = c(rep("x",4),rep("y",4),rep("z",4)),
CANONICAL = rep(c("YES","NO"),6),
x_LOT = c(rep("False", 3), rep("True", 5), rep("False",2), "True","False"),
y_LOT = c(rep("False", 8), rep("True",2), rep("False",2)),
x3=c(12,43,64,34,93,16,32,74,84,89,45,67))
#write.csv(df, "df.csv")

# MY APP
library(shiny)
library(DT) # for data tables
library(dplyr)
library(shinyWidgets)
# library(lazyeval)
# library(data.table)

callback <- function(rows){
c(
sprintf("var rows = [%s];", toString(rows)),
"$('#LOT').on('click', function(){",
" for(var i=0; i<rows.length; ++i){",
" var row = table.row(rows[i]);",
" if(row.length){",
" row.node().style.backgroundColor = ",
" $(this).prop('checked') ? 'yellow' : '';",
" }",
" }",
"})"
)
}

ui <- function(request) {
fluidPage(
sidebarLayout(
sidebarPanel(
fileInput("file1", "Upload your File",multiple = FALSE,
accept = c("text/csv", "text/comma-separated-values,text/plain",".csv")),
pickerInput("Consequence", "Consequence:", choices = NULL, options = list(`actions-box` = TRUE),
selected = NULL, multiple = TRUE ),
prettyCheckbox(inputId = "CANONICAL", label = "CANONICAL", value = FALSE,
outline = TRUE, fill = TRUE, bigger = TRUE, status = 'success',width = NULL),
prettyCheckbox(inputId="LOT", label = "LOT", value = FALSE,
outline= TRUE, fill = TRUE, status = 'success', width = NULL)),

mainPanel(
DTOutput("contents")
)))}

server <- function(input, output, session) {

df <- reactive({
# req(input$file1)
# df <- read.csv(input$file1$datapath)
DF
})

observeEvent(df(), {
req(df())
updatePickerInput(session, inputId = "Consequence",
choices = levels(df()$Consequence),
selected = levels(df()$Consequence))
})

filtered_df <- reactive({
df() %>%
filter( Consequence %in% input$Consequence ) %>%
filter(if (input$CANONICAL == TRUE) CANONICAL == "YES" else !is.na(CANONICAL))
})

yellowRows <- reactive({
req(filtered_df())
which(filtered_df()$x_LOT == "True" | filtered_df()$y_LOT == "True") - 1L
})

output$contents <- renderDT({
req(filtered_df())
datatable(
filtered_df(),
class = "display nowrap compact",
filter = "top",
callback = JS(callback(yellowRows())),
options = list(
pageLength = 6)
)},
server = FALSE
)
}

shinyApp(ui, server)

Shiny conditional variable-columns formatting with formatStyle

Consider changing it to datatable

server <- function(input, output) {

selected <- reactive({
input$select
})

col_names <- c('col1', 'col2', 'col3', 'col4')

output$table <- DT::renderDT(

{dat <- df[[selected()]]
nm1 <- intersect(names(dat), col_names)

DT::datatable(dat, options = list(pageLength = 15)) %>%
formatStyle(nm1, backgroundColor = 'yellow')
}

)

}

shinyApp(ui=ui,server=server)

-output

Sample Image

Conditional formatting in DT Data Table R Shiny

Here is my solution to your problem:

library(shinydashboard)
library(DT)
library(magrittr)

entity <- c('entity1', 'entity2', 'entity3')
value1 <- c(21000, 23400, 26800)
value2 <- c(21234, 23445, 26834)
value3 <- c(21123, 234789, 26811)
value4 <- c(27000, 23400, 26811)
entity.data <- data.frame(entity, value1, value2, value3, value4)

# Create a vector of max values
max_val <- apply(entity.data[, -1], 1, max)

header <- dashboardHeader()
sidebar <- dashboardSidebar()
body <- dashboardBody(DT::dataTableOutput("entity.dataTable"))

shinyApp(
ui = dashboardPage(header, sidebar, body),
server = function(input, output) {
output$entity.dataTable <- renderDataTable({
DT::datatable(
entity.data,
selection = "single",
filter = 'bottom',
extensions = c('Buttons', 'ColReorder', 'FixedHeader', 'Scroller'),
rownames = FALSE,
options = list(
dom = 'Bfrtip',
searching = T,
pageLength = 25,
searchHighlight = TRUE,
colReorder = TRUE,
fixedHeader = TRUE,
filter = 'top',
buttons = c('copy', 'csv', 'excel', 'print'),
paging = TRUE,
deferRender = TRUE,
scroller = TRUE,
scrollX = TRUE,
scrollY = 550
)
) %>% # Style cells with max_val vector
formatStyle(
columns = 2:5,
backgroundColor = styleEqual(levels = max_val, values = rep("yellow", length(max_val)))
)
})
}
)

So what you need to do is to create a vector of max values. Then use it in the helper function styleEqual() inside formatStyle() as shown in the code above.

maintaining gt conditional color formatting in shiny selection

I figured out that the simple way to achieve this goal is to set the domain to equal the min and max values of the column you are attempting to conditionally format.

library(tidyverse)
library(shiny)
library(gt)
library(scales)

### create data
dat <- tribble(~player, ~fg_pct,
"A", 0.43,
"B", 0.427,
"C", 0.475,
"D", 0.36,
"E", 0.4,
"F", 0.382,
"G", 0.48,
"H", 0.291,
"I", 0.45)

# user interface
u <- fluidPage(

selectInput("player",
label = "Player:",
choices = unique(dat$player),
selected = "A",
multiple = TRUE),

gt_output(outputId = "tbl")
)

# server
s <- function(input, output){

tbl_df <- reactive({
dat %>%
filter(player %in% input$player)
})


output$tbl <- render_gt({

tbl_df() %>%
arrange(desc(fg_pct)) %>%
gt() %>%
data_color(
vars(fg_pct),
apply = "fill",
colors = col_numeric(palette = c("red","white","green"), domain = c(min(dat$fg_pct), max(dat$fg_pct)
)))
})

}

# run app
shinyApp(u, s)

Alternatively, if you'd like to do this in DT instead of gt you can do it like this (just need to preset the breaks and colors).

# preset breaks and coloring
brks <- as.vector(quantile(dat$fg_pct, probs = seq(0, 1, 0.1)))
ramp <- colorRampPalette(c("red", "green"))
clrs <- ramp(length(brks) + 1)

u <- fluidPage(

selectInput("player",
label = "Player:",
choices = unique(dat$player),
selected = "A",
multiple = TRUE),

DTOutput(outputId = "tbl")
)

# server
s <- function(input, output){

tbl_df <- reactive({
dat %>%
filter(player %in% input$player)
})


output$tbl <- renderDT({

tbl_df() %>%
arrange(desc(fg_pct)) %>%
datatable() %>%
formatStyle(columns = "fg_pct",
background = styleInterval(
cuts = brks,
values = clrs))

})

}

# run app
shinyApp(u, s)

R shiny renderTable - conditional formatting

Another option for you using two for loops to look through the table and style the relevant cells with a green background in html

if (interactive()) {
library(DT)

fruit <- c("Apple", "Orange", "Pear", "Banana")
num <- c(54, 25, 51, 32)
Oct2020 <- c(10, 15, 20, 25)
Nov2020 <- c(5, 7, 10, 15)
Dec2020 <- c(7, 9, 12, 17)
Jan2021 <- c(6, 9, 2, 0)
Feb2021 <- c(15, 30, 12, 2)
Mar2021 <- c(6, 7, 8, 10)

data <- data.frame(fruit, num, Oct2020, Nov2020, Dec2020, Jan2021, Feb2021, Mar2021)

ui <- fluidPage(
fluidRow(
column(width = 1, numericInput("numFruit", "Number of Fruit", value = 10)),
column(width = 1, div(style = "margin-top: 25px", actionButton("btnUpdate", "Update")))
),

fluidRow(
tableOutput("dt_Fruit")
)
)

server <- function(input, output, session) {

values <- reactiveValues(data = data, data2 = data)

observeEvent(input$btnUpdate, {

data2 <- values$data
num_lim <- input$numFruit
for (r in 1:nrow(data)){
for (c in 3:ncol(data)){
if(data[r,c] > num_lim){
data2[r,c] <- paste0('<div style="background-color: green;"><span>', data[r,c], '</span></div>')
}
}
}
values$data2 <- data2

})
output$dt_Fruit <- renderTable({values$data2 }, sanitize.text.function = function(x) x)
}
shinyApp(ui, server)
}



Related Topics



Leave a reply



Submit