Working with reactiveValues and ggplot in shiny - r

Im using R and shiny to query an SQL database. The user can search and add to a reactive data frame, the output of which is plotted in ggplot. However, I need to change the columns of the reactive data frames to factors for plotting. I can do this directly with ggplot (aes(factor(...), ). However, if I add the option of changing the plotted variable using a reactive input, I must use aes_string. If I use aes_string it does not like aes(factor(...),. Here is a working example:
Server:
# Create example data
set.seed(10)
MeasurementA <- rnorm(1000, 5, 2)
MeasurementB <- rnorm(1000, 5, 2)
Wafer <- rep(c(1:100), each=10)
ID <- rep(c(101:200), each=10)
Batch <- rep(c(1:10), each=100)
dd <- data.frame(Batch, Wafer, ID, MeasurementA, MeasurementB)
# Create local connection (in reality this will be a connection to a host site)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "dd", dd)
query <- function(...) dbGetQuery(con, ...)
# Create empty data frames to populate
wq = data.frame()
sq = data.frame()
shinyServer(function(input, output){
# create data frame to store reactive data set from query
values <- reactiveValues()
values$df <- data.frame()
# Action button for first query
d <- eventReactive(input$do, { input$wafer })
# First stage of reactive query
a <- reactive({ paste("Select ID from dd where Wafer=",d(), sep="") })
wq <- reactive({ query( a() ) })
# Output to confirm query is correct
output$que <- renderPrint({ a() })
output$pos <- renderPrint( wq()[1,1] )
# Action button to add results from query to a data frame
e <- eventReactive(input$do2, { wq()[1,1] })
b <- reactive({ paste("select Wafer, Batch, MeasurementA, MeasurementB from dd where ID=",e()," Order by ID asc ;", sep="") })
# observe e() so that data is not added until user presses action button
observe({
if (!is.null(e())) {
sq <- reactive({ query( b() ) })
# add query to reactive data frame
values$df <- rbind(isolate(values$df), sq())
}
})
# output of results
# Without mesurement choice (works)
output$boxV <- renderPlot({
ggplot(values$df, aes(factor(Wafer), MeasurementA, fill=factor(Batch))) + geom_boxplot()
})
# With measurement choice (doesnt work)
#output$boxV <- renderPlot({
#ggplot(values$df, aes_string(factor('Wafer'), input$char, fill=factor('Batch'))) + geom_boxplot()
#})
})
UI:
library(markdown)
shinyUI(fluidPage(
titlePanel("Generic grapher"),
sidebarLayout(
sidebarPanel(
numericInput("wafer", label = h3("Input wafer ID:"), value = NULL),
actionButton("do", "Search wafer"),
actionButton("do2", "Add to data frame"),
selectInput("char", label="Boxplot choice:",
choices = list("A"="MeasurementA", "B"="MeasurementB"),
selected="Von.fwd")
),
mainPanel(
verbatimTextOutput("que"),
verbatimTextOutput("pos"),
plotOutput("boxV")
)
)
)
)
Ive added output plot code for both working and non-working (non-working is commented out).
Now, ive read this (Formatting reactive data.frames in Shiny) and this (R shiny modify reactive data frame) but im confused. Because im using reactiveValues to store data, I use the code values$df to access the data...but what if i I want to turn a column to a factor for purpose of above? this doesnt seem to work:
new <- reactive(as.factor(values$df$Wafer))
Perhaps I am barking up the wrong tree with this?

Ok, I solved the problem by changing the data type within the query itself:
b <- reactive({ paste("select cast(Wafer as varchar) as Wafer, cast(Batch as varchar) as Batch, MeasurementA, MeasurementB from dd where ID=",e()," Order by ID asc ;", sep="") })
That way I didnt have to mess about afterwards. It works for me but if anyone reading this wants to tell me that its a bad idea, please do. Im new to SQL and R so please correct me so I can learn. Thanks

Related

Update variable created by eventReactive in another observeEvent

I'm struggling to update a reactive variable, that is created with eventReactive(), in an observeEvent() with new data.
The background is following: I have a data.frame df with some variables (x and y) and number of observations depending on the selected city (created randomly for this example).
x and y are initialized with zeros.
Because I need to further process df, I pass df to city_df in an eventReactive().
So far, so good. Next, I want to add new data to city_df. The computation of this new data is dependent on the "compute" actionButton (input$compute), wherefore I update city_df in an observeEvent(). I manage to read the data stored in city_df, but I am struggling to overwrite its content.
Actually, I am a bit unsure if this is possible at all, but I hope that some of you could give me a hint on how to update the reactive variable city_df with the new data in this observeEvent() and have its output evaluated in the app(?).
library(shiny)
# global variables
cities <- c("Nairobi", "Kansas", "Uppsala", "Sangon", "Auckland", "Temuco")
# ui
ui <- fluidPage(
fluidPage(
fluidRow(
column(2,
selectInput("city", "Select city",
choices = cities,
selected = sample(cities,
size = 1)
),
actionButton("compute",
"Compute")),
column(8,
verbatimTextOutput("the_city"))
))
)
# server
server <- function(input, output, session) {
# create variable
city_df <- eventReactive(input$city, {
len <- round(runif(1, 20, 50), 0)
df <- data.frame(city = rep(input$city, len))
# initialize x and y with zeros
df <- cbind(df,
data.frame(x = rep.int(0, len),
y = rep.int(0, len)))
})
output$the_city <- renderText({
paste(city_df())
})
observeEvent(input$compute, {
# grab data
test <- city_df()
# compute new data
test$x <- runif(dim(test)[1], 11, 12)
test$y <- runif(dim(test)[1], 100, 1000)
# and how to send this values back to city_df?
})
}
# run app
shinyApp(ui, server)
The actual app is far more complex--so forgive me if this MWE app seems a bit overly complicated to achieve this usually simple task (I hope I managed to represent the more complex case in the MWE).
Instead of a data.frame, I am parsing layers of a GeoPackage and append some variables initialized with zeros. The selected layer is displayed in a Leaflet map. On pressing the "compute" button, a function computes new data that I wish to add to the layer to then have it displayed on the map.
The alternative solution I have on mind is to write the new data to the GeoPackage and then, reread the layer. However, I would appreciate if I could avoid this detour as loading the layer takes some time...
Many thanks :)
Rather than using an eventReactive, if you use a proper reactiveVal, then you can change the value whenever you like. Here's what that would look like
server <- function(input, output, session) {
# create variable
city_df <- reactiveVal(NULL)
observeEvent(input$city, {
len <- round(runif(1, 20, 50), 0)
df <- data.frame(city = rep(input$city, len))
# initialize x and y with zeros
df <- cbind(df,
data.frame(x = rep.int(0, len),
y = rep.int(0, len)))
city_df(df)
})
output$the_city <- renderText({
paste(city_df())
})
observeEvent(input$compute, {
# grab data
test <- city_df()
test$x <- runif(dim(test)[1], 11, 12)
test$y <- runif(dim(test)[1], 100, 1000)
city_df(test)
})
}
So calling city_df() get the current value and calling city_df(newval) updates the variable with a new value. We just swap out the eventReactive with observeEvent and do the updating ourselves.

Updating a data frame in real time in RShiny

I am trying to get my head around RShiny by building what I thought would be a pretty simple but useful app. What I would like the app to do is allow the user to input some data made up of dates, numeric, and characters. Then when the user presses a save/submit button this data is appended onto a pre-existing data frame made up of previous recordings and over write the .csv of these recordings. I would also like this data to be presented to the users in the form of a table in the UI which is updated as soon as the user presses the save/submit button.
I have managed to get the majority of the UI features working, however, I am having real difficulty 1) saving the data in the correct format and 2) updating the table displayed on the UI. My current method of saving the data involves creating an isolated list of the input values and rbinding this to the original data frame. However, the formats of the input values appear to all revert to factors which is especially problematic with the date as the output is meaningless as far as I am aware. In terms of updating the UI I have attempted to create a reactive object out of the data frame and use this object as the data displayed in renderDataTable but this approach seems to have no affect.
I have created a dummy minimal example below.
Thank you for all your help in advance.
require(shiny)
require(tidyverse)
require(lubridate)
require(plotly)
#Would import the data in reality using read.csv() but to allow for an easily
#recreated example I made a dummy data frame
DateRecorded <- dmy(c("10/07/2018", "11/07/2018", "13/07/2018"))
Value <- c(1, 2, 3)
Person <- c("Bob", "Amy", "Charlotte")
df <- data.frame(DateRecorded, Value, Person)
ui <- fluidPage(
#UI Inputs
dateInput(inputId = "SessionDate", label = "Date Recorded", format = "dd-mm-yyyy"),
numericInput(inputId = "SessionValue", label = "Value Recorded", value = 0),
textInput(inputId = "SessionPerson", label = "Person Recording"),
actionButton(inputId = "Save", label = "Save"),
#UI Outputs
dataTableOutput("TheData"),
textOutput("TotRecorded")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#When "Save" is pressed should append data to df and export
observeEvent(input$Save, {
newLine <- isolate(c(input$SessionDate, input$SessionValue, input$SessionPerson))
isolate(df <- rbind(as.matrix(df), unlist(newLine)))
write.csv(df, "ExampleDataFrame.csv") #This export works but the date is saved incorrectly as "17729" not sure why
})
#Create a reactive dataset to allow for easy updating
ReactiveDf <- reactive({
df
})
#Create the table of all the data
output$TheData <- renderDataTable({
ReactiveDf()
})
#Create the totals print outs
output$TotRecorded <- renderPrint({
data <- ReactiveDf()
cat(nrow(data))
})
}
# Run the application
shinyApp(ui = ui, server = server)
I made some small tweaks.
You do not need isolate in the body of the observeEvent; it does not take a reactive dependency to values in its body.
I made ReactiveDf a reactiveVal instead of a reactive. This allows you to write its value from inside an observeEvent.
Instead of rowbinding a matrix and unlisting a list - the issue is that all the new values are parsed to the same class, while they are obviously not - it might be easier to rbind two data.frames, so create the newLine with newLine <- data.frame(DateRecorded = input$SessionDate, Value = input$SessionValue, Person = input$SessionPerson)
So a working example would be as shown below. Hope this helps!
require(shiny)
require(tidyverse)
require(lubridate)
require(plotly)
#Would import the data in reality using read.csv() but to allow for an easily
#recreated example I made a dummy data frame
DateRecorded <- dmy(c("10/07/2018", "11/07/2018", "13/07/2018"))
Value <- c(1, 2, 3)
Person <- c("Bob", "Amy", "Charlotte")
df <- data.frame(DateRecorded, Value, Person)
ui <- fluidPage(
#UI Inputs
dateInput(inputId = "SessionDate", label = "Date Recorded", format = "dd-mm-yyyy"),
numericInput(inputId = "SessionValue", label = "Value Recorded", value = 0),
textInput(inputId = "SessionPerson", label = "Person Recording"),
actionButton(inputId = "Save", label = "Save"),
#UI Outputs
dataTableOutput("TheData"),
textOutput("TotRecorded")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#When "Save" is pressed should append data to df and export
observeEvent(input$Save, {
newLine <- data.frame(DateRecorded = input$SessionDate, Value = input$SessionValue, Person = input$SessionPerson)
df <- rbind(df, newLine)
ReactiveDf(df) # set reactiveVal's value.
write.csv(df, "ExampleDataFrame.csv") #This export works but the date is saved incorrectly as "17729" not sure why
})
#Create a reactive dataset to allow for easy updating
ReactiveDf <- reactiveVal(df)
#Create the table of all the data
output$TheData <- renderDataTable({
ReactiveDf()
})
#Create the totals print outs
output$TotRecorded <- renderPrint({
data <- ReactiveDf()
cat(nrow(data))
})
}
# Run the application
shinyApp(ui = ui, server = server)

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)

Shiny - renaming factors in reactive data frame

I'm building a shiny app that queries an SQL database so the user can ggplot the data. I would like the user to be able to rename factors manually but am struggling to get going. Here is an example of what I want to do:
ui.R
library(markdown)
shinyUI(fluidPage(
titlePanel("Reactive factor label"),
sidebarLayout(
sidebarPanel(
numericInput("wafer", label = h3("Input wafer ID:"), value = NULL),
actionButton("do", "Search wafer"),
textInput("text", label = h3("Factor name to change"), value = ""),
textInput("text", label = h3("New factor name"), value = ""),
actionButton("do2", "Change name")
),
mainPanel(
verbatimTextOutput("waf"),
verbatimTextOutput("que"),
verbatimTextOutput("pos"),
dataTableOutput(outputId="tstat")
)
)
)
)
server.R
# Create example data
Name <- factor(c("Happy", "New", "Year"))
Id <- 1:3
dd <- data.frame(Id, Name)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "dd", dd)
query <- function(...) dbGetQuery(con, ...)
wq = data.frame()
sq = data.frame()
shinyServer(function(input, output){
# create data frame to store reactive data set from query
values <- reactiveValues()
values$df <- data.frame()
# Wait for user to search
d <- eventReactive(input$do, { input$wafer })
# Reactive SQL query
a <- reactive({ paste0("Select * from dd where Id=",d()) })
wq <- reactive({ query( a() ) })
# Check outputs
output$waf <- renderPrint(input$wafer)
output$que <- renderPrint({ a() })
output$pos <- renderPrint( wq()[1,1] )
# observe d() so that data is not added until user presses action button
observe({
if (!is.null(d())) {
sq <- reactive({ query( a() ) })
# add query to reactive data frame
values$df <- rbind(isolate(values$df), sq())
}
})
output$tstat <- renderDataTable({
data <- values$df
})
})
In static R I would normally use data table to rename factors i.e.:
DT <- data.table(df)
DT[Name=="Happy", Name:="Sad"]
But I'm not sure how to go about this with a reactiveValues i.e. values$df.
I have read this (R shiny: How to get an reactive data frame updated each time pressing an actionButton without creating a new reactive data frame?). This lead me to try this but it doesn't do anything (even no error):
observeEvent(input$do2, {
DT <- data.table(values$df)
DT[Name == input$text1, Name := input$text2]
values$df <- data.frame(values$df)
})
Perhaps there is a way around this..maybe there is a way to use an action button to "lock in" the data as a new data frame, which can then be used to rename?
Sorry for such a long winded question. My real app is much longer and more complex. I have tried to strip it down.
Your approach works but there are a few issues in your app.
In ui.R, both textInput have the same id, they need to be different so you can refer to them in the server.R. In the observeEvent you posted, you refer to input$text1 and input$text2 so you should change the id of the textInputs to text1 and text2.
In the observeEvent you posted, the last line should be values$df <- as.data.frame(DT), otherwise it does not change anything.

How to pass input variable to SQL statement in R shiny?

Usually, variables can be passed to SQL statements using paste. Interestingly this doesn't work with input variables in R shiny. Using the below code I get the following error message. How can I solve this?
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.)
--ui.R--
shinyUI(bootstrapPage(
selectInput(inputId = "segment",
label = "segment",
choices = c(1, 2, 3, 4),
selected = 1),
plotOutput(outputId = "main_plot", height = "300px")
))
--server.R--
shinyServer(function(input, output) {
database <- dbConnect(MySQL(), group= "zugangsdaten", dbname= 'database')
input<- input$segment
table <- dbGetQuery(database, statement =
paste("
SELECT a,b FROM table1
WHERE id = ",input,"
AND created_at>='2015-08-01'
"))
output$main_plot <- renderPlot({
plot(a,b)
})
})
The data query needs to be evaluated in a reactive context.
One way would be to move the data query itself into the renderPlot() context e.g.
--server.R--
shinyServer(function(input, output) {
database <- dbConnect(MySQL(), group= "zugangsdaten", dbname= 'database')
output$main_plot <- renderPlot({
table <- dbGetQuery(database, statement =
paste("
SELECT a,b FROM table1
WHERE id = ",input$segment,"
AND created_at>='2015-08-01'
"))
plot(table$a,table$b)
})
})
However, it's better to construct a reactive conductor for the data which can be evaluated once when any updates happen and re-used in multiple reactive end-points (see here for details).
This would look something like:
--server.R--
shinyServer(function(input, output) {
database <- dbConnect(MySQL(), group= "zugangsdaten", dbname= 'database')
table <- reactive({
dbGetQuery(database, statement =
paste("
SELECT a,b FROM table1
WHERE id = ",input$segment,"
AND created_at>='2015-08-01'
")
)
})
output$main_plot <- renderPlot({
plot(table()$a,table()$b)
})
})
For flexibility you can also use sub function to substitute part of the query string, this is quite clean approach
table <- reactive({
my_query <- 'SELECT a,b FROM table1 WHERE id = SOMETHING AND created_at >= 2015-08-01'
my_query <- sub("SOMETHING",input$segment,my_query)
dbGetQuery(database,noquote(my_query))
})

Resources