Shiny R Application to let users modify dataframe by lasso selection - r

I've created an R Shiny application to help me streamline some common data cleaning tasks for working with high dimensional chemical composition data. Specifically, this app uses the fluidPage ui and ggplot/plotly interface to create a biplot with user selected X and Y variables and color/symbol attributes. The event_data function allows users to see attributes associated with points they interactively select via the rectangular selection or lasso. I'm new to Shiny so the code is not very elegant but I've managed to do all of the above.
I'm hoping to add one additional feature and I'm stuck on the best way to approach this. Specifically I'd like to be able to change one field in the dataset for points that are currently selected on a given plot. My current idea is to have a text field input that will allow me to type in what I'd like the new value in the field and have the change execute with an actionButton.
I found the answers to the question linked here quite useful but I still haven't managed to get this to work. Below is my current application script and a screenshot of the output as it stands now.
Any help or suggestions for new approaches would be greatly appreciated.
library(plotly)
library(shiny)
library(knitr)
library(kableExtra)
myApp <- function(attributes,dat1) {
dataset <- cbind(attributes,dat1)
ui <- fluidPage(
plotlyOutput('plot', width='1000px', height='600px'),
fluidRow(
column(2,
selectInput('xvar','X',names(dat1)),
selectInput('yvar','Y',names(dat1))),
column(3,offset=0.5,
selectInput('Code','GROUP',names(attributes)),
checkboxInput('Conf','Confidence Hull',value=TRUE)),
column(3,offset=0.5,
actionButton('Change','Change Group Assignment'),
textInput('NewGroup', label = 'Enter new group designation')),
column(3,offset=0.5,
actionButton("exit", label = "Return to R and write data"))),
verbatimTextOutput('brush')
)
server <- function(input, output) {
data.sel <- reactive({
dataset[,c(input$xvar,input$yvar,input$Code)]
})
output$plot <- renderPlotly({
p <- ggplot(data.sel(), aes(x=data.sel()[,1], y=data.sel()[,2],
color=data.sel()[,3], shape=data.sel()[,3])) +
geom_point() +
labs(x=input$xvar,y=input$yvar)
if(input$Conf) {p <- p + stat_ellipse(level=0.95)}
ggplotly(p) %>% layout(dragmode = 'select')
})
output$brush <- renderPrint({
d <- event_data('plotly_selected')
dd <- round(cbind(d[[3]],d[[4]]),3)
vv <- attributes[which(round(data.sel()[,1],3) %in% dd[,1] &
round(data.sel()[,2],3) %in% dd[,2]),]
if (is.null(d)) 'Click and drag events (i.e., select/lasso) appear here
(double-click to clear)' else kable(vv)
})
observe({
if(input$exit > 0)
stopApp()})
}
runApp(shinyApp(ui, server))
return(dataset)
}
In order to test this you can use a modified version of the iris data as I show below. Essentially, I'd like to be able to change the text in the new variable I'm adding to the iris data.
iris2 <- cbind(iris,rep('A',150))
names(iris2)[6] <- 'Assignment'
myApp(iris2[,5:6],iris2[,-(5:6)])
Here is a screenshot of the app in action. I've included the buttons to go along with my proposed solution but they currently do nothing.
Screenshot:

I was able to get this working as I originally intended once I understood how scoping assignment works in Shiny in relation to reactive statements. This app now mostly does everything I want it do, though I feel the code is really just cobbled together at this point and needs to be fixed in many areas. In particular I have a very janky solution to finding the selected items in my original dataframe as I really don't like the curvenumber/pointnumber index system.
library(plotly)
library(shiny)
library(knitr)
library(kableExtra)
theme_set(theme_light())
myApp <- function(attributes,dat1) {
dataset <- cbind(attributes,dat1)
vv <- NULL
ui <- fluidPage(
plotlyOutput('plot', width='1000px', height='600px'),
fluidRow(
column(2,
selectInput('xvar','X',names(dat1),selected='cs'),
selectInput('yvar','Y',names(dat1),selected='ta')),
column(3,offset=0.5,
selectInput('Code','GROUP',names(attributes),selected='CORE'),
checkboxInput('Conf','Confidence Elipse',value=TRUE),
sliderInput('int.set','Set Confidence Interval',min=0.80,max=0.99,step=0.01,value=0.95)),
column(3,offset=0.5,
br(),
actionButton('Change','Change Group Assignment'),
textInput('NewGroup', label = 'Enter new group designation')),
column(3,offset=0.5,
br(),
actionButton('refresh', label='Refresh Plot with New Assignments'),
br(),br(),
actionButton("exit", label = "Return to R and write data"))),
verbatimTextOutput('brush')
)
server <- function(input, output) {
values <- reactiveValues(vv = NULL)
data.sel <- reactive({
dataset[,c(input$xvar,input$yvar,input$Code)]
})
output$plot <- renderPlotly({
g1 <- data.sel()
p <- ggplot(g1, aes(x=g1[,1], y=g1[,2], color=g1[,3], shape=g1[,3])) +
geom_point() +
labs(x=input$xvar,y=input$yvar,color=input$Code,shape=input$Code)
if(input$Conf) {p <- p + stat_ellipse(level=input$int.set)}
ggplotly(p) %>% layout(dragmode = 'select')
})
output$brush<- renderPrint({
g1 <- data.sel()
d <- event_data('plotly_selected')
dd <- round(cbind(d[[3]],d[[4]]),3)
vv <- attributes[which(round(g1[,1],3) %in% dd[,1] & round(g1[,2],3) %in% dd[,2]),]
vv <<- vv
if (is.null(vv)) "Click and drag events (i.e., select/lasso) appear here (double-click to clear)" else kable(vv)
})
observeEvent(input$Change > 0, {
if (!is.null(vv)) {
dataset[which(row.names(dataset) %in% row.names(vv)),]$CORE <<-
input$NewGroup
}})
observe({
if(input$exit > 0)
stopApp()})
}
runApp(shinyApp(ui, server))
return(dataset)
}
And some test data
data(iris)
iris2 <- cbind(iris,rep('a',nrow(iris)))
names(iris2)[6] <- 'CORE'
out <- myApp(iris2[,5:6],iris2[,1:4])

Related

add another layer to ggplot2/ggtree based on user input Rshiny

The example below is using ggtree in which I can brush the tips in the phylogeny and add an annotation label ("clade"). Steps to get the app going -
load the tree - called vert.tree
brush over (highlight) tips (test with human and lemur) and press the 'annotate tree' button to add the label in red.
What I want to do is add another annotation onto the tree while maintaining the first annotation (human and lemur). For example, a second label for the pig and cow tips. Essentially, I want to be able to add a line onto a phylogenetic tree based on user input and then repeat that based on second input from the user while maintaining the first line on the image. Currently, the label gets reset every time I brush a different pair so only one annotation is displayed at a time.
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
library(shiny)
library(treeio)
library(ggtree)
library(phytools)
library(ape)
#make phylogenetic tree
text.string <-"(((((((cow, pig),whale),(bat,(lemur,human))),(robin,iguana)),coelacanth),gold_fish),shark);"
#read in the tree
vert.tree<-ape::read.tree(text=text.string)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Test"),
actionButton("add_annotation","Add clade annotation"),
# Show a plot of the generated distribution
mainPanel(plotOutput("treeDisplay", brush ="plot_brush")
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#reactive that holds base tree - this is how I am building the base tree
make_tree <- reactive({
ggtree::ggtree(vert.tree)+
ggtree::geom_tiplab()+
ggplot2::xlim(NA, 10)})
#render base tree
output$treeDisplay <- renderPlot({
make_tree()
})
#reactive that holds the brushed points on a plot
dataWithSelection <- reactive({
brushedPoints(make_tree()$data, input$plot_brush)
})
#add to label to vector if isTip == True
dataWithSelection2 <- reactive({
tipVector <- c()
for (i in 1:length(dataWithSelection()$label)){ if(dataWithSelection()$isTip[i] == TRUE) tipVector <- c(tipVector,dataWithSelection()$label[i])}
return(tipVector)
})
# incorporate the tipVector information for adding layer
layer <- reactive({
ggtree::geom_cladelabel(node=phytools::findMRCA(ape::as.phylo(make_tree()), dataWithSelection2()), label = "Clade", color = "red")
})
#display that layer onto the tree
observeEvent(input$add_annotation, {
output$treeDisplay <- renderPlot({make_tree() + layer()})
})
}
# Run the application
shinyApp(ui = ui, server = server)
Suggestions greatly appreciated!
updated to include a base tree (vert.tree)
Hope you found the solution already, but if not, here is an approach.
First it helps to do the problem in a non-shiny setting. What we need is a list that accumulates vectors of tips. Then we cycle over this list to generate annotations:
tree_plot <-
ggtree::ggtree(vert.tree) +
ggtree::geom_tiplab() +
ggplot2::xlim(NA, 10)
tip_vector <- list(c("human", "lemur"), c("pig", "cow"))
make_layer <- function(tree, tips, label, color) {
ggtree::geom_cladelabel(
node = phytools::findMRCA(ape::as.phylo(tree), tips),
label = label,
color = color
)
}
x + lapply(1:2, function(i)
make_layer(
tree_plot,
tips = tip_vector[[i]],
label = paste("Clade", i),
color = "red"
))
The key bit is in the lapply call, where generate the annotation layer for each member of the tip_vector list.
Now that this is working, we go to shiny. In your app, every time you click add annotation the brushed points data frame is refreshed and your tip vector is just a vector of the newly brushed tips. Any previously selected clades are forgotten.
To remember these, we can introduce two reactive values. One n_annotations is a numeric reactiveVal counting how many times we click add annotation. The other annotations is a reactiveValues list which stores all the brushed clades under the names paste0("ann", n_annotations()).
Then, the actual adding of the layer of annotations proceeds as in the non-reactive example with lapply cycling over the reactiveValues.
App code:
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
library(shiny)
library(treeio)
library(ggtree)
library(phytools)
library(ape)
#make phylogenetic tree
text.string <-"(((((((cow, pig),whale),(bat,(lemur,human))),(robin,iguana)),coelacanth),gold_fish),shark);"
#read in the tree
vert.tree<-ape::read.tree(text=text.string)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Test"),
actionButton("add_annotation","Add clade annotation"),
# Show a plot of the generated distribution
mainPanel(plotOutput("treeDisplay", brush ="plot_brush"),
plotOutput("treeDisplay2")
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#reactive that holds base tree - this is how I am building the base tree
make_tree <- reactive({
ggtree::ggtree(vert.tree) +
ggtree::geom_tiplab() +
ggplot2::xlim(NA, 10)
})
#render base tree
output$treeDisplay <- renderPlot({
make_tree()
})
# Initialize a reactive value and set to zero
n_annotations <- reactiveVal(0)
annotations <- reactiveValues()
#reactive that holds the brushed points on a plot
dataWithSelection <- reactive({
brushedPoints(make_tree()$data, input$plot_brush)
})
#add to label to vector if isTip == True
dataWithSelection2 <- eventReactive(input$plot_brush, {
tipVector <- c()
for (i in 1:length(dataWithSelection()$label)) {
if (dataWithSelection()$isTip[i] == TRUE)
tipVector <- c(tipVector, dataWithSelection()$label[i])
}
tipVector
})
make_layer <- function(tree, tips, label, color) {
ggtree::geom_cladelabel(
node = phytools::findMRCA(ape::as.phylo(tree), tips),
label = label,
color = color
)
}
#display that layer onto the tree
anno_plot <- eventReactive(input$add_annotation, {
# update the reactive value
new <- n_annotations() + 1
n_annotations(new)
annotations[[paste0("ann", n_annotations())]] <- dataWithSelection2()
plt <-
make_tree() +
lapply(1:n_annotations(), function(i)
make_layer(
make_tree(),
tips = annotations[[paste0("ann", i)]],
label = paste("Clade", i),
color = "red"
))
return(plt)
})
output$treeDisplay2 <- renderPlot({
anno_plot()
})
}
# Run the application
shinyApp(ui = ui, server = server)
Edit: how the reactive values work without the phylo stuff
I tried to comment this thoroughly.
ui <- basicPage(
actionButton("add_anno", "Add annotation"),
helpText("n_annotation is counting clicks"),
textOutput("n_anno"),
helpText("clades is accumulating clades"),
verbatimTextOutput("clades")
)
server <- function(input, output) {
# this initializes a reactive value
# and sets the initial state to 0
n_anno <- reactiveVal(0)
# makes an empty reactive list
# this can be populated and index
# like a normal list
# e.g., clades[["first"]] <- c("bird", "lizard")
clades <- reactiveValues()
observeEvent(input$add_anno, {
# increment the number of clicks
new_count <- n_anno() + 1
# update the reactiveValue
# works the same way we initialized it
# except instead of zero we set the incremented value
n_anno(new_count)
# making a name for an element in the clades list
# we use the n_anno number of clicks to increment the clades
# message just prints it on console
message( paste0("clade", n_anno() ))
# populate the list of clades for annotations
clades[[ paste0("clade", n_anno() ) ]] <- sample(LETTERS, 3)
})
output$n_anno <- renderText(n_anno())
output$clades <- renderPrint(
str(reactiveValuesToList(clades))
)
}
shinyApp(ui, server)
hmmm - okay when I tested your suggestion
dataWithSelection2 <- reactive({
tipVector <- c()
for (i in 1:length(dataWithSelection()$label)){
if(!is.null(dataWithSelection()$isTip[i])) {
tipVector <- c(tipVector,dataWithSelection()$label[i])
}
}
return(tipVector)
})
I get the error: missing value where TRUE/FALSE needed....

Filtering reactive data in an R Shiny App

I have a dataframe that has these columns:
document, user, month, views
I am using a selectInput to filter the data by document.
I want to plot a (Plotly) line chart of views per month, for each user, for the selected document.
E.g. If one filters to a document for which ten users exist, I want to display ten plots, each showing the relevant user's views per month.
At current:
- I filter the data to the selected document (dplyr).
- I pass the filtered data to a function.
- In the function, I loop through the current document's users.
- In each loop, I filter the data to the current user (dplyr), and append a Plotly output to a output list.
- At the end of the function, I return the output list.
- The result of the function is assigne to a UI output.
The app successfully runs, but where the plots should display, I get a Result must have length x, not y error.
How would you go about this? I appreciate any advice you can give me.
For security reasons I cannot share my existing code, sorry - I understand that it's not very useful.
Edit: I've created a minimal reproducible example, based on this.
The process has changed slightly from my original question, mainly that I'm not using a separate function.
library(plotly)
library(tidyverse)
# DATA
data <- data.frame(
document= c("doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc1","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2","doc2"),
user= c("user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user3","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user1","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user2","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4","user4"),
month= as.Date(c("2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01","2017-01-01","2017-02-01","2017-03-01","2017-04-01","2017-05-01","2017-06-01","2017-07-01","2017-08-01","2017-09-01","2017-10-01","2017-11-01","2017-12-01","2018-01-01","2018-02-01","2018-03-01","2018-04-01","2018-05-01","2018-06-01","2018-07-01")),
views= c(19,39,34,3,25,5,1,16,37,21,46,34,23,0,8,10,46,3,47,16,32,4,44,42,12,8,27,39,28,30,26,45,49,38,32,32,1,16,23,34,41,46,37,0,23,44,10,3,43,43,22,38,1,33,11,15,8,21,37,17,7,29,1,33,47,45,37,20,9,41,37,18,30,46,24,45,48,42,49,3,10,17,46,6,12,29,13,6,4,44,37,26,43,5,19,28,44,20,35,40,32,20,41,46,25,47,35,3,25,25,41,5,26,32)
)
# SERVER
server <- shinyServer(function(input, output) {
output$plots <- renderUI({
doc_data <- filter(data, document == input$select_doc) # This is the breaking line
plot_output_list <- lapply(1:length(unique(doc_data$user)), function(i) {
plotname <- paste("plot", i, sep="")
plotlyOutput(plotname)
})
do.call(tagList, plot_output_list)
})
for (i in 1:length(unique(doc_data$user))) {
local({
local_i <- i
doc_users <- unique(doc_data$user)
plotname <- paste("plot", local_i, sep="")
plot_data <- filter(doc_data, user == doc_users[local_i]) %>%
arrange(month)
output[[plotname]] <- renderPlotly({
p <- plot_ly(x= plot_data$month, y= plot_data$views, type = 'scatter', mode = 'lines')
p$elementId <- NULL
p
})
})
}
})
# UI
ui <- shinyUI(pageWithSidebar(
headerPanel("Minimum reproducible example"),
sidebarPanel(
selectInput("select_doc", choices= unique(data$document), label="", selected= 'doc1')#,
),
mainPanel(
uiOutput("plots")
)
))
# RUN
shinyApp(ui, server)

ggvis hover ability in for loop in shiny app

I am trying to create a variable number of plots in a shiny app, each with hover ability from ggvis using add_tooltip() to display actual data points. To create a variable number of plots I am using a for loop. See below for a toy example that can be run on its own.
For some reason in my code the hover over ability only works correctly for the final plot that is created. Does anyone know how I might be able to fix this or maybe have a suggestion for a better approach?
Thanks!
library(shiny)
library(ggvis)
# Define ui for variable amounts of plots
ui <- fluidPage(
fluidRow(
uiOutput("mydisplay")
)
)
server <- function(input, output) {
# toy data example
x = data.frame(
id = 1:30,
myname = c(rep("First 10",10),rep("Second 10",10),rep("Third 10",10)),
stringsAsFactors = F
)
# ggvis add_tooltip() function
all_values <- function(x) {
if(is.null(x)) return(NULL)
row <- mydf[mydf$id == x$id, c("id","myname") ]
paste0(names(row), ": ", format(row), collapse = "<br />")
}
# For loop to create variable number of plots
for (k in 1:length(unique(x$myname))){
mydf = subset(x,x$myname==unique(x$myname)[k])
mydf %>% ggvis(~id, ~id) %>%
layer_points(size := 30, key := ~id) %>%
add_tooltip(all_values,"hover") %>%
bind_shiny(paste0("p_",k), paste0("p_ui_",k))
}
# For displaying in the UI
output$mydisplay = renderUI({
lapply(1:length(unique(x$myname)), function(j) {
fluidRow(
column(7, ggvisOutput(paste0("p_",j)))
)
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
Answered my own question thanks to this question here. The ggvis code needs to be wrapped in a reactive({}) function. Hope this helps someone.

R: creating a route map with multiple filters

~~EDIT~~~
The answer below worked, please see my code to be complete and question answered for anyone who needs the help in the future.
~~~~~~~~
I am trying to create a dashboard in R (first one!) that will create a map that shows thousands of routes taken between the package initial location and the package end location (travelling all over the world). I would then like to have filters in order to show different routes based on criteria (and if possible, give a box to tell how many routes are there based on the selection).
I was able to make the dashboard and the map with all the lines and it looks great. The issue now is I cannot seem to figure out how to create the filters to be interactive. My problem is I am currently creating the lines and plotting them in the For Loop so they are not being saved anywhere.
I have three filters: Department (which I call SBU), Manufacturing plant (which I call Plant and is a sub set of SBU), and Customer.
I.e You can have SBU A with all Plants associated with SBU A and look at Customer Y. You will then see those specific routes associated with this.
I.e. You can have SBU B with Plant K and look at all Customers
Unfortunately, I cannot give out the raw data.
Any help would be greatly appreciated as I am very new to R!
library(shiny)
library(shinydashboard)
library(maps)
library(geosphere)
library(maps)
library(mapproj)
library(geosphere)
library(ggrepel)
library(scales)
###########################/ui.R/##################################
#Setting drive where files are located
setwd("C:/R Files")
#Pulling in outside Data Files
Network <- read.csv("Network Codes.csv")
Data <- read.csv("Raw Data2.csv")
#Header
header <- dashboardHeader(
title = "Intake Routes")
#SideBar
sidebar <- dashboardSidebar(
#SBU Selection List
selectInput(inputId = "SBU", "SBU:", selected="ALL",
choices = unique(as.character(Network$SBU))),
#Plant Selection List
uiOutput("Plant"),
#Customer Selection List
selectInput(inputId = "Customer", "Customer:", multiple = TRUE, selected="ALL",
choices = unique(as.character(Data$Customer.Name.Standard))))
#Body
body <- dashboardBody(
plotOutput(outputId = "map")
)
#Builds Dashboard Page
ui <- dashboardPage(header, sidebar, body)
###########################/server.R/###############################
server <- function(input, output) {
##INPUT##
#Dependant Plant SideBar List Dependant on SBU
output$Plant <- renderUI({
selectInput(inputId = "Plant", "Plant:", multiple = TRUE,
choices = as.character(Network[Network$SBU == input$SBU, "Plant.Name"]))
})
#Reactive data set based on inputs
Reactive_Data1 <- reactive({
if (input$SBU == "ALL") {Data}
else {Data[Data$SBU == input$SBU,]}
})
Reactive_Data2 <- reactive({
if (input$Plant == "ALL") {Reactive_Data1()}
else {Reactive_Data1()[Reactive_Data1()$Plant == (input$Plant),]}
})
Reactive_Data3 <- reactive({
if (input$Customer == "ALL") {Reactive_Data2()}
else {Reactive_Data2()[Reactive_Data2()$Customer.Name.Standard == input$Customer,]}
})
output$map <- renderPlot({
#Map coordinates
xlim <- c(-170,170)
ylim <- c(-55,75)
map("world", col="#f2f2f2", fill=TRUE, bg="white", lwd=0.05, xlim=xlim, ylim=ylim)
npoints <- 20
nroutes <- nrow(Data)
for(i in 1:nroutes){
inter <- gcIntermediate(c(Data$Ship.From.Longitude[i],
Data$Ship.From.Latitude[i]),
c(Data$Ship.To.Longitude[i],
Data$Ship.To.Latitude[i]),
n=npoints, addStartEnd = T, breakAtDateLine = T)
if (is.list(inter)) {
inter1 <- inter[[1]]
inter2 <- inter[[2]]
lines(inter1, col = "green", lwd=0.50)
lines(inter2, col = "blue", lwd=0.50)}
else {
lines(inter, col = "grey", lwd=0.50)}
}
})
}
#Combines Dashboard and Data together
shinyApp(ui, server)
You need to make the dataset "reactive" to your inputs and then keep using and referring to that reactive dataset so it updates each time your inputs change.
Here is an example of making a new reactive variable called reactive_dat based on a filtered version of your Data variable.
reactive_dat <- reactive({
Data[Data$SBU == input$SBU, ]
})

Shiny Module that calls a reactive data set in parent Shiny server

I'm looking to port some older Shiny apps to use Shiny Modules, but running into trouble trying to port over my reactive expressions.
According to the documentation:
The goal is not to prevent modules from interacting with their
containing apps, but rather, to make these interactions explicit. If a
module needs to use a reactive expression, take the reactive
expression as a function parameter.
I have existing reactive expressions that import data from APIs etc. that I would like to pass in, but can't seem to find the syntax. If I modify the given Shiny module example below I can get to the same problem.
Could anyone modify the below so that you can pass in the car_data() reactive data into the module? I've tried just about every combination of isolate and car_data/car_data() I can think of and am stumped :)
I would prefer to not need to call the data within the module itself, as in my case I'm trying to generalise an ETL function applicable to lots of datasets.
library(shiny)
library(ggplot2)
linkedScatterUI <- function(id) {
ns <- NS(id)
fluidRow(
column(6, plotOutput(ns("plot1"), brush = ns("brush"))),
column(6, plotOutput(ns("plot2"), brush = ns("brush")))
)
}
linkedScatter <- function(input, output, session, data, left, right) {
# Yields the data frame with an additional column "selected_"
# that indicates whether that observation is brushed
dataWithSelection <- reactive({
brushedPoints(data(), input$brush, allRows = TRUE)
})
output$plot1 <- renderPlot({
scatterPlot(dataWithSelection(), left())
})
output$plot2 <- renderPlot({
scatterPlot(dataWithSelection(), right())
})
return(dataWithSelection)
}
scatterPlot <- function(data, cols) {
ggplot(data, aes_string(x = cols[1], y = cols[2])) +
geom_point(aes(color = selected_)) +
scale_color_manual(values = c("black", "#66D65C"), guide = FALSE)
}
ui <- fixedPage(
h2("Module example"),
linkedScatterUI("scatters"),
textOutput("summary")
)
server <- function(input, output, session) {
### My modification
### making the reactive outside of module call
car_data <- reactive({
mpg
})
## This doesn't work
## What is the syntax for being able to call car_data()?
df <- callModule(linkedScatter, "scatters", car_data(),
left = reactive(c("cty", "hwy")),
right = reactive(c("drv", "hwy"))
)
output$summary <- renderText({
sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
})
}
shinyApp(ui, server)
Drop the parens after car_data:
df <- callModule(linkedScatter, "scatters", car_data,
left = reactive(c("cty", "hwy")),
right = reactive(c("drv", "hwy"))
)
The module seems to want "unresolved" reactives. The parentheses "resolves" them.
If you want to pass input which is not part of the module just wrap it around reactive() as stated in a tutorial.
If a module needs to access an input that isn’t part of the module,
the containing app should pass the input value wrapped in a reactive
expression (i.e. reactive(...)):
callModule(myModule, "myModule1", reactive(input$checkbox1))
Update:
As correctly stated in another answer and Joe Cheng correct way to pass reactive expression is without brackets ()
callModule(linkedScatter, "scatters", car_data)
One option is also to modularize your API input function so you don't need to define reactive expression outside modules. Example of modularized input can be found from this answer.
Below your code with right answer.
library(shiny)
library(ggplot2)
linkedScatterUI <- function(id) {
ns <- NS(id)
fluidRow(
column(6, plotOutput(ns("plot1"), brush = ns("brush"))),
column(6, plotOutput(ns("plot2"), brush = ns("brush")))
)
}
linkedScatter <- function(input, output, session, data, left, right) {
# Yields the data frame with an additional column "selected_"
# that indicates whether that observation is brushed
dataWithSelection <- reactive({
brushedPoints(data(), input$brush, allRows = TRUE)
})
output$plot1 <- renderPlot({
scatterPlot(dataWithSelection(), left())
})
output$plot2 <- renderPlot({
scatterPlot(dataWithSelection(), right())
})
return(dataWithSelection)
}
scatterPlot <- function(data, cols) {
ggplot(data, aes_string(x = cols[1], y = cols[2])) +
geom_point(aes(color = selected_)) +
scale_color_manual(values = c("black", "#66D65C"), guide = FALSE)
}
ui <- fixedPage(
h2("Module example"),
linkedScatterUI("scatters"),
textOutput("summary")
)
server <- function(input, output, session) {
data(mpg)
### My modification
### making the reactive outside of module call
car_data <- reactive({
mpg
})
## Fix This doesn't work by reactive (var) no brackets()
## What is the syntax for being able to call car_data()?
df <- callModule(linkedScatter, "scatters", reactive(car_data),
left = reactive(c("cty", "hwy")),
right = reactive(c("drv", "hwy"))
)
output$summary <- renderText({
sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
})
}
shinyApp(ui, server)

Resources