Shiny -- 'ggsave' printed plots looks different from plot displayed in shiny app - r

I've made a cool Shiny app that allows users to resize their plot and move it around on the canvas.
The problem is, sometimes the downloaded plot looks very different from what is displayed in the view panel - it comes out as a square graphic that might have the plot cut off if as you change the scale and positioning.
If you draw it at 0.5 scale it looks the same in browser and in the downloaded png. -- But if you make it to scale 0.8, or even simply scale 1.0, it'll look perfect in the view panel but ugly and misaligned when printed with ggsave().
Perhaps this is a question about adjusting parameters to ggsave? Or is it that my background image is sized strangely (1920 x 1028 pixels)? Or perhaps the reactive panel throws it off somehow...? I'm confused at this point.
Anyway I drew a grey background behind the plot area so that you can easy tell the canvas from the panel.
And here is a minimal reproducible example.
library(shiny)
library(tidyverse)
library(magick)
library(cowplot)
picture <- 'https://content.halocdn.com/media/Default/games/halo-5-guardians/map-images/arena/arena_maps_sustain_array04-bddc574f8e3445d08f24ef859fb96941.jpg'
background <- image_read(picture)
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
sliderInput("xposition",
"X-Axis Position",
min = -1,
max = 1,
value = 0,
step = 0.1),
sliderInput("yposition",
"Y-Axis Position",
min = -1,
max = 1,
value = 0,
step = 0.1),
sliderInput("scale",
"Chart Scale",
min = .25,
max = 1,
value = .5,
step = 0.1)),
# Show a plot over grey background to see how canvas shifts
mainPanel(style = "background:grey",
plotOutput("distPlot"),
downloadButton("downloadPlot", "Download")
)
)
)
server <- function(input, output) {
xposition <- reactive({input$xposition})
yposition <- reactive({input$yposition})
scale <- reactive({input$scale})
plot <- reactive({
base_plot <- ggplot(faithful, aes(x = waiting), bins = input$bin) +
geom_histogram()
ggdraw() +
draw_image(background) +
draw_plot(base_plot, x = xposition(), y = yposition(), scale = scale())
})
## Use Cowplot package to alter scale and position
output$distPlot <- renderPlot({
# draw the histogram with the specified number of bins
plot()
})
output$downloadPlot <- downloadHandler(
filename = "my_awesome_plot.png",
content = function(file) {
ggsave(file, plot())
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
If you try scale 0.5, scale 0.8, and scale 1.0, you'll see what I mean!
Any help????
Note: The reproducible example above should be enough to find the problem, but if you want to see a more "in depth" example here is my real app which has more images in it and more reactive scalings and positionings.
https://jjohn987.shinyapps.io/Halo5_Stats/
Type in a player's tag name (e.g., these work => hi, k, hairball9000, or other gamer tags people use, but no commas or spaces) and you'll see that what appears to be "the bottom" of the plot is very different than what the bottom is in the download... Keep in mind I haven't applied for any significant api bandwidth from Microsoft so the calls might get timed out depending on how many people try to help!

Related

Interactive plot updated during sliding in R

I need an interactive plot with two sliders, and I want the plot to be updated smoothly as I slide, without releasing mouse button. Do you know of any other solution in R which is able to do that?
I found a nice library manipulate (see example below), but the plot is only updated after releasing mouse button, so I have to stop sliding to see updated plot (which makes the experience not smooth). I want the plot to be updated during sliding already.
library(manipulate)
manipulate(curve(amp*sin(freq*x), xlim = c(0,10), ylim =c(-1,1)), amp = slider(0.1,1), freq = slider(0.1,10))
PS: I am used to just base graphics library, so I prefer simple solutions using that; but if there's no other way, ggplot/lattice is good as well :-))
I appreciate you were looking for a answer in base R, however shiny is typically a good choice for interactive plots in R.
The following code should achieve your desired output (and you do not have to release the mouse button for the plot to update):
library(shiny)
ui <- fluidPage(
# Sidebar with a slider input
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "amp",
label = "Amp:",
min = 0.1,
max = 1,
value = 0.5
),
sliderInput(
inputId = "freq",
label = "Freq:",
min = 0.1,
max = 10,
value = 0.5
)
),
# Show a plot
mainPanel(
plotOutput("plot")
)
)
)
# Define server logic
server <- function(input, output) {
output$plot <- renderPlot({
amp <- input$amp
freq <- input$freq
curve(amp * sin(freq * x), xlim = c(0, 10), ylim = c(-1, 1))
})
}
# Run the application
shinyApp(ui = ui, server = server)

r shiny hover with multi-panel plots

Hi I have a shiny app that helps a user visualize electropherograms (essentially spectra) that come from automated DNA sequencers. I would like the user to be able to hover over a peak and find out the time the peak came off the instrument. I also would like to be able to compare multiple electropherograms by plotting the spectra one above the other.
If I plot a single spectrum, I can recover the mouse x-position with the 'hover' option provided to plotOutput() in the ui. If I stack plots using base graphics and the par(mfrow=c(n,1)) where n is the number of spectra, the results are unpredictable. Basically the x-position is recoverable in part of the plot region but not throughout.
As a final piece of information, I wrote this app several years ago and it's been working as expected until I updated R and the shiny package to fairly recent versions: (R 3.4.4; shiny 1.2.0).
I've included an app.R file that reproduces this issue on a simple case using histograms and the old-faithful data and shows the approach I have been taking. I get the same behavior whether I set 'clip' in hoverOpts to TRUE or FALSE.
Thanks for any help
allan
library(shiny)
ui = fluidPage(
titlePanel("Mulitpanel hover: Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
sliderInput("bins","Number of bins:",min = 1,max = 50,value = 30)
),
mainPanel(
textOutput("xpos"),
plotOutput("distPlot",hover=hoverOpts(id="plot_hover",clip=TRUE))
)
)
)
server = function(input, output) {
output$xpos = renderText({paste("x coord:",input$plot_hover$x)})
output$distPlot = renderPlot({
x = faithful[, 2]
bins = seq(min(x), max(x), length.out = input$bins + 1)
par(mfrow=c(2,1))
hist(x, breaks = bins, col = 'darkgray', border = 'white')
hist(x, breaks = bins, col = 'orange', border = 'white')
})
}
shinyApp(ui = ui, server = server)

Stop R Shiny animation dead

My Shiny animation app needs to do lot of plotting. I can change the animation interval for the plot using a control slider.
This reproducible example plots and animates random points :
library(shiny)
library(ggplot2)
library(dplyr)
df <- data.frame(frame_no = rep(1:500,10), x = runif(5000), y=runif(5000))
ui <- fluidPage(
sliderInput("frameInterval", "Set frame Interval (ms.)", min=0,
max=2000, value=100,step=50),
uiOutput("frameSlider"),
plotOutput("plot.positions", height=500, width = 500)
)
server <- function(input, output, session) {
output$frameSlider <- renderUI( sliderInput("frame", label= h3("Animate"),
min=1, max = 5000,
value=1, step=1, sep=NULL,
animate = animationOptions(interval = input$frameInterval, loop =
TRUE))
)
output$plot.positions <- renderPlot( {
f <- filter(df, frame_no==input$frame)
ggplot(f, aes(x=x, y=y)) + xlim(0,1) + ylim(0,1) +
geom_point(aes(size=3)) +
annotate("text", x= 0.5, y= 0.5, label=paste("Frame ", input$frame),
size=6)
},
bg="transparent"
)
}
shinyApp(ui = ui, server = server)
The problem I have is that when the animation interval is low (say 100ms) the plot lags behind the slider, as shown by comparing the slider value and the plotted frame number.
Then, if I want to pause the animation by clicking the slider pause, the display continues running until it catches up again with the slider value.
Is there any way to stop the animation instantly, even if it is lagging behind?
Thanks for any advice.

Plotly 'Download as png' resizing image when used in RShiny application

I'm using plotly package for R to display some large graphs in my shiny application, the graphs display nicely as intended. However, when I click the "Download as png" button, the downloaded .png has been resized. here is a demo of this behavior.
How can I specify the resolution for the exported plot?
Here is a minimal example that demonstates the issue by having a really long title that gets clipped on download
app.R
library(shiny)
library(ggplot2)
library(plotly)
ui <- fluidPage(titlePanel("Plotly Download demo"),
plotlyOutput("demoPlotly"))
server <- function(input, output) {
output$demoPlotly <- renderPlotly({
#make an arbritrary graph with a long title
p <- iris %>%
ggplot(aes(x = Petal.Length, y = Petal.Width, color = Species)) +
geom_point() +
labs(title = "This is a really long title to demo my problem of plotly resizing my downloaded output")
ggplotly(p)
})
}
# Run the application
shinyApp(ui = ui, server = server)
The downloaded graph looks like this:
Michael,
Take a look at plotly_IMAGE(). [Version 4.7.1]. The function does allow you to specify width and height for the saved image. For example 800 by 600.
Regarding your example, I have provided an example of how you might modify the layout to accommodate the title wrapping. Note the changes the margin and padding. I also inserted a call to plotly_IMAGE to provide a simplified example of its usage - it slows things down as it is in line with the plot generation. I'd recommend you add a button and separate out from image display.
Hope this helps
Take care
T.
Pre-requisite - Plotly Credentials:
To use this plotly example you need to register and obtain a plotly api key. Once obtained you need add these entries to your ~/.Renviron file.
Ref: https://plot.ly/r/getting-started/
Example .Renviron file entries
plotly_username="xxxxx"
plotly_api_key="xxxxxx"
Example Code
library(shiny)
library(ggplot2)
library(plotly)
wrapper <- function(x, ...) {
paste(strwrap(x, ...), collapse = "\n")
}
# Example usage wrapper(my_title, width = 20)
my_title <- "This is a really long title to demo my problem of plotly resizing my downloaded output"
ui <- fluidPage(titlePanel("Plotly Download demo"), plotlyOutput("demoPlotly"))
pal <- c("blue", "red", "green")
pal <- setNames(pal, c("virginica", "setosa", "versicolor"))
layout <- list(title = wrapper(my_title, width = 60),
xaxis = list(title = "Petal Length"),
yaxis = list(title = "Petal Width"),
margin = list(l = 50, r = 50, b = 100, t = 100, pad = 4))
server <- function(input, output) {
output$demoPlotly <- renderPlotly({
# make an arbitrary graph with a long title
p <- iris %>%
plot_ly(x = ~Sepal.Length,
y = ~Petal.Width,
color = ~Species,
colors = pal,
mode = "markers",
type = "scatter") %>%
layout(autosize = F,
title = layout$title,
xaxis = layout$xaxis,
yaxis = layout$yaxis,
margin = layout$margin)
p$elementId <- NULL
# Example usage of plotly image
plotly_IMAGE(p,
width = 800,
height = 600,
format = "png",
scale = 1,
out_file = "output.png")
p
})
}
# Run the application
shinyApp(ui = ui, server = server)
Screen Shot Image - screen capture
Plotly save - screen click
plotly_IMAGE() save image output 800 by 600

Shiny with animated scatterplot in R base graphics or ggplot possible?

I've been working on an animated slider project in Shiny and I nearly have what I am after, but not quite. Instead of displaying each successive graph in the animated sequence, it appears to be showing the graph with all of the data (non sequenced). I'm not quite sure where my error is but I suspect it is in the reactive function call or the renderPlot function call in the server section. I've tried searching the Web and I've tried placing different code blocks in different locations but I can not seem to get the animation to work in Shiny. Eventually I want to change the numeric months (1,2,3...) to date objects for more clarity but I'll tackle that one after the animation is working.
Please note - I HAVE been able to get a successful motion chart for this data using googleVic, gvisMotionChart and Shiny but I found with that approach I do not have control of the bubble chart colors or the bubble size (I want a constant size that is much smaller than google's bubble chart default size due to overlap). So, I am hoping to accomplish this animation with R's base graphics or with ggplot.
Here is small set of data to represent what I am using:
d1 <- data.table( id = 1:21,
Region = rep(c("R1","R2","R3"), each=7),
Month = 1,
Spend = round(runif(21,100,500)),
Age = round(runif(21,72,100)),
Color = rep(c("#E69F00","#D55E00","#009E73"),each=7))
d2 <- copy(d1)
d2[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
d3 <- copy(d2)
d3[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
d4 <- copy(d3)
d4[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
d5 <- copy(d4)
d5[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
d6 <- copy(d5)
d6[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
d7 <- copy(d6)
d7[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
d8 <- copy(d7)
d8[,Month:=Month+1][,Age:=Age+1][,Spend:=Spend+round(runif(21,0,4))]
dat <- rbindlist(list(d1,d2,d3,d4,d5,d6,d7,d8))
Here is an animated GIF to show what the animation would look like with base graphics
saveGIF({
for(i in 1:8){
plot(dat[Month==i,Age],dat[Month==i,Spend],col=dat[Month==i,Color],
pch=16, xlim=c(min(dat$Age)*.95,max(dat$Age)*1.1),
ylim=c(min(dat$Spend)*.95,max(dat$Spend)*1.1),
xlab="Age in Months",ylab="Dollars", las=1, cex.axis=.7)
legend("topright",inset=.05,c("Reg 1","Reg 2","Reg 3"),
pch=16,col=c("#E69F00","#D55E00","#009E73"),
cex=.8)
ani.pause()
}
}, interval = 0.25, ani.width = 750, ani.height = 550)
This is my current nonworking Shiny code
library(shiny)
library(ggplot2)
# Shiny app with slider and animation
# ui section
ui = fluidPage(
# Title
titlePanel("Spend vs Age by Region"),
# Sidebar with slider and controls for animation
sidebarLayout(
# sidebar with slider
sidebarPanel(
# Slider with looping
sliderInput("theMonth", "Month", 1, 8, 1, step = 1,
animate=animationOptions(interval=1000, loop = T,
playButton = T, pauseButton = T))
),
# Show the animated graph
mainPanel(
plotOutput(outputId="case_age_plot")
)
)
)
# server section
server = function(input, output) {
# Reactive expression to create data frame and graph
aniGraph <- reactive({
# subset the data frame into the portion that has the data for the
# graph in the animation sequence
dat[Month==input$theMonth,]
# create the graph
plot(dat[,Age],dat[,Spend],col=dat[,Color],
pch=16, xlim=c(min(dat$Age)*.95,max(dat$Age)*1.1),
ylim=c(min(dat$Spend)*.95,max(dat$Spend)*1.1),
xlab="Age in Months",ylab="Dollars", las=1, cex.axis=.7)
legend("topright",inset=.05,c("Reg 1","Reg 2","Reg 3"),
pch=16,col=c("#E69F00","#D55E00","#009E73"),cex=.8)
})
# Show the graph
output$case_age_plot <- renderPlot({
aniGraph()
})
}
# run the app
runApp(list(ui = ui, server = server))
If anyone has a solution or thoughts I would be grateful.
the problem was that you didn't save the subset of dat. I slightly modified your code to get the same animation as in gif animation.
library(shiny)
library(ggplot2)
# Shiny app with slider and animation
# ui section
ui = fluidPage(
# Title
titlePanel("Spend vs Age by Region"),
# Sidebar with slider and controls for animation
sidebarLayout(
# sidebar with slider
sidebarPanel(
# Slider with looping
sliderInput("theMonth", "Month", 1, 8, 1, step = 1,
animate=animationOptions(interval=1000, loop = T,
playButton = T, pauseButton = T))
),
# Show the animated graph
mainPanel(
plotOutput(outputId="case_age_plot")
)
)
)
# server section
server = function(input, output) {
# Reactive expression to create data frame and graph
aniGraph <- reactive({
# subset the data frame into the portion that has the data for the
# graph in the animation sequence
# Save subset of 'dat' and pass it to the plot
dat_sub <- dat[Month==input$theMonth,]
# create the graph
plot(dat_sub[,Age],dat_sub[,Spend],col=dat_sub[,Color],
pch=16, xlim=c(min(dat$Age)*.95,max(dat$Age)*1.1),
ylim=c(min(dat$Spend)*.95,max(dat$Spend)*1.1),
xlab="Age in Months",ylab="Dollars", las=1, cex.axis=.7)
legend("topright",inset=.05,c("Reg 1","Reg 2","Reg 3"),
pch=16,col=c("#E69F00","#D55E00","#009E73"),cex=.8)
})
# Show the graph
output$case_age_plot <- renderPlot({
aniGraph()
})
}
# run the app
runApp(list(ui = ui, server = server))
I wanted my graph to just animate without a slider. With a duckduck search I found Dan Mohamed's article at https://nhsrcommunity.com/animating-a-graph-over-time-in-shiny/ which pointed me to reactiveTimer(). Here is a condensed version of my implementation.
propagator <- 0
ui <- fluidPage( . . . )
server <- function(input, output) {
autoInvalidate <- reactiveTimer()
observe( {autoInvalidate()} )
output$orb <- renderPlot({
autoInvalidate()
propagator <<- propagator + 1
. . .
})
}

Resources