Sam Hoppen
Sam Hoppen

Reputation: 365

Row-wise conditional formatting in Shiny Reactable table

I have the following dataframe that I am importing into a Shiny app:

final_odds <- structure(list(player_prop = c("Aaron Jones: Rush + Rec Yards", 
                                             "Aaron Jones: Rush + Rec Yards", "Aaron Rodgers: Interceptions", 
                                             "Aaron Rodgers: Interceptions", "Aaron Rodgers: Pass TDs", "Aaron Rodgers: Pass TDs", 
                                             "Aaron Rodgers: Pass Yards", "Aaron Rodgers: Pass Yards", "Adam Thielen: Rec Yards", 
                                             "Adam Thielen: Rec Yards"), Side = c("Over", "Under", "Over", 
                                                                                  "Under", "Over", "Under", "Over", "Under", "Over", "Under"), 
                             DraftKings = c("1300.5 (-115)", "1300.5 (-115)", "7.5 (115)", 
                                            "7.5 (-140)", "31.5 (-120)", "31.5 (100)", "4050.5 (-110)", 
                                            "4050.5 (-110)", "750.5 (-110)", "750.5 (-120)"), BetMGM = c("-", 
                                                                                                         "-", "-", "-", "-", "-", "-", "-", "699.5 (-125)", "699.5 (-105)"
                                            ), FanDuel = c("-", "-", "-", "-", "30.5 (-112)", "30.5 (-112)", 
                                                           "3950.5 (-112)", "3950.5 (-112)", "750.5 (-112)", "750.5 (-112)"
                                            ), Caesars = c("-", "-", "-", "-", "30.5 (-115)", "30.5 (-115)", 
                                                           "4000.5 (-115)", "4000.5 (-115)", "775.5 (-115)", "775.5 (-115)"
                                            )), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"
                                            ))

What I want to do is make it so that the lowest value (of the numbers not in parentheses) in columns 3-6 is highlighted in green for each row of data. The complications here are two-fold: 1) there isn't always a value in each of the columns, and 2) I just need to evaluate the numbers outside of the parentheses.

Here is a basic outline of the Shiny app as it stands:

library(shiny)
library(tidyverse)
library(dplyr)
library(reactable)


final_odds <- structure(list(player_prop = c("Aaron Jones: Rush + Rec Yards", 
                                             "Aaron Jones: Rush + Rec Yards", "Aaron Rodgers: Interceptions", 
                                             "Aaron Rodgers: Interceptions", "Aaron Rodgers: Pass TDs", "Aaron Rodgers: Pass TDs", 
                                             "Aaron Rodgers: Pass Yards", "Aaron Rodgers: Pass Yards", "Adam Thielen: Rec Yards", 
                                             "Adam Thielen: Rec Yards"), Side = c("Over", "Under", "Over", 
                                                                                  "Under", "Over", "Under", "Over", "Under", "Over", "Under"), 
                             DraftKings = c("1300.5 (-115)", "1300.5 (-115)", "7.5 (115)", 
                                            "7.5 (-140)", "31.5 (-120)", "31.5 (100)", "4050.5 (-110)", 
                                            "4050.5 (-110)", "750.5 (-110)", "750.5 (-120)"), BetMGM = c("-", 
                                                                                                         "-", "-", "-", "-", "-", "-", "-", "699.5 (-125)", "699.5 (-105)"
                                            ), FanDuel = c("-", "-", "-", "-", "30.5 (-112)", "30.5 (-112)", 
                                                           "3950.5 (-112)", "3950.5 (-112)", "750.5 (-112)", "750.5 (-112)"
                                            ), Caesars = c("-", "-", "-", "-", "30.5 (-115)", "30.5 (-115)", 
                                                           "4000.5 (-115)", "4000.5 (-115)", "775.5 (-115)", "775.5 (-115)"
                                            )), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"
                                            ))



ui <- fluidPage(
  reactableOutput("odds_table")
)

server <- function(input, output) {

    output$odds_table <- renderReactable({
    reactable(final_odds)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

Upvotes: 1

Views: 1246

Answers (1)

akrun
akrun

Reputation: 887911

We may get the index of the min values from columns 3 to 6 by looping across those columns, get the number outside the () with parse_number, find the min and get the index with which

library(dplyr)
library(reactable)
final_odds1 <-  final_odds %>%
          summarise(across(3:6, ~ {
              x1 <- readr::parse_number(.x)
              which(x1 %in% min(x1, na.rm = TRUE))
          }))

Now, we create the reactable object by specifying the columns and loop over the row index in style to highlight the rows where they are minimum by comparing with the index stored data from 'final_odds1'

reactable(final_odds, columns = list(
  DraftKings = colDef(
    style = function(value, index) {
      if(index %in% final_odds1$DraftKings) {
        color <- "green"
      } else {
        color <- "black"
      }
      list(color = color)
        
      
      
    }
  ),
  BetMGM = colDef(
    style = function(value, index) {
      if(index %in% final_odds1$BetMGM) {
        color <- "green"
      } else {
        color <- "black"
      }
      list(color = color)
      
      
      
    }
  ),
  FanDuel = colDef(
    style = function(value, index) {
      if(index %in% final_odds1$FanDuel) {
        color <- "green"
      } else {
        color <- "black"
      }
      list(color = color)
      
      
      
    }
  ), 
  
  
  Caesars = colDef(
    style = function(value, index) {
      if(index %in% final_odds1$Caesars) {
        color <- "green"
      } else {
        color <- "black"
      }
      list(color = color)
      
      
      
    }
  )
  
  
))

-output

enter image description here


If there are many columns, create a function wrapper and then loop over the names with map or imap

library(purrr)
final_odds1 <-  final_odds %>%
          summarise(across(3:6, ~ {
              x1 <- readr::parse_number(.x)
              which(x1 %in% min(x1, na.rm = TRUE))
          }))


style_fun <- function(colnm) {
        colDef(
          style =  function(value, index) {
            if(index %in% final_odds1[[colnm]]) {
              color <- "green"
            } else {
              color <- "black"
            }
            list(color = color)
            
            
            
          }
        )
  
  
  
}
reactable(final_odds, 
 columns = setNames(map(names(final_odds)[3:6], 
      ~ style_fun(.x)), names(final_odds[3:6])))

If it is grouped by 'player_prop'

library(stringr)
final_odds1 <-  final_odds %>%
          mutate(rn = row_number()) %>%
          group_by(player_prop) %>%
          mutate(across(DraftKings:Caesars, ~ {
              x1 <- as.numeric(na_if(str_remove(.x, "\\s*\\(.*"), "-"))
              
              list(if(all(is.na(x1))) NA_integer_ else 
      rn[x1 %in% min(x1, na.rm = TRUE)])
              
          })) %>%
        ungroup


style_fun <- function(colnm) {
        colDef(
          style =  function(value, index) {
            if(index %in% final_odds1[[colnm]][[index]]) {
              color <- "green"
            } else {
              color <- "black"
            }
            list(color = color)
            
            
            
          }
        )
  
  
  
}
reactable(final_odds, columns = setNames(map(names(final_odds)[3:6],
      ~ style_fun(.x)), names(final_odds[3:6])))

Upvotes: 3

Related Questions