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

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
. . .
})
}

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)

Code for a reactive table to plot in Shiny

I'm new to shiny, and I am trying to do a simple density plot where there are 2 groups of data, with reactive 'shifts' in the mean etc.
A simplified summary of this is that one set of data has a mean of 0, and variance of 1. The second set of data has a mean of shift, which is defined in a slider.
I have tried to use reactiveValues, as shown in the code below to store the matrix of observations d1, generated from the density function y values, and the corresponding x values are stored in x.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("shift",
"shift of 2nd set",
min = -1,
max = 1,
value = 0)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
data <- reactiveValues({
d1 <- matrix(nrow=100, ncol=512)
for(i in 1:70){
d1[i,] <- density(rnorm(1000),from = -3, to = 3)$y
}
for(i in 71:100){
d1[i,] <- density(rnorm(1000, input$shift),from = -3, to = 3)$y
}
x <- density(rnorm(1000),from = -3, to = 3)$x
})
output$distPlot <- renderPlot({
matplot(data$x, t(data$d1), type = "l", lty = 1, col = c(rep(1,70),rep(2,30)))
})
}
# Run the application
shinyApp(ui = ui, server = server)
The above code is largely from the example shiny app, so please excuse any generic references. It should still work.
I was expecting a shiny plot with a slider on the left, and a plot on the right with 100 density lines in 2 colours. When the shift slider is changed the second set of data (red) will slide left or right depending on the shift.
Instead, I get the error message
55: stop
54: .getReactiveEnvironment()$currentContext
53: .subset2(x, "impl")$get
52: $.reactivevalues
47: server [/beavis/Documents/test/app.R#37]
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
Is anyone able to help me fix this code? Any help will be greatly appreciated. Having played around for an hour I believe the issue lies in the reactiveValues section, but nothing so far has worked.
Nice try. You're quite close. What you're missing is two things. You are creating a data object, which becomes reactive (I use reactive instead). This means that whenever you are calling data, you need to call it as data().
Second, your data returns only x. Output of this reactive environment should in your case be a list of x and d1.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("shift",
"shift of 2nd set",
min = -1,
max = 1,
value = 0,
step = 0.1) # I added a step
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# This reactive environment can be accessed using data().
data <- reactive({
d1 <- matrix(nrow=100, ncol=512)
for(i in 1:70){
d1[i,] <- density(rnorm(1000),from = -3, to = 3)$y
}
for(i in 71:100){
d1[i,] <- density(rnorm(1000, input$shift),from = -3, to = 3)$y
}
x <- density(rnorm(1000), from = -3, to = 3)$x
list(x = x, d1 = d1) # make sure that all objects are returned
})
output$distPlot <- renderPlot({
matplot(data()$x, t(data()$d1), type = "l", lty = 1, col = c(rep(1,70),rep(2,30)))
})
}
# Run the application
shinyApp(ui = ui, server = server)

Interactive Scatter plot in R using Plotly and Shiny

I am relatively new to Shiny and Plotly and have the following code snippet:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
library(plotly)
library(odbc)
library(DBI)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Demo"),
#Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 0,
max = 100,
value = 70)
),
# Show a plot of the generated distribution
mainPanel(
tabPanel("Heading", plotlyOutput("tbTable"))
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
QueriedData <- reactive({
connn <- DBI::dbConnect(odbc::odbc(),.connection_string = "XXX", uid = "AB", pwd = "CD")
lat_rec.df <- dbGetQuery(connn, "PQR")
dbDisconnect(connn)
lat_rec.df1
})
output$tbTable <- renderPlotly({
plot_ly(QueriedData(),x = ~TotalCount, y = ~MyScore, type = 'scatter', mode = 'markers')
})
}
# Run the application
shinyApp(ui = ui, server = server)
As you can see above, I am plotting a scatter plot of my dataframe which I have read from the Database (as mentioned in the reactive function). I have a couple of questions here:
I want to use the slider bar input as my Y Axis (MyScore). How do I do that? I am currently unable to link slider bar (bins) into my plotly graph. I want the scatter plot to update as per the slider input.
I am slightly confused about reactive functions. Does it mean that each time, I change the slider bar, the DB is going to get called (in reactive function)? How does it work?
If I have other Database tables to read and plot in other areas, do I include it in the reactive function? Please advice.
Thanks in advance for all your help! Cheers!
My solutions/answers to your three questions.
1.As you want to know how to control Y axis with sliderInput below code explains how to do it.
library(shiny)
library(plotly)
library(DBI)
library(pool)
pool <- dbPool(drv = RMySQL::MySQL(),dbname = "db",host = "localhost",username = "root",password = "psw", port = 3306)
data <- dbGetQuery(pool, "SELECT * FROM testTable;")
ui <- fluidPage(
titlePanel("Demo"),
sidebarLayout(
sidebarPanel(
sliderInput("bins", "Number of bins:", min = 0, max = 100, value = 70)
),
mainPanel(
tabPanel("Heading", plotlyOutput("tbTable"),
plotOutput("basicPlot") # Added extra as an Example for 3rd question
)
)
)
)
server <- function(input, output, session) {
QueriedData <- reactive({
df <- data[data$total <= input$bins,] # filtering datafarme based on sliderInput
return(df)
})
output$tbTable <- renderPlotly({
plot_ly(QueriedData(), x = ~count, y = ~total, type = 'scatter', mode = 'markers')
})
# Added extra as an Example for 3rd question
output$basicPlot <- renderPlot({
data_for_plot <- dbGetQuery(pool, "SELECT * FROM dummyTable WHERE uid = 2018;")
plot(x = data_for_plot$category, y = data_for_plot$performance, type = "p")
})
}
shinyApp(ui = ui, server = server)
2.For reactivity it is better to fetch the table into a dataframe once, then place that dataframe in reactive environment. So that you can avoid multiple database calls. You can check in above code for the same.
3.Using of reactive environment purely depends on the requirement when you want to have interactivity with your shiny application. If you want to fetch the data from other tables and use in different plots, then no need to place your database connection string in reactive environment. Just query the database according to your requirement like in above code.

Get axis ranges after plotly resize in Shiny

I have a Shiny app with a Plotly plot of time-series data. I want the user to be able to pan the x (time) axis, but keeping a consistent window of one day. To do this, I need to get the current x-axis range after each resize.
Why am I not just using a rangeSlider? Because I have about 25,000 data points, and using a rangeSlider requires loading all of the data into the plot when the app is initialized, which slows things down considerably.
You can use event_data with plotly_relayout. The official plotly documentation has a demonstration.
Here's a small example showing the xlimits from a plotly timeseries. Note that plotly_relayout will return NULL when the plot is initially rendered, dimensions of plot upon user resizing page, and TRUE when user autoscales plot by double-clicking.
library(shiny)
library(plotly)
ui <- fluidPage(
fluidRow(column(width = 6,
br(),
plotlyOutput("plot")),
column(width = 6,
br(), br(),
htmlOutput("xlims"),
br(),
h4("Verbatim plotly `relayout` data"),
verbatimTextOutput("relayout")))
)
server <- function(input, output, session) {
# create the plotly time series
output$plot <- renderPlotly({
today <- Sys.Date()
tm <- seq(0, 600, by = 10)
x <- today - tm
y <- rnorm(length(x))
p <- plot_ly(x = ~x, y = ~y, mode = 'lines',
text = paste(tm, "days from today"), source = "source")
})
# print the xlims
output$xlims <- renderText({
zoom <- event_data("plotly_relayout", "source")
# if plot just rendered, event_data is NULL
# if user double clicks for autozoom, then zoom$xaxis.autorange is TRUE
# if user resizes page, then zoom$width is pixels of plot width
if(is.null(zoom) || names(zoom[1]) %in% c("xaxis.autorange", "width")) {
xlim <- "default of plot"
} else {
xmin <- zoom$`xaxis.range[0]`
xmax <- zoom$`xaxis.range[1]`
xlim <- paste0("Min: ", xmin, "<br>Max: ", xmax)
}
paste0("<h4>X-Axis Limits:</h4> ", xlim)
})
# print the verbatim event_data for plotly_relayout
output$relayout <- renderPrint({event_data("plotly_relayout", "source")})
}
shinyApp(ui, server)

R, Shiny, plotting a reactive data frame (data goes "missing")

Given the following ui.R and server.R and circuit.csv; I can produce a simple plot which reacts to the user input (power in this case).
However, not all values for power are returned. For example, .5 produces a plot whereas .6 does not, so on and so forth at random occurrence throughout the power range.
If i plot as a table instead, to check my work, same thing, certain power inputs work as expected and others produce no table, and also no plot when asking to plot.
ui.R
library(shiny)
library(ggplot2)
shinyUI(fluidPage(
hr(),
sidebarLayout(
sidebarPanel(
sliderInput("power",label = "Power",
min = 0, max = 5, value = .5, step = .1)
),
mainPanel(
p("Lum vs Distance by Power"),
plotOutput('plot1')
)
)
))
server.R
library(shiny)
library(ggplot2)
df <- read.table(file = "circuit.csv", sep=",", header = TRUE)
shinyServer(function(input, output) {
output$plot1 <- renderPlot({
df2 <- subset(df,df$pow==input$power)
p <- ggplot(df2)+
geom_point(aes(x=dist, y=lum))
print(p)
})
})
Link to github (for csv data)
I would post images but am not allowed to do so at this time.

Resources