How to modularize a simple bar plot in Shiny? - r

This is my app code:
app.R
library(shiny)
source("func.R")
# create data
name <- c("Moller", "Mayer", "Bernard")
sales <- c(35000, 40000, 60000)
df <- data.frame(name, sales)
# app
server <- function(input, output, session) {
x <- callModule(testPlot, "test", data = reactive(df), xAxis = reactive("name"), yAxis = reactive("sales"))
}
ui <- fluidPage(
testPlotUI(id = "test", stringName = "test")
)
shinyApp(ui = ui, server = server)
And this is my module code:
func.R
library(shiny)
library(ggplot2)
testPlotUI <- function(id, stringName){
ns <- NS(id)
fluidRow(
column(12,
plotOutput(stringName)
)
)
}
testPlot <- function(data, xAxis, yAxis){
output$test <- renderPlot({
ggplot(data(), aes_string(x=xAxis(), y=yAxis())) + geom_bar(stat = "identity")
})
}
This code ends up with this error:
Error in module(childScope$input, childScope$output, childScope, ...)
: unused arguments (childScope$input, childScope$output,
childScope)
How can I make this work?

The reason you are getting that error is that it is essential that the first three arguments to the server part of the module be input, output and session. So you need to change:
testPlot <- function(data, xAxis, yAxis){
output$test <- renderPlot({
ggplot(data(), aes_string(x=xAxis(), y=yAxis())) + geom_bar(stat = "identity")
})
}
into:
testPlot <- function(input, output, session, data, xAxis, yAxis){
output$test <- renderPlot({
ggplot(data(), aes_string(x=xAxis(), y=yAxis())) + geom_bar(stat = "identity")
})
}
With that change alone, your code will now run without any errors. However, nothing will appear. That is because you forgot another key component of using modules, which is to wrap all input/output ids in the ns() function. So change:
column(12,
plotOutput(stringName)
)
into:
column(12,
plotOutput(ns(stringName))
)
Now you should see your plot appear with no problems.

Related

How to dynamically change chart type?

I want to change chart type from e_line to e_bar based on a condition. What I tried to do was using some reactive expression or if else inside plot, but neither of them works. Any ideas how to tackle this?
So, I need to change dynamically e_line, I tried this:
newChartType <- reactive({
df = switch(
someCondition,
'1' = echarts4r::e_line(ColumnName2),
'2' = echarts4r::e_bar(ColumnName2)
)
})
output$plot <- echarts4r::renderEcharts4r({
dataChartStats() %>%
echarts4r::e_charts(ColumnName1) %>%
newChartType() %>%
echarts4r::e_legend(show = FALSE)
})
but it doesn't work. I'm interested in general rule on how to change dynamically building elements of plot code (can be ggplot as well etc, here I used echarts4r).
I couldn't find a way of obtaining the chart type directly from an input element, but here's one way of doing it:
library(shiny)
library(tidyverse)
ui <- fluidPage(
selectInput(
"type",
"Select a chart type:",
c("point", "line")),
plotOutput("plot")
)
server <- function(input, output) {
output$plot <- renderPlot({
if (input$type == "line") {
mtcars %>% ggplot() + geom_line(aes(x=mpg, y=disp))
} else {
mtcars %>% ggplot() + geom_point(aes(x=mpg, y=disp))
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
Next time, please provide a minimum working example.
EDIT in response to OP's request for a solution based on a button click:
library(shiny)
library(tidyverse)
ui <- fluidPage(
actionButton("go", "Click me!"),
textOutput("type"),
# selectInput(
# "type",
# "Select a chart type:",
# c("point", "line")),
plotOutput("plot")
)
server <- function(input, output) {
v <- reactiveValues(type="line")
observeEvent(input$go, {
if (v$type == "line") v$type <- "point"
else v$type <- "line"
})
output$type <- renderText({ v$type })
output$plot <- renderPlot({
if (v$type == "line") {
mtcars %>% ggplot() + geom_line(aes(x=mpg, y=disp))
} else {
mtcars %>% ggplot() + geom_point(aes(x=mpg, y=disp))
}
})
}
# Run the application
shinyApp(ui = ui, server = server)

ggplotly get data values of clicks

How can I get the x and y coordinates of an interactive map created with ggplot and plotly in R shiny? I want to get the x axis values and based on that display other data. Here is some dummy code.
library(shiny)
library(plotly)
library(ggplot2)
ui <- fluidPage(
plotlyOutput("distPlot")
)
server <- function(input, output) {
output$distPlot <- renderPlotly({
gg1 = iris %>% ggplot(aes(x = Petal.Length, y = Petal.Width)) + geom_point()
ggplotly(gg1)
})
}
shinyApp(ui = ui, server = server)
Maybe this is what your are looking for. The plotly package offers a function event_data() to get e.g. the coordinates of click events inside of a shiny app. See here. If you have multiple plots you could use the source argument to set an id and to get the event data for a specific plot:
library(shiny)
library(plotly)
library(ggplot2)
ui <- fluidPage(
plotlyOutput("distPlot"),
verbatimTextOutput("info")
)
server <- function(input, output) {
output$distPlot <- renderPlotly({
gg1 = iris %>% ggplot(aes(x = Petal.Length, y = Petal.Width)) + geom_point()
ggplotly(gg1, source = "Plot1")
})
output$info <- renderPrint({
d <- event_data("plotly_click", source = "Plot1")
if (is.null(d)) {
"Click events appear here (double-click to clear)"
} else {
x <- round(d$x, 2)
y <- round(d$y, 2)
cat("[", x, ", ", y, "]", sep = "")
}
})
}
shinyApp(ui = ui, server = server)

How can I unpick the renderPlot() function in shinyr with ggplot

Below is functioning code for a basic shiny app that allows the user to pick a column and then plots a ggplot::histogram() of the selected column:
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
titlePanel("ggplot"),
sidebarLayout(
sidebarPanel(
uiOutput("column_select")
),
mainPanel(plotOutput("plot"))
)
)
# Define server logic required to draw a histogram
server <- function(input, output){
dat <- reactive({iris})
output$column_select <- renderUI({selectInput("col", label = "column", choices = as.list(names(iris)), selected = "Sepal.Length")})
output$plot <- renderPlot({ggplot(dat(), aes_string(x = input$col)) +
geom_histogram()})
p <- ggplot(dat(), aes_string(x = input$col)) +
geom_histogram()
renderPlot
}
# Run the application
shinyApp(ui = ui, server = server)
I am not sure, however, why I am unable to remove the ggplot() function from within renderPlot() and still get the same result. I have tried:
p <- reactive({ggplot(dat(), aes_string(x = input$col)) +
geom_histogram()})
outputPlot <- renderPlot({p})
But this results in no plot being drawn.
I assume there is a simple fix to this, but thus far it escapes me.

How to access a column of a DataFrame from a passed variable in Shiny?

I'm trying to create a simple shiny app where the user can select a variable from a drop down menu and then a plot is generated. The selected variable is seen as input$Feature w/in the server function but I am getting errors when trying to access the column of my data frame as df$input$Feature. I'm not sure how to do this.
bw <- read.xls('filename')
ui <- fluidPage(
selectInput(inputId = 'Feature',
label = 'Select a feature to plot:',
c(colnames(bw))),
plotOutput('graph')
)
server <- function(input, output){
output$graph <- renderPlot({
p <- ggplot(bw, aes(bw$Date))
p <- p + geom_line(aes(y=bw$input$Feature, colour='red', group=1))
p <- p + labs(x = 'Date', y = 'Feature Name')
print(p)
})
}
shinyApp(ui=ui, server=server)
Try using this in the geom_line instead:
bw[,input$Feature]
Can't be of much more help without having a reproducible example to work with.
EDIT:
This worked for me:
bw <- mtcars
library(shiny)
ui <- fluidPage(
selectInput(inputId = 'Feature',
label = 'Select a feature to plot:',
c(colnames(bw)),
selected=colnames(bw)[1]),
verbatimTextOutput('value'),
plotOutput('graph')
)
server <- function(input, output){
output$value <- renderPrint(columname())
columname <- reactive({input$Feature})
output$graph <- renderPlot({
p <- ggplot(bw, aes(bw$qsec))+ geom_line(aes_string(y=bw[, input$Feature]))
p <- p + labs(x = 'Date', y = 'Feature Name')
p
})
}
shinyApp(ui=ui, server=server)

Calling variables from reactive functions inside render*()

I have a server.R file in the following form:
server.R
shinyServer(
function(input, output, session) {
mydata<- reactive({
df<- dataframe1
variable1
variable2
list(df, variable1, variable2)
})
output$plot<- renderPlot({
p<-ggplot(mydata()$df, aes(y=V8, x = 1:nrow(mydata()$df), fill = V8))
print(p)
})
})
My issue is that the call to ggplot, while it seems to recognize mydata$df(), it returns the error
Error in nrow(mydata()$df) : could not find function "mydata".
I am not sure where my syntax is wrong. Can anyone shed some light? Thanks!
To my knowledge, reactive shiny objects don't play well with lists. As it appears you aren't using 'variable1' and 'variable2' just omit them and just do the dataframe (which I assume has been made globally accessible and isn't imported?). It also could simply be calling the reactive before the ggplot call, but I err towards simplicity if not using those extra variables. A very quick example:
runApp(
list(ui = basicPage(
h1('Demo Shiny'),
plotOutput("plot")
)
,server = function(input, output) {
mydata <- reactive({
dataframe1 <- data.frame(cond = rep(c("A", "B"), each=10),
xvar = 1:20 + rnorm(20,sd=3),
yvar = 1:20 + rnorm(20,sd=3))
dataframe1
})
output$plot = renderPlot({
df <- mydata()
p<-ggplot(df, aes(x=xvar, y = yvar)) + geom_point()
print(p)
})
})
)
I'm going to shamless steal most of #charles code, but i think the problem in this case is actually your aes(). This seems to work
runApp(
list(ui = basicPage(
h1('Demo Shiny'),
plotOutput("plot")
)
,server = function(input, output) {
mydata <- reactive({
df <- data.frame( V8=sample(1:4, 20, replace=T))
list(df=df, variable1=1, variable2=2)
})
output$plot = renderPlot({
p<-ggplot(mydata()$df, aes(x=seq_along(V8), y = V8)) + geom_point()
print(p)
})
})
)
The problem was referring to variables in your aes that were not in your data.frame that you passed to ggplot2. Here by making sure to include a proper variable from the df, we seem to be fine.

Resources