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 アプリケーションを対話的に開発およびテストできます。

開始するには、次の手順に従ってください。

  1. R ノートブックを作成します。

  2. Shiny パッケージをインポートし、サンプル アプリ 01_hello を次のように実行します。

      library(shiny)
      runExample("01_hello")
    
  3. アプリの準備ができると、出力には、新しいタブを開くクリック可能なリンクとして Shiny アプリの URL が含まれます。 このアプリを他のユーザーと共有するには、「 Shiny アプリの URL を共有する」を参照してください。

    Shiny アプリの例

  • コマンド結果には、例に示すデフォルトのログメッセージ(Listening on http://0.0.0.0:5150)と同様にログメッセージが表示されます。

  • Shiny アプリケーションを停止するには、[ キャンセル] をクリックします。

  • Shiny アプリケーションは、ノートブック R プロセスを使用します。 ノートブックをクラスターから切り離すか、アプリケーションを実行しているセルをキャンセルすると、Shiny アプリケーションは終了します。 Shiny アプリケーションの実行中に他のセルを実行することはできません。

Databricks Git フォルダーから Shiny アプリを実行する

Databricks Git フォルダーにチェックインされている Shiny アプリを実行できます。

  1. リモート Git リポジトリを複製します

  2. アプリケーションを実行します。

    library(shiny)
    runApp("006-tabsets")
    

ファイルから Shiny アプリを実行する

Shiny アプリケーション コードがバージョン管理によって管理されるプロジェクトの一部である場合は、ノートブック内で実行できます。

絶対パスを使用するか、 setwd()を使用して作業ディレクトリを設定する必要があります。

  1. 次のようなコードを使用して、リポジトリからコードをチェックアウトします。

      %sh git clone https://github.com/rstudio/shiny-examples.git
      cloning into 'shiny-examples'...
    
  2. アプリケーションを実行するには、次のようなコードを別のセルに入力します。

    library(shiny)
    runApp("/databricks/driver/shiny-examples/007-widgets/")
    

Shiny アプリの URL を共有する

アプリの起動時に生成される Shiny アプリの URL は、他のユーザーと共有できます。 クラスターに対する CAN ATTACH TO 権限を持つ Databricks ユーザーは、アプリとクラスターの両方が実行されている限り、アプリを表示および操作できます。

アプリが実行されているクラスターが終了すると、アプリにアクセスできなくなります。 自動終了はクラスター設定で 無効にできます

Shiny アプリをホストしているノートブックを別のクラスターにアタッチして実行すると、Shiny URL が変更されます。 また、同じクラスターでアプリを再起動すると、Shiny は別のランダムなポートを選択する可能性があります。 安定した URL を確保するには、 shiny.port オプションを設定するか、同じクラスターでアプリを再起動するときに port 引数を指定できます。

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)
Spark Shiny アプリ

よくある質問(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 では、表示されるデータをダウンサンプリングしたり、画像の解像度を下げたりするなどして、データ サイズを小さくすることをお勧めします。

Shiny サーバーに公開して Databricks 上のデータにアクセスできる Shiny アプリケーションを開発するにはどうすればよいですか?

Databricks での開発およびテスト中は、SparkR または sparklyr を使用して自然にデータにアクセスできますが、Shiny アプリケーションがスタンドアロン ホスティング サービスに発行された後は、Databricks 上のデータとテーブルに直接アクセスすることはできません。

アプリケーションが Databricks の外部で機能できるようにするには、データへのアクセス方法を書き直す必要があります。 いくつかのオプションがあります。

  • JDBC/ODBC を使用して、クエリーをデータブリック クラスターに送信します。

  • Databricks接続を使用します。

  • オブジェクトストレージ上のデータに直接アクセスします。

Databricks では、Databricks ソリューション チームと協力して、既存のデータとアナリティクスのアーキテクチャに最適なアプローチを見つけることをお勧めします。

Databricks ノートブック内で Shiny アプリケーションを開発できますか?

はい、Databricks ノートブック内で Shiny アプリケーションを開発できます。