I have a complicated shiny app (here is a simpler example) which looks like that:
The app gives user the possibility to change four parameters (selectInput). The lower parameter depends on the highter one (ex. month on year, type on year and month and so on). Everything works but the fact that when I change one parameter, the other one changes too. It is needed in some situations, but not always. It is needed when the level chosen earlier does not exist in new configuration but for example when I have the following situation it should not be changed. Ex. I chose type 'AGD' and size 'medium' for some year and month and I show the prise or something for this combination. Then I would like to compare it to the same size in type 'RTV' so I change type parameter. Everything works but the size changes to the 'big' while I wanted it still to be 'medium'. I can make another click but what for? It is very inconvenient then...
Do you know how to deal with a problem like that?
I managed to do it for two dependencies using observe and reactive values, but for four dependencies it does not work.
Here is my code:
library("shiny")
library("plotly")
library("dplyr")
data <- data.frame(year = rep(c(rep(2018, 6), rep(2019, 11)), each = 5),
month = rep(c(7:12, 1:11), each = 5),
type = rep(c("AGD", "AGD", "AGD", "RTV", "RTV"), 6 + 11),
value = sample(1:100, 85),
size = rep(c("big", "small", "medium", "big", "miedium"), 6 + 11))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("year"),
uiOutput("month"),
uiOutput("type"),
uiOutput("size")
),
mainPanel(
)
)
)
server <- function(input, output, session) {
output$year <- renderUI({
year <- data %>%
select(year) %>%
unique()
selectInput("year",
"YEAR",
year$year,
selected = max(year$year))
})
output$month <- renderUI({
month <- data %>%
filter(year == input$year) %>%
select(month) %>%
unique() %>%
arrange()
selectInput("month",
"MONTH",
month$month,
selected = max(month$month))
})
output$type <- renderUI({
type <- data %>%
filter(year == input$year,
month == input$month) %>%
select(type) %>%
unique() %>%
arrange()
selectInput("type",
"TYPE",
type$type,
selected = type$type[1])
})
output$size <- renderUI({
size <- data %>%
filter(year == input$year,
month == input$month,
type == input$type) %>%
select(size) %>%
unique() %>%
arrange()
selectInput("size",
"SIZE",
size$size,
selected = size$size[1])
})
}
shinyApp(ui = ui, server = server)
Issues With the Existing Code
There are a couple of issues with the code here and the solution allows us to introduce the concept of memory into the app. First and foremost there are two issues I would like to address right off the bat.
c("big", "small", "medium", "big", "medium") and not c("big", "small", "medium", "big", "miedium")
The uiOutput() and renderUI() combination results the server serving a new selectInput button, everytime the input is changed. Instead we can simply instantiate a static UI element and update it using updateSelectInput()
Solution
To solve this problem lets first fix 1) and 2) described above. Then we need to introduce the concept of memory. The server needs to know what was previously selected, so that we can set it as the default option when the selectInput is updated. We can store this as a regular list (a variable for year, month, type and size) or a reactive list using reactiveValues.
Its great that you have settled on a clear cut logic for the filtering options, there is a clear hierarchy from years-> months -> type -> size. However, everytime months was changed for example a new input was generated for type and size.
We would now like to introduce a simple logic where the input selection only modifies the memory selected_vals. Then a change in memory triggers the other inputs to be updated. This is best seen in the solution below.
Code Solution
library("shiny")
library("plotly")
library("dplyr")
data <- data.frame(year = rep(c(rep(2018, 6), rep(2019, 11)), each = 5),
month = rep(c(7:12, 1:11), each = 5),
type = rep(c("AGD", "AGD", "AGD", "RTV", "RTV"), 6 + 11),
value = sample(1:100, 85),
size = rep(c("big", "small", "medium", "big", "medium"), 6 + 11))
years = data %>% arrange(year) %>% .$year %>% unique(.)
month = data %>% arrange(month) %>% .$month %>% unique(.)
type = data %>% arrange(type)%>% .$type %>% unique(.)
size = data %>% arrange(size) %>%.$size %>% unique(.)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("year","Year",choices = years,selected = 2018),
selectInput("month","Month",choices = month,selected = 7),
selectInput("type","Type",choices = type,selected = "AGD"),
selectInput("size","Size",choices = size,selected = "big")
),
mainPanel(
)
)
)
server <- function(input, output, session) {
#------- Initialize the Memory ----------
selected_vals = reactiveValues(year = 2019,month = 7, type = "AGD", size = "big")
#------ Whenever any of the inputs are changed, it only modifies the memory----
observe({
req(input$year,input$month,input$type,input$size)
selected_vals$year <- input$year
selected_vals$month <- input$month
selected_vals$type <- input$type
selected_vals$size <- input$size
})
#------ Update all UI elements using the values stored in memory ------
observe({
year <- data %>%
select(year) %>%
unique()
updateSelectInput(session,"year",choices = year$year,selected = selected_vals$year)
})
observe({
month <- data %>%
filter(year == selected_vals$year) %>%
select(month) %>%
unique() %>%
arrange()
#Check if the value is in memory, if not return NULL (it defaults to the first element)
if (selected_vals$month %in% month$month) displayVal = selected_vals$month else displayVal = NULL
updateSelectInput(session,"month",choices = month$month,selected = displayVal)
})
observe({
type <- data %>%
filter(year == selected_vals$year,
month == selected_vals$month) %>%
select(type) %>%
unique() %>%
arrange()
#Check if the value is in memory, if not return NULL (it defaults to the first element)
if (selected_vals$type %in% type$type) displayVal = selected_vals$type else displayVal = NULL
updateSelectInput(session,"type",choices = type$type,selected = displayVal)
})
observe({
size <- data %>%
filter(year == selected_vals$year,
month == selected_vals$month,
type == selected_vals$type) %>%
select(size) %>%
unique() %>%
arrange()
#Check if the value is in memory, if not return NULL (it defaults to the first element)
if(selected_vals$size %in% size$size) displayVal = selected_vals$size else displayVal = NULL
updateSelectInput(session,"size",choices = size$size,selected = displayVal)
})
}
shinyApp(ui = ui, server = server)
Edit
As mentioned in the comment below there is a bug in the code. This is caused by the fact that then displayVal = NULL shiny sets the default value to display as the first element in he array. However we forget to store this in memory, selected_vals. The code below fixes this.
library("shiny")
library("plotly")
library("dplyr")
data <- data.frame(year = rep(c(rep(2018, 6), rep(2019, 11)), each = 5),
month = rep(c(7:12, 1:11), each = 5),
type = rep(c("AGD", "AGD", "AGD", "RTV", "RTV"), 6 + 11),
value = sample(1:100, 85),
size = rep(c("big", "small", "medium", "big", "medium"), 6 + 11))
years = data %>% arrange(year) %>% .$year %>% unique(.)
month = data %>% arrange(month) %>% .$month %>% unique(.)
type = data %>% arrange(type)%>% .$type %>% unique(.)
size = data %>% arrange(size) %>%.$size %>% unique(.)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("year","Year",choices = years,selected = 2018),
selectInput("month","Month",choices = month,selected = 7),
selectInput("type","Type",choices = type,selected = "AGD"),
selectInput("size","Size",choices = size,selected = "big")
),
mainPanel(
)
)
)
server <- function(input, output, session) {
#------- Initialize the Memory ----------
selected_vals = reactiveValues(year = 2019,month = 7, type = "AGD", size = "big")
#------ Whenever any of the inputs are changed, it only modifies the memory----
observe({
req(input$year,input$month,input$type,input$size)
selected_vals$year <- input$year
selected_vals$month <- input$month
selected_vals$type <- input$type
selected_vals$size <- input$size
})
#------ Update all UI elements using the values stored in memory ------
observe({
year <- data %>%
select(year) %>%
unique()
updateSelectInput(session,"year",choices = year$year,selected = selected_vals$year)
})
observe({
month <- data %>%
filter(year == selected_vals$year) %>%
select(month) %>%
unique() %>%
arrange()
#Check if the value is in memory, if not return NULL (it defaults to the first element)
if (selected_vals$month %in% month$month){
displayVal = selected_vals$month
}else{
displayVal = NULL
selected_vals$month = month$month[1]
}
updateSelectInput(session,"month",choices = month$month,selected = displayVal)
})
observe({
type <- data %>%
filter(year == selected_vals$year,
month == selected_vals$month) %>%
select(type) %>%
unique() %>%
arrange()
#Check if the value is in memory, if not return NULL (it defaults to the first element)
if (selected_vals$type %in% type$type){
displayVal = selected_vals$type
}else{
displayVal = NULL
selected_vals$type = tpye$type[1]
}
updateSelectInput(session,"type",choices = type$type,selected = displayVal)
})
observe({
size <- data %>%
filter(year == selected_vals$year,
month == selected_vals$month,
type == selected_vals$type) %>%
select(size) %>%
unique() %>%
arrange()
#Check if the value is in memory, if not return NULL (it defaults to the first element)
if(selected_vals$size %in% size$size){
displayVal = selected_vals$size
} else{
displayVal = NULL
selected_vals$size = size$size[1]
}
updateSelectInput(session,"size",choices = size$size,selected = displayVal)
})
}
shinyApp(ui = ui, server = server)
Related
output$selected <- renderText({
paste(input$name)
})
output$hcontainer8 <- renderHighchart({
var <- textOutput("selected")
hc2 <- df_clean %>% filter(df_clean$ManagerName == input$name)
hchart(hc2, "lollipop", hcaes(name = Employee_Name, low = Absences ),name = "Absences") %>%
hc_xAxis(type = "category") %>%
hc_yAxis(labels = list(format = "{value}")) %>%
hc_title(text="Employee Absences under {var}",align="center") %>%
hc_subtitle(text = "with mean value from each department group by Performance Score", align = "center")
})
so this is my code for the server.r and I want to show the manager name in the plot title
based on the selected input
(text="Employee Absences under {var}",align="center")
this is a closer look at the code, so I want to display it in {var} is there a way to do it?
input$name is a string that you can use to both filter and within the hc_title() call.
Try:
hc_title(text = paste0("Employee Absences under ", input$name), align="center") %>%
i am pretty new to programmring but i have to make a shiny app for a university course.
As you can see i webscraped a data table thats presents different bike geometries and i wanted to create a shiny app, where i can compare the geometries with each other. I am quite happy with my progress, but now i got the problem that it always shows me the error: "Error in : Problem with filter() input ..1.
x Input ..1 must be of size 19 or 1, not size 0.
i Input ..1 is !=.... 161: "
I want that its possible in the app to choose one bike and it automatically compares the bike and shows me the 10 most similar bikes.
#table
Canyon <- read_html("https://enduro-mtb.com/canyon-strive-cfr-9-0-ltd-test-2020/")
Rose <- read_html("https://enduro-mtb.com/rose-root-miller-2020-test/")
Ghost <- read_html("https://enduro-mtb.com/ghost-riot-enduro-2021-erster-test/")
Cube <- read_html("https://enduro-mtb.com/cube-stereo-170-sl-29-test-2020/")
Comparison <- tibble(
Geometry = Canyon %>%
html_nodes(".geometry strong") %>%
html_text()%>%
str_trim(),
CanyonStrive = Canyon %>%
html_nodes("td:nth-child(3)") %>%
html_text()%>%
str_trim(),
GhostRiot = Ghost %>%
html_nodes("td:nth-child(3)") %>%
html_text()%>%
str_trim(),
CubeStereo = Cube %>%
html_nodes("td:nth-child(3)") %>%
html_text()%>%
str_trim(),
RoseRootMiller = Rose %>%
html_nodes("td:nth-child(3)") %>%
html_text()%>%
str_trim(),
)
ComparisonTable <- Comparison %>%
mutate_all(~gsub("mm|°|-.*|/.*|\\.", "", .)) %>%
mutate_all(~gsub(",", ".", .)) %>%
mutate_all(type.convert, as.is=TRUE) %>%
gather("Bikes", "value", 2:ncol(Comparison)) %>%
spread(Geometry,value)
Art <- c("Enduro", "Enduro", "AllMountain", "Enduro")
ComparisonTableHallo <- ComparisonTable
ComparisonTableHallo$Art <- Art
# server
server <- function(input, output, session) {
selectedData1 <- reactive({
ComparisonTableHallo %>%
filter(ComparisonTableHallo$Bikes != gsub("[[:space:]]*$","",gsub("- .*",'',input$Bikes)))
})
selectedData2 <- reactive({
selectedData1() %>%
select(1:12) %>%
filter(selectedData1()$Art %in% input$Art)
})
selectedData3 <- reactive({
ComparisonTableHallo %>%
select(1:12) %>%
filter(ComparisonTableHallo$Bikes == gsub("[[:space:]]*$","",gsub("- .*",'',input$Bikes)))
})
selectedData4 <- reactive({
rbind(selectedData3(),selectedData2())
})
selectedData5 <- reactive({
selectedData4() %>%
select(3:11)
})
selectedData6 <- reactive({
as.numeric(knnx.index(selectedData5(), selectedData5()[1, , drop=FALSE], k=2))
})
selectedData7 <- reactive({
selectedData4()[selectedData6(),]
})
selectedData8 <- reactive({
selectedData7() %>%
select(3:11)
})
# Combine the selected variables into a new data frame
output$plot1 <- renderPlotly({
validate(
need(dim(selectedData2())[1]>=2, "Sorry, no ten similar bikes were found.
Please change the input filters."
)
)
plot_ly(
type = 'scatterpolar',
mode = "closest",
fill = 'toself'
) %>%
add_trace(
r = as.matrix(selectedData8()[1,]),
theta = c("Kettenstrebe", "Lenkwinkel","Oberrohr","Radstand","Reach","Sattelrohr","Sitzwinkel","Stack","Steuerrohr",
"Tretlagerabsenkung"),
showlegend = TRUE,
mode = "markers",
name = selectedData7()[1,1]
) %>%
add_trace(
r = as.matrix(selectedData8()[2,]),
theta = c("Kettenstrebe","Lenkwinkel","Oberrohr","Radstand","Reach","Sattelrohr","Sitzwinkel","Stack","Steuerrohr",
"Tretlagerabsenkung"),
showlegend = TRUE,
mode = "markers",
visible="legendonly",
name = selectedData7()[2,1]
) %>%
layout(
polar = list(
radialaxis = list(
visible = T,
range = c(0,100)
)
),
showlegend=TRUE
)
})
}
#shiny app
ui <- fluidPage(navbarPage("Bike Comparison",
tabPanel("Graphic",fluidPage(theme = shinytheme("flatly")),
tags$head(
tags$style(HTML(".shiny-output-error-validation{color: red;}"))),
pageWithSidebar(
headerPanel('Apply filters'),
sidebarPanel(width = 4,
selectInput('Bike', 'Choose a Bike:',paste(ComparisonTableHallo$Bikes)),
checkboxGroupInput(inputId = "Art",
label = 'Art:', choices = c("Enduro" = "Enduro", "AllMountain" = "AllMountain"
),
selected = c("Enduro" = "Enduro","AllMountain" = "AllMountain"),inline=TRUE),
submitButton("Update filters")
),
mainPanel(
column(8, plotlyOutput("plot1", width = 800, height=700),
p("To visualize the graph of the player, click the icon at side of names
in the graphic legend. It is worth noting that graphics will be overlapped.",
style = "font-size:25px")
)
)
)))
)
shinyApp(ui = ui, server = server)
On your UI, your input is named Bike, on your server, you are referring to input$Bikes. Either Bike needs to change to Bikes, or the opposite.
Edit: (elaboration) Your error is telling you that you have a problem with one your arguments to the function filter. Specifically, you're passing an object of length 0 to the function. You are trying to pass the Bike. An empty select input would pass "", so that isn't the problem. "" has length 1. However an input you never assigned would pass NULL. That has length 0.
Problem:
I was trying to build a shiny app that plot frequency of n-grams based on a user specified column from a user uploaded csv. In addition, a function was added to plot the senetiment over time, based on a date column specified by the user as well.
The app was working okay locally, with Warning, but failed work after published. Please see the following for a reproducible example.
Preparation: libraries and example data
# Load R packages
library(shiny)
library(tidyverse)
library(shinythemes)
library(lubridate)
library(tidytext)
library(textdata)
# Creating a example csv file for upload
Sample_csv <-
data.frame(text = janeaustenr::emma,
id = 1:length(janeaustenr::emma),
date = sample(seq(as.Date('1900/01/01'), as.Date('1920/01/01'), by="day"),
replace = T,
length(janeaustenr::emma)))
write.csv(Sample_csv, "Sample_csv.csv", row.names = F)
UI
ui <- fluidPage(theme = shinytheme("united"),
titlePanel("Text glancer"),
sidebarLayout(
sidebarPanel(
# Input: Select a file ----
fileInput("csv_file", "Feed csv here",
multiple = FALSE,
accept = c(".csv")),
#Conditional panel
conditionalPanel(
# use a server side condition
condition = "output$fileUploaded",
# Input: Select ----
uiOutput("text_select"),
# Input: Select ----
uiOutput("date_select"),
# Input: Simple integer interval ----
sliderInput("top_frequency", "Top n ngrams to be plotted:",
min = 5, max = 20, value = 10),
# Input: Select ----
selectInput("ngrams", "Ngrams of your choice:",
c("Single word" = 1,
"Bigram" = 2,
"Trigram" = 3)
)
),
# Submit bottom
submitButton("Update View", icon("refresh"))
),
# sidebarPanel
mainPanel(
tabsetPanel(
tabPanel(h2("Most frequenlty used n-grams"),
plotOutput("frequency_plot", height = 900, width = 1200)),
tabPanel(h2("Sentiment of the months"),
plotOutput("sentiment_plot", height = 900, width = 1200))
)
)
)
)
server
server <- function(input, output, session) {
# create reactive version of the dataset (a data.frame object)
LOAD_DATA <- reactive({
infile <- input$csv_file
if (is.null(infile))
{return(NULL)}
{read_csv(infile$datapath)}
})
# inform conditionalPanel wheter dropdowns sohould be hidden
output$fileUploaded <- reactive({
return(!is.null(LOAD_DATA()))
})
outputOptions(output, 'fileUploaded', suspendWhenHidden=FALSE)
## update 'column' selectors
output$text_select <- renderUI({
if(is.null(LOAD_DATA()))
{return(NULL)}
else
selectInput("text_col", "Select the text column:", colnames(LOAD_DATA()))
})
output$date_select <- renderUI({
if(is.null(LOAD_DATA()))
{return(NULL)}
else
selectInput("date_col", "Select the date column (ymd):", colnames(LOAD_DATA()))
})
# Create reactive parameters
TOP_FREQUENCY <- reactive({
input$top_frequency
})
N_GRAMS <- reactive({
as.numeric(as.character(input$ngrams))
})
# Output frequency of ngrams
output$frequency_plot <- renderPlot( {
if(is.null(LOAD_DATA()))
{return(NULL)}
else{
WORK_DATA <- LOAD_DATA()[,c(input$text_col,
input$date_col)]
names(WORK_DATA) <- c("TEXTS", "DATES")
CSV_DOC_N_Grams <-
WORK_DATA %>%
# LOAD_DATA() %>%
# select(TEXTS = TEXT_COL(), DATES = DATE_COL()) %>%
mutate(TEXTS = gsub("http.*", " ", TEXTS)) %>%
# mutate(text = gsub("\\#.* |\\#.* .|\\#.* ,", " ", text)) %>%
unnest_tokens(words, TEXTS, token = "ngrams", n = N_GRAMS()) %>%
select(words) %>%
filter(str_detect(words, "[a-zA-Z]")) %>%
separate(words, c("word1","word2","word3"),sep = " ", remove = F) %>%
filter(! word1 %in% stop_words$word &
! word2 %in% stop_words$word&
! word3 %in% stop_words$word)
#Counting ngrams
CSV_DOC_N_Gramss_Count <-
CSV_DOC_N_Grams %>%
count(words, sort=T) %>%
select(N_Gram_Text = words,
N_Gram_Count = n)
#Plotting ngram frequency
CSV_DOC_N_Gramss_Count_freq <-
CSV_DOC_N_Gramss_Count %>%
mutate(N_Gram_Text = fct_reorder(N_Gram_Text, N_Gram_Count)) %>%
top_n(TOP_FREQUENCY(), N_Gram_Count) %>%
ggplot(aes(x = N_Gram_Text,
y = N_Gram_Count,
fill = N_Gram_Count)) +
geom_col()+
coord_flip() +
scale_fill_gradient2()+
labs(title = paste0("Top ", TOP_FREQUENCY(), " ngrams used in csv doc"),
x = "ngrams",
y = "frequency") +
theme_bw()+
theme(legend.position = "none",
axis.text.x = element_text(face='bold',size=12),
axis.text.y = element_text(face='bold',size=12),
axis.title.x = element_text(face='bold',size=18),
axis.title.y = element_blank())
print(CSV_DOC_N_Gramss_Count_freq)
}
})
output$sentiment_plot <- renderPlot( {
if(is.null(LOAD_DATA())){return(NULL)}
else{
WORK_DATA <- LOAD_DATA()[,c(input$text_col,
input$date_col)]
names(WORK_DATA) <- c("TEXTS", "DATES")
tk_afinn <-
WORK_DATA %>%
mutate(TEXTS = gsub("http.*", " ", TEXTS)) %>%
unnest_tokens(word, TEXTS) %>%
filter(! word %in% stop_words$word) %>%
filter(str_detect(word, "[a-zA-Z]")) %>%
filter(! DATES %in% NA) %>%
inner_join(get_sentiments("afinn")) %>%
mutate(YEAR_Month = ymd(paste(year(DATES),
month(DATES),
"1", sep="-"))) %>%
group_by(index = YEAR_Month) %>%
summarise(sentiment = sum(value))
tk_afinn_plot <-
tk_afinn %>%
ggplot(aes(x = index, y = sentiment)) +
geom_line()+
labs(x = "date (year-month)",
y = "sentiment of the month") +
theme_bw()+
theme(legend.position = "none",
axis.text.x = element_text(face='bold',size=12),
axis.text.y = element_text(face='bold',size=12),
axis.title.x = element_text(face='bold',size=18),
axis.title.y = element_blank())
print(tk_afinn_plot)
}
})
}
Fuse
shinyApp(ui = ui, server = server)
Warnings:
After loading the csv file, the local app reports :
"Problem with mutate() input TEXTS.
object 'TEXTS' not found
Input TEXTS is gsub("http.*", " ", TEXTS)."
After specify the text column and date column, both tab showed plots. However, after publishing it to shinyapp.io, it reports error and would not run.
Can anybody help with this issue? I have consulted the other thread includin this>https://stackoverflow.com/questions/47248534/dynamically-list-choices-for-selectinput-from-a-user-selected-column, but still no luck.
Any insight would be greatly appreciated!
I'm almost done building an app to explore data published in one of my papers, and thought that it would be nice to have something a little more interactive by adding a dygraph instead of a regular ggplot. Hence my problem... :)
Here is the code I have so far.
EDIT: Thanks to Waldi's comments below, I've slightly modified my code, and minimized it here to facilitate the process
library(shiny)
library(dygraphs)
library(xts)
library(tidyverse)
library(openxlsx)
Sys.setlocale("LC_TIME", "C")
data <- read.xlsx("https://www.bloomassociation.org/wp-content/uploads/2020/08/data.xlsx", sheet = 1) %>%
mutate(date = as.Date(date, origin = "1899-12-30"))
# Define UI for application that draws a histogram
ui <- fluidPage(# Define filters
fluidRow(
column(4,
selectInput("variableInput", label = h4("Show fisheries by:"),
unique(data$variable))),
column(4,
selectInput("unitInput", label = h4("Display data as:"),
unique(data$unit))),
column(4,
sliderInput("dateInput", label = h4("Select time range:"),
min = as.Date("2000-01-01"),
max = as.Date("2017-12-31"),
value = c(as.Date("2000-01-01"), as.Date("2017-12-31")),
timeFormat = "%b %Y")
),
# Display results
tabsetPanel(
tabPanel("Graphical view", withSpinner(dygraphOutput("distPlot"), type = getOption("spinner.type", default = 5), color = getOption("spinner.color", default = "#0A1D27"), size = getOption("spinner.size", default = 0.5))))
))
# Define server logic required to draw a histogram
server <- function(input, output) {
filtered_xts <- reactive({
data_ <- data %>%
filter(variable == input$variableInput,
unit == input$unitInput,
date >= input$dateInput[1],
date <= input$dateInput[2]
) %>%
select(-c(4:5)) %>%
mutate(quantity = round(quantity, 1)) %>%
spread(key = "category", value = "quantity") %>%
replace(is.na(.), 0)
# Debug the filtering // Solution provided by #Waldi; seems to fix most of my problem (see below)
print(data_)
data_ <- xts(data_, order.by = data_$date)
# Debug the xts conversion step
print(data_)
})
output$distPlot <- renderDygraph({
dygraph(filtered_xts()) %>%
dyOptions(fillGraph = TRUE, drawGrid = TRUE, stackedGraph = FALSE) #When stackedGraph = FALSE, everything works well, but I want it TRUE => it no longer works...
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
As you can see, everything works fine when stackedGraph = FALSEin the dyOptions() but it looks like only (part of) the first time-series is included when TRUE... what am I missing?
Looks like filtered_xts() doesn't output any value.
Try:
filtered_xts <- reactive({
data_ <- data %>%
filter(variable == input$variableInput,
unit == input$unitInput,
date >= input$dateInput[1],
date <= input$dateInput[2]
) %>%
select(-c(4:5)) %>%
mutate(quantity = round(quantity, 1)) %>%
spread(key = "category", value = "quantity") %>%
replace(is.na(.), 0) %>% data.table::as.data.table()
})
Following our discussion in comments, the conversion to data.table is more efficient than conversion to xts to be able to fully use dygraphs options.
I am providing here an executable simple R shiny application to plot two lines based on column names.
library(shiny)
library(reshape2)
library(ggplot2)
library(dplyr)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("moreControls")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Cross dating", plotOutput("plot1"))
)
)
)
))
server <- shinyServer(function(input, output) {
datasetInput <- data.frame(chrono_A = rnorm(16,0),chrono_B = rnorm(16,0))
row.names(datasetInput) <- c(seq(2000, 2015))
col_names <- colnames(datasetInput)
output$moreControls <- renderUI({
checkboxGroupInput("variable", "Filter Options", col_names)
})
# Plot data
output$plot1 <- renderPlot({
datasetInput_short <- mutate(datasetInput, year = as.numeric(row.names(datasetInput)))
datasetInput_short <- melt(datasetInput_short, id = c("year"))
datasetInput_short <- dplyr::filter(datasetInput_short, variable %in% input$variable)
ggplot(datasetInput_short, aes(x = year, y = value, group = variable, col = variable)) +
geom_line() + theme_bw() + ylim(-3, 3)
})
})
shinyApp(ui = ui, server = server)
I would like to add two features, which would allow me to move the plotted lines in two ways:
By adding a window where I can directly add the final year for the curve (ideally, the current final year would be entered automatically)
By adding two additional buttons (+ and -), and by clicking them I move each line by one year
Please, see the image below:
Any suggestion is highly appreciated.
I reread your description, maybe this might be helpful, though not entirely sure this is what you have in mind.
You can add two textInput widgets and then add filters to your data so that the data displayed for A and B have years less than these values.
In addition, you can have reactiveValues that include an offsets for A and B that increase/decrease when the buttons are pressed. These offsets will change the year column from the filtered data for A and/or B.
server <- shinyServer(function(input, output) {
datasetInput <- data.frame(chrono_A = rnorm(16,0),chrono_B = rnorm(16,0))
row.names(datasetInput) <- c(seq(2000, 2015))
col_names <- colnames(datasetInput)
rv <- reactiveValues(offset_A = 0, offset_B = 0)
observeEvent(input$but_plus_A, {
rv$offset_A <- rv$offset_A + 1
})
observeEvent(input$but_minus_A, {
rv$offset_A <- rv$offset_A - 1
})
observeEvent(input$but_plus_B, {
rv$offset_B <- rv$offset_B + 1
})
observeEvent(input$but_minus_B, {
rv$offset_B <- rv$offset_B - 1
})
datasetInput_short <- reactive({
datasetInput %>%
mutate(year = as.numeric(row.names(.))) %>%
pivot_longer(cols = starts_with("chrono_"), names_to = "variable", values_to = "value") %>%
dplyr::filter(variable %in% input$variable,
(variable == "chrono_A" & year < input$sel_A) | (variable == "chrono_B" & year < input$sel_B)) %>%
mutate(year = if_else(variable == "chrono_A", year + rv$offset_A, year),
year = if_else(variable == "chrono_B", year + rv$offset_B, year))
})
output$moreControls <- renderUI({
list(
checkboxGroupInput("variable", "Filter Options", col_names),
textInput("sel_A", "Year A", 2015),
actionButton("but_plus_A", "", icon = icon("plus")),
actionButton("but_minus_A", "", icon = icon("minus")),
textInput("sel_B", "Year B", 2015),
actionButton("but_plus_B", "", icon = icon("plus")),
actionButton("but_minus_B", "", icon = icon("minus"))
)
})
# Plot data
output$plot1 <- renderPlot({
ggplot(datasetInput_short(), aes(x = year, y = value, group = variable, col = variable)) +
geom_line() + theme_bw() + ylim(-3, 3)
})
})
Edit:
Based on OP comment below, say you do not know the column names, but there are always 2 columns to work with, you can do the following. You can use the column names vector col_names when filtering.
datasetInput_short <- reactive({
datasetInput %>%
mutate(year = as.numeric(row.names(.))) %>%
pivot_longer(cols = all_of(col_names), names_to = "variable", values_to = "value") %>%
dplyr::filter(variable %in% input$variable,
(variable == col_names[1] & year < input$sel_A) | (variable == col_names[2] & year < input$sel_B)) %>%
mutate(year = if_else(variable == col_names[1], year + rv$offset_A, year),
year = if_else(variable == col_names[2], year + rv$offset_B, year))
})
It is possible to dynamically generate more than 2 sets of inputs (for example, using 3: A, B, and C) but that would be a bit more complex.