R: Referring to a variable name with special characters - r

For a Shiny program I'm writing, I have input variables that contain a dash, commas and brackets. Spaces I can substitute out but the rest are needed since they are refering to chemical compounds and don't make sense without them. As expected, these characters make the Shiny app unable to find the desired variable; whilst variables with none of these characters work fine.
EDITED: The code below is a test Shiny app. With Chemical-X(a,b) the app returns "could not find function X". With Chemical.B the app returns "object Chemical.B not found" which is the desired result since the app sees the chemical as an object and not some function that doesn't exist.
library (shiny)
library (ggplot2)
dat <- as.data.frame(c("Chemical-X(a,b)", "Chemical.B"))
dat[,2] <- (c(6,3))
colnames(dat) <- c("Chemical", "Count")
ui <- fluidPage(
titlePanel("SE Test"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "varX",
label = "Chemical",
choices = dat[,1],
width = "200px"),
selectInput(inputId = "varY1",
label = "Count",
choices = dat[,2],
width = "200px")
),
mainPanel(
plotOutput("chemPlot")
)
)
)
server <- function(input, output){
output$chemPlot <- renderPlot({
plot.data <- ggplot(data = dat)
point <- plot.data + geom_point(
aes_string(x = input$varX, y = input$varY1))
plot(point)
})
}
shinyApp(ui = ui, server = server)
Is there a known way of doing this or will I need to come up with some viable work around? I have tried using backticks as suggested here but this hasn't worked.
Thanks, Matt

I have found that backticks and aes_string usually works for me.
library("ggplot2")
my_dodgy_var <- "name with~special character"
mtcars[[my_dodgy_var]] <- mtcars$cyl
ggplot(mtcars, aes_string(x=paste0("`", my_dodgy_var, "`"), y="mpg")) +
geom_point()
I often use a helper function paste_aes to do this, eg:
paste_aes <- function(x) paste0("`", x, "`")

I've fixed it now by calling as.name the Shiny input$ variable. For the example above it would look like this.
server <- function(input, output){
output$chemPlot <- renderPlot({
plot.data <- ggplot(data = dat)
point <- plot.data + geom_point(
aes_string(x = as.name(input$varX), y = as.name(input$varY1)))
plot(point)
This appears to work now as intended. Thank you aocall for your efforts.

Related

R Shiny - Create an interactive group_by() and summarise() plot inside a dashboard

Brand new to R so wondering if someone can help me understand where I'm going wrong here. Apologies if I've butchered the format or handed you terrible code to work from.
My goal is to get dashboard out containing a plot, whereby I can choose a column from my data and group_by that selected column, then calculate the mean of a different column for each bin in the selected column. Giving me a graph with the column selected across the x-axis and the average calculated up the y. Hopefully the code I've tried so far will shed more light on this:
library(tidyverse)
library(shiny)
library(shinydashboard)
df <-
data.frame(Car_Col = c("Red","Blue","Red","Black","white","Black","Black","White","Red","Blue"),
Car_Type = c("E","G","D","G","G","D","G","D","E","G"),
Sold = (c(1,0,1,0,0,0,1,1,0,0)))
## app.R ##
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(fluidRow(selectInput(inputId = "x",
label = "Select variable",
choices = c("Car_Col","Car_Type", selected = "Car_Col"))),
plotOutput("graph1")
)
)
server <- function(input, output) {
output$graph1 <- renderPlot({
df_summarise <-
df %>%
group_by(get(input$x)) %>%
summarise(Avg_Sold = mean(Sold)) %>%
ungroup() >%>
ggplot(df, mapping = aes(x=get(input$x), y=Avg_Sold, group=1) +geom_point(size=2)) +geom_line()
})
}
shinyApp(ui, server)
So for instance I want a plot where the user can select if they want to see the average sold across Car_Col, or across Car_Type - obviously wanting them calculated uniquely to the selection.
So for example the mean value of sold cars across colour I'd want Red, Blue, Black and White across the x-axis, with their respective means (so red having 2 sold and 3 cars should be at 0.67, etc)
Currently the error I'm getting is this:
Warning: Error in group_by: Problem adding computed columns.
Caused by error in `mutate()`:
! Problem while computing `..1 = get(input$x)`.
Caused by error in `get()`:
! invalid first argument
...
...
Warning: Error in ggplot: Mapping should be created with `aes()` or `aes_()`.
I believe from what I can find from other similar questions that I'm treating the input$x incorrectly and the error is stemming, as above, from the get(input$x), but whenever I try to do it outside of the plot then it's outside of a reactive environment - and I can't make that work either (though I only tried something like using var = reactive({input$x)} and referencing var() in place of get(input$x) but that didn't work either).
Any help would be appreciated, thank you!
There are several issues with your code. First you placed several closing parentheses in the wrong place. Second, you pipe your data into ggplot but additionally pass df to the data argument. Third, there is a typo in one of the pipes, i.e. you use >%> instead of %>%. Finally group_by(get(input$x)) will not work. Use e.g. group_by(across(input$x)) or group_by(data[[input$x]]) as I do below.
Additionally, while x=get(input$x) works I would still suggest to use x = .data[[input$x]].
library(tidyverse)
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidRow(selectInput(
inputId = "x",
label = "Select variable",
choices = c("Car_Col", "Car_Type"), selected = "Car_Col"
)),
plotOutput("graph1")
)
)
server <- function(input, output) {
output$graph1 <- renderPlot({
df %>%
group_by(.data[[input$x]]) %>%
summarise(Avg_Sold = mean(Sold)) %>%
ungroup() %>%
ggplot(mapping = aes(x = .data[[input$x]], y = Avg_Sold, group = 1)) +
geom_point(size = 2) +
geom_line()
})
}
shinyApp(ui, server)
#>
#> Listening on http://127.0.0.1:5153
library(tidyverse)
library(shiny)
library(shinydashboard)
df <-
data.frame(Car_Col = c("Red","Blue","Red","Black","white","Black","Black","White","Red","Blue"),
Car_Type = c("E","G","D","G","G","D","G","D","E","G"),
Sold = (c(1,0,1,0,0,0,1,1,0,0)))
## app.R ##
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(fluidRow(selectInput(inputId = "x",
label = "Select variable",
choices = c("Car_Col","Car_Type", selected = "Car_Col"))),
plotOutput("graph1")
)
)
server <- function(input, output) {
output$graph1 <- renderPlot({
req(input$x)
df %>%
group_by_at(input$x) %>%
summarise(Avg_Sold = mean(Sold)) %>%
ungroup() %>%
ggplot(mapping = aes_string(x=input$x, y='Avg_Sold', group=1)) +
geom_point(size=2) +
geom_line()
})
}
shinyApp(ui, server)
value in input$x is a string, you need to use group_by_at which accepts a character vector. In aes, it needs column name without quotes, so we use the string version aes_string.

How do I create a dataframe from merging csv files and then create a shiny app based on it?

I have a question which may be stupid, but I just wanted to create my first R shiny app. I was thinking of writing a code in which 2 separate csv files would be read and then combined into a single dataframe. I need to add a couple columns in each dataframe before combining them, as shown in the code. So I thought I could manipulate the data outside of the ui and server and then create the app which would allow the user to select what they want to be plotted on each axis. The first part of the code outside the shiny part works fine and I get the desired combined dataframe. However, when I run the code the dataframe does not seem to be created and as such I end up with an empty interface.
Any help would be greatly appreciated! Attached I have a picture of the columns in one of the csv files, the second csv file is similar.
shop1.csv
library(tidyverse)
library(shiny)
twofiles<-c("shop1.csv","shop2.csv")
shop_list<-lapply(twofiles, read.csv, header=TRUE, sep=",")
shop_list<- lapply(seq_along(shop_list), function(i){
df <- shop_list[[i]]
df<-transform(df,ratio1=price_apples/price_pears)
df<-transform(df,ratio2=price_apples/price_cherries)
df<-transform(df,datano=i)
})
finaldata <- do.call(rbind, shop_list)
finaldata$datano <- factor(finaldata$datano)
ui<-fluidPage(
titlePanel("Shops plots"),
sidebarMenu(
selectInput(inputId = "x", label = "Select x-axis:",
choices = c("Year"="year","Hour"="hour"), selected="year"),
selectInput(inputId = "y", label = "Select y-axis:",
choices = c("Ratio 1"="ratio1","Ratio 2"="ratio2"),selected="ratio1"),
mainPanel(
plotOutput("plot")
)
)
)
server<-function(input, output) {
output$plot <-renderPlot({
ggplot(finaldata, aes(x=input$x, y=input$y, group=datano, color = datano)) +
geom_line()
})
}
shinyApp(ui=ui, server=server)
Too long for comments:
Here is the example code with the mtcars dataset:
You have to use aes_string not aes in ggplot.
Dataframe manipulation including loading etc.. should be done server side.
One more thing is that group=datano and color=datano may prone to the problem also. Try here yourdataframe$datano
library(shiny)
library(dplyr)
library(ggplot2)
ui<-fluidPage(
titlePanel("XXX"),
sidebarMenu(
selectInput(inputId = "x", label = "Select x-axis:",
choices = c("mpg"="mpg","disp"="disp"), selected="mpg"),
selectInput(inputId = "y", label = "Select y-axis:",
choices = c("hp"="hp","drat"="drat"),selected="hp"),
mainPanel(
plotOutput("plot")
)
)
)
server<-function(input, output,session) {
output$plot <-renderPlot({
ggplot(finaldata, aes_string(x=input$x, y=input$y, group=mtcars$cyl, color = mtcars$cyl)) +
geom_line()
})
}
shinyApp(ui=ui, server=server)
I think the problem you're having has nothing to do with the frame, but with programmatic use of ggplot2. In general, look up ggplot2 and quasiquotation and you'll find yourself in a quagmire of NSE (non-standard evaluation) mechanisms that you thought you'd never need (or never knew you would need, to be honest).
I can replicate some non-workingness with this simple non-shiny example.
library(ggplot2)
input <- list(x = "mpg", y = "disp")
This produces the wrong chart, just a single point:
ggplot(mtcars, aes(x=input$x, y=input$y)) + geom_point()
But if you switch to this, it should work:
ggplot(mtcars, aes(x=!!sym(input$x), y=!!sym(input$y))) + geom_point()
That is, using !!sym(input$x) instead of just input$x (and for anything else you will be placing within aes(...).
If you're curious, the older (soft-deprecated) solution would have been to use aes_string, which still works (but I don't know for how long):
ggplot(mtcars, aes_string(x=input$x, y=input$y)) + geom_point()
Side note:
I think it'd be a bit more direct (and code-golf simpler) to read in your data as:
twofiles <- setNames(nm = c("shop1.csv","shop2.csv"))
shop_list <- lapply(twofiles, read.csv, header=TRUE, sep=",")
finaldata <- transform(
dplyr::bind_rows(shop_list, .id = "datano"),
ratio1 = price_apples/price_pears,
ratio2 = price_apples/price_cherries,
datano = factor(datano)
)
Though since you're already pulling in all of tidyverse, you might as well switch to the dplyr-methods ... not that it buys you a lot here.

ggplot not working properly inside eventReactive() in shiny

I wasted hours to find out why my plot is automatically updating itself when I change inputs while it was supposed to wait for the Run button but it simply ignored that step and I ended up finally finding ggplot as the trouble maker!!! This is my minimal code:
library(ggplot2)
library(tidyverse)
varnames <- names(cars)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(
width = 12,
# Variables Inputs:
varSelectInput("variables", "Select Input Variables", cars, multiple = TRUE),
selectizeInput("outvar", "Select Output Variable", choices = varnames, "speed", multiple = F),
# Run Button
actionButton(inputId = "run", label = "Run")
)
)
),
# Main panel for displaying outputs ----
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output, session) {
df <- reactive({
cars %>% dplyr::select(!!!input$variables, input$outvar)
})
plt <- eventReactive(input$run, {
#Just creating lm formula
current_formula <- paste0(input$outvar, " ~ ", paste0(input$variables, collapse = " + "))
current_formula <- as.formula(current_formula)
#Fitting lm
fit <- lm(current_formula, data = df())
pred <- predict(fit, newdata = df())
#Plotting
ggplot(df(), aes(df()[, input$outvar], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
#plot(df()[, input$outvar], pred) #This one works fine!!!!
})
output$plot <- renderPlot({
plt()
})
}
# Run the application
shinyApp(ui = ui, server = server)
If you run this, you'll notice that ggplot doesn't care anymore about the Run button after the 1st run and it keeps updating as you change the inputs!! However, if you use the simple base plot function (which I put in a comment in the code) there wouldn't be any problems and that works just fine! Sadly I need ggplot in my app because base plot is ugly. I am seeing suggestion for using isolate() to solve this issue but I have no clue where isolate() should be put to fix my problem also it doesn't make sense to use isolate() when base plot function works fine without it and it's the ggplot that makes the problem. Any explanation would be appreciated.
The issue is that ggplot aesthetics are lazy evaluated. You really want to put symbols into the aes() rather that reactive data values. Change your plotting code to
ggplot(df(), aes(.data[[input$outvar]], pred)) +
labs(x = "Observed", y = "Predicted") +
geom_point() +
theme_bw()
With ggplot you use the .data pronoun to access the current data source rather than trigger the reactive df() object again.

R Shiny Interactive plot title for ggplot

I am trying to create Shiny App which is able to display interactive plot title (dependent on the choosen value for x axis)
Very simple example:
library(shiny)
library(DT)
library(ggplot2)
x <- as.numeric(1:1000000)
y <- as.numeric(1:1000000)
z <- as.numeric(1:1000000)
data <- data.frame(x,y, z)
shinyApp(
ui = fluidPage(selectInput(inputId = "yaxis",
label = "Y-axis",
choices = list("x","y","z"),
selected = c("x")),
dataTableOutput('tableId'),
plotOutput('plot1')),
server = function(input, output) {
output$tableId = renderDataTable({
datatable(data, options = list(pageLength = 10, lengthMenu=c(10,20,30)))
})
output$plot1 = renderPlot({
filtered_data <- data[input$tableId_rows_all, ]
ggplot(data=filtered_data, aes_string(x="x",y=input$yaxis)) + geom_line()
})
}
)
I have tried this code:
ggtitle("Line plot of x vs",input$yaxis)
It was not working, plot has not been displayed, giving me an Error:
Warning: Error in ggtitle: unused argument (input$yaxis)
[IMPORTANT]
using ggtitle(input$yaxis) gives me an interactive title, however i need to build up a sentence (like: Line plot of x vs input$yaxis), in which the reactive argument (input$yaxis) is a part of it!
Thanks for any help!
Cheers
Change:
ggtitle("Line plot of x vs",input$yaxis)
To
ggtitle(paste("Line plot of x vs",input$yaxis))
As the error suggests, you have too many arguments passed to the ggtitle function, paste will create a single character out of your two inputs, with a space in between. You can vary the separation between the two with sep =.

Error in eval(expr, envir, enclos) : object 'input' not found

I'm trying to allow the user to select which column of data (selecty) they would like to make a plot of. Everything else in my app works except for this part. Here are the relevant portions of my code:
#ui.R
library(shiny)
library(ggplot2)
shinyUI(fluidPage(h1("Title 1"),
# Sidebar
sidebarLayout(
sidebarPanel(
h3("Title 2"),
selectInput("pnum",
label = "Select the patient number",
choices = unique(pnums), selected = pnums[1]),
selectInput("selecty",
label = "Choose a variable to display on the y-axis",
choices = list('Var1', 'Var2', 'Var3', 'Var4')),
dateRangeInput("dates", label= "Date Range"),
submitButton("Create Graph")
),
# Show a plot
mainPanel(
plotOutput('makeplot')
)
)
))
#server.R
#relevant portion only - ggplot
library(shiny)
require(RODBC)
library(ggplot2)
library(quantmod)
library(reshape)
shinyServer(function(input, output) {
output$makeplot <- renderPlot({
p <- ggplot(pdata(), aes(x = Time_Stamp, y = input$yselect)) + geom_point()
print(p)
})
})
The data is brought from an online database and is a reactive function. My main issue is that in the user select for the column, it has to be in single quotes, although I would like to transfer it over to the ggplot function without the quotes so it acts as the column name. I've also tried to make a switch statement within the 'y = ' but I get the same error.
First, in your ggplot call, you should have y = input$selectyto be consistent with your naming in ui.R.
Then, if I understood correctly, you should take a look at the aes_string function in ggplot because aes uses non-standard evaluation which allows you to specify variable names directly and not as characters. Here, input$yselect is passed as a character so you need to replace in server.R :
p <- ggplot(pdata(), aes(x = Time_Stamp, y = input$yselect)) + geom_point()
with
p <- ggplot(pdata(), aes_string(x = "Time_Stamp", y = input$selecty)) + geom_point()

Resources