LoginSignup
1
0

More than 5 years have passed since last update.

shinyでJOINする

Last updated at Posted at 2018-08-28

RでWEBアプリが簡単に作れるshinyですが、
具体的にどう作るのかよく分かってなかったので、
色々参考に書いて試してつまずいてr-wakalangで教えていただいたことをまとめます。

やりたいこと

dplryの各種JOIN関数を、shinyアプリ内で実行したい。

各種JOIN関数についてはこちらをいつも参考にさせていただいてます。

要は、2つのデータを取り込んでそれをくっ付ける、という処理をshiny内でやりたいのです。

「shinyでの処理のコツはなんでもshiny内でやろうとしないこと」というのは
先人の教えにもありますが(ソースを忘れた)、
それ以前に職場内で「なんでもExcelでやろうとしない」ってことを言いたかったので、
そんならノンプログラミングでとにかくJOINだけ出来るツールを作ったろうじゃん!
…というのが動機です。

さて、やりたいことをフツーのRでやるならこんな感じです↓

require(shiny)
require(tidyverse)
# コレ
left_join(iris, iris %>% gather(key, val, -Species), 
          by = ("Species" = "Species")) %>% View()

あ、上でやっていることはあくまで何かJOINさせてみたかっただけなので、
変な結合してレコードが3万くらいになります。。。

まずはUI部分

とりあえずCSVファイルのみで対応するよう以下のように書いてます
(単にコチラの100本ノックの内容を元に書いただけです。こちらはすごい勉強になりました…!)

ui <-   
  fluidPage(
    titlePanel("RでVLOOKUPツール(csv編)"),
    sidebarLayout(
      sidebarPanel(
        h4("1.ファイルを選択↓"),
        # ファイル1選択
        fileInput("inputfile_1", "File1を選ぶ",
                  accept = c(
                    "text/csv",
                    "text/comma-separated-values,text/plain",
                    ".csv")
        ),
        tags$hr(),
        # ファイル2選択
        fileInput("inputfile_2", "File2を選ぶ",
                  accept = c(
                    "text/csv",
                    "text/comma-separated-values,text/plain",
                    ".csv")
        ),
        tags$hr(),
        # 変数選択
        h4("2.キーにする変数を選択"),
        htmlOutput("file1_col"),
        htmlOutput("file2_col"),
        tags$hr(),
        # マージ
        h4("3.マージ&DL"),
        radioButtons("join_type", label = "マージ方法を選択",
                     choices = list("left_join",
                                    "right_join",
                                    "inner_join",
                                    "full_join",
                                    "anti_join")),
        actionButton("submit", "マージ"),
        downloadButton('downloadData', 'ダウンロード')
      ),
      # メインパネルはテーブル出力のみ
      mainPanel(
        tabsetPanel(type = "tabs",
                    tabPanel("Table1", tableOutput('table1')),
                    tabPanel("Table2", tableOutput('table2')),
                    tabPanel("Merged_Table", tableOutput('merged_table'))
        )
      )
    )
  )

続いてserver部分

こちらはまず以下のように書いて、submit部分が上手くいきませんでした。

server <- function(input, output, session) {
  # ファイル1の読み込み
  observeEvent(input$inputfile_1, {

    csv_file = reactive(read.csv(input$inputfile_1$datapath))
    output$table1 = renderTable(csv_file())

    output$file1_col = renderUI({ 
      selectInput("x", "ファイル1", colnames(csv_file()))
    })

    })

  # ファイル2の読み込み
  observeEvent(input$inputfile_2, {

    csv_file = reactive(read.csv(input$inputfile_2$datapath))
    output$table2 = renderTable(csv_file())

    output$file2_col = renderUI({ 
      selectInput("y", "ファイル2", colnames(csv_file()))
    })
  })

  # テーブルのマージ
  observeEvent(input$submit, {
    # ファイル指定
    csv_file_1 = reactive(read.csv(input$inputfile_1$datapath))
    csv_file_2 = reactive(read.csv(input$inputfile_2$datapath))
    # by用変数名指定
    by_1 = reactive(output$file1_col)
    by_2 = reactive(output$file2_col)

    output$merged_table = renderTable({
      left_join(csv_file_1(), csv_file_2(), 
                by = c(by_1() = by_2())
                )
      })
  })

  output$downloadData = downloadHandler(
    filename = "merged_data.csv",
    content = function(file) {
      csv_file = reactive(read.csv(input$file$datapath))}
      )
}

# Run the application ----
shinyApp(ui = ui, server = server)

…なぜleft_join(x, y, by = c())だとエラーになるのに
left_join(x, y, by = ())だと通るんだろう…。
どのみちやりたいことはできないんだけど。

このsubmit部分が上手くいかない件は、dplryのJOIN関数は以下のように書きますが、
byで指定する文字列がshinyoutputとして出てきていて、文字列として扱われてなかったことによるみたい。

dplyr::left_join(a , b , by = c("x1" = "x2"))

参考:https://qiita.com/matsuou1/items/b1bd9778610e3a586e71#_reference-235e237bcce2ff2685fd

解決策は、setNames()

さぁそしたらどーしたらいいんだ?と困っていたところでr-wakalangで質問。
で、以下のようにしたら良いと(@ksmznさん、ありがとうございます…!)

  # テーブルのマージ
  observeEvent(input$submit, {
    # ファイル指定
    table_file_1 = reactive(read.csv(input$inputfile_1$datapath))
    table_file_2 = reactive(read.csv(input$inputfile_2$datapath))

    # by用変数名指定
    by_1 = input$x
    by_2 = input$y

    # マージ方法指定
    choise = input$join_type

    output$merged_table = renderTable({
      eval(
        parse(
          text = paste0(
            choise, "(table_file_1(), table_file_2(), by = setNames(by_1, by_2))"
          )
        )
      )
    })
  })

この関数はコチラにもあるように、
「名前付きベクトル」をセットするというもの。

…なるほど、分からん!

ということで

  • shinyはいつものRとちょっと違う考え方を意識しとく。
  • 文字列を指定する部分はsetNames()で
  • 分からなくなったらr-wakalangで聴いてみると、Rおじさん達が優しく教えてくれます
  • shiny楽しい。

その他

以下、よく分かってないから調べるメモ。

  • なんでby=c()のcがなくてもOKだったのか…ナゾ。
  • reactive()、何回か読んだけどなんかまだピンと来てない
  • 「名前付きベクトル」にすると何がおいしいんだろう。
  • output$~input$~、色々ごっちゃになる。。。ビデオデッキの入出力とかもダメ。苦手。

参考にしたサイト

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0