I'm trying to make a flexdashboard using IMDb data, that has an interactive jitter plot where you can change the x and y for visualizing hierarchical clustering result. The code that I've already made can change only the x and number of k. I think I should use reactive function but I don't really understand in using that. I've already tried many other ways from youtube and some documentary but still can't change the y. Here is layout of my dashboard, The y stuck at the runtime variable
data=df %>%
select(Rating, Votes, Gross, Runtime, Metascore)
selectInput("x", label = "X : ",choices = names(data))
selectInput("y", label = "Y : ",choices = names(data))
sliderInput('k',"Cluster",min = 2,max = 10, value = 6)
selectedData=reactive({
data %>% select(input$x, input$y)
})
data_scaled=scale(data)
dist_data=dist(data_scaled, method='euclidean')
hc_data=hclust(dist_data, method = "average")
renderPlot({
ggplot(selectedData(),
aes(x=!!rlang::sym(input$x), y=!!rlang::sym(input$y),
col=factor(cutree(hc_data, k=input$k))))+
geom_jitter(size=5, alpha=0.5 )+
labs(col="Cluster")
})
Here is an alternative example that seems to work, using the diamonds dataset from ggplot2. My guess is that the scaling and clustering steps take so long to run that the the y reactive only appears not to work. I would suggest pre-processing your data if app run times are a problem.
data=diamonds[1:1e3,] %>%
dplyr::select(where(is.numeric))
selectInput("x", label = "X : ",choices = names(data))
selectInput("y", label = "Y : ",choices = names(data))
sliderInput('k',"Cluster",min = 2,max = 10, value = 6)
data_scaled=scale(data)
dist_data=dist(data_scaled, method='euclidean')
hc_data=hclust(dist_data, method = "average")
renderPlot({
ggplot(data,
aes(x=!!rlang::sym(input$x), y=!!rlang::sym(input$y),
col=factor(cutree(hc_data, k=input$k))))+
geom_jitter(size=5, alpha=0.5 )+
labs(col="Cluster")
})
Related
I am fairly new to R Shiny and I've been working on an app with an interactive world map which shows each country's performance at the Olympics, using scale_fill_gradient. The app user gets to choose the performance indicator (total medals won, gold only, weighted score) and the year (1996 to 2020).
The problem is there's no more error shown, but the plot doesn't show either! I have tried to run the functions as normal R script and they worked fine there (the plot showed up in the viewer pane). I found a few others who have also run into problems with no plot or error showing, but their cases are different to mine (e.g. mismatch in Output and Render) so the resolutions don't work for me.
It's a massive dataset so I've not included it here, I thought I might check first if the error could be spotted from the code alone. Here's what I've used:
function
world_map1 <- function(WorldMap, year, performance) {
w_plot1 <- WorldMap %>%
filter(Year == year) %>%
select("long", "lat", "group", "region", all_of(performance)) %>%
replace(is.na(.), 0) %>%
rename_at(performance, ~ "Value") %>%
mutate(Value = as.numeric(as.character(Value)))
tooltip_css <- "background-color:#2E2E2E; font-family: Calibri; color:#F2F2F2;"
w_g1 <- ggplot() +
geom_polygon_interactive(data = subset(w_plot1, lat >= -60 & lat <= 90),
aes(x = long,
y = lat,
fill = Value,
group = group,
tooltip = sprintf("%s<br/>%s", region, Value))) +
scale_fill_gradient(name = "Medals /Score",
low = "lightgoldenrodyellow",
high = "goldenrod1",
na.value = "white")
return(
girafe(
ggobj = w_g1,
options = list(
opts_tooltip(
css = tooltip_css
)
))
)
}
ui
ui <- fluidPage(
titlePanel("Title"),
sidebarLayout(
sidebarPanel(
radioButtons(inputId = "performance", label = "Performance measure:",
choices = c("Total medals won" = "Total",
"Gold medals won" = "Gold",
"Weighted points system" = "Weighted"
)),
width = 3
),
mainPanel(
girafeOutput("mapPlot1"),
sliderInput(inputId = "year", label = "Year:",
min = 1996, max = 2020, step = 4, value = 1996, ticks = FALSE, sep = ""
)
)
)
)
server
server <- function(input, output) {
output$mapPlot1 <- renderGirafe({
ggiraph(code = print(world_map1(WorldMap, input$year, input$performance)))
}
)
}
run app
shinyApp(ui = ui, server = server)
Any help or insights appreciated!
I thought it was my theme() block so I removed that, as shown above. Also checked other cases on no plot showing here, couldn't find one with fixes that would work for me because it seems the underlying problem is different?
A new self-learner for Shiny apps, still trying to explore the structure of Shiny apps. I have a piece of code which I want to convert into Shiny apps. I wonder if someone can walk me through how the process goes. My goal is to make the figure title/subtitle dynamic based on inputs (i.e., lnHR0, p_ctl0, tfu) to function. Thanks!!!
R code:
library(tidyverse)
library(ggplot2)
WR_sim_OC <- function(n_trt, n_ctl, lnHR0, p_trt0, med_ctl, tfu, enroll, dur_boost){
#n_trt=60: number of subjects (treatment)
#n_ctl=30: number of subjects (control)
#lnHR0=log(1): Overall Survival Log Hazard Ratio
#p_trt0 = seq(0.46, 0.66, 0.01): Response Rate (Treatment)
#med_ctl=21.8: Median Overall Survival (Control)
#tfu=9: Minimum Follow-up Time
#enroll=19: Enrollment Time
#dur_boost=0: Durability Boost (percentage)
## insert real simulation code here ##
## Fake Results
results <- tibble(ORR_trt = p_trt0, avg_HR = rep(0.774, times = 4), maturity = rep(36.7, times = 4), ORR = c(20.71, 38.87, 60.61, 78.95),
WR = c(46.8, 56.0, 64.3, 72.8), WO = c(46.0, 55.0, 63.7, 71.9), OS = rep(55.0, times = 4))
## Create Operating Characteristic Figure
dat <- results %>% pivot_longer(c(ORR, WR, WO, OS), names_to = 'Method', values_to = 'POS')
out1 <- ggplot(data = dat, aes(x = ORR_trt, y = POS, group = Method)) +
geom_line(aes(color = Method), size = 1) +
geom_point(aes(color = Method), size = 2.5) +
theme(legend.position = 'bottom') +
labs(title = 'HR=1.0 Treatment vs. Control', subtitle = 'ORR in Control Arm=46%, 9mo follow-up', color = 'Method') +
ylab('Probability of Incorrect Go') +
xlab('ORR in Treatment Arm') +
ylim(0, 100)
## Output OC Table + Figure to shiny app
list(results, out1)
}
WR_sim_OC(n_trt=60, n_ctl=30, lnHR0=log(1), p_ctl0=0.46, p_trt0 = seq(0.46, 0.66, 0.06), med_ctl=21.8, tfu=9, enroll=19, dur_boost=0)
I tried writing the ui.R as follows (suppose the suv_plot is the output name), which I know is wrong. The server.R part is too hard for me... Can someone help?
fluidPage(
numericInput("lnHRO",
label = h3("ln(HRO)"),
value = log(1)),
numericInput("pctl",
label = h3("Response Rate (Control)"),
value = 0.46),
numericInput("tfu",
label = h3("Minimum Follow-up Time (Month)"),
value = 9),
hr(),
plotOutput("suv_plot")
)
My first suggestion is just to look at tutorials on shiny, they give a great overview on how to start a project: https://shiny.rstudio.com/tutorial/
I didn't know a thing about programming a few years back, so I understand it can be hard figuring out where to start, so I wanted to give you an idea of how to implement a function, and use shiny inputs to make the resulting table/plot be dynamic.
I switched up your code to be easier to reproduce for myself. I hope this gives you the starting point you need:
library(tidyverse)
library(ggplot2)
library(shiny)
WR_sim_OC <- function(MPG, CYL, DISP){
results <- mtcars%>% #Function to make a table
filter(cyl > CYL,
mpg > MPG,
disp > DISP)
out1 <- ggplot(data = results, aes(x = mpg, y = disp, group = cyl)) +
geom_line(aes(color = hp), size = 1) #Function to make a plot
list(results, out1) #List to create table and function
}
ui <- fluidPage(
numericInput("MilesPerGallon", "mpg", value = 15),
numericInput("Cylinders", "cyl", value = 4),
numericInput("Displacement", "disp", value = 200),
tableOutput("TABLE"),
plotOutput("PLOT")
)
server <- function(input, output, session) {
output$TABLE<-renderTable({
req(input$MilesPerGallon, input$Cylinders, input$Displacement) #Requires all three inputs before it makes the table
WR_sim_OC(input$MilesPerGallon, input$Cylinders, input$Displacement)[1] #Only pulling the table from the function
})
output$PLOT<-renderPlot({
req(input$MilesPerGallon, input$Cylinders, input$Displacement) #Requires all three inputs before it makes the plot
WR_sim_OC(input$MilesPerGallon, input$Cylinders, input$Displacement)[2] #Only pulling the plot from the function
})
}
shinyApp(ui, server)
Essentially on the server side where you render the plot or table, you use those inputs from the ui as the dynamic points in your function. I used req() for both of the renderTable and renderPlot to make sure the inputs are filled out before it makes the table plot. Best of luck!
I'm trying to include a plotly plot in a shiny app where the y variable is selected by the user. I initially used ggplot2 and plotly together, and the code I have works just fine for that. But because the number of data points is quite large, the plot takes several minutes to load, so I tried switching to plotly only because I read somewhere that that makes it faster. Unfortunately I cannot get the y variable selection to work.
I have tried the suggestions given here: Change plotly chart y variable based on selectInput and here: Error: invalid first argument with R Shiny plot and none of them work. At this point I have tried so many things I don't remember in detail, but basically I either get the error "invalid first argument" when using some variation of yvar <- get(input$yvariable1) and then including ~yvar in the plot function, or I get "Error: cannot set attribute on a symbol" when it's y = ~input$yvariable1. When I use y = newdata[ ,input$yvariable1] something gets plotted but it's completely wrong (the scale of the axis is up to 50k or something instead of 10 and the distribution is not right either - basically it looks nothing like when I plot it by simply entering the same y variable non-reactively).
My code looks as follows - in UI:
uiOutput("ySelection1")
in server:
function(input, output) {
output$ySelection1 <- renderUI({
varSelectInput("yvariable1", "Y Variable:", df[, c('PO_count_citing', 'cpc_3digits_count_citing', 'cpc_4digits_count_citing')], selected='PO_count_citing')
})
yvar1 <- eventReactive(input$yvariable1, {input$yvariable1})
output$plot1 <- renderPlotly({
newdata <- subset(df, Technology == input$type & appln_auth%in%input$PO)
validate(no_data(nrow(newdata)))
#yvar <- get(yvar1()) (failed attempt at making this work)
#yvar <- get(input$yvariable1) (another failed attempt)
scatterPlot <- plot_ly(newdata, x = ~appln_filing_year, y = ~input$yvariable1, type="scatter", mode="markers",
# Hover text:
text = ~paste(some text),
color = ~appln_auth)
})
}
But I can't get it to work. In the original ggplot2 version it was entered as aes(x = appln_filing_year, y = !!yvar1(), bla bla)
But the !! or even one ! or removing the brackets after yvar1 all throw up errors in plotly.
Does anyone have any suggestions?
Here is a simple example using get:
library(shiny)
library(plotly)
DF <- setNames(data.frame(rep(1:20, 5), mapply(runif, min = 1:5, max = 2:6, MoreArgs = list(n = 20))), c("x", paste0("y", 1:5)))
library(shiny)
ui <- fluidPage(
plotlyOutput("myPlot"),
selectInput("yvariable", "Select the Y variable", paste0("y", 1:5))
)
server <- function(input, output, session) {
output$myPlot <- renderPlotly({
req(input$yvariable)
plot_ly(data = DF, x = ~x, y = ~get(input$yvariable), type = "scatter", mode = "markers")
})
}
shinyApp(ui, server)
This is my first attempt at using Shiny.
I have a simulated patient-level dataset with 4 variables:
group: Categorical, takes on values A, B and C. Represents 3 different treatment types that were used in the study.
week: Numeric variable, takes on values 1, 4, 8.Represents follow-up week.
painscore: Numeric variable, score on scale of 1-10, with 1 indicating no pain, 10 indicating extreme pain.
dependscore: Numeric variable, score on scale of 1-10, with 1 indicating no dependency on pain meds, 10 indicating extreme dependency.
Trying to build a simple app that accepts two inputs: the week, and the variable, and provides two outputs:
A boxplot of distribution of scores for the selected variable for the selected week. The x axis would represent the 3 levels of group (A, B and C).
A summary table the shows the number of observations, median, 25th percentile, 75th percentile and number of missing.
I was able to create the interactive boxplot, but I am unable to create the summary table. I was able to create static versions of this table in RMarkdown using the summaryBy function from doBy, but I am not able to implement it in Shiny. Tried following the advice here and here but I'm missing something.
Here's my code for reproducibility. Excuse the extensive annotations, (I'm a complete beginner) they are more for myself than for anyone else.
#libraries--------------------
library(shiny)
library(tidyverse)
library(knitr)
library(doBy)
#----------------------------
#input data
set.seed(123)
mydf <- data.frame( group = rep(rep(c("A","B","C"), each = 3), times = 3),
week = rep(rep(c(1,4,8), each = 9)),
painscore = sample(1:10, 27, replace = TRUE),
dependscore = sample(1:10, 27, replace = TRUE) )
#--------------------------
#define custom function to calculate summary statistics for column of interest.
#function explained in a little more detail when applied in the server function.
fun <- function(x) {
c( n = length(x),
m = median(x),
firstq = round(quantile(x)[2], 1),
lastq = round(quantile(x)[4], 1),
missing = mean(is.na(x)))
}
#-------------------------
#UI
ui <- fluidPage(
titlePanel("Shiny Boxplot and Table"),
#User can provide two different inputs
sidebarLayout(
sidebarPanel(
#1. allow user to pick week using radiobuttons
radioButtons(inputId = "pickedwk",
label = "week you want to display",
choices = c(1,4,8),
selected = 1),
#2. user can pick variable to visualize using dropdownboxes
selectInput(inputId = "var",
label = "variable to visualize",
list("How much pain did you feel today?" = "painscore",
"How dependent are you on medication?" = "dependscore")),
#helpertext
helpText("Enter week/variable choices here")
),
#Spaceholders for output
mainPanel(
plotOutput("boxplot"), #boxplot placeholder
htmlOutput("descriptives") #kable html table placeholder
)
)
)
#-------------------------
#Server
server <- function(input, output) {
#create dataset that subsets down to the week picked by user.
weeksub <- reactive({
mydf %>% filter(week == input$pickedwk[1])
})
#1. use reactive datasubset to render boxplot.
output$boxplot <- renderPlot({
ggplot(weeksub(), aes_string(x = "group", y = input$var)) + #input$var works here
geom_boxplot(fill = "red", outlier.shape = 15, outlier.color = "black") +
scale_y_continuous(name = as.character(input$var)) +
scale_x_discrete(name = "group") +
ggtitle(paste("Distribution of", as.character(input$var), "by treatment group"))
})
#2. use same reactive datasubset to render kable descriptive statistics for the variable picked.
output$descriptives <- renderText({
kable(summaryBy(input$var ~ group, data = as.data.frame(weeksub()), FUN = fun),
#note: here, I'm using the summaryBy function from package doBy. It takes the form var~ categoricalvar
# so, either painscore ~ group, or dependscore ~ group depending on what the user picked, and uses
#my custom function to return a table of count, median, 25th percentile, 75th percentile and missing count for
#the 3 levels of the group variable (for A, B, and C)
col.names = c("Number", "Median", "1Q", "3Q", "Missing"))
})
}#server function ends
# Run the application
shinyApp(ui = ui, server = server)
There are a couple of problems in your code:
The formula notation doesn't know how to deal with input$var. summaryBy supports an alternate syntax that works better. (You could also use as.formula and paste to build a formula.)
You are missing the "Group" column in col.names
You have to generate HTML from kable and pass it as HTML to the UI.
Change your table output to this:
output$descriptives <- renderUI({
HTML(kable(summaryBy(list(input$var, 'group'), data = as.data.frame(weeksub()), FUN = fun),
col.names = c('Group', "Number", "Median", "1Q", "3Q", "Missing"),
format='html'
))
})
In the following shiny app, the plotly package is used to create an interactive correlation heat map. When individual tiles are clicked, the corresponding scatter plot appears. One can then download the individual scatters by clicking download plot as png. But is there a way to download all the possible scatter plots at once without having to click each individual tile and save each individual one? Thank you
library(plotly)
library(shiny)
# compute a correlation matrix
correlation <- round(cor(mtcars), 3)
nms <- names(mtcars)
ui <- fluidPage(
mainPanel(
plotlyOutput("heat"),
plotlyOutput("scatterplot")
),
verbatimTextOutput("selection")
)
server <- function(input, output, session) {
output$heat <- renderPlotly({
plot_ly(x = nms, y = nms, z = correlation,
key = correlation, type = "heatmap", source = "heatplot") %>%
layout(xaxis = list(title = ""),
yaxis = list(title = ""))
})
output$selection <- renderPrint({
s <- event_data("plotly_click")
if (length(s) == 0) {
"Click on a cell in the heatmap to display a scatterplot"
} else {
cat("You selected: \n\n")
as.list(s)
}
})
output$scatterplot <- renderPlotly({
s <- event_data("plotly_click", source = "heatplot")
if (length(s)) {
vars <- c(s[["x"]], s[["y"]])
d <- setNames(mtcars[vars], c("x", "y"))
yhat <- fitted(lm(y ~ x, data = d))
plot_ly(d, x = ~x) %>%
add_markers(y = ~y) %>%
add_lines(y = ~yhat) %>%
layout(xaxis = list(title = s[["x"]]),
yaxis = list(title = s[["y"]]),
showlegend = FALSE)
} else {
plotly_empty()
}
})
}
shinyApp(ui, server)
You can use webshot to capture a static image of Plotly's HTML output using the instructions here: https://plot.ly/r/static-image-export/
An example for loop below generates random scatter plots from mtcars.
library(plotly)
library(webshot)
## You'll need to run the function the first time if you dont't have phantomjs installed
#webshot::install_phantomjs()
ColumnOptions <- colnames(mtcars)
for (i in seq_len(5)){
xCol <- sample(ColumnOptions,1)
yCol <- sample(ColumnOptions,1)
ThisFileName <- paste0("Scatter_",xCol,"_vs_",yCol,".png")
plot_ly(x = mtcars[[xCol]], y = mtcars[[yCol]], type = "scatter", mode = "markers") %>%
export(., file = ThisFileName)
}
However, if you're going to be potentially doing this dozens of times, the amount of computation required to go through the following steps really adds up.
Generate a JSON plotly object from R
Use htmlwidgets/htmltoolsto generate a self-contained HTML web page
Render that HTML as a browser would see it with an external program --webshot
Use webshot to render an image of that HTML and save it as a PNG
This isn't really a reflection of plotly being slow, but to make an analogy it's kind've like using an airplane to travel half a mile -- the plane gets you there, but if you need to make that trip more than a few times you should probably consider a car.
The plotly loop above takes 27 seconds to render 5 PNG images, but the alternative method below using ggplot2 takes 1.2 seconds.
library(ggplot2)
ColumnOptions <- colnames(mtcars)
for (i in seq_len(5)){
xCol <- sample(ColumnOptions,1)
yCol <- sample(ColumnOptions,1)
ThisFileName <- paste0("ggplot2_Scatter_",xCol,"_vs_",yCol,".png")
ggplot() +
geom_point(aes(x = mtcars[[xCol]], y = mtcars[[yCol]])) +
labs(x = xCol, y = yCol) -> ThisPlot
ggsave(plot = ThisPlot, filename = ThisFileName)
}