Shiny: R语言来建立开源交互式数据分析微服务的神器
先来说个应用场景: 假设你需要快速Prototype一个数据分析的服务, 而且需要给业务客户一定的自由度来理解数据分析的强大, 例如更换数据, 更换分析手段。 但是你就只有几个小时的时间去准备。 你该怎么办? Shiny就是你要的那个神器!
题目有点长, 需要稍微解释一下。
一, 交互式微服务
首先, 服务!
Web Service 这个概念比较成功了, 在SOAP服务之后, Restful服务的普及, 使得数据库的CRUD操作通过网络无限延伸。 普遍的好处是:
1) 你不需要安装任何程序, 携带数据, 只要有网的地方, 你就能演示。(远程)
2) 只要安装一次, 可以多人使用, 可以让别人试用。(多用户)
3) 不用担心软件或者代码泄密, 可以控制用户使用时间和权限。 (服务)
其次,微服务!!
Microservice的概念和大部分微概念提出一样, 例如微机, 微内核等等, 就是要脱离庞大的依赖而单独运行。 服务框架,也就是Service Oriented Architecture (SOA) 正在变得越来越复杂。 微服务就是解耦合这种复杂性的时髦概念。 简单来说就是要独立任务独立项目独立运行。
再次,交互式微服务!!!
其实这里借用了概念, 交互式主要是针对交互式数据分析, 而交互式数据分析和传统的静态数据分析的最重要的差异就是,是不是把"模型选择和评估的快速迭代"作为内嵌的实时的需求。
当然了, 这种实时的操作的反馈,在服务框架下, 必然要求前后台有个交互设计。
R VS Java VS Python
在如上的需求下, Java用户需要怎么设计前后台? 利用Spring-Servlet框架, 后台可以利用WEKA,SPARK, 前台用AJAX和D3.js?一听就工程量不小。 更不要谈部署了Tomcat/Resin,还是Maven的Jetty插件吧。 总之几天才能搞定的样子。 要不Python吧, 现有的Flask/CherryPy, 以后很容易升级Pyramid/Django等, 后台Scikit-Learn, 前台呢?可以用Senborn的图片或者MPLD3等等, 看上去至少也是1整天的活,还得很熟悉相关框架。 Shiny就是这么强大的登场了, 对的几个小时!!!
二, Shiny如何做到的?
Shiny之Hello World
Shiny就可以作为专一的微服务框架, 很容易实现如下的架构。
运行上面这样的服务, Shiny只要两行代码。
library(shiny)
runApp("my_app_name")
或者你可以先试试hello-world。也是类似的两行代码。
library(shiny)
runExample("01_hello")
立马你可以得到一个可以通过浏览器使用的Shiny的应用APP。 可以利用Histogram来展示观察值得分布。 并且这是一个可以交互的App, 你可以调整观察总数来变换得到一个新的图。
还有其他很多例子可以参考。可以通过如下命令来找到对应的App名字和源码。
system.file("examples",package="shiny")
Shiny之实现
那么Shiny是如何实现这种强大的功能的呢?
Shiny本身是基于R和Node.js的一个应用服务,有了强大的javascript异步后台, 那么如何把Shiny解析后交由R解释器运行后台数据分析和可视化生成, 交由node.js运行前台的展示和交互。
Node.js 本身是对Chrome的V8引擎的封装的异步事件引擎。
Shiny之代码结构
根据前面对Shiny的后台实现的分析, Shiny代码可以分成两部分, 一部分主要由R解释器去执行的server.R, 另外一部分是主要由node.js去执行的ui.R。 这样通过Socket建立一个独立的端口服务, 一个微服务系统就构建完成了。
除了上面的双文件结构, 我们也可以直接定义一个shinyApp, 单一文件结构:
shinyApp(ui=fluidPage(sidebarLayout(sidebarPanel(sliderInput("bins","Number of bins:",min=1,max=75,value=30)),mainPanel(plotOutput("distPlot")))),server=function(input,output,session){output$distPlot<-renderPlot({hist(faithful$eruptions,breaks=input$bins)})})
更多例子参考: http://zevross.com/blog/2016/04/19/r-powered-web-applications-with-shiny-a-tutorial-and-cheat-sheet-with-40-example-apps/
三, 如何才能速度的完成Shiny服务部署?
Shiny安装
1. 直接通过包管理器安装
install.packages("shiny")
2. 利用devtools通过github安装
if(!require("devtools")) install.packages("devtools")devtools::install_github("rstudio/shiny")
这里要注意, 如果需要利用代理
library(httr)set_config(use_proxy(url="18.91.12.23",port=8080,username="user",password="password"))
或者要设定专门的版本号
devtools::install_version("shiny",version="0.10.2.2")
Shiny 代码结构和放置
前面提到Shiny主要是两个文件ui.R和server.R。 这两个文件都是典型的Domain Specific Lanugage(DSL)。 通常DSL都是关键词引领的模块化语言, 可以参考Groovy的DSL设计。 这样的好处是解释器实现简单高效。
用户接口user interface设计主要是利用declarative语言来描述页面如何布局, 有哪些图标,哪些控件。
# ui.R
library(shiny)
shinyUI(fluidPage(
))
# server.R
library(shiny)
shinyServer(function(input,output){
})
而服务器端server.R主要是一个接口函数的实现。 简单来说就是输入是什么, 我希望运行的输出是什么。
ui 和 server直接是怎么来进行通信的呢? 主要是通过input 和 output NameSpace来进行通信的。譬如我们在ui.R中定义一个输入控件,命名为"bins", 那么在server.R中就通过"input$bins"来获取这个控件的值。
# ui.R
sliderInput("bins","Number of bins:",min=1,max=50,value=30)
# server.Rbins<-seq(min(x),max(x),length.out=input$bins+1)
而同样, 在server.R中想返回的对象, 也放到output Namespace下面。 然后再ui.R中直接通过名称获取。 例如,在server.R中定义了一个renderPlot的绘图, 放到“output$distPlot”。 然后再ui.R中直接获取“distPlot”进行展示。
# server.R
output$distPlot<-renderPlot({x<-faithful[,2]bins<-seq(min(x),max(x),length.out=input$bins+1)hist(x,breaks=bins,col='darkgray',border='white')})
# ui.R
plotOutput("distPlot")
所以我们只要选好匹配的输入输出的Shiny控件, 例如输入的“sliderInput”, 返回的对绘图进行封装的“renderPlot”, 和展示的“plotOutput”。
部署的话, 把server.R和ui.R放到同一个目录下面, 譬如"App-1", 然后运行runApp来运行就可以了。
> setwd("Project_HOME")
>
library(shiny)
>runApp("App-1")
如果是在RStudio环境下面,RStudio会自动识别runApp命令。当你打开ui.R的时候, 直接点击Run App按钮就好了。 非常方便。
甚至, 可以直接从github上直接运行, 只要指定用户, Repository,和文件夹就可以定位一个App对应的文件夹了。
runGitHub(“ShinyAppsForR”, “msiddiqi”, subdir=”upperCaseText”)
因此某种意义上来说github是一种非常好的管理shiny app的工具。
四, Shiny常用技巧
布局(Layout)
在布局中需要考虑Panel是如何放置的, 主要有titlePanel, sidebarPanel, mainPanel。 还有其他Panel,例如tabsetPanel,navlistPanel,navbarPanel
在考虑Panel的时候, 我们可以利用Layout布局器,譬如sidebarLayout(),或者更复杂的网格布局器fluidRow()
shinyUI(fluidPage(titlePanel("title panel"),
sidebarLayout(sidebarPanel("sidebar panel",
selectInput('element_id',label='Select one option',choices=LETTERS[1:10]),
textInput('title_text_box_id',label='Enter a title for the plot')),
mainPanel("main panel",h1('The title of some text'),p('And here is some content that is put into the first paragraph'),p(textOutput('dynamicText')),
plotOutput
('dynamicPlot')))
))
更多的布局可以参考:http://shiny.rstudio.com/articles/layout-guide.html
构件(Widget)
各种输入只能通过Shiny定义好的丰富的widget来实现。
各种细节就不细说了, 更为高级的部分可以到“画廊 Gallery”里面去找
http://shiny.rstudio.com/gallery/
主题(Theme)
当然我们可以指定一些主题,通过自定义css。
shinyUI(fluidPage(theme="bootstrap.css",titlePanel("My Application")# application UI))
另外, “shinythemes” 包提供更多更好用的主题。 安装以后,很方便使用。
install.packages("shinythemes")
不是直接指定一个css文件, 而是通过名字来指定主题。 本人比较喜欢simplex
(http://bootswatch.com/simplex/)
## ui.R ##
library(shinythemes)
fluidPage(theme =shinytheme("simplex"), ...)
优化(Optimization)
Shiny提供一些缓存优化的机制, 其中最有效的是Reactive Expression 反冲表达式。
Reactive机制使得交互渲染的效率大幅度提高。 举个例子, server.R返回一个数据绘图, 但是每次运行需要重新读取数据。 但是当如果输入没有变化的时候, 这种重复的数据读取会极大的延迟反应, 浪费资源。
# server.R
library(ggplot2)
shinyServer(function(input,output){output$dynamicPlot<-renderPlot({dat=get_data(input$ui_element1,input$ui_element2)plot(dat,input$ui_element3)})})
基于这种考虑, 我们使用reactive关键词重写了数据读取模块。reactive像一个带cache的模块, 并且当reactive检查到数据输入没有任何变化, 那么输出也不会有变化的时候, reactive就会使用缓存的数据,避免了重复运算, 加速了反应。
# server.R # use reactive
library
(ggplot2)
shinyServer(function(input,output){input_data=reactive({dat=get_data(input$ui_element1,input$ui_element2)})output$dynamicPlot<-renderPlot({plot(input_data(),input$ui_element3)})})
更深入的reactive理解, 可以参考 http://shiny.rstudio.com/articles/reactivity-overview.html。
还有一些其他的优化技巧包括: streamline computation, warm up, dependency 技巧可以参考 http://shiny.rstudio.com/tutorial/lesson6/
交互管理(Session)
在早期的Shiny交互中, 如果服务器和浏览器有中断, 那么所有的前期交互全部丢失了。 在最新的版本中, Shiny加入了session和重连的机制,只要在server.R的函数中加入 “session$allowReconnect(TRUE)”, 很简单吧。
# server.R # use session
library
(ggplot2)
shinyServer(function(input,output){input_data=reactive({dat=get_data(input$ui_element1,input$ui_element2)})output$dynamicPlot<-renderPlot({plot(input_data(),input$ui_element3)})
session$allowReconnect(TRUE)
})
更多细节参考:http://shiny.rstudio.com/articles/reconnecting.html
扩展(Extending)
有一部分重要的扩展,主要是画图和交互的,譬如rCharts, DataTables, 会让Shiny的交互性变得非常强大。
shinydashboard – Shiny powered dashboards
shinyURL – Facilities for saving and restoring user input values by encoding them in the app’s URL query string
htmlwidgets – A framework for embedding JavaScript visualizations into R. Ready to use examples include:
leaflet – Geo-spatial mapping
dygraphs – Time series charting
MetricsGraphics – Scatterplots and line charts with D3
networkD3 – Graph data visualization with D3
DataTables – Tabular data display
threejs – 3D scatterplots and globes
rCharts – Multiple JavaScript charting libraries
五, Shiny 服务器
假如你有创建了很多的Shiny服务, 那么一个专门的Shiny服务器会方便部署。
一般可以有两种部署, 一种是自己搭建一个Shiny Server。 这样就可以同时使用很多的Shiny 应用了。
另外一种是直接部署到Shiny云服务上去, 譬如“www.shinyapps.io” 注册一个云账号, 然后把服务部署到云上去。在注册完成后, 你只要遵照详细的链接, 授权, 和部署的步骤,就可以把本地Shiny App上传部署。
综上, Shiny提供一个迅速原型化交互数据分析的工具。 用好Shiny会使得你的数据分析的创意和设计备受关注,更快迭代。如果想更深入理解,请阅读Shiny文章 http://shiny.rstudio.com/articles/。
参考:
http://shiny.rstudio.com/tutorial/
http://shiny.rstudio.com/tutorial/lesson1/
http://docs.rstudio.com/shiny-server/
https://github.com/rstudio/shiny-server
http://shiny.rstudio.com/articles/
https://github.com/jcheng5/user2016-tutorial-shiny
https://www.shinyapps.io
https://rstudio.github.io/shinythemes/
http://shiny.rstudio.com/gallery/
http://deanattali.com/blog/building-shiny-apps-tutorial/
http://stcorp.nl/R_course/tutorial_shiny.html
https://www.bioconductor.org/help/course-materials/2015/CSAMA2015/lab/shiny.html