Categories
日常应用

Shiny的架构浅析

不是说学一门语言学的不仅仅是他的语法,更重要的是他背后的思想么?R本身是个大杂烩,ggplot可以单独拎出来作为一门语言学,shiny其实也可以单独拎出来学一番。

只是简单的实现一个shiny app确实不难,就像官网上一进去看到的那个例子那样。基本上如果只是做一些比较简单的可控的dashboard,shiny的代码无非就是写的细致一点,谈不上什么架构之类的。

直到某一天...你发现这东西还可以玩的更深,然后就毅然跳入了下一个大坑——shiny reactivity。官网的开场白很直接

It’s easy to build interactive applications with Shiny, but to get the most out of it, you’ll need to understand the reactive programming model used by Shiny.

然后就介绍三剑客:reactive sources, reactive conductors, and reactive endpoints。

最简单的情况:没有conductor,直接从source到endpoint。

继续拷贝官网图:

嗯,听起来不难...其实大部分情况下shiny app处理的都是这样的情况。基本上就是一个输入(input)和输出(output)的过程。最简单的,就是我们经常在shiny app里面需要处理的input对象,会读入各种用户操作带来的值,然后后面返回一个表格或者图或者文字什么的。回到官网的例子(继续拷贝官网图)

shinyServer(function(input, output) {
  output$distPlot <- renderPlot({
    hist(rnorm(input$obs))
  })
})

大概就是这么一个简单的架构。在这种架构下,自然可以写很多if else之类的在server.R中加入各种各样输入输出的组合,然后在ui.R中排列一下输入的各种框框和输出的各种图标文字什么的。

考虑conductors的情况

shiny用的稍稍熟悉了,就开始想更多的控制这个东西。其实conductor说白了有点中间变量的感觉,就是他本身并不会最终显示出来,但是作为一个中间过程存在着。比如官网一个计算Fibonacci 序列的例子:

fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))

shinyServer(function(input, output) {
  currentFib         <- reactive({ fib(as.numeric(input$n)) })

  output$nthValue    <- renderText({ currentFib() })
  output$nthValueInv <- renderText({ 1 / currentFib() })
})

这里无非就是多了一个新的对象currentFib(跟input和output都无关),暂时的存储了一下计算过程中的变量,然后基于这个东西,又衍生了两个最终显示出来的返回值。新的架构图就多了一个conductor这样。

再复杂一点:考虑Reactive expressions

按照官网的说法,这个东西包括:

  • accessing a database
  • reading data from a file
  • downloading data over the network
  • performing an expensive computation

基本就是从其他的数据源而不是用户的input那里读数据的情况。常见的场景比如,按照用户给定的一些条件(由input传入),返回一个符合条件的数据子集。如果数据全集是会不断更新的(比如每次都应该重新load("xxx.rdata")这样),那么这个这句话就必须写在reactive里面。同理,如果直接利用input生成一句SQL然后连接数据库读取数据,那么也要写在reactive里面。

再复杂一点:自动还是手动更新

shiny默认的情况下,如果没有放submit按钮,那么就是随便一个操作就trigger整个reactive的流程。比如官网首页上,我们拖拖鼠标那个直方图就跟着变化。如果不想让他直接变化,那么简单的可以放一个submit按钮,然后只有submit之后才会变化。

如果希望更好的控制一个app界面上各个部分呢?就要动用更高级一点的actionButton

这东西还要配合一个isolate(),就是你要告诉shiny哪些东西跟着这个button变化,哪些不属于这个button的控制范围。比如官网的例子:

shinyServer(function(input, output) {
  output$distPlot <- renderPlot({
    # Take a dependency on input$goButton
    input$goButton

    # Use isolate() to avoid dependency on input$obs
    dist <- isolate(rnorm(input$obs))
    hist(dist)
  })
})

这里只有goButton被点击的时候,才会执行下面的抽随机数并画图。actionButton是随着每次点击而不断增加计数的,从0开始一直增加过去,所以可以在server.R里面利用这个button的返回值来进行流程控制。

篇外废话:

shiny有希望成长成一个可以替代php这样实现客户端和服务器端大部分交互请求的巨人么?从这点来看,Tableau还是限制太多了。

Categories
日常应用

在R中的第一次尝试

好吧,我承认我是比较懒的,虽然写了一篇长长的统计软件:R,但是我还是没有动手装上R的。

只是刚才看到了Yihui同学的留言,其中一行代码让我倍感好奇:

plot(1, type = "n"); text(1, 1, "\VE", cex = 20, vfont = c("serif", "plain"))

所以就只能老老实实的打开迅雷,下载下来R,安装...

嗯,不错,至少还是个中文版……可是欣慰了没多久,安装完打开R之后,就被彻底的打击了……

这界面也太……简单了吧!

然后那行神奇的代码出现的结果,居然就是……怎一个,囧,字了得!

于是乎,我的第一次R尝试,就是画了如斯一张图片……唉!