Lesson6 – reactive 式を使用する

Shiny アプリは、実行速度が速く、素早く作れるのでユーザを驚かせます。しかし、アプリが多数重い計算をする必要がある場合はどうでしょうか?

このレッスンでは、reactive 式を使用した Shiny アプリの効率化方法について説明します。reactive 式を使用すると、アプリのどの部分で更新するかを制御できるため、処理速度を低下させる不要な計算を防ぐことができます。

始める前に:

  • 作業ディレクトリに stockVis という名前の新しいフォルダを作成してください。
  • 次のファイルをダウンロードして、それらを stockVis に配置してください:app.Rhelpers.R
  • runApp(“stockVis”) でアプリを起動します。

StockVis は、R の quantmod パッケージを使用しているので、まだインストールしていない場合は、install.packages(“quantmod”)quantmod をインストールする必要があります。

(訳注)2018年3月、Googleが株価情報の提供を中止したため、app.Rにエラーが発生します。これを回避するには、app.R の 48行目を次の通り変更します。以下、「Google」と言う名称が出てきた場合は、全て「Yahoo」と読み替える必要があるでしょう(断定はできませんが)。

新アプリ:stockVis

stockVis アプリは、(株式)銘柄コードで株価を検索し、その結果を折れ線グラフとして表示します。 このアプリでは、

  1. 調べる株を選択する
  2. 評価する日付の範囲を選ぶ
  3. y 軸には、株価をプロットするのか、株価を対数でプロットするのかを選択し、
  4. インフレに対して価格を訂正するかどうかを決める。


“Adjust prices for inflation” チェックボックスはまだ機能しないので注意してください。このレッスンのタスクの 1 つは、このチェックボックスを修正することです。

デフォルトで、stockVis は SPY 銘柄( S&P 500 全体のインデクス)を表示します。別の株を探すには、Google ファイナンスが認識する株式の記号を入力します。ここで Yahoo の株式記号を調べることができます。一般的な記号の中には、GOOG(Google)、AAPL(Apple)、GS(Goldman Sachs)などがあります。

stockVis は、次の quantmod パッケージの 2 つの関数に大きく依存しています:

  1. getSymbols を使用し、Google ファイナンスやセントルイス連邦準備銀行などの Web サイトから、財務データを R に直接ダウンロードします。
  2. ChartSeries を使用し、価格を魅力あるチャートに表示します。

stockVis は、helpers.R という R スクリプトにも依存しています。これには、インフレに対する株価を調整する関数が含まれています。

チェックボックスと日付範囲

stockVis アプリは、いくつかの新しいウィジェットを使用しています。

  • dateRangeInput で作成した日付範囲セレクタ
  • checkboxInput で作成したいくつかのチェックボックス。チェックウィジェットは非常に簡単です。チェックボックスをチェックした場合は TRUE を、チェックを外した場合は FALSE を返します。

ui オブジェクト内で、このチェックボックスにはそれぞれ log と adjust という名前を付けていますので、server 関数内では input$log と input$adjust として参照します。ウィジェットとその値の使い方を再確認したい場合は、Lesson 3 と Lesson 4 をご覧ください。

計算の効率化

stockVis アプリには問題があります。

“Plot y axis on the log scale” をクリックすると、input$log の値が変わるため、renderPlot の式全体が再実行されます:

renderPlot を再実行するたびに

  1. getSymbols を使用してGoogle ファイナンスからデータを再取得し、
  2. 軸を修正してでチャートを描画し直します。

プロットを再描画するのにデータを再フェッチする必要がないので、これは好ましくありません。 実際、データをあまりにも頻繁に再フェッチすると、Google ファイナンスは接続を切り離してしまいます(ボットのように見えてしまうためです)。しかし、もっと重要なことは、getSymbols の再実行は不要で、アプリの処理速度を低下させると共に、サーバの帯域幅が消費される可能性があると言うことです。

reactive 式

reactive 式で、リアクション中に再実行するものを制限することができます。

reactive 式は、ウィジェット入力を使用して値を返す R 式です。reactive 式は、オリジナルのウィジェットが変更されるたびに値を更新します。

reactive 式を作成するには、R 関数を波括弧で囲んだ reactive 関数を使用します(render* 関数と同じです)。

例えば、下には Google からのデータを取得するために stockVis のウィジェットを使用する reactive 式があります。

式を実行すると、getSymbols が実行され、結果として価格データのデータフレームが返されます。dataInput() を呼び出すことによって、renderPlot で価格データにアクセスするための式を使用することができます。

reactive 式は、通常の R 関数より少しスマートで、値をキャッシュし、その値が何時対象外になったかを認識します。これは何を意味するのでしょうか?最初に reactive 式を実行すると、式はその結果をコンピュータのメモリに保存します。次回 reactive 式を呼び出すと、計算を実行せずにこの保存した結果を返します(これにより、アプリの処理速度が向上します)。

reactive 式は、結果が最新である場合にのみ、保存した結果を返します。reactive 式は、ウィジェットが変更され結果が古くなった場合には、結果を再計算して新しい結果を返し、そのコピーを保存します。reactive 式は、新しいコピーが古くなるまで使用します。

この動作をまとめましょう:

  • reactive 式は、初めて実行されたときにその結果を保存する。
  • その後、reactive 式が呼び出された場合は、保存されている値が古くなったかどうか(すなわち、依存するウィジェットが変更されたかどうか)をチェックする。
  • 値が古い場合、reactive オブジェクトはそれを再計算する(そして新しい結果を保存する)。
  • 値が最新の場合、reactive 式は計算を行わずに保存された値を返す。

この動作を使用して、Shiny がコードを不必要に再実行しないようにすることができます。下の新しい StockVis アプリでreactive 式がどのように動作するかを考えてみましょう。

“Plot y axis on the log scale” をクリックすると、input$log が変更され、renderPlot が再実行されます。今

    1. renderPlot は、dataInput()を呼び出す
    2. dataInput は、日付ウィジェットと symb ウィジェットが変更されていないことをチェックする
    3. dataInput は、Google からデータを再取得せずに保存された株価データを返す
    4. renderPlotは、グラフを正しい軸で再描画する

 

依存関係

ユーザが symb ウィジェット内の銘柄コードを変更した場合はどうなりますか?

変更した場合、renderPlot で描いたプロットが古くなっていますが、renderPlot はもう input$symb を呼び出さなくなっています。Shinyは、input$symb のプロットが古くなったことを知っているのでしょうか?

はい、Shiny はそのことを知り、プロットを再描画します。 Shiny は、出力オブジェクトが依存している reactive 式やウィジェットの入力を追跡します。次の場合、 Shiny は自動的にオブジェクトを再構築します

      • オブジェクトの render* 関数の入力値が変更されるか、または
      • オブジェクトの render* 関数の reactive 式が古くなった場合

reactive 式は、入力値を出力オブジェクトに接続するチェーン内のリンクと考えることができます。出力内のオブジェクトは、チェーンの下流のどこで行った変更にも応答します。(reactive 式は他の reactive 式を呼び出すことができるので、長いチェーンを作ることができます)

reactive 式または render* 関数内から reactive 式を呼び出すだけです。どうして?これらの R 関数のみが reactive 出力を処理するために用意されており、警告なしで変更することができます。実際、Shiny はこれらの関数の外で reactive な式を呼び出さないようにします。

準備作業

“Adjust prices for inflation” という動作しないチェックボックスを修正していきます。ユーザは、インフレーションに対して調整した価格と、調整していない価格に切り換えることができます。

helpers.R の調整関数では、セントルイス連邦準備銀行が提供する消費者物価指数データを使用して、過去の価格を現在の価格に変換します。しかし、どのようにして、アプリにこれを実装できるでしょうか?

下に 1 つの解決方法がありますが、理想的なものではありません。理由がわかりますか?再び、input$log と関係してきます。

Reveal answer
adjust は renderPlot の内部で呼び出されています。adjust ボックスがチェックさた場合、y のスケールを通常のものと対数変換したもの(または、その逆)に切換えるたびに、すべての価格が再調整されます。この再調整は不要な計算です。

やってみよう

この問題を解決するには、新しい reactive 式をアプリに追加します。reactive 式は、dataInput の値を調整した(または調整していない)データのコピーを返す必要があります。

解答を思いついたら、下のモデル解答と比較してください。 ユーザが “Plot y axis on the log scale” をクリックしたときに、アプリ内でどの計算が行われ、どの計算が行われないのかを理解してください。

Reveal answer
各入力を、reactive 式または render* 関数に分離しました。入力が変更されると、古くなった式だけが再実行されます。

フローの例を次に示します。

      • ユーザが “Plot y axis on the log scale.” をクリックする。
      • renderPlot を再実行する。
      • renderPlot は finalInput を呼び出す。
      • finalInput は dataInput と input$adjust でチェックする。
      • 何も変更されていない場合、finalInput は保存された値を返す。
      • 何れかが変更された場合、finalInput は現在の入力値で新しい値を計算する。新しい値は renderPlot に渡され、将来のクエリに新しい値を格納する。

まとめ

reactive 式でコードをモジュール化することにより、アプリをより速くすることができます。

      • reactive 式は、入力値または他の reactive 式からの値を取得し、新しい値を返します
      • reactive 式は、結果を保存し、入力が変更された場合にのみ再計算します
      • reactive 式は、reactive({ }) で作成します
      • reactive 式は、式の名前の後に括弧 () を付けてを呼び出します
      • reactive 式は、他の reactive 式か render* 関数内からのみ呼び出します

今やあなたは、洗練された効率的な Shiny アプリケーションを作成することができます。このチュートリアルの最後のレッスンでは、あなたのアプリケーションを他の人との共有方法を説明します。

Lesson7 へ