A few weeks ago I've asked about reactivity in R - Shiny, and I still haven't managed to crack the nut. It is driving me insane, I've read 100s of articles about reactivity, but I just can't make it work!
This is what I got so far:
ui.R:
column(width = 3,
box(
title = "Pick a metric",
status = "primary",
width = 12,
height = 200,
solidHeader = FALSE,
collapsible = FALSE,
selectInput("value", "Metric:",
c("Pages / Session" = "pageviewsPerSession",
"Avg. Session Duration" = "avgSessionDuration"))
) )
column(width = 9,
box(
title = "Title",
status = "primary",
width = 12,
height = 950,
solidHeader = FALSE,
collapsible = FALSE,
plotlyOutput("Scatter1")
server.R:
output$Scatter1 <- renderPlotly({
datasetInput <- reactive({
switch(input$value,
"Pages / Session" = SourcesDetail$pageviewsPerSession,
"Avg. Session Duration" = SourcesDetail$avgSessionDuration)
})
p <- plot_ly(SourcesDetail, x = datasetInput(), y = SourcesDetail$visits, text = paste("Source/Medium: ", SourcesDetail$sourceMedium),
mode = "markers", color = SourcesDetail$medium, opacity = SourcesDetail$sourceMedium, marker = list(size = 15))
})
There is no error message, there is simply no graph in my output.
Any help would be appreciated!!
You are assigning the output to p but not outputting p itself. Either remove p <- or put a single p at the end of renderPlotly:
Either
output$Scatter1 <- renderPlotly({
datasetInput <- reactive({
switch(input$value,
"Pages / Session" = SourcesDetail$pageviewsPerSession,
"Avg. Session Duration" = SourcesDetail$avgSessionDuration)
})
plot_ly(SourcesDetail, x = datasetInput(), y = SourcesDetail$visits, text = paste("Source/Medium: ", SourcesDetail$sourceMedium),
mode = "markers", color = SourcesDetail$medium, opacity = SourcesDetail$sourceMedium, marker = list(size = 15))
})
or
output$Scatter1 <- renderPlotly({
datasetInput <- reactive({
switch(input$value,
"Pages / Session" = SourcesDetail$pageviewsPerSession,
"Avg. Session Duration" = SourcesDetail$avgSessionDuration)
})
p <- plot_ly(SourcesDetail, x = datasetInput(), y = SourcesDetail$visits, text = paste("Source/Medium: ", SourcesDetail$sourceMedium),
mode = "markers", color = SourcesDetail$medium, opacity = SourcesDetail$sourceMedium, marker = list(size = 15))
p
})
Related
I am trying to make a shiny app based on historical data. I am using a multiple shiny page approach.
For some reason when I run app, my output gets outputted only in the Viewer of RStudio and not on the main panel of the Shiny popup.
Here's the code:
Code in UI:
body <- dashboardBody(
tabItems(
tabItem(tabName = "dashboard",
fluidRow(
plotlyOutput(outputId="chart1")
),
fluidRow(
gaugeOutput(outputId="gauge")
)
)
))
Code in Server:
output$gauge <- renderPlotly({
plot_ly(
domain = list(x = c(0, 1), y = c(0, 1)),
value = fatality_rate,
number= list(valueformat=".2f", suffix = "%"),
title = list(text = "Fatality Rate", font = list(size = 24, color = "black")),
type = "indicator",
mode = "gauge+number",
gauge = list(
axis = list(range=list(NULL, 50)),
bar= list(color="darkorange"),
borderwidth = 2,
bordercolor = "black",
threshold = list(
line = list(color="red", width = 4),
thickness = 0.75,
value = 49
)
))
})
I'm putting together a shiny app to play around with some athlete GPS data. Essentially, I'm looking to structure my script so that each time the user selects an area of interest on the plotly plot and the "Add" actionButton is clicked, the table below will add the calculated Start_time, Time_at_peak, Max_velocity, Time_to_peak, and Distance_to_peak values.
The issue can be seen in the GIF below: - Once the area of interest is selected and the "Add" button clicked, the first values seem correct. However, when the user selects a second area of interest to add to the table, it overwrites the initial entry and will keep overwriting each time a new selection is made. This is seemingly because because the code is inside the observeEvent(event_data("plotly_selected"), which, confusingly, it needs to be in order to calculate the variables of interest.
I'm currently a little stumped and can't seem to find any relevant information. As such, any guidance would be greatly appreciated!
Here is a we transfer link to some test data that can be uploaded to the app: https://wetransfer.com/downloads/5a7c5da5a7647bdbe133eb3fdac79c6b20211119052848/afe3e5
library(shiny)
library(readr)
library(DT)
library(dplyr)
library(plotly)
library(lubridate)
library(tidyr)
library(purrr)
library(htmlwidgets)
library(shinydashboard)
library(dashboardthemes)
library(shinyjs)
x_df <- data.frame(Start_time = character(1), Time_at_peak = character(1), Max_velocity = integer(1),
Time_to_peak = integer(1), Distance_to_peak = integer(1))
x_df$Start_time <- as.character("0:00:00.0")
x_df$Time_at_peak <- as.character("0:00:00.0")
x_df$Max_velocity <- as.integer(0)
x_df$Time_to_peak <- as.integer(0)
x_df$Distance_to_peak <- as.integer(0)
runApp(shinyApp(
ui=(fluidPage(
titlePanel("Event to Table"),
mainPanel(
fileInput(
inputId = "filedata",
label = "Upload data file (.csv)",
accept = c(".csv")),
plotlyOutput('myPlot'),br(),br(),br(),br(),
DTOutput("testing"), br(), br(),
fluidRow(
valueBoxOutput("starttime", width = 2),
valueBoxOutput("endtime", width = 2),
valueBoxOutput("maxvelocity", width = 2),
valueBoxOutput("timediff", width = 3),
valueBoxOutput("distance", width = 3)
),
useShinyjs(),
fluidRow(
div(style = "text-align:center", actionButton("Add", "Add Data to Table"),
downloadButton("export", "Export Table as .CSV"))), br(),
DTOutput(outputId = "table")))
),
server = (function(input, output, session) {
data<-reactive({
req(input$filedata)
read.csv(input$filedata$datapath, header = TRUE)%>%
rename(Velocity = 'Speed..m.s.',
Player = 'Player.Display.Name',
Latitude = 'Lat',
Longtitude = 'Lon',
AccelImpulse = 'Instantaneous.Acceleration.Impulse',
HeartRate = 'Heart.Rate..bpm.')
})
observe({
thedata<-data()
updateSelectInput(session, 'y', choices = names(data))
})
output$myPlot = renderPlotly({
plot_ly(data = data(), x = ~Time, y = ~Velocity, height = 450,
marker =list(color = 'rgb(132,179,202)', size = 0.1),
line = list(color = 'rgb(132,179,202)', size = 0.1, width = 0.9),
type = 'scatter', mode = 'markers+lines') %>%
layout(dragmode = "select",
showlegend = F,
title = list(text = 'Velocity Trace', font = list(size = 20)),
xaxis = list(title = list(text = "", standoff = 0), nticks = 10),
yaxis = list(title = list(text = "Velocity (m/s)"), nticks = 5, gridcolor = "#46505a"),
font = list(color = 'black'),
margin = list(t = 70))
})
observeEvent(event_data("plotly_selected"), {
event.data <- event_data("plotly_selected")
if (max(event.data$y) < 1.5) {
maxvel <- (max(event.data$y))
maxpos <- match(maxvel, event.data$y)
}
else {
filter1 <- filter (event.data, event.data$y > 1.5)
maxvel <- (max(filter1$y))
maxpos <- match(maxvel, event.data$y)
}
zero_val <- function(x) x == 0
zero_index <- event.data$y[1:maxpos] %>% detect_index(zero_val, .dir = "backward")
if (zero_index==0) {starttime <- event.data$x[1]}
else {starttime <- event.data$x[zero_index]}
endvel <- which.max(event.data$y)
endtime <- event.data$x[endvel]
timediff <- paste(today(), endtime) %>% as_datetime() - paste(today(), starttime) %>% as_datetime()
sprint <- as_tibble(event.data$y[zero_index:endvel])
ms <- as_tibble(rep(0.1, count(sprint)))
time_vel <- cbind(ms, sprint)
distance <- sum(time_vel[1]*time_vel[2])
sprintselect <- as_tibble(cbind(Start_time = starttime,
Time_at_peak = endtime,
Max_velocity = round(maxvel, 2),
Time_to_peak = round(timediff, 1),
Distance_to_peak = round(distance, 1)))
values <- reactiveValues()
values$df <- x_df
addData <- observe({
if(input$Add > 0) {
newLine <- isolate(data.frame(Start_time = sprintselect$Start_time, Time_at_peak = sprintselect$Time_at_peak,
Max_velocity = sprintselect$Max_velocity,
Time_to_peak = sprintselect$Time_to_peak,
Distance_to_peak = sprintselect$Distance_to_peak,
stringsAsFactors= FALSE))
values$df <- isolate(rbind(values$df, newLine))}
})
output$testing <- renderDataTable({values$df})
})
})
))
I've managed to figure it out and thought I'd post an answer rather than delete the question - just in case someone out there is looking to do a similar thing and they are unsure how to do it.
Firstly, I removed the pre-populated table x_df from the beginning - it was no longer required.
Although I thought the code needed to sit inside the observeEvent(event_data("plotly_selected") to function correctly, it did not - thankfully, because that was at the root of the issue. Instead, I used observeEvent(input$Add, { (which is the correct code to use as opposed to if(input$Add > 0)) to anchor the event to the click of the Add button.
The values <- reactiveValues() was placed outside the observeEvent() and an IF statement was used to either add the data to the values$df data frame on it's own if it was the first selection, or bind it to the existing saved data.
Here's the new code and a GIF demonstrating.
library(shiny)
library(readr)
library(DT)
library(dplyr)
library(plotly)
library(lubridate)
library(tidyr)
library(purrr)
library(htmlwidgets)
library(shinydashboard)
library(dashboardthemes)
library(shinyjs)
runApp(shinyApp(
ui=(fluidPage(
titlePanel("Event to Table"),
mainPanel(
fileInput(
inputId = "filedata",
label = "Upload data file (.csv)",
accept = c(".csv")),
plotlyOutput('myPlot'),br(),br(),br(),br(),
DTOutput("testing"), br(), br(),
fluidRow(
valueBoxOutput("starttime", width = 2),
valueBoxOutput("endtime", width = 2),
valueBoxOutput("maxvelocity", width = 2),
valueBoxOutput("timediff", width = 3),
valueBoxOutput("distance", width = 3)
),
useShinyjs(),
fluidRow(
div(style = "text-align:center", actionButton("Add", "Add Data to Table"),
downloadButton("export", "Export Table as .CSV"))), br(),
DTOutput(outputId = "table")))
),
server = (function(input, output, session) {
values <- reactiveValues(df_data = NULL)
data<-reactive({
req(input$filedata)
read.csv(input$filedata$datapath, header = TRUE)%>%
rename(Velocity = 'Speed..m.s.',
Player = 'Player.Display.Name',
Latitude = 'Lat',
Longtitude = 'Lon',
AccelImpulse = 'Instantaneous.Acceleration.Impulse',
HeartRate = 'Heart.Rate..bpm.')
})
observe({
thedata<-data()
updateSelectInput(session, 'y', choices = names(data))
})
output$myPlot = renderPlotly({
plot_ly(data = data(), x = ~Time, y = ~Velocity, height = 450,
marker =list(color = 'rgb(132,179,202)', size = 0.1),
line = list(color = 'rgb(132,179,202)', size = 0.1, width = 0.9),
type = 'scatter', mode = 'markers+lines') %>%
layout(dragmode = "select",
showlegend = F,
title = list(text = 'Velocity Trace', font = list(size = 20)),
xaxis = list(title = list(text = "", standoff = 0), nticks = 10),
yaxis = list(title = list(text = "Velocity (m/s)"), nticks = 5, gridcolor = "#46505a"),
font = list(color = 'black'),
margin = list(t = 70))
})
observeEvent(input$Add, {
event.data <- event_data("plotly_selected")
if (max(event.data$y) < 1.5) {
maxvel <- (max(event.data$y))
maxpos <- match(maxvel, event.data$y)
}
else {
filter1 <- filter (event.data, event.data$y > 1.5)
maxvel <- (max(filter1$y))
maxpos <- match(maxvel, event.data$y)
}
zero_val <- function(x) x == 0
zero_index <- event.data$y[1:maxpos] %>% detect_index(zero_val, .dir = "backward")
if (zero_index==0) {starttime <- event.data$x[1]}
else {starttime <- event.data$x[zero_index]}
endvel <- which.max(event.data$y)
endtime <- event.data$x[endvel]
timediff <- paste(today(), endtime) %>% as_datetime() - paste(today(), starttime) %>% as_datetime()
sprint <- as_tibble(event.data$y[zero_index:endvel])
ms <- as_tibble(rep(0.1, count(sprint)))
time_vel <- cbind(ms, sprint)
distance <- sum(time_vel[1]*time_vel[2])
sprintselect <- as_tibble(cbind(Start_time = starttime,
Time_at_peak = endtime,
Max_velocity = round(maxvel, 2),
Time_to_peak = round(timediff, 1),
Distance_to_peak = round(distance, 1)))
newLine <- isolate(data.frame(Start_time = sprintselect$Start_time, Time_at_peak = sprintselect$Time_at_peak,
Max_velocity = sprintselect$Max_velocity,
Time_to_peak = sprintselect$Time_to_peak,
Distance_to_peak = sprintselect$Distance_to_peak,
stringsAsFactors= FALSE))
if (is.null(values$df)){
values$df <- newLine}
else {
values$df <- isolate(rbind(values$df, newLine))}
output$testing <- renderDataTable({values$df})
})
})
))
i'm newly to the R world and i'm just trying to build a Dashboard on Shiny.
My problem is that i want to display some text only if certain conditions are met in the renderplotly function.
shinyUI(fluidPage(
titlePanel("Posti occupati in terapia intensiva"),
sidebarLayout(
sidebarPanel(
selectInput("region","Scegli regione",unique(as.character(region_dataset$denominazione_regione),)
),
dateInput("day","Scegli data", min=region_dataset$data[1], max=region_dataset$data[nrow(region_dataset)], format="dd/mm/yyyy",value=region_dataset$data[nrow(region_dataset)]
),
),
mainPanel(
plotlyOutput(outputId = "TI"),
textOutput(outputId= "text")
)
),
))
This is the ui page and i show you the server
shinyServer(function(input, output) {
output$TI <- renderPlotly({
day <- input$day
region <- input$region
request <- filter(region_dataset,region_dataset$data==day & region_dataset$denominazione_regione==region)
plot_ly(as.data.frame(request$terapia_intensiva),
domain = list(x = c(0, 1), y = c(0, 1)),
value = request$terapia_intensiva,
title = list(text = "Posti occupati TI"),
type = "indicator",
mode = "gauge+number+delta",
delta = (reference = as.integer(request$terapia_intensiva[nrow(request$data)-1])),
gauge = list(
axis =list(range = list(NULL, request$posti_TI)),
bar = list(color = "darkmagenta"),
borderwidth = 3,
steps = list(
list(range = c(0, 0.33*request$posti_TI), color = "green"),
list(range = c(0.33*request$posti_TI, 0.66*request$posti_TI), color = "yellow"),
list(range = c(0.66*request$posti_TI, request$posti_TI), color = "red")),
threshold = list(
line = list(color = "cyan", width = 5),
thickness = 0.75,
value = request$posti_TI)))
})
output$text <- renderText("Numero massimo di posti occupati")
})
My problem is that i want to display the text in the panel only if request$terapia_intensiva>request$posti_TI
I can't find out a solution to this problem, i've tried using reactive function and conditional panel but with no results.
Thanks for helping.
renderText() can contain logic, so
output$text <- renderText({
if (request$terapia_intensiva>request$posti_TI) "Numero massimo di posti occupati"
})
If the if() returns FALSE, renderText returns NULL. If you want to be explicit, you can always add else NULL or else rturn(NULL) if you wish.
I want to make an animation in R Shiny where my scatter plot is progressively updated at each iteration, here is my current plot
library(shiny)
library(plotly)
ui <- fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(
actionButton("launch", "Launch Simulation"),
radioButtons("display","Show every iteration", selected = 10,
choices = c(1,5,10,50),inline = FALSE),
numericInput("iter","Maximum number of iterations", value = 2000,
min = 500,max = 5000, step = 500)
),
mainPanel(
plotlyOutput('plot')
)
)
)
server <- function(input, output) {
rv <- reactiveValues(i = 0,
df = data.frame(x = -1,y = -1))
observeEvent(input$launch,{
rv$i = 0
rv$df = data.frame(x = runif(5000, min = -1,max = 1),
y = runif(5000, min = -1,max = 1))
})
observe({
isolate({
rv$i = rv$i + as.numeric(input$display)
})
if ((rv$i < input$iter)&input$launch){
invalidateLater(0)
}
})
output$plot <- renderPlotly({
df = data.frame(x = 0,y = -1)
df = rbind(df,rv$df)
plot_ly(df[1:(rv$i + 1),], x = ~x, y = ~y,
type = 'scatter', mode = 'markers',
marker = list(size = 4), hoverinfo="none") %>%
layout(showlegend = FALSE)
})
}
shinyApp(ui = ui, server = server)
The code is working fine at the beginning but after around 1000 iterations, the animation becomes very slow. I think the main problem is that because in my code, I have to re-make the plot all over again at each iteration, is there a smoother way to do what I want to do?
(Not necessarily with Plotly but it is important to me that I keep track of the number of the iterations outside of the plot (here rv$i))
This question already has answers here:
Sort (order) data frame rows by multiple columns
(19 answers)
Closed 4 years ago.
I created a shiny app and need some help with the subset of my data. I insert a dateRangeInput where the client can filter between a start and end date. This filter is included into my ggplot code, so that the plot always automatically changes when a different date is selected. My problem is it does not filter based on the selected date, the data of partC. The problem is this line of code: geom_line(aes(x = Date, y = OLS.Data[partC]), color="red"). partC is a variable that connects to selectinputs to have access to my dataframe. Example: Client selects input1 = Informed and input2 = Full, partC makes InformedFull (which is the name of one column of my dataset) and so on. So partC is just a a connector of the two inputs, and this is my problem. If I put into my geom_line this code e.g geom_line(aes(x = Date, y = InformedFull), color="red"), instead the above everything works perfect, but I need it with partC.
Here is my ui.R code (only necessary part):
box(
title = "Controls-0",
status = "primary",
solidHeader = TRUE,
width = 3,
height = 142,
dateRangeInput("daterange", "SELECT DATE:", start = min(OLS.Data$Date), end = max(OLS.Data$Date))
),
box(
title = "Investor Control",
status = "primary",
solidHeader = TRUE,
width = 3,
selectInput("investor", label="Select Investor", choices = list("Informed" = "Informed", "Noise" = "Noise"), selected = "Informed")
),
box(
title = "Category Control",
status = "primary",
solidHeader = TRUE,
width = 3,
selectInput("category", label="Select Category", choices = list("Full" = "Full", "Fact" = "Fact", "Fact Positive" = "Fact.Pos", "Fact Negative" = "Fact.Neg", "Emotions" = "Emotions", "Emotions Fact" = "EmotionsFact"), selected = "Full")
),
Update server.R with ggplot:
server <- function(input, output) {
partC = NULL
makeReactiveBinding("partC")
observeEvent(input$investor, {
partA<<-input$investor
partA<<-as.character(partA)
})
observeEvent(input$category, {
partB<<-input$category
partB<<-as.character(partB)
})
OLS.Data$InformedEmotionsFact <- as.numeric(as.character(OLS.Data$InformedEmotionsFact))
OLS.Data$NoiseEmotionsFact <- as.numeric(as.character(OLS.Data$NoiseEmotionsFact))
output$myPlotVisu <- renderPlot({
partC<-as.character(paste(partA,partB,sep=""))
OLS.Data %>%
select(partC, NYSE,Date,Sector) %>%
filter(Date >= input$daterange[1], Date <= input$daterange[2]) %>%
ggplot(aes(x = Date, y = NYSE)) +
geom_line() +
ggtitle(paste(input$investor,input$category,sep = "")) +
theme(plot.title = element_text(hjust = 0.5,face="bold")) +
labs(x="Time",y="Return S&P500") +
geom_line(aes(x = Date, y = OLS.Data[partC]), color="red")
})
I dont know why you assign partA/partB to the global environment, and even twice. You dont need to do that. I created an reactiveValues object instead, where you store the values (partA, partB and partC). Then you can use them wherever you want in your app.
Maybe the following example will help you with your code. I created some dummy data for it.
library(shiny)
library(shinydashboard)
library(ggplot2)
## DATA #######################
DateSeq = seq(as.Date("1910/1/1"), as.Date("1911/1/1"), "days")
OLS.Data = data.frame(
ID = 1:length(DateSeq),
Date = DateSeq,
NoiseEmotionsFact = sample(1:100,length(DateSeq), T),
InformedEmotionsFact = sample(100:1000,length(DateSeq), T),
InformedFull = sample(10:1000,length(DateSeq), T),
NoiseFull = sample(50:5000,length(DateSeq), T),
NoiseFact = sample(1:15,length(DateSeq), T),
NoiseFact.Pos = sample(100:110,length(DateSeq), T),
NoiseFact.Pos = sample(10:200,length(DateSeq), T)
)
## UI #######################
ui <- {dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
plotOutput("myPlot"),
box(
title = "Controls-0",
status = "primary",
solidHeader = TRUE,
width = 3,
height = 142,
dateRangeInput("daterange", "SELECT DATE:", start = min(OLS.Data$Date), end = max(OLS.Data$Date))
),
box(
title = "Alpha",
sliderInput("alphaVisu", label = "Alpha :", min = 0, max = 1, value = 0.4, step = 0.1)
),
box(
title = "Investor Control",
status = "primary",
solidHeader = TRUE,
width = 3,
selectInput("investor", label="Select Investor",
choices = list("Informed" = "Informed", "Noise" = "Noise"), selected = "Informed")
),
box(
title = "Category Control",
status = "primary",
solidHeader = TRUE,
width = 3,
selectInput("category", label="Select Category",
choices = list("Full" = "Full", "Fact" = "Fact", "Fact Positive" = "Fact.Pos",
"Fact Negative" = "Fact.Neg", "Emotions" = "Emotions",
"Emotions Fact" = "EmotionsFact"), selected = "Full")
)
)
)}
## SERVER #######################
server <- function(input, output) {
## Reactive Values ############
parts <- reactiveValues(partA=NULL, partB=NULL, partC=NULL)
## Observe Events ############
observeEvent(input$investor, {
parts$partA <- as.character(input$investor)
})
observeEvent(input$category, {
parts$partB <- as.character(input$category)
})
## Plot ############
output$myPlot <- renderPlot({
parts$partC <- as.character(paste(parts$partA, parts$partB,sep=""))
OLS.Data.filtered <- OLS.Data %>%
filter(Date >= input$daterange[1], Date <= input$daterange[2])
req(OLS.Data.filtered)
OLS.Data.filtered %>%
ggplot(aes(x = Date, y = ID)) +
geom_line() +
ggtitle(paste("input$investor","input$category",sep = "")) +
theme(plot.title = element_text(hjust = 0.5,face="bold")) +
labs(x="Time",y="Return S&P500") +
geom_line(aes(x = Date, y = OLS.Data.filtered[parts$partC]), color="red",
alpha = rep(as.numeric(input$alphaVisu), nrow(OLS.Data.filtered[parts$partC])))
})
}
shinyApp(ui, server)