I want to include a ternary plot in a shiny app. I'm use package ggtern.
When I run the following code:
dd <- data.frame(x=c(3,1,5), y=c(45,29,10), z=c(10,45,94),
ss=c(58,75,109))
ggtern(data=dd,
aes(x=x,y=y,z=z)) +
geom_mask() +
geom_point() +
Larrowlab("var1") + Tarrowlab("var2") + Rarrowlab("var3") +
theme_showarrows() +
theme(tern.axis.arrow.show=T)#,
#tern.axis.text.show=F)
I get:
Inside a shiny app:
library(ggtern)
library(shiny)
## data ####
dd <- data.frame(x=c(3,1,5), y=c(45,29,10), z=c(10,45,94),
ss=c(58,75,109))
################
runApp(
## UI ####
list(ui = (basicPage(
headerPanel("ternary test"),
mainPanel(
plotOutput("gg", click = "plot_click")
)
)),
## server ####
server = function(input, output) {
output$gg <- renderPlot({
ggtern(data=dd,
aes(x=x,y=y,z=z)) +
geom_mask() +
geom_point() +
Larrowlab("var1") + Tarrowlab("var2") + Rarrowlab("var3") +
theme_showarrows() +
theme(tern.axis.arrow.show=T)#,
#tern.axis.text.show=F)
})
}
))
I get this:
Why the differences?
Is this a bug? Anyway around it?
Thanks,
António
Using print(ggtern_plot), as suggested in the comments above does not translate plot clicks properly and does not scale the plot.
ggtern does not work with shiny well because ggtern overloads plot.ggplot but shiny does not use the overloaded function. I managed to fix the plot this way:
custom_print.ggplot <- function(x) {
ggtern:::ggint$set_last_plot(x) # this is line needed by ggtern
# based on ggplot2:::custom_print.ggplot
grid::grid.newpage()
data <- ggplot_build(x)
gtable <- ggplot_gtable(data)
grid::grid.draw(gtable)
invisible(data)
structure(list(
build = data,
gtable = gtable
), class = "ggplot_build_gtable")
}
assignInNamespace("custom_print.ggplot", custom_print.ggplot, "shiny")
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)
I want to use the output of ggtern() in a shiny app. However it seems to fail due to some constraints.
This is what it should look like:
This is the actual shiny output:
See here for a reproducible example:
library(shiny)
library(ggtern)
ui <- fluidPage(
mainPanel(
plotOutput("ggtern")
)
)
server <- function(input, output) {
output$ggtern <- renderPlot({
ggtern(data.frame(x=10, y=30, z=60), aes(x, y, z)) + geom_point()
})
}
shinyApp(ui = ui, server = server)
Do I overlook something?
place the plot function within a print command:
output$ggtern <- renderPlot({
print(ggtern(data.frame(x=10, y=30, z=60), aes(x, y, z)) + geom_point())
})
I'm trying to plot a vertical line on a plot in shiny, where the x intercept is a function of two of the user inputs. The variable is used in other outputs, so I am creating it an storing it in a variable. I can print the variable in an renderText so I know the calculation is correct, however ggplot is returning "object 'xintercept' not found". Below is the server function that isn't working. Note the ggplot call, when the geom_vline is removed, works find. Furthermore if the time_after_formation() is wrapped in a renderText works fine too.
library(shiny)
library(ggplot2)
library(data.table)
ui = fluidPage(
sidebarLayout(
# Inputs
sidebarPanel(
numericInput(inputId = 'lon', label="Longitude:", value=-20),
numericInput(inputId = 'lat', label="Lattitude:", value=20),
numericInput(inputId = 'time_bp', label="Time:", value=65)
),
mainPanel(
plotOutput(outputId="ocean_sub", width="auto", height="200px")
)))
server = function(input, output){
elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})
seafloor_age_drop = data.table(lon=-20, lat=20, age=150)
setnames(floor_age_drop, c("lon", "lat", "age"))
setkey(floor_age_drop, lon, lat)
# Make model
model_age = seq(0,200, by=0.1)
crosby_depth =
ifelse(model_age <=75, 2652+324*sqrt(model_age),
ifelse(model_age>75 & model_age <=160, 5028+5.26*model_age-250*sin(((model_age-75)/30)),
ifelse(model_age>160, 5750,NA)))
crosby_model = data.frame(age=model_age, depth=crosby_depth)
# Reactives
age_from_grid = reactive({floor_age_drop[.(round(input$lon, digits=1), round(input$lat, digits=1)),'age']})
time_after_floor_formation = reactive({age_from_grid() - input$time_bp})
depth_crosby = reactive({crosby_model$depth[elementwise.all.equal(crosby_model$age, round(time_after_floor_formation(), 1))]
})
output$ocean_sub=renderPlot({
ggplot(crosby_model, aes(x=age, y=depth)) + geom_line() +
scale_y_reverse(lim=c(6000, 2500)) +
geom_vline(xintercept=time_after_floor_formation(), colour='red')})
}
shinyApp(ui=ui, server=server)
First create the plot in a reactive environment and then you use render plot for plotting it something like this:
p = reactive({ggplot(my_model, aes(x=age, y=depth)) + geom_line() +
scale_y_reverse(lim=c(6000, 2500)) +
geom_vline(xintercept=time_after_formation(), colour='red')})
output$ocean_sub = renderPlot({p})
EDITED
I found your problem and it is that the variable is data.table and that's why it doesn't work. just rap it up in as.numeric and it works perfect
ggplot(crosby_model, aes(x=age, y=depth)) + geom_line() +
scale_y_reverse(lim=c(6000, 2500)) +
geom_vline(xintercept=as.numeric(time_after_floor_formation()), colour='red')
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"))
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()