shinyでJOINする


RでWEBアプリが簡単に作れるshinyですが、
具体的にどう作るのかよく分かってなかったので、
色々参考に書いて試してつまずいて[r-wakalang](https://qiita.com/uri/items/5583e91bb5301ed5a4ba)で教えていただいたことをまとめます。

# やりたいこと

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

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

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

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

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

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

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


#まずはUI部分

とりあえずCSVファイルのみで対応するよう以下のように書いてます
（単に[コチラの100本ノック](http://www.randpy.tokyo/entry/shiny_16)の内容を元に書いただけです。こちらはすごい勉強になりました…！）

```R
ui <-   
  fluidPage(
    titlePanel("RでVLOOKUPツール（csv編）"),
    sidebarLayout(
      sidebarPanel(
        h4("１．ファイルを選択↓"),
        # ファイル１選択
        fileInput("inputfile_1", "File１を選ぶ",
                  accept = c(
                    "text/csv",
                    "text/comma-separated-values,text/plain",
                    ".csv")
        ),
        tags$hr(),
        # ファイル２選択
        fileInput("inputfile_2", "File２を選ぶ",
                  accept = c(
                    "text/csv",
                    "text/comma-separated-values,text/plain",
                    ".csv")
        ),
        tags$hr(),
        # 変数選択
        h4("２．キーにする変数を選択"),
        htmlOutput("file1_col"),
        htmlOutput("file2_col"),
        tags$hr(),
        # マージ
        h4("３．マージ＆ＤＬ"),
        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部分が上手くいきませんでした。

```R
server <- function(input, output, session) {
  # ファイル１の読み込み
  observeEvent(input$inputfile_1, {
    
    csv_file = reactive(read.csv(input$inputfile_1$datapath))
    output$table1 = renderTable(csv_file())
    
    output$file1_col = renderUI({ 
      selectInput("x", "ファイル１", colnames(csv_file()))
    })
    
    })

  # ファイル２の読み込み
  observeEvent(input$inputfile_2, {
    
    csv_file = reactive(read.csv(input$inputfile_2$datapath))
    output$table2 = renderTable(csv_file())
    
    output$file2_col = renderUI({ 
      selectInput("y", "ファイル２", 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として出てきていて、文字列として扱われてなかったことによるみたい。

```R
dplyr::left_join(a , b , by = c("x1" = "x2"))
```
参考：https://qiita.com/matsuou1/items/b1bd9778610e3a586e71#_reference-235e237bcce2ff2685fd

#解決策は、setNames()

さぁそしたらどーしたらいいんだ？と困っていたところで[r-wakalang](https://qiita.com/uri/items/5583e91bb5301ed5a4ba)で質問。
で、以下のようにしたら良いと（@ksmznさん、ありがとうございます…！）

```R  
  # テーブルのマージ
  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))"
          )
        )
      )
    })
  })
```

この関数は[コチラ](http://d.hatena.ne.jp/teramonagi/20131213/1386883675)にもあるように、
「名前付きベクトル」をセットするというもの。

…なるほど、分からん！

#ということで

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

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

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

# 参考にしたサイト
* https://qiita.com/uri/items/5583e91bb5301ed5a4ba
* http://www.randpy.tokyo/entry/shiny_16
* https://qiita.com/matsuou1/items/b1bd9778610e3a586e71#_reference-235e237bcce2ff2685fd