読者です 読者をやめる 読者になる 読者になる

StatsFragments

Python, R, Rust, 統計, 機械学習とか

{rbokeh} で Bokeh を R から使いたい

はじめに

BokehPython 以外にも R, Scala, Julia 用のパッケージを提供している。パッケージといっても PythonBokeh と連携するものではなく、Bokeh がブラウザでのレンダリングに使っている Bokeh.js を各言語で扱えるようにするもののようだ。そのため、各パッケージはそれぞれ単体で利用できる。

うち、R 用のパッケージである {rbokeh} を使ってみたい。R には {htmlwidgets} を使った JavaScript 利用の可視化パッケージが多数提供されているが、数が多くて使い方が覚えられない。できれば {rbokeh} だけを使いたい。また、Pythonと見た目が揃えられると嬉しい。

インストール

現時点で CRAN には公開されていないため、GitHub からインストールする。現時点のバージョンは 0.2.3.2。

install.packages('htmlwidgets')
install.packages('devtools')
library(devtools)
devtools::install_github('bokeh/rbokeh')

データの準備

サンプルとして {ggplot2} に含まれる mpg データを利用する。単に {rbokeh} を利用するだけなら {ggplot2} は不要。

library(ggplot2)
library(rbokeh)

dim(mpg)
# [1] 234  11

head(mpg)
#   manufacturer model displ year cyl      trans drv cty hwy fl   class
# 1         audi    a4   1.8 1999   4   auto(l5)   f  18  29  p compact
# 2         audi    a4   1.8 1999   4 manual(m5)   f  21  29  p compact
# 3         audi    a4   2.0 2008   4 manual(m6)   f  20  31  p compact
# 4         audi    a4   2.0 2008   4   auto(av)   f  21  30  p compact
# 5         audi    a4   2.8 1999   6   auto(l5)   f  16  26  p compact
# 6         audi    a4   2.8 1999   6 manual(m5)   f  18  26  p compact

基本的な操作

PythonBokehmatplotlib を連想させる API を持っているが、 {rbokeh} は別の API ( {ggplot2}{ggvis} に近い ) を持つ。そのため、API は言語ごとに覚える必要がある。

mpg データの displ 列と hwy 列を利用して散布図を描画する。

figure() %>%
  ly_points(x = displ, y = hwy, data = mpg) 

f:id:sinhrks:20150725215331p:plain

加えて、 class 列で色分けする。サイズの変更など、利用できるオプションは help(ly_points) で確認できる。

figure() %>%
  ly_points(x = displ, y = hwy, color = class, data = mpg) 

f:id:sinhrks:20150725215338p:plain

色名は文字列で指定することもできる。

figure() %>%
  ly_points(x = displ, y = hwy, color = 'red', data = mpg) 

f:id:sinhrks:20150725215347p:plain

NSE と SE

{rbokeh} では、{ggplot2} のように NSE ( Non-standard evaluation )と SE ( Standard evaluation ) 用の関数を明示的に分けていない。そのため、一部のオプションは以下のようにSEでも動作する。

# scatter
figure() %>%
  ly_points(x = 'displ', y = 'hwy', data = mpg) 
# 出力省略

が、 SE では動作しないものもあるため、現時点では NSE を使ったほうがよさそうだ。

# scatter
figure() %>%
  ly_points(x = 'displ', y = 'hwy', color = 'class', data = mpg) 
# 以下にエラー nchar(opts[[fld]]) :  'nchar()' は文字ベクトルを要求します 

平滑化 / 回帰直線の描画

R のモデルを関数に渡すことで描画できる。lowess に対しては ly_lines を、lm に対しては ly_abline を用いる。この使い分けはちょっと覚えにくい。

figure() %>%
  ly_points(x = displ, y = hwy, data = mpg) %>%
  ly_lines(lowess(mpg$displ, mpg$hwy), color = "red", type = 2) %>%
  ly_abline(lm(hwy ~ displ, data = mpg), type = 2)

f:id:sinhrks:20150725215406p:plain

サブプロット ( facet ) の描画

{htmlwidgets} ブログ中の {rbokeh} 紹介記事 では、サブプロットを lapply + {pipeR} を使って描画する例が記載されている。自分は {dplyr} 好きなので {dplyr} でやりたい。

library(dplyr)

mpg %>% dplyr::group_by(class) %>%
 dplyr::do(dummy = ly_points(figure(width = 200, height = 200), 
                             x = displ, y = hwy,
                             data = ., size = 2)) %>%
  { as.list(.[['dummy']]) } %>%
  grid_plot(nrow = 3, ncol = 3, same_axes = TRUE )

f:id:sinhrks:20150725215420p:plain

様々なプロット

以降、{rbokeh} で描画できるプロットを例示する。

箱ひげ図
figure() %>%
  ly_boxplot(x = drv, y = hwy, data = mpg)

f:id:sinhrks:20150725215429p:plain

ヒストグラム
figure() %>%
  ly_hist(x = hwy, data = mpg)

f:id:sinhrks:20150725215438p:plain

ヒストグラムをグループ別に描画するには少し手間が必要。グループ別に塗り分けるためのカラーパレットを用意し、Reduce で各グループ別のヒストグラムを追加する。

library(scales)
colors <- scales::hue_pal()(length(levels(mpg$class)))
colors <- setNames(colors, levels(mpg$class))
colors
#    2seater    compact    midsize    minivan     pickup subcompact        suv 
#  "#F8766D"  "#C49A00"  "#53B400"  "#00C094"  "#00B6EB"  "#A58AFF"  "#FB61D7" 

ghist <- function(fig, group) {
  ly_hist(fig, x = hwy, data = dplyr::filter(mpg, class == group),
          breaks = seq(10, 45, 5), color = colors[[group]])
}
Reduce(ghist, levels(mpg$class), figure())

f:id:sinhrks:20150725215450p:plain

カーネル密度推定のプロット

グループ分けしたい場合はヒストグラム同様の処理が必要。

figure() %>%
  ly_density(x = hwy, data = mpg)

f:id:sinhrks:20150725215458p:plain

折れ線グラフ
df <- data.frame(x = c(1, 2, 3), y = c(4, 2, 5))
figure() %>%
  ly_lines(x = x, y = y, data = df)

f:id:sinhrks:20150725215508p:plain

地図

地図は {maps} パッケージのデータを利用してプロットができる。

library(maps)
figure() %>%
  ly_map("state", color = "gray")

f:id:sinhrks:20150725215520p:plain

また、 Google Maps を利用することもできる。

gmap(lat = 35.684, lng = 139.753, zoom = 15, map_type = 'terrain')

f:id:sinhrks:20150725215528p:plain

現在 (簡単には) できないもの

R でよく使う種類のプロットを優先しているせいか、以下のような基本的なプロットにはまだ対応していないようだ。

種類 GitHub Issue 備考
棒グラフ #8 頑張れば ly_hist で描ける。
リアプロット #10
ヒートマップ #86 ly_hexbin はある。ly_image で近いことはできるが、軸のラベル付けが手間。
円/ドーナツ

まとめ

{rbokeh}、上に挙げた基本的なプロットが使えるようになれば実用できそうな感じだ。Bokeh.js 側には主要な可視化が揃っているため、他のパッケージと比べると利用範囲が広いこと、見た目を複数言語で揃えられるのがメリットかなと思う。