Shiny で動的に図の数を変更する

by
カテゴリ:
タグ:

Shiny において動的にUIを生成するには,サーバーサイドで renderUI して作った UI を,UIサイドで outputUI すれば良い.

この時,renderUI の中で作ることのできる UI は一つだけではなく list あるいは htmltools::tagList で束ねた複数の UI でもよい.

この性質を利用すると,以下のように選択した変数の数に応じてプロットの数を変更できる.

複数の図を作製するには,一つずつ異なる名前を与えて output に格納する必要がある.この操作を observe の中で for ループを回しながら local 環境で行うことがポイントだ.更に local の中では for を回すためのループ変数を別の変数にコピーしてやる必要がある. (https://gist.github.com/wch/5436415/).local や変数のコピーを行わなかった場合,同じプロットが大量に生成されてしまった.

ちなみに,各プロットを display: inline-blockdiv で囲んでおくと,ページ幅に合わせて図を並べてくれる.

library(shiny)
library(ggplot2)

ui <- pageWithSidebar(
  tags$h1("Dynamic multiple plots"),
  sidebarPanel(
    # shiny::selectInput より複数選択がしやすい
    shinyWidgets::pickerInput("x", "x", names(iris)),
    shinyWidgets::pickerInput("y", "y", names(iris), multiple = TRUE)
  ),
  mainPanel(uiOutput("plots"))
)

server <- function(input, output, session) {
  
  observe( # input$x, input$y が変更される度に処理を実行する
    for(.y in input$y) {
      local({ # local 環境にしておく必要あり
        y <- .y # .y を新しい変数にコピーしておく
        output[[y]] <- renderPlot( # 図はそれぞれ固有の ID を持つ
          qplot(!!sym(input$x), !!sym(y), data = iris)
        )
      }
    )}
  )
  
  output$plots <- renderUI(
    lapply(
      lapply(input$y, plotOutput, height = "200", width = "200"),
      div,
      style = htmltools::css(display = "inline-block")
    )
  )
}

runGadget(ui, server)

Enjoy!