问题
I want something like this to happen in Shiny (but just 1 plot, not 4):
THe following steps are the ones that I want for my Shiny app plot:
- Print an empty plot
- Draw the red abline
- Sequentially draw n segments with points
- Draw blue dotted abline
- Stop
But I got 2 problems with my function that I don't know how to fix:
In the slider "n", for example if I put 10, and press "Correr", I get 8 segments first, I want none at first, and when I press the "play" button the pattern I wrote above starts.
Again, say I put 10, as soon as the blue dotted abline appears, I get an error of "subscript out of bounds"
ui:
ui <-fluidPage(
tabsetPanel(
tabPanel("Simulacion 1",
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "a",label = "a",value = 1 ,min = 1,max = 20),
sliderInput(inputId = "b",label = "b",value = 1 ,min = 1,max= 20),
sliderInput(inputId = "sig",label = "sig",value = 1 ,min = 1,max=50),
sliderInput(inputId = "n",label = "n",value = 1 ,min = 1,max=50,step=1,animate=animationOptions(interval=1000)),
actionButton(inputId= "correr", label = "Correr")),
mainPanel(plotOutput(outputId = "simul1")
)))),)
server:
server <-function(input, output) {
z=qnorm(0.975)
intercepto=c()
pendiente=c()
sim.beta=function(a,b,sig,n){
x=runif(n,1,10)
y=a+b*x+rnorm(n,0,sig)
return(data.frame(x,y))
}
Animacion=eventReactive(input$correr,{
set.seed(123)
s1=sim.beta(input$a,input$b,input$sig,input$n)
grafico=vector('list', ncol(s1))
X=s1$x;Y=s1$y
mod1=lm(s1$y~s1$x)
r11=X;r21=input$a+input$b*r11
for (i in 1:c(input$n+3)) {
if(i==1){
grafico[[i]]=ggplot(s1,aes(x,y))+
theme_classic()+
xlim(0,10)+
ylim(input$a-z*input$sig,2*input$a+input$b*10+z*input$sig)
}
if(i==2){
grafico[[i]]=grafico[[i-1]]+
geom_abline(intercept = input$a,slope = input$b,col=2)
}
else if(i>2 & i<=c(input$n+2)){
grafico[[i]]= local({
i=i
ggplot(s1[1:c(i-2),],aes(x,y))+
theme_classic()+
xlim(0,10)+
ylim(input$a-z*input$sig,2*input$a+input$b*10+z*input$sig)+
geom_abline(intercept = input$a,slope = input$b,col=2)+
geom_segment(aes(x=r11[1:c(i-2)],y=r21[1:c(i-2)]+z*input$sig,xend=r11[1:c(i-2)],yend=r21[1:c(i-2)]-z*input$sig),linetype=2)+
geom_point(aes(X[1:c(i-2)],Y[1:c(i-2)]),col=3,size=3)
})
}
else if(i==c(input$n+3)){
grafico[[i]]= grafico[[i-1]]+
geom_abline(aes(intercept=mod1$coefficients[1],slope=mod1$coefficients[2]),col=4,linetype=2)
}
}
grafico
})
output$simul1 <- renderPlot({
Animacion()[[input$n]]
}, height = 650, width = 650)
}
shinyApp(ui = ui, server = server)
回答1:
Here is a working solution. I've made a few changes:
animacion
is now only dependent on areactiveTimer
. This allows you to generate new plot data in a given intervalanimacion
only returns one plot, which is then plotted -> you don't need to mess around withinput$n
which plot should be shown- I've added some logic to introduce a timer counter/index, so that the correct output data is sequentially generated
- because the parameter change should only applied when "Correr" is pressed, I've added a new
eventReactive
to store the parameters (otherwise the updated values would be automatically used every time the timer triggers)
library(shiny)
library(ggplot2)
ui <-fluidPage(
tabsetPanel(
tabPanel("Simulacion 1",
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "a",label = "a",value = 1 ,min = 1,max = 20),
sliderInput(inputId = "b",label = "b",value = 1 ,min = 1,max= 20),
sliderInput(inputId = "sig",label = "sig",value = 1 ,min = 1,max=50),
sliderInput(inputId = "n",label = "n",value = 1 ,min = 1,max=50,step=1,animate=animationOptions(interval=1000)),
actionButton(inputId= "correr", label = "Correr")),
mainPanel(plotOutput(outputId = "simul1")
)))))
server <-function(input, output) {
z=qnorm(0.975)
intercepto=c()
pendiente=c()
animation_timer <- reactiveTimer(1500)
timer_index <- 1
grafico <- list()
sim.beta=function(a,b,sig,n){
x=runif(n,1,10)
y=a+b*x+rnorm(n,0,sig)
return(data.frame(x,y))
}
animation_parameters <- eventReactive(input$correr, {
list(a = input$a,
b = input$b,
sig = input$sig,
n = input$n)
})
Animacion = eventReactive(animation_timer(),{
set.seed(123)
s1=sim.beta(animation_parameters()$a,
animation_parameters()$b,
animation_parameters()$sig,
animation_parameters()$n)
X=s1$x;Y=s1$y
mod1=lm(s1$y~s1$x)
r11=X;r21=input$a+input$b*r11
if(timer_index ==1){
grafico <<- ggplot(s1,aes(x,y))+
theme_classic()+
xlim(0,10)+
ylim(animation_parameters()$a-z*animation_parameters()$sig,2*animation_parameters()$a+animation_parameters()$b*10+z*animation_parameters()$sig)
}
if(timer_index ==2){
grafico <<- grafico +
geom_abline(intercept = animation_parameters()$a,slope = animation_parameters()$b,col=2)
}
else if(timer_index>2 & timer_index<=c(animation_parameters()$n+2)){
grafico <<- local({
i= timer_index
ggplot(s1[1:c(i-2),],aes(x,y))+
theme_classic()+
xlim(0,10)+
ylim(animation_parameters()$a-z*animation_parameters()$sig,2*animation_parameters()$a+animation_parameters()$b*10+z*animation_parameters()$sig)+
geom_abline(intercept = animation_parameters()$a,slope = animation_parameters()$b,col=2)+
geom_segment(aes(x=r11[1:c(i-2)],y=r21[1:c(i-2)]+z*animation_parameters()$sig,xend=r11[1:c(i-2)],yend=r21[1:c(i-2)]-z*animation_parameters()$sig),linetype=2)+
geom_point(aes(X[1:c(i-2)],Y[1:c(i-2)]),col=3,size=3)
})
}
else if(timer_index ==c(animation_parameters()$n+3)){
grafico <<- grafico +
geom_abline(aes(intercept=mod1$coefficients[1],slope=mod1$coefficients[2]),col=4,linetype=2)
}
# update timer_index
if (timer_index == animation_parameters()$n + 3) {
timer_index <<- 1
} else {
timer_index <<- timer_index + 1
}
grafico
})
output$simul1 <- renderPlot({
Animacion()
}, height = 650, width = 650)
}
shinyApp(ui = ui, server = server)
P.S. I'm actually not sure if local
and animation_parameters
work together, maybe you have to tweak that.
来源:https://stackoverflow.com/questions/63636698/how-to-print-a-list-of-ggplots-in-shiny-in-a-for-loop