これで無理なら諦めて!世界一やさしいデータ分析教室

オーブンソースデータなどWeb上から入手できるデータを用いて、RとPython両方使って分析した結果を書いていきます

【R Shiny】Drag & Dropでアップロードしたファイルを元にプロットしてみる

本記事は、Shiny Advent Calendar 2017の9日目の記事です。



Shiny100本ノックの第12弾です。

前回は、Drag&DropでShiny上にデータを読み込む機能を作ってみました。
www.randpy.tokyo

今回はその続編!ということで、読み込んだデータを元にプロットをしてみます。

とりあえず手元のデータをササッと可視化してみたい、という時に割と便利なんじゃないかなと思います。

なお、コードはいつも通りGithubにも置いてありますので、そちらも是非。
github.com

完成形のイメージ

今回作るShinyアプリケーションですが、以下のような画面となっています。
csvファイルをドラッグ&ドロップ後
f:id:Np-Ur:20171205230610p:plain
「プロット」ボタンクリック後
f:id:Np-Ur:20171205230617p:plain

まず前回やったように、赤枠部分にデータをドラッグ&ドロップします。
すると、プロットする際にx軸とy軸にどのデータを用いるのか選択できるようになります。

選択後、「プロット」ボタンをクリックすると、散布図プロットされます。

今回作成するShinyアプリケーションでは、可視化できるのは一番シンプルな散布図プロットのみですが、ここも改良すると更に便利なツールになりそうですね。

ソースコード解説

JavasrciptファイルとCSSファイルは前回と同様なので割愛します。

ui.Rのソースコード

まず、ui.Rから紹介します。

library(shiny)

shinyUI(
  fluidPage(
    tags$head(tags$link(rel = "stylesheet", href = "styles.css", type = "text/css"),
              tags$script(src = "getdata3.js")),
    sidebarLayout(
      sidebarPanel(
        h2("赤枠にデータをドロップ"),
        div(id="drop-area", ondragover = "f1(event)", ondrop = "f2(event)"),
        h2("プロットするデータを選択"),
        htmlOutput("colname1"),
        htmlOutput("colname2"),
        actionButton("submit", "プロット")
      ),
      mainPanel(
        tabsetPanel(type = "tabs",
                    tabPanel("Table", tableOutput('table')),
                    tabPanel("Plot", plotOutput("plot"))
        )
      )
    )
  )
)

今回追加したところは、まずsiderbarLayoutの中の、

h2("プロットするデータを選択"),
htmlOutput("colname1"),
htmlOutput("colname2"),
actionButton("submit", "プロット")

こちら。

colname1とcolname2についてはserver.Rから送られてくるもので、x軸とy軸に適用するデータの列名を選択するための「selectInput」メソッドを表示しています。(server.Rを説明する際に、もう少し追記します。)

そしてsubmitボタンを設置し、ボタンがクリックされた時点でserver.R側に上で選択した列情報を送るようになっています。

続いて、以下の部分について簡単に説明します。

mainPanel(
  tabsetPanel(type = "tabs",
     tabPanel("Table", tableOutput('table')),
     tabPanel("Plot", plotOutput("plot"))
   )
)

〈データをテーブル表示〉と〈プロット表示〉という、2つの表示形式があるので、tabsetPanelメソッドを使って簡潔なUIになるようにしてみました。

tabsePanelについては以下にまとめてあるので、ご覧ください。
www.randpy.tokyo


server.Rのソースコード

次に、server.Rファイルを紹介します。

server = function(input, output, session) {
  observeEvent(input$mydata, {

    name = names(input$mydata)
    csv_file = reactive(read.csv(text=input$mydata[[name]]))
    output$table = renderTable(csv_file())
  
    output$colname1 = renderUI({ 
      selectInput("x", "x軸方向", colnames(csv_file()))
    })
    output$colname2 = renderUI({ 
      selectInput("y", "y軸方向", colnames(csv_file()))
    })
  })
  
  observeEvent(input$submit, {
    name = names(input$mydata)
    csv_file = reactive(read.csv(text=input$mydata[[name]]))
    
    x = csv_file()[input$x]
    y = csv_file()[input$y]
    
    output$plot = renderPlot({
      plot(x[,1], y[,1])
    })
  })
}

前回から追加したところでは、データをドラッグ&ドロップされた際に以下の処理を実行するようになっています。

output$colname1 = renderUI({ 
  selectInput("x", "x軸方向", colnames(csv_file()))
})
output$colname2 = renderUI({ 
  selectInput("y", "y軸方向", colnames(csv_file()))
})

先ほどui.Rで出てきたcolname1とcolname2はこちらで定義しています。

読み込んだcsvファイルの列名をcolnamesで読み取り、selectInputメソッドの選択肢として設定しています。
そして、その内容をrenderUIでui.R側に返しています。

このようにserver.Rで書いておくことで、ui.R側の「htmlOutput("colname1")」部分で「selectInput("x", "x軸方向", colnames(csv_file()))」がレンダリングされるようになります。

続いて、プロット部分。

  observeEvent(input$submit, {
    name = names(input$mydata)
    csv_file = reactive(read.csv(text=input$mydata[[name]]))
    
    x = csv_file()[input$x]
    y = csv_file()[input$y]
    
    output$plot = renderPlot({
      plot(x[,1], y[,1])
    })
  })

submitという変数を持つアクションボタンがクリックされたときにこちらの処理が走ります。

ui.RのselectInput部分で選択された、x軸の変数(input$x)とy軸の変数(input$y)を元に、プロットをしています。
この辺りはよくある書き方なので問題ないかなと思います。

ちょっとつまったところ

ドラッグ&ドロップで読み込んだ情報を元に、selectInputで列名選択を行う必要があります。
そのためui.R側で事前にselectInputの選択肢を定義するのではなく、server.R側で柔軟に定義する必要があります。

この辺りがどうすればよいのだろう…?と少し悩みました。

server.R側のrenderUI({})においてui.Rで表示したい内容を書き、それをui.R側のhtmlOutput()で受け取ればいいんですね!
Shiny便利!!

まとめ

今回は、前回行ったドラッグ&ドロップでのデータ読み込みアプリケーションを、もう少し発展させてみました。

ここに更にプロットの種類(折れ線・円グラフ・面グラフなど)まで選択できるようになると、相当使い勝手の良いツールに変身しそうです。

また追加で機能を作成しましたら、また本ブログで紹介しようと思います。
お楽しみに!!