Lucas Habegger
Lucas Habegger

Reputation: 1

R Shiny Performance issues - generating action buttons in datatable for long dataframe

I have a moderatly large data.frame (5000 - 10000) in my Shiny app that should be rendered dynamically as a datatable, together with dynamically created action buttons.

I used the pattern from this stackoverflow post to create the buttons: R Shiny: Handle Action Buttons in Data Table

This works perfectly, however, with larger data.frames i ran into performance issues. Converting the shiny.tags to character (either with as.character() or with htmltools::renderTags()) makes things very slow. I ran a small microbenchmark on this:

library(microbenchmark)
library(shiny)
library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

create_info_btns_mapChr <- function(FUN, len, id, ...) {
  args <- rlang::list2(...)
  list <-
    seq_len(len) %>% purrr::map(.f = FUN, inputId = paste0(id), ...)
  chr <- list %>% purrr::map( ~ as.character(.x))
}

create_info_btns_map <- function(FUN, len, id, ...) {
  args <- rlang::list2(...)
  list <-
    seq_len(len) %>% purrr::map(.f = FUN, inputId = paste0(id), ...)
}

df <- diamonds[1:10000, ]

mbm <- microbenchmark::microbenchmark(tags = {
  create_info_btns_map(
    actionButton,
    nrow(df),
    id = "test_",
    label = icon("info-circle"),
    icon = NULL)
  
},
html = {
  out <- create_info_btns_mapChr(
    actionButton,
    nrow(df),
    id = "test_",
    label = icon("info-circle"),
    icon = NULL)
},
times = 2)

mbm
#> Unit: seconds
#>  expr       min        lq      mean    median        uq       max neval cld
#>  tags  1.411491  1.411491  1.424619  1.424619  1.437746  1.437746     2  a 
#>  html 15.852125 15.852125 15.951569 15.951569 16.051013 16.051013     2   b
autoplot(mbm)
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Created on 2022-10-07 by the reprex package (v2.0.1)

Output image can be found here (cant include it directly yet) "https://i.imgur.com/eHhwbEo.png"

I have not found good entry points for optimization here. Maybe not adding all the buttons at once, but only for the visible records - but I would not know how to tackle that.. Any suggestions?

Best

Upvotes: 0

Views: 135

Answers (1)

langtang
langtang

Reputation: 24845

Use a helper function to generate the character tag directly. In addition, in the example below, I convert the frame (df) to data.table, using setDT(), and then I add the btn column inplace using :=

library(data.table)
df <- diamonds[1:10000, ]
setDT(df)


make_button <- function(id) {
  paste0('<button id="',id,
         '" type="button" class="btn btn-default action-button">',
         id,'_btn</button>')
}

# This takes about 1 millisecond on my machine
df[,id:=.I][, btn:=make_button(id)]  

Output (head(df)):

   carat       cut color clarity depth table price     x     y     z    id
   <num>     <ord> <ord>   <ord> <num> <num> <int> <num> <num> <num> <int>
1:  0.23     Ideal     E     SI2  61.5    55   326  3.95  3.98  2.43     1
2:  0.21   Premium     E     SI1  59.8    61   326  3.89  3.84  2.31     2
3:  0.23      Good     E     VS1  56.9    65   327  4.05  4.07  2.31     3
4:  0.29   Premium     I     VS2  62.4    58   334  4.20  4.23  2.63     4
5:  0.31      Good     J     SI2  63.3    58   335  4.34  4.35  2.75     5
6:  0.24 Very Good     J    VVS2  62.8    57   336  3.94  3.96  2.48     6
                                                                                 btn
                                                                              <char>
1: <button id="1" type="button" class="btn btn-default action-button">1_btn</button>
2: <button id="2" type="button" class="btn btn-default action-button">2_btn</button>
3: <button id="3" type="button" class="btn btn-default action-button">3_btn</button>
4: <button id="4" type="button" class="btn btn-default action-button">4_btn</button>
5: <button id="5" type="button" class="btn btn-default action-button">5_btn</button>
6: <button id="6" type="button" class="btn btn-default action-button">6_btn</button>

Adjust your make_btn function above to fit your needs.

Upvotes: 1

Related Questions