How to renderPrint? - r

I'm trying to create a console and enter code to display in the panel.
Based on this solution, I created this code.
But when running, for example, the following lines the output is printed in the console, but not on the Shiny App.
x <- data.frame("SN" = 1:2, "Age" = c(21,15), "Name" = c("John","Dora"))
print(x)
How do I make this output appear on the Shiny app?
Code:
library(shiny)
library(shinyAce)
ui <- fluidPage(
wellPanel(
tagList(wellPanel(uiOutput("plotorprint")))
),
wellPanel(
aceEditor("code", mode = "r", height = "100px",
highlightActiveLine = FALSE,
showLineNumbers = FALSE,
minLines = 2,
maxLines = 30,
fontSize = 16,
autoScrollEditorIntoView = TRUE,
placeholder = "CONSOLE"))
)
server <- function(input, output) {
ace_obj <- reactive({
eval(parse(text=input$code))
})
output$printout <- renderPrint({
ace_obj()
})
output$plotout <- renderPlot({
ace_obj()
})
output$plotorprint <- renderUI({
if (is.data.frame(ace_obj)) { # Check if output of f(x) is data.frame
verbatimTextOutput("printout") # If so, create a print
} else { # If not,
plotOutput("plotout") # create a plot
}
})
}
shinyApp(ui = ui, server = server)
I'm trying to display output in the white space above the console panel.

You could use req() (require) to only evaluate renderPrint and renderPlot if your condition is met (you can add as many conditions as you like for both plot and print in require):
library(shiny)
library(shinyAce)
ui <- fluidPage(
wellPanel(
tagList(wellPanel(
verbatimTextOutput("printout"),
plotOutput("plotout")
))
),
wellPanel(
aceEditor("code", mode = "r", height = "100px",
highlightActiveLine = FALSE,
showLineNumbers = FALSE,
minLines = 2,
maxLines = 30,
fontSize = 16,
autoScrollEditorIntoView = TRUE,
placeholder = "CONSOLE"))
)
server <- function(input, output) {
ace_obj <- reactive({
eval(parse(text=input$code))
})
output$printout <- renderPrint({
req(ace_obj(), is.data.frame(ace_obj()))
ace_obj()
})
output$plotout <- renderPlot({
req(ace_obj())
ace_obj()
})
}
shinyApp(ui = ui, server = server)

A simpler solution including renderUI is fixing the if-condition. ace_obj is a reactive object and is evaluated only with ace_obj():
output$plotorprint <- renderUI({
if (is.data.frame(ace_obj())) { # Check if output of f(x) is data.frame
verbatimTextOutput("printout") # If so, create a print
} else { # If not,
plotOutput("plotout") # create a plot
}
})
Then evaluate in your interactive shiny console without print():
x <- data.frame("SN" = 1:2, "Age" = c(21,15), "Name" = c("John","Dora"))
x

Related

Using a dynamic UI to draw a 3d plot in shiny

I have a dataframe:
df1<-data.frame(a=rnorm(100),b=rnorm(100),c=rnorm(100),ID1=c("A","B"),ID2=(c("A","B","C","D")))
I am drawing a 3d plot with plotly by adding add_trace in a loop, like:
library(shiny)
library(plotly)
library(tidyverse)
df1<-data.frame(a=rnorm(100),b=rnorm(100),c=rnorm(100),ID1=c("A","B"),ID2=(c("A","B","C","D")))
test<-unique(df1$ID2)
tempt.col<-c("red","blue","green","yellow")
p<-plot_ly()
for(i in 1:length(test)){
df2<-df1[df1$ID2==test[i],] %>%
select(a,b,c)
p<-add_trace(p=p,
data = df2,
x=~a,y=~b,z=~c,
type="scatter3d",
marker = list(size=5,color=tempt.col[i]),
mode="markers"
)
}
p
It works very well like:
Now I want to achieve this in shiny, I would like to generate colourInput based on the length of the selected ID, the ui:
ui<-fluidPage(
fluidRow(
sidebarPanel(
selectInput("select1","Select the ID",choices = colnames(df1[,4:5]),multiple = FALSE),
actionButton("act1","Go"),
uiOutput("ui1"),
),
mainPanel(
tableOutput("table1"),
plotlyOutput("plot.3d",height = "1000px")
)
)
)
server:
server<-function(input,output){
tempt.group<-reactive({
unique(df1[,input$select1])
})
observeEvent(input$act1,{
tempt.vector<-list()
tempt.col.name<-isolate(
vector(mode = "list",length = 2)
)
for(i in 1:length(tempt.group())){
tempt.vector[[i]]<-colourpicker::colourInput(
inputId = paste0("ColorID",i),
label = tempt.group()[i])
tempt.col.name[[1]][i]<-paste0("ColorID",i)
tempt.col.name[[2]][i]<-tempt.group()[i]
}
output$ui1<-renderUI({
tempt.vector
})
names(tempt.col.name)<-c("inputId","label")
col.name<-reactive({
data.frame(sapply(tempt.col.name,cbind))
})
col.df<-reactive({
tempt.col.df<-reactiveValuesToList(input)
data.frame(
names = names(tempt.col.df[grepl("ColorID", names(tempt.col.df))]),
values = unlist(tempt.col.df[grepl("ColorID", names(tempt.col.df))], use.names = FALSE)
)
})
group.col.df<-reactive({
merge(col.df(),col.name(),by.x="names",by.y="inputId")
})
output$table1<-renderTable(
group.col.df()
)
pp<-reactive({
p<-plot_ly()
for(i in 1:length(tempt.group())){
# col<-group.col.df()[group.col.df()[,"label"]==tempt.group()[i],"values"] ####it should be something wrong with here
df2<-df1[df1$ID==tempt.group()[i],] %>%
select(a,b,c)
p<-add_trace(p=p,
data = df2,
x=~a,y=~b,z=~c,
type="scatter3d",
# marker = list(size=5,color=col[i]), ####it should be something wrong with here
mode="markers"
)
}
p
})
output$plot.3d<-renderPlotly({
pp()
})
})
}
shinyApp(ui=ui,server=server)
The app is like:
I want to fetch the colourInput and pass to the color of the 3d scatter plot, but nothing works. The page either keeps refreshing or frozen,
That must be something wrong with col<-group.col.df()[group.col.df()[,"label"]==tempt.group()[i],"values"] and marker = list(size=5,color=col[i]),
please help.
The below works as intended.
library(shiny)
library(plotly)
df1<-data.frame(a=rnorm(100),b=rnorm(100),c=rnorm(100),ID1=c("A","B"),ID2=(c("A","B","C","D")))
# Define UI
ui<-fluidPage(
fluidRow(
sidebarPanel(
selectInput("select1","Select the ID",choices = colnames(df1[,4:5]),multiple = FALSE),
# actionButton("act1","Go"),
uiOutput("myui"),
# keep track of the last selection on all selectInput created dynamically
),
mainPanel(
#tableOutput("table1"),
plotlyOutput("plot.3d",height = "1000px")
)
)
)
# Define server logic required to draw a histogram
server<-function(input,output){
rv <- reactiveValues(mygroup=0, uitaglist = list(), uilabels = list(), input_subset = list(), plotly=NULL)
observeEvent(input$select1, {
newgroup <- unique(df1[,input$select1])
rv$mygroup <- newgroup
# ui tags
rv$uitaglist <- list()
for(i in 1:length(rv$mygroup)){
rv$uitaglist[[i]]<-colourpicker::colourInput(
inputId = paste0("ColorID",i),
label = rv$mygroup[i])
rv$uilabels[[i]] <- paste0("ColorID",i)
}
})
output$myui <- renderUI({
rv$input_subset <- rv$uitaglist
})
observe({
rv$input_subset <- lapply(rv$uilabels, function(x) input[[x]])
p<-plot_ly()
for(i in 1:length(rv$mygroup)) {
df2<-df1[df1$ID2 == rv$mygroup[i],] %>% select(a,b,c)
p<-add_trace(p=p,
data = df2,
x=~a,y=~b,z=~c,
type="scatter3d",
marker = list(size=5,color=rv$input_subset[[i]]),
mode="markers"
)
}
rv$plotly <- p
})
output$plot.3d<-renderPlotly({
rv$plotly
})
} # end server
# Run the application
shinyApp(ui = ui, server = server)
The main difficulty was to observe all your dynamically-generated UI inputs at once. Turns out it could be done using observe and lapply.
Observing several inputs is problematic because the error Must use single string to index into reactivevalues is returned by trying to index input by a vector or list.
Now, Why this can't be done out-of-the-box is a good question.

bring the colour widget to front in shiny app when using colourInput from colourpicker

We use "colourInput" from the package "colourpicker" in a shinyApp for picking various colours.
When the colourInput is used by itself (# example 1 below), the widget pops up in the app and everything works fine (image 1 in attached figure)
It still works when we use splitLayout with the homemade "split_color_input" function (# example 2 below). However, the widget is now "hidden" inside a scroll-bar window (image 2 in attached figure). How can we Bring it to Front like in example 1?
Figure
# example 1
ui <- fluidPage(
colourpicker::colourInput(inputId = "id1",
label = "label1",
value = "hotpink",
allowTransparent = TRUE,
returnName = TRUE,
closeOnClick = TRUE)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
# example 2
split_color_input = function(n, id, labs, vals, allowTransparent){
if (n%%2==1){
colourpicker::colourInput(
inputId = paste0(id, '.', 1+(n-1)/2),
label=labs[1+(n-1)/2],
value=vals[1+(n-1)/2],
allowTransparent = allowTransparent,
returnName = TRUE,
closeOnClick = TRUE)
}else{
p("")
}
}
id = "id2"
labs = c("label2.1", "label2.2")
vals = c("steelblue3", "hotpink")
cellwidths = c("45%", "10%", "45%")
ui <- fluidPage(
do.call(what=splitLayout, args = c(lapply(1:length(cellwidths), split_color_input, id, labs, vals, allowTransparent=TRUE),
list(cellWidths=as.list(cellwidths)),
list(width=list('500px'))) )
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
a quick way to fix is to overwrite shiny's style .shiny-split-layout>div {overflow: visible}.
# example 2
split_color_input = function(n, id, labs, vals, allowTransparent){
if (n%%2==1){
colourpicker::colourInput(
inputId = paste0(id, '.', 1+(n-1)/2),
label=labs[1+(n-1)/2],
value=vals[1+(n-1)/2],
allowTransparent = allowTransparent,
returnName = TRUE,
closeOnClick = TRUE)
}else{
p("")
}
}
id = "id2"
labs = c("label2.1", "label2.2")
vals = c("steelblue3", "hotpink")
cellwidths = c("45%", "10%", "45%")
ui <- fluidPage(
do.call(what=splitLayout, args = c(lapply(1:length(cellwidths), split_color_input, id, labs, vals, allowTransparent=TRUE),
list(cellWidths=as.list(cellwidths)),
list(width=list('500px')))
),
tags$style(HTML(".shiny-split-layout>div {overflow: visible}"))
)
server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Add click event to shiny app with 2 SunburstR plots

I am looking to add a click event to a shiny app that has 2 sund2b plots on the page. I would like the text output to just be in reference to whichever plot they clicked last, however it only seems to react to the first plot and not the second. The output is great for the one but I would like it to work for both. This is a simplified example of what I have:
library(shiny)
library(sunburstR)
sequences <- read.csv(
system.file("examples/visit-sequences.csv",package="sunburstR")
,header = FALSE
,stringsAsFactors = FALSE
)[1:200,]
sequences2 <- read.csv(
system.file("examples/visit-sequences.csv",package="sunburstR")
,header = FALSE
,stringsAsFactors = FALSE
)[201:400,]
server <- function(input,output,session){
#sunburst1
output$sunburst <- renderSund2b({
add_shiny(sund2b(sequences))
})
#sunburst2
output$sunburst2 <- renderSund2b({
add_shiny(sund2b(sequences2))
})
#sunburst click event
selection <- reactive({
input$sunburst_click
})
output$selection <- renderText(selection())
}
ui<-fluidPage(
sidebarLayout(
sidebarPanel(
),
# plot sunburst
mainPanel(
sund2bOutput("sunburst"),
sund2bOutput("sunburst2"),
textOutput("selection")
)
)
)
shinyApp(ui = ui, server = server)
The name of the click event input depends on the output name (input$sunburst2_click for output$sunburst2).
Please check the following:
library(shiny)
library(sunburstR)
sequences <- read.csv(
system.file("examples/visit-sequences.csv", package = "sunburstR"),
header = FALSE,
stringsAsFactors = FALSE
)[1:200,]
sequences2 <- read.csv(
system.file("examples/visit-sequences.csv", package = "sunburstR"),
header = FALSE,
stringsAsFactors = FALSE
)[201:400,]
ui <- fluidPage(sidebarLayout(
sidebarPanel(),
# plot sunburst
mainPanel(
sund2bOutput("sunburst"),
sund2bOutput("sunburst2"),
textOutput("selection"),
textOutput("selection2"),
textOutput("selectionLatest")
)
))
server <- function(input, output, session) {
#sunburst1
output$sunburst <- renderSund2b({
add_shiny(sund2b(sequences))
})
#sunburst2
output$sunburst2 <- renderSund2b({
add_shiny(sund2b(sequences2))
})
#sunburst click event
selection <- reactive({
input$sunburst_click
})
selection2 <- reactive({
input$sunburst2_click
})
latestClick <- reactiveVal()
observeEvent(input$sunburst_click, {
latestClick(input$sunburst_click)
})
observeEvent(input$sunburst2_click, {
latestClick(input$sunburst2_click)
})
output$selection <- renderText(paste("plot 1:", paste(selection(), collapse = " - ")))
output$selection2 <- renderText(paste("plot 2:", paste(selection2(), collapse = " - ")))
output$selectionLatest <- renderText(paste("plot 1 or 2:", paste(latestClick(), collapse = " - ")))
}
shinyApp(ui = ui, server = server)
Here the "official" example can be found.

R Shiny: Print output from dynamically created bucket lists

I am unable to print the entire output of bucket list inputs (verbatimTextOutput("bucket_outputs")). See:
library(shiny)
library(sortable)
example_Table = cbind(c(1,2,3,4),c("a","b","c","d"))
ui <- fluidPage(
uiOutput("Dyanmic_Bucket"),
uiOutput("Dyanmic_Bucket_2"),
###The following output is incomplete
verbatimTextOutput("bucket_outputs")
)
server <- function(input, output, session) {
rank_list_items <- lapply(seq(nrow(example_Table)), function(x) {
add_rank_list(
text = example_Table[x,1],
labels = example_Table[x,2]
)
})
output$Dyanmic_Bucket <- renderUI({
do.call("bucket_list", args = c(
list(header = "",
group_name = "Dyanmic_Bucket",
orientation = "horizontal"),
rank_list_items
))
})
rank_list_items_2 <- lapply(seq(nrow(example_Table)), function(x) {
add_rank_list(
text = example_Table[x,1],
labels = example_Table[x,2]
)
})
output$Dyanmic_Bucket_2 <- renderUI({
do.call("bucket_list", args = c(
list(header = "",
group_name = "Dyanmic_Bucket",
orientation = "horizontal"),
rank_list_items_2
))
})
output$bucket_outputs = renderPrint(input$Dyanmic_Bucket)
}
shinyApp(ui, server)
Initially, it only prints the output of Dyanmic_Bucket_2, but even this is inconsistent upon re-ordering, or doubling up some of the options.
Renaming the group name of each bucket will not work, as I would then no longer be able to drag and drop across the different rows.
I would like to see a printed output of both dynamically created buckets. Help much appreciated.

Manipulating reactive objects in Shiny - Error in <-: invalid (NULL) left side of assignment

I'm running into the error "Error in <-: invalid (NULL) left side of assignment" over and over again as I attempt to take a reactive object in Shiny and further manipulate it. I've provided an illustrative example below.
testdf <- data.frame(row1 = c(1,3,6), row2 = c(7, 5, 1))
ui <- fluidPage(
titlePanel("Education in Tanzania"),
sidebarLayout(
sidebarPanel(
#Select aggregation level of data
selectInput("AggregationSelect",
label = "Aggregation",
choices = list("School" = 1,
"District" = 2,
"Region" = 3),
selected = 1)
),
mainPanel(
DT::dataTableOutput("OutputTable")
)
)
)
server <- function(input, output) {
Output1 <- reactive({
testdf
})
observe({
if(2 %in% input$AggregationSelect) {
observe({Output1()$name[3] <- "b"})
} else if(3 %in% input$AggregationSelect) {
observe({Output1()$name[2] <- "c"})
} else if(1 %in% input$AggregationSelect) {
observe({Output1()$name[1] <- "a"})
}
})
output$OutputTable <- {DT::renderDataTable({
DT::datatable(Output1(),
options = list(pagelength = 25,
searching = TRUE,
paging = TRUE,
lengthChange = FALSE),
rownames = FALSE)
})
}
}
shinyApp(ui = ui, server = server)
What I need to do in my actual code is assemble a dataframe through the UI (which I am able to do and therefore have just subbed a random df in here) and then add some information (represented here with the added "names" column) based on what has been selected in the UI. It seems like it shouldn't be all that difficult to add a column to a df, but within the reactive object context, nothing I have attempted has worked. Other ways to modify reactive objects are welcome as long as they can be applied to more complex multi-step scenarios - there's no way I can get everything I need bundled into the initial assignment of the reactive object.
Reactive expressions cannot be modified from outside. You can only modify reactive values.
Generally you should never need to use observe. Use reactive expression if you don't need side effect, use reactive values with observeEvent when needed.
You must read reactive tutorials before going forward. There are quite some concepts need to be understood before you can do anything complex, especially the "force update habit". You need to let Shiny do the update properly and setup the logic correctly.
I suggest you read all the tutorials, articles about reactive in RStudio website, then watch the reactive tutorial video in Shiny conference.
Im not 100% what you're doing but I think its best if you use eventReactive that would listen to your selectInput. Note that I added the variable names to the dataframe:
library(shiny)
testdf <- data.frame(names = c(1,3,6), row2 = c(7, 5, 1))
ui <- fluidPage(
titlePanel("Education in Tanzania"),
sidebarLayout(
sidebarPanel(
#Select aggregation level of data
selectInput("AggregationSelect", label = "Aggregation",
choices = list("School" = 1, "District" = 2, "Region" = 3),selected = 1)
),
mainPanel(
DT::dataTableOutput("OutputTable")
)
)
)
server <- function(input, output) {
Output1 <- eventReactive(input$AggregationSelect,{
if(input$AggregationSelect %in% "2"){
testdf$name[3] <- "b"
return(testdf)
}
else if(input$AggregationSelect %in% "3"){
testdf$name[2] <- "c"
return(testdf)
}
else if(input$AggregationSelect %in% "1"){
testdf$name[1] <- "a"
return(testdf)
}
else{
return(testdf)
}
})
output$OutputTable <- {DT::renderDataTable({
print(Output1())
DT::datatable(Output1(),options = list(pagelength = 25,searching = TRUE,paging = TRUE,lengthChange = FALSE),rownames = FALSE)
})
}
}
shinyApp(ui = ui, server = server)
As per my understanding of your problem, i've tweaked you code as following :
testdf <- data.frame(name = c(1,2,3), freq = c(100, 200, 300))
ui <- fluidPage(
titlePanel("Education in Tanzania"),
sidebarLayout(
sidebarPanel(
#Select aggregation level of data
selectInput("AggregationSelect",
label = "Aggregation",
choices = list("School" = 1,
"District" = 2,
"Region" = 3))
),
mainPanel(
DT::dataTableOutput("OutputTable")
)
))
server <- function(input, output) {
Output1 <- reactive({
input$AggregationSelect
selection <- input$AggregationSelect
if(2 %in% selection){
testdf$name[3] <- "b"
}
else if(3 %in% selection){
testdf$name[2] <- "c"
}
else if(1 %in% selection){
testdf$name[1] <- "a"
}
testdf
})
output$OutputTable <- {DT::renderDataTable({
DT::datatable(Output1(),
options = list(pagelength = 25,
searching = TRUE,
paging = TRUE,
lengthChange = FALSE),
rownames = FALSE)
})
}}
shinyApp(ui = ui, server = server)

Resources