在R Shiny应用程序中渲染ggplot2和一个可绘制对象 [英] Rendering a ggplot2 and a plotly object in an R Shiny app
问题描述
我有一个R
shiny
应用程序,它同时使用R
的plotly
和ggplot2
来生成和显示图形.
I have an R
shiny
app that uses both R
's plotly
and ggplot2
to produce and display figures.
因为在shiny
中渲染plotly
图形需要plotly
的renderPlotly
函数,所以ggplot2
图形被转换为renderPlotly
部分中的plotly
对象,这使它们有些混乱.
Because rendering plotly
figures in shiny
requires plotly
's the renderPlotly
function the ggplot2
figures get converted to plotly
objects in the renderPlotly
part, which messes them up a bit.
这是一个例子. 首先,生成一些数据:
Here's an example. First, generate some data:
set.seed(1)
meta.df <- data.frame(cell = c(paste0("c_",1:1000,"_1w"), paste0("c_",1:1000,"_2w"), paste0("c_",1:1000,"_3w")),
cluster = c(sample(c("cl1","cl2","cl3"),1000,replace=T)),
age = c(rep(1,1000),rep(2,1000),rep(3,1000)),
x = rnorm(3000), y = rnorm(3000))
expression.mat <- cbind(matrix(rnorm(20*1000,1,1), nrow=20, ncol=1000, dimnames=list(paste0("g",1:20),meta.df$cell[1:1000])),
matrix(rnorm(20*1000,2,1), nrow=20, ncol=1000, dimnames=list(paste0("g",1:20),meta.df$cell[1001:2000])),
matrix(rnorm(20*1000,3,1), nrow=20, ncol=1000, dimnames=list(paste0("g",1:20),meta.df$cell[2001:3000])))
这是应用程序代码:
library(shiny)
library(dplyr)
library(ggplot2)
library(ggpmisc)
server <- function(input, output, session)
{
output$gene <- renderUI({
selectInput("gene", "Select Gene to Display", choices = rownames(expression.mat))
})
output$group <- renderUI({
if(input$plotType == "Distribution Plot"){
selectInput("group", "Select Group", choices = c("cluster","age"))
}
})
scatter.plot <- reactive({
scatter.plot <- NULL
if(!is.null(input$gene)){
gene.idx <- which(rownames(expression.mat) == input$gene)
plot.df <- suppressWarnings(meta.df %>% dplyr::left_join(data.frame(cell=colnames(expression.mat),value=expression.mat[gene.idx,]),by=c("cell"="cell")))
scatter.plot <- suppressWarnings(plotly::plot_ly(marker=list(size=3),type='scatter',mode="markers",color=plot.df$value,x=plot.df$x,y=plot.df$y,showlegend=F,colors=colorRamp(c("lightgray","darkred"))) %>%
plotly::layout(title=input$gene,xaxis=list(zeroline=F,showticklabels=F,showgrid=F),yaxis=list(zeroline=F,showticklabels=F,showgrid=F)) %>%
plotly::colorbar(limits=c(min(plot.df$value,na.rm=T),max(plot.df$value,na.rm=T)),len=0.4,title="Scaled Expression"))
}
return(scatter.plot)
})
distribution.plot <- reactive({
distribution.plot <- NULL
if(!is.null(input$gene) & !is.null(input$group)){
gene.idx <- which(rownames(expression.mat) == input$gene)
plot.df <- suppressWarnings(meta.df %>% dplyr::left_join(data.frame(cell=colnames(expression.mat),value=expression.mat[gene.idx,]),by=c("cell"="cell")))
if(input$group == "cluster"){
distribution.plot <- suppressWarnings(plotly::plot_ly(x=plot.df$cluster,y=plot.df$value,split=plot.df$cluster,type='violin',box=list(visible=T),points=T,color=plot.df$cluster,showlegend=F) %>%
plotly::layout(title=input$gene,xaxis=list(title=input$group,zeroline=F),yaxis=list(title="Scaled Expression",zeroline=F)))
} else{
plot.df <- plot.df %>% dplyr::mutate(time=age) %>% dplyr::arrange(time)
plot.df$age <- factor(plot.df$age,levels=unique(plot.df$age))
distribution.plot <- suppressWarnings(ggplot(plot.df,aes(x=time,y=value)) +
geom_violin(aes(fill=age,color=age),alpha=0.3) +
geom_boxplot(width=0.1,aes(color=age),fill=NA) +
geom_smooth(mapping=aes(x=time,y=value,group=cluster),color="black",method='lm',size=1,se=T) +
stat_poly_eq(mapping=aes(x=time,y=value,group=cluster,label=stat(p.value.label)),formula=y~x,parse=T,npcx="center",npcy="bottom") +
scale_x_discrete(name=NULL,labels=levels(plot.df$cluster),breaks=unique(plot.df$time)) +
facet_wrap(~cluster) + theme_minimal() + ylab(paste0("#",input$gene," Scaled Expressioh"))+theme(legend.title=element_blank()))
}
}
return(distribution.plot)
})
output$out.plot <- plotly::renderPlotly({
if(input$plotType == "Scatter Plot"){
scatter.plot()
} else if(input$plotType == "Distribution Plot"){
distribution.plot()
}
})
}
ui <- fluidPage(
titlePanel("Explorer"),
sidebarLayout(
sidebarPanel(
tags$head(
tags$style(HTML(".multicol {-webkit-column-count: 3; /* Chrome, Safari, Opera */-moz-column-count: 3; /* Firefox */column-count: 3;}")),
tags$style(type="text/css", "#loadmessage {position: fixed;top: 0px;left: 0px;width: 100%;padding: 5px 0px 5px 0px;text-align: center;font-weight: bold;font-size: 100%;color: #000000;background-color: #CCFF66;z-index: 105;}"),
tags$style(type="text/css",".shiny-output-error { visibility: hidden; }",".shiny-output-error:before { visibility: hidden; }")),
conditionalPanel(condition="$('html').hasClass('shiny-busy')",tags$div("In Progress...",id="loadmessage")),
selectInput("plotType", "Plot Type", choices = c("Scatter Plot","Distribution Plot")),
uiOutput("gene"),
uiOutput("group"),
),
mainPanel(
plotly::plotlyOutput("out.plot")
)
)
)
shinyApp(ui = ui, server = server)
如果用户选择Distribution Plot
Plot Type
和年龄" Group
,然后用ggplot2
和ggpmisc
生成图形.作为ggplot2
对象,这些数字如下所示:
If the user chooses the Distribution Plot
Plot Type
and the "age" Group
then the figure is generated with ggplot2
and ggpmisc
. As ggplot2
objects these figures look like this:
但是作为plotly
对象(我想像plotly::renderPlotly
部分使用plotly
的ggplotly
函数从ggplot2
对象转换),它变为:
But as a plotly
object (which I imagine the plotly::renderPlotly
part converts from a ggplot2
object using plotly
's ggplotly
function) it becomes:
如您所见,底部的P值丢失了,图例为行为异常".
As you can see the P-values in the bottom are missing and the legend is 'misbehaving'.
首选解决方案是,如果对象是plotly
对象,请使用plotly::renderPlotly
函数;如果对象是ggplot2
对象,请使用render
,但是我不知道如何实现(认为很难解决并更正ggplot2
对象在转换为plotly
对象时所经历的修改).
The preferred solution is to have shiny use the plotly::renderPlotly
function if the object is a plotly
object and the render
if it is a ggplot2
object but I don't know how to implement that (I think it'll be harder to work out and correct the modifications that the ggplot2
object undergoes in its conversion to a plotly
object).
有什么主意吗?
推荐答案
我认为最简单的解决方案是定义2个输出,一个在plotly
绘图之前,一个在ggplot
绘图之前,并使用shinyjs
进行显示/根据输入隐藏正确的图:
I think the easiest solution is to define 2 outputs, one fore the plotly
plots and one for the ggplot
plots and use shinyjs
to show/hide the correct plot based on the inputs:
set.seed(1)
meta.df <- data.frame(cell = c(paste0("c_",1:1000,"_1w"), paste0("c_",1:1000,"_2w"), paste0("c_",1:1000,"_3w")),
cluster = c(sample(c("cl1","cl2","cl3"),1000,replace=T)),
age = c(rep(1,1000),rep(2,1000),rep(3,1000)),
x = rnorm(3000), y = rnorm(3000))
expression.mat <- cbind(matrix(rnorm(20*1000,1,1), nrow=20, ncol=1000, dimnames=list(paste0("g",1:20),meta.df$cell[1:1000])),
matrix(rnorm(20*1000,2,1), nrow=20, ncol=1000, dimnames=list(paste0("g",1:20),meta.df$cell[1001:2000])),
matrix(rnorm(20*1000,3,1), nrow=20, ncol=1000, dimnames=list(paste0("g",1:20),meta.df$cell[2001:3000])))
library(shiny)
library(dplyr)
library(ggplot2)
library(ggpmisc)
library(shinyjs)
server <- function(input, output, session)
{
output$gene <- renderUI({
selectInput("gene", "Select Gene to Display", choices = rownames(expression.mat))
})
output$group <- renderUI({
if(input$plotType == "Distribution Plot"){
selectInput("group", "Select Group", choices = c("cluster","age"))
}
})
scatter.plot <- reactive({
scatter.plot <- NULL
if(!is.null(input$gene)){
gene.idx <- which(rownames(expression.mat) == input$gene)
plot.df <- suppressWarnings(meta.df %>% dplyr::left_join(data.frame(cell=colnames(expression.mat),value=expression.mat[gene.idx,]),by=c("cell"="cell")))
scatter.plot <- suppressWarnings(plotly::plot_ly(marker=list(size=3),type='scatter',mode="markers",color=plot.df$value,x=plot.df$x,y=plot.df$y,showlegend=F,colors=colorRamp(c("lightgray","darkred"))) %>%
plotly::layout(title=input$gene,xaxis=list(zeroline=F,showticklabels=F,showgrid=F),yaxis=list(zeroline=F,showticklabels=F,showgrid=F)) %>%
plotly::colorbar(limits=c(min(plot.df$value,na.rm=T),max(plot.df$value,na.rm=T)),len=0.4,title="Scaled Expression"))
}
return(scatter.plot)
})
distribution.plot <- reactive({
distribution.plot <- NULL
if(!is.null(input$gene) & !is.null(input$group)){
gene.idx <- which(rownames(expression.mat) == input$gene)
plot.df <- suppressWarnings(meta.df %>% dplyr::left_join(data.frame(cell=colnames(expression.mat),value=expression.mat[gene.idx,]),by=c("cell"="cell")))
if(input$group == "cluster"){
distribution.plot <- suppressWarnings(plotly::plot_ly(x=plot.df$cluster,y=plot.df$value,split=plot.df$cluster,type='violin',box=list(visible=T),points=T,color=plot.df$cluster,showlegend=F) %>%
plotly::layout(title=input$gene,xaxis=list(title=input$group,zeroline=F),yaxis=list(title="Scaled Expression",zeroline=F)))
} else{
plot.df <- plot.df %>% dplyr::mutate(time=age) %>% dplyr::arrange(time)
plot.df$age <- factor(plot.df$age,levels=unique(plot.df$age))
distribution.plot <- suppressWarnings(ggplot(plot.df,aes(x=time,y=value)) +
geom_violin(aes(fill=age,color=age),alpha=0.3) +
geom_boxplot(width=0.1,aes(color=age),fill=NA) +
geom_smooth(mapping=aes(x=time,y=value,group=cluster),color="black",method='lm',size=1,se=T) +
stat_poly_eq(mapping=aes(x=time,y=value,group=cluster,label=stat(p.value.label)),formula=y~x,parse=T,npcx="center",npcy="bottom") +
scale_x_discrete(name=NULL,labels=levels(plot.df$cluster),breaks=unique(plot.df$time)) +
facet_wrap(~cluster) + theme_minimal() + ylab(paste0("#",input$gene," Scaled Expressioh"))+theme(legend.title=element_blank()))
}
}
return(distribution.plot)
})
output$out.plot_plotly <- plotly::renderPlotly({
if(input$plotType == "Scatter Plot"){
scatter.plot()
} else {
req(input$group)
if (input$plotType == "Distribution Plot" && input$group != "age"){
distribution.plot()
}
}
})
output$out.plot_plot <- renderPlot({
req(input$group)
if (input$plotType == "Distribution Plot" && input$group == "age") {
distribution.plot()
}
})
observeEvent(c(input$group, input$plotType), {
req(input$group)
if (input$group == "age" && input$plotType == "Distribution Plot") {
hide("out.plot_plotly")
show("out.plot_plot")
} else {
hide("out.plot_plot")
show("out.plot_plotly")
}
})
}
ui <- fluidPage(
titlePanel("Explorer"),
useShinyjs(),
sidebarLayout(
sidebarPanel(
tags$head(
tags$style(HTML(".multicol {-webkit-column-count: 3; /* Chrome, Safari, Opera */-moz-column-count: 3; /* Firefox */column-count: 3;}")),
tags$style(type="text/css", "#loadmessage {position: fixed;top: 0px;left: 0px;width: 100%;padding: 5px 0px 5px 0px;text-align: center;font-weight: bold;font-size: 100%;color: #000000;background-color: #CCFF66;z-index: 105;}"),
tags$style(type="text/css",".shiny-output-error { visibility: hidden; }",".shiny-output-error:before { visibility: hidden; }")),
conditionalPanel(condition="$('html').hasClass('shiny-busy')",tags$div("In Progress...",id="loadmessage")),
selectInput("plotType", "Plot Type", choices = c("Scatter Plot","Distribution Plot")),
uiOutput("gene"),
uiOutput("group"),
),
mainPanel(
plotly::plotlyOutput("out.plot_plotly"),
plotOutput("out.plot_plot")
)
)
)
shinyApp(ui = ui, server = server)
这篇关于在R Shiny应用程序中渲染ggplot2和一个可绘制对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!