Shiny チュートリアル レッスン5: Rスクリプトとデータを使用する

この記事は約18分で読めます。

この記事はShiny公式チュートリアルを翻訳したものです。
公式サイト: https://shiny.posit.co/r/getstarted/shiny-basics/lesson1/index.html

チュートリアルの目次はこちら

このレッスンでは、Shinyアプリで使用するデータ、R スクリプト、およびパッケージをロードする方法を説明します。その過程で、米国国勢調査データを視覚化する洗練されたアプリを構築します。

counties.rds

counties.rdsは、UScensus2010 Rパッケージで収集された、米国の各郡の人口統計データのデータセットです。ここからダウンロードできます。

ファイルを入手したら、

  • census-appディレクトリ内に新しいdataという名前のフォルダーを作成します。
  • dataフォルダ内のcounties.rdsに移動します。

完了すると、census-appフォルダーは次のようになります。

counties.rdsのデータセットは以下を含みます。

  • 米国の各郡の名前
  • 郡の総人口
  • 郡内の住民のうち白人、黒人、ヒスパニック系、またはアジア人の割合
counties <- readRDS("census-app/data/counties.rds")
head(counties)
             name total.pop white black hispanic asian
1 alabama,autauga     54571  77.2  19.3      2.4   0.9
2 alabama,baldwin    182265  83.5  10.9      4.4   0.7
3 alabama,barbour     27457  46.8  47.8      5.1   0.4
4    alabama,bibb     22915  75.0  22.9      1.8   0.1
5  alabama,blount     57322  88.9   2.5      8.1   0.2
6 alabama,bullock     10914  21.9  71.0      7.1   0.2

helpers.R

helpers.Rは、上の図のようなコロプレスマップの作成に役立つRスクリプトです。コロプレスマップは、色を使用して変数の地域的変動を表示するマップです。今回の場合、helpers.Rcounties.rdsのデータをマップするように設計されたpercent_map関数を作成します。ここからhelpers.Rをダウンロードできます。

helpers.Rでは、mapsおよびmapprojパッケージを使用します。これらのパッケージをこれまでにインストールしたことがない場合は、このアプリを作成する前にインストールする必要があります。以下を実行します。

install.packages(c("maps", "mapproj"))

以下のように,census-appディレクトリ内にhelpers.R保存します。

helpers.Rpercent_map関数は5つの引数を取ります。

ArgumentInput
varcountes.rdsデータセットの列ベクトル
colorcolors()の出力に表示される任意の文字列
legend.titleプロットの凡例のタイトルとして使用する文字列
maxシェード範囲を制御するためのパラメータ (デフォルトは 100)
minシェード範囲を制御するためのパラメータ (デフォルトは 0)

コマンドラインでpercent_map関数を使用すると、郡データをコロプレスマップとしてプロットできます。

library(maps)
library(mapproj)
source("census-app/helpers.R")
counties <- readRDS("census-app/data/counties.rds")
percent_map(counties$white, "darkgreen", "% White")

注: 上記のコードは、census-appが作業ディレクトリ内のサブディレクトリであることを前提としています。作業ディレクトリをcensus-appの親ディレクトリとして設定してください。作業ディレクトリの場所を変更するには、RStudioメニュー バーで「Session > Set Working Directory > Choose Directory」をクリックします。

percent_map関数は郡データをコロプレスマップとしてプロットします。ここでは、郡内の白人住民の割合を濃い緑色でプロットします。

ファイルのロードとファイルパス

上記のコードを見てください。percent_map関数を使用するには、まずsource関数でhelpers.Rを走らせ、次にreadRDS関数でcounties.rdsをロードします。library(maps), library(mapproj)も実行します。

percent_map関数をアプリで使用する前に、Shiny に同じ関数を呼び出すように要求する必要がありますが、これらの関数の記述方法は変わります。source関数とreadRDS関数の両方にファイルパスが必要ですが、ファイルパスはShinyアプリではコマンドラインでの場合と同じようには動作しません。

Shinyがserver.Rでコマンドを実行すると、すべてのファイルパスがserver.Rと同じディレクトリから始まるように処理されます。つまり、server.Rを保存したディレクトリがShinyアプリの作業ディレクトリになります。

server.Rと同じディレクトリにhelpers.Rを保存したので、Shinyに以下のようにロードするよう命令できます。

source("helpers.R")

server.Rがあるディレクトリのサブディレクトリ(名前付きデータ)にcounties.rdsを保存したので、以下で読み込むことができます。

counties <- readRDS("data/counties.rds")

通常の方法でmapmapprojパッケージをロードできます。

library(maps)
library(mapproj)

ファイルパスは必要ありません。

実行

これらのコマンドをapp.Rスクリプトに配置すると、Shinyはこれらのコマンドをすべて実行します。ただし、スクリプトをどこに配置するかによって、スクリプトが実行 (または再実行) される回数が決まり、それがアプリのパフォーマンスに影響します。これは、Shinyがスクリプトの一部のセクションを他のセクションよりも頻繁に実行するためです。

Shinyは、初めてrunAppを呼び出したときにスクリプト全体を実行します。これにより、Shinyがserver関数を実行します。

Shinyは、新しいユーザーが到着するまでserver関数を保存します。新しいユーザーがアプリにアクセスするたびに、Shinyはserver関数を一回再実行します。この関数は、Shinyがユーザーごとに個別のリアクティブオブジェクトのセットを構築するのに役立ちます。

ユーザーがウィジェットを操作してその値を変更すると、Shinyは、値が変更されたウィジェットに依存する各リアクティブオブジェクトに割り当てられた Rコードを再実行します。ユーザーが非常にアクティブである場合、これらの式は 1 秒間に何度も再実行される可能性があります。

これまでに学んだことは次のとおりです。

  • shinyApp関数はアプリの起動時に1回実行されます
  • server関数は、ユーザーがアプリにアクセスするたびに1回実行されます。
  • render*関数内のRコードは何度も実行されます。Shinyは、ユーザーがウィジェットの値を変更するたびに、それらを1回実行します。

この情報はどのように使用できるでしょうか?

app.Rの先頭,つまりserver関数の外側において,スクリプトを読み込み、ライブラリをロードし、データ セットを読み込んでください。 Shinyはこのコードを1回だけ実行します。これだけで、 serverに含まれるRコードを実行するように設定できます。

ユーザー固有のオブジェクトをserver関数内で、かつrender*関数の外で定義します。これらは、各ユーザーが独自の個人用コピーを必要とすると考えられるオブジェクトです。たとえば、ユーザーのセッション情報を記録するオブジェクトです。このコードはユーザーごとに1回実行されます。

Shiny がオブジェクトを構築するために再実行する必要があるコードのみをrender*関数内に配置します。Shinyは、ユーザーがチャンク内でのウィジェットを変更するたびに、render*チャンク内のすべてのコードを再実行します。これは非常に頻繁に起こります。

一般に、必要のないコードをrender*関数内に配置することは避けてください。そうすると、アプリ全体の速度が低下します。

Your turn 1

次のapp.Rファイルをコピーしてcensus-appディレクトリに貼り付けます。
そして以下をapp.Rファイルに加えます。

source("helpers.R")
counties <- readRDS("data/counties.rds")
library(maps)
library(mapproj)

コマンドは効率的な場所に配置してください。

注: これは、アプリを完成させる2つの手順のうちの最初の手順です。上記のコードを挿入する最適な場所を選択してください。ただし、アプリを実行しないでください。2つ目のステップ (Your turn 2) で実際のコードに#some arguments部分を置き換えるまで、アプリはエラーを返します。

ui.R

# User interface ----
ui <- page_sidebar(
  title = "censusVis",

  sidebar = sidebar(
    helpText(
      "Create demographic maps with information from the 2010 US Census."
    ),
    selectInput(
      "var",
      label = "Choose a variable to display",
      choices =
        c(
          "Percent White",
          "Percent Black",
          "Percent Hispanic",
          "Percent Asian"
        ),
      selected = "Percent White"
    ),
    sliderInput(
      "range",
      label = "Range of interest:",
      min = 0, 
      max = 100, 
      value = c(0, 100)
    )
  ),

  card(plotOutput("map"))
)

# Server logic ----
server <- function(input, output) {
  output$map <- renderPlot({
    percent_map( # some arguments )
  })
}

# Run app ----
shinyApp(ui, server)

模範解答1

アプリはhelpers.Rcounties.rdsを1回読み込むだけでよいため、uiserver関数の外に置く必要があります。これは、mapライブラリ (percent_map関数に使用する)をロードするのにも適した場所です。

library(maps)
library(mapproj)
source("helpers.R")
counties <- readRDS("data/counties.rds")

# User interface ----
ui <- page_sidebar(
  title = "censusVis",
  sidebar = sidebar(
    helpText(
      "Create demographic maps with information from the 2010 US Census."
    ),
    selectInput(
      "var",
      label = "Choose a variable to display",
      choices =
        c(
          "Percent White",
          "Percent Black",
          "Percent Hispanic",
          "Percent Asian"
        ),
      selected = "Percent White"
    ),
    sliderInput(
      "range",
      label = "Range of interest:",
      min = 0,
      max = 100,
      value = c(0, 100)
    ),
  ),
  card(plotOutput("map"))
)

# Server logic ----
server <- function(input, output) {
  output$map <- renderPlot({
    percent_map( # some arguments )
  })
}

# Run app ----
shinyApp(ui, server)

「各ユーザーがcountiespercent_mapの独自のコピーを必要とするのではないか?」と疑問に思うかもしれません。 (これは、コードがserver関数の内部に入る必要があることを意味します)。いいえ、各ユーザーはそうではありません。

ユーザーのコンピューターでは ShinyアプリのRコードは実行されないことに注意してください。実際、彼らのコンピュータはRコードさえ認識しません。サーバーとして使用するコンピューターは、すべてのユーザーに必要なすべてのRコードを実行します。結果は HTML要素としてユーザーに送信されます。

サーバーは、 counties.rdspercent_mapの単一のグローバルコピーに依存してすべてのユーザーに必要なRコードをすべて実行できます。オブジェクトの値がユーザーごとに異なる場合にのみ、ユーザーごとに個別のオブジェクトを構築する必要があります。

アプリを終了する

国勢調査視覚化アプリには、 "map"という名前のプロットである1つのリアクティブオブジェクトがあります。プロットは5つの引数を取るpercent_map関数を使用して構築されます。

  • 最初の 3 つの引数varcolor、および はlegend.titleは,選択ボックスウィジェットの値によって異なります。
  • 最後の2つの引数maxminは、スライダーバーウィジェットの最大値と最小値に対応します。

以下のserver関数は、percent_map関数のリアクティブな引数を作成する1つの方法を示しています。 Rのswitch関数は、セレクトボックスウィジェットの値を任意のものに変換できます。ただし、スクリプトは不完全です。color、legend.title、max、またはminの値は定義されていません。注: スクリプトはそのままでは実行されません。スクリプトを実行する前にスクリプトを完成させる必要があります。これがYour turn 2のタスクです。

server <- function(input, output) {
  output$map <- renderPlot({
    data <- switch(input$var,
                   "Percent White" = counties$white,
                   "Percent Black" = counties$black,
                   "Percent Hispanic" = counties$hispanic,
                   "Percent Asian" = counties$asian)

    percent_map(var = data, color = ?, legend.title = ?, max = ?, min = ?)
  })
}

Your turn 2

コードを完成させて、きちんと動く国勢調査視覚化アプリを構築します。

アプリをデプロイする準備ができたら、app.Rファイルを保存してrunApp("census-app")を実行します。すべてが機能すると、アプリは下の図のようになります。

以下はあなたが決める必要があります

  • percent_map関数の引数の値を作成する方法、
  • これらの引数を作成するコードをどこに置くか

ユーザーが関連するウィジェットを変更するたびに引数の値を切り替える必要があることに注意してください。完了した場合、または行き詰まった場合は、以下の模範解答を読んでください。

模範解答2

# Load packages ----
library(shiny)
library(maps)
library(mapproj)

# Load data ----
counties <- readRDS("data/counties.rds")

# Source helper functions -----
source("helpers.R")

# User interface ----
ui <- page_sidebar(
  title = "censusVis",
  sidebar = sidebar(
    helpText(
      "Create demographic maps with information from the 2010 US Census."
    ),
    selectInput(
      "var",
      label = "Choose a variable to display",
      choices =
        c(
          "Percent White",
          "Percent Black",
          "Percent Hispanic",
          "Percent Asian"
        ),
      selected = "Percent White"
    ),
    sliderInput(
      "range",
      label = "Range of interest:",
      min = 0,
      max = 100,
      value = c(0, 100)
    )
  ),
  card(plotOutput("map"))
)

# Server logic ----
server <- function(input, output) {
  output$map <- renderPlot({
    data <- switch(input$var,
                   "Percent White" = counties$white,
                   "Percent Black" = counties$black,
                   "Percent Hispanic" = counties$hispanic,
                   "Percent Asian" = counties$asian)

    color <- switch(input$var,
                    "Percent White" = "darkgreen",
                    "Percent Black" = "black",
                    "Percent Hispanic" = "darkorange",
                    "Percent Asian" = "darkviolet")

    legend <- switch(input$var,
                     "Percent White" = "% White",
                     "Percent Black" = "% Black",
                     "Percent Hispanic" = "% Hispanic",
                     "Percent Asian" = "% Asian")

    percent_map(data, color, legend, input$range[1], input$range[2])
  })
}

# Run app ----
shinyApp(ui, server)

server関数のより簡潔なバージョンは次のとおりです。

server <- function(input, output) {
  output$map <- renderPlot({
    args <- switch(input$var,
      "Percent White" = list(counties$white, "darkgreen", "% White"),
      "Percent Black" = list(counties$black, "black", "% Black"),
      "Percent Hispanic" = list(counties$hispanic, "darkorange", "% Hispanic"),
      "Percent Asian" = list(counties$asian, "darkviolet", "% Asian"))

    args$min <- input$range[1]
    args$max <- input$range[2]

    do.call(percent_map, args)
  })
}

要約

R スクリプト、パッケージ、データ セットを読み込むことで、より複雑なShinyアプリを作成できます。

以下の点に留意してください:

  • app.RのあるディレクトリがShinyアプリの作業ディレクトリになります。
  • Shiny は、app.Rの最初、つまりserver関数の前に配置されたコードを、アプリの動作中に1回だけ実行します。
  • Shinyはserver関数内に配置されたコードを複数回実行するため、これによりアプリの速度が低下する可能性があります。

また、switch関数が複数選択のShinyウィジェットを使用する際に便利なことも学びました。ウィジェットの値をRの中で変更するためにswitch関数を使用します。

アプリが複雑になると、非効率的で遅くなる可能性があります。レッスン 6では、リアクティブな表現を使用して高速なモジュール型アプリを構築する方法を説明します。

コメント