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$~
、色々ごっちゃになる。。。ビデオデッキの入出力とかもダメ。苦手。