I have this shiny app where I'm plotting several dygraphs. Unfortunately, I dont't know how many plots there will be plottet. It may vary from time to time. So I came up with using uiOutput and renderUI to build an app that reacts on the number of plots. See https://shiny.rstudio.com/articles/dynamic-ui.html
Now I wnat to show the legend of each dygraph outside the respective plot as was shown here: Is there a way to add legend next to a dygraph in R, not exactly on the plot?
My problem is now that the <div> elements of the legend do not have the same height as the ones of the plots.
My code is:
UI:
library(dygraphs)
shinyUI(fluidPage(
titlePanel("Simple example"),
sidebarLayout(
sidebarPanel(),
mainPanel(
fluidRow(column(10, uiOutput("graphs")),
column(2, uiOutput("legends")))
)
)
))
server:
library(dygraphs)
library(xts)
shinyServer(function(input, output, session) {
# load xts sample data
data("sample_matrix")
sample.xts <- as.xts(sample_matrix)
output$graphs <- renderUI({
plot_output_list <- lapply(1:3, function(i) {
dygraphOutput(paste0('div_graph_', i))
})
})
output$legends <- renderUI({
legend_output_list <- lapply(1:3, function(i) {
htmlOutput(paste0("div_legende",i), height = "400px")
})
})
# do the plotting
lapply(1:3, function(i) {
output[[paste0('div_graph_', i)]] <- renderDygraph({
dygraph(sample.xts[,i],main=i)%>%
dyLegend(labelsDiv = paste0("div_legende",i), show = "always")
})
})
})
This leads to this plot, where you can see the legends of all three plots are directly pasted together. I want them to be right of their respective plot.
I got it.
Creating a plotOutput and an empty plot does the trick:
Ui stays the same.
Server:
library(dygraphs)
library(xts)
shinyServer(function(input, output, session) {
data("sample_matrix")
sample.xts <- as.xts(sample_matrix)
output$graphs <- renderUI({
plot_output_list <- lapply(1:3, function(i) {
dygraphOutput(paste0('div_graph_', i))
})
})
output$legends <- renderUI({
legend_output_list <- lapply(1:3, function(i) {
plotOutput(paste0("div_legende",i), height = "400px")
})
})
lapply(1:3, function(i) {
output[[paste("div_legende",i)]] <- renderPlot(
plot(1,1,type="n",xaxt="n",yaxt="n",ylab="",xlab="",bty="n"),
height = "400px"
)
output[[paste0('div_graph_', i)]] <- renderDygraph({
dygraph(sample.xts[,i],main=i)%>%
dyLegend(labelsDiv = paste0("div_legende",i),
show = "always")
})
})
})
Related
I have the data frame below:
Name<-c("John","Bob","Jack")
Number<-c(3,3,5)
NN<-data.frame(Name,Number)
And a simple shiny app which creates a plotly histogram out of it. My goal is to click on a bar of the histogram and display the Name in a datatable that correspond to this bar. For example if I click on the first bar which is 3 I will take a table with John and Bob names.
library(plotly)
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
plotlyOutput("heat")
),
DT::dataTableOutput('tbl4')
)
server <- function(input, output, session) {
output$heat <- renderPlotly({
p <- plot_ly(x = NN$Number, type = "histogram")
})
output$tbl4 <- renderDataTable({
s <- event_data("plotly_click")
if (length(s) == 0) {
"Click on a bar in the histogram to see its values"
} else {
NN[ which(NN$Number==as.numeric(s[2])), 1]
}
})
}
shinyApp(ui, server)
I am adding the solution by modifying your data.frame as mentioned in the comment:
library(plotly)
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
plotlyOutput("heat")
),
DT::dataTableOutput('tbl4')
)
server <- function(input, output, session) {
output$heat <- renderPlotly({
Name<-c("John","Bob","Jack")
Number<-c(3,3,5)
Count<-c(2,2,1)
NN<-data.frame(Name,Number,Count)
render_value(NN) # You need function otherwise data.frame NN is not visible
p <- plot_ly(x = NN$Number, type = "histogram",source="subset") # set source so
# that you can get values from source using click_event
})
render_value=function(NN){
output$tbl4 <- renderDataTable({
s <- event_data("plotly_click",source = "subset")
print(s)
return(DT::datatable(NN[NN$Count==s$y,]))
})
}
}
shinyApp(ui, server)
Screenshot from solution:
for (i in 1:4){
v <- rnorm(50)
plot(v, main=paste("Iteration ", i))
}
I have code that iterates through and produces a plot each time, like the above. How would I allow a user to click to see the next plot in a Shiny application?
Use the slickR package to make a nice slideshow.
library(shiny)
library(slickR)
library(svglite)
plots <- lapply(1:5, function(i){
xmlSVG({plot(rnorm(50), main=paste0("Iteration ", i))}, standalone = TRUE)
})
#make the plot self contained SVG to pass into slickR
plotsAsSVG <- sapply(plots, function(sv){
paste0("data:image/svg+xml;utf8,",as.character(sv))
})
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
####
),
mainPanel(
slickROutput("slickr", width="500px")
)
)
)
server <- function(input, output) {
output$slickr <- renderSlickR({
imgs <- plotsAsSVG
slickR(imgs)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I'm using R shiny would like to put several ggplotly plots side by side with the help of gridExtra.
One plot (without gridExtra) works just fine:
library(shiny)
library(plotly)
u <- fluidPage(plotlyOutput(outputId = "myplots"))
s <- function(input, output) {
pt1 <- reactive({
ggplotly(qplot(42))
})
output$myplots <- renderPlotly({
pt1()
})
}
shinyApp(u, s)
Now when I try to add one more plot via gridExtra, it refused to work:
library(shiny)
library(plotly)
library(gridExtra)
u <- fluidPage(plotlyOutput(
outputId = "myplots"
))
s <- function(input, output){
pt1 <- reactive({
ggplotly(qplot(42))
})
pt2 <- reactive({
ggplotly(qplot(57))
})
output$myplots <- renderPlotly({
grid.arrange(pt1(), pt2(),
widths = c(1, 1),
ncol = 2)
})
}
shinyApp(u, s)
giving me
Error in gList: only 'grobs' allowed in "gList"
Rather than using grid.arrange to pass many plots to a single plotlyOutput, it would be better to set up your ui to accept several plots and then pass them individually. For example, your ui and server could look like this
Note that defining columns like this uses Bootstrap theming, which means the widths need to add to 12. Thats why I've defined each column to have a width of 6 - each will naturally fill half the page
library(shiny)
library(plotly)
library(gridExtra)
u <- fluidPage(
fluidRow(
column(6,
plotlyOutput("pt1")),
column(6,
plotlyOutput("pt2"))
)
)
s <- function(input, output){
output$pt1 <- renderPlotly({
ggplotly(qplot(42))
})
output$pt2 <- renderPlotly({
ggplotly(qplot(57))
})
}
shinyApp(u, s)
I'm creating Shiny app and I want to use checkboxGroupInput in order to print out multiple plots. However, I want to print out plots only for the elements of checkboxGroupInput that were checked. There is a similar example in Shiny gallery to create UI elements in a loop that uses lapply. Here is a simplified version of that example to show what I want to do:
#server.R
library(shiny)
library(ggplot2)
shinyServer(function(input, output, session) {
numberInput <- reactive({
input$checkbox
})
lapply(1:10, function(i) {
output[[paste0('b', i)]] <- renderPlot({
qplot(x = rnorm(100, mean = as.numeric(numberInput()[i]))) +
ggtitle(paste("This plot was plotted with", numberInput()[i], "option"))
})
})
})
#ui.R
library(shiny)
shinyUI(fluidPage(
title = 'lapply example',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkbox", "Checkbox",
choices = sample(1:10, 5))
),
mainPanel(
lapply(1:10, function(i) {
plotOutput(paste0('b', i))
})
)
)
))
This works, but obviously when Shiny tries to extract numberInput()[i] where i is bigger than number of currently checked elements, there is nothing to extract and instead of a plot there is an error. Therefore I need to somehow tell lapply to iterate only n number of times where n is length(input$checkbox).
I tried to use length(input$checkbox) directly, tried putting that element in the numberInput() reactive statement and returning it as the list, I tried to use reactiveValues() in a following way:
v <- reactiveValues(n = length(input$checkbox))
lapply(1:isolate(v$n), function(i) {
However, in all of those instances Shiny complains about lack of active reactive context.
So, what am I missing? How can I use length of input in lapply outside of reactive context?
I've generally had more luck using this approach (only because it's easier for me to wrap my head around it), but the idea is to render your plots into a UI on the server and then render the UI in ui.R
#server.R
library(shiny)
library(ggplot2)
server <- shinyServer(function(input, output, session) {
output$checks <- renderText(input$checkbox)
output$plots <- renderUI({
plot_output_list <-
lapply(input$checkbox,
function(i){
plotOutput(paste0("plot", i))
})
do.call(tagList, plot_output_list)
})
observe({
for (i in input$checkbox) {
local({
local_i <- i
output[[paste0("plot", local_i)]] <-
renderPlot({
qplot(x = rnorm(100, mean = as.numeric(local_i))) +
ggtitle(paste("This plot was plotted with", local_i, "option"))
})
})
}
})
})
#ui.R
library(shiny)
ui <- shinyUI(fluidPage(
title = 'lapply example',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkbox", "Checkbox",
choices = sample(1:10, 5))
),
mainPanel(
verbatimTextOutput("checks"),
uiOutput('plots')
)
)
))
shinyApp(ui = ui, server = server)
I am trying to create an app using Shiny, where I want the user to be able to select the color of each line in a plot. The general idea is to import the data in the app and then plot each variable in the data. I tried to use the colorpicker 'jscolorInput' from the shinysky package, which works fine when placed in the ui.r file, but since I want my app to be dynamic for each dataset uploaded, I need to put the colorpicker in the server.R, using a reactive function.
When placed in the server, the 'jscolorInput' does not work.
What I want to do is:
Reproduce the colorpicker as many times as the number of
variables in the data
Take the input from the color and pass it
as color argument in the plot
I am very new in both shiny development and stackoverflow, so please excuse my mistakes.
Here is a reproducible example that does not work.
require(shinysky)
require(shiny)
dat <- data.frame(matrix(rnorm(120, 2, 3), ncol=3))
runApp(list(
ui = bootstrapPage(
# The reactive colorpicker
uiOutput('myPanel'),
# The plot
plotOutput('plot')
),
server = function(input, output) {
# Print as many colorpickers as the columns in the dataset
cols <- reactive({
n <- ncol(dat)
for(i in 1:n){
print(jscolorInput(paste("col", i, sep="_")))
}
})
output$myPanel <- renderPrint({cols()})
# Put all the input in a vector
colors <- reactive({
n <- ncol(dat)
lapply(1:n, function(i) {
input[[paste("col", i, sep="_")]]
})
})
output$plot <- renderPlot({
cols <- ifelse(is.null(input$col_1), rep("000000 ", n), colors())
plot(dat[,1], col= paste0("#", cols[1], ""))
for(i in 2:ncol(dat))lines(dat[,i], col=cols[i])
})
}
))
Here is a working version of what you are trying to do. Look at the differences between our code, there were a few problems with your code. Also, note that I'm not using shinysky because it doesn't have the colourpicker anymore (it's moved to a different package that's inactive), so instead I'm using the inputColour from shinyjs.
library(shiny)
library(shinyjs)
dat <- data.frame(matrix(rnorm(120, 2, 3), ncol=3))
runApp(shinyApp(
ui = fluidPage(
uiOutput('myPanel'),
plotOutput("plot")
),
server = function(input, output, session) {
cols <- reactive({
lapply(seq_along(dat), function(i) {
colourInput(paste("col", i, sep="_"), "Choose colour:", "black")
})
})
output$myPanel <- renderUI({cols()})
# Put all the input in a vector
colors <- reactive({
lapply(seq_along(dat), function(i) {
input[[paste("col", i, sep="_")]]
})
})
output$plot <- renderPlot({
if (is.null(input$col_1)) {
cols <- rep("#000000", ncol(dat))
} else {
cols <- unlist(colors())
}
plot(dat[,1], col = cols[1])
for(i in 2:ncol(dat)) lines(dat[,i], col = cols[i])
})
}
))
Disclaimer: I'm the author of shinyjs