Shiny on Databricks
Shiny は CRAN で利用可能な R パッケージで、対話型の R アプリケーションとダッシュボードの構築に使用されます。 Shiny アプリケーションは、Databricks ノートブックから直接開発、ホスト、共有できます。
Shiny の使用を開始するには、 Shiny のチュートリアルを参照してください。 これらのチュートリアルは、Databricks ノートブックで実行できます。
この記事では、Databricks で Shiny アプリケーションを実行し、Shiny アプリケーション内で Apache Spark を使用する方法について説明します。
Rノートブック内の Shiny
R ノートブック内の Shiny の使用を開始する
Shiny パッケージは Databricks Runtimeに含まれています。ホストされている RStudio と同様に、Databricks R ノートブック内で Shiny アプリケーションを対話的に開発およびテストできます。
開始するには、次の手順に従ってください。
R ノートブックを作成します。
Shiny パッケージをインポートし、サンプル アプリ
01_hello
を次のように実行します。library(shiny) runExample("01_hello")
アプリの準備ができると、出力には、新しいタブを開くクリック可能なリンクとして Shiny アプリの URL が含まれます。 このアプリを他のユーザーと共有するには、「 Shiny アプリの URL を共有する」を参照してください。
注
コマンド結果には、例に示すデフォルトのログメッセージ(
Listening on http://0.0.0.0:5150
)と同様にログメッセージが表示されます。Shiny アプリケーションを停止するには、[ キャンセル] をクリックします。
Shiny アプリケーションは、ノートブック R プロセスを使用します。 ノートブックをクラスターから切り離すか、アプリケーションを実行しているセルをキャンセルすると、Shiny アプリケーションは終了します。 Shiny アプリケーションの実行中に他のセルを実行することはできません。
Databricks Git フォルダーから Shiny アプリを実行する
Databricks Git フォルダーにチェックインされている Shiny アプリを実行できます。
アプリケーションを実行します。
library(shiny) runApp("006-tabsets")
ファイルから Shiny アプリを実行する
Shiny アプリケーション コードがバージョン管理によって管理されるプロジェクトの一部である場合は、ノートブック内で実行できます。
注
絶対パスを使用するか、 setwd()
を使用して作業ディレクトリを設定する必要があります。
次のようなコードを使用して、リポジトリからコードをチェックアウトします。
%sh git clone https://github.com/rstudio/shiny-examples.git cloning into 'shiny-examples'...
アプリケーションを実行するには、次のようなコードを別のセルに入力します。
library(shiny) runApp("/databricks/driver/shiny-examples/007-widgets/")
Shiny アプリ内で Apache Spark を使用する
Apache Spark は、SparkR または sparklyr のいずれかを使用して、Shiny アプリケーション内で使用できます。
ノートブックで SparkR を Shiny と共に使用する
library(shiny)
library(SparkR)
sparkR.session()
ui <- fluidPage(
mainPanel(
textOutput("value")
)
)
server <- function(input, output) {
output$value <- renderText({ nrow(createDataFrame(iris)) })
}
shinyApp(ui = ui, server = server)
ノートブックで Shiny と共に sparklyr を使用する
library(shiny)
library(sparklyr)
sc <- spark_connect(method = "databricks")
ui <- fluidPage(
mainPanel(
textOutput("value")
)
)
server <- function(input, output) {
output$value <- renderText({
df <- sdf_len(sc, 5, repartition = 1) %>%
spark_apply(function(e) sum(e)) %>%
collect()
df$result
})
}
shinyApp(ui = ui, server = server)
library(dplyr)
library(ggplot2)
library(shiny)
library(sparklyr)
sc <- spark_connect(method = "databricks")
diamonds_tbl <- spark_read_csv(sc, path = "/databricks-datasets/Rdatasets/data-001/csv/ggplot2/diamonds.csv")
# Define the UI
ui <- fluidPage(
sliderInput("carat", "Select Carat Range:",
min = 0, max = 5, value = c(0, 5), step = 0.01),
plotOutput('plot')
)
# Define the server code
server <- function(input, output) {
output$plot <- renderPlot({
# Select diamonds in carat range
df <- diamonds_tbl %>%
dplyr::select("carat", "price") %>%
dplyr::filter(carat >= !!input$carat[[1]], carat <= !!input$carat[[2]])
# Scatter plot with smoothed means
ggplot(df, aes(carat, price)) +
geom_point(alpha = 1/2) +
geom_smooth() +
scale_size_area(max_size = 2) +
ggtitle("Price vs. Carat")
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
よくある質問(FAQ)
しばらくするとShinyアプリがグレー表示されるのはなぜですか?
Shiny アプリとの対話がない場合、アプリへの接続は約 5 分後に閉じます。
再接続するには、Shiny アプリのページを更新します。 ダッシュボードの状態がリセットされます。
しばらくすると Shiny ビューアウィンドウが消えるのはなぜですか?
数分間アイドリングした後に Shiny ビューアウィンドウが消える場合は、「グレーアウト」シナリオと同じタイムアウトが原因です。
長い Spark ジョブが戻らないのはなぜですか?
これは、アイドルタイムアウトが原因でもあります。 前述のタイムアウトよりも長く実行されている Spark ジョブは、ジョブが戻る前に接続が閉じられるため、結果をレンダリングできません。
タイムアウトを回避するにはどうすればいいですか?
「機能要求: クライアントにキープアライブ メッセージを送信して、Github 上の一部のロード バランサーで TCP タイムアウトを防ぐ 」で提案されている回避策があります。回避策は、アプリがアイドル状態のときに WebSocket 接続を維持するためにハートビートを送信します。 ただし、実行時間の長い計算によってアプリがブロックされている場合、この回避策は機能しません。
Shiny は実行時間の長いタスクをサポートしていません。 Shiny ブログ記事では、 promise と future を使用して長いタスクを非同期的に実行し、アプリのブロックを解除することをお勧めします。 ハートビートを使用して Shiny アプリを存続させ、
future
コンストラクトで長時間実行される Spark ジョブを実行する例を次に示します。# Write an app that uses spark to access data on Databricks # First, install the following packages: install.packages(‘future’) install.packages(‘promises’) library(shiny) library(promises) library(future) plan(multisession) HEARTBEAT_INTERVAL_MILLIS = 1000 # 1 second # Define the long Spark job here run_spark <- function(x) { # Environment setting library("SparkR", lib.loc = "/databricks/spark/R/lib") sparkR.session() irisDF <- createDataFrame(iris) collect(irisDF) Sys.sleep(3) x + 1 } run_spark_sparklyr <- function(x) { # Environment setting library(sparklyr) library(dplyr) library("SparkR", lib.loc = "/databricks/spark/R/lib") sparkR.session() sc <- spark_connect(method = "databricks") iris_tbl <- copy_to(sc, iris, overwrite = TRUE) collect(iris_tbl) x + 1 } ui <- fluidPage( sidebarLayout( # Display heartbeat sidebarPanel(textOutput("keep_alive")), # Display the Input and Output of the Spark job mainPanel( numericInput('num', label = 'Input', value = 1), actionButton('submit', 'Submit'), textOutput('value') ) ) ) server <- function(input, output) { #### Heartbeat #### # Define reactive variable cnt <- reactiveVal(0) # Define time dependent trigger autoInvalidate <- reactiveTimer(HEARTBEAT_INTERVAL_MILLIS) # Time dependent change of variable observeEvent(autoInvalidate(), { cnt(cnt() + 1) }) # Render print output$keep_alive <- renderPrint(cnt()) #### Spark job #### result <- reactiveVal() # the result of the spark job busy <- reactiveVal(0) # whether the spark job is running # Launch a spark job in a future when actionButton is clicked observeEvent(input$submit, { if (busy() != 0) { showNotification("Already running Spark job...") return(NULL) } showNotification("Launching a new Spark job...") # input$num must be read outside the future input_x <- input$num fut <- future({ run_spark(input_x) }) %...>% result() # Or: fut <- future({ run_spark_sparklyr(input_x) }) %...>% result() busy(1) # Catch exceptions and notify the user fut <- catch(fut, function(e) { result(NULL) cat(e$message) showNotification(e$message) }) fut <- finally(fut, function() { busy(0) }) # Return something other than the promise so shiny remains responsive NULL }) # When the spark job returns, render the value output$value <- renderPrint(result()) } shinyApp(ui = ui, server = server)
最初のページ読み込みから 12 時間のハード制限があり、その後は、アクティブな場合でも、すべての接続が終了します。 このような場合は、Shiny アプリを更新して再接続する必要があります。 ただし、基になる WebSocket 接続は、ネットワークの不安定性やコンピューターのスリープ モードなど、さまざまな要因によっていつでも閉じる可能性があります。 Databricks では、有効期間の長い接続を必要とせず、セッション状態に過度に依存しないように、Shiny アプリを書き直すことをお勧めします。
起動直後にアプリがクラッシュしますが、コードは正しいようです。どうなっているのですか。
Databricks の Shiny アプリに表示できるデータの合計量には 50 MB の制限があります。 アプリケーションの合計データサイズがこの制限を超えると、起動直後にクラッシュします。 これを回避するために、Databricks では、表示されるデータをダウンサンプリングしたり、画像の解像度を下げたりするなどして、データ サイズを小さくすることをお勧めします。
開発中に 1 つの Shiny アプリ リンクに対して受け入れる接続の数はいくつですか?
Databricks では、最大 20 個を推奨しています。
Databricks Runtime にインストールされているものとは異なるバージョンの Shiny パッケージを使用できますか?
はい。「 R パッケージのバージョンを修正する」を参照してください。
Shiny サーバーに公開して Databricks 上のデータにアクセスできる Shiny アプリケーションを開発するにはどうすればよいですか?
Databricks での開発およびテスト中は、SparkR または sparklyr を使用して自然にデータにアクセスできますが、Shiny アプリケーションがスタンドアロン ホスティング サービスに発行された後は、Databricks 上のデータとテーブルに直接アクセスすることはできません。
アプリケーションが Databricks の外部で機能できるようにするには、データへのアクセス方法を書き直す必要があります。 いくつかのオプションがあります。
JDBC/ODBC を使用して、クエリーをデータブリック クラスターに送信します。
Databricks接続を使用します。
オブジェクトストレージ上のデータに直接アクセスします。
Databricks では、Databricks ソリューション チームと協力して、既存のデータとアナリティクスのアーキテクチャに最適なアプローチを見つけることをお勧めします。
Databricks ノートブック内で Shiny アプリケーションを開発できますか?
はい、Databricks ノートブック内で Shiny アプリケーションを開発できます。