R markdown with rotating gallery-like slides for each plot? - r

So for example suppose I have these three plots
p1 <- iris%>%ggplot(aes(x=Sepal.Length,y=Sepal.Width))+geom_point()
p2 <- iris%>%ggplot(aes(x=Sepal.Length,y=Sepal.Width))+geom_bar(stat="identity", width = 1, fill="#98ff98")
p3 <- iris%>%ggplot(aes(x=Species,y=Sepal.Width))+geom_bar(stat="identity", width = 1, fill="blue")
So instead printing each plot out separately in the html markdown so that the user has to scroll down to view each of the figures, is there a way to output some sort of ui where the left hand side is the plot and right hand side are the selection for the plots. Then the user can simply select which plot to view and it will appear on the left. Is this possible? The reason why I ask is because often I can have 10-20 figures per comparison that can get unwieldy very fast and I think this would be an excellent way to organize them.
thanks!

Maybe something like this can get you started
library(shiny)
# create a list of plots
plots <- list(
p1 = iris%>%ggplot(aes(x=Sepal.Length,y=Sepal.Width))+geom_point(),
p2 = iris%>%ggplot(aes(x=Sepal.Length,y=Sepal.Width))+geom_bar(stat="identity", width = 1, fill="#98ff98"),
p3 = iris%>%ggplot(aes(x=Species,y=Sepal.Width))+geom_bar(stat="identity", width = 1, fill="blue")
)
# put names of plots in a list in sidebar
ui <- fluidPage(sidebarLayout(
sidebarPanel(tags$ul(purrr::map(names(plots), ~tags$li(actionLink(paste0("show", .), .))))),
mainPanel(plotOutput("currentplot"))
))
server <- function(input, output, session) {
# draw the first plot by default
current_plot <- reactiveVal(plots[[1]]);
# set up observers for each of the action links in the UI
purrr::map(names(plots), function(p) {
observeEvent(input[[paste0("show",p)]], {
# set current plot
current_plot(plots[[p]])
})
})
# render whatever the current plot is
output$currentplot <- renderPlot(current_plot())
}
shinyApp(ui, server)
This will give you a list of plots on the left and will draw which ever you click on on the right.
you could probably make this more efficient if you wanted to write some javascript, but this at least gives a basic idea of how it might work.

Related

Shiny Interactive Graph plot showing row names

I am using Shiny and ggplot2 for an interactive graph. I also used "plot1_click" to get x and y locations.
output$selected <- renderText({
paste0("Value=", input$plot1_click$x, "\n",
"Names=", input$plot1_click$y) }) #how to get names???
This is part of server code. Here what I want is, instead of printing the "y" coordinates, I want to print the corresponding name written in y-axis. Is there any possible way for it??
As far as I know clicking points is not supported in plotOutput. Click events will only return coordinates of the click location. Those coordinates can however be used to figure out the nearest point.
This shiny app from the shiny gallery pages uses the function shiny::nearPoints which does exactly that. Here is a minimal example.
library(shiny)
library(ggplot2)
shinyApp(
fluidPage(
plotOutput("plot", click = "plot_click"),
verbatimTextOutput('print')
),
server = function(input, output, session){
output$plot <- renderPlot({ggplot(mtcars, aes(wt, mpg)) + geom_point()})
output$print = renderPrint({
nearPoints(
mtcars, # the plotting data
input$plot_click, # input variable to get the x/y coordinates from
maxpoints = 1, # only show the single nearest point
threshold = 1000 # basically a search radius. set this big enough
# to show at least one point per click
)
})
}
)
The verbatimTextOutput shows you the nearest point to the clicked location. Notice that nearPoints only works with ggplots like that. But the help page suggests that there is also a way to use this with base graphics.

Passing outputId into ggplotly()

I've just started at an internship where my first task is to improve a Shiny application by updating all existing ggplot graphs into Plotly graphs. I'm pretty new to R and Shiny, so I'm having a couple of issues getting started.
Currently, one of the graphs in this application are generated as follows:
output$barBoxPlot <- renderUI({
plotOutput("Barplot", width = 700, height = 300
, click = "plot_click"
, hover = "plot_hover"
, brush = "plot_brush")
})
Where "Barplot" is an outputId that corresponds to a ggplot graph generated in another part of the code.
I'm trying to plot this graph ("Barplot") as a Plotly graph, using the ggplotly() function, using something like this:
output$barBoxPlot <- renderPlotly({
ggplotly(<insert graph here>)
})
However, I cannot figure out how to pass in the "Barplot" parameter (which is an outputId) into ggplotly(). ggplotly takes in an instance of a ggplot graph, and not an outputId.
Is there any way to pass in an outputId into ggplotly()?
Thank you!
Would've put this in a comment but can't to that yet because of reputation:
There are multiple ways to solve this wihtout trying to pass an outputID to ggplotly().
In the reactive that is in the output$Barplot <- renderPlot(this_reactive_here()) , use ggplotly() directly (if you don't need the original output$barBoxPlot
Create a function or a reactive that takes a ggplot object and only does return(ggplotly(x)) , then put that into your renderPlotly()
EDIT for clarification on 2:
The Barplot that is in plotOutput should have a server entry that looks something like this:
output$Barplot <- renderPlot({
ggplot(data, aes(...)) # etc
})
Take the code inside and put it in a reactive, like this:
barplot_ggplot <- reactive({
ggplot(data, aes(...)) # etc
})
Then you can use it in the output$Barplot as barplot_ggplot() and in the renderPlotly() as ggplotly(barplot_ggplot()).
I hope that's a little bit clearer.

R Shiny application: Modifying plot without re-rendering it

I've been looking into ways to update a plot within an R Shiny application without having to re-render the whole plot. I'm working with temporal data which is animated via a Slider Input (animationOptions(playButton = TRUE)). The idea is to somehow highlight the part of the plot which is selected via the Slider Input. Re-rendering the whole plot at every animation step would make the whole application uselessly slow.
The most elegant solution with ggplot2 would have been, if shiny offered a way to add layers to the ggplot (e.g. + geom line()) and integrated this layer seamlessly into the plot without re-rendering it. Sadly, this does not seem to work. A bit of a hack could include creating a second ggplot-instance with exactly the same x/y-dimensions and overlapping the two plots.
EDIT:
I've just learnt that there are more javascript oriented plotting methods than ggplot2. For example, using dygraphs and adding a layer of dyShading, the selected area gets highlighted nicely. The basic question remains the same though, since changing the start- and end values of dyShading() seems to require re-rendering the whole plot.
library(shiny)
library(dygraphs)
library(xts)
data <- data.frame(
datetime = as.POSIXct("2016-06-20 17:00:00", tz = "UTC") + 1:100*60,
y = rnorm(100)
)
data_xts <- as.xts(data[,-1], data[,1])
minDatetime <- min(data$datetime)
maxDatetime <- max(data$datetime)
minY = min(data$y)
maxY = max(data$y)
plotlimits <- lims(x = c(minDatetime, maxDatetime), y = c(minY, maxY))
ui <- fluidPage(
sliderInput("timeslider", "Time Slider",
min = minDatetime,
max = maxDatetime,
value = c(minDatetime, minDatetime+10*60),
animate = animationOptions(interval=200)
),
dygraphOutput("dyplot")
)
server <- function(input, output) {
data_fil <- reactive({
data[data$datetime <= input$timeslider[2] & data$datetime >= input$timeslider[1],]
})
output$dyplot <- renderDygraph({
dygraph(data_xts) %>%
dyShading(
from = as.character(input$timeslider[1]),
to = as.character(input$timeslider[2]),
color = "tomato")
})
}
shinyApp(ui = ui, server = server)

Visualizing faceted data using interactive plotting in shiny

I'm having some trouble creating interactive plots in shiny that facet my data.
Here's some code that shows what I want, but it uses ggplot2 which is not interactive.
library(shiny)
library(data.table)
library(ggplot2)
x <- 1:10000
dt <- data.frame(a = 1:100, b = sample(x,100,replace=T), c = sample(x,100,replace=T), d = sample(x,100,replace=T))
dt.molten <- melt(dt,id.vars="a")
ui <- fluidPage(
plotOutput("gplot")
)
server = function(input, output) {
output$gplot <- renderPlot({
ggplot(y.molten,aes(x = value)) +
geom_histogram(binwidth = 100000) +
facet_wrap( ~ variable,ncol = 1)
})
}
shinyApp(ui = ui, server = server)
In my actual app, the amount of facets varies, so I can't simply hard code in 3 separate plots using highcharter, ggvis,or plotly.
Ideally I'd like it to look something like this:
require(highcharter)
x <- stl(log(AirPassengers), "per")
hchart(x)
Except with histograms instead of time-series data.
The main issue is that the data i'm plotting stretches over like -3,000,000 to +3,000,000 with a high concentration around 0. This makes it hard to see the bars at the edges, so I'd like for users to be able to zoom into certain ranges of the plot without having to select it via some ui element.
I'm open to suggestions using any plotting method in R, although i'd like to stay away from rCharts.
EDIT: I did discover that using plotly::ggplotly almost achieved what I'm looking for, however it isn't very clean. I did g <- ggplot(*plot code*) then ggplotly(g). Works decent, but it'll take quite a bit of work to clean up I think.

Create a number of renderPlot functions, based on the number of plots I have in a list of ggplots?

Is there any way I can dynamically create a number of renderPlot functions, based on the number of plots I have in a list of ggplots?
I have a Shiny app where instead of having a stable UI, and instead of using renderUI, I am relying on a user-supplied config file to tell Shiny how many plots to show. The config file also supplies data and pretty much helps do most of the heavy lifting.
After much battling, I'm mostly there. With the handy-dandy config file, I can build the correct UI, and generate the correct number of ggplots. The ggplots live in a list, creatively named list_of_ggplots.
But now, I'm at a point where I have a list of ggplots, and I need to allow them to be plotted by using them like this:
output$plot1 <- renderPlot({
print(list_of_ggplots[[1]])
})
But now I have an existentialist crisis -- I can't do it like this, since the user-supplied config file tells me how many plots I have. I can no longer hard code the renderPlot call like is usually done in Shiny, since the number of these functions needed is defined in the config file.
Given my list of ggplots, I need some way to generate the renderPlot calls.
Has anyone done this or have any ideas? Much appreciated.
Here's my code:
SERVER.R:
library(shiny)
library(ggplot2)
# 3 simple plots of different colors -- used here instead of all the complicated stuff
# where someone uses the config file that specified 3 plots, with data, etc.
ggplot_names <- c("p1", "p2", "p3")
ggplot_colors <- c("red", "blue", "green")
list_of_ggplots <- list()
j = 1
for (i in ggplot_names){
i <- ggplot(data.frame(x = c(-3, 3)))
i <- i + aes(x)
i <- i + stat_function(fun = dnorm, colour=ggplot_colors[[j]])
list_of_ggplots[[j]] <- i
j <- j+ 1
}
## here's the problem -- the user specified 3 plots.
## I can't hardcode the following shinyServer functions!!!
## What if tomorrow, the user specifies 2 plots instead?
shinyServer(function(input, output) {
output$plot1 <- renderPlot({
print(list_of_ggplots[[1]])
})
output$plot2 <- renderPlot({
print(list_of_ggplots[[2]])
})
output$plot3 <- renderPlot({
print(list_of_ggplots[[3]])
})
})
UI.R
## this top part is actually sourced from the config file
## since Shiny needs to know how many tabPages to use,
## names for the tabs, etc
number_of_tabPages <- 3
tab_names <- c("", "Tab1", "Tab2", "Tab3")
tabs<-list()
tabs[[1]]=""
for (i in 2:(number_of_tabPages+1)){
tabs[[i]]=tabPanel(tab_names[i],plotOutput(paste0("plot",i-1)))}
## Here's the familiar UI part
shinyUI(fluidRow(
column(12,
"",
do.call(navbarPage,tabs)
)
)
)
You can use this solution (I modified only the shinyServer part of your scripts, so I don't list the repeating code here):
shinyServer(function(input, output) {
observe(
lapply(seq(3),function(i) output[[paste0("plot",i)]] <- renderPlot(list_of_ggplots[[i]]))
)
})
Of course, you can replace 3 by a variable.

Resources