niszetの日記

細かい情報を載せていくブログ

(R) 階層化Rmdとループとタブの話(階層化Rmdの3回目の話)

階層化するモチベーションはどこにあるのかということで。

さて、RMarkdownの階層化の話の続きです。

RMarkdown、基本的には1つのファイルに収めておいた方があっちこっち見に行かなくて良い分、管理は楽になります。

しかし、コード1000行を超えてくるあたり1で厳しさを感じるようになります。
特に、同じような図を作成するようなケースではコードのコピペが横行しがち2で、ちょっとしたフォーマットをいじるときにも数十か所を頑張って修正するという苦行が発生したりします。

よく言われるように、同じコードが3回出てきたらまとめましょう。Rのコードなら関数、関数のまとまりならパッケージ、RMarkdownのチャンクなら階層化したRMarkdown、といった感じですね。

さて、実際にやってみます。前回からちょっと変えています。まずはiris_child.Rmdの方。

---
title: "plot iris in RMarkdown with params"
author: "niszet"
date:  "`r Sys.Date()`"
output: html_document
params:
  x: "Sepal.Length"
---

/```{r setup, include=FALSE}
library(tidyverse)
library(ggridges)
/```

## plot of `r params$x`
/```{r echo=FALSE, message=FALSE}
iris %>% ggplot()+geom_density_ridges(
  aes_string(x=params$x,y="Species", fill= "Species", 
             point_color="Species", point_fill="Species"),
  position="raincloud", jittered_points=TRUE, alpha=0.3, scale=0.4)+
  theme_bw()
/```

次に親の方ですが、iris_parent.Rmdというファイル名で

---
title: "plot iris in hierarchical RMarkdown with params"
author: "niszet"
date:  "`r Sys.Date()`"
output: html_document
---

/```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
options(knitr.duplicate.label = 'allow')
/```

# Plots of iris {.tabset}
/```{r echo=FALSE, message=FALSE}
envir <- new.env(parent = parent.frame())
envir$params <- NULL
out <- NULL
xs <- c("Sepal.Length", "Petal.Length", "Sepal.Width", "Petal.Width")
for (xx in xs){
  envir$params$x <- xx
  out <- c(out, knitr::knit_child("iris_child.Rmd", envir=envir, quiet=TRUE))
}

knitr::asis_output(out)

/```

のようにしてみます。前回との違いは、呼び出し側でforのループがあります。で、envir$params$x"Sepal.Length", "Petal.Length", "Sepal.Width", "Petal.Width"を順に送り込んでいます。
また、親の方にh1のレベルでPlots of irisと書いて、tabsetの指定をしています。これは

rmarkdown.rstudio.com

にあるように、#のレベルで見て下位のレベルのセクションをタブ化してくれるというものです。これは実物を見た方がわかりやすい。

そんなわけで、親RmdでKnitしますと、

f:id:niszet:20180528132432p:plain

とか、

f:id:niszet:20180528132452p:plain

こんな感じに。

タブを選択して、それぞれの図が表示されているのがわかるでしょうか?このタブはrmarkdownパッケージが入っていないとダメらしいですが、まぁRMarkdown書いている人は多分入ってるよね…。

これにtoctoc_floatthemeなどを設定するとすごい簡単にシャレオツレポートの完成です。逆に言えば、そういう見た目に騙されてはいけないよ…ということですね。

ちなみに、これらのキャプチャ画像は手でやってしまっているので、再現可能性もへったくれもないです。すみません…。webshot使えばいいんですけどね…ブログなので許してください、ということで。

Enjoy!!


  1. 一例。内容や状況によって増減します。念のため。

  2. niszetの体験に基づくアレ

(R) 階層化RMarkdownとパラメータの話(階層化Rmdの2回目の話)

ggridgesでプロットするよ

(1つの記事に色々盛り込むのでトピックが安定しませんね…。)

さて、RMarkdownには色々な機能がありますが、パラメータ渡すことも出来ます。

rmarkdown.rstudio.com

これによって、テンプレート化したRmdに対して、色々とパラメータを変えてレポートを作成することが可能です。

使用例はこちらの@tomotagworkさんの記事が詳しいです。

R Markdownによるレポート生成

ちょっと実際にやってみましょうか。 下記のコードをiris_child.Rmdとして保存します。例によって/はハイライト対策です。使うときは削除してくださいね。

---
title: "plot iris in RMarkdown with params"
author: "niszet"
date:  "`r Sys.Date()`"
output: html_document
params:
  x: "Sepal.Length"
---

/```{r setup, include=FALSE}
library(tidyverse)
library(ggridges)
/```

## plot of `r params$x`
/```{r}
iris %>% ggplot()+geom_density_ridges(
  aes_string(x=params$x,y="Species", fill= "Species", 
             point_color="Species", point_fill="Species"),
  position="raincloud", jittered_points=TRUE, alpha=0.3, scale=0.4)+
  theme_bw()
/```

これで、

f:id:niszet:20180528114257p:plain

のように表示されます。いい感じですね。これは、YAMLヘッダ部分にparams:x: "Sepal.Length"と書いてあるので、実行時にparams$x"Sepal.Length"に置き換わって実行されます。aes_string()は引数を文字列で受け取るタイプのaes()です。これもid:yutannihilationさんに教えていただきました。

ちなみに、tidyevalは来月リリースのggplot 2.3.0から使用できるようです。備えよう。

なお、tidyevalに対応しているかどうかを知る方法はないようです。helpとか読むしかない。(deprecatedもそうですよね)

こういう方法もあるとのこと。

話がそれました。この、パラメータ部分は実行時に書き換えができます。Knitのボタンの▽を押すと[Knit with Parameters]が出てくるので、

f:id:niszet:20180528115344p:plain

押すとデフォルトのパラメータが入った状態で下記のような入力画面が出ます。これを下記のように書き換えて

f:id:niszet:20180528115357p:plain

knitを押すと

f:id:niszet:20180528115612p:plain

このように。ちゃんと横軸が変わっていますね(しかも、タイトルも変わっているのです。詳しくはコードを眺めてみてください)

階層化の話

さて、これを親から呼んでみます。環境を別に渡しても良いし、そのまま呼んでも良いですが。今回は環境を与えます。

---
title: "plot iris in hierarchical RMarkdown with params"
author: "niszet"
date:  "`r Sys.Date()`"
output: html_document
---

/```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
options(knitr.duplicate.label = 'allow')
/```

/```{r}
envir <- new.env(parent = parent.frame())
params <- NULL
params$x <- "Petal.Length"
envir$params <- params
knitr::asis_output(knitr::knit_child("iris_child.Rmd", envir=envir, quiet=TRUE))
/```

これをknitすると、

f:id:niszet:20180528120138p:plain

こんな感じになります。なお、envir$paramsとして与えていますが、コレは必須です。具体的に書かれているところを確認していませんが、子Rmdとして呼ばれる場合はyamlヘッダ内の宣言は無視されるのだと思います(そうしないとスタイルが統一されないし、まぁそうなのかなーと)

前回、

niszet.hatenablog.com

で階層化して変数のやり取りをする際には環境を与えた方が良いという話をしましたが、子Rmd側では「どの変数が親から与えられる変数かわからない」「子Rmdを単体でKnit出来ない」という問題がありました。今回のように、paramsとして与えることで、単体の場合はYAMLヘッダ内(あるいは実行時オプション)で値を与え、階層化する場合は親からparamsというパラメータのリストを受け取ることでこのあたりのIFを統一し、どの変数が他所から与えられる(と期待している)かが明確になります。

階層化するメリットについては次回書きます。まぁ、上記のようにテンプレート化した子Rmdをパラメータを変えることによってその書式を統一したまま使いまわせるってだけで目新しい内容ではないんですが…

Enjoy!!

(R) data.treeパッケージを使って環境のツリー構造を表示する

Rとdataとtreeのぐぐらびりてぃの低さですよ...

data.treeというパッケージがあります。

github.com

vignettesが公開されていて、これを見ると良いのですが、

Introduction to data.tree

Rではtree構造のデータを表示するのはちょっと難儀しますね。ast(abstract syntax tree)であれば、pryr::ast()とかlobstr::ast()とかで表示できますね。

これを、他のデータ構造、listとかdata.frameとかでやりたいわけです。data.treeパッケージでそれが出来ます。といっても使い始めたばかりで細かい部分はわかっていないのですが、上記vignettesを読んでいじってみた範囲でやってみました。

個人的には、environmentの階層構造をパッと表示したいのでして、View()で確認でも良いのですが、単純にパッケージがどの順序でアタッチされたのか?を見るだけならちょっと手間ですよね。

ということで、とりあえず下記のコードで出来ることを確認しました。

library(magrittr)
library(stringr)
rlang::env_parents(environment()) %>% purrr::map(environmentName)  %>% 
  setNames(., nm = stringr::str_remove(., "package:")) %>% 
  purrr::map(function(.) setNames(as.list(x=.), str_remove(., "package:") )) %>% rev %>% 
  data.tree::as.Node()

で、結果は

           levelName
1  Root             
2   |--R_EmptyEnv   
3   |--base         
4   |--Autoloads    
5   |--methods      
6   |--datasets     
7   |--utils        
8   |--grDevices    
9   |--graphics     
10  |--stats        
11  |--tools:rstudio
12  |--magrittr     
13  °--stringr     

となります。上側にR_EmptyEnvが入ってほしいので、revを挟んでいます。
本当は、各パッケージの名前空間の下に入るような構造ですが、それでインデントが深くなって表示しにくいのも微妙だなと思って、特に対応していません。このやり方を継承して、階層構造を正しく?表示をやる場合は、要素を前の要素の子として入れるような、本当に階層構造となったlistを作る必要がありますね。

結構前に思いついたもののやっておらず。ようやく重い腰を上げたのでした…。

個人的にはコレ結構便利…。

追記

ちなみに、View()で確認すると、

rlang::env_parents(environment()) %>% View()

こんな感じ。

f:id:niszet:20180527174224p:plain

対応取れていますね(当たり前ですが…)
View()だと、Autoloadsとtools:rstudioが見てわからない(Show Attributesにチェックを入れて、その環境の要素のname属性を見ればわかります。これを取得しているのが↑のenvironmentName関数というわけですね)ですが、この方法だと確認が楽になりますね。これらの環境自体は知っているのであまり効果はないですが、自作のenvironmentの時には確認に便利です。

Enjoy!!

(R) skimrのヒストグラムがwindows上のknitr下ではうまく表示されない(未解決)

文字コードの問題なのでwindows限定だと思いますが

さて、人類はいつまで文字コードに悩まされなくてはいけないのでしょうか。Windows上でしか動作しないものもあるので、ともに生きる道を探るしかありませんね…。

さて、タイトルの通りskimrヒストグラムknitr環境下ではうまく表示できないという問題があります。未解決です。

同様の質問はググると沢山出てきて、例えば以下にも。

community.rstudio.com

さて、skimrについては以前も記事に書きました(3回目まである)

niszet.hatenablog.com

が、簡単に説明するとskimr::skim()は強化版summary()で、たとえばこんな感じに表示できます。(コンソール上では)

> skimr::skim(iris)
Skim summary statistics
 n obs: 150 
 n variables: 5 

Variable type: factor 
 variable missing complete   n n_unique                       top_counts ordered
  Species       0      150 150        3 set: 50, ver: 50, vir: 50, NA: 0   FALSE

Variable type: numeric 
     variable missing complete   n mean   sd  p0 p25  p50 p75 p100     hist
 Petal.Length       0      150 150 3.76 1.77 1   1.6 4.35 5.1  6.9 ▇▁▁▂▅▅▃▁
  Petal.Width       0      150 150 1.2  0.76 0.1 0.3 1.3  1.8  2.5 ▇▁▁▅▃▃▂▂
 Sepal.Length       0      150 150 5.84 0.83 4.3 5.1 5.8  6.4  7.9 ▂▇▅▇▆▅▂▂
  Sepal.Width       0      150 150 3.06 0.44 2   2.8 3    3.3  4.4 ▁▂▅▇▃▂▁▁

これをRMarkdown中で下記のように(```の前の\ははてなブログ上でのコードハイライトの関係です。コピペする場合は取ってください)

\---
title: "skimr_test"
author: "niszet"
date: "2018年5月26日"
output: html_document
\---

\```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
\```

\```{r}
skimr::skim(iris)
\```

書いてknitrでhtmlへの返還を実行すると、下記

f:id:niszet:20180526232358p:plain

のようになります。これは、中間生成物のMarkdownを残して(keep_mdTRUEに設定する)確認してもこの状態だったので、これはPandocではなくてknitrの仕業ということになります。

この、RMarkdownがhtmlなどのファイルに変換される仕組みの裏側については一年ほど前に🐘さんがTokyoR#61で発表されています。1

R Markdownの内部と テンプレート開発

正直これを理解するのはちょっと難しいのと、理解できていなくてもRMarkdownは使えますのでご安心を。RMarkdown周りを自分好みにカスタマイズしたい…!という方は是非読みましょう。

で、knitr周りを結構調べてはみたものの解決策は特に見つからず。しかし、このままでは文字化けした大量の記号を眺めるだけになってしまうのでなんとかしたい。

これは、skimrの記事の2回目の方法を使って対応できます。

niszet.hatenablog.com

が、今回はもうちょっと綺麗に行きます。exportされていない関数ですが、get_funs()を使うことで一旦関数たちを抜き出して必要な部分を修正して入れ直すという方法です。

exportこそされていませんが、この関数はインターフェースが綺麗に作られているので普段使っても問題はないのではないかなーと思います(が、予告なく変更は入るかもしれませんね)

今回、ヒストグラムが不要なのでhistという要素そのものを消してしまうという方法です。リストの要素を丸ごと消すならNULLの代入ですね。

hoge <- skimr:::get_funs("numeric")
hoge[["hist"]] <- NULL
skimr:::set_options(type = "numeric", env = "functions", append = FALSE, newopts = hoge)
skimr::skim(iris)

これを先のチャンクと入れ替えると、下記のような出力になります。

f:id:niszet:20180526232255p:plain

hist以外にも不要な列があれば、都度NULLを代入して消せばOKです。また、ここではnumericのみ対応していますが、integerなど他の型についても同様に出来ます。

コレ、いつか直るのだろうか…。

追記(2018/6/10)

issueにあがっていた。あと、上記の方法についてはもっと良い方法があるので追って全訂の予定です。

github.com

Enjoy!!


  1. 完全に余談ですが、この回が私がTokyoRに参加した第一回目です。懐かしいですねぇ…。この時は勉強会初参加、ブログも書いて1か月、R触ってようやく半年くらいでしたね。おっさんなのにずいぶんと気持ちがフレッシュだった。いや、今もですけどね。この時にもちょっとだけkazutanさんとも話せたんですよねぇ。大仏様ともここで初めて会った…って話が長くなるな…。

Oculus Go買った。良かった。(語彙力

あまり流行に敏感ではない方ですが…。

さて、5月の頭に日本国内で販売開始されたOculus Goですが、なんかTwitterではよく見るのでなんだかんだで購入してしまいました。
既に購入している方のツイートなどから、住所や名前はアルファベットで、ということと、レンズを太陽に向けるな(凸レンズが入っていて、中が焼けてしまうため)ということを知っている私に死角はなかった。

購入はPayPalだったので、アカウントない場合は作りましょう。最近PayPal率高い気がしますね。便利だからね。 購入から到着まで4-5日ほどでした。はやっ。特に難なく。

セットアップ等

アカウント作成時に認証のメールが来なかったので再度アカウント作成の手順を踏みましたが、それ以外は特に問題なくできました。日本語で解説したサイトもあるので、わからなければ調べれば情報有りますね。マニュアル類は日本語では書かれていないのですが、まぁ大体わかると思うので問題ないかな。

アカウントはFBとは紐づけずに独立していて、しかも検索できなくなっているのでもしniszetとVR空間でつながりが欲しいという変態さんが居ればご連絡ください。

使ってみた感じ。

さて、とりあえずデモムービーを見た程度ですが、思ったことをつらつらと。

思った以上に使い勝手が良い。

ケーブルレスであること

Oculus Goはケーブルレスで独立して運用することが出来ます。まずコレが非常に大きな特長で、数年前のVRゴーグルラッシュ時もこのケーブルの稼働領域の問題がクリアされれば使いやすくなるのに…と言っていました。で、クリアされました。
ケーブルがあるということは動ける範囲がPC周辺になってしまうこと、そしてVR空間にいるのにケーブルが手にあたるなどして現実に帰ってしまうことがあったので、ケーブルレスであることは非常に大きいと思いますね。さんざん言われていると思いますが。僕にも言わせて。

また、音についてもヘッドホンをつけなくて良いです。これは良しあしがあるかもしれませんが。外部環境の音が聞こえるので、ちょっとした危険は回避できるのでは、と。あと付け心地的にも束縛感はなくなって良いかなと個人的には思います。

単体運用

また、PC(やPS4など)に束縛されないので、PC持っていない層にも届きますね。アクティベートはスマホタブレット端末で出来ます。

価格

いうことないほどに安いです。2万円台でこれはヤバいでしょう。流石に10万超えのモノに画質は勝てないものの、それ以外の利便性、入手容易性などがすごいです(語彙力ゼロの感想)

視野角

詳しい構造を知らないのですが、画面の端側を見ようとすると、文字がにじんでいたり、色が分離されて見える気がします。これは、見たい対象物を自分の体の中心に来るようにすることで回避できますが、視線だけで端の方を追うとコレに悩まされます。なので、コレがいやならば首を動かしてみるようにしましょう。疲れますが。 これはレンズのせいなんだろうか?調べよう。

電源がオフにできない…

まだ良く分かっていませんが、なぜか電源をOFFにしても起動しなおしてしまいます。なーぜー。

ってなってたんですが、からあげさんid:karaageのつぶやきで、

知りました。

vr-blogs.com

ほほーそんなことになっていたのですねぇ

とりあえず、Ghost In The Shellの体験版ムービー的なものを見ていましたが、画質の粗さはちょっと気になるものの、十分に没入できるっすね。やはり映画とか見ると良いのではないかと。

個人的にはここからもう一度高価格帯に振れて、この利便性を活かしたまま高性能を実現してほしい。そのためにはOculus Goが普及してプラットフォーム化する必要があるのかな、と(コンテンツ側が揃わないとそもそも、ね)人が増えれば需要が増えて色々と出てくる(成果物だけじゃなく、良いもの悪いもの、いろいろ)と思いますが、これはちょっと久々に流れ・動きを追うのが楽しい。

秋葉原で売られる→家電量販店で売られる、で良いので入手容易性が徐々に上がって普及してほしいなぁ。そして高性能化を、是非。

まだ買って起動して1時間なので、徐々に楽しんでいこう…。何か作れるといいんですけどねー(そっちの才能はない…)

ちょっと前に風疹・麻疹の予防接種受けてきた

結構前のような、そうでもないような。

こんにちは。お仕事忙しいことを言い訳にしているniszetおじさんです。
最近ニュース見てないのですが、まだ麻疹は流行っているんですかね・・・。ちょうど世代的に予防接種1回世代なので、この間打ってきてもらいました。

予約して、過去の病歴(あれば)書いて、それですぐ受けられましたね。ちょっと通常の検診で混んでいたのでその分は待ちましたが。 健康診断で採血することに慣れたので、予防接種は全然痛くないですね…。針の太さか、打つ人の技量の差か...

とりあえずはこれでひと安心かなー。

あと、最近は睡眠時無呼吸症候群かも?と思って耳鼻科にも行ったのですが、こちらは特に問題なしでした(耳鼻科的に)
まぁ、薬は沢山もらいましたが…。ということは別の原因かなぁ。寝苦しいのは確かなんですよね…

まぁ、追々やっていこう。

(R小ネタ) 最近気になったパッケージたち

ゆるい記事です…

使い方を調べて書くのが面倒くさい時間がかかってしまうので、気になったパッケージを列挙するだけの記事です。

bench。r-libでつい最近作られたっぽいのだが、今まで色々ベンチマーク系のパッケージがあったはずで、優位な点は何だろう?可視化なのかな。

github.com

shinytest。rstudio謹製。自動テストってことで、結構重要になるのでは?と思っているのでCRANにリリースされたら使ってみようと思います。 github.com

shinyでのSemantic UIを実現するためのパッケージらしい。これもshiny本格的に使うときに使おうと思っている(shiny、使いどころが難しい)

https://appsilon.github.io/shiny.semantic/appsilon.github.io

RからJupyterが使える(逆か?)らしい。まだ試していない…。Rmdは静的なページになってしまう。shinyだとserverどうするって話が出てくる、そこで、動かせるサンプルとしてjupyterならどうだろう?と思っているので、動かせるサンプルプログラムを書くときに使ってみようかなぁと(社内向けにね) github.com

そんなわけで、最近はパッケージ眺めてニヤニヤしているので良くないですね。使っていきましょう?

Enjoy!!