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)
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:
I am building an RShiny-app where I am creating a plot based on a data table which I can edit and another data table which I cannot. I eventually want to save all data points on the plot in a data table which I can display and export.
I have seen many ways to do this using ggplot (ie layer_data, ggplot_build), but no efficient ways when just using plot and lines. My plots will be getting quite complicated so it would be really helpful to find an easy way to do this rather than hardcoding everything in.
A very simple example of my code is below (Note: plots will be getting much more complicated than this. They will be line graphs, but I will just need the y values at each x value marked with a number on the x axis):
x <- data.frame('col_1' = c(1,2,3,4,5), 'col_2' = c(4,5,6,7,8))
y <- data.frame('col_1' = c(5,4,3,6,7), 'col_2' = c(1,2,3,4,5))
#import necessary libraries
library(shiny)
library(DT)
library(shinythemes)
library(rhandsontable)
#ui
ui <- fluidPage(theme = shinytheme("flatly"),
titlePanel("Test"),
sidebarLayout(
sidebarPanel(
#display data
rHandsontableOutput('contents'),
#update plot button
actionButton("go", "Plot Update"),
width=4
),
mainPanel(
tabsetPanel(
#plot
tabPanel("Plot", plotOutput("plot_1")) )
))
)
#server
server <- function(input, output, session) {
#data table
output$table_b <- renderTable(x)
indat <- reactiveValues(data=y)
observe({
if(!is.null(input$contents))
indat$data <- hot_to_r(input$contents)
})
output$contents <- renderRHandsontable({
rhandsontable(indat$data)
})
#save updated data
test <- eventReactive(input$go, {
live_data = hot_to_r(input$contents)
return(live_data)
})
#plot
output$plot_1 <- renderPlot({
plot(x[,1],x[,2],col='red',type = 'l')
lines(test()[,1],x[,2], col='black', type='l')
# need a way to grab data from plot a create a table
})
}
shinyApp(ui, server)
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")
})
})
})
I have a plotly plot in R Shiny. I want to be able to click many points and have them displayed in a table. The plot is working great and I can get 1 plotly_click (via event_data()) to show in a table. How can a grow a vector of many event_data points. Here is some sample code. I was trying to save the event in d_save. Thanks.
library(shiny)
library(plotly)
data1 <- data.frame(cbind(seq(1,1000,1),seq(1,1000,1)*5))
colnames(data1) <- c('index','data')
data_points <- data.frame(cbind(seq(1,1000,5),seq(1,1000,5)*5))
colnames(data_points) <- c('index','data')
ui <- fluidPage(
plotlyOutput("plot1"),
tableOutput("dataTable")
)
d_save <- vector()
server <- function(input, output, session) {
# make plotly plot
output$plot1 <- renderPlotly({
p <- plot_ly(data1, x = data1$index, y = data1$data,mode = "lines")
add_trace(p, x = data_points$index, y = data_points$data, mode = "markers")
})
# show table of stances
output$dataTable <- renderTable({
d <- event_data("plotly_click")
d_save <- c(d_save,d$pointNumber[2]+1)
data.frame(d_save)
})
}
shinyApp(ui, server)
There is nothing seriously wrong with this and it was weird that it never got answered. It is not a bad example of pure plotly (without using ggplot).
I fixed it by:
changing the d_save <- c(...) assignment to a d_save <<- c(...) (using a reactiveValues here would be cleaner).
changing the plotly call to be a pipe, which seemingly allows some settings to carry over (like the type=scatter default) - eliminating the warning:
No trace type specified: Based on info supplied, a 'scatter' trace
seems appropriate.
fixed an "off-by-one" indexing error in the d_save assignment.
added a layout(...) to give it a title (this is useful for a lot of things).
The resulting code:
library(shiny)
library(plotly)
data1 <- data.frame(cbind(seq(1,1000,1),seq(1,1000,1)*5))
colnames(data1) <- c('index','data')
data_points <- data.frame(cbind(seq(1,1000,5),seq(1,1000,5)*5))
colnames(data_points) <- c('index','data')
ui <- fluidPage(
plotlyOutput("plot1"),
tableOutput("dataTable")
)
d_save <- vector()
server <- function(input, output, session) {
# make plotly plot
output$plot1 <- renderPlotly({
plot_ly(data1, x=data1$index, y=data1$data,mode = "lines") %>%
add_trace(x = data_points$index, y=data_points$data, mode = "markers") %>%
layout(title="Plotly_click Test")
})
# show table of point markers clicked on by number
output$dataTable <- renderTable({
d <- event_data("plotly_click")
d_save <<- c(d_save,d$pointNumber[1]+1)
data.frame(d_save)
})
}
shinyApp(ui, server)
The image:
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)