まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

ggplotのfacet日本語テキストを折り返す

これはなにか

ggplotのfacet内の文字を任意の文字数で折り返し(改行)をしたい。

どういうことかというと、下記のようにirisベースでテキトーにSpeciesを日本語化して文字数を増やしたもので考える。

library(dplyr)

iris2 = iris %>% 
  mutate(Species_jp = case_when(Species == 'setosa' ~ 'セトサ',
                                Species == 'versicolor' ~ 'バージカラー',
                                Species == 'virginica' ~ 'バージニカ'),
         Species_jp2 = paste0(Species_jp, Species_jp, Species_jp),
         Species2 = paste(Species, Species, Species, sep = ' ')) %>% 
  tibble()

f:id:chito_ng:20200619184132p:plain

このSpecies_jp2をfacetにすると文字数が長いので見切れる。

iris2 %>%
  ggplot() + 
  aes(Sepal.Length, Sepal.Width) + 
  geom_point() + 
  facet_wrap(~ Species_jp2) +
  theme(strip.text = element_text(family = 'HiraKakuProN-W3'))

f:id:chito_ng:20200619184325p:plain:w550

これをテキトーな文字数で折り返したい。

このようなモチベーションの際、英語であれば label_wrap_gen を使うことで対応できる。
label_wrap_gen は、 「空白までの間でn文字以上になった場合折り返して、満たない場合は次の空白までをカウントしてその合計がn文字以上なら直前の空白で折り返す」といった処理をおこなってくれる。
例えば「Mostly harmless econometrics」という文章はn = 10であれば、「Mostly harmless (改行) econometrics」として表示される(下記コードはSpeciesを空白付で3回繰り返したもの)。

iris2 %>% 
  ggplot(aes(Sepal.Length, Petal.Length)) +
  geom_point() +
  facet_wrap(. ~ Species2, labeller = label_wrap_gen(5))

f:id:chito_ng:20200619185439p:plain:w600

ggplot2.tidyverse.org

しかし、日本語文の場合単語を空白で区切らないので label_wrap_gen は使えない。

解決方法

facet_xxxの引数labbelerは指定した関数でテキストを処理してくれる。
そのため、テキストに対してn文字毎に改行コード\nを入れる関数を渡すことでやりたいことが実現できる。

ちなみに、 label_wrap_genも前述のような処理をおこなっている関数というだけ。

# characterの全列に対して、5文字毎に改行を入れた状態に上書きする関数
facet_splitter = function(x) mutate_all(x, ~ str_extract_all(.x, ".{1,5}") %>% 
                                          map_chr(., ~ paste(.x, collapse = "\n")))

iris2 %>%
  ggplot() + 
  aes(Sepal.Length, Sepal.Width) + 
  geom_point() + 
  facet_wrap(~ Species_jp2, labeller = facet_splitter) + # 上記関数を適用
  theme(strip.text = element_text(family = 'HiraKakuProN-W3'))

f:id:chito_ng:20200619190531p:plain:w600

ちなみに、labellerを使わないでも先にmutateでfacetに指定する列に同様のことをおこなっておいてもよい。

iris2 %>%
  mutate(Species_jp2 = (str_extract_all(Species_jp2, '.{1,5}') %>% map_chr(., ~ paste(., collapse = "\n")))) %>% # 直接書き換える
  ggplot() + 
  aes(Sepal.Length, Sepal.Width) + 
  geom_point() + 
  facet_wrap(~ Species_jp2) + # 上記関数を適用
  theme(strip.text = element_text(family = 'HiraKakuProN-W3'))

f:id:chito_ng:20200619190531p:plain:w600

ただし、決まった文字数で改行しているので単語の中途半端なところで改行は発生している。本当は、単語を識別していい感じのところで改行して欲しいがまぁそれは色々と難しそう(Mecabとか使いながら色々処理する?)なので置いておく。

余談

世の中の情報のほとんどは英語で落ちているため、今回のようなことはググっても日本語では見当たらないし、英語で探しても「 label_wrap_gen を使えばいいよ :)」 という回答ばかりで困っていたので、 r-wakalangで質問した。回答してくださった方々ありがとうございました!