R_tidyverse

Rのテーブル作成パッケージを試す

めちゃくちゃ久しぶりの更新です。

ついにSASを使うことがなくなってしまい、大学の研究者になってしまいました。

SASのライセンス費用は出せないし、他の方もSAS使ってなさそうなので俺もRユーザーデビューです。今までもRは使ってたけど、一切SASが使えない環境になるのは初めてなのでどうなることやら。とりあえずSASでやってる業務手順をそのままRに移植することを検討するところから始めてます。

RはSASよりもレポーティング機能が充実しているので、報告書を作成する場合はむしろSASよりも捗るのですが、パッケージは複数存在するのでどのパッケージが一番よさそうか検討してみました。

必要条件

業務としてつかうため、以下の条件はクリアすることが条件となります。

  • ダブルプログラミングで検証できる(解析結果をデータフレームとして保存してコンペアするか、成果物そのものをコンペアする)
  • excel、rtf、word形式のいずれかで出力する (HTMLやPDFはだめ)
  • 日本語フォントが正常に使用できる

ぶっちゃけ俺は臨床の統計解析担当者ではないので製薬業界のルールに従う必要はないのですが、一緒に仕事する仲間は統計解析担当者だったりするので、できれば上記のルールは守りたいです。

ダブルプログラミングは決して優秀な検証方法とは言えないですが、かといってもっといい方法もあるわけではないのでダブルプログラミングとコンペアができる仕組みは必須です。そのため集計結果をデータフレームとしてまとめて検証用として保存し、それをそのままテーブルの形に出力する流れになると思います。テキストベースの成果物なら成果物そのものをコンペアできるかも。

検討するパッケージ

gtsummaryのような集計や解析を勝手にやって自動で表に出力するパッケージは極力避けたいです。臨床試験などで作成するテーブルは様々な統計量を表示することもあり、なんでも自動でやってくれるパッケージですべて対応できるとは限らないでしょうし。

文書を生成するプログラムは大抵出力形式はHTMLかPDFが多いです。しかし出力したドキュメントを人力で修正あるいはプレゼン資料に利用することが多いですからoffice形式の出力も欲しいです。PDFならlatexを使いますから見た目の綺麗さはこっちなんですけど成果物の再利用のしやすさのほうが重要です。

フォント回りも和文の成果物を作る時は問題になります。マイナー言語である日本語の対応がいまいちなソフトなんてたくさんありますからね。

とりあえず上記の条件を満たせそうで開発が継続しているパッケージは多分以下の3つだと思います。

gt

flextble

huxtable

この中でdocxのサポートを明記しているのはflextableとhuxtableですが、huxtableはdocx出力にはflextableを使用しているらしいので、単体でdocxに対応しているのはflextableのみです。

そのため結果はある程度見えているのですが、一応gtとhuxtableも試してみました。バージョンアップで変わってるかもしれないし。

テスト環境

OS:

windows 11

開発環境:

  • Rstudio 2024.12.1 Build 563
  • R for windows 4.4.3

パッケージ:

  • tidyverse 2.0.0
  • gt 0.11.1
  • flextable 0.9.7
  • officer 0.6.7 (flextableと併用)
  • huxtable 5.6.0

各テーブルパッケージを使ってirisデータセットを表として出力します。

出力フォーマットはrtfまたはdocxとしました。また出力はパッケージの関数とRmarkdownの両方を試します。

テーブルのフォントはMS明朝とし、列幅は極力自動調整とします。罫線は水平線のみ表示します。

データは以下のコードで作成します。ヘッダー列もデータフレーム内に格納することにしました。

header <- tribble(
  ~"out1",~"out2",~"out3",~"out4",~"out5",
   "がく片の長さ", "がく片の幅", "花弁の長さ","花弁の幅", "種類"
)

dat <- iris %>%
  head(10) %>%
  dplyr::mutate(
    out1=as.character(Sepal.Length),
    out2=as.character(Sepal.Width),
    out3=as.character(Petal.Length),
    out4=as.character(Petal.Width),
    out5=as.character(Species)
  ) %>%
  dplyr::select(starts_with("out"))

res <- bind_rows(header, dat)
出力データテーブル出力するデータ

実行結果

docxファイル

gt

以下のコードでテーブルオブジェクトを生成します。Rmarkdownの場合はこのコードをチャンク内に記述し、gtオブジェクトをそのまま表示します。

gt <- res %>%
  gt() %>%
  tab_options(
    column_labels.hidden = TRUE,
    table.font.names = "MS Mincho",
    table.border.top.style = "solid",
    table.border.top.width = 3,
    table.border.top.color = "black",
    table.border.bottom.style = "solid",
    table.border.bottom.width = 3,
    table.border.bottom.color = "black",
    table_body.hlines.width = 0) %>%
  
tab_style(
  
  style = list(
        cell_borders(
          sides = "bottom",
          color = "#black",
          weight = 3
        )),
        
      locations=cells_body(rows = 1)
      ) 

 

gtsave関数でdocx及びrtf形式で出力します。

gtsave(gt,"gt_output.docx")

 

出力ファイルをwordの印刷プレビューで開いてスクリーンショットを取った結果は以下の通りです。

docx出力結果(gt, Rmarkdown)docx出力結果(gt, Rmarkdown)

 

docx出力結果(gt, gtsave)docx出力結果(gt, gtsave)

 

gtはすべての設定がwordに反映されていません。一方htmlで出力すると反映されているように見えました。wordだけははやり完全に対応していないようです。

flextable

gtと同様にテーブルオブジェクトを作成します。

ft <- res %>%

  flextable::flextable() %>%
  flextable::delete_part() %>%
  flextable::hline_top(border=fp_border()) %>%
  flextable::hline_bottom(border=fp_border()) %>%
  flextable::hline(i=1, border=fp_border()) %>%
  flextable::font(fontname="MS Mincho", part="all") %>%
  flextable::bold(i=1, part="body") %>%
  flextable::align(align="center", part="all") %>%
  flextable::autofit(add_w = 5, unit="mm")

 

flextableもdocx出力する関数が用意されていますのでこちらを使います。

flextable::save_as_docx(ft, path="flextable_docx_output.docx")
docx出力結果(flextable, Rmarkdown)docx出力結果(flextable, Rmarkdown)

 

docx出力結果(flextable, save_as_docx())docx出力結果(flextable, save_as_docx)

 

Rmarkdown, 出力関数ともにフォント設定も問題なく正しく出力できました。

huxtable

gtと同様にテーブルオブジェクトを作成します。

ht <- res %>%   
    huxtable::hux(add_colnames = FALSE) %>%
    huxtable::set_font(row=1:length(res$out1),  value="MS Mincho") %>%
    huxtable::set_width(1) %>%
    huxtable::set_align(col=everywhere, value="center") %>%
    huxtable::set_top_border(row=1, col=everywhere) %>%
    huxtable::set_bold(row=1, value=TRUE) %>%
    huxtable::set_bottom_border(row=1, col=everywhere) %>%
    huxtable::set_bottom_border(row=length(res$out1), col=everywhere) 

huxtableの場合はquick_*関数を使う方が簡単なので、quick_docx関数を使用します。

huxtable::quick_docx(ht, file="huxtable_docx_output.docx")
docx出力結果(huxtable, Rmarkdown)docx出力結果(huxtable, Rmarkdown)
docx出力結果(huxtable, quick_docx)docx出力結果(huxtable, quick_docx)

 

罫線は問題ないですが、Rmarkdown、出力関数ともにフォントが正しく設定できていません。

rtfファイル

RTFの場合はRmarkdownのYAMLヘッダに設定を記述するか、RTF出力関数を使用すれば出力できます。

gt

gtsave(gt,"gt_output.rtf")
rtf出力結果(gt, Rmarkdown)rtf出力結果(gt, Rmarkdown)
rtf出力結果(gt, gtsave)rtf出力結果(gt, gtsave)

 

rtfファイルもdocxファイル同様全く設定が反映されていません。

flextable

flextable::save_as_rtf(ft,path="flextable_rtf_output.rtf")
rtf出力結果(flextable, Rmarkdown)rtf出力結果(flextable, Rmarkdown)
rtf出力結果(flextable, save_as_rtf)

 

出力関数を使用した場合は正常に出力できましたが、Rmarkdownの場合はフォントが正しく設定できませんでした。さらに画像ファイルとして出力されています。

huxtable

huxtable::quick_rtf(ht, file="huxtable_rtf_output.rtf")
rtf出力結果(huxtable, Rmarkdown)rtf出力結果(huxtable, Rmarkdown)
rtf出力結果(huxtable, quick_rtf)rtf出力結果(huxtable, quick_rtf)

 

出力関数を使用した場合はフォント含めて正しく出力されていますが、Rmarkdownの場合はフォントが正しく設定できていません。

結論

docxファイルを出力する場合は flextable一択です。

rtfファイルを出力する場合はflextableまたはhuxtableですが、正常に出力できるのは出力関数を使用した場合であり、Rmarkdownのチャンクに記述しても正しく出力できませんでした。

出力関数で作成したrtfファイルをRmarkdownに埋め込む方法も試しました。RTFファイルはテキストファイルなので、readlines関数を用いてRmarkdownにRTFファイルを挿入することができます。

---
title: "insert RTF document"
output: rtf_document
---
  
## Flextable
  
```{r, echo=FALSE}

knitr::raw_output(readLines('flextable_rtf_output.rtf'))
  
```

ファイル自体は出力されるのですが、フォント設定が消えてしまうし、なぜかフォントが赤になってしまうのでこの方法は使えませんでした。

rtfはあくまでCSRに挿入する用途で使用し、テーブル以外の要素は出力しない(つまりRTFを最終的な成果物としては使わない)と割り切った方がいいと感じました。

一方docxの場合はRmarkdownのYAMLヘッダでreference documentを設定すると、ヘッダフッタが設定してあるひな形docxファイルをロードして文書を作成してくれます。成果物をそのまま配布、提出するのであればdocx形式のほうがいいと思いました。

RTFにヘッダフッタを設定するためにはpharmaRTF等を使えば行けると思いますが、pharmaRTFはあまりアップデートされておらず、ちゃんとできるのかは見てみないとわかりません。次回ブログのテーマにします。

所感

どのパッケージも少々癖のある操作だと感じましたが、何度か使えば設定ルールはすんなり覚えられそうです。

例えばflextableにおいて任意の位置に水平線を引くにはflextable::hline()関数を使います。引数iは行番号を指定するんですよね。。引数の名前はrowじゃないんかい。

flextable::hline(i=行番号, 
                 border=fp_borderオブジェクト, 
                 part=場所("all", "header", "body"))

 

ちなみに引数jは列番号を指定できます。

border引数には水平線の設定を渡すのですが、officerパッケージのfp_border関数で指定するルールです。「fp_border()」と指定すればデフォルトの色=黒、幅=1の設定を渡すことができます。論文のような体裁ならデフォルトのままでOKでしょう。

huxtableやgtも同様で独自の文法が存在するので最初は苦戦しそうです。特にhuxtableは公式ドキュメントのexampleに実際のレンダリング例が記載されていないので猶更めんどくさい。とはいえ紙ベースの成果物を作るのであれば概ね同じコードを使いまわすことになりそうなので、最初に検討すればあとはスムーズでしょう。