R
Shiny

Shinyで同じ階層構造のUIを連続して生成する

※20180205 データフレームの取り扱いを変更
以下のようなイメージで同じ要素グループを連続して並べる時のコードです。それぞれのカタマリで個別のnumericInputを参照し、それぞれヒストグラムを描画しているデモです。
スクリーンショット 2017-08-04 14.28.16.png

app.R
library(shiny)
# UI description
rep <- 5
ui <- fluidPage(
  lapply(1:rep, function(i) {
    div(
      numericInput(paste0('n', i), paste0('num=', i), i ^ 2 * 100),
      plotOutput(paste0('p', i))
    )
  })
)
# Server logic
server <- function(input, output, session) {
  dflist <- list()
  lapply(1:rep, function(i) {
    output[[paste0('p', i)]] <- renderPlot({
      dflist[[i]] <- rnorm(input[[paste0('n', i)]])
      hist(dflist[[i]])
    })
  })
}
shinyApp(ui, server)

ポイント

for/whileは使用しないほうが良い

描画とreactivityの演算はループで指定した順番とは別で行われるため(データがステートレスにならなくてはいけない)、逐次を想定した記述をしてもうまくいきません。ステートレスということはlapplyでやりきれる、ということですので頑張ってlapplyで記述しましょう。生成数を動的に変更するためにinsertUIを使用した例はこちらで記述しています。

繰り返しに複数要素を入れ込むときはdivる

複数繰り返しはdiv()でくくりましょう。仮想的にクラス化されるshinydashboard::box みたいな関数でも同じことが実現できます。

div(
numericInput(paste0('n', i), paste0('num=', i), i ^ 2 * 100),
plotOutput(paste0('p', i))
)

異なるデータセットを使用する場合は親リスト以下にデータフレームを格納する

dflist <- list()
まずデータフレームを格納するリストを作成し
dflist[[i]] <- rnorm(input[[paste0('n', i)]])
その後それぞれのデータフレームをその下に格納していきます。ちなみにreactive contextに触れないようにisolateでくくるか、例のようにcontext外になるようplotの中に入れてしまうかする必要があります。