cucumber flesh

Rを中心としたデータ分析・統計解析らへんの話題をしていくだけ

⚙API暮らし時代におけるRでの環境変数の管理

先週はセキュリティ関係の記事をよく見かけた...気がした。これとか。

qiita.com

RでもウェブAPIを利用するパッケージが増えてきて( {RGA}, {slackr}, {twitteR}, {qiitar}などなど)、誰もがAPI暮らしを夢見ている

そんなわけでAPI生活の質を高めるためのRで環境変数を設定する方法と注意点について自分の中で整理しておきたい

🔧 Sys.setenv()とOptions()関数の使い分け

まず、Rで利用者が関数の挙動を制御する関数としてSys.setenv()options()の2つがある。2つの関数の違いは微妙なところで、うまく説明できないけど個人的にはoptions()が関数やパッケージの挙動を制御する値、Sys.setenv()がユーザーの設定として用意しておく値という使い分けをしている。

Sys.setenv()関数は環境変数を定義する関数で、Sys.getenv()関数と対をなしている。環境変数って何かというとRのパッケージを管理するパスや諸々のプログラムについて指定しているものみたいな。現在の環境設定を確認するにはSys.getenv()を実行する。するとだらだらと環境設定が表示される。自分の場合は

# 例えば
Sys.getenv(c("R_HOME", "R_LIBS_USER", "R_PLATFORM"))
##                                      R_HOME 
## "/Library/Frameworks/R.framework/Resources" 
##                                 R_LIBS_USER 
##                   "~/Library/R/3.2/library" 
##                                  R_PLATFORM 
##                 "x86_64-apple-darwin13.4.0"
# 現在設定している環境変数
names(Sys.getenv()) %>% head(10)
##  [1] "__CF_USER_TEXT_ENCODING"    "ANALYTICS_SECRET"          
##  [3] "ANALYTICS_TOKEN"            "Apple_PubSub_Socket_Render"
##  [5] "DISPLAY"                    "DYLD_FALLBACK_LIBRARY_PATH"
##  [7] "EDITOR"                     "ESA_IO_TOKEN"              
##  [9] "ESTAT_TOKEN"                "GIT_ASKPASS"

のようになっている。環境変数を変更するにはSys.setrenv()を使う。ただパスとかR本体の挙動を変えてしまうような項目があるので、適当な値を与えるとあまりよくない。

Sys.getenv("LANGUAGE")
# En# Error: object 'あ' not found
Sys.setenv(LANGUAGE = "Ja")# エラー: オブジェクト 'あ' がありません

今度はoptions()options()関数はdigits()などの設定で使ったことがある人が多いと思う。現在の設定を確認するためにはgetOptions()あるいはoptions()関数にオプションとなる項目名を渡す。また、オプションの一覧は.Options()というオブジェクトに保存されている。

# 現在のオプション項目
names(.Options) %>% head(10)
##  [1] "prompt"         "continue"       "expressions"    "width"         
##  [5] "deparse.cutoff" "digits"         "echo"           "verbose"       
##  [9] "check.bounds"   "keep.source"
getOption(x = "width")
## [1] 75
getOption("defaultPackages")
## [1] "datasets"  "utils"     "grDevices" "graphics"  "stats"     "methods"
getOption("prompt")
## [1] "> "

options()でオプションの値を変えることができる。

# 数値の表示桁数を制御するオプション
options("digits")
## $digits
## [1] 7
pi
## [1] 3.141593
# 変更する
options(digits = 10)
pi
## [1] 3.141592654

オプションで設定しておくと、都度関数の引数として機能するものもあるので、使い方によっては便利。例えば、データフレームを作成するdata.frame()関数では文字列をfactor型としているがoptions()で既定値を指定しておくとそれを防ぐことができる。ただ個人的には関数の引数の挙動を操作するのは関数内で行いたいのであまり設定していない。

df_pri <- data.frame(cure = c("キュアフローラ", 
    "キュアマーメイド", "キュアトゥインクル", 
    "キュアスカーレット"))
df_pri$cure %>% class()
## [1] "factor"
options(stringsAsFactors = FALSE)
df_pri <- data.frame(cure = c("キュアフローラ", 
    "キュアマーメイド", "キュアトゥインクル", 
    "キュアスカーレット"))
df_pri$cure %>% class()
## [1] "character"

オプション値はパッケージの関数によって与えられているものもある。

# パッケージが設定している項目もある
names(.Options) %>% grep("^dplyr", ., value = TRUE)
## character(0)
library(dplyr)
names(.Options) %>% grep("^dplyr", ., value = TRUE)
## [1] "dplyr.strict_sql"    "dplyr.print_min"     "dplyr.print_max"    
## [4] "dplyr.show_progress"

👍 関数の仮引数として設定しておくと便利

というわけで本題。

Rで各種ウェブAPIをラップしたパッケージはウェブサービスが提供するAPIを使用したり制限回数を管理したりするためのAPIキーなりアクセストークンを発行したりしている。例えばR上でGoogle Analytics APIを使うための{RGA}パッケージでは認証を行うauthorize()関数の引数で以下の値を用意している

formals(RGA::authorize)
## $username
## getOption("rga.username")
## 
## $client.id
## getOption("rga.client.id")
## 
## $client.secret
## getOption("rga.client.secret")
## 
## $cache
## getOption("rga.cache")
## 
## $reauth
## [1] FALSE

ここで注目すべきなのはいくつかの引数の仮引数の値としてgetOption()で与えられる項目があることだ。こうした仮引数を用意しておくことで、利用者がoptions()として該当する値を与えている場合には関数内で入力する手間を省くことができる。また、外部にコードを公開する際もその値を隠すことができる。

# options()で設定していない場合(デフォルト)
# それぞれの引数で値を渡す必要がある
RGA::authorize(username = "<user name>", 
    client.id = "<client id>", client.secret = "<client secret key>")
# options()で値が設定されている場合
# 関数内の仮引数がそのまま使われるので省略可能
RGA::authorize()

またR上でSlackへ投稿ができる{slackr}パッケージでは設定を読み込むためのslackr_setup()関数があって、今度はSys.setenv()の値を参照するようになっている

他にもいろいろなパッケージでこうした設定を見かける。getOptions()で値を得るか、Sys.setenv()から読み込んでくるか、というのは開発者によって違っているので曖昧な気がする。自分はウェブAPIのためのAPIキーなどの管理はSys.setenv()でやっていきたい。

というわけで、こうした値は.Rprofileに書いておくといちいち入力する必要がなくなるので便利。その一方で、GitHubなどに環境設定の値を含んだ(なんかのパスワードとか暗証番号とか)をあげちゃうとちょっと⚠️危険。まあ、R程度で冒頭のクラウド破産なんてことはしないだろうけど(🏁フラグ。

📚 参考