ユーザ用ツール

サイト用ツール


メモ:rmarkdown

Rmarkdown

主にRmarkdownからPDFを生成する際のメモ。Latexを経由するためLatexの知識も必要だが、RmarkdownからLatexにコマンドなどを渡す際の方法など、直接Latexを記述する場合とは異なるtipsがある。また、Rmarkdownでloopを回してチャンクを作成する方法など、効率化する手段も色々ありそう。

PDF出力用の基本設定

新しいコマンドを理解するたびに拡張している。とりあえず2020/05/05現在のもの。

ヘッダ部分

title: "タイトル"
author: "作成者"
date: "2020/05/05"
output:
  pdf_document:
    keep_tex: yes
    latex_engine: xelatex
    number_sections: yes
always_allow_html: true
# bxjsarticleクラスはgeometryパッケージを呼んでいるらしくここで使うとエラーになったため「no」を設定。
# どこかのバージョンアップにて設定項目があるとエラーになるようになったため削除
#geometry: no
header-includes:
- \usepackage{amsmath,amssymb}

# 別のパッケージと競合して1ページ目に無駄な文字列が出力されるので無効に
#- \usepackage{mathspec}

- \usepackage{indentfirst}
- \parindent = 1em
- \usepackage{zxjatype}
- \setmainfont{Century}
- \setsansfont{Century}

# 日本語フォント設定
# 太字指定の場合のフォントをオプションで指定
# 太字指定(\textbf)でゴシック体にする。kableExtraのrow_specなどのBold設定の場合など
- \setjamainfont[BoldFont=IPAexGothic]{IPAexMincho}
- \setjasansfont{IPAexGothic}
- \usepackage{float}
- \setpagelayout * {margin=20mm}
- \renewcommand{\baselinestretch}{0.9}
- 
- \usepackage[compact]{titlesec}
- \titlespacing{\section}{0pt}{5pt}{3pt}
- \titlespacing{\subsection}{0pt}{5pt}{3pt}

- \usepackage{secdot} # 章番号の後ろに「.」を追加するパッケージ。
- \sectiondot{section} # セクション番号に「.」を追加
#- \renewcommand{\thesection}{\arabic{section}} # セクション番号のフォーマットを変更するとき

- \renewcommand{\labelitemi}{■}
- \renewcommand{\labelitemii}{$\circ$}
- \usepackage{caption} 
- \captionsetup[table]{skip=5pt}
- \captionsetup[figure]{skip=2mm}

- \renewcommand{\textfraction}{0.001}
- \renewcommand\floatpagefraction{0.99}
- \setcounter{topnumber}{10}
- \setcounter{bottomnumber}{10}
- \setcounter{totalnumber}{20}

- \makeatletter
- \setlength{\@fptop}{0pt}
- \setlength{\@fpsep}{8pt}
- \setlength{\@fpbot}{0pt plus 1fil}
- \makeatother

- \makeatletter
- \renewcommand{\theequation}{% 式番号の付け方
- \thesection-\arabic{equation}}
- \@addtoreset{equation}{section}
- \renewcommand{\thefigure}{% 図番号の付け方
- \thesection-\arabic{figure}}
- \@addtoreset{figure}{section}
- \renewcommand{\thetable}{% 表番号の付け方
- \thesection-\arabic{table}}
- \@addtoreset{table}{section}
- \makeatother

- \usepackage{xcolor} % kableExtraのkable_stylingでlatex_optionsをstripedで表を縞模様にする場合に必要(以前はいらなった)
#- \definecolor{my_color}{rgb}{0.95,0.98,1.00} % stripe_color = "my_color"で縞模様の色を自分で作って指定する場合

# ページを横置きにするコマンドを定義:\blandscapeで開始、\elandscapeで戻す
- \usepackage{lscape}
- \newcommand{\blandscape}{\begin{landscape}}
- \newcommand{\elandscape}{\end{landscape}}

# header、footerのカスタマイズ
# 余白を調整するとそのままでは本文文字にページ番号などが被る場合がある
# fancyhdrパッケージで調整する。複雑なヘッダ、フッタも作成できるが、とりあえず余白との兼ね合いを調整するためだけに読み込み
#- \usepackage{fancyhdr}
#- \pagestyle{fancy} # ページスタイルを適用
#- \lhead{} # ヘッダ左
#- \chead{} # ヘッダ中央
#- \rhead{} # ヘッダ右・・・いずれも空にする
#- \renewcommand{\headrulewidth}{0.0pt} # ヘッダの区切り線を消す
#- \cfoot{\thepage} # フッタ中央にページ番号記載

# footerの位置調整
- \setlength{\footskip}{10mm}

documentclass: bxjsarticle
subparagraph: yes
classoption: xelatex,ja=standard

最初のsetupチャンク

PDFファイルを作成するとき、ggplotで日本語を含む図表を美しく出力するために図表をcairo PDFで作成する設定や、全体のチャンクオプションを一括して設定する。

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
knitr::opts_chunk$set(warning = FALSE, message = FALSE)

# Latexのfloatを使う場合の図表のfloatオプションを設定
# 図を強制的にその位置に出力
knitr::opts_chunk$set(fig.pos = 'H')

# キャッシュの設定。執筆中は時間短縮のためキャッシュを有効に。最後にOff
knitr::opts_chunk$set(cache = TRUE)

# 出力フォーマットが TeX(PDF含む)の場合のみ対処する

if (knitr::opts_knit$get("rmarkdown.pandoc.to") %in% c("beamer", "latex")) {
  # conversion failure on '...' in 'mbcsToSbcs' の Warning 発生の workaround
  options(
    device = function(file, width = 17, height = 17, ...){
      cairo_pdf(tempfile(), width = width, height = height, ...)
    }
  )
  ## 1. cairo_pdf を使う方法
  # * family には OS にインストールされているフォント名を指定する。
  knitr::opts_chunk$set(dev="cairo_pdf", dev.args=list(family="IPAexMincho"))
}
```

図のチャンクオプション

詳細な設定はもっといろいろあるが、基本の設定。PDFの出力が前提なので、ベクタ画像を利用するため解像度はあまり意識しなくてよいようなので、out.widthで横幅を設定し、縦は横幅との相対的な比率で調整するようにした。図は中央寄せ。

```{r チャンク名, fig.cap="キャプション", out.width="90%", fig.asp=0.5, fig.align="center"}

```

RmarkdownにLatexコマンドを直接埋め込む

Rmarkdown内に「\」で始まるコマンドを直接書けば、単純なコマンドならばpandocが自動的にLatexコマンドと判断してそのまま利用できる。しかし、複雑なコマンドは文字列として解釈されてしまい、Latexソースに変換する段階でエスケープされてしまう。それを回避するためには、Latexチャンクで直接Latexコマンドを記載するとよい。

```{=latex}
Latexコマンド...

```

チャンクに「=latex」と指定すれば、チャンク内はすべてLatexにそのまま渡される。

パラグラフ間の余白(パラグラフ送り)

パラグラフ間の字送り(余白)の設定。標準では1行空きになっているようだ。全体の設定を変更するにはヘッダ部分に以下を記述。

- \setlength{\parskip}{1.5em}

Loopでチャンク作成

図表を連続して作成する場合など、チャンク内のLoopから別のチャンクを生成したい場合がある。

まずテンプレートとなるチャンクを作成する。ループで回すように、必要な個所は変数化し該当箇所を「{{変数名}}」にしておく。以下の例では、チャンク名とキャプション、ggplotのy軸に使う変数名を外部から与えるパラメータにした。

```{r チャンク名_{{prm_chankname}}, fig.cap="{{fig_name}}", out.width="90%", fig.asp=0.45, fig.align="center"}
ggplot(data=DAT, aes(x=時間, y={{target_var}}) +
geom_line() ...

```

呼び出し側はknitr::knit_expandを利用する。第1引数はテンプレート名、第2引数以降は設定したパラメータを列挙する。ループなどを使う場合は、変数でパラメータを渡してやればよい。他にもif_elseなどで条件別にパラメータを変更したりなど、いろいろできる。Knit_expandでテンプレートに変数を埋め込んだ状態のコードをオブジェクトにいったん保存する。次に、knitr::knitでオブジェクトに入れておいたチャンクを解釈したうえでcatでRmarkdown側に出力する。これで、ループを使って連続的にチャンクを生成できる。デバッグをする場合などは、インラインで図表を確認できない(生成されたチャンクのコードが表示される)のでちょっと不便。

rmd <- paste(
  knitr::knit_expand(
    "sub.Rmd",
    prm_chankname="チャンク名",
    fig_name="キャプション",
    target_var="工場1"
  ),
  collapse = "\n"
)
rendered <- knitr::knit(text = rmd, quiet = TRUE)
cat(rendered, sep="\n")

コードチャンク内からLatexの改ページを出力

ループなどを使って連続的にグラフを出力するとき、グラフ以外にLatexのコードも出力したい場合がある。

改ページを出力する場合。チャンク出力の変更と、Latexコマンド前後に改行を入れて、Latexコードに変換された際に適切に解釈できるようにすることが重要。

# チャンクの出力をasisにして生のRの結果を出力
```{r results="asis"}
  # catで\newpageを出力
  # 前後に改行を入れ、コマンド冒頭の「\」をエスケープ
  cat("\n\n\\newpage\n")

```

半角スペース

日本語を出力する関係でtexエンジンはxelatexを使うため、半角スペースは適切にエスケープしておかないと出力されない。エスケープの仕方は場所によって異なる。例えばknitr::kableのcaptionに設定する場合ならば

caption="日本語のテキストに\\ 半角スペースを入れます。"

のように、半角スペースの前に「\\」を入れる。他方で、ggplotなどでグラフを出力する際に、チャンクオプションに指定する「fig.cap」では以下のようにする。

... fig.cap="日本語のテキストに\\\ 半角スペースを入れます"

こちらではは「\」が3つ必要。おそらく1つ目はLatexコード認識のため、2つ目は3つ目の「\」のためのエスケープ。

テーブルの5行ごとに縦スペースを追加(しない)

Rで作成した表は、knitr::kableおよびkableExtrパッケージを利用すると、比較的容易に美しくPDFに出力できる。

Latexの機能かRmarkdown、pandocの機能かは調べていないが、標準ではLatexに変換した段階で表の5行ごとに\addlinespaceが入っている。場合によっては等間隔にしたい。これを解除するためには、kableに以下のようにオプションを渡す。

kable(...., linesep="")

linesep=““はkableのヘルプに記載されいないが、kable_latexに引数がそのまま渡されて解除されるようだ。

テーブルなどのラベルを改行する

Latexに変換するときに改行を保持するための関数を使わないと、R側の改行が無視される。

col.names = linebreak(c("男性","女性","クラス\n全体"))

このように、改行「\n」が入った文字列やベクトルをlinebreakに渡してやるとよい。

回帰分析の結果出力

回帰分析の結果の出力は、texregパッケージを使うと手間なく美しくできる。類似パッケージもあるが、とりあえずこのパッケージを使っている。

ここでは主にPDFに出力するために使っているので、要するにLatexのコードを出力するために利用している。

単純に回帰分析の結果を出力するだけなら以下。

# lm()の結果がres.lmに代入されているとして
texreg(
  list(res.lm),
  digits = 3,
  caption = "回帰分析の結果",
  float.pos = "h"
)

関数の第一引数に回帰分析の結果が入ったオブジェクトをリストで渡してやればよい。複数の結果を与えると、自動的に横に並べて表示してくれる。2つのモデルの結果で同じ変数があれば、同じ行に示してくれる。モデル内の変数が異なる行は、存在しない場合は空白になって表示される。その他によく使うのは、小数点以下の桁数、キャプション、Latex側でfloatで表を挿入する際のオプション指定など。これ以外にも、モデル名を設定(custom.model.names)や変数名を設定(custom.coef.names)、変数の順序(reorder.coef)、表示しない変数(omit.coef:正規表現で除外変数を指定)など細かく指定できる。

なお、開発中は「texreg」の部分を「screenreg」にしておくと、インラインで見やすいように出力してくれる。

ロジスティック回帰を行ったときなど、結果の係数を指数変換してオッズ比にしたい場合などがある。その場合は、次のように係数をいったん取り出して、オーバーライドしてやればよい。

# res.glmにglm()の結果が代入されているとして

# texregパッケージのextract関数で各種係数を取り出す
# ロードしていてもパッケージ名を指定しないと同名の関数が被ることがよくある
res.glm.ext <- texreg::extract(res.glm)

texreg(
  list(res.glm),
  # 上のリストで与えたモデルと同じ順で上書きする係数の入ったオブジェクトをリストで渡す
  #  ここでは先にextractで取り出した係数を指数変換してオッズ比にしている
  override.coef = list(exp(res.glm.ext@coef)),
  override.se = list(exp(res.glm.ext@se)),
  digits = 3,
  caption = "ロジスティック回帰の結果",
  float.pos = "h"
)

数値の桁そろえ

texregでも、次のトピックのようにPDF出力時にsiunitxを利用して桁そろえができる。

オプションに以下を与えれば自動的に適用される。指定しなければ、有意水準のマークがつくとずれてしまう。

siunitx = TRUE

数字の桁そろえ

RmarkdownからPDに表を出力する際に、表内の数値の桁をそろえる場合の方法。数値だけの表であれば、基本的にKableで設定できるが、有意水準を示す「***」マークなどを含める場合に、マークの分だけずれてしまう。理想的には、小数点の位置など、マークを抜いた数値部分の桁が揃ってほしい。それを実現するパッケージがLatexの「siunitx」。

使い方は、YAMLでパッケージを読み込む。

- \usepackage{siunitx}
#- \newcolumntype{d}{S[table-format=3.2]}
#- \newcolumntype{d}{S[table-format=+3]}

次に、kableのalignオプションで、桁そろえを適用する列に「S」を設定する。ないしは「S[オプション]」のように、siunitxのオプションも一緒に設定する。オプションは、上記のように別の文字にカラムタイプを設定し、その文字を設定してもよい。

knitr::kable(
  caption="表のタイトル",
  align = c("l", "S", "l", "S"),
  digits = 3,
  format.args = list(nsmall=3)
  ...

ついでに、数字だけの桁そろえは、小数点以下の桁数はdigitsで指定し、その桁まで少数がない場合に0で埋めるには、format.argsにnsmallを与えると、その桁まで必ず表示される。

なお、siunitxを利用すると”striped”とは相性が良くないらしく、縞模様がおかしくなる。そこで、先にsiunitxを適用してから行の色を指定する。

# 色を付ける行にextra_latex_afterでLatexのオプションを与えて色を付ける。
## なお、ここでの指定はヘッダではなく表の内容の1行目が「0」として、色を付ける行数を指定する。
row_spec(c(2, 4)-1, extra_latex_after="\\rowcolor{gray!6}")

図のテキスト回り込み

例によってRmarkdownからPDFを出力する際の細かい設定は、RmarkdownからLatexを制御しなければならないため、やや複雑になる。他に利用しているパッケージによってはうまく動かないなど、実用レベルではない気もする。とりあえず現状のメモ。

まず、Latexでwrapfigを読み込む。

header-includes:
  - \usepackage{wrapfig}

ただし、titlesecパッケージを読み込むとうまく動かない。

  1. \usepackage[compact]{titlesec}

bxjsarticleのクラスオプションにeveryparhook=compatを指定しないとうまく動かない。

documentclass: bxjsarticle
classoption: xelatex,ja=standard,everyparhook=compat

次に、Rmarkdownで図を出力する際にLatex側に指定する環境を書き換える(たぶん)

```{r, include = F}
defOut <- knitr::knit_hooks$get("plot")  # save the default plot hook 
knitr::knit_hooks$set(plot = function(x, options) {  # set new plot hook ...
  x <- defOut(x, options)  # first apply the default hook
  if(!is.null(options$wrapfigure)) {  # then, if option wrapfigure is given ...
    # create the new opening string for the wrapfigure environment ...
    wf <- sprintf("\\begin{wrapfigure}{%s}{%g\\textwidth}", options$wrapfigure[[1]], options$wrapfigure[[2]])
    x  <- gsub("\\begin{figure}", wf, x, fixed = T)  # and replace the default one with it.
    x  <- gsub("{figure}", "{wrapfigure}", x, fixed = T)  # also replace the environment ending
  }
  return(x)
})
```

 図を出力するチャンクでwrapfigureオプションを指定する。ここでは図を右寄せで行幅の70%の大きさに指定している。

```{r echo = F, warning = F, message = F, fig.width=7, fig.height = 6, out.width = ".7\\textwidth", fig.cap = "My Flowchart", fig.align="right", wrapfigure = list("R", .7)}
plot(mpg ~ hp, data = mtcars)
```

ということで、何とか回り込みを設定できるものの、いろいろと問題が多い。素直にRmarkdownから指定できるようになってほしい。

knitrボタンとrender()

Rstudioのknitボタンではうまくいくのに、コンソールからrender()関数でRmdファイルを呼び出してPDFを生成するとき、kableExtraでテーブルにいろいろやっている場合にエラーが出る。スマートな解決ではないが、一応原因らしきものがわかった。原因の追究に役立ったのは以下。

基本的には、knitボタンとrender()関数は同じはずだが、render()関数のenvirの設定などで異なる場合があるらしい。しかし、envir=new.env()としても、エラーは変わらない。上記URLで言及されているが、kableExtraパッケージは、ロードされた段階でYAMLヘッダに必要なLatexパッケージをロードする処理が入っているらしい。knitボタンを押すと、これらが正常に機能するのでうまくPDFまで変換できる。これをreder()関数で処理すると、不足するLatexパッケージをロードしないため、LatexからPDFに変換するところで未定義のコマンドなどが生じてエラーとなる。

YAMLヘッダのpdf_documentのオプションで「keep_tex: yes」として、knitボタンとrender()関数で同じRmdファイルを処理して途中経過のtexファイルを比較すると、render()関数で処理した方のプリアンブルに、knitボタンで処理した場合に含まれる\usepakage行がないことがわかる。この差分をRmdファイルにあらかじめ書き込んでやれば、問題なくPDFに変換できるようになる。本来はkableExtraパッケージが適切に処理をしてくれるはずなのだが、キャッシュなどの関係でうまくいっていないらしい。現時点で解決もしていないようだ。

kableExtraパッケージが必要とするLatexパッケージは、同パッケージのkableExtra_latex_packages()関数で確認できる。ここで出力されるパッケージをあらかじめYAMLヘッダに含めておくと問題が発生しない。

> kableExtra::kableExtra_latex_packages()
header-includes:
  - \usepackage{booktabs}
  - \usepackage{longtable}
  - \usepackage{array}
  - \usepackage{multirow}
  - \usepackage{wrapfig}
  - \usepackage{float}
  - \usepackage{colortbl}
  - \usepackage{pdflscape}
  - \usepackage{tabu}
  - \usepackage{threeparttable}
  - \usepackage{threeparttablex}
  - \usepackage[normalem]{ulem}
  - \usepackage[utf8]{inputenc}
  - \usepackage{makecell}
  - \usepackage{xcolor}

キャッシュが問題らしいということで、もう少し検証を進めてみた。RstudioでRセッションを再起動し、その直後(環境に何もロードしていない状態)にしてrender()関数を呼ぶと、上記の処理をしなくても問題なくPDFに変換される。ただし、同じ関数をもう一度呼ぶとエラーになる。このとき、既に環境としてkableExtraがロードしているため問題になるようだ。render()関数のenvirにnew.env()を設定しても、完全に真っ新な環境から始まるわけではないため、既にkableExtraが読み込まれた状態だと判断されるのだろうか。

この問題に行き当たったのは、連続的にPDFを生成しようとしてrender()関数をループで呼び出したときにエラーが出たことである。knitボタンを使う場合、Rstudioの新しい環境を用意したうえで作業が行われるが、Rスクリプトからrender()関数を呼ぶ場合は、new.env()を与えても、結局は現在のglobal環境から派生した新しい環境になる(Rスクリプトがわで用意した変数に、renderで呼び出したRmdファイル内からアクセスできている)ので、kableExtraパッケージがロードされている前提でうごいてしまい、YAMLヘッダにLatexパッケージが読み込まれないような気がする。rstudioapi::restartSession()などで、一度環境をクリアする方法も考えたが、そうするとループ処理の範囲が大きくなり、同じデータの前処理を無駄に何度もやらなければならない。したがって、YAMLヘッダにあらかじめ必要なLatexパッケージを書き込むのが、いまのところ最善策だろう。

丸囲み文字

Rmarkdownで丸囲み文字を出力するには、Latexのコマンドを使って以下のようにする。

\raise0.2ex\hbox{\textcircled{\scriptsize{1}}}

しかし、行頭で用いるとエラーになってしまう。

! You can't use `\raise' in vertical mode.

はっきりわかっていないが、\raiseコマンドを行頭に持ってくると「垂直モード」で利用していると判定されてエラーになってしまうようだ。\raiseは無くてもかまわないが、行が若干ずれるため見栄え的には入れたい。そこで色々試した結果、行頭に持ってくる場合は以下の設定があまり問題ないようである。

\indent\raise0.2ex\hbox{\textcircled{\scriptsize{4}}}
または
\noindent\raise0.2ex\hbox{\textcircled{\scriptsize{4}}}  

インデントを入れてやることで、行内だと判断できるようになるようだ。どちらを使うかは、行頭のインデントの絶対設定と合わせておけばよいだろう。

丸囲み数字等のフォント

Rmarkdownからxelatexを経由してPDFを作成する際に、丸囲み数字など(①)がフォント抜けで豆腐になっていたのを解決。問題は単純なフォント設定だったが、フォント設定は基本的に変えたくなかったので、いろいろ調査してわかったこと。

xelatexはTTFフォントを直接指定することができる便利なtexなので、Rmarkdownからの出力はこれを利用している。この時、日本語を利用するために以下の設定をしている。ドキュメントクラスの読み込みでfontspecやzxjatypeはusepackageをしなくてもよいかもしれない。

RmakdownのYAMLヘッダ(該当箇所のみ)

documentclass: bxjsarticle
classoption: xelatex,ja=standard,base=10.5pt,jbase=10.5pt,a4paper
header-includes:
  # 欧文フォントの指定
  - \usepackage{fontspec}
  - \setmainfont{Times New Roman}
  - \setsansfont{Times New Roman}
  # 和文フォントの指定
  - \usepackage{zxjatype}
  - \setCJKmainfont[BoldFont=IPAexGothic]{IPAexMincho}
  - \setCJKsansfont{IPAexGothic}

Texソースでは以下のように展開される。

\documentclass[xelatex,ja=standard,base=10.5pt,jbase=10.5pt,a4paper]{bxjsarticle}
\setmainfont{Century}
\setsansfont{Century}
\setCJKmainfont[BoldFont=IPAexGothic]{IPAexMincho}
\setCJKsansfont{IPAexMincho}

使っているフォントはともかくとして、以上の設定でだいたいスムーズにタイプセットしてPDFがうまく生成されていたのだが、丸囲み数字などがフォント抜けしてしまい、その都度丸囲み数字を生成するコードを挿入していた。文章を書いている場合には、自分で丸囲み数字を使う箇所を認識しているので問題はないが、アンケートの自由記述欄などテキストデータを読み込んで、一括して整形・出力するようなコードを書くと、どうしても入力された文字に混ざってしまう。逐一見つけて置換などをしても、気づかないフォント抜けなどが発生してしまうことがあり、いままで気づいた文字を置換リストに貯めていたが、非効率だしユニコードの世界では解決できるはずだと思い調査。

原因は非常に単純な点にあった。問題は、丸囲み数字の箇所が「欧文」として扱われており、Times New Romanにコードポイントに対応するフォントが用意されておらず、フォントが抜けてしまう様だった。試しに欧文フォントをIPAフォントに変えてみると、ごく普通に丸囲み数字もPDFで表示できる。丸囲み数字を入力するのが日本語IMEで変換するため、てっきり日本語の全角文字扱いだと思っていたが、かつてと異なりユニコードでは①はU+2460というコードポイントが割り当てられており、このコードが入力されている。そして、このコードがある範囲がxelatexでは欧文として扱われるため、欧文フォントの設定が適用されていたためにフォント抜けが発生していた。

単純な解決策は、欧文もすべて日本語フォントにすること。IPAex明朝などは、半角文字もきれいに出力できるので、これはこれでよい。しかし、どうしてお欧文には別フォントを利用したい場合、丸囲み数字の箇所だけフォント指定するとか、段落単位で逐一フォント指定するとか個別対応が必要となる。これでは、個別に置換するのと手間はあまり変わらない。そこで以下を参考に設定を追加。

\xeCJKDeclareSubCJKBlock{kigou}{"20A0 -> "2BFF}

上記のように、xeCJKパッケージの\xeCJKDeclareSubCJKBlockで、和文として扱うコードの範囲を指定してやる。単純に和文として扱うだけなら、上記のように必要な個所のコードを指定すれば、全体で設定している和文フォントが利用される。「kana」は識別名なので何でもよい。範囲の指定は、コードポイントでも、コードポイントに対応する文字でも指定できるようだ。また、必要に応じてここで指定したブロックだけ別フォントを割り当てることも可能。ここでは横着して、とりあえずASCII文字+α以上のコードポイントはすべて和文と指定して丸囲み数字等をすべて日本語フォントにしてしまうが、もう少し丁寧に設定する方が望ましいだろう。範囲指定の際は、Unicodeのブロック表などを参照するとよい。

\xeCJKDeclareSubCJKBlockでASCII以外のほぼすべてのコードポイントを指定していたが、やはり横着したのはよくなかった。詳細は調べていないが、ここで指定されたブロックは、文字送りや禁則処理のルールが別扱いになるようで、「。」が行頭に出てしまったりと、美しい組版にあらない。ということで、記号が入っているブロックに絞って指定する。とりあえず「“20A0 → “2BFF」の範囲としたが、様子を見て微調整する。とりあえず丸囲み数字の範囲は抑えているので、当面は問題ないはず。

kableから出力されるLatexコードを加工する

kableExtraのcollapse_rowsを使って繰り返しのあるセルをまとめてBooktabs形式の罫線を引くと、まとめたセルの縦方向の位置がおかしくなる。

collapse_rows(1:2, valign="top", latex_hline="full")

この場合、縦に結合したセル内の文字が、結合していない行の一番上の行と同じところに来てほしいが、自動的にはそろわない。kableExtraの作者によると、Latexのmultirowの長さ計算の問題らしい。latex_hline=“major”などで、罫線を引かないようにすれば計算が合うので問題ないが、罫線を入れたいときは出力されるLatex側のコードを無理やり書き換えて対応する。

collapse_rows(1:2, valign="top", latex_hline="full") %>% 
  gsub("\\\\multirow\\[t\\]\\{-3\\}\\{\\*\\}","\\\\multirow[t]{-3}{*}[3.5mm]", .)

このように、\multirowのコマンドの部分の<vmove>オプションを入れるように書き換える。今のところ、出てきたコードに場当たり的に修正を加えるしか思いつかない。\multirowの開発者の人に頑張ってもらいたい。。

kableで出力するテーブル内の文字の回転

ラベルに長い文字列を使うようなとき、表頭の文字列を90度回転して縦方向にしたい時がある。その場合は、kableExtraのrow_specを使えばよい。

kable() %>% 
.... %>%
.... %>%
row_spec(0, angle = -90)

これで0行目=ヘッダの文字列を90度回転できる。この他にも、セルごとに個別に設定するには、cell_specも使える。

DATA[1, 5] <- cell_spec(DATA[1, 5], "latex", angle = 90)

cell_specはセル内にLatexなりHTMLのコードを直接埋め込む。したがって、個々のセルに対して処理を行っておく必要がある。テーブルのヘッダの場合は、cell_specを利用して必要なコードを埋め込んだヘッダのベクトルを生成し、kableのcol.namesに渡すという手間がかかる。なおcell_specはLatexのコードをデータに直接埋め込むので、kabelのオプションで「escape=FALSE」を指定しておかないと、コードがそのまま文字として出力されてしまう。

上記は両方指定しても効果を発揮する。そのため、行全体を90度回転させ、特定のセルだけ-90度回転するように指定すれば、そのセルだけ回転させないということができる。

文字列を回転する際に、回転の中心点を決めることができる。angleを指定すると、Latex側では\rotateboxで文字列が回転される。この時、標準では文字列の左端を中心に回転が行われる。「angle=90」ならば、文字列の先頭を中心に反時計回りに回転するので、行内の全てのセルにangle=90が設定されている場合、文字列の先頭が下にきて先頭がセルの下で揃うことになる。逆に「angle=-90」とすると、文字列の先頭を中心に時計回りに回転するため、文字列の先頭が上にきてセルの上で先頭が揃う。

この時、angle=-90で時計回りに回転しつつ、文字列をセルの下で揃えたい場合には、回転の中心を文字列の末尾=右端にする必要がある。右端を中心に時計回りすると、行頭が持ち上がる形で回転し、文字列はセルの下側で揃うことになる。しかしkableExtra側ではオプションがないので、Latexのコマンドで調整する。angleを設定した際に利用される\rotateboxのオプションは以下の通り。

\rotatebox[回転の中心など]{角度}{対象の文字列}

kableExtraでは\rotateboxの回転の中心は指定されずデフォルトでハードコーディングされているため、このページに記載したようにLatexのコードを文字列として変数に入れ無理やり正規表現で書き換えるか、一時的に\rotateboxコマンドの定義を書き換える。後者の場合は、以下のようにする。

\let\orgrotatebox\rotatebox%
\renewcommand{\rotatebox}[2]{\orgrotatebox[origin=r]{#1}{#2}}%

基本的には、renewcommandで\rotateboxの定義をオプションを指定した状態に書き換えることで、kableから呼ばれたときに、改変された\rotateboxが用いられるため、文字列の右端を中心に回転され目的の挙動となる。ここで注意が必要なのは、\rotateboxの再定義に\rotateboxコマンドを直接使うと、再帰的に展開されてリソースを食い尽くしてエラーとなる(ハマった)。そこで、いったん\letコマンドを使って一旦「\rotatebox」を「\orgrotatebox」にコピーしておき、コピーしたコマンドを使って\rotateboxの定義を再定義する。コピーしておいたコマンドは、ここでの変更を元に戻す際にも使う。なお、行末の「%」はコマンドは、Latexで単一改行が空白として扱われてコマンドが連続したように解釈され、不用意なエラーを生じることを回避するもので、ここでは必要ないかもしれない。

\renewcommandの使い方は

\renewcommand{対象のコマンド}[引数の数]{再定義したコマンドの内容}

なので、ここでは\orgrotateboxにコピーしたオリジナルの\rotateboxを使って、オプションの回転中心点を決める[origin=r]を指定した状態の\rotateboxコマンドを再定義している。origin=rで文字列の右端を回転軸にする。他にもx,yで細かく指定することも可能。

メモ/rmarkdown.txt · 最終更新: 2021/11/10 10:31 by Wiki Editor