Related
I'm working on cancer data from TCGA.
Im new to shiny and creating web applications (learning it!!)
I'm working on a shiny tool to plot the volcanoplot using highcharter package.
sometimes I'm successfully able to plot the volcanoplot in the UI. but sometimes it fails to plot it and throws an error saying,
"An error has occurred!
could not find function "highchartOutput"
and one warning message is given for the error;
Listening on http://127.0.0.1:5335
Warning: Error in highchartOutput: could not find function "highchartOutput"
83: dots_list
82: div
81: tabPanel
I think there is some problem with the tabset panel.
is this error has anything to do with indentation? (wherever I adjust the brackets it works magically. not sure how it works for sometimes.)
I am attaching the UI and server files with this post.
code is attached for one type of comparison
UI file below:
library(shiny)
# Define UI for application
shinyUI(fluidPage(
# Application title
titlePanel("miR-Gyn-Explorer"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
## select the count matrix
selectInput("file", label = h3("Count Matrix"),
choices = list("Stage I - Normal" = list("TCGA-BRCA" = "Data/TCGA-BRCASI_NT.rda", "TCGA-UCEC" = "Data/TCGA-UCECSI_NT.rda"))),
## select the phenodata of samples
selectInput("phenofile", label = h3("Sample Phenodata"),
choices = list("Stage I - Normal" = list("TCGA-BRCA" = "Data/TCGA-BRCA_phenoSI_NT.rda", "TCGA-UCEC" = "Data/TCGA-UCEC_phenoSI_NT.rda"))),
submitButton("Update View")
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
tabPanel("DEmiRNA", DT::dataTableOutput("DEmiRNA"),
"Volcano-Plot", highchartOutput("volcanoPlot", height = "500px"))
#tabPanel("miRNA-Targets", DT::dataTableOutput('miRTarget'),
#plotOutput("GO"))
)
)
)
)
)
server file:
library(shiny)
library(R.utils)
##function to find the DEmiRNA by edgeR method
library(limma)
library(edgeR)
library(DT)
library(dplyr)
library(multiMiR)
library(miRBaseConverter)
library(ggplot2)
#library(ggrepel)
library(tidyverse)
library(highcharter)
library(org.Hs.eg.db)
library(clusterProfiler)
library(purrr)
gdcDEmiRNA <- function(counts, groups, comparison, filter=TRUE) {
## method = edgeR
dge = DGEList(counts = counts, samples = groups)
group <- factor(groups$group)
design <- model.matrix(~0+group)
colnames(design) <- levels(group)
contrast.matrix <- makeContrasts(contrasts=comparison,
levels=design)
keep = filterByExpr(dge,design)
dge <- dge[keep,,keep.lib.sizes = TRUE]
dge <- calcNormFactors(dge)
dge <- estimateDisp(dge, design)
fit <- glmFit(dge, design)
lrt <- glmLRT(fit, contrast=contrast.matrix)
DEGAll <- lrt$table
DEGAll$FDR <- p.adjust(DEGAll$PValue, method = 'fdr')
o <- order(DEGAll$FDR)
DEGAll <- DEGAll[o,]
return (DEGAll)
}
# Define server logic required to perform the DEmiRNA analysis
server <- function(input, output) {
d <- reactive({
#DEmiRNA calculation
file <- load(input$file)
phenofile <- load(input$phenofile)
if(file == "SI_NT"){
if(phenofile == "phenoSI_NT"){
DEmiRNA <- gdcDEmiRNA(counts = SI_NT, groups = phenoSI_NT,
comparison = 'StageI-Normal')
}
}
})
output$DEmiRNA <- DT::renderDataTable({
mir <- d()
#mir <- mir[mir$FDR < input$FDR,]
})
output$volcanoPlot <- renderHighchart({
x <- d()
x$mirna <- rownames(x)
x$sig <- ifelse(x$PValue < 0.05 & abs(x$logFC) > 0.57, "DEmiRNA", "Not Regulated")
hc <- highchart() %>%
hc_add_series(x, "scatter", hcaes(logFC, -log10(PValue), group = sig, value = mirna),
color = c('rgba(67, 67, 72, 0.6)', 'rgba(124, 181, 236, 0.6)'),
enableMouseTracking = c(TRUE, TRUE),
showInLegend = TRUE, marker = list(radius = 4)) %>%
hc_tooltip(pointFormat = "{point.value}", headerFormat = "") %>%
hc_xAxis(title = list(text = "Log fold change"), gridLineWidth = 1,
tickLength = 0, startOnTick = "true", endOnTick = "true", min = -6, max = 6) %>%
hc_yAxis(title = list(text = "-Log10(p-value)")) %>%
hc_chart(zoomType = "xy", width=700) %>%
hc_exporting(enabled = TRUE, filename = "volcano")
hc
})
}
any comment and help from you guys is appreciated
Thank you in advance!
-Ankita
I'm currently working on an Rshiny webapp to use for some simple classification. Currently, I've been working on creating a table that contains the CCR and MCR of both the CART and LDA methods on the data. My aim is then to highlight the column of the MCR and CCR of the best method (the method with the highest CCR... or lowest MCR). I have ran the code and viewed that it works correctly using the Viewer Pane. However, when I load the app, I obtain the error 'data' must be 2-dimensional (e.g. data frame or matrix).
Here is my code:
data <- read.csv("Fatality-task2.csv")
data$Rate <- as.factor(data$Rate)
library(shiny)
library(dplyr)
library(ggplot2)
library(markdown)
library(gtsummary)
library(ggdendro)
library(factoextra)
library(mclust)
library(cluster)
library(rpart)
library(rpart.plot)
library(DT)
#library(MASS)
glimpse(data)
#################################################################
ui <- fluidPage(
navbarPage("",
tabPanel("Data Exploration",
sidebarLayout(
sidebarPanel(
selectInput("variable",
"Variable",
colnames(data)),
selectInput("rate",
"Rate",
levels(data$Rate))
),
mainPanel(
tableOutput("table"),
plotOutput("plot")
)
)
),
tabPanel("Classification tools",
sidebarLayout(
sidebarPanel(
sliderInput("train.prop",
"Training data proportion",
min = 0.4,
max = 0.8,
step = 0.1,
value = 0.6),
radioButtons("prune",
"Pruning option",
choices = c("view pruned tree",
"view unpruned tree"))
),
mainPanel(
DTOutput("table2"),
plotOutput("plot2")
)
)
)
)
)
#################################################################
server <- function(input, output) {
output$table <- renderTable({
req(input$variable,input$rate)
data <- data %>%
filter(Rate == input$rate) %>%
dplyr::select(input$variable) %>%
summary() %>%
as.data.frame() %>%
tidyr::separate(Freq, c("Stat", "Value"), sep=":") %>%
tidyr::pivot_wider(names_from =Stat, values_from = Value)
data <- data[, -c(1,2)]
})
output$plot <- renderPlot({
req(input$variable)
if (input$variable == "jaild" | input$variable == "Rate"){
ggplot(data, aes(x = Rate, fill = .data[[as.name(input$variable)]])) +
geom_bar(position = "dodge", width = 0.7) +
if (input$variable == "Rate"){
theme(legend.position = "none")
}
} else {
ggplot(data, aes(x = Rate, y = .data[[as.name(input$variable)]], fill = Rate)) +
geom_boxplot() +
theme(legend.position = "none")
}
})
output$plot2 <- renderPlot({
req(input$train.prop,input$prune)
set.seed(1234)
n <- nrow(data)
ind1 <- sample(c(1:n), round(n*as.numeric(input$train.prop)))
ind2 <- sample(c(1:n)[-ind1], length(c(1:n)[-ind1]))
train.data <- data[ind1,]
valid.data <- data[ind2,]
fit.tree <- rpart(Rate~., data = train.data, method = "class")
ptree <- prune(fit.tree, cp = fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"])
if (input$prune == "view pruned tree"){
rpart.plot(ptree, uniform =TRUE)
} else {
rpart.plot(fit.tree)
}
})
output$table2 <- DT::renderDT({
library(MASS)
set.seed(1234)
n <- nrow(data)
ind1 <- sample(c(1:n), round(n*0.6))
#ind2 <- sample(c(1:n)[-ind1], length(c(1:n)[-ind1]))
ind2 <- setdiff(c(1:n), ind1)
train.data <- data[ind1,]
valid.data <- data[ind2,]
#################################
### fit cart model
fit.tree <- rpart(Rate~., data = train.data, method = "class")
### prune the tree
ptree <- prune(fit.tree, cp = fit.tree$cptable[which.min(fit.tree$cptable[,"xerror"]),"CP"])
### predict using the validation data on the pruned tree
pred <- predict(ptree, newdata = valid.data[,-6], type = "class")
### lda
#lda.model <- lda(train.data[,-6], train.data[,6])
lda.model <- lda(Rate~., data = train.data)
lda.pred <- predict(lda.model, newdata = valid.data[,-6])
### create a classification table
length(lda.model)
x <- pred == valid.data[,6]
CCR <- length(x[x == TRUE])/nrow(valid.data)
MCR <- 1 - CCR
CR <- c(CCR, MCR)
z <- lda.pred$class == valid.data[,6]
lda.CCR <- length(z[z == TRUE])/nrow(valid.data)
lda.MCR <- 1 - lda.CCR
lda.CR <- c(lda.CCR, lda.MCR)
y <- cbind(CR, lda.CR)
y <- as.data.frame(y)
colnames(y) <- c("CART", "LDA")
rownames(y) <- c("CCR", "MCR")
#y
DT::datatable(y, options=list(dom = "t")) %>%
formatRound(columns = c(1,2), digits = 6) %>%
formatStyle(columns = colnames(y[which.max(y[1,])]), background = "green")
#colnames(y[1])
#colnames(y[which.max(y[1,])])
},
rownames = TRUE)
}
?formatStyle
?formatRound()
#################################################################
shinyApp(ui, server)
and here is some of my data:
"beertax","jaild","vmiles","unrate","perinc","Rate"
1.53937947750092,"no",7.23388720703125,14.3999996185303,10544.15234375,1
1.78899073600769,"no",7.83634765625,13.6999998092651,10732.7978515625,1
1.71428561210632,"no",8.262990234375,11.1000003814697,11108.791015625,1
1.65254235267639,"no",8.7269169921875,8.89999961853027,11332.626953125,1
1.60990703105927,"no",8.952853515625,9.80000019073486,11661.5068359375,1
1.55999994277954,"no",9.1663017578125,7.80000019073486,11944,1
1.50144362449646,"no",9.6743232421875,7.19999980926514,12368.6240234375,1
0.214797139167786,"yes",6.81015673828125,9.89999961853027,12309.0693359375,1
0.206422030925751,"yes",6.58749462890625,9.10000038146973,12693.8076171875,1
0.296703308820724,"yes",6.70997021484375,5,13265.93359375,1
0.381355941295624,"yes",6.7712626953125,6.5,13726.6953125,1
0.371517032384872,"yes",8.1290078125,6.90000009536743,14107.3271484375,1
0.360000014305115,"yes",9.370654296875,6.19999980926514,14241,1
0.346487015485764,"yes",9.815720703125,6.30000019073486,14408.0849609375,1
0.650358021259308,"no",7.20850048828125,9.80000019073486,10267.302734375,1
0.67545872926712,"no",7.1759169921875,10.1000003814697,10433.486328125,1
0.598901093006134,"no",7.08481982421875,8.89999961853027,10916.4833984375,1
0.577330529689789,"no",7.25391796875,8.69999980926514,11149.3642578125,1
0.562435507774353,"no",7.4689990234375,8.69999980926514,11399.380859375,1
0.545000016689301,"no",7.66583056640625,8.10000038146973,11537,1
0.52454286813736,"no",8.02462548828125,7.69999980926514,11760.3466796875,1
0.107398569583893,"no",6.8586767578125,9.89999961853027,15797.1357421875,0
0.103211015462875,"no",7.21629150390625,9.69999980926514,15970.18359375,0
0.0989011004567146,"no",7.61917578125,7.80000019073486,16590.109375,0
0.0953389853239059,"no",7.87406689453125,7.19999980926514,16985.169921875,0
0.0928792580962181,"no",8.03491015625,6.69999980926514,17356.037109375,0
0.0900000035762787,"no",8.18063330078125,5.80000019073486,17846,0
0.0866217538714409,"no",8.531990234375,5.30000019073486,18049.0859375,0
0.214797139167786,"no",7.742841796875,7.69999980926514,15082.3388671875,1
0.206422030925751,"no",7.65606298828125,6.59999990463257,15131.880859375,1
0.197802200913429,"no",7.7078525390625,5.59999990463257,15486.8134765625,0
0.190677970647812,"no",8.09220947265625,5.90000009536743,15569.9150390625,0
0.185758516192436,"no",8.13137451171875,7.40000009536743,15616.0986328125,0
0.180000007152557,"no",8.18202783203125,7.69999980926514,15605,0
0.173243507742882,"no",8.3807685546875,6.40000009536743,15845.04296875,0
0.224343672394753,"no",6.4400537109375,6.90000009536743,17255.369140625,0
0.233563080430031,"no",6.57004296875,6,17744.265625,0
0.248010993003845,"no",6.68019287109375,4.59999990463257,18760.439453125,0
0.239078402519226,"yes",6.97921484375,4.90000009536743,19312.5,0
I know the code works properly - I just want it to be able to run properly on the app. Please help!
I have developed a shiny app, in which I am uploading a number of CSV files in each tab. After performing some mathematical operations on the uploaded data, I am getting N number of data tables as an output. I am rendering those data tables using DT::renderDataTable.
Now, let say I have 3 different datatables rendered using DT::renderDataTable I want to use the output rendered in those datatables to plot a combined graph. (3 geom_line() on top of each other)
This is how I am rendering the data in the datatable:
output$Data_FSC <- DT::renderDataTable({
x1 <- data2()[, c(input$xcol2, input$ycol2)]
M <- x1
#calculate rotation angle
alpha <- -atan((M[1,2]-tail(M,1)[,2])/(M[1,1]-tail(M,1)[,1]))
#rotation matrix
rotm <- matrix(c(cos(alpha),sin(alpha),-sin(alpha),cos(alpha)),ncol=2)
#shift, rotate, shift back
M2 <- t(rotm %*% (t(M)-c(M[1,1],M[1,2]))+c(M[1,1],M[1,2]))
M2[nrow(M2),2] <- M2[1,2]
M2
d_f3 <- data.frame(x = M2[,1], y = (M2[,2]-min(M2[1,2])))
v_f1 <- subset(d_f3, y > ((input$below2)/1000) & y < ((input$above2)/1000), select = c(x,y))
fla_upper2 <- lm(formula = y+((input$Upper_Poly_Limit2)/1000000) ~ poly(x,input$degree2, raw = TRUE), v_f1)
fla_lower2 <- lm(formula = y-((input$Lower_Poly_Limit2)/1000000) ~ poly(x,input$degree2, raw = TRUE), v_f1)
v_f1$upper2 <- predict(fla_upper2, newdata=v_f1)
v_f1$lower2 <- predict(fla_lower2, newdata=v_f1)
v_f1$region2 <- ifelse(v_f1[,2] <= v_f1$upper2 & v_f1[,2] >= v_f1$lower2, 'inside', 'outside')
kl <- subset(v_f1, region2 =='inside')
g <- ggplot() + theme_bw() +
geom_smooth(data = kl, aes_string(kl[,1], kl[,2]), formula = y ~ poly(x,input$degree_2, raw = TRUE), method = "lm", color = "green3", level = 1, size = 0.5)
r <- ggplot_build(g)$data[[1]]
q <- data.frame(x = r[,1], y = r[,2])
#calculate rotation angle
beta <- -atan((q[1,2]-tail(q,1)[,2])/(q[1,1]-tail(q,1)[,1]))
#rotation matrix
rot_m <- matrix(c(cos(beta),sin(beta),-sin(beta),cos(beta)),ncol=2)
#shift, rotate, shift back
M_2 <- t(rot_m %*% (t(q)-c(q[1,1],q[1,2]))+c(q[1,1],q[1,2]))
M_2[nrow(M_2),2] <- M_2[1,2]
M_2
M_3 <- data.frame(x= (M_2[,1]-median(M_2[,1])), y= (M_2[,2]-min(M_2[,2])))
the_data <- reactive(M_3)
the_data()
})
I tried feeding the output of the DT::renderDataTable as input for ggplot but my shiny app is showing me an error saying that
Reading from shinyoutput object is not allowed.
I already knew that 'Reading from shinyoutput object is not allowed'.
I just want to know whether there is any way I can use the output rendered in datatable for further plotting in a shiny app.
Here's a MWE demonstrating what I think you want to do.
Notice the separation of data from presentation: t1, t2 and t3 are reactives representing your CSV files. Each is rendered in a different data table. allData is a reactive containing union of the CSV data. This is used as the source data for the plot.
library(shiny)
library(DT)
library(tidyverse)
ui <- fluidPage(
numericInput("n", "Number of points:", min=2, max=20, value=10),
plotOutput("plot"),
dataTableOutput("table1"),
dataTableOutput("table2"),
dataTableOutput("table3")
)
server <- function(input, output) {
t1 <- reactive({ tibble(x=1:input$n, y=rnorm(input$n), key="Table 1") })
t2 <- reactive({ tibble(x=1:input$n, y=rnorm(input$n), key="Table 2") })
t3 <- reactive({ tibble(x=1:input$n, y=rnorm(input$n), key="Table 3") })
allData <- reactive({ bind_rows(t1(), t2(), t3()) })
output$table1 <- renderDT({ t1() })
output$table2 <- renderDT({ t2() })
output$table3 <- renderDT({ t3() })
output$plot <-renderPlot({ allData() %>% ggplot() + geom_line(aes(x=x, y=y, colour=key)) })
}
shinyApp(ui = ui, server = server)
It might be worth looking at using modules to manage and present the CSV files.
I am trying to make an animated plot where new traces are introduced, the traces are animated, and the axes are then rescaled. I am having trouble getting these things to work together. A reprex is below. It works except when I have both Animate Traces and Rescale Axis, then the axis rescaling gets reset on every iteration.
Using Proxy Interface in Plotly/Shiny to dynamically change data
https://community.plot.ly/t/how-to-efficiently-restyle-update-modify-plot-containing-frames/5553
https://plot.ly/javascript/plotlyjs-function-reference/
https://plot.ly/javascript/animations/
It's rather hard to follow the Plotly documentation. I couldn't get addFrames, relayout, restyle, react, or update to work for me. I've had the most luck with animate. I would greatly appreciate any help, I've been struggling with this for two weeks already.
# plotly_add_anim13.R
library(shiny)
library(plotly)
library(dplyr)
library(purrr)
ui <- fluidPage(
checkboxInput("add", "Add Trace", TRUE),
checkboxInput("animate", "Animate Traces", FALSE),
checkboxInput("rescale", "Rescale Axis", FALSE),
plotlyOutput("plot")
)
server <- function(input, output, session){
my <- reactiveValues(
fnumber = NA, # frame number
frame = NA, # frame data list
ntraces = NA, # number of traces
xrange = NA # xaxis range
)
speed = 1000 # redraw interval in milliseconds
output$plot <- renderPlotly({
isolate({
cat("renderPlotly\n")
my$fnumber <- 1
my$ntraces <- 2
f <- as.character(my$fnumber)
x <- runif(2)
y <- rep(runif(1), 2)
t <- c("A", "B")
ids0 <- paste0(my$ntraces-2, letters[1:2])
ids1 <- paste0(my$ntraces-1, letters[1:2])
my$xrange <- c(0,1)
# https://community.plot.ly/t/how-to-efficiently-restyle-update-modify-plot-containing-frames/5553
my$frame <- list(
name = f,
data = list(
list(x=x, y=y, frame=f, ids=ids0, type="scatter", mode="lines", showlegend=FALSE),
list(x=x, y=y, frame=f, ids=ids1, type="scatter", mode="text", text=t, showlegend=FALSE)
),
traces = as.list(as.integer(c(my$ntraces-2, my$ntraces-1))),
layout = list(xaxis=list(range=my$xrange, zeroline=FALSE),
yaxis=list(range=c(0,1), tickmode="array", tickvals=seq(0,1,0.2), ticktext=seq(0,1,0.2)))
)
p <- plot_ly()
p <- do.call(add_trace, prepend(my$frame$data[[1]], list(p)))
p <- do.call(add_trace, prepend(my$frame$data[[2]], list(p)))
p <- do.call(layout, prepend(my$frame$layout, list(p)))
p <- animation_opts(p, frame=speed, transition=speed)
p
})
})
proxy <- plotlyProxy("plot", session=session)
# https://shiny.rstudio.com/reference/shiny/0.14/reactiveTimer.html
autoInvalidate <- reactiveTimer(speed*2)
observeEvent(autoInvalidate(), {
# req(NULL)
# https://stackoverflow.com/questions/50620360/using-proxy-interface-in-plotly-shiny-to-dynamically-change-data
# https://community.plot.ly/t/how-to-efficiently-restyle-update-modify-plot-containing-frames/5553
# https://plot.ly/javascript/animations/#frame-groups-and-animation-modes
# https://plot.ly/javascript/animations/
if (input$add){
cat("add trace\n")
my$fnumber <- my$fnumber + 1
my$ntraces <- my$ntraces + 2
f <- as.character(my$fnumber)
x <- runif(2)
y <- rep(runif(1), 2)
t <- c("A", "B")
ids0 <- paste0(my$ntraces-2, letters[1:2])
ids1 <- paste0(my$ntraces-1, letters[1:2])
my$frame$name <- f
my$frame$data[[my$ntraces-1]] <- list(x=x, y=y, frame=f, ids=ids0, type="scatter", mode="lines", showlegend=FALSE)
my$frame$data[[my$ntraces-0]] <- list(x=x, y=y, frame=f, ids=ids1, type="scatter", mode="text", text=t, showlegend=FALSE)
my$frame$traces <- as.list(as.integer(1:my$ntraces - 1))
plotlyProxyInvoke(proxy, "addTraces",
list(
my$frame$data[[my$ntraces-1]],
my$frame$data[[my$ntraces-0]]
))
plotlyProxyInvoke(proxy, "animate",
# frameOrGroupNameOrFrameList
list(
name = my$frame$name,
data = my$frame$data,
traces = my$frame$traces
),
# animationAttributes
list(
frame=list(duration=0),
transition=list(duration=0)
)
)# animate
}
if (input$animate){
cat("animate traces\n")
my$fnumber <- my$fnumber + 1
f <- as.character(my$fnumber)
traces <- 1:my$ntraces - 1
for (i in seq(0, my$ntraces-2, 2)){
x <- runif(2)
y <- rep(runif(1), 2)
t <- c("A", "B")
ids0 <- paste0(i, letters[1:2])
ids1 <- paste0(i+1, letters[1:2])
my$frame$data[[i+1]] <- list(x=x, y=y, frame=f, ids=ids0, type="scatter", mode="lines", showlegend=FALSE)
my$frame$data[[i+2]] <- list(x=x, y=y, frame=f, ids=ids1, type="scatter", mode="text", text=t, showlegend=FALSE)
}
my$frame$name <- f
plotlyProxyInvoke(proxy, "animate",
# frameOrGroupNameOrFrameList
list(
name = my$frame$name,
data = my$frame$data,
traces = my$frame$traces
),
# animationAttributes
list(
frame=list(duration=speed),
transition=list(duration=speed)
)
)# animate
}
if (input$rescale){
cat("animate layout\n")
my$fnumber <- my$fnumber + 1
f <- as.character(my$fnumber)
my$xrange <- runif(2)*0.1+c(-0.1,1)
my$frame$name <- f
my$frame$layout <- list(xaxis=list(range=my$xrange))
plotlyProxyInvoke(proxy, "animate",
# frameOrGroupNameOrFrameList
list(
name = my$frame$name,
data = my$frame$data,
traces = my$frame$traces,
layout = my$frame$layout
),
# animationAttributes
list(
frame=list(duration=speed),
transition=list(duration=speed)
)
) # animate
}
}) # observeEvent
}
shinyApp(ui, server)
I'm trying to set up an R shiny app that will enable viewing three types of plots relating to gene expression data.
The data are comprised of:
A data.frame which has the output of the differential expression analysis (each row is a gene and the columns are the effect sizes and their p-values):
set.seed(1)
model.df <- data.frame(id = paste0("g",1:30),symbol = sample(LETTERS[1:5],30,replace=T),
group.effect.size = rnorm(30), group.p.value = runif(30,0,1),
sex.effect.size = rnorm(30), sex.p.value = runif(30,0,1),
stringsAsFactors = F)
A data.frame which has the design of the study (each row is a sample and the columns are the factors that the sample is associated with):
set.seed(1)
design.df <- data.frame(group = c(rep("A",6),rep("B",6)), sex = rep(c(rep("F",3),rep("M",3)),2), replicate = rep(1:6,2)) %>%
dplyr::mutate(sample = paste0(group,".",sex,"_",replicate))
design.df$group <- factor(design.df$group, levels = c("A","B"))
design.df$sex <- factor(design.df$sex, levels = c("F","M"))
A matrix which has the abundance (each row is a gene and each column is a sample):
set.seed(1)
abundance.mat <- matrix(rnorm(30*12), nrow=30, ncol=12, dimnames=list(model.df$id,design.df$sample))
A data.frame which has the results of a gene set enrichment analysis (each row is a set name and the columns are the enrichment test p-values for each factor in design.df):
set.seed(1)
gsea.df <- data.frame(set.name = paste0("S",1:4), group.p.value = format(round(runif(4,0,1),2),scientific = T), sex.p.value = format(round(runif(4,0,1),2),scientific = T), stringsAsFactors = F)
And finally, a data.frame which associates the genes with each set.name in gsea.df:
set.seed(1)
gene.sets.df <- do.call(rbind,lapply(1:4,function(s) data.frame(set.name = paste0("S",s), id = sample(model.df$id,10,replace = F),stringsAsFactors = F)))
I would like the shiny app to enable viewing these types of plots:
Feature Plot - plotting expression level of a single user-selected gene on the y-axis and sample on the x-axis, and that would be combined with an inset of a caterpillar plot showing the estimated effects:
Feature User-Defined Sets Plot - same as Feature Plot, however rather than showing a single -selected gene this will show a set of user-selected-genes and hence rather than points it will show violins of the distributions:
Feature Sets GSEA Plot - a combined list of volcano plots, where in each one the x-axis is the effect size of the factor, the y-axis is the -log10(p-value) of the effect, and the genes are colored red if they belong to the selected gene set:
Here are the three functions for generating these figures given the user selection:
featurePlot <- function(selected.id)
{
replicate.df <- reshape2::melt(abundance.mat[which(rownames(abundance.mat) == selected.id),,drop=F], varnames=c("id","sample")) %>%
dplyr::left_join(design.df)
effects.df <- data.frame(factor.name = c("group","sex"),
effect.size = c(dplyr::filter(model.df,id == selected.id)$group.effect.size,dplyr::filter(model.df,id == selected.id)$sex.effect.size),
p.value = c(dplyr::filter(model.df,id == selected.id)$group.p.value,dplyr::filter(model.df,id == selected.id)$sex.p.value),
stringsAsFactors = F)
effects.df$factor.name <- factor(effects.df$factor.name, levels = c("group","sex"))
main.plot <- ggplot(replicate.df,aes(x=replicate,y=value,color=group,shape=sex))+
geom_point(size=3)+facet_grid(~group,scales="free_x")+
labs(x="Replicate",y="TPM")+theme_minimal()
xlims <- c(-1*max(abs(effects.df$effect.size))-0.1*max(abs(effects.df$effect.size)),max(abs(effects.df$effect.size))+0.1*max(abs(effects.df$effect.size)))
effects.plot <- ggplot(effects.df,aes(x=effect.size,y=factor.name,color=factor.name))+
geom_point()+
geom_vline(xintercept=0,linetype="longdash",colour="black",size=0.25)+theme_minimal()+xlim(xlims)+
theme(legend.position="none")+ylab("")+xlab("Effect Size")
null.plot <- ggplot(data.frame())+geom_point()+geom_blank()+theme_minimal()
combined.plot <- gridExtra::arrangeGrob(main.plot,gridExtra::arrangeGrob(null.plot,effects.plot,ncol=1),nrow=1,ncol=2,widths=c(5,2.5))
return(combined.plot)
}
featureSetPlot <- function(selected.ids)
{
replicate.df <- reshape2::melt(abundance.mat[which(rownames(abundance.mat) %in% selected.ids),,drop=F], varnames=c("id","sample")) %>%
dplyr::left_join(design.df)
replicate.df$replicate <- as.factor(replicate.df$replicate)
effects.df <- data.frame(factor.name = c("group","sex"),
effect.size = c(dplyr::filter(model.df,id %in% selected.ids)$group.effect.size,dplyr::filter(model.df,id %in% selected.ids)$sex.effect.size),
p.value = c(dplyr::filter(model.df,id %in% selected.ids)$group.p.value,dplyr::filter(model.df,id %in% selected.ids)$sex.p.value),
stringsAsFactors = F)
effects.df$factor.name <- factor(effects.df$factor.name, levels = c("group","sex"))
main.plot <- ggplot(replicate.df,aes(x=replicate,y=value,color=group,fill=sex))+
geom_violin(trim=F,draw_quantiles=c(0.25,0.5,0.75),alpha=0.25)+facet_grid(~group,scales="free_x")+
labs(x="Replicate",y="TPM")+theme_minimal()
effects.plot <- ggplot(effects.df,aes(y=effect.size,x=factor.name,color=factor.name,fill=factor.name))+
geom_violin(trim=F,draw_quantiles=c(0.25,0.5,0.75),alpha=0.25)+coord_flip()+
geom_hline(yintercept=0,linetype="longdash",colour="black",size=0.25)+theme_minimal()+
theme(legend.position="none")+xlab("")+ylab("Effect Size Distribution")
null.plot <- ggplot(data.frame())+geom_point()+geom_blank()+theme_minimal()
combined.plot <- gridExtra::arrangeGrob(main.plot,gridExtra::arrangeGrob(null.plot,effects.plot,ncol=1),nrow=1,ncol=2,widths=c(5,2.5))
return(combined.plot)
}
gseaPlot <- function(selected.set)
{
plot.df <- model.df %>%
dplyr::left_join(gene.sets.df %>% dplyr::filter(set.name == selected.set))
plot.df$set.name[which(is.na(plot.df$set.name))] <- "non.selected"
plot.df$set.name <- factor(plot.df$set.name, levels = c("non.selected",selected.set))
factor.names <- c("group","sex")
gsea.volcano.plot <- lapply(factor.names,function(f)
plotly::plot_ly(type='scatter',mode="markers",marker=list(size=5),color=plot.df$set.name,colors=c("lightgray","darkred"),x=plot.df[,paste0(f,".effect.size")],y=-log10(plot.df[,paste0(f,".p.value")]),showlegend=F) %>%
plotly::layout(annotations=list(showarrow=F,x=0.5,y=0.95,align="center",xref="paper",xanchor="center",yref="paper",yanchor="bottom",font=list(size=12,color="darkred"),text=paste0(f," (",dplyr::filter(gsea.df,set.name == selected.set)[,paste0(f,".p.value")],")")),
xaxis=list(title=paste0(f," Effect"),zeroline=F),yaxis=list(title="-log10(p-value)",zeroline=F))
) %>% plotly::subplot(nrows=1,shareX=F,shareY=T,titleX=T,titleY=T) %>%
plotly::layout(title=selected.set)
return(gsea.volcano.plot)
}
Thus:
plot.type.choices <- c('Feature User-Defined Set Plot','Feature Sets GSEA Plot','Feature Plot')
So the first two use ggplot2 for generating each of the two figures they combine, which is then achieved using gridExtra::arrangeGrob. The last one uses plotly.
Here's the shiny code part I've been trying out, but with no luck:
server <- function(input, output)
{
out.plot <- reactive({
if(input$plotType == "Feature Plot"){
out.plot <- featurePlot(selected.id=dplyr::filter(model.df,symbol == input$symbol)$id[1])
} else if(input$plotType == "Feature User-Defined Set Plot"){
out.plot <- featureSetPlot(selected.ids=unique(dplyr::filter(model.df,symbol == input$set.symbols)$id))
} else if(input$plotType == "Feature Sets GSEA Plot"){
out.plot <- gseaVolcanoPlot(selected.set=input$set.name)
}
})
output$out.plot <- renderPlot({
if(input$plotType != "Feature Sets GSEA Plot"){
grid::grid.draw(out.plot())
} else{
out.plot()
}
})
output$save <- downloadHandler(
filename = function() {
paste0("./plot.pdf")
},
content = function(file) {
ggsave(out.plot(),filename=file,width=10,height=5)
}
)
}
ui <- fluidPage(
tags$style(type="text/css",".shiny-output-error { visibility: hidden; }",".shiny-output-error:before { visibility: hidden; }"),
titlePanel("Results Explorer"),
sidebarLayout(
sidebarPanel(
# select plot type
selectInput("plotType","Plot Type",choices=plot.type.choices),
#in case Feature User-Defined Set Plot was chosen select the genes
conditionalPanel(condition="input.plotType=='Feature User-Defined Set Plot'",
selectizeInput(inputId="set.symbols",label="Features Set Symbols",choices=unique(model.df$symbol),selected=model.df$symbol[1],multiple=T)),
#in case Feature Sets GSEA Plot was chosen select the databses
conditionalPanel(condition="input.plotType=='Feature Sets GSEA Plot'",
selectizeInput(inputId="set.name",label="Set Name",choices=unique(gene.sets.df$set.name),selected=gene.sets.df$set.name[1],multiple=F)),
#in case Feature Plot was chosen select the gene
conditionalPanel(condition="input.plotType=='Feature Plot'",
selectizeInput(inputId="symbol",label="Feature Symbol",choices=unique(model.df$symbol),selected=unique(model.df$symbol)[1],multiple=F)),
downloadButton('save', 'Save to File')
),
mainPanel(
plotOutput("output.plot")
)
)
)
shinyApp(ui = ui, server = server)
I'm suspecting that the renderPlot here may be the issue since I probably have to use plotly::renderPlotly for the Feature Sets GSEA Plot option but I'm not really sure how to tie it all up in the shiny server part.
Another complication that exists and it would be nice to have a solution for is the fact that the gene symbols are not unique WRT gene IDs (as shown in model.df). So it would be nice to have a list that's added if the user selected the Feature Plot option, and that list will show the subset of gene IDs which the selected symbol maps to (dplyr::filter(model.df == input$symbol)$id)
Thanks!
I also guess the problem is "renderPlot".
One, not so very elegant way that should solve this problem would be to instead of one output, split it in two, but only ever display one of both using "req()".
This piece of code would become:
output$out.plot <- renderPlot({
....
})
This:
output$out.plot1 <- renderPlot({
req(input$plotType != "Feature Sets GSEA Plot")
grid::grid.draw(out.plot())
})
output$out.plot2 <- renderPlotly({
req(input$plotType == "Feature Sets GSEA Plot")
out.plot()
})
You can now just add the the plots below each other in you UI.
"req()" makes sure absolultely nothing is plotted when the statement inside it is not "truthy" (see ?req), in this case "TRUE". The user would not see a difference between this and replacing one output like you tried.
Here's my solution from start to end:
Packages to load:
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(plotly))
suppressPackageStartupMessages(library(shiny))
Generate example data:
set.seed(1)
model.df <- data.frame(id = paste0("g",1:30),symbol = sample(LETTERS[1:5],30,replace=T),
group.effect.size = rnorm(30), group.p.value = runif(30,0,1),
sex.effect.size = rnorm(30), sex.p.value = runif(30,0,1),
stringsAsFactors = F)
set.seed(1)
design.df <- data.frame(group = c(rep("A",6),rep("B",6)), sex = rep(c(rep("F",3),rep("M",3)),2), replicate = rep(1:6,2)) %>%
dplyr::mutate(sample = paste0(group,".",sex,"_",replicate))
design.df$group <- factor(design.df$group, levels = c("A","B"))
design.df$sex <- factor(design.df$sex, levels = c("F","M"))
set.seed(1)
abundance.mat <- matrix(rnorm(30*12), nrow=30, ncol=12, dimnames=list(model.df$id,design.df$sample))
set.seed(1)
gsea.df <- data.frame(set.name = paste0("S",1:4), group.p.value = format(round(runif(4,0,1),2),scientific = T), sex.p.value = format(round(runif(4,0,1),2),scientific = T), stringsAsFactors = F)
set.seed(1)
gene.sets.df <- do.call(rbind,lapply(1:4,function(s) data.frame(set.name = paste0("S",s), id = sample(model.df$id,10,replace = F),stringsAsFactors = F)))
plot.type.choices <- c("Feature Plot","User-Defined Feature Set Plot","Feature Sets GSEA Plot")
Plotting functions:
featurePlot <- function(selected.id)
{
replicate.df <- reshape2::melt(abundance.mat[which(rownames(abundance.mat) == selected.id),,drop=F], varnames=c("id","sample")) %>%
dplyr::left_join(design.df)
effects.df <- data.frame(factor.name = c("group","sex"),
effect.size = c(dplyr::filter(model.df,id == selected.id)$group.effect.size,dplyr::filter(model.df,id == selected.id)$sex.effect.size),
p.value = c(dplyr::filter(model.df,id == selected.id)$group.p.value,dplyr::filter(model.df,id == selected.id)$sex.p.value),
stringsAsFactors = F)
effects.df$factor.name <- factor(effects.df$factor.name, levels = c("group","sex"))
main.plot <- ggplot(replicate.df,aes(x=replicate,y=value,color=group,shape=sex))+
geom_point(size=3)+facet_grid(~group,scales="free_x")+
labs(x="Replicate",y="TPM")+theme_minimal()
xlims <- c(-1*max(abs(effects.df$effect.size))-0.1*max(abs(effects.df$effect.size)),max(abs(effects.df$effect.size))+0.1*max(abs(effects.df$effect.size)))
effects.plot <- ggplot(effects.df,aes(x=effect.size,y=factor.name,color=factor.name))+
geom_point()+
geom_vline(xintercept=0,linetype="longdash",colour="black",size=0.25)+theme_minimal()+xlim(xlims)+
theme(legend.position="none")+ylab("")+xlab("Effect Size")
null.plot <- ggplot(data.frame())+geom_point()+geom_blank()+theme_minimal()
combined.plot <- gridExtra::arrangeGrob(main.plot,gridExtra::arrangeGrob(null.plot,effects.plot,ncol=1),nrow=1,ncol=2,widths=c(5,2.5))
return(combined.plot)
}
featureSetPlot <- function(selected.ids)
{
replicate.df <- reshape2::melt(abundance.mat[which(rownames(abundance.mat) %in% selected.ids),,drop=F], varnames=c("id","sample")) %>%
dplyr::left_join(design.df)
replicate.df$replicate <- as.factor(replicate.df$replicate)
effects.df <- data.frame(factor.name = c("group","sex"),
effect.size = c(dplyr::filter(model.df,id %in% selected.ids)$group.effect.size,dplyr::filter(model.df,id %in% selected.ids)$sex.effect.size),
p.value = c(dplyr::filter(model.df,id %in% selected.ids)$group.p.value,dplyr::filter(model.df,id %in% selected.ids)$sex.p.value),
stringsAsFactors = F)
effects.df$factor.name <- factor(effects.df$factor.name, levels = c("group","sex"))
main.plot <- ggplot(replicate.df,aes(x=replicate,y=value,color=group,fill=sex))+
geom_violin(trim=F,draw_quantiles=c(0.25,0.5,0.75),alpha=0.25)+facet_grid(~group,scales="free_x")+
labs(x="Replicate",y="TPM")+theme_minimal()
effects.plot <- ggplot(effects.df,aes(y=effect.size,x=factor.name,color=factor.name,fill=factor.name))+
geom_violin(trim=F,draw_quantiles=c(0.25,0.5,0.75),alpha=0.25)+coord_flip()+
geom_hline(yintercept=0,linetype="longdash",colour="black",size=0.25)+theme_minimal()+
theme(legend.position="none")+xlab("")+ylab("Effect Size Distribution")
null.plot <- ggplot(data.frame())+geom_point()+geom_blank()+theme_minimal()
combined.plot <- gridExtra::arrangeGrob(main.plot,gridExtra::arrangeGrob(null.plot,effects.plot,ncol=1),nrow=1,ncol=2,widths=c(5,2.5))
return(combined.plot)
}
gseaPlot <- function(selected.set)
{
plot.df <- model.df %>%
dplyr::left_join(gene.sets.df %>% dplyr::filter(set.name == selected.set))
plot.df$set.name[which(is.na(plot.df$set.name))] <- "non.selected"
plot.df$set.name <- factor(plot.df$set.name, levels = c("non.selected",selected.set))
factor.names <- c("group","sex")
gsea.plot <- lapply(factor.names,function(f)
plotly::plot_ly(type='scatter',mode="markers",marker=list(size=5),color=plot.df$set.name,colors=c("lightgray","darkred"),x=plot.df[,paste0(f,".effect.size")],y=-log10(plot.df[,paste0(f,".p.value")]),showlegend=F) %>%
plotly::layout(annotations=list(showarrow=F,x=0.5,y=0.95,align="center",xref="paper",xanchor="center",yref="paper",yanchor="bottom",font=list(size=12,color="darkred"),text=paste0(f," (",dplyr::filter(gsea.df,set.name == selected.set)[,paste0(f,".p.value")],")")),
xaxis=list(title=paste0(f," Effect"),zeroline=F),yaxis=list(title="-log10(p-value)",zeroline=F))
) %>% plotly::subplot(nrows=1,shareX=F,shareY=T,titleX=T,titleY=T) %>%
plotly::layout(title=selected.set)
return(gsea.plot)
}
Server:
server <- function(input, output)
{
out.plot <- reactive({
if(input$plotType == "Feature Plot"){
out.plot <- featurePlot(selected.id=dplyr::filter(model.df,symbol == input$symbol)$id[1])
} else if(input$plotType == "User-Defined Feature Set Plot"){
out.plot <- featureSetPlot(selected.ids=unique(dplyr::filter(model.df,symbol == input$set.symbols)$id))
} else if(input$plotType == "Feature Sets GSEA Plot"){
out.plot <- gseaPlot(selected.set=input$set.name)
}
})
output$feature.plot <- renderPlot({
req(input$plotType == "Feature Plot")
grid::grid.draw(out.plot())
})
output$user.defined.feature.set.plot <- renderPlot({
req(input$plotType == "User-Defined Feature Set Plot")
grid::grid.draw(out.plot())
})
output$feature.set.gsea.plot <- renderPlotly({
req(input$plotType == "Feature Sets GSEA Plot")
out.plot()
})
output$save <- downloadHandler(
filename = function() {
paste0("./plot.pdf")
},
content = function(file) {
if(input$plotType != "Feature Sets GSEA Plot"){
ggsave(out.plot(),filename=file,width=10,height=5)
} else{
plotly::export(out.plot(),file=file)
}
}
)
}
UI:
ui <- fluidPage(
tags$style(type="text/css",".shiny-output-error { visibility: hidden; }",".shiny-output-error:before { visibility: hidden; }"),
titlePanel("Results Explorer"),
sidebarLayout(
sidebarPanel(
# select plot type
selectInput("plotType","Plot Type",choices=plot.type.choices),
#in case User-Defined Feature Set Plot was chosen select the genes
conditionalPanel(condition="input.plotType == 'User-Defined Feature Set Plot'",
selectizeInput(inputId="set.symbols",label="Features Set Symbols",choices=unique(model.df$symbol),selected=model.df$symbol[1],multiple=T)),
#in case Feature Sets GSEA Plot was chosen select the databses
conditionalPanel(condition="input.plotType == 'Feature Sets GSEA Plot'",
selectizeInput(inputId="set.name",label="Set Name",choices=unique(gene.sets.df$set.name),selected=gene.sets.df$set.name[1],multiple=F)),
#in case Feature Plot was chosen select the gene
conditionalPanel(condition="input.plotType == 'Feature Plot'",
selectizeInput(inputId="symbol",label="Feature Symbol",choices=unique(model.df$symbol),selected=unique(model.df$symbol)[1],multiple=F)),
downloadButton('save', 'Save to File')
),
mainPanel(
conditionalPanel(
condition = "input.plotType == 'User-Defined Feature Set Plot'",
plotOutput("user.defined.feature.set.plot")
),
conditionalPanel(
condition = "input.plotType == 'Feature Sets GSEA Plot'",
plotly::plotlyOutput("feature.set.gsea.plot")
),
conditionalPanel(
condition = "input.plotType == 'Feature Plot'",
plotOutput("feature.plot")
)
)
)
)
Call:
shinyApp(ui = ui, server = server)