Math mode in shiny table - r

Using withMathJax, I would like to render a table with rownames with some math expressions. Here is a basic example:
library(shiny)
ui <- fluidPage(
titlePanel("Hello Shiny!"),
mainPanel(withMathJax(),
tableOutput(outputId = "table"))
)
server <- function(input, output) {
output$table <- renderTable({
x <- rnorm(2)
y <- rnorm(2, 1)
tab <- data.frame(x = x, y = y)
withMathJax()
rownames(tab) <- c("\\(\\alpha\\)",
"\\(\\beta\\)")
tab
},
include.rownames = T,
include.colnames = T)
}
shinyApp(ui, server)
This unfortunately does not work. I also tried:
rownames(tab) <- c(withMathJax("\\(\\alpha\\)"),
withMathJax("\\(\\beta\\)"))
and
rownames(tab) <- c(paste(withMathJax("\\(\\alpha\\)")),
paste(withMathJax("\\(\\beta\\)")))
but without any success. In latter case I got alpha and beta correctly rendered, however with also
<script>if (window.MathJax) MathJax.Hub.Queue(["Typeset", MathJax.Hub]);</script>
EDIT:
The approach should preferably work even in case when table is re-rendered. Using suggestion by #Stéphane Laurent, I updated the code:
library(shiny)
ui <- fluidPage(
titlePanel("Hello Shiny!"),
mainPanel(
numericInput("mean", label = "mean", value = 1),
withMathJax(tableOutput("table"))
)
)
server <- function(input, output) {
output$table <- renderTable({
x <- rnorm(2)
y <- rnorm(2, input$mean)
tab <- data.frame(x = x, y = y)
rownames(tab) <- c("\\(\\alpha\\)",
"\\(\\beta\\)")
tab
},
include.rownames = TRUE,
include.colnames = TRUE)
}
shinyApp(ui, server)

You can use xtable to generate a LaTeX table:
library(shiny)
library(xtable)
ui <- fluidPage(
titlePanel("Hello Shiny!"),
mainPanel(
uiOutput("table")
)
)
server <- function(input, output) {
output$table <- renderUI({
x <- rnorm(2)
y <- rnorm(2, 1)
tab <- data.frame(x = x, y = y)
rownames(tab) <- c("\\alpha",
"\\beta")
LaTeXtab <- print(xtable(tab, align=rep("c", ncol(tab)+1)),
floating=FALSE, tabular.environment="array", comment=FALSE,
print.results=FALSE,
sanitize.rownames.function = function(x) x)
tagList(
withMathJax(),
HTML(paste0("$$", LaTeXtab, "$$"))
)
})
}
shinyApp(ui, server)
If you don't want to use xtable, you can do:
library(shiny)
ui <- fluidPage(
titlePanel("Hello Shiny!"),
mainPanel(
withMathJax(tableOutput("table"))
)
)
server <- function(input, output) {
output$table <- renderTable({
x <- rnorm(2)
y <- rnorm(2, 1)
tab <- data.frame(x = x, y = y)
rownames(tab) <- c("\\(\\alpha\\)",
"\\(\\beta\\)")
tab
},
include.rownames = TRUE,
include.colnames = TRUE)
}
shinyApp(ui, server)
EDIT
As noted by the OP, this doesn't work when the table is re-rendered. Here is a working solution:
ui <- fluidPage(
titlePanel("Hello Shiny!"),
mainPanel(
numericInput("mean", label = "mean", value = 1),
uiOutput("tableUI")
)
)
server <- function(input, output) {
output$table <- renderTable({
x <- rnorm(2)
y <- rnorm(2, input$mean)
tab <- data.frame(x = x, y = y)
rownames(tab) <- c("\\(\\alpha\\)",
"\\(\\beta\\)")
tab
},
include.rownames = TRUE,
include.colnames = TRUE)
output$tableUI <- renderUI({
input$mean # in order to re-render when input$mean changes
tagList(
withMathJax(),
withMathJax(tableOutput("table"))
)
})
}
EDIT 2
The previous solution works but there are some jumps, and it is not convenient because it requires to include the reactive dependencies in the renderUI. Below is a solution which uses katex instead of MathJax. No jumps, and no renderUI.
library(shiny)
js <- "
$(document).on('shiny:value', function(event) {
if(event.name === 'table'){
var matches = event.value.match(/(%%+[^%]+%%)/g);
var newvalue = event.value;
for(var i=0; i<matches.length; i++){
var code = '\\\\' + matches[i].slice(2,-2);
newvalue = newvalue.replace(matches[i], katex.renderToString(code));
}
event.value = newvalue;
}
})
"
ui <- fluidPage(
tags$head(
tags$link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/katex#0.10.0-beta/dist/katex.min.css", integrity="sha384-9tPv11A+glH/on/wEu99NVwDPwkMQESOocs/ZGXPoIiLE8MU/qkqUcZ3zzL+6DuH", crossorigin="anonymous"),
tags$script(src="https://cdn.jsdelivr.net/npm/katex#0.10.0-beta/dist/katex.min.js", integrity="sha384-U8Vrjwb8fuHMt6ewaCy8uqeUXv4oitYACKdB0VziCerzt011iQ/0TqlSlv8MReCm", crossorigin="anonymous"),
tags$script(HTML(js))
),
titlePanel("Hello Shiny!"),
mainPanel(
numericInput("mean", "Enter mean", value = 1),
tableOutput("table")
)
)
server <- function(input, output) {
output$table <- renderTable({
x <- rnorm(2)
y <- rnorm(2, input$mean)
tab <- data.frame(x = x, y = y, z = c("hello", "%%gamma%%%%delta%%"))
rownames(tab) <- c("%%alpha%%", "%%beta%%")
tab
}, rownames = TRUE)
}
shinyApp(ui, server)
Every occurrence like %%string%% is replaced by \\string and then rendered in math.

Related

`x` must contain exactly 1 expression, not 2 error in r shiny

I want to create a r shiny app in which for each y variable one tab is produced (so tabs must be dynamically generated) and in each tab the plots are in two columns and n rows (as in the figure). However, I received the following error:
`x` must contain exactly 1 expression, not 2
In fact, I want to combine the two codes to create what I described above:
Code 1: This code creates one tab for every y variable
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "x", label = "X var", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "y", label = "Y var", choices = names(mtcars), selected = names(mtcars)[1], multiple = T)
),
mainPanel(
uiOutput("plots")
)
)
)
server <- function(input, output, session) {
output$plots <- renderUI({
plt_list <- list()
plt_list <- lapply(input$y, function(y){
renderPlot({
ggplot(mtcars, aes_string(input$x, y)) + geom_point()
})
})
names(plt_list) <- input$y
do.call(tabsetPanel, c(id='tab',lapply(input$y, function(y) {
tabPanel(
title=paste0('tab ', y),
fluidRow(column(6, plt_list[[y]]))
)
})))
})
}
shinyApp(ui, server)
Code 2: This code creates multiple plots of y vs. different x variables, and the plots are arranged in a way so that we have two columns and n rows.
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1], multiple = F),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
})
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plt_list[1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plt_list[x]))
)
}
return(plottoUI)
})
}
shinyApp(ui, server)
Code 3: The combination of the codes above that results in the error:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectizeInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1:2], multiple = T),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1:3],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
output$allplots <- renderUI({
plt_list <- list()
plots <- lapply(input$y, function(y){
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = input$y)) + geom_point()
})
})
})
names(plots) <- input$y
plotarrange <- lapply(input$y, function(y){
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plots[[y]][1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plots[[y]][x]))
)
}
return(plottoUI)
})
names(plotarrange) <- input$y
do.call(tabsetPanel, c(id='tab',lapply(input$y, function(y) {
tabPanel(
title=paste0('tab ', y),
plotarrange[[y]]
)
})))
})
}
shinyApp(ui, server)
What is the source of the error and how do I resolve it ?
I found the source of the error in code 3, input$y should have been changes to y as follows
plots <- lapply(input$y, function(y){
plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = y)) + geom_point()
})
})
})
Therefore:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(selectizeInput(inputId = "y", label = "Y", choices = names(mtcars), selected = names(mtcars)[1:2], multiple = T),
selectizeInput(inputId = "xvars", label = "X", choices = names(mtcars), selected = names(mtcars)[1:3],
multiple = T)),
mainPanel(uiOutput("allplots"))
)
)
server <- function(input, output, session) {
rval <- reactiveValues(
plt_list = NULL
)
output$allplots <- renderUI({
plt_list <- list()
plots <- lapply(input$y, function(y){
rval$plt_list <- lapply(input$xvars, function(x){
renderPlotly({
mtcars %>% ggplot(aes_string(x = x, y = y)) + geom_point()
})
})
})
names(plots) <- input$y
plotarrange <- lapply(input$y, function(y){
if (length(input$xvars) == 1) {
plottoUI <- fluidRow(column(12, plots[[y]][1]))
} else {
plottoUI <- fluidRow(
lapply(1:length(input$xvars), function(x) column(6, plots[[y]][x]))
)
}
return(plottoUI)
})
names(plotarrange) <- input$y
do.call(tabsetPanel, c(id='tab',lapply(input$y, function(y) {
tabPanel(
title=paste0('tab ', y),
plotarrange[[y]]
)
})))
})
}
shinyApp(ui, server)

How to add rows to R Shiny table

I am trying to build a form with R Shiny which will be used to populate a table once the action button at the end of the form is clicked. What I have not been able to figure out is how to pick up the data in the form and add it to a new row in the table. Right now, it just keeps updating the first row with whatever is in the form. I have reproduced a simple version of the code here:
#ui.r
library(shiny)
shinyUI(fluidPage(
# Application title
titlePanel("Test App"),
sidebarPanel(
numericInput("x", "Enter Value of X", 1),
numericInput("y", "Enter Value of Y", 1),
actionButton("add_data", "Add Data", width="100%")
),
mainPanel(
tableOutput("xy_Table")
)
)
)
#server.R
library(shiny)
library(tidyverse)
shinyServer(function(input, output) {
x <- vector("numeric")
y <- vector("numeric")
xyTable <- tibble(x, y)
e <- reactive(input$x)
f <- reactive(input$y)
eventReactive(input$add_data, {
xyTable %>% add_row(x=e(), y=f())
})
output$xy_Table <- renderTable({
xyTable
})
})
Thanks a lot for any help.
You need to use a reactive xyTable in order for the output to update. Also,
append the rows inside an observer rather than a reactive expression, and make sure to save the updated reactive value:
library(shiny)
library(tidyverse)
ui <- fluidPage(
sidebarPanel(
numericInput("x", "Enter Value of X", 1),
numericInput("y", "Enter Value of Y", 1),
actionButton("add_data", "Add Data", width = "100%")
),
mainPanel(
tableOutput("xy_Table")
)
)
server <- function(input, output, session) {
xyTable <- reactiveVal(
tibble(x = numeric(), y = numeric())
)
observeEvent(input$add_data, {
xyTable() %>%
add_row(
x = input$x,
y = input$y,
) %>%
xyTable()
})
output$xy_Table <- renderTable(xyTable())
}
shinyApp(ui, server)
Try this:
library(shiny)
library(tidyverse)
#ui.r
ui <- fluidPage(
# Application title
titlePanel("Test App"),
sidebarPanel(
numericInput("x", "Enter Value of X", 1),
numericInput("y", "Enter Value of Y", 1),
actionButton("add_data", "Add Data", width = "100%")
),
mainPanel(
tableOutput("xy_Table")
)
)
#server.R
server <- function(input, output) {
xyTable <- reactiveValues(
table1 = tibble(x = numeric(), y = numeric())
)
# what happens when `add_data` is clicked?
observeEvent(input$add_data, {
xyTable$table1 <- xyTable$table1 |>
add_row(x = input$x, y = input$y)
})
output$xy_Table <- renderTable({
xyTable$table1
})
}
shinyApp(ui, server)
#ui.r
library(shiny)
ui <- shinyUI(fluidPage(
# Application title
titlePanel("Test App"),
sidebarPanel(
numericInput("x", "Enter Value of X", 1),
numericInput("y", "Enter Value of Y", 1),
actionButton("add_data", "Add Data", width="100%")
),
mainPanel(
tableOutput("xy_Table")
)
)
)
#server.R
library(shiny)
library(tidyverse)
server <- shinyServer(function(input, output) {
x <- vector("numeric")
y <- vector("numeric")
xyTable <- reactiveValues()
xyTable$df <- tibble(x, y)
e <- reactive(input$x)
f <- reactive(input$y)
observeEvent(input$add_data, {
xyTable$df <- xyTable$df %>% add_row(x=e(), y=f())
})
output$xy_Table <- renderTable({
xyTable$df
})
})
shinyApp(ui,server)

Change backgorund color of cell of data table while its value is edited in Rshiny

I have renderDatatable with editable=TRUE options, what i am looking for is when user modify any value of cell, the cell background color should change (say -"green"). it is necessary because end user can have an idea about the changes he/she has made to the table when he see later.
below is the code I am trying with
library(shiny)
library(shinydashboard)
library(tidyverse)
library(DT)
library(data.table)
header <- dashboardHeader(title = "demo")
sidebar <- dashboardSidebar(
sidebarMenu(id = 'sidebarmenu',
menuItem("admin", tabName = "admin", icon = icon("adjust"))
)
)
body <- dashboardBody(
tabItems(
tabItem(
tabName = 'admin',
fluidRow(
dataTableOutput('userTable')
)
)
)
)
ui <- dashboardPage(title = 'admin function test', header, sidebar, body, skin='blue')
server <- function(input, output, session){
dat <- data.table::data.table(v1 = c(1,2,3), v2 = c(2,3,4), v3=c(4,5,8), v4=c("a","b","c"))
###Tracking Changes###
rvs <- reactiveValues(
data = NA, #dynamic data object,
logical = NA
)
observe({
rvs$data <- dat
})
observeEvent(input$userTable_cell_edit, {
rvs$data <<- editData(rvs$data, input$userTable_cell_edit, rownames = FALSE,resetPaging = TRUE)
## below code is to keep track of cell that is edited
rvs$logical <<- rvs$data == dat
})
output$userTable <- renderDataTable({
#rvs$data[, v3 := v1+v2]
DT::datatable(rvs$data,editable = TRUE,rownames = FALSE) %>% formatStyle(
colnames(rvs$data),
target = "cell",
## here I am trying to change background color of cell which has been
## edited using refrence from TRUE/FALSE of matrix rvs$logical
## but it is not working
backgroundColor = styleEqual( c(1,0), c('green', 'red') )
)
})
}
shinyApp(ui = ui, server = server)
library(shiny)
library(shinyjs)
library(DT)
js <- HTML(
"function colorizeCell(i, j){
var selector = '#dtable tr:nth-child(' + i + ') td:nth-child(' + j + ')';
$(selector).css({'background-color': 'yellow'});
}"
)
colorizeCell <- function(i, j){
sprintf("colorizeCell(%d, %d)", i, j)
}
ui <- fluidPage(
useShinyjs(),
tags$head(
tags$script(js)
),
br(),
DTOutput("dtable")
)
dat <- iris[1:5, ]
server <- function(input, output, session){
output[["dtable"]] <- renderDT({
datatable(dat, editable = TRUE, selection = "none")
}, server = FALSE)
observeEvent(input[["dtable_cell_edit"]], {
info <- input[["dtable_cell_edit"]]
i <- info[["row"]]
j <- info[["col"]]
runjs(colorizeCell(i, j+1))
})
}
shinyApp(ui, server)

Shiny Modules: Handling a list of buttons

I am trying to build an app that relies on a list of buttons created via lapply. I can successfully reference the buttons using observeEvent when I am not working with modularized code. However, when I try to use modules, the observeEvent doesn't work. I suspect it has something to do with how Shiny handles the namespace id's, but despite a couple of days of experimentation, I have not been able to solve the issue.
Below I will post first the non-modularized dummy app that does work (stolen from this other stack overflow question: R Shiny: How to write loop for observeEvent). Then I will share my existing modularized code that does not work.
Working non-modularized code:
library("shiny")
ui <- fluidPage(
fluidRow(
column(
width = 6,
lapply(
X = 1:6,
FUN = function(i) {
sliderInput(inputId = paste0("d", i), label = i, min = 0, max = 10, value = i)
}
)
),
column(
width = 6,
verbatimTextOutput(outputId = "test")
)
)
)
server <- function(input, output){
vals <- reactiveValues()
lapply(
X = 1:6,
FUN = function(i){
observeEvent(input[[paste0("d", i)]], {
vals[[paste0("slider", i)]] <- input[[paste0("d", i)]]
})
}
)
output$test <- renderPrint({
reactiveValuesToList(vals)
})
}
shinyApp(ui = ui, server = server)
Modularized Code that fails:
library(shiny)
slidersUI <- function(id){
ns <- NS(id)
tagList(
fluidRow(
column(
width = 6,
lapply(
X = 1:6,
FUN = function(i) {
sliderInput(inputId = paste0("d", i), label = i, min = 0, max = 10, value = i)
} ),
column(
width = 6,
verbatimTextOutput(outputId = "test")
)
)))
}
slidersServer <- function(input, output, session){
vals <- reactiveValues()
lapply(
X = 1:6,
FUN = function(i){
output$test2 <- renderText(paste0("this is i:", i))
observeEvent(input[[paste0("d", i)]], {
vals[[paste0("slider", i)]] <- input[[paste0("d", i)]]
})
}
)
output$test <- renderPrint({
reactiveValuesToList(vals)
})
}
library("shiny")
ui <- fluidPage(
slidersUI("TheID")
)
server <- function(input, output){
callModule(slidersServer, "TheID")
}
shinyApp(ui = ui, server = server)
Thank you!
You need to wrap your IDs in ns to get the correct namespace. Here is the corrected module ui:
slidersUI <- function(id){
ns <- NS(id)
tagList(
fluidRow(
column(
width = 6,
lapply(
X = 1:6,
FUN = function(i) {
sliderInput(inputId = ns(paste0("d", i)), label = i, min = 0, max = 10, value = i)
} ),
column(
width = 6,
verbatimTextOutput(outputId = ns("test"))
)
)))
}

Change tabs when any button on data table is clicked

I'm trying to tweak the following code to change tabs when any button on data table is clicked. The error message says that a button parameter is missing, which i can't find. My ultimate goal is to use these buttons to filter related data and display in another tab. Please take a look. Thank you for your time.
library(shiny)
library(ggplot2)
library(DT)
library(DBI)
library(shinyjs)
library(shinydashboard)
library(data.table)
library(pool)
library(dplyr)
library(shinyWidgets)
ui <- fluidPage(
title = "Examples of DataTables",
tabsetPanel(
id = 'dataset',
tabPanel("tab 1", DT::dataTableOutput("tab1"), verbatimTextOutput('printMsg')),
tabPanel("tab 2", DT::dataTableOutput("tab2")),
tabPanel("tab 2", DT::dataTableOutput("tab3"))
)
)
server <- function(input, output) {
printText <- reactiveValues(run_id = '')
buttonInput <- function(FUN, len, id, ...) {
inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))
}
inputs
}
vals <- reactiveValues()
#vals$Data <- data.table(
vals$Data <- data.table(
Brands = paste0("Brand", 1:10),
Forecasted_Growth = sample(1:20, 10),
Last_Year_Purchase = round(rnorm(10, 1000, 1000) ^ 2),
Contact = paste0("Brand", 1:10, "#email.com"),
'Lane Summary' = buttonInput(
FUN = actionButton,
len = 10,
id = 'button_',
label = "+",
onclick = 'Shiny.onInputChange(\"lastClick\", this.id)'
)
)
output$tab1 <- DT::renderDataTable({
DT = vals$Data
datatable(DT, escape = F)
})
observeEvent(input$lastClick, {
selectedRow <- as.numeric(strsplit(input$lastClick, "_")[[1]][2])
printText$run_id <<- paste('clicked on ',vals$Data[selectedRow,1])
#change tabs
updateTabsetPanel(session, "dataset",
selected = "tab 2")
})
output$printMsg <- renderText({
printText$run_id
})
}
shinyApp(ui, server)
You need to include session in your server function as an argument, then it works. The use of session is not mandatory due to legacy reasons, but now recommended. In your case, you need it because updateTabsetPanel uses the session object:
library(shiny)
library(ggplot2)
library(DT)
library(shinyjs)
library(shinydashboard)
library(data.table)
library(dplyr)
library(shinyWidgets)
ui <- fluidPage(
title = "Examples of DataTables",
tabsetPanel(
id = 'dataset',
tabPanel("tab 1", DT::dataTableOutput("tab1"), verbatimTextOutput('printMsg')),
tabPanel("tab 2", DT::dataTableOutput("tab2")),
tabPanel("tab 2", DT::dataTableOutput("tab3"))
)
)
server <- function(input, output, session) {
printText <- reactiveValues(run_id = '')
buttonInput <- function(FUN, len, id, ...) {
inputs <- character(len)
for (i in seq_len(len)) {
inputs[i] <- as.character(FUN(paste0(id, i), ...))
}
inputs
}
vals <- reactiveValues()
#vals$Data <- data.table(
vals$Data <- data.table(
Brands = paste0("Brand", 1:10),
Forecasted_Growth = sample(1:20, 10),
Last_Year_Purchase = round(rnorm(10, 1000, 1000) ^ 2),
Contact = paste0("Brand", 1:10, "#email.com"),
'Lane Summary' = buttonInput(
FUN = actionButton,
len = 10,
id = 'button_',
label = "+",
onclick = 'Shiny.onInputChange(\"lastClick\", this.id)'
)
)
output$tab1 <- DT::renderDataTable({
DT = vals$Data
datatable(DT, escape = F)
})
observeEvent(input$lastClick, {
selectedRow <- as.numeric(strsplit(input$lastClick, "_")[[1]][2])
printText$run_id <<- paste('clicked on ',vals$Data[selectedRow,1])
#change tabs
updateTabsetPanel(session, "dataset",
selected = "tab 2")
})
output$printMsg <- renderText({
printText$run_id
})
}
shinyApp(ui, server)

Resources