I'm trying to make a table using renderTable but it's not rendering on the browser. Here's the ss
#imgur
Full table's code
This's the renderTable code:
library(shiny)
library(diceR)
output$clustensemble <- renderTable({
#load file
data <- data()
#consensus cluster
cc <- consensus_cluster(data, nk=3, reps=1, algorithms=c("km","hc"), progress=FALSE)
ce <- as.matrix(cc)
tce <- t(ce)
tce })
I've tried using
sanitize.text.function = function(x) x;
but it didn't work, as stated in here
I've also tried using renderUI, but instead it generate another error.
The table consist of number and string, but i think that's not the issue.
Still new at this kind of R project, so I didn't know any other solution. What should I do, and what is the cause of this problem? Thanks!
(EDIT)
Sample data csv
ui.R
server.R
app.R
Without seeing the data that you use and the ui, we can only guess.
Using the example data from diceR I am able to print out a table using base shiny or DT.
library(shiny)
library(DT)
library(diceR)
data(hgsc)
# Custom distance function
manh <- function(x) {
stats::dist(x, method = "manhattan")
}
# Custom clustering algorithm
agnes <- function(d, k) {
return(as.integer(stats::cutree(cluster::agnes(d, diss = TRUE), k)))
}
assign("agnes", agnes, 1)
ui <- fluidPage(
DT::dataTableOutput("tableDT"),
tableOutput("table")
)
server <- function(input, output){
data <- reactive({
dat <- hgsc[1:10, 1:50]
cc <- consensus_cluster(dat, reps = 6, algorithms = c("pam", "agnes"),
distance = c("euclidean", "manh"), progress = FALSE)
ce <- as.matrix(cc)
t(ce)
})
output$tableDT <- DT::renderDataTable({
data()
})
output$table <- renderTable({
data()
})
}
shinyApp(ui, server)
Related
I want to build a module in shiny that renders a tabBox with the number of tabPanel as a function of the data. The simulated data (see script below) has the tank or pond variable (column) ("viveiro" in Portuguese) whose quantity can be a variable. So the number of panels is a function of this variable. But the biggest problem is when inside each tabPanel I render a simple table (with renderTable()) that corresponds to a subset of each "viveiro" (tank/pond). I use the lapply() function both to build the renderUI and to assign the reactive expression to the outputs (see the applicable example below). nCiclo() is a reactive that represent the number of "viveiro" (tank/pond as you prefer) that can correspond to a sequence of 1:6 for example. It works well on the first lapply() in renderUI() for output$tab_box, but it doesn't work when I use it on the second lapply() for the output[[paste0('outCiclo',j)]] outputs in renderTable below.
Question:
How do I put this last lapply() function as a function of the number of "viveiro" (tank/pond) in the simulation data? I tried to replace the fix sequence 1:6 for reactive nCiclo() but does not work.
library(shiny)
library(shinydashboard)
library(openxlsx)
rm(list = ls())
#--------------------------------------------------
# Simulated data for the app
(n = 2*sample(3:8,1)) # tank/pond (portuguese viveiro) number (quantity) / random variable in the data
bio <- data.frame(
semana = rep(1:5,n),
peso = rnorm(5*n,85,15),
viveiro = rep(1:2,each=(5*n)/2),
ciclo = rep(1:n,each=5)
)
# An excel file will be saved to your Working Directory
# Use the file to import into the app
write.xlsx(bio,'bio.xlsx')
#--------------------------------------------------
####### Module #######
# UI Module
dashMenuUI <- function(id){
ns <- NS(id)
uiOutput(ns("tab_box"))
}
# Server Module
dashMenuServer <- function(id,df){
moduleServer(id,function(input,output,session){
ns <- session$ns
nCiclo <- reactive(unique(df()$ciclo)) # nCycle is simply 1:6 sequence.
output$tab_box <- renderUI({
do.call(tabBox, c(id='tabCiclo',
lapply(nCiclo(), function(i) {
tabPanel(
paste('ciclo', i),
tableOutput(outputId = ns(paste0('outCiclo',i)) )
)
}))
)
})
# The problem is here. I want to put the lapply function as a function of the pond/tank (portuguese viveiro) number (simulated data).
# but the nCycle() reactive doesn't work in place of 1:6
lapply(1:6, function(j) {
output[[paste0('outCiclo',j)]] <- renderTable({
subset(df(), ciclo==j)
})
})
})
}
#------------------------------------------------------
ui <- dashboardPage(
dashboardHeader(title = "Teste Módulo TabBox Dinâmico"),
dashboardSidebar(
sidebarMenu(
menuItem('Ciclo e viveiro',tabName = 'box_din')
)
),
dashboardBody(
tabItems(
tabItem(tabName='box_din',
fileInput(inputId = "upload",label = "Carregue seu arquivo", accept = c(".xlsx")),
dashMenuUI('tabRender')
)
)
)
)
server <- function(input, output, session) {
dados <- reactive({
req(input$upload)
file <- input$upload
ext <- tools::file_ext(file$datapath)
req(file)
validate(need(ext == "xlsx", "Por gentileza insira um arquivo de Excel (extensão .xlsx)"))
df <- read.xlsx(file$datapath,sheet = 1)
df
})
# Ciclo output
dashMenuServer('tabRender',dados)
}
shinyApp(ui, server)
When running the first session of the script note that you get an excel file (.xlsx) in your Working Directory, it is the simulated data to import into the app. The problem is that the 1:6 sequence is fixed and doesn't vary depending on the data (the cycles above 6 are not rendered in the panels), when I replace 1:6 with nCiclo() (try to test for yourself) (it is found in the server module) doesn't work.
I'm not sure if I made myself clear or if the English are understandable, but I thank you for taking the time to read the problem and help in my learning.
Calling nCicle() must be done in a reactive environment, which #Mikael's solution creates using observeEvent() (see comments). Another way is simply to move the lapply(nCiclo(), ...)) up into the output$tab_box <- renderUI() function:
output$tab_box <- renderUI({
lapply(nCiclo(), function(j) {
output[[paste0('outCiclo',j)]] <- renderTable({
subset(df(), ciclo==j)
})
})
do.call(tabBox, c(id='tabCiclo',
lapply(nCiclo(), function(i) {
tabPanel(
paste('ciclo', i),
tableOutput(outputId = ns(paste0('outCiclo', i)) )
)}
))
)
})
Good example of creating dynamic content in a Shiny app.
Currently, i am trying to output multiple tables i managed to retrieve with an api call onto a dashboard page with a single uiOutput().
took some reference from this post:
R Shiny - Display multiple plots selected with checkboxGroupInput
however, while i was succesful in putting it into a list for the overall layout and output it into uioutput(), i was not able to acheive the desired results as all the tables were the same, in reality it should be different as i have already tagged a unique dataframe to each renderdatatable()
below shows the screen shot and the code. would appreciate some help here thank you!
[1]: https://i.stack.imgur.com/W0B26.png
library(httr)
library(jsonlite)
library(plyr)
library(data.table)
library(rlist)
library(shiny)
########### UI ############
ui <- fluidPage(uiOutput('datatables'))
######### SERVER ###########3
server <- function(input, output, session){
output$datatables <- renderUI({
link <- 'https://api.zapper.fi/v1/protocols/balances/supported?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'
test <- GET(link)
test <- fromJSON(rawToChar(test$content))
counter1 <- 0
out <- list()
df <- list()
for (i in seq(from = 1, to = length(test$network))){
network <- test$network[i]
#counter <- counter + 1
out <- list(out, h2(paste0(str_to_title(network),' Network')))
for (e in ldply(test$protocols[i], data.frame)$protocol){
link1 <- paste0(paste0('https://api.zapper.fi/v1/protocols/',e),paste0(paste0('/balances?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&network=',network),'&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'))
data <- fromJSON(rawToChar(GET(link1)$content))
wallet <- '0x58bbae0159117a75225e72d941dbe35ffd99f894'
#info <- ldply(eval(parse(text=sprintf("data$'%s'$products$assets",wallet))),data.frame)
out <- list(out, h3(paste0(str_to_title(e),' Protocol')))
counter1 <- counter1 + 1
df[[counter1]] <- ldply(eval(parse(text=sprintf("data$'%s'$products$assets",wallet))),data.frame)
out<- list(out, renderDataTable(df[[counter1]]))
}
}
return(out)
})
}
shinyApp(ui, server)
UPDATE: I ALSO TRIED TO WRAP IT IN AN OBSERVE() AND LOCAL () FOR THE DIFFERENT OUTPUTS, STILL DIDNT ACHEIVE THE DESIRED RESULTS, ALL SAME TABLES WHICH IS WRONG
I almost achieved the desired result with the following code:
library(httr)
library(jsonlite)
library(plyr)
library(data.table)
library(rlist)
library(shiny)
# added :
library(DT)
library(stringr)
########### UI ############
ui <- fluidPage(
uiOutput('datatables')
)
######### SERVER ###########
server <- function(input, output, session){
link <- 'https://api.zapper.fi/v1/protocols/balances/supported?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'
test <- GET(link)
test <- fromJSON(rawToChar(test$content))
output$datatables <- renderUI({
outputlist <- lapply(1:length(test$network), function(i) {
network <- test$network[i]
networkTitle <- paste0("networktitle", i)
lapply(seq_along(ldply(test$protocols[i], data.frame)$protocol), function(j) {
protocolTitle <- paste0("protocoltitle", i, j)
outputId <- paste0("network", i, "protocol", j)
tagList(
#uiOutput(networkTitle),
uiOutput(protocolTitle),
DTOutput(outputId)
)
})
})
})
lapply(1:length(test$network), function(i) {
network <- test$network[i]
my_i <- i
networkTitle <- paste0("networktitle", my_i)
#local({
# my_network <- network
# output[[networkTitle]] <- renderUI({
# tags$h2(paste0(str_to_title(my_network),' Network'))
# })
#})
lapply(seq_along(ldply(test$protocols[my_i], data.frame)$protocol), function(j) {
e <- ldply(test$protocols[my_i], data.frame)$protocol[[j]]
local({
my_network <- network
my_e <- e
my_j <- j
protocolTitle <- paste0("protocoltitle", my_i, my_j)
outputId <- paste0("network", my_i, "protocol", my_j)
link1 <- paste0(paste0('https://api.zapper.fi/v1/protocols/',my_e),paste0(paste0('/balances?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&network=',my_network),'&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'))
data <- fromJSON(rawToChar(GET(link1)$content))
wallet <- '0x58bbae0159117a75225e72d941dbe35ffd99f894'
output[[protocolTitle]] <- renderUI({
tags$h3(paste0(str_to_title(my_network),' Network - ', str_to_title(my_e),' Protocol'))
})
output[[outputId]] <- renderDT({
ldply(eval(parse(text=sprintf("data$'%s'$products$assets",wallet))),data.frame)
})
})
})
})
}
shinyApp(ui, server)
I used two nested lapply but this might work with for loops as well (using local()).
As you mentioned, we have several difficulties here:
the need to use local() to get unique IDs for each protocol title and datatable (see this)
the need to use one renderUI in the server part to generate several types of outputs dynamically (uiOutput for titles, DTOutput for datatables), all contained in a tagList(). See this and this again.
The need to use two nested for loops/lapply functions to render the shiny outputs used in the renderUI() part.
I combined the network and protocol titles because I was not able to get network titles as unique h2 titles as shown in your example figure. Duplicate output IDs were still generated for h2 titles when using the commented code. I let the commented code as reference if someone wants to try improve it.
I have a cumbersome function inside a server output variable.
The function returns a list of 2 data frames.
I want to extract these tables and plot them side by side.
However I do not want to create two different outputs for them in server, as I don't want the heavy function to run twice.
For the sake of giving a reproducible code :
(my getListOfDataFrames function is much heavier than in this example)
I want df1 and df2 displayed side by side with scrollX = TRUE in options
library(shiny)
library(DT)
ui <- fluidPage(
dataTableOutput("output1")
)
server <- function(input, output){
getListOfDataFrames <- function(df){
return(list(df[1:5,], df[6:10,]))
}
output$output1 <- renderDataTable({
myList <- getListOfDataFrames(mtcars)
df1 <- as.data.frame(myList[1])
df2 <- as.data.frame(myList[2])
})
}
shinyApp(ui, server)
There are plenty of examples how to create dynamic content, like example below:
library(shiny)
library(DT)
ui <- fluidPage(
uiOutput("dt")
)
server <- function(input, output){
getListOfDataFrames <- function(df){
return(list(df[1:5,], df[6:10,]))
}
myList <- getListOfDataFrames(mtcars)
output$dt <- renderUI({
ntables <- seq(myList)
# we want to create the width depending how many tables we have
width <- paste0(99/max(ntables),"%;")
lapply(ntables, function(i) {
id <- paste0("dt", i)
div(style=paste0("display:inline-block;width:",width),DT::dataTableOutput(id))
})
})
observe({
# Dynamically creating 2 tables with separate ids
lapply(seq(myList), function(i){
id <- paste0("dt", i)
output[[id]] <- DT::renderDataTable(as.data.frame(myList[i]))
})
})
}
shinyApp(ui, server)
within the server for my shinyApp, I created a dataframe based on the inputs. However, I want to add a new column that utilizes two of the columns of that dataframe.
server <- function(input, output, session) {
l.out <- reactive({
BatchGetSymbols(tickers = input$stock,
first.date = Sys.Date() - as.integer(input$length),
last.date = Sys.Date())
})
stock_info <- reactive({
l.out()$df.tickers
})
stock_info()$return <- reactive({
rep(0, length(stock_info()$ref.date))
})
stock_info()$return <- reactive({
for (i in 2:length(stock_info()$ref.date)){
stock_info()$return[i] <- ((stock_info()$price.close[i] -
stock_info()$price.close[i - 1]) / stock_info$price.close[i - 1])
}
})
I have tried it like this, and it works up until I try to create stock_info()$return, where I keep getting the error that NULL left assignment.
Any tips?
I'm not familiar with the BatchGetSymbols package, but the concepts in the example below should be applicable for your use case as well.
First things first, for lack of an elegant way to say this, I'm pretty sure the expression...
stock_info()$return <- reactive({
rep(0, length(stock_info()$ref.date))
})
...just isn't really how shiny reactive objects and the associated syntax work.
It looks like you could simplify your code a lot by condensing a bunch of your intermediate steps into a single expression. If you only have one set of reactive data you will use in all of your outputs, this might be a more straight forward approach.
library(shiny)
ui <- fluidPage(
textInput('stock','stock',"GE"),
sliderInput('length', 'length', min = 1, max = 10, value = 5),
dataTableOutput('my_table')
)
server <- function(input, output, session) {
## This will update whenever either input$length or input$stock change
stock_info <- reactive({
length <- as.integer(input$length)
temp_stock_info <- data.frame(stock = input$stock,
foo = seq_len(length),
bar = rnorm(length))
temp_stock_info$baz <- paste("xxx",length)
return(temp_stock_info)
})
## Return an output
output$my_table <- renderDataTable({
stock_info()
})
}
shinyApp(ui, server)
However, if you are using the intermediate object l.out for a variety of end outputs, it might make sense to make it a reactive object of it's own. Then, we can update l.out whenever a relevant input changes, and then use that intermediate variable to cascade updates through the other downstream reactives.
In addition, we can update downstream reactive objects like stock_info based on other conditions that don't affect l.out without re-running l.out every time.
library(shiny)
ui <- fluidPage(
textInput('stock','stock',"GE"),
sliderInput('length', 'length', min = 1, max = 100, value = 50),
sliderInput('displayLength', 'displayLength', min = 1, max = 20, value = 5),
dataTableOutput('my_table')
)
server <- function(input, output, session) {
## l.out will change with input$length and input$stock
## but NOT input$displayLength
l.out <- reactive({
data.frame(stock = input$stock,
foo = rnorm(input$length),
l.out_update_time = Sys.time())
})
## stock_info will update whenever l.out changes or the displayLength changes.
## l.out will NOT be updated if only input$displayLength changes
stock_info <- reactive({
tmp_stock_info <- head(x = l.out(), n = input$displayLength)
tmp_stock_info$stock_info_update_time <- Sys.time()
return(tmp_stock_info)
})
## Return an output
output$my_table <- renderDataTable({
stock_info()
})
}
shinyApp(ui, server)
I am doing the following:
using R ShinyUI, get client inputs on ranges of variables A, B, C;
in R ShinyServer, read in a csv file, and using the client inputs to slice the csv, and get the portion that I need;
Perform a loop calculation on the csv, calculate various statistics from the loop output, and plot all these statistics.
Pseudo code:
data = read.csv('file.csv')
shinyServer(function(input, output) {
data <- reactive({
data = data[data$A<INPUT1 & data$B> INPUT2 & data$C<INPUT3,]
})
for (i in 1:dim(data)[1]){
result1[i] = xxx
result2[i] = xxx
}
output$plot <- renderPlot({
plot(result1)
})
})
The above code does not work. I want to know:
How to correctly incorporate user input and get the variable "data,"
How to plot result1 and result2 from output$plot
Thanks!
The for loop should be inside a the renderPlot, so each time the input$month changes, the reactive data will change and then the for lop will update your variables. If you have the for loop outside a reactive expression, it will be executed only once when the app starts, but after changes in the input.
Below is simple example based on the pseudo code you provide in your original question to illustrate the possible solution.
library(shiny)
ui <- shinyUI( fluidPage(
fluidRow(
column(4,
numericInput("input1", "Speed >", 8),
numericInput("input2", "Dist >", 15)
),
column(8,
plotOutput("plot")
)
)
))
server <- shinyServer(function(input, output) {
dat0 <- cars
data <- reactive({
dat0[dat0$speed > input$input1 & dat0$dist > input$input2,]
})
output$plot <- renderPlot({
s <- dim(data())[1]
result1 <- numeric(s)
result2 <- numeric(s)
for (i in 1:s){
result1[i] <- data()[i, 1]
result2[i] <- data()[i, 2]
}
plot(result1, result2)
})
})
shinyApp(ui = ui, server = server)