ggplot2を使っていると、こんな感じのもじゃもじゃしたデータに出会うことがあります。データの系列が多すぎるともう色が見分けられなくてあんまり意味をなしません。
library(tidyverse) set.seed(1) d <- tibble( idx = 1:10000, value = runif(idx, -1, 1), type = sample(letters, size = length(idx), replace = TRUE) ) %>% group_by(type) %>% mutate(value = cumsum(value)) %>% ungroup() ggplot(d) + geom_line(aes(idx, value, colour = type))
こういうときは、例えば、Y軸方向の最大値が20より大きい系列だけをハイライトしてみよう、みたいな気持ちになるでしょう。
正攻法でやればこんな感じでしょう。まず、データを整形します。最大値が20以下の列にはNA
を入れてcolour
という列を作ります。
d2 <- d %>% group_by(type) %>% mutate(max_value = max(value), colour = if_else(max_value > 20, type, NA_character_))
ちなみにここでなぜconditionを一度max_value
という列に入れているかというと、if_else()
はcondition
がひとつなのにtrue
やfalse
が複数あるという状況だとエラーになるからです(さっき気づいた…)。
if_else(TRUE, 0, 1:10) #> Error: `false` must be length 1 (length of `condition`), not 10
さておきデータはできたのでグラフを描きます。
ggplot(d2, aes(idx, value, group = type, colour = colour)) + geom_line()
まあこれでぜんぜんいいんですけど、探索的にいろいろな条件でグラフを描きたいときは、元データを加工する→ggplot()
というのをいったりきたりするのがややめんどうです。
ということで、ggplot2だけでできるパッケージをつくろうとしています(まだあんまりちゃんと動かないです。インストールしたいという奇特な方は、次期バージョンのdplyrが必要なのでdevtools::install_github("tidyverse/dplyr")
してから使ってください)
こんな風に書くと、
ggplot(d) + geom_line(aes(idx, value, colour = type)) + scale_highlight_colour(max(value) > 20)
こんなグラフが描けます。
なんですけど、ここで、
max(value) > 20
と書いていますが、ggplot2の内部実装だと、Scaleオブジェクトは元データを知ることができません。なので、普通にやるとvalue
ってなに?というエラーで死にます。普通にやると。
さて、普通を強調するからには当然の流れですが、今は普通じゃないことをしています。こんな感じで、引数として元データが渡されないので、呼び出し元の環境を這い上がってむりやりデータを取ってくる、というアクロバットをしています。
# dirty hack index_lapply <- rlang::caller_env(5)$i df_raw <- rlang::caller_env(6)$layer_data[[index_lapply]] mapping <- unclass(rlang::caller_env(6)$layers[[index_lapply]]$mapping)
(https://github.com/yutannihilation/gghighlight/blob/master/R/scale_highlight.R#L43-L46)
なんかいい方法あるんでしょうか…。ggplot2の内部実装にお詳しい方のタレコミをお待ちしています。