この記事は Shiny 公式サイトのチュートリアルを翻訳したものです。
http://shiny.rstudio.com/tutorial/lesson5/
チュートリアル目次:http://d.hatena.ne.jp/hoxo_m/20151222/p1
LESSON5 Rスクリプトとデータの利用
このレッスンでは、Shiny アプリの中で「データ」「R スクリプト」「パッケージ」を使用するために、どうやって読み込むのかを見ていきます。
そのために、アメリカの国勢調査データを可視化する、少し複雑なアプリを構築していきます。
counties.rds
counties.rds
は、UScensus2010
パッケージに付属する、アメリカの各郡(county)に対する人口統計のデータセットをファイル化したものです。
このデータセットファイルは、ここからダウンロードできます。
このデータファイルを入手したら、
-
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.R
は、counties.rds
のデータを地図上に表示するための関数 percent_map
を生成します。
helpers.R
はここからダウンロードできます。
helpers.R
は、maps
および mapproj
パッケージを使用します。
これらのパッケージをインストールしていない場合は、このアプリを作成する前にインストールしておく必要があります。
これらのパッケージをインストールするには、次のコードを実行します。
install.packages(c("maps", "mapproj"))
helpers.R
を census-app
ディレクトリに保存して下さい。
次のようになります。
helpers.R
内の percent_map
関数は、次の 5 つの引数を持ちます:
引数 | 入力するもの |
---|---|
var |
counties.rds データセット内の列名のベクトル |
color |
色を指定するための文字列。使用可能な色は colors() で見ることができる |
legend.title |
プロットの凡例のタイトルに使われる文字列 |
max |
陰の範囲を制御するための数値(デフォルト100) |
min |
陰の範囲を制御するための数値(デフォルト0) |
percent_map
関数は次のようにコマンドラインから使うことができます。
percent_map
関数は郡データをコロプレス地図としてプロットします。
library(maps)
library(mapproj)
source("census-app/helpers.R")
counties <- readRDS("census-app/data/counties.rds")
percent_map(counties$white, "darkgreen", "% white", 0, 100)
注意:上記のコードでは、census-app
はワーキングディレクトリの下にあると仮定しています。ワーキングディレクトリが census-app
の親ディレクトリになっているか確認 して下さい。ワーキングディレクトリを変更するには、RStudio のメニューバーから Session > Set Working Directory > Choose Directory... をクリックします。
percent_map
関数は、郡データをコロプレス地図としてプロットします。
ここでは、郡内の白人の住民の割合を深緑(dark green)でプロットしています。
ファイルの読み込みとファイルパス
上のコードを見てみましょう。
percent_map
関数を使うために、まず source
関数によって helpers.R
を実行し、次に、readRDS
関数によって counties.rds
を読み込みました。
また、library(maps)
および library(mapproj)
を実行しました。
Shiny がアプリの中で percent_map
関数を呼び出す前に、これらの関数を実行しておく必要があります。
しかし、どのように書いたら良いのでしょうか?
source
関数と readRDS
関数には、ファイルパスを入力する必要があります。
そして、ファイルパスは Shiny アプリの中ではコマンドラインと同じようには動きません。
Shiny は、server.R
の中でコマンドを実行するとき、すべてのファイルパスを server.R
と同じディレクトリから始まるように取り扱います。
別の言い方をすれば、server.R
を保存したディレクトリは、Shiny アプリのワーキングディレクトリとなります。
helpers.R
は server.R
と同じディレクトリに保存されているため、これを読み込むには、次のようにします。
source("helpers.R")
counties.rds
は data
サブディレクトリに保存されているため、これを読み込むには、次のようにします。
counties <- readRDS("data/counties.rds")
maps
および mapproj
パッケージを読み込むのは、通常の方法で大丈夫です。
library(maps)
library(mapproj)
これらにはファイルパスを入力する必要はありません。
実行
server.R
の中にこれらのコードを書けば、Shiny はすべてのコマンドを実行してくれます。
しかし、server.R
のどこに書くかで、何回実行されるのかが異なります。
これは、アプリの実行速度に影響するため注意が必要です。
server.R
の中には、Shiny が何度もコードを実行するセクションがあります。
Shiny は、runApp
関数が呼ばれた際に、一度だけコード全体を実行します。
これは、Shiny が shinyServer
関数を実行する引き金となります。
shinyServer
関数は、最初の引数である無名関数を Shiny に渡します。
Shiny は、新しいユーザが訪問するまで、この関数を保存しておきます。
新しいユーザがアプリを訪問したとき、Shiny はこの無名関数を再実行します。
この関数は、Shiny が各ユーザに対して異なる reactive オブジェクトを構築するために使われます。
ユーザがウィジェットを変更すると、Shiny は reactive オブジェクトに代入された表現式を再実行します。
この表現式は、ユーザによって、短い時間の間に何度も何度も実行される可能性があります。
ここまでで学んだことは、以下のとおりです:
-
server.R
スクリプトは、アプリを立ち上げた時に一度だけ実行されます。 -
shinyServer
内の無名関数は、ユーザがアプリを訪問するたびに実行されます。 -
render*
関数の中の表現式は、ユーザがウィジェットを変更するたびに、何度も実行されます。
ここから何が言えるでしょうか?
「スクリプト」「ライブラリ」「データセット」を読み込むためのコードは、server.R
の最初、shinyServer
関数の外側に書いて下さい。
Shiny は、そのコードを一度だけ実行します。
これにより、shinyServer
に含まれる表現式を実行するための準備がすべて完了します。
ユーザごとに必要なオブジェクトは、shinyServer
の無名関数の中、render*
関数の外に定義して下さい。
このようなオブジェクトが必要になるのは、各ユーザに対して個人的な情報が必要になるときです。
例えば、ユーザのセッション情報を記録したいときなどです。
render*
関数の中には、Shiny が本当に再構築する必要のあるオブジェクトだけを書いてください。
ユーザが render*
関数内に記載されたウィジェットを変更するたびに、render*
関数内のすべてのコードが再実行されます。
これはかなりの頻度になる可能性があります。
render*
関数の中では、必要のないコードを書くのを避けるべきです。
余分なコードはアプリ全体の実行速度を落とす原因となります。
やってみよう 1
以下に示す ui.R
と server.R
をコピー & ペーストし、census-app
ディレクトリ内にファイルとして保存しましょう。
そして、次のコードを server.R
に加えて下さい。
source("helpers.R")
counties <- readRDS("data/counties.rds")
library(maps)
library(mapproj)
必ず、効率的になるような場所に、これらのコマンドを置くようにして下さい。
注意:これはアプリを完成させるための最初のステップです。上のコードを挿入する最適な場所を選んで下さい。ただし、アプリはまだ実行してはいけません。ステップ 2 で # some arguments
を実際のコードにするまでは、このアプリはエラーを返すでしょう。
ui.R
# ui.R
shinyUI(fluidPage(
titlePanel("censusVis"),
sidebarLayout(
sidebarPanel(
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))
),
mainPanel(plotOutput("map"))
)
))
server.R
# server.R
shinyServer(
function(input, output) {
output$map <- renderPlot({
percent_map( # some arguments )
})
}
)
(訳注:模範解答は公式サイトの Model Answer 1 にあります)
アプリを完成させる
この censusVis アプリは、1 つの reactive オブジェクトを持ちます。
"map" という名前のプロットオブジェクトです。
このプロットは、percent_map
関数によって生成されます。
percent_map
関数は、5 つの引数を持ちます。
- 最初の 3 つの引数は、
var
,color
,legend.title
です。これらの引数は、セレクトボックスウィジェットの値に依存します。 - 最後の 2 つの引数は、
max
とmin
です。これらは、スライダーバーウィジェットの最大値と最小値になります。
下記の server.R
スクリプトは、percent_map
関数に対する reactive な引数を作る方法を示しています。
R の switch
関数を使えば、セレクトボックスウィジェットの出力を好きなように変換することができます。
ただし、このスクリプトは不完全です。
color
, legend.title
, max
, min
に対する値が与えられていません。
注意:このスクリプトはこのままでは動きません。「やってみよう 2」でこのスクリプトを完成させてから実行して下さい。
# server.R
library(maps)
library(mapproj)
counties <- readRDS("data/counties.rds")
source("helpers.R")
shinyServer(
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 = ?)
})
}
)
やってみよう 2
censusVis アプリが動くようにコードを完成させて下さい。
アプリを起動する準備が整ったら、server.R
と ui.R
を保存して、runApp("census-app")
を実行して下さい。
もし、すべてがうまくいったら、アプリは下の画像のようになります。
アプリを完成させるためには、次のことを決める必要があります。
- どのようにして
percent_map
に渡す引数の値を作るか - それらの引数を作るためのコードをどこに書くか
引数の値は、ユーザが対応するウィジェットを変化させると切り替わることを忘れないで下さい。
コードが完成するか、または行き詰まってしまったら、模範解答を見て下さい。
(訳注:模範解答は公式サイトの Model Answers 2 にあります)
まとめ
このレッスンでは、「R スクリプト」「パッケージ」「データセット」を使った、より複雑な Shiny アプリの作り方を学びました。
次のことを忘れないで下さい:
-
server.R
ファイルのあるディレクトリが、Shiny アプリのワーキングディレクトリになる。 -
server.R
のコードの中で、shinyServer
より前にあるコードは、Shiny アプリの起動時に一度だけ実行される。 -
shinyServer
の中に書かれたコードは、何度も実行されるため、アプリの速度を低下させる原因となる。
また、switch
関数が、複数選択の Shiny ウィジェットとの対応付けに便利であることを学びました。
ウィジェットの値を R の表現式に変換するには、switch
関数を使って下さい。
アプリが複雑になるにつれて、非効率的で遅くなってしまう可能性がでてきます。
レッスン6 では、reactive な表現式を使って、高速かつ部品化されたアプリを作る方法を学びます。