Shiny应用基础(4):数据与图像输出

前面3节的内容都没有涉及数据处理,恐怕有人等不及了。R语言的强项是统计和图形处理,拿它去做网页设计是不是舍本逐末?确实,我觉得也是这样。如果不是为其他客户专门开发的应用,我们不应把时间浪费在外观设计上。多关心数据,多考虑我们需要获得的结果。

本文先介绍shiny的输出方法,后面再介绍数据输入和响应。这里说的“输出”是动态的输出或必需使用R函数处理才能够获得的输出。完全静态的文字或图像的输出应把它归到前面的UI设计部分。

1 Shiny输出语法

先看下面例子:

shinyApp(
    ui = fixedPage(
        textOutput('tx', container=h1),
        plotOutput('pl', width='100%', height='400px')
    ),
    server = function(input, output, session) {
        output$tx <- renderText({
            "这是服务器输出的文字"
        })
        output$pl <- renderPlot({
            a <- rnorm(20)
            par(mar=c(3, 3, 0.5, 0.5), mgp=c(2, 0.5, 0))
            plot(a)
        })
    }
)


保存和运行该程序后产生的结果如下:

Shiny应用基础(4):数据与图像输出_第1张图片


可以看出,Shiny的动态输出需要ui和server两部分的共同完成:

  • server向ui提供输出内容,ui部分一般使用xxxOutput类型函数产生的容器显示输出
  • 服务器对数据的处理结果通过server中的output列表参数向ui传递
  • server使用不同的函数捕获不同类型的输出数据,如文字用renderText、屏幕图像用renderPlot

2 output列表

shinyApp的server参数需要设置为一个“server函数”,这个函数最关键的三个参数是input、output和session,使用过程中它们似乎是顺序可调但名称不可变。探讨源代码过于复杂,这里只说一下和实际应用有关的一些内容。

还是上面的shiny程序,运行后查看页面的HTML代码会发现body中有下面两行内容:


第一行h1容器是textOutput函数产生的,而第二行的div容器是plotOutput产生的。只看见ui中设置的内容,而server提供(通过jQuery)的内容在代码上完全不可见。对于这两个容器(h1和div),应该注意的是它们的id和class属性。

  • id属性与server中output列表项的名称/ID对应,即:如果ui中的某容器id是xx,server中相应部分是output$xx
  • class属性与server中的render结果呈现类型对应,如shiny-text-output类容器对应renderText函数结果

如果把shinyApp中的内容替换为下面内容,程序完全可以正常运转,但是去掉id或class中的任何一项都不会得到想要的结果:

HTML('

"tx" class="shiny-text-output">

'
), HTML('
"pl" class="shiny-plot-output" style="width: 100% ; height: 400px">
'
)

这其实是改变shiny输出结果外观的方法。但是请务必清楚:shiny提供的某些UI函数隐式加载了某些组件,有时候仅仅修改HTML容器是不够的。一般来说,需要额外交互功能的输出都要加载额外的jQuery组件,比如下面的dataTableOutput函数。

3 xxxOutput和renderXXX函数

当前版本的shiny有8个xxxOutput函数和7个renderXXX函数:

packageVersion("shiny")
## [1] '0.12.1'
library('shiny')
ls("package:shiny", pattern="Output$")
## [1] "dataTableOutput"    "htmlOutput"         "imageOutput"       
## [4] "plotOutput"         "tableOutput"        "textOutput"        
## [7] "uiOutput"           "verbatimTextOutput"
ls("package:shiny", pattern="^render")
## [1] "renderDataTable" "renderImage"     "renderPlot"      "renderPrint"    
## [5] "renderTable"     "renderText"      "renderUI"


它们的对应关系基本上可以从名称可以看出,只有2个例外:htmlOutput可使用renderUI输出,而verbatimTextOutput可使用renderPrint输出。但名称并不说明什么,它们的对应关系不是绝对的。

UI函数的设置相对简单,所有xxxOutput函数都有而且必需的参数是outputId,一些函数没有其他可选参数。

renderXXX函数的一般形式是:

renderXXX(expr, ...)


它们唯一必需的关键参数是 expr,即表达式。除非预先产生一个变量,否则expr不会只有一个语句,所以实际看到的使用形式就是:

renderXXX({})

花括号内以及它所围的全部内容就是expr参数的内容。


3.1 文本输出

3.1.1 server端函数

在server端有3个函数renderText、renderPrint和renderUI。

renderText和renderPrint为纯文本输出,返回的都是一个字符串,但差别是:

  • renderText只把函数内最后一个表达式的值转成字符串进行输出,如果不能转就出错
  • renderPrint把函数内所有语句的输出捕获成一个字符串返回,

renderUI返回的是HTML格式的文本,它把最后一个表达式的值转为tags字符串列表输出。renderUI还有另外一项功能:引入tags可能依赖的其他组件如js/jQuery代码文件,不过似乎还没有功能实现。不管怎样,renderUI的作用还是很强的,除了直接输出文字外还可动态设置UI中的其他内容,比如:

output$tx1 <- renderUI({
    list(
        tags$style(".container {width: 400px;}"),
        tags$script(src="loading.js"),
        tags$title("新标题")
    )
})

上面代码改变了页面主显示区的宽度、加载了一个动画脚本、把窗口标题改为“新标题”。


3.1.2 ui端函数

在ui端有4个函数:htmlOutput、uiOutput、textOutput和verbatimTextOutput、server端函数源代码较复杂,这里我们只看看ui端函数的代码:

textOutput <- function(outputId, container = if (inline) span else div, inline = FALSE) {
  container(id = outputId, class = "shiny-text-output")
}
verbatimTextOutput <- function(outputId) {
  textOutput(outputId, container = pre)
}
htmlOutput <- function(outputId, inline = FALSE,
  container = if (inline) span else div, ...)
{
  if (anyUnnamed(list(...))) {
    warning("Unnamed elements in ... will be replaced with dynamic UI.")
  }
  container(id = outputId, class="shiny-html-output", ...)
}
uiOutput <- htmlOutput


很显然,这几个函数除了设置HTML容器和属性外没有其他功能,而且重复较多:

  • htmlOutput和uiOutput事实上也是一样的,只需要一个
  • verbatimTextOutput只是容器类型为pre的textOutput,嫌名字长臭就不要记它了
  • textOutput也是多余的,完全可以用htmlOutput代替

3.2 表格输出

server端有两个函数:renderTable和 renderDataTable,ui端对应有两个函数tableOutput和dataTableOutput。

renderTable通过print和xtable函数将函数内最后一个表达式的值转成html表格,它的返回值其实就是html代码,所以在ui端可以用html/uiOutput捕获,也可以用专用函数tableOutput输出。tableOutput其实就是container设置为div的htmlOutput:

tableOutput <- function(outputId) {
    div(id = outputId, class="shiny-html-output")
}


renderDataTable比renderTable复杂得多,它产生的是jQuery dataTable类型的表格框架和JSON数据。ui端的renderDataTable仅复杂产生外部容器和引入jquery.dataTable组件。

dataTableOutput <- function(outputId) {
  attachDependencies(
    div(id = outputId, class="shiny-datatable-output"),
    dataTableDependency
  )
}


dataTable是功能很强大的表格组件,可实现数据库的简单查询、筛选和排序等功能,值得使用。为减少不必要的麻烦,最好将renderDataTable和dataTableOutput成对使用。


3.3 图像输出

这部分只介绍函数用法。

在server端有两个函数:renderImage和renderPlot。先看前者:

renderImage(expr, env = parent.frame(), quoted = FALSE, deleteFile = TRUE)


其实我们只需要理解第一个参数expr的用法即可。shiny的函数说明中只提到该参数是“返回一个列表的表达式”,至于这是一个什么样的列表、需要什么内容,只能从它附带的例子中参详。这其实是用于设置图像标签(img)属性的一个有名列表,列表元素的名称和img标签的属性名称一一对应,比如src、width、height,alt,class,style等。其中src是必需的,用于设置图片文件,其他属性可选。

src的值怎么得到呢?因为它引用的是一个图像文件,所以我们在expr前面的代码中得产生一个。比如我们前面例子server中的绘图部分可以改成这样:

output$pl <- renderImage({
    imagefile <- 'tmpfile.png'  ## 设置文件名
    png(imagefile)
    par(mar=c(3, 3, 0.5, 0.5), mgp=c(2, 0.5, 0))
    plot(rnorm(20))
    dev.off()
    list(src=imagefile)  ## 这是expr的返回值
})


OK,就这么简单。deleteFile参数为TRUE表示图像文件给客户端发送后就从服务器端删除。

renderPlot呢?其实就是对renderImage的简化和调用,图像文件自动设置,你不用自己设临时文件,不用dev.off关闭文件设备,也不用设置返回值列表。

ui端函数嘛,也有两个:plotOutput和imageOutput,其实它们也是通用的,因为server端的两个函数本质是一样的。

imageOutput(outputId, width = "100%", height = "400px", click = NULL,
            dblclick = NULL, hover = NULL, hoverDelay = NULL,
            hoverDelayType = NULL, brush = NULL, clickId = NULL, hoverId = NULL,
            inline = FALSE)
plotOutput(outputId, width = "100%", height = "400px", click = NULL,
           dblclick = NULL, hover = NULL, hoverDelay = NULL,
           hoverDelayType = NULL, brush = NULL, clickId = NULL, hoverId = NULL,
           inline = FALSE)


前三个参数都很明白,其他的click、dblclick和hover等参数后面介绍图形交互的时候再说吧。




作者: ZGUANG@LZU

Created: 2015-08-22 六 20:13

Emacs 24.4.1 (Org mode 8.2.10)

你可能感兴趣的:(R,Shiny)