ggplot2の等高線にラベルをつける

Tokyo.Rでの@berobero11さんの発表を見てて、この等高線どう描くんだろうなあ、とかつぶやいてたら、

こんなすてきなリプが飛んできました!!!

そのお礼といってはおこがましいですが、等高線にラベルを付ける方法を模索してみました。

directlabelsを使う

意外と知られてない気がしますが、directlabelsというパッケージがあります。これを使うと、ggplot2のグラフに直接ラベルを書き入れることができます。

ちょっと使ってみます。

(beroberoさんのコードを、ggsave(p, file='demo_contour_diff.png', dpi=300, w=6, h=5)の直前まで実行)

directlabels::direct.label(p)
#> Error in direct.label.ggplot(p) : 
#>  Need colour aesthetic to infer default direct labels.

むむ。なんかエラーが出ました。aesにcolourを指定していないとだめ、と出てますが、どうやらコードを読んだ感じgeom_contourには対応してないみたいです。(こういうとこで気が利かないのがdirectlabelsがイケてないなと感じるところです。。)

追記:
aes(colour = ..level..)を付け加えればいけるみたいでした。 ggplot2 - R: How to Label Specific Contours using direct.label - Stack Overflow

自前で頑張る

仕方ないから自分で頑張ります。

ggplot2のコードを読むと、geom_contour()は内部ではcontour_linesという関数を使ってるみたいです。

https://github.com/hadley/ggplot2/blob/254327c99cbfe061ba6c3fcc30bbb6115768a722/R/stat-contour.r#L90-L124

よく分かりませんが、関数の上にサンプルコードがついてるので実行してみます。contour_linesはエクスポートされてない関数なので:::(コロン3つ)で呼び出します。

v3d <- reshape2::melt(volcano)
names(v3d) <- c("x", "y", "z")

breaks <- seq(95, 195, length = 10)
contours <- ggplot2:::contour_lines(v3d, breaks)
qplot(x, y, data = contours, geom = "path") + facet_wrap(~ piece)

f:id:yutannihilation:20150224003402p:plain

なんかよくわかりませんが、contour_linesは、等高線のレベルごとに分かれたdata.frame的なものを返してくれるみたいです。ということは、各レベルから適当に一点取ってきてgeom_text()を使えばラベルつけれる?と思って試してみたら↓のような感じになりました。

# contour_linesに渡すのは必ずx, y, zじゃないとむり
d.label <- select(d.cont, x, y, z = z.diff)

# geom_contour()はbinsかbinwidthを指定しないとデフォルトで10が使われます。なのでここも10の区間に分割しています。
contours <- ggplot2:::contour_lines(d.label, pretty(range(d.label$z), 10))

# それぞれのレベルの真ん中にある点をラベルの座標として使う
contour_labels <- contours %>%
  mutate(label = round(level, digits = 1)) %>%
  group_by(label) %>%
  summarise(x = nth(x, round(n()/2)), y = nth(y, round(n()/2)))

p <- p + geom_text(data = contour_labels, aes(x, y, label = label), size = 4)
ggsave(p, file='demo_contour_diff_with_label.png', dpi=300, w=6, h=5)

f:id:yutannihilation:20150224004642p:plain

ラベルつきました!

今後の課題

今回はうまくいきましたが、等高線のあいだがせまいときだとラベルがかぶったりする予感です。その辺をうまいことやるにはもうちょっと工夫が必要そうです。

あと、まだちょっと見にくいので、文字に白でアウトラインをつけたいんですが、いいやり方が見つかりません。これ↓はなんか無理やりやってますが、もっとスマートにやる方法ないのかな。。

r - Outlined text with ggplot2 - Stack Overflow

究極的にはgeom_contourlabel()みたいなのをつくりたいんですが、公式のGeomの作り方を見ても今の私のレベルではさっぱり分からないので、とりあえず断念しました。時間があるとき探求したいところではあります。

Creating a new geom · hadley/ggplot2 Wiki · GitHub