cucumber flesh

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

否定条件: stringr 1.4.0で追加された引数negageとpurrrを使った文字列以外のさまざまなデータへの適用例

要約

f:id:u_ribo:20190224083558p:plain

  • 条件に一致しない要素に対してTRUEを返す否定条件をさまざまなデータ型に適用する例を紹介します
    • 文字列データに対しては stringrパッケージ(バージョン1.4.0以降)またはstringiのnegate引数を利用する
    • purrrパッケージのnegate()compose()を用いることで、文字列以外のデータにも適用可能な否定処理を実行できる
  • これらの関数を使った処理では、!演算子を使わずに記述できるためパイプ処理とも親和性が高い

はじめに

ある条件のもとで一致しない、つまり偽 (FALSE)となる要素を 真 (TRUE) とするためにRでは!演算子を使います。

# 1は2と等しくない
1 != 2
## [1] TRUE

また、grep(invert = )のように否定条件の指定が引数で与えられている関数もあります。

# 文字列dを含まない要素の位置を参照する
grep("d", c("abc", "def", "ghi"), invert = TRUE)
## [1] 1 3

この記事ではこのような!演算子を使わない否定条件の処理をさまざまなデータに対して実行する例を紹介します。まずは先日(2月10日)、バージョン1.4.0に更新されたRでの文字列処理の定番であるstringrパッケージによる文字列データへの適用例をみます。続いて、文字列以外の多様なデータ形式に否定処理を行うためにpurrrパッケージの関数を使った処理を示します。

stringr 1.4.0に実装されたnegate引数

stringr 1.4.0では、str_detect()などの主要な関数に引数negateが追加されました。この引数は与えられたパターンに一致しない要素を検出する機能をもちます。引数には論理値を指定し、TRUEを与えた際に否定条件を成立させます。

3つの要素を含んだ文字列ベクトルについて、“a”を含まない“melon”が TRUE となる処理を行います。まずは否定ではない、つまり“a”を含む要素にTRUEを返す処理を確認します。

library(stringr)

fruits <- c("banana", "apple", "melon")

str_detect(fruits, "a")
## [1]  TRUE  TRUE FALSE

目的はこの逆です。引数negateにTRUEを与えて結果をみましょう。

str_detect(fruits, "a", negate = TRUE)
## [1] FALSE FALSE  TRUE

否定条件に一致する要素である“melon”に対してTRUEを得ることができました。

これまでの否定条件は、!演算子を処理の先頭に置くことで指定していました。しかしこの形式だと次のようにパイプ処理と相性がいまいちなようにに思えます。

!str_detect(fruits, "a")
# パイプ処理で否定処理を記述する例
# 1. {}で無名関数化する
fruits %>% {
  !str_detect(., "a")
}
# 2. purr::map_lgl()を使う
fruits %>% 
  purrr::map_lgl(~ !str_detect(.x, "a"))

negate引数が実装されたことで適用する関数の中で処理が完結するようになります。negate引数はもともとstringiパッケージにあったものだそうです(初めて知りました)。stringrパッケージに実装してくれた id:yutannihilation さんに感謝です。

# stringiにもnegate引数は存在する
stringi::stri_detect(fruits, regex = "a", negate = TRUE)

purrrパッケージで否定条件を成立させる

さて、文字列の場合にはstringrでnegate引数を利用すれば良いという話でした。それでは、文字列以外でnegateのような否定の処理を行うにはどうすれば良いでしょうか。シンプルな答えは!演算子を使うことです。ですがここではpurrrパッケージに用意されたnegate()compose()による否定処理を紹介します。

library(purrr, warn.conflicts = FALSE)

まずはnegate()です。この関数は、関数を生成する特殊な関数です。具体的には記述した条件を否定する関数を生成します。引数.pに成立させたくない条件を記述します。例えばNULLでない要素を与えた時にTRUEを返してほしい際は下記のようにします。

fn <- 
  negate(is.null)

fn
## function (x) 
## {
##     !.Primitive("is.null")(x)
## }

!is.null(x)として機能する関数ができました。引数に値を渡して実行結果を確認してみましょう。

# is.null(x) が成立せず FALSE となります
fn(x = NULL)
## [1] FALSE
list(NULL, 1, "a") %>% 
  purrr::map_lgl(~ fn(x = .x))
## [1] FALSE  TRUE  TRUE

便利ですね。では次はnetage()を使って数値が素数でないかを判定したいと思います。primesパッケージに素数かどうかの判定をするis_prime()があるのでそれを用います。

# 1から6までの実数を用意します
nums <- 
  seq(1L, 6L)

primesには素数である場合にTRUEとなる関数が用意されています。その否定処理は次のように!で実現できますがnegate()で同じ処理ができます。

primes::is_prime(nums)
## [1] FALSE  TRUE  TRUE FALSE  TRUE FALSE
nums %>% 
  negate(~ primes::is_prime(.x))()
## [1]  TRUE FALSE FALSE  TRUE FALSE  TRUE

ここで注意するのがnegate(.p = )()のように括弧が後ろに付いている点です。これはパイプ処理をしているせいもありますが、この書き方は見慣れません (パイプ処理を使わない場合は negate(~ primes::is_prime(.x))(nums) とします)。この書き方が嫌な場合は文字列の処理で示したようにmap_lgl()で記述することも可能です。

nums %>% 
  map_lgl(
  negate(~ primes::is_prime(.x)))

map()で各要素に対して処理を適用するようにしているのでやや冗長に感じます。それではpurrrでの否定処理の2番目の例としてcompose()を紹介しましょう。compose()(g•f)(x) = g(f(x))として機能する合成関数を生成する関数です。この合成関数の機能を使うことで、関数を作る必要が生じますが、コードを見通しの良いものにできます。

is_false_prime <- 
  # 作成する関数 (unmatch_str_a()の第一引数に与えた値が str_detect)
  compose(`!`, ~ primes::is_prime(x = .x))
# negate()を使う場合は compose(negate(~ primes::is_prime(.x)))
is_false_prime(nums)
## [1]  TRUE FALSE FALSE  TRUE FALSE  TRUE

論理値ではなく、マッチしなかった値そのものがほしい場合はkeep()で要素を取り出せます。stringrで文字列データを扱う場合はstr_subset()です。

nums %>% 
  keep(negate(~ primes::is_prime(.x)))
## [1] 1 4 6
nums %>% 
  keep(is_false_prime)
fruits %>% 
  str_subset("a", negate = TRUE)
## [1] "melon"

それでは!

DockerイメージでGitHub上のRパッケージのインストールを行う際の注意: GitHub Personal Access Tokenの設定

前回の記事では、CRANやそれ以外のGitリポジトリからRパッケージのインストールを行うremotesパッケージの特徴と、GitHubで管理されたパッケージのインストールを行う際のAPIに関する注意を書きました。

uribo.hatenablog.com

今回はさらにニッチな内容だと思いますが、DockerでRStudio Serverを利用する際に、GitHub上のパッケージをインストールする必要がある時のtipsとなります。昨年末、仕事納め間際にハマってしまった障害とその解決策の備忘録でもあります。

要約

  • DockerコンテナでGitHubからインストールするRパッケージがある場合もGitHub Personal Access Token (PAT) の設定が必要
  • Dockerコンテナ上のRStudio Serverを立ち上げる際は/usr/local/lib/R/etc/RenvironにPATを保存する
  • PATをバージョン管理の対象、dockerイメージ間で共有しないようにするには.envargsコマンドを活用する

f:id:u_ribo:20190115063923p:plain

続きを読む

RパッケージをGitHubからインストールする場合はPersonal Access Tokenを取得・設定しておきましょう

RのパッケージはCRANやBioconductorといったリポジトリに加えて、GitHubやGitLab、Bitbucketといった分散型Gitリポジトリサービス(リモートリポジトリ)からインストールすることが可能です。リモートリポジトリには開発版のものや、CRAN等に公開されていないパッケージがあり、これらを利用する場面がしばしばあります。こうしたリモートリポジトリからRパッケージのインストールを行う方法は複数ありますが、remotesパッケージはこれらを含めたRパッケージをインストールするのに適した下記の機能を備えており便利です1

  • 依存パッケージの追加インストール
  • GitHub、GitLab、BitBucketを含めたGitリポジトリからのインストール (install_github() など)
  • ローカルファイルやURL指定でのインストール (install_local(), install_url())
  • CRANパッケージのバージョンを指定したインストール (install_version())

remotesパッケージの使い方に関してはRAEDMEに詳しく書かれていますので、ここではその説明を省略し、remotesパッケージでGitHub上のパッケージをインストールするinstall_github()での認証設定について記述します。

install_github()の挙動

前述の通り、install_github()GitHubリポジトリ名とリポジトリ管理ユーザの名称を引数に渡してパッケージのインストールを行う関数です。

library(remotes)

install_github("<user>/<repository>")

この関数を実行するとGitHub API (remotesパッケージが利用しているのはv3) を経由して必要なファイルがダウンロードされます。このAPIは、公開リポジトリに対しては誰でもリクエストできるようになっていますが、非公開のプライベートリポジトリやアカウントに関する操作を行う際は認証が必要になります。認証を行わないAPIの実行では次の制約があります。

install_github()では、この認証情報を引数auth_tokenで制御しています。初期値にはgithub_pat()を実行した際の値が使われます。試しにremotes:::github_pat()を実行してみましょう。

# github_pat()はパッケージ内部で利用される関数のため:::演算子を使って呼び出します。
remotes:::github_pat()
# NULL

上記のようにNULLが返却された場合は認証が行われていないことを示します。そのため、短時間に大量のインストールを行う場合やプライベートリポジトリ上のパッケージをインストールする時にはinstall_github()の実行は失敗します。

GitHub PATの取得と設定

では認証を行い、install_github()の機能を十分に活用できるようにしましょう。install_github()の認証は、Personal Access Token (PAT)という形態を採用しています。そのためGitHubからトークンを発行する必要があります。これにはusethis::browse_github_pat()を使います。

この関数を実行するとウェブブラウザが起動し、トークンに対して許可する操作をチェックした状態の次の画面が表示されます。

f:id:u_ribo:20190111073913p:plain

usethisパッケージのbrowse_github_pat()は、install_github()で必要な操作に対してあらかじめチェックがついた状態を用意してくれているので、問題がなければGenerate Tokenを押して次に進みます。すると、トークン発行画面に遷移します。ここで表示された値がPersonal Access Tokenとなります。この値は一度しか表示されないので、コピーを忘れずにしておきましょう(もし忘れてしまった場合はPersonal Access Tokenを削除して再発行して同じ手続きをとればOKです)。

コピーした値をinstall_github(..., auth_token = "<コピーしたPAT>")に渡して実行すれば認証つきの状態でGitHub APIへのリクエストが行われます。しかし、実行のたびにトークンを記述するのは面倒臭く、セキュリティの面でもよろしくないため、次に環境変数として扱えるようにしておきましょう。

remotes:::github_pat()環境変数に定義されている"GITHUB_PAT"の値をPATとして扱います。これによりinstall_github()を行った際にPATの使い回しが可能になり、PATの値自体は表示されなくなります。

環境変数の定義はSys.setenv(GITHUB_PAT = "<コピーしたPAT>")としておけば良いですが、セッションを終了しても環境変数を保持しておくためにRenviron.Rprofileに記述しておくのが良いです。ここでは.rprofileに記述する方法を紹介しますが、Renvironの場合も同様の記述でOKです。

usethis::edit_r_profile()を実行します。Rプロジェクトごとに異なる.Rprofileを用意している場合はusethis::edit_r_profile(scope = "project")にしてください。関数を実行すると.Rprofileを編集するための画面またはタブが表示されます。

ここにSys.setenv()GITHUB_PATを設定する記述をして保存します。これにより、次のセッションから環境変数GITHUB_PATが有効になります。

Sys.setenv(GITHUB_PAT = "<コピーしたPAT>")

Rを再起動して、remotes:::github_pat()にPATの値が表示されればGitHub PATの設定は完了です。次回は、このGITHUB_PATをDockerで起動したRStudio Serverへ反映させる方法を書く予定です。


  1. devtoolsパッケージに備わっている機能から、パッケージのインストールを行う関数を独立、発展させたパッケージです。devtools 2.0.0ではremotesパッケージに加えて、いくつかの機能をまとめた関数がdevtoolsから独立してパッケージ化されました。 ref) https://cran.r-project.org/web/packages/devtools/news/news.html