R Shiny を使ってWebアプリを作って動くまでやってみる(8) ファイルアップロード画面

本シリーズでは、「心理学の尺度開発」に特化した、GUIベースの統計処理ソフトを作り上げることを目指しています。

本日は、最初のメニュー「データアップロード」部分を作ります。

サンプルデータ

この開発でテストに使うデータを準備します。心理尺度開発に利用する元データは通常アンケートなどを配布して収集します。収集の方法やプロトタイプの作り方については別の専門書籍などを参考にしていただくとして、今回はネット上でスケール開発の元データを公開しているサイトがありますのでそこから取得したサンプルを利用してみます。例えば以下のサイトにアクセスしてください

Open psychology data: Raw data from online personality tests
About this website

いわゆるBig Five等を含めていろいろな生データにアクセスすることができます。この開発では、データ件数も少なめな[3376件」”Answers to the Sexual Compulsivity Scale from Kalichman and Rompa (1995).”を使って開発していきます。ちなみに私はこのスケール”SCS”を使ったことはありませんが、質問項目を見てみると性的なな思考や行動が日常の生活に影響を与える程度(アディクションの程度)を計測する尺度のようです。

上記サイトからSCSをダウンロードしたデータをデスクトップなどわかりやすい場所に保存しておいてください。

前回の残り

前回メニューバーを横型にすることにしました。このとき`dashboardHeader`に特になにも記載していませんでしたが、見栄えを整えるために以下の2行目のように変更します。

画面を見ていただくとタイトル”Psycho Scale Developer”が出ているのがわかります。ここではdisableとtitleWidthのオプションを使っていますが、これを変更してみると違いがわかりますのでやってみてください。

#ui.R
dashboardHeader(title = "Psycho Scale Developer", disable = FALSE, titleWidth = "300px"),
dashboardSidebar(disable = TRUE),

ファイルアップロード画面のプログラム

ファイルのアップロードの方法はいくつかありますが、R/Shinyで最も基本的な`fileInput`を使ってみたいと思います。詳細は以下のブログを見てください。フォルダーからファイルを指定する方法に加えて、ドラッグ&ドロップにも対応しているので使いやすいことから採用しました。

`fileInput`の詳細は以下のマニュアルを参照してください。

ではプログラム部分の当該箇所を掲載します。前回作ったメニューのタブパネルの”Data Upload”部分を以下のプログラムに差し替えます。

#ui.R
tabPanel("Data Upload", value="dataUpload", icon = icon("file-upload"),
    tags$head(tags$style(".progress-bar{background-color:#bcbcbc;}")),
    fluidRow(
        column(width=12, align="center", br(),
            div("Please use 'header' in the first row", style="font-size:16px; color:red; font-weight:bold; margin-bottom: 1em;"),
            fileInput(inputId = "file1", label = tags$p("Choose CSV File", style="font-size:14px; margin-bottom: 0px;"),
                accept = c(".csv"),
                multiple = FALSE,
                width = "50%",
                buttonLabel = "Browse...",
                placeholder = "No file selected"
             )                                    
         )
    ), # end of fluidRow
    fluidRow(
        column(width=10, offset=1, align="center", br(),
             uiOutput("quickLook")
        )
    ) # end of fluidRow
),

一方サーバー側のプログラムは以下のとおりです。

#server.R
shinyServer(function(input, output) {
  
  ########################
  # read CSV file
  ########################
  #read csv file
  inFile = reactive({
    inFile1 = input$file1
    if (is.null(inFile1))
      return(NULL)
    df = read.csv(inFile1$datapath, header=TRUE)
    return(df)
  })
  
  ########################
  # file quick look
  ########################
  observeEvent(inFile(), {
    output$quickLook = renderUI({
      panel(heading = "Data Quick Look",
            solidHeader = TRUE,
            DT::renderDataTable(data.frame(inFile()),
                selection = "none",
                extensions = "Scroller",
                options=list(dom="tpli",
                    scrollY = "45vh",
                    scrollCollapse = FALSE,
                    deferRender = TRUE,
                    scroller = TRUE,
                    autoWidth = FALSE,
                    scrollX = TRUE
                )
            )
      )
    })
  })
})

また、グローバルファイルは以下のようなライブラリーを設定します。

#global.R
library(shiny)
library(shinydashboard)
library(DT)
library(shinyWidgets)

上記のプログラムを動かすと以下のような画面が出てくればOKです。

プログラムの補足説明

ui.R

プログレスバー

3行目のプログレスバーですが、この行をコメントアウトすると標準(青系)のプログレスバーになります。ここでは色をモノトーン系(background-color:#bcbcbc;)に変更するコードとなっています。以下を参照にしました。

なお、プログレスバー自体を非表示とする場合は以下のように入れ替えていただければOKです。

tags$style(“.shiny-file-input-progress {display: none}”)

fileInput

4−15行目がファイルの読み込み画面のプログラムになります。uploadされたファイルが`file1`という変数として格納されます。

クイックルック

16−20行目は読み込んだデータを一時的に表示して内容を確認できるようにするための部分です。`quickLook`の変数を`server.R`側で作って表示しています。

server.R

csvファイルの読み込みコントロール

8−14行目がCSVファイルを読み込む部分です。実質的には9行目`inFile1 = input$file1`と12行目`df = read.csv(inFile1$datapath, header=TRUE)`が実質的な読み込み部分となっています。

なお、8行目の`reactive()`はShinyの重要な機能の一つのリアクティブ関数を使っています。`Shiny`の特徴として、変数の数値に変更があると、それに関連する要素が”すべて”再計算される仕組みなので、スライダーを動かしたりするだけで再計算できるという便利な機能を提供してくれるのですが、逆にこれが悪さして、再計算してほしくなくてもいちいち関連する部分をすべて再計算して動きが重たくなる要因になります。これはチョット計算負荷が重いプログラムを組んだときは結構致命傷になります。そこで、登場するのがリアクティブ関数です。reactive()で設定した変数、この場合は`inFile`という変数はこの変数の中身に変化がない限り再計算を行わなくなります。よって、8−14行目のデータファイルの読み込みが無駄に再計算されるのを防げます。

ただし、リアクティブ関数で設定した変数を利用する場合には変数の後を`()`で囲む必要がある点に留意してください。具体的には、このケースでは、19行目で使っていますが`inFile()`としているところです。こうしないと正しくリアクティブ関数として機能しません。

9行目: `ui.R`側でのCSVファイルの読み込みコンポーネントのinputIdは`file1`として引き渡しますが、server.R側でこのinputIdの取り出しには`input$file1`で取り出します。これを一旦`inFile1`という変数に格納します。

12行目: fileInputで読み込まれたデータの中身は`datapath`として格納されますので、`df = read.csv(inFile1$datapath, header=TRUE)`の部分は一種おまじないの様に使っても良いかと思います。なお、読み込むCSVファイルの1行目がヘッダーの場合には`header=TRUE`とします。ヘッダーがない場合は、`header=FALSE`です。この部分をチェックボックスで”あり・なし”を選択させるようにしても面白いと思います。このあたりの修正の方法は検索すると見つかると思います。

Quick Look

19-37行目は読み込んだデータを確認するためのクイックルックのプログラムです。19行目は、CSVファイルを読み込んで`inFile`ができあがった後に`observeEvent(…)`部分が実行されるためのコードです。この部分を説明するためには相応のボリュームが必要なので別途説明したいと思いますがここではごく簡単に説明します。この関数は`observeEvent(変数A,・・・)`のように使い、変数Aに変更がないかどうかを常に観察(observe)しています。このケースでは`inFile()` [=8行目で設定したリアクティブ変数でカッコをつけて使います]を観察して、内容に変化があった時だけ`observeEvent`の中身を実行するという命令です。すなわちファイルの読み込みがあった時だけ実行されることになります。

このプログラムを実行してみると最初はデータを表示する表(quickLook)が全く表示されていませんが、読み込んだ後に表が表示されるのはこの部分で制御(ファイルを読み込んだ時だけ表示する)されているからです。`observeEvent`関数はShinyではよく使うのでまた次の機会にさらに説明します。

23-34行目の`DT::dataTable`はテーブルを美しく表示するDTパッケージの機能です。このあたりの詳細は別途説明しますが、検索すると色々説明やコードの例が見つかると思います。

ちなみに27行目の`scrollY = “45vh”`は画面の縦の大きさに対して45%分のスクロール高さを確保しますので概ね画面の下半分がデータの表示領域になります。このあたりはお好みで変更してください。

実際にサンプルデータを読み込むと以下のようにデータが表示されるので正しく読み込まれているかどうか確認可能となります。

scrollCollapseのバグ

データの行数が少ない場合、空白行が表示されて見栄えが悪いので、そういった場合の縦幅を自動的に縮めて表示するオプションも用意されています。その場合は`scrollCollapse = TRUE`とすれば良いのですが、これはバグが存在しており画面が乱れますので、この場合の対処については以下のブログを参照してください。

まとめ

今回はファイルのアップロード関連のプログラムを作りました。細かく説明する時間がなかったのでざっくりした解説になりました多が今後解説を追加していきたいと思います。

プログラム

ここまでのプログラムをまとめてアップしておきます