からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

ggtext::geom_textbox関数によるラベル付け

はじめに

 R言語でグラフに文字列を書く込むための関数を確認していきます。
 この記事では、ggtextパッケージのgeom_textbox()を扱います。

【他の関数】

www.anarchive-beta.com

www.anarchive-beta.com

【他の内容】

www.anarchive-beta.com

【この記事の内容】

ggtext::geom_textbox関数

 ggtextパッケージのラベル付け関数geom_textbox()の引数を確認します。共通する引数については説明を省略するので、「ggplot2::geom_label関数」と「ggtext::geom_richtext関数」も参照してください。ただし、label.***系の引数はbox.***になります。
 この記事での作図コードやデータフレームは、あくまで参考図を作るためのものです。必要な部分を汲み取って利用してください。

 利用するパッケージを読み込みます。

# 利用パッケージ
library(tidyverse)

 この記事では、基本的にパッケージ名::関数名()の記法を使うので、パッケージを読み込む必要はありません。ただし、作図コードがごちゃごちゃしないようにパッケージ名を省略しているため、ggplot2は読み込む必要があります。
 また、ネイティブパイプ演算子|>を使っています。magrittrパッケージのパイプ演算子%>%に置き換えても処理できますが、その場合はmagrittrも読み込む必要があります。

デフォルト

 まずは、デフォルトの設定で描画してみます。

# ダミーのデータフレーム
dummy_df <- tidyr::tibble(v = 0)

# デフォルトのラベルを確認
ggplot(data = dummy_df) + 
  ggtext::geom_textbox(x = 0, y = 0, label = "label text") + # ラベル
  coord_equal() + # アスペクト比
  lims(x = c(-1, 1), y = c(-1, 1)) + # 表示範囲
  labs(title = "ggtext::geom_textbox()")

デフォルトの設定

 プロット位置(x, y引数)は、geom_richtext()の引数として(aes()の外で)指定できますが、何かしらのデータフレームを渡しておく必要があります。(1データであってもデータフレームに値を持たせて描画した方がいいのでしょう。)

 ggplot2::geom_label()ggtext::geom_richtext()とは異なり、デフォルトでは文字列に応じて枠の横幅が自動で調整されません。

width引数

 横サイズをunit()を使ってwidth引数で設定できます。

作図コード(クリックで展開)

# 文字列を指定
char_df <- tidyr::tibble(y = 1:26) |> 
  dplyr::group_by(y) |> 
  dplyr::mutate(label = stringr::str_flatten(LETTERS[1:y])) |> # 確認用ラベル
  dplyr::ungroup()
char_df
## # A tibble: 26 × 2
##        y label     
##    <int> <chr>     
##  1     1 A         
##  2     2 AB        
##  3     3 ABC       
##  4     4 ABCD      
##  5     5 ABCDE     
##  6     6 ABCDEF    
##  7     7 ABCDEFG   
##  8     8 ABCDEFGH  
##  9     9 ABCDEFGHI 
## 10    10 ABCDEFGHIJ
## # … with 16 more rows

 設定を変えて比較します。

# 横サイズを確認
ggplot(data = char_df, mapping = aes(y = y, label = label)) + 
  ggtext::geom_textbox(x = 0) + # デフォルト
  ggtext::geom_textbox(x = 1, width = unit(3, units = "inch")) + # 横サイズ指定
  ggtext::geom_textbox(x = 2, hjust = 0.5, width = NULL) + # 横サイズ可変・中央揃え
  ggtext::geom_textbox(x = 3, hjust = 0, width = NULL) + # 横サイズ可変・左揃え
  scale_x_continuous(breaks = 0:3, 
                     labels = c("default", 'width = unit(3, "inch")', "width = NULL, hjust = 0.5", "width = NULL, hjust = 0"), 
                     limits = c(-0.5, 4)) + 
  labs(title = "ggtext::geom_textbox()")


width引数

 値を指定すると横サイズが固定され、NULLを指定すると文字列に応じて自動で調整されます。
 デフォルトはunit(2, "inch")です。

height引数

 同様に、縦サイズをunit()を使ってheight引数で設定できます。

作図コード(クリックで展開)

# 文字列を指定
char_df <- tidyr::tibble(x = 1:10) |> 
  dplyr::group_by(x) |> 
  dplyr::mutate(label = stringr::str_c(LETTERS[1:x], collapse = "<br>")) |> # 確認用ラベル
  dplyr::ungroup()
char_df
## # A tibble: 10 × 2
##        x label                                         
##    <int> <chr>                                         
##  1     1 A                                             
##  2     2 A<br>B                                        
##  3     3 A<br>B<br>C                                   
##  4     4 A<br>B<br>C<br>D                              
##  5     5 A<br>B<br>C<br>D<br>E                         
##  6     6 A<br>B<br>C<br>D<br>E<br>F                    
##  7     7 A<br>B<br>C<br>D<br>E<br>F<br>G               
##  8     8 A<br>B<br>C<br>D<br>E<br>F<br>G<br>H          
##  9     9 A<br>B<br>C<br>D<br>E<br>F<br>G<br>H<br>I     
## 10    10 A<br>B<br>C<br>D<br>E<br>F<br>G<br>H<br>I<br>J

 設定を変えて比較します。

# 縦サイズを確認
ggplot(data = char_df, mapping = aes(x = x, label = label)) + 
  ggtext::geom_textbox(y = 0, width = NULL) + # デフォルト
  ggtext::geom_textbox(y = 1, width = NULL, height = unit(1, units = "inch")) + # 縦サイズ指定
  ggtext::geom_textbox(y = 2, vjust = 1, width = NULL, height = NULL) + # 縦サイズ可変・上揃え
  scale_y_reverse(breaks = 0:-2, 
                     labels = c("default", 'width = unit(1, "inch")', "height = NULL, hjust = 1"), 
                     limits = c(-3, 0.5)) + 
  labs(title = "ggtext::geom_textbox()")


height引数

 値を指定すると縦サイズが固定され、NULLを指定すると文字列に応じて自動で調整されます。
 デフォルトはNULLです。

halign引数・valign引数

 枠内のラベルテキストの横方向の配置をhalign、縦方向の配置をvalign引数で設定できます。

作図コード(クリックで展開)

# 揃え位置を指定
align_df <- tidyr::expand_grid(
  h = c(0, 0.5, 1), 
  v = c(0, 0.5, 1)
) |> # 格子点を作成
  dplyr::mutate(
    x = h, 
    y = v, 
    label = paste0(
      "halign = ", stringr::str_pad(h, width = 3, side = "right"), "<br>", 
      "valign = ", stringr::str_pad(v, width = 3, side = "right")
    ) # 確認用ラベル
  )
align_df
## # A tibble: 9 × 5
##       h     v     x     y label                         
##   <dbl> <dbl> <dbl> <dbl> <chr>                         
## 1   0     0     0     0   "halign = 0  <br>valign = 0  "
## 2   0     0.5   0     0.5 "halign = 0  <br>valign = 0.5"
## 3   0     1     0     1   "halign = 0  <br>valign = 1  "
## 4   0.5   0     0.5   0   "halign = 0.5<br>valign = 0  "
## 5   0.5   0.5   0.5   0.5 "halign = 0.5<br>valign = 0.5"
## 6   0.5   1     0.5   1   "halign = 0.5<br>valign = 1  "
## 7   1     0     1     0   "halign = 1  <br>valign = 0  "
## 8   1     0.5   1     0.5 "halign = 1  <br>valign = 0.5"
## 9   1     1     1     1   "halign = 1  <br>valign = 1  "

 aes()の引数として、データごとに指定できます。

# 揃え位置を確認
ggplot(data = align_df, mapping = aes(x = x, y = y)) + 
  ggtext::geom_textbox(mapping = aes(label = label, halign = h, valign = v), 
                       width = unit(5, units = "lines"), height = unit(5, units = "lines")) + 
  geom_point(color = "red", size = 3) + # プロット位置を確認
  lims(x = c(-0.5, 1.5), y = c(-0.5, 1.5)) + # 表示範囲
  labs(title = "ggtext::geom_textbox()")


halign引数とvalign引数

 プロット位置を赤色の点で示しています。
 haligne = 0なら左揃え、haligne = 1なら右揃え、valigne = 0なら下揃え、valigne = 1なら上揃えになります。また、それぞれ0.5なら中央揃えになります。それ以外の値も指定できます。
 デフォルトhalign = 0, valign = 0.5です。

box.margin引数

 枠線の外側(上下左右の4方向)の余白をunit()を使ってlabel.padding引数で設定できます。ただし、指定する順序は上右下左(上から時計回り)です。

作図コード(クリックで展開)

# 4方向の値を指定
margin_df <- tidyr::expand_grid(
  margin_x = seq(from = -40, to = 40, by = 20), 
  margin_y = seq(from = -40, to = 40, by = 10)
) |> 
  dplyr::mutate(
    top = dplyr::if_else(margin_y > 0, true = margin_y, false = 0), 
    right = dplyr::if_else(margin_x > 0, true = margin_x, false = 0), 
    bottom = dplyr::if_else(margin_y < 0, true = -margin_y, false = 0), 
    left = dplyr::if_else(margin_x < 0, true = -margin_x, false = 0), 
    label = paste0(
      "c(", 
      stringr::str_pad(top, width = 3, side = "left"), ", ", 
      stringr::str_pad(right, width = 3, side = "left"), ", ", 
      stringr::str_pad(bottom, width = 3, side = "left"), ", ", 
      stringr::str_pad(left, width = 3, side = "left"), 
      ")"
    ) # 確認用ラベル
  )
margin_df
## # A tibble: 45 × 7
##    margin_x margin_y   top right bottom  left label                
##       <dbl>    <dbl> <dbl> <dbl>  <dbl> <dbl> <chr>                
##  1      -40      -40     0     0     40    40 c(  0,   0,  40,  40)
##  2      -40      -30     0     0     30    40 c(  0,   0,  30,  40)
##  3      -40      -20     0     0     20    40 c(  0,   0,  20,  40)
##  4      -40      -10     0     0     10    40 c(  0,   0,  10,  40)
##  5      -40        0     0     0      0    40 c(  0,   0,   0,  40)
##  6      -40       10    10     0      0    40 c( 10,   0,   0,  40)
##  7      -40       20    20     0      0    40 c( 20,   0,   0,  40)
##  8      -40       30    30     0      0    40 c( 30,   0,   0,  40)
##  9      -40       40    40     0      0    40 c( 40,   0,   0,  40)
## 10      -20      -40     0     0     40    20 c(  0,   0,  40,  20)
## # … with 35 more rows

 データごとに(aes()の中で)設定できません。

# ダミーのデータフレーム
dummy_df <- tidyr::tibble(v = 0)

# ラベル外側の余白を確認
g <- ggplot() + 
  lims(x = c(-1, 1), y = c(-1, 1)) + # 表示範囲
  #lims(x = c(-10, 10), y = c(-10, 10)) + # 表示範囲
  labs(title = 'ggtext::geom_textbox(box.margin = unit(c(top, right, bottom, left), "lines"), width = NULL, height = NULL)')
for(i in 1:nrow(margin_df)) {
  # 値ごとにラベルを重ねて描画
  g <- g + 
    ggtext::geom_textbox(data = margin_df[i, ], mapping = aes(label = label), 
                         x = 0, y = 0, hjust = 0.5, vjust = 0.5, 
                         width = NULL, height = NULL, 
                         box.margin = unit(as.numeric(margin_df[i, c("top", "right", "bottom", "left")]), "lines"))
}
g <- g + 
  geom_point(data = dummy_df, x = 0, y = 0, color = "red", shape = 13, size = 13) # プロット位置を確認
g

 (box.***引数はデータごとに指定できないため、for()を使って1データずつ設定を変更して描画しています。)


box.margin引数

 プロット位置を赤色の印で示しています。この図の全てのラベルは、同じ位置を基準にプロットされています。
 値が大きいほど、指定した方向の余白が広くなるので、プロット位置から離れていきます。ただし、相対的な配置位置なので軸の値は影響しません。
 縦横のサイズを自動調整に設定(width = NULL, height = NULLを指定)すると、geom_richtext()と同様の図になります。

 縦横のサイズを固定したときの、ラベルサイズと余白の関係を確認します。

作図コード(クリックで展開)

# ダミーのデータフレーム
dummy_df <- tidyr::tibble(v = 0)

# ラベルサイズと余白の関係を確認
ggplot(data = dummy_df) + 
  ggtext::geom_textbox(label = 'width = unit(25, "lines")<br>height = unit(20, "lines")<br>box.margin = unit(c(0, 0, 0, 0), "lines")', 
                       x = 0, y = 0, hjust = 0.5, vjust = 0.5, halign = 0.5, valign = 1, 
                       width = unit(25, units = "lines"), height = unit(20, units = "lines"), 
                       box.margin = unit(c(0, 0, 0, 0), units = "lines"), 
                       color = "red", fill = NA, box.size = unit(1, units = "lines")) + # 余白なし
  ggtext::geom_textbox(label = 'width = unit(25, "lines")<br>height = unit(20, "lines")<br>box.margin = unit(c(5, 5, 5, 5), "lines")', 
                       x = 0, y = 0, hjust = 0.5, vjust = 0.5, halign = 0.5, valign = 0.5, 
                       width = unit(25, units = "lines"), height = unit(20, units = "lines"), 
                       box.margin = unit(c(5, 5, 5, 5), units = "lines"), 
                       color = "gold", box.size = unit(1, units = "lines")) + # 余白あり
  lims(x = c(-1, 1), y = c(-1, 1)) + # 表示範囲
  labs(title = 'ggtext::geom_textbox(box.margin = unit(c(top, right, bottom, left), "lines"))')


box.margin引数

 描画されるラベルの横方向のサイズは、width引数の値から左右の余白を引いた値になります。縦方向のサイズも同様です。そのため、width, hight引数の設定は同じでも、余白を指定した黄色のラベルの方が小さくなります。
 デフォルトは余白なしbox.margin = unit(c(0, 0, 0, 0), "pt")です。

orientation引数

 ラベルの表示角度をorientation引数で設定できます。ggplot2::geom_label()ggtext::geom_richtext()では、angle引数にラベルの表示角度を実数で指定できます。ggtext::geom_textbox()では、上下左右の4方向のみを特定の文字列で指定できます。

作図コード(クリックで展開)

# 表示角度を指定
angle_df <- tibble::tibble(
  x = c(0, 0, 1, 1), 
  y = c(0, 1, 0, 1), 
  angle = c("left-rotated", "upright", "inverted", "right-rotated"), 
  label = paste0('orientation = "', angle, '"') # 確認用ラベル
)
angle_df
## # A tibble: 4 × 4
##       x     y angle         label                            
##   <dbl> <dbl> <chr>         <chr>                            
## 1     0     0 left-rotated  "orientation = \"left-rotated\"" 
## 2     0     1 upright       "orientation = \"upright\""      
## 3     1     0 inverted      "orientation = \"inverted\""     
## 4     1     1 right-rotated "orientation = \"right-rotated\""

 aes()の引数として、データごとに指定できます。

# 表示角度を確認
ggplot() + 
  ggtext::geom_textbox(data = angle_df, mapping = aes(x = x, y = y, label = label, orientation = angle), 
                       width = NULL, box.padding = unit(rep(2, times = 4), units = "lines")) + 
  lims(x = c(-0.5, 1.5), y = c(-0.5, 1.5)) + # 表示範囲
  theme(axis.title = element_blank(), 
        axis.text = element_blank(), 
        axis.ticks = element_blank(), 
        panel.grid.minor = element_blank()) + # 軸線を非表示化
  labs(title = 'ggtext::geom_textbox()')


orientation引数

 デフォルトが"upright"で上向き(そのまま)描画されます。"right-rotated"だと右に、"left-rotated"左に90度回転します。"inverted"だと上下を逆転します。

 この記事では、ggtextパッケージのgeom_textbox()を解説しました。

参考

おわりに

 知りたかったのはこの関数の使い方で、他の2つはそのついでに生えた記事です。他の関数と比較してできること・できないことを知らないと、この関数を使う嬉しさも分かりませんよね。というのは後付けで、引数の仕様がよく分からず1から調べることになっただけです。もっと言うと、本当は画像データに1・2行の文字列を書き込みたかっただけで、それが簡単にできたらこの記事を書くこともなかったことでしょう。
 思いのほか苦労したので、誰かの役に立てば嬉しいです。