まだ厨二病

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

最近お気に入りのRStudio(ver.1.1.423)の機能: ファイル内検索とTODOハイライト

先日、RStudioのバージョン1.1.423がリリースされました

v1.1からの更新内容については先日開催されたrstudio::conf 2018にて、開発者の一人であるKevinが発表しているので詳細はこちらをご覧ください。国内ではぞうさんこと@kazutanが記事にしています。

RStudio v1.1 – What’s New

kazutan.github.io

そんな開発が活発なRStudioですが、最新のv1.1.423で利用可能な個人的におすすめしたいプロジェクトの運用がしやすくなる2つの機能について紹介します。

プロジェクト中のファイル内検索

どうしてこの機能に気がつかなかったのだろう。この機能を使う前と後では私のRStudioの使い方は一変してしまいました、そのくらいのお気に入りの機能。

プロジェクトやパッケージが肥大してくると、全てのRファイルや関数について把握するのは難しくなります。「このデータファイルはここで使っている」、「この図はあそこのRファイルで出力している」などなど。対象のファイルの名前やパス、オブジェクト名を変更した際、影響を及ぼす範囲を把握しきれていないと変更が追いつかず、ある日エラーになっている、という経験が皆さんにもあるでしょう。

そんな時にこの機能を使うと、Rプロジェクト中のファイルから、任意の文字列を検索し、どのファイル、どの部分で記述されているかがわかって大変便利です。特に誰かのパッケージやプロジェクトに首を突っ込む時、自分が変更した箇所が他のファイルに反映されているかを確かめるときに重宝します。

ショートカットキーが当てられていて、macOSでは「コマンド + シフト + F」で検索用の画面が起動します。正規表現の利用や対象のパス、ファイル形式について設定できて高機能です。

f:id:u_ribo:20180221071044g:plain

検索結果は、RStudioのコンソールパネルのタブとして表示されます。検索結果を見ると、検索した文字列がどこで使われているかがハイライトされます。また、特定行をクリックすることで、その部分をソースパネルで編集することができます。

全てのファイルの中身を一度に変更することはできませんが、こうして影響のある部分を書き換えていくことで、変更によるトラブルを防げるようになります。

TODOハイライト

こちらはid:niszetさんのつぶやきで知りましたが、Rファイル中で特定の文字列がハイライトされるようになっています。特定の文字列とは"TODO", "FIXME"の2つです

github.com

コード中にこうしたコメントを残しておくことで、視認性・コードの保守性が向上しますね。

f:id:u_ribo:20180221073136p:plain

todorパッケージ

github.com

この機能と組み合わせて、todorパッケージが便利です。このパッケージは、プロジェクト中の"TODO"や"FIXME"などの特定の文字列を検索します。こちらが対応しているのはRStudioのものよりも多いです。

CRANには登録されていないため、利用の際にはGitHub経由でインストールを行います。

remotes::install_github("dokato/todor")

f:id:u_ribo:20180221073343g:plain

todor::todor()およびRStudio Addinsから実行します。

RStudio v1.2に向けて

GitHubのNEWSを見ると、どうやら次のバージョン(v1.2)ではreticulateパッケージによるPythonとの連携機能の強化が行われている様子。

また、daily build版だけなのかもしれませんが、アイコンが...

Enjoy!

RStanのおさらいをしながら読む 岩波DS1 (ややアップデートした2018年版)

最近、以前書いた岩波DSの久保さんパートのおさらい記事を久保さん本人がRTしていたのを見て(ああ、あの頃はまだ大学院生をやっていたのだと懐かしくなったと同時に)、移り変わりのあるRコードは今も動くのか心配になった。結論を先に言うと、RPubsに書いたコードは現在の環境でも問題なく動作した。が、その頃にはtidyverseも十分に整備されていなかったし、私のR技術力も低く、ヘンテコなコードを書いていたのが気になる。そこで、新しいパッケージも出ていることだし、2018年版ということで再度実行してみた、というのがこの記事の顛末である。

元の記事でもstanコードは id:StatModeling さんの記事に書かれているものをコピーしたが、代入に「<-」を使うとstanが警告文を出すので「=」に書き換えておいた(非推奨なだけで実行自体はしてくれる。<-が非推奨〜云々は「StanとRでベイズ統計モデリング」でも書かれている)。

StanとRでベイズ統計モデリング (Wonderful R)

StanとRでベイズ統計モデリング (Wonderful R)

以下、変更した部分だけを書く。結果は変わらないので内容ではない。

まず例題として用意されているデータ(久保さんのウェブサイトからダウンロード)をdplyr::glimepse()で確認しているが、これと同じデータの確認は、私は最近skimrパッケージのskim()を使ってやることが多い。

skimr::skim(d)
# Skim summary statistics
#  n obs: 20 
#  n variables: 6 
# 
# Variable type: factor 
#  variable missing complete  n n_unique             top_counts ordered
#      pref       0       20 20       10 A: 2, B: 2, C: 2, D: 2   FALSE
# 
# Variable type: integer 
#  variable missing complete  n mean   sd p0   p25 median p75 p100     hist
#         N       0       20 20 54.1 2.77 49 52.75   54    56   59 ▃▃▂▇▆▃▂▅
#         X       0       20 20  0.5 0.51  0  0       0.5   1    1 ▇▁▁▁▁▁▁▇
# 
# Variable type: numeric 
#  variable missing complete  n   mean   sd     p0    p25 median    p75   p100     hist
#       Age       0       20 20   0.5  0.51   0      0      0.5    1      1    ▇▁▁▁▁▁▁▇
#    mean.Y       0       20 20 155.3  2.8  151.36 153.06 155.69 157.22 161.71 ▃▇▁▂▇▁▁▁
#      sd.Y       0       20 20   2.97 0.23   2.45   2.9    3.06   3.11   3.21 ▂▁▂▁▁▂▇▆

これだけでも数値からなる変数のデータのばらつきが把握できて大変良い。なおskimrパッケージの良さは id:niszet 氏が語ってくれている。

niszet.hatenablog.com

skimrはさらにここからdplyrパッケージの関数と組み合わせて利用することでグループ化したデータの要約や並びかえも行える。例えば、都道府県 pref 、測定回数 Age (1,2回目の測定をそれぞれ0と1で示す)ごとの平均身長 mean.Yの中央値を求め、平均身長の順に並びかえて表示するには次のようにする。dplyr(あるいはtidyverse)は、モデリングを行う際でも使う機会があるので最初に読み込んでおくと良いだろう。

library(dplyr)

# skimr::skim()以外はすべてdplyrパッケージのデータ操作関数を利用
d %>%
  # 必要な変数にのみ制限
  select(pref, Age, mean.Y) %>%
  # 都道府県、測定回数でグループ化し
  group_by(pref, Age) %>%
  # 統計量を求める
  skimr::skim() %>% 
  ungroup() %>% 
  # 統計量から中央値を抽出
  filter(stat == "median") %>% 
  # 平均身長の中央値が低い順に並びをかえる
  arrange(value)
# # A tibble: 20 x 8
#    pref    Age variable type    stat   level value formatted
#    <fct> <dbl> <chr>    <chr>   <chr>  <chr> <dbl> <chr>    
#  1 A      0    mean.Y   numeric median .all    151 151.36   
#  2 B      0    mean.Y   numeric median .all    152 151.56   
#  3 C      0    mean.Y   numeric median .all    152 152.22   
#  4 I      0    mean.Y   numeric median .all    153 152.67   
#  5 G      0    mean.Y   numeric median .all    153 152.98   
#  6 D      0    mean.Y   numeric median .all    153 153.09   
#  7 E      0    mean.Y   numeric median .all    153 153.22   
#  8 H      0    mean.Y   numeric median .all    153 153.27   
#  9 F      0    mean.Y   numeric median .all    153 153.31   
# 10 J      0    mean.Y   numeric median .all    155 155.37   
# 11 D      1.00 mean.Y   numeric median .all    156 156      
# 12 I      1.00 mean.Y   numeric median .all    157 156.82   
# 13 B      1.00 mean.Y   numeric median .all    157 156.83   
# 14 C      1.00 mean.Y   numeric median .all    157 157.08   
# 15 F      1.00 mean.Y   numeric median .all    157 157.22   
# 16 E      1.00 mean.Y   numeric median .all    157 157.24   
# 17 A      1.00 mean.Y   numeric median .all    157 157.27   
# 18 G      1.00 mean.Y   numeric median .all    158 157.81   
# 19 H      1.00 mean.Y   numeric median .all    159 158.95   
# 20 J      1.00 mean.Y   numeric median .all    162 161.71

glm()の部分は依然としてbroomパッケージを使っているので変更なし。

さて以前はstanコードのdataブロックに渡すデータをリストで作成したが、ここも変わりがない。強いて言うなら、 list()の代わりにtibble::lst()にすることや、ユニークな都道府県の数をカウントした値(length(unique(d$pref)))はよく使うので、別なオブジェクトにしておくと良いだろう。また、length(unique(x))の部分はn_distinct(x)に書き換えるのが新しいやり方。もしくはこうした定数は別なファイルに記述して、configパッケージで必要な時に参照する方法もある。

n_pref <- n_distinct(d$pref)

# list
tibble::lst(
  N_r     = 2,
  N_pref  = n_pref,
  Mean_Y  = t(matrix(d$mean.Y, n_pref, 2)),
  Sigma_Y = t(matrix(d$sd.Y / sqrt(d$N), n_pref, 2)),
  X       = t(matrix(d$X, n_pref, 2)),
  Age     = t(matrix(d$Age, n_pref, 2)))

suryu.me

blog.hoxo-m.com

stanコードのビルド、MCMCサンプリングの実行も変わりがない。記事を書いた時からStanは色々なアップデートがあっただろうが、動いてよかった..。

最後にサンプリング結果から推定したパラメータの可視化を行なっているが、ここもそのままのコードが動作する。ただしgridExtraを使った複数のggplot2作図の結合はちょっと古いかつ、若干わかりにくいのでeggあるいはpatchwork (2018年2月現在 GitHub版のみ利用可能 https://github.com/thomasp85/patchwork) パッケージで代替する。eggの方はgridExtraの開発者が中心に開発していて正式な後継っぽいのだが、個人的にはpatchworkの方が好み。

事後分布のヒストグラムやら、ggmcmcで作図するところまでは同じ。まずはeggを使った方法から。

library(egg)
(p <- ggarrange(p1, p2, p3, p4, widths = 1:2))
library(patchwork)
(p <- p1 + p2 + p3 + p4 + plot_layout(ncol = 2))
# egg, patchwork いずれも保存は同じ方法
ggsave(filename = "samping_parameters_result.png",
       p, 
       width = 6, height = 4)

f:id:u_ribo:20180213002849p:plain

ああ、まだ「ベイズモデリングの世界」が買ったまま読めていない...。

他にも、私が追えていないだけで更新できる内容があるかもしれないが、2018年版ということでひとまずは Enjoy!

ベイズモデリングの世界

ベイズモデリングの世界

(こっちもよろしくね😼)

hereパッケージの導入でファイル参照のパス問題の悩みを解消

去年から気になっていたものの、その利点や使い道について理解できていなかったhereパッケージ、ようやくにして少しわかった気がする。今は声を大にして言える。hereは良いぞ。hereをプロジェクトに導入することで、これまでにあったWindows - macOS間でのパス表記の違いや作業ディレクトリの階層性によるパスが正しく指定できないといった課題を解決することができるかもしれない。と言う訳で布教用の記事を書く。また、テスト用のリポジトリも用意しているので、hereを使ってそのご利益を享受されたい方はこちらをcloneなりダウンロードするなりして手前で実行してほしい。

github.com

参考) https://github.com/jennybc/here_here

問題

Rを使っていて、ファイルの保存先を指定するのに失敗してイラつきを覚える、そんな経験は誰しもがあるだろう。Rでは、作業ディレクトリ (working directory。getwd()で現在の作業ディレクトリを確認する)と呼ばれる基盤となるディレクトリが設定される。これにより目的のファイルまで簡単にたどり着くことができるなどの利点もあるのだが、複数のプロジェクトを運用している場合、プロジェクトのフォルダをまたいでしまったり、作業ディレクトリを頻繁に切り替える作業が発生してしまう(setwd()により作業ディレクトリは変更される)。皆さんには作図したファイルの保存先にfigフォルダを作っていたのに、全く異なる場所にファイルを保存してしまった、ということがないだろうか?私はしばしばそんな経験をした。ここに書いたにもパスに関する多様な問題があるだろうが、もっとも頻繁に遭遇する問題だと思われる2点について、次にまとめる。

RStudioの一機能であるRプロジェクトは、プロジェクトに応じた作業ディレクトリの構築を可能にする。Rプロジェクトであることを示す .Rprojファイルはプロジェクトとして扱うフォルダのトップの階層に置かれ、そこを起点とした作業ディレクトリでプロジェクトを回していくことができる。プロジェクトに関する全てのファイルをプロジェクトの中に置くことで利便性が増し、上記の問題を防ぐことが可能となる。

一方で、ファイルの参照形式はOS間で異なることがあり、参照がうまくできないことがしばしばある。次のコードはWindowsにおいてdataフォルダ中のiris.csvを読み込むための処理だが、これをUNIXベースのOSで実行するとエラーとなる。UNIXではフォルダの区切り文字に/を使うのが習慣であり、他方Windowsでは区切り文字に/の他、\\ ()の使用が認められるためである。

# Windowsで実行可能な読み込みはUNIX環境ではエラーとなる
read.csv("data\\iris.csv")

またプロジェクトの外部で、ドライブからパスを指定しなくてはいけない時も、Windowsでは次のように指定する。

# ドライブからファイルを参照する
read.csv("C:/documents/uribo/here_demonstration/data/iris.csv")

UNIXではユーザディレクトリは /home/<username>であったり、/Users/<username>となっていたりするがホームディレクトリは ~で表記を省略することもできる。また省略されたパスをpath.expand()により完全な形で示せる。

# ホームディレクトリからファイルを参照する
read.csv("~/here_demonstration/data/iris.csv")
path.expand("~/here_demonstration/data/iris.csv")
## [1] "/home/rstudio/here_demonstration/data/iris.csv"

これはプロジェクト機能をもってしても残り続ける課題である。

またRmdファイルをプロジェクト内のフォルダに保存しておくと、プロジェクトの作業ディレクトリよりも深い階層でRコードを実行することになるため、コンソールでの実行とRmdファイル中のコードの記述が異ってしまう。具体的には、data/iris.csvを参照するには、一つ階層を遡って../data/iris.csvとしなくてはいけない。また、Rmdファイルをより深い階層においた場合、さらに遡って../../data/iris.csvと記述することとなる。これに対応するのは手間である。

hereによるファイル参照

こうした課題を解決するために、hereパッケージは優れた機能を提供する。hereはCRANNからインストールができる。この記事を書いた際のバージョンは0.1である。Windows環境にインストールした際、backportsパッケージの古いバージョンをインストールしておかないといけないとエラーになったので、参考のリンクを貼っておく。

library(here)
## here() starts at /home/rstudio/here_demonstration

hereパッケージを読み込むと、上記のメッセージが出力される。出力先のパスが基点を示している。Rプロジェクトが同じ階層内にある場合、その階層が基点にみなされる。

hereの考え方としては、あるディレクトリをプロジェクトとみなした場合、ファイルの参照を常にプロジェクトの基点から表記する、というものである。関数hereはパスを表現するのに用いられるが、従来の区切り文字で表記する方法の他にRの文字列としてフォルダ名を与えていくことでパスを表現することもができる。

# 従来のパス表記方法
here("data/iris.csv")
## [1] "/home/rstudio/here_demonstration/data/iris.csv"
# 文字区切りでフォルダ、ファイルを示す
here("data", "iris.csv")
## [1] "/home/rstudio/here_demonstration/data/iris.csv"

パスを出力するだけなので、ファイルの有無は気にしない。

file.exists("aaa/bbb/ccc.csv")
## [1] FALSE
here("aaa", "bbb", "ccc.csv")
## [1] "/home/rstudio/here_demonstration/aaa/bbb/ccc.csv"

c()を使ってこんなこともできる。複数の文字列を指定した際は各要素の位置に対応したパスが出力される。

here("data", c("aa", "bb"), "iris.csv")
## [1] "/home/rstudio/here_demonstration/data/aa/iris.csv"
## [2] "/home/rstudio/here_demonstration/data/bb/iris.csv"
here("data", c("aa", "bb"), c("iris.csv", "mtcars.csv"))
## [1] "/home/rstudio/here_demonstration/data/aa/iris.csv"  
## [2] "/home/rstudio/here_demonstration/data/bb/mtcars.csv"

here()がその効力を発揮するのは、特に深い階層にあるRコードやRmdファイルを実行する時である。サンプルリポジトリに作成したhoge/fuga/sample2.Rmdからdata/iris.csvを参照するには../../data/iris.csvと記述しなくてはいけなかったところをhere("data", "iris.csv")の記述に置き換えられる。data/iris.csvを参照するには、常にhere("data", "iris.csv")とすれば良いのである。

dim(read.csv(here("data", "iris.csv")))
## [1] 150   6
# 作業ディレクトリを変更しても data/iris.csvの参照方法は変わらない
setwd("hoge/fuga/")
# The working directory was changed to /home/rstudio/here_demonstration/hoge/fuga inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
dim(read.csv(here("data", "iris.csv")))
## [1] 150   6
# 作業ディレクトリの変更は維持される
getwd()
## [1] "/home/rstudio/here_demonstration/hoge/fuga"

なお、RMarkdownのチャンクでは、チャンク内で完結し、チャンクが切り替わると基点に戻る。

getwd()
## [1] "/home/rstudio/here_demonstration"

素晴らしい!

Rプロジェクトの外部でhere!

hereはRプロジェクトのあるフォルダを基点とするのが基本だが、他にも基点となり得るファイルやフォルダが認められている。GitリポジトリやRパッケージ開発に必要なDESCRIPTIONファイル(^Package:で始まること)などである。加えて、独自の.hereファイルがあるフォルダも基点にすることができる。これはRプロジェクトを用いない分析プロジェクトでは有効だろう。任意の作業ディレクトリでset_here()を実行あるいはpath引数にパスを指定して、.hereを作成できる。

パス問題は、これまで多く悩まされてきたものなので、Rプロジェクトの台頭によって大分ストレスがなくなったが、hereを導入することでさらにストレスフリーで作業を進められそうである。

Enjoy!