『TBtools-plugin』与shiny App联姻小试牛刀
扯在前面的蛋
紧急插播:mac用户在安装插件之前,需要先打开Rserver输入以下代码:
install.packages('https://cran.r-project.org/bin/macosx/contrib/4.0/cachem_1.0.3.tgz', repo=NULL,type='source')
原因是cachem
包前几天更新了,是shiny的依赖包,不知道是tuna没有更新cachem
在macos下的二进制包还是怎么回事,R会从源码(src)编译,在win下没问题,但是貌似在mac下编译会出错... 所以mac用户这段时期只能安装cran
的binaries
,而且测试了下新的1.0.4版本貌似不能用,(不清楚是shiny没更新还是tuna的问题)1.0.3版本目前没问题。
自从CJ大佬打包了R之后,实现了不需要折腾linux或者unix终端,或者windows下用Rscript运行R脚本,直接通过点点点实现,其实这个操作在之前的Shiny中早就实现了,但是一直没有勇气去折腾... 潜哥和邵博建议我去鼓捣下,过年这段日子也没法干活,就跟着B站搬运油管的shiny app入门的视频教程学了起来。传送门:点击阅读原文
B站:手把手 如何制作R Shiny app 油管:R Shiny app tutorial # 1 - How to make shiny apps - An introduction to Shiny github: R-Shinyapp-Tutorial
作者:Abhinav Agrawal这位老哥的教程可以说是一勺一勺喂,感觉在稍微精通R的情况下,只要不憨,把视频啃完,上手shiny App没问题...额,时间有限,没有看完大佬的讲解,直接上github把代码撸下来看着理解。现在也基本看了不到1/5,了解一点点皮毛,就开始上手搞了...依样画葫芦,用辣眼睛的代码把之前的那个bubble plot做成了shiny app。虽然很粗糙,but,又不是不能用!
Shiny App的优点在于你可以实时调整参数而且看到调参后的结果,这个就很爽了。试想一下之后吧OneStepWGCNA写成Shiny App,可以放更多的参数,甚至可以分步做,第一步样品聚类剔除掉离群值,然后看sft选择合适的power,接着根据分模块的情况选择最小模块基因数量...后续还有拿出感兴趣的基因做GS MM,导出关系等...(我好像给自己挖了个坑...)
而今天的推文主要是我发现shiny app其实可以写成脚本用Rscript启动,然后本地测试,既然能用Rscript跑,那么就能搞成插件... 搞了一天整出来了。后续还是CJ大佬的一个常规操作,点击运行后直接弹出浏览器,运行Shiny App。
Shiny App界面
简单来讲可以把它看做一个网页工具。
我们把左边的灰色的这一栏叫做side bar
里面是一些调节参数的部件,
Browse... 点击后上传你的富集分析结果, Input File Type 选择你输入的文件是什么软件导出的结果,TBtools的GO enrichment工具还是kegg enrichment工具,这两种结果可以直接上传软件生成的原始表格,但是其他富集分析的结果比如 David
等一些其他软件导出的结果需要按照我之前提到的格式修改表格并且保存成制表符分隔符的txt文件再进行导入。这里我放了GSEA的结果,这个是新加的。如果有GSEA的结果想要展示需要按照以下格式整理,就是将GSEA结果中gsea_report_for_xxx.A.tsv
和gsea_report_for_xxx.B.tsv
两个文件中的term合并,当然筛选条目的标准自己定义,这里不做推荐。GSEA结果的可视化现在还不完美,后续会继续调整...
Select the value from Slider 额,这里偷懒忘了改label了,这里其实想说的是The number of GO term (KEGG pathway) to be displayed 就是图片上想展示的GO Term或者KEGG pathway数量。 Pvalue color Minimum/maxmum pvalue的颜色范围,这个好理解,点开就是个取色板,玩过PS或者AI的同学应该熟悉。 Project name 我执意保留的一个没啥用的选项... 用来区分你不同的富集分析结果导出图片的名字。 Graph size-width/height 在插件版中,我自己写了一个 figsize
的函数来自动调整图片大小随着 longest term和term number变化自动调节,这里把这个权利还给了用户,让你自定义大小,输出自己看着顺眼的图片。
右边有两个页面,一个Table,一个plot
table目前没有做扩展,只是简单的将你输入的表格显示出来,后续可能会出一些选项实现筛选,过滤等操作.... plot就是简洁明了的图片展示,现在还没有找到调整height的合适方法,不过可以达到预览的效果... 凑合用吧... Download按钮 如果你GO term(或者kegg pathway)中最长的那个term又臭又长,我建议把size-width调大一点,如果你选择的GO term很多,比如100,建议把height也调大一点。不过那么大的图片基本放论文里不可能了.... 后续可能会把上限调高一点... 看用户反馈
下面内容适合插件开发者。
TBtools plugin Shiny App 脚本结构
可能有人觉得我是脱了裤放屁,多此一举,都整成shiny app了,为啥还要打成TBtools插件,难道是在搞套娃吗..其实不然,
首先,我没有自己的服务器去部署shiny app,即便之前买了3年的腾讯云,但是1核2G... 还是鸡肋。所以不如打包在自己的pc上跑; 其次,就算写好了R脚本,运行起来,不管通过Rstudio还是Rscript,都有一定的门槛,虽然很低... 不是说写成插件就所有人都会用了,还可能有一部分同学搞不明白,根据之前的反馈,多半是死在了包安装上... 但这是我能想到的目前可行的最方便的方法了...
我们看Shiny App的脚本结构基本分为ui
和server
两部分,最后shinyApp(ui = ui, server = server)
,当然要让软件跑起来,还需要预先做好准备工作所以我的脚本大概分为下面4部分:
准备工作
一些预设选项和包加载,其中shiny
和htmltools
是必须装的,而且独立安装htmltools的版本和shiny
要求的版本不符,所以建议先装shiny,然后library一下,会提示需要装哪个版本的htmltools
然后再装htmltools
就搞定了,另外jscode
这里和shinyjs
也写上,这些是为后面在界面上出现一个关闭APP按钮而设置的,不然你用插件打开shinny app后没办法关闭它,在你关机或重启之前,你那个localhost的端口会一直被占用。
## options and import packagesoptions(stringsAsFactors = F)options('repos' = c(CRAN='https://mirrors.tuna.tsinghua.edu.cn/CRAN/'))jscode <- 'shinyjs.closeWindow = function() { window.close(); }' # using shinyjs to stop shiny appif (!require('shiny')) install.packages('shiny')library(shiny)if (!require('htmltools')) install.packages('htmltools')if (!require('ggplot2')) install.packages('ggplot2')if (!require('dplyr')) install.packages('dplyr')if (!require('stringr')) install.packages('stringr')if (!require('magrittr')) install.packages('magrittr')if (!require('shinyjs')) install.packages('shinyjs')if (!require('colourpicker')) install.packages('colourpicker')library(shiny)library(shinyjs)library(colourpicker)library(ggplot2)library(dplyr)library(magrittr)library(stringr)
提前写好function
主要是方便后面调用,如果到了server中再去写这些会使server部分的代码看起来很臃肿,其次是这些function我提前写好了....
## functions
## 01.GO Bubble Plot
## function中内容太长了,省略掉了
GOBubblePlot = function(expfile, num, name,colormin,colormax,Gtype){...}
KEGGBubblePlot = function(expfile, num, name,colormin,colormax,Gtype){...}
customBubblePlot = function(expfile, num, name,colormin,colormax,Gtype){...}
GSEAplot = function(expfile, num, name,colormin,colormax,Gtype){...}
ui(界面)
没有用shinydashboard,就默认的...美化之类的是以后有空琢磨的事,现在的任务就是把它跑出来。节省版面,具体设置省略 代码放在gitee.大框架就是先整一个fluidPage,下层分别是:
标题框 调用shinyjs 动作是关闭窗口 动作按钮设置 sidebar 框架 sidebar (参数调整界面) mainPanel (结果呈现界面)
ui = shinyUI(fluidPage( titlePanel(h4('Enrichment Bubble Plot')),
# stop shiny app via js code useShinyjs(), extendShinyjs(text = jscode, functions = c('closeWindow')), actionButton('close', 'Stop the App', class = 'btn-warning'), # sidebar setting sidebarLayout( sidebarPanel( # Input files fileInput(inputId = 'file', label = 'Upload enrichment result'), help('The results of TBtools enrichment can be uploaded directly, results generated from other softwares need to be input in the required format'), # Select the input file type. radioButtons(...), # Term number, how many terms do you want to demonstrate sliderInput(...), # Min color colourpicker::colourInput(...), # Max color colourpicker::colourInput(...), # Graph type, bubble plot or bar plot radioButtons(...), # Project name textInput(...), # output graph size width sliderInput(...), # output graph size height sliderInput(...), p( 'If there is a longer GO term or Pathway name, it is recommended to increase the width of the picture. If the number of terms you select is to large, it is recommended to increase the height of the picture.') ), # demonstration part mainPanel( uiOutput('tb') ) ) ))
server(代码执行)
辣眼睛的来了...如果把ui界面想成参数设置的板块,那么server这块就是用之前的参数,数据进行处理,最终生成结果。学艺不精... 本着能用就行的态度写了这一串又臭又长的reactive... 早上邵阳老铁指出可以精简的方法,哈哈,正在消化。其实也可以看出在写server板块之前先把function写好,确实会让server代码看起来更加清爽一点..
server <- shinyServer(function(input,output){
## set para
DataF <- reactive({
as.character(input$DataF)
})
num <- reactive({
as.numeric(input$num)
})
name <- reactive({
as.character(input$name)
})
colormin <- reactive({
as.character(input$colormin)
})
colormax <- reactive({
as.character(input$colormax)
})
Gtype <- reactive({
as.character(input$Gtype)
})
w <- reactive({
as.numeric(input$w)
})
h <- reactive({
as.numeric(input$h)
})
# import file
data <- reactive({...
})
## show tables
output$finalTable <- renderTable({...
})
p <- reactive({
if(is.null(data())){return()}
if (DataF() == 'TBtools_GO') {
GOBubblePlot(...)
} else if (DataF() == 'TBtools_KEGG') {
KEGGBubblePlot(...)
} else if (DataF() == 'customized') {
customBubblePlot(...)
} else if (DataF() == 'GSEA') {
GSEAplot(...)
}
})
## exhibit plot
output$finalplot <- renderPlot({
p()
})
## download
output$down <- downloadHandler(
filename = function(){
paste0(name(),'Bubble_plot.pdf')
},
content = function(file){
ggsave(file,plot = p(),width = w(),height = h(),dpi = 300)
}
)
## table sets
output$tb <- renderUI({
if(is.null(data()))
h5('Please upload your enrichment result')
else
tabsetPanel(tabPanel(title = 'Table',tableOutput('finalTable')),
# tabPanel(title = 'Information',tableOutput('info')),
tabPanel(title = 'plot',
plotOutput('finalplot'),
downloadButton('down','Download the plot in PDF formate')))
})
## close window
observeEvent(input$close, {
js$closeWindow()
stopApp()
})
})
总结
写shiny的确是一种挑战,我这次小试牛刀就感觉真的麻烦无比,但是这种感觉好像刚开始学习R一样,或许写着写着就熟练了... 先达到能用,再到界面布局优化,再到代码高效,整洁。蹒跚学步,抛砖引玉... 最后期待下各位大神的作品吧!献丑了...