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)
Related
I want to design a reactive interaction graph for a time series for shiny.
It is used for when I have a time series. I change the first point of the time series.
Then I can get the reactive graph changed for the point changed.
I have trouble for how to assign the value input into the function. Specifically, for the
yinput<-sensi(num). I want to the input be some number range from 0.1 to 0.9.but my sensitive's
function only allow to enter one input.
Is there any idea for that?
Thanks
library(ggplot2)
sensi<-function(input)
{
y<-c(input,5,12,21,30,50,90,100)
return(y)
}
Date = c("2020/07/16","2020/07/23","2020/07/30","2020/08/06","2020/08/13","2020/08/20","2020/08/27","2020/09/13")
num<-c(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9)
yinput<-sensi(num)
df <- data.frame(yinput, Date = as.Date(Date))
ui <- fluidPage(
titlePanel(title=h4("plot1", align="center")),
sidebarPanel(
sliderInput("num", "Number:",min = 0, max = 0.9,value=0.5)),
mainPanel(plotOutput("plot2")))
server <- function(input,output){
dat <- reactive({
test <- df[df$num %in% seq(from=min(input$num),to=max(input$num),by=1),]
print(test)
test
})
output$plot2<-renderPlot({
ggplot(df) + aes(Date, y) + geom_line()})}
shinyApp(ui, server)
Here's a very basic example
You should be doing your 'thinking' inside your 'server' function.
Hopefully this example will show you how reactive inputs and outputs relate to each other, and you can extend it to your plot.
require(shiny)
ui <- fluidPage(
sliderInput("num", "Number:", min = 0.1, max = 0.9, value = 0.5),
textOutput("number_display")
)
server <- function(input, output){
output$number_display <-
renderText({
as.character(input$num)
})
}
shinyApp(ui = ui, server = server)
Where I've said as.character(input$num), you'll want to do something with num to make your plot.
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.
I have a randomly generated data.frame. The user can modify a slider to choose the number of points. Then I plot this data.frame.
I want to add a button than when clicked, it performs a modification in the previous randomly generated data.frame (but without regenerating the data.frame). The modification is a voronoid relaxation, and it should be performed once per each time the button is clicked and the graph generated.
Until now, I have not achieved anything similar...
ui.R
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Map Generator:"),
# Sidebar with a slider input for the number of bins
sidebarLayout(
sidebarPanel(
p("Select the power p to generate 2^p points."),
sliderInput("NumPoints",
"Number of points:",
min = 1,
max = 10,
value = 9),
actionButton("GenPoints", "Generate"),
actionButton("LloydAlg", "Relaxe")
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot",height = 700, width = "auto")
)
)
))
server.R
library(shiny)
library(deldir)
shinyServer(function(input, output) {
observeEvent(input$NumPoints,{
x = data.frame(X = runif(2^input$NumPoints,1,1E6),
Y = runif(2^input$NumPoints,1,1E6))
observeEvent(input$LloydAlg, {
x = tile.centroids(tile.list(deldir(x)))
})
output$distPlot <- renderPlot({
plot(x,pch = 20,asp=1,xlim=c(0,1E6),ylim = c(0,1E6))
})
})
})
Of course there is something that I must be doing wrong, but I am quite new into shiny I can't figure it out what I am doing wrong...
This should work (even though I am pretty sure this could be improved):
shinyServer(function(input, output) {
library(deldir)
data = data.frame(
X = runif(2^9, 1, 1E6),
Y = runif(2^9, 1, 1E6)
)
rv <- reactiveValues(x = data)
observeEvent(input$GenPoints, {
rv$x <- data.frame(
X = runif(2^input$NumPoints,1,1E6),
Y = runif(2^input$NumPoints,1,1E6)
)
})
observeEvent(input$LloydAlg, {
rv$x = tile.centroids(tile.list(deldir(rv$x)))
})
output$distPlot <- renderPlot({
plot(rv$x,pch = 20,asp=1,xlim=c(0,1E6),ylim = c(0,1E6))
})
})
So first I initialize the points to plot. I use runif(2^9, 1, 1E6) because the starting value of the sliderInput is 9 all the time.
I also removed the observeEvent from the sliderInput and moved it to the GenPoints actionButton.
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
. . .
})
}
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.