Just discovering shiny apps but this is driving me insane.......I have looked at numerous examples of server.R and ui.R code and cannot figure out what I am doing wrong. Apologies in advance if it's something very basic..........
Taking the iris dataset as an example, I want to plot one column against another, something simple using qplot or preferably ggplot
However, using qplot I get this:
and using ggplot2, I get the error:
I don't think I need the reactive function as I'm not subsetting the dataset, just extracting columns to plot.
server.R code
library(shiny)
library(shinyapps)
library(ggplot2)
shinyServer(function(input, output, session) {
output$text1 <- renderText({input$id1})
output$text2 <- renderText({input$select1})
output$plot1 <- renderPlot({
g <- qplot(Sepal.Length, input$select1, data = iris)
print(g)
})
})
or using ggplot function to replace the qplot call
g <- ggplot(iris, aes(x = Sepal.Length, y = input$select1))
g <- g + geom_line(col = "green", lwd =1) +
labs(x = "Date", y = "Ranking") +
theme_bw() + scale_y_reverse()
ui.R code
library(shiny)
library(shinyapps)
data(iris)
opts <- unique(colnames(iris))
opts <- opts[-1] ## want to keep Sepal.Length as the x values
shinyUI(pageWithSidebar(
headerPanel('test with iris database'),
sidebarPanel(
selectInput(inputId = "select1", label = "select",
choices = opts),
textInput(inputId = "id1", label = "Input Text", "")
),
mainPanel(
p('Output text1'),
textOutput('text1'),
textOutput('text2'),
plotOutput('plot1')
)
))
Change your aes statement to aes_string and make x a string. This should fix the problem.
g <- ggplot(iris, aes_string(x = "Sepal.Length", y = input$select1))
g <- g + geom_line(col = "green", lwd =1) +
labs(x = "Date", y = "Ranking") +
theme_bw() + scale_y_reverse()
Related
My file is one set of game data and PitchNo tells the pitch of the game. I am trying to generate plots of each batter's pitches seen in a game and I have labeled the PitchNo in the ggplot function. I want my plot for each batter to label the pitches seen, say 1-10, but instead it is still labeling them as the PitchNo of the game.
I added Batter$PitchNo <- 1:nrow(Batter) in my reactive filter. That line of code works in normal R but I cannot seem to find a way to make in work in a Shiny app.
library(shiny)
library(tidyverse)
GameDataFile <- read_csv("GameDataFile.csv")
GameDataFile %>% group_by(PitchNo)
playerlist <- sort(unique(GameDataFile$Batter))
ui = fluidPage(
titlePanel("Title"),
sidebarLayout(
sidebarPanel(
selectInput(
inputId="Batter1",
label="Player:",
choices = playerlist)
),
mainPanel(
plotOutput("myZone")
)))
server = function(input, output){
output$myZone <- renderPlot({
GameDataFile$PlateLocSide <- (GameDataFile$PlateLocSide * -1)
newdata <- reactive({
GameDataFile %>% filter(
Batter %in% c(input$Batter1),
Batter$PitchNo <- 1:nrow(Batter)) # Error is here
})
ggplot(data = newdata(), aes(x = PlateLocSide, y = PlateLocHeight)) +
xlim(-2.5,2.5) + ylim(0,5) + scale_fill_viridis_c() + geom_point() +
labs(x = "", y = "") + facet_wrap(~ Batter, ncol = 2) +
geom_text(aes(label = PitchNo), vjust = -0.5) + theme_bw() +
theme(strip.text = element_text(size=20, face="bold"))
}, width=400, height=500)
}
shinyApp(ui = ui, server = server)
When I do a facet_grid in ggplotly() for a Shiny App, with a large number of faceting groups, the plot is messed up. However it works correctly outside Shiny.
How can I fix this?
I suspect it is linked to the Y scale but I couldn't find the solution.
Here's a reproducible example based on diamonds example from plotly.
Comparison of Shiny vs non Shiny outputs : Comparison of facet_grid outside and within Shiny
Code
Outside Shiny:
library(ggplot2)
data(diamonds, package = "ggplot2")
# new faceting group
diamonds$rdmGroup <- as.factor(sample(LETTERS, dim(diamonds)[1], replace=TRUE))
# subset of diamonds
diamonds <- diamonds[sample(nrow(diamonds), 1000),]
ggplot(diamonds , aes_string(x = diamonds$x, y = diamonds$y, color = diamonds$x)) +
geom_point() + facet_grid(rdmGroup~.) +
guides(color=FALSE) +
labs(x = "X", y="Y")
The same code in a Shiny App:
library(shiny)
library(plotly)
library(ggplot2)
data(diamonds, package = "ggplot2")
# new faceting group
diamonds$rdmGroup <- as.factor(sample(LETTERS, dim(diamonds)[1], replace=TRUE))
# subset of diamonds
diamonds <- diamonds[sample(nrow(diamonds), 1000),]
ui <- fluidPage(
headerPanel("Diamonds Explorer"),
sidebarPanel(
sliderInput('plotHeight', 'Height of plot (in pixels)',
min = 100, max = 2000, value = 1000)
),
mainPanel(
plotlyOutput('trendPlot')
)
)
server <- function(input, output) {
output$trendPlot <- renderPlotly({
p <- ggplot(diamonds, aes_string(x = diamonds$x, y =diamonds$y, color = diamonds$x)) +
geom_point()+ facet_grid(rdmGroup~., scales = "free_y") +
labs(x = "X", y="Y")
ggplotly(p) %>%
layout(height = input$plotHeight, autosize=TRUE)
})
}
shinyApp(ui, server)
PS: I used aes_string() instead of aes() intentionally as I need it in my real app.
The first thing to note is that the problem has nothing to do with Shiny but rather your use of ggplotly. The problem can be replicated with just:
library(ggplot2)
library(plotly)
data(diamonds, package = "ggplot2")
# new faceting group
diamonds$rdmGroup <- as.factor(sample(LETTERS, dim(diamonds)[1], replace=TRUE))
# subset of diamonds
diamonds <- diamonds[sample(nrow(diamonds), 1000),]
p <- ggplot(diamonds , aes_string(x = diamonds$x, y = diamonds$y, color = diamonds$x)) +
geom_point() + facet_grid(rdmGroup~.)
ggplotly(p)
though you will need something to view the output in, which may well be shiny.
In answer to your question, the problem seems to be that you cannot have more than 25 facets. If you remove any single group from rdmGroup then the plotly output works fine e.g.
diamonds <- subset(diamonds, rdmGroup != "Q")
To update your shiny example:
library(shiny)
library(plotly)
library(ggplot2)
data(diamonds, package = "ggplot2")
# new faceting group
diamonds$rdmGroup <- as.factor(sample(LETTERS, dim(diamonds)[1], replace=TRUE))
# subset of diamonds
diamonds <- diamonds[sample(nrow(diamonds), 1000),]
diamonds <- subset(diamonds, rdmGroup != "Q")
ui <- fluidPage(
headerPanel("Diamonds Explorer"),
sidebarPanel(
sliderInput('plotHeight', 'Height of plot (in pixels)',
min = 100, max = 2000, value = 1000)
),
mainPanel(
plotlyOutput('trendPlot')
)
)
server <- function(input, output) {
output$trendPlot <- renderPlotly({
p <- ggplot(diamonds, aes_string(x = diamonds$x, y =diamonds$y, color = diamonds$x)) +
geom_point()+ facet_grid(rdmGroup~., scales = "free_y") +
labs(x = "X", y="Y")
ggplotly(p) %>%
layout(height = input$plotHeight, autosize=TRUE)
})
}
shinyApp(ui, server)
provides the following output:
A workaround could be to simply have more than one plot, splitting the dataset into groups of 25.
EDIT: I did some more research and the plot stops displaying as expected when the panel margins are too large to allow all of the plots to display. You can display all 26 by reducing the panel.spacing.y but this will only go so far depending on how many rows you need:
p <- ggplot(diamonds, aes_string(x = diamonds$x, y =diamonds$y, color = diamonds$x)) +
geom_point()+ facet_grid(rdmGroup~., scales = "free_y") +
labs(x = "X", y="Y") + theme(panel.spacing.y = unit(0.2, "lines"))
So I am using mpg dataset to practice my R-shiny skills, but I encountered a problem.
I want to write a app which I could choose different variables to make graph, if it involves at least one discrete variable, then I draw a geom_boxplot, else, I will just draw a geom_point.
My ui.R looks like this:
library(shiny)
shinyUI(fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput(inputId = "var1",
label = "Choose x variable",
choices =
names(mpg)
),
selectInput(inputId = "var2",
label = "Choose y variable",
choices =
names(mpg))
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
))
And
My server.R looks like this:
library(shiny)
library(tidyverse)
library(dplyr)
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
if(typeof(mpg[, input$var1]) == "chr" ||
typeof(mpg[, input$var2]) == "chr")
{
ggplot(data = mpg) +
geom_boxplot(mapping =
aes(x = mpg[, input$var1],
y = mpg[, input$var2])) +
xlab(input$var1) +
ylab(input$var2) +
ggtitle(paste("Plot", input$var1, "vs", input$var2))
}
else
{
ggplot(data = mpg) +
geom_point(mapping =
aes(x = mpg[, input$var1],
y = mpg[, input$var2])) +
xlab(input$var1) +
ylab(input$var2) +
ggtitle(paste("Plot", input$var1, "vs", input$var2))
}
})
})
But it simply does not work! When I choose discrete variable, it will return me an error message says "Discrete value applied to continuous scale". However, if I both choose continuous value, it seems to be work fine.
Why it shows such an error message like that?
Please help me!
Thank you very much!
You have a couple of issues here all on the server side:
One: If you check an example of your typeof you'll see that you always get a list:
typeof(mpg[,"model"])
#[1] "list"
typeof(mpg[,"displ"])
#[1] "list"
This is because [ doesn't extract the actual element, but the list containing that element. From ?"[":
Indexing by [ is similar to atomic vectors and selects a list of the specified element(s).
Rather you should use typeof(mpg[[input$var1]]), etc because you want to extract the element of the list (not the list that contains the element).
Two:
There is actually a specific function in ggplot, aes_string which selects the columns to plot based on a string.
Incorporating these two changes should make your shiny app work. I also simplified you server a bit to get rid of the common ggplot code.
server <- function(input,output){
output$distPlot <- renderPlot({
p <- ggplot(mpg) + xlab(input$var1) +
ylab(input$var2) +
ggtitle(paste("Plot", input$var1, "vs", input$var2))
if(typeof(mpg[[input$var1]]) == "character" |
typeof(mpg[[input$var2]]) == "character")
{
p <- p + geom_boxplot(mapping =
aes_string(x = input$var1,
y = input$var2))
}
else
{
p <- p + geom_point(mapping =
aes_string(x = input$var1,
y = input$var2))
}
return(p)
})
}
working on integrating plotly into shiny dashboard and ran into an error when using conditional statements. I'm trying to switch through charts using an 'if' statement with selectInput. I've used this with the circlize package and ggplot graphs without issue but when trying to use it with plotly I get the following error:
Error in UseMethod: no applicable method for 'plotly_build' applied to an object of class "NULL"
I found a posting here with similar issues, but not exactly answering my specific question:
Convert ggplot object to plotly in shiny application
Here is a sample using a similar code to the one used in the posting above but with modifications to show what I'm looking to do and the error that keeps popping up:
library(shiny)
library(ggplot2)
library(ggthemes)
library(plotly)
ui = dashboardPage(
dashboardHeader(title = 'sample'),
dashboardSidebar(), ##Body content dashboardBody(
fluidRow(
box(background = "green", selectInput(inputId = "dimension",
label = strong("Choose Metric"),
choices = c('choice' = '1', 'choice2' = '2'),
multiple = FALSE, selectize = TRUE)),
box(plotlyOutput(outputId = 'plot2')))
))
server < - function(input, output) {
output$plot2 < -renderPlotly({
print(
ggplotly(
ggplot(data = mtcars, aes(x = disp, y = cyl)) + geom_smooth(method =
lm, formula = y~x) + geom_point() + theme_gdocs()))
if (input$dimension == '2') {
print(
ggplotly(
ggplot(data = mtcars, aes(x = hp, y = cyl)) + geom_smooth(method =
lm, formula = y~x) + geom_point() + theme_gdocs()))
}
})
}
shinyApp(ui, server)
I'm still learning so I'm sure this a simple error that escapes me but I am unsure what it might be. Appreciate the help!
Shortly, the problem was that if input$dimension was '1' there was no return value. You were printing the plot out but R went then one step further and checked if the condition was met. There are few ways to code it correctly.
You can save the plot in the object, say res, and if the condition is met, overwrite it with a new plot and finally return it at the end of the function - see the example below. You could also use the else statement.
library(shiny)
library(shinydashboard)
library(ggplot2)
library(ggthemes)
library(plotly)
ui = dashboardPage(
dashboardHeader(title = 'sample') ,
dashboardSidebar(),
## Body content
dashboardBody(
fluidRow(
box(background="green", selectInput(inputId = "dimension",
label = strong("Choose Metric"),
choices = c('choice'='1','choice2'='2'),
multiple = FALSE, selectize = TRUE)),
box(plotlyOutput(outputId = 'plot2')))
))
server <- function(input, output) {
output$plot2 <- renderPlotly({
res <- ggplotly(
ggplot(data = mtcars, aes(x = disp, y = cyl)) +
geom_smooth(method = lm, formula = y ~ x) +
geom_point() +
theme_gdocs())
if (input$dimension == '2') {
res <- ggplotly(
ggplot(data = mtcars, aes(x = hp, y = cyl)) +
geom_smooth(method = lm, formula = y ~ x) +
geom_point() +
theme_gdocs())
}
res
})
}
shinyApp(ui, server)
I'm creating a simple line chart which renders correctly in Shiny.
I've now added a selectInput with the names of 2 different measures, written as they appear in my data set. I'd like my y variable to change accordingly.
p <- plot_ly(data = LineChartData(), x= Calendar.Month, y = input$Measure, type = "line", group = Calendar.Year, col = Calendar.Year)
Unfortunately, the chart renders with just one point. It's not taking input$Measure and finding that field in my data set.
I know when using ggplot, i'd switch my aes to aes_string. Is there a similar solution in plotly?
EDIT: here's some reproducible code
Here's the ui.R file
#ui.R
shinyUI(
fluidPage(
titlePanel("Inbound Intermediary Performance"),
sidebarLayout(
sidebarPanel(
h4("Parameters"),
br(),
selectInput("Measure", "Measure", c("Var1","Var2"))
),
mainPanel(
plotlyOutput("lineChart")
)
)
)
)
server.R
#server.R
library(plotly)
library(shiny)
library(ggplot2)
#Create data
data <- data.frame(Month = c(1,2,3,4,5,6,7,8,9,10,11,12), Var1 = c(36,33,30,27,24,21,18,15,12,9,6,3), Var2 = c(4,8,12,16,20,24,28,32,36,40,44,48))
shinyServer(function(input, output) {
#Create plot
output$lineChart <- renderPlotly({
#using ggplot
p <- ggplot(data=data, aes_string(x='Month', y = input$Measure)) +geom_line(size = 1.5) + theme_minimal()
ggplotly(p)
#Using PLotly
#p <- plot_ly(data = data, x= Month, y = input$Measure, type = "line")
})
})
In the example above, I can use my drop down to switch between Var1 and Var2. My plot changes accordingly. The code uses ggplot and it's aes_string function to take an input. This is then converted into a plotly interactive plot using the ggplotly function.
Is there a way I can do this natively with plotly?
Use base::get() function:
p <- plot_ly(data = data, x = ~Month, y = ~get(input$Measure), type = "line")
or the same using ggplot:
p <- ggplot(data = data, aes(x = Month, y = get(input$Measure))) +
geom_line(size = 1.5) +
theme_minimal()
ggplotly(p)
Simply just use data[ ,input$Measure] as Your y variable:
p <- plot_ly(data = data, x= Month, y = data[ ,input$Measure], type = "line")