29 見やすい表の作り方

この章では flextable パッケージを使って、要約したデータフレームを見やすい表に変換する方法を紹介します。これらの表は、パワーポイントのスライド、HTML ページ、PDF や Word 文書などに挿入することができます。

flextable パッケージを使用する に、要約表をデータフレームとして作成する必要があります。記述統計表の作り方およびデータの縦横変換の章に紹介されている方法を使い、表計算、クロス集計などを作成してください作成されたデータフレームを flextable に渡して、見やすいように書式設定を行うことができます。

本章では flextable パッケージを紹介していますが、他にも見やすい表を作成するために利用できる R パッケージはたくさんあります。 knitr パッケージとそこに含まれる kable() を使った例は、接触者の追跡の 章で紹介しています。同様に、DT パッケージは Shiny で作るダッシュボード の章で紹介しています。GT パッケージや huxtable パッケージのような他のパッケージは、推奨するパッケージ の章で紹介しています。

29.1 準備

パッケージの読み込み

flextable パッケージをインストールして読み込みましょう。以下のコードを実行すると、分析に必要なパッケージが読み込まれます。このハンドブックでは、パッケージを読み込むために、pacman パッケージの p_load() を主に使用しています。p_load() は、必要に応じてパッケージをインストールし、現在の R セッションで使用するためにパッケージを読み込む関数です。また、すでにインストールされたパッケージは、R の基本パッケージである base (以下、base R)の library() を使用して読み込むこともできます。R のパッケージに関する詳細は R の基礎 の章をご覧ください。

pacman::p_load(
  rio,            # インポート/エクスポート
  here,           # ファイルパス指定
  flextable,      # HTML表を作成 
  officer,        # 作表に関するヘルパー関数
  tidyverse)      # データ管理、要約、ビジュアライゼーション

データをインポート

エボラ出血熱の流行をシミュレートしたデータセットをインポートします。お手元の環境でこの章の内容を実行したい方は、 クリックして「前処理された」ラインリスト(linelist)データをダウンロードしてください>(.rds 形式で取得できます)。データは rio パッケージの import() を利用してインポートしましょう(rio パッケージは、.xlsx、.csv、.rds など様々な種類のファイルを取り扱うことができます。詳細は、インポートとエクスポート の章をご覧ください。)

# ラインリストをインポート
linelist <- import("linelist_cleaned.rds")

ラインリスト の最初の 50 行を以下に表示します。

テーブルの準備

flextable パッケージを使い始める に、テーブルをデータフレームとして 作成する 必要があります。janitordplyr などのパッケージを利用してデータフレームを作成する方法を学ぶには 記述統計表の作り方およびデータの縦横変換の章をご覧ください。表を表示したい内容に行と列を整えることが必要です。その後、データフレームを flextable に渡して、色、ヘッダ、フォントなどを設定していきます。

以下は、 記述統計表の作り方 の章の例で、症例 linelist を病院ごとに患者の転帰と CT 値をまとめたデータフレームに変換し、下部に合計(Total)行を設定したものです。出力結果を table として保存します。

table <- linelist %>% 
  
  # 病院と転帰のグループごとの要約量を取得
  ###############################################
  group_by(hospital, outcome) %>%                      # データのグループ化
  summarise(                                           # 関心のある指標の要約する列を新規作成
    N = n(),                                            # 病院-転帰グループごとの行数     
    ct_value = median(ct_blood, na.rm=T)) %>%           # グループごとのCTの中央値
  
  # 合計行を追加
  ############
  bind_rows(                                           # 前の表とこのミニ表の合計を結合する
    linelist %>% 
      filter(!is.na(outcome) & hospital != "Missing") %>%
      group_by(outcome) %>%                            # hospitalごとではなく、outcomeのみでグループ化     
      summarise(
        N = n(),                                       # データセット全体の行数     
        ct_value = median(ct_blood, na.rm=T))) %>%     # データセット全体のCTの中央値
  
  # 横にピボット変換してフォーマット
  ########################
  mutate(hospital = replace_na(hospital, "Total")) %>% 
  pivot_wider(                                         # 縦から横への縦横変換
    values_from = c(ct_value, N),                       # CT値とカウント列からの新規の値
    names_from = outcome) %>%                           # 転帰を新しい列名に
  mutate(                                              # 新しい列の追加
    N_Known = N_Death + N_Recover,                               # 転帰がわかっている症例数
    Pct_Death = scales::percent(N_Death / N_Known, 0.1),         # 死亡症例のパーセント(小数点1桁)
    Pct_Recover = scales::percent(N_Recover / N_Known, 0.1)) %>% #回復症例のパーセント(小数点1桁)
  select(                                              # 列の再並び替え
    hospital, N_Known,                                   # 最初の列
    N_Recover, Pct_Recover, ct_value_Recover,            # 回復症例の列
    N_Death, Pct_Death, ct_value_Death)  %>%             # 死亡症例の列
  arrange(N_Known)                                    # 行を低い順から高い順に(合計は最下部)

table  # 出力
## # A tibble: 7 × 8
## # Groups:   hospital [7]
##   hospital               N_Known N_Rec…¹ Pct_R…² ct_va…³ N_Death Pct_D…⁴ ct_va…⁵
##   <chr>                    <int>   <int> <chr>     <dbl>   <int> <chr>     <dbl>
## 1 St. Mark's Maternity …     325     126 38.8%        22     199 61.2%        22
## 2 Central Hospital           358     165 46.1%        22     193 53.9%        22
## 3 Other                      685     290 42.3%        21     395 57.7%        22
## 4 Military Hospital          708     309 43.6%        22     399 56.4%        21
## 5 Missing                   1125     514 45.7%        21     611 54.3%        21
## 6 Port Hospital             1364     579 42.4%        21     785 57.6%        22
## 7 Total                     3440    1469 42.7%        22    1971 57.3%        22
## # … with abbreviated variable names ¹​N_Recover, ²​Pct_Recover,
## #   ³​ct_value_Recover, ⁴​Pct_Death, ⁵​ct_value_Death

29.2 flextable の基本

flextable を作成する

flextable オブジェクトを作成・管理するために、まずデータフレームを flextable() に渡します。その結果を my_table として保存します。

my_table <- flextable(table) 
my_table

このようにした後、 my_table オブジェクトをさらに flextable パッケージに含まれる表をフォーマットするための関数に次々とパイプ演算子で渡していくことができます。

この章では、わかりやすくするために、途中の段階でテーブルを my_tableとして保存し、段階的に flextable の機能を追加していきます。最初から最後までの すべて のコードをまとめて見たい場合は、下記のすべてのコードをまとめるセクションをご覧ください。

flextable パッケージのコードの各行の一般的な構文は以下の通りです。:

  • function(table, i = X, j = X, part = "X"), と記載して:
    • ‘function’ には、列の幅を決める width() 、背景色を設定する bg() 、テキストを中央/右/左に揃えるかどうかを設定する align() など、さまざまな関数のうちのひとつを指定することができます。
    • table = は、データフレームの名前ですが、データフレームを関数にパイプ演算子で渡している場合は、記述する必要はありません。
    • part = 関数がテーブルのどの部分に適用されるかを示します。例:“header”、“body”、“all”。
    • i = 関数を適用する を、‘X’ に行番号を入力して指定します。複数の行、例えば 1 行目から 3 行目までを指定する場合は、 i = c(1:3) とします。なお、 ‘body’ を選択した場合、最初の行はヘッダセクションの下から始まります。
    • j = は、関数を適用する を、‘x’ に列番号または列名を入力して指定します。複数の列、例えば 5列目と6列目を指定する場合は、 j = c(5,6) とします。

flextable パッケージのフォーマット関数の全リストは ここ でご覧いただけます。また、 ?flextableと入力してドキュメントを確認することもできます。

列の幅

autofit() を使うと、各セルに 1 行分のテキストしか入らないように表をうまい具合に引き伸ばすことができます。qflextable() は、 flextable() および autofit()を簡潔にした便利なものです。

my_table %>% autofit()

しかし、セル内の値が非常に長く、表がページに収まらない場合など、必ずしも適切ではない場合があります。

その代わりに、 width() で幅を指定することができます。以下の例では、1 列目、2 列目、4 列目から 8 列目にそれぞれ異なる幅を指定しています。

my_table <- my_table %>% 
  width(j=1, width = 2.7) %>% 
  width(j=2, width = 1.5) %>% 
  width(j=c(4,5,7,8), width = 1)

my_table

列のヘッダ

表の内容をわかりやすくするために、ヘッダをより明確にしたいことがあります。

この表では、同じサブグループをカバーする列がグループ化されるように、2 つ目のヘッダレイヤを追加したいと思います。そのために、 top = TRUEadd_header_row() を使用します。各列の新しい名前を values =に指定し、 後で統合することがわかっている列には空の値"" を指定します。

また、別の set_header_labels() コマンドで、2 つ目のヘッダのヘッダ名を変更します。

最後に、特定の列のヘッダをトップヘッダに「結合」するために、 merge_at() を使用してトップヘッダ行の列のヘッダを結合します。

my_table <- my_table %>% 
  
  add_header_row(
    top = TRUE,                # 既存のヘッダ列の上に新しいヘッダを配置
    values = c("Hospital",     # 各列のヘッダ値は以下の通り
               "Total cases with known outcome", 
               "Recovered",    # この列と次の2つの列のトップレベルのヘッダ
               "",
               "",
               "Died",         # この列と次の2つの列のトップレベルのヘッダ
               "",             # 「死亡」と統合させるので空欄のまま
               "")) %>% 
    
  set_header_labels(         # 元のヘッダ行の列名を変更
      hospital = "", 
      N_Known = "",                  
      N_Recover = "Total",
      Pct_Recover = "% of cases",
      ct_value_Recover = "Median CT values",
      N_Death = "Total",
      Pct_Death = "% of cases",
      ct_value_Death = "Median CT values")  %>% 
  
  merge_at(i = 1, j = 3:5, part = "header") %>% # 3列目から5列目までを新しいヘッダ行に水平に結合
  merge_at(i = 1, j = 6:8, part = "header")     # 6列目から8列目までを新しいヘッダ行に水平に結合

my_table  # print

罫線と背景

罫線や内線などの調整は、さまざまな flextable パッケージの関数で行うことができます border_remove()で既存の罫線をすべて削除することから始めると、簡単な場合が多いです。

そして、 theme_box()theme_booktabs()theme_alafoli()にテーブルを渡すことで、デフォルトの罫線テーマを適用することができます。

多彩な機能で縦線や横線を入れることができます。 hline()vline() は、それぞれ指定した行や列に線を追加する関数です。それぞれの関数の中で、 part = の部分を “all”、“body”、“header” のいずれかに指定する必要があります。縦線の場合は j =までの列を、横線の場合は i =までの行を指定します。また、 vline_right()vline_left()hline_top()hline_bottom() などの関数は、外側のみに線を追加します。

これらのすべての関数において、実際の罫線のスタイル自体は border = で指定する必要があり、 officer パッケージの fp_border() を使用した別のコマンドで出力しなければなりません。この関数は、線の幅と色を定義するのに役立ちます。以下のように、表コマンドの上で定義することができます。

# 罫線のスタイルを定義する
border_style = officer::fp_border(color="black", width=1)

# 表に罫線を追加
my_table <- my_table %>% 

  # 既存の罫線をすべて削除
  border_remove() %>%  
  
  # 既定のテーマ設定のまま水平線を追加
  theme_booktabs() %>% 
  
  # 回復症例 と 死亡症例のセクションを分けるために縦線を追加
  vline(part = "all", j = 2, border = border_style) %>%   # 2列目
  vline(part = "all", j = 5, border = border_style)       # 5列目

my_table

フォントと配置

flextable パッケージの align() を使って、病院名のある左端の列以外のすべての列を中央揃えにします。

my_table <- my_table %>% 
   flextable::align(align = "center", j = c(2:8), part = "all") 
my_table

さらに、ヘッダのフォントサイズを大きくして、ボールドに変更することもできます。また、全体の行を太字にすることもできます。

my_table <-  my_table %>%  
  fontsize(i = 1, size = 12, part = "header") %>%   # ヘッダのフォントサイズを調整
  bold(i = 1, bold = TRUE, part = "header") %>%     # ヘッダの太字を調整
  bold(i = 7, bold = TRUE, part = "body")           # 合計行の太字を調整する(本文7行目)

my_table

colformat_num() を使用して、比率の列のみを小数点 1 桁まで表示することができます。なお、これはデータ管理の段階で round() を用いて行うこともできます。

my_table <- colformat_num(my_table, j = c(4,7), digits = 1)
my_table

セルの結合

ヘッダ行で水平方向にセルを結合したように、merge_at() で行 (i) と列 (j) を指定して垂直方向にセルを結合することもできます。ここでは、「Hospital(病院)」と「Total cases with known outcome(転帰がわかっている症例の合計数)」の値を垂直方向に結合して、スペースを確保しています。

my_table <- my_table %>% 
  merge_at(i = 1:2, j = 1, part = "header") %>% 
  merge_at(i = 1:2, j = 2, part = "header")

my_table

背景色

表の内容をヘッダと区別するために、背景色の変更などの書式設定を追加したい場合があります。この例では、表の本文をグレーに変更します。

my_table <- my_table %>% 
    bg(part = "body", bg = "gray95")  

my_table 

29.3 条件付き書式設定

例えば、55% 以上の症例が死亡した場所など、あるルールを満たす列のすべての値を強調表示することができます。基準を i = または j = の引数に入れ、その前にチルダ ~を付けるだけです。表示する見出しの値ではなく、データフレームの列を参照します。

my_table %>% 
  bg(j = 7, i = ~ Pct_Death >= 55, part = "body", bg = "red") 

また、関心のある病院など、特定の基準を満たす行全体を強調表示することもできます。そのためには、列(j) の指定を外すだけで、基準がすべての列に適用されます。

my_table %>% 
  bg(., i= ~ hospital == "Military Hospital", part = "body", bg = "#91c293") 

29.4 すべてのコードをまとめる

以下に、上記のセクションのコードをすべてまとめて示します。

border_style = officer::fp_border(color="black", width=1)

pacman::p_load(
  rio,            # インポート/エクスポート
  here,           # ファイルパス指定
  flextable,      # HTML表を作成 
  officer,        # 作表に関するヘルパー関数
  tidyverse)      # データ管理、要約、ビジュアライゼーション

table <- linelist %>% 

  # 病院と転帰のグループごとの要約を取得
  ###############################################
  group_by(hospital, outcome) %>%                      # データのグループ化
  summarise(                                           # 関心のある指標の要約する列を新規作成
    N = n(),                                            # 病院-転帰グループごとの行数     
    ct_value = median(ct_blood, na.rm=T)) %>%           # グループごとのCTの中央値
  
  # 合計行を追加
  ############
  bind_rows(                                           # 前の表とこのミニ表の合計を結合する
    linelist %>% 
      filter(!is.na(outcome) & hospital != "Missing") %>%
      group_by(outcome) %>%                            # 病院をなくして転帰のみでグループ化     
      summarise(
        N = n(),                                       # データセット全体の行数     
        ct_value = median(ct_blood, na.rm=T))) %>%     # データセット全体のCTの中央値
  
  # ピボットの幅とフォーマット
  ########################
  mutate(hospital = replace_na(hospital, "Total")) %>% 
  pivot_wider(                                         # 縦持ちから横持ちへの縦横変換
    values_from = c(ct_value, N),                       # CT値とカウント列からの新規の値
    names_from = outcome) %>%                           # 転帰を新しい列名に
  mutate(                                              # 新しい列の追加
    N_Known = N_Death + N_Recover,                               # 転帰がわかっている症例数
    Pct_Death = scales::percent(N_Death / N_Known, 0.1),         # 死亡症例のパーセント(小数点1桁)
    Pct_Recover = scales::percent(N_Recover / N_Known, 0.1)) %>% # 回復症例のパーセント(小数点1桁))
  select(                                              # 列の再並び替え
    hospital, N_Known,                                   # 最初の列
    N_Recover, Pct_Recover, ct_value_Recover,            # 回復症例の列
    N_Death, Pct_Death, ct_value_Death)  %>%             # 死亡症例の列
  arrange(N_Known) %>%                                 # 行を低い順から高い順に(合計は最下部)

  # フォーマット
  ############
  flextable() %>%              # 表は上からパイプ演算子で渡す
  add_header_row(
    top = TRUE,                # 既存のヘッダ列の上に新しいヘッダを配置
    values = c("Hospital",     # 各列のヘッダ値は以下の通り
               "Total cases with known outcome", 
               "Recovered",    # この列と次の2つの列のトップレベルのヘッダ
               "",
               "",
               "Died",         # この列と次の2つの列のトップレベルのヘッダ
               "",             # 「死亡」と統合させるので空欄のまま
               "")) %>% 
    set_header_labels(         # 元のヘッダ行の列名を変更
      hospital = "", 
      N_Known = "",                  
      N_Recover = "Total",
      Pct_Recover = "% of cases",
      ct_value_Recover = "Median CT values",
      N_Death = "Total",
      Pct_Death = "% of cases",
      ct_value_Death = "Median CT values")  %>% 
  merge_at(i = 1, j = 3:5, part = "header") %>% # 3列目から5列目までを新しいヘッダ行に水平に結合
  merge_at(i = 1, j = 6:8, part = "header") %>%  
  border_remove() %>%  
  theme_booktabs() %>% 
  vline(part = "all", j = 2, border = border_style) %>%   # 2行目 
  vline(part = "all", j = 5, border = border_style) %>%   # 5行目
  merge_at(i = 1:2, j = 1, part = "header") %>% 
  merge_at(i = 1:2, j = 2, part = "header") %>% 
  width(j=1, width = 2.7) %>% 
  width(j=2, width = 1.5) %>% 
  width(j=c(4,5,7,8), width = 1) %>% 
  flextable::align(., align = "center", j = c(2:8), part = "all") %>% 
  bg(., part = "body", bg = "gray95")  %>% 
  bg(., j=c(1:8), i= ~ hospital == "Military Hospital", part = "body", bg = "#91c293") %>% 
  colformat_num(., j = c(4,7), digits = 1) %>%
  bold(i = 1, bold = TRUE, part = "header") %>% 
  bold(i = 7, bold = TRUE, part = "body")
## `summarise()` has grouped output by 'hospital'. You can override using the
## `.groups` argument.
table

29.5 表の保存

この表を出力方式にはいろいろな方法があります。

単一のテーブルとして保存

表を Word、PowerPoint、HTML、または画像(PNG)ファイルとしてエクスポートすることができます。これを行うには、次のいずれかの機能を使用します。:

  • save_as_docx()
  • save_as_pptx()
  • save_as_image()
  • save_as_html()

最初の引数の構文に注意してください - flextable オブジェクトの名前(例: my_table)だけを指定することもできますし、以下のように「名前」を指定することもできます(名前は “my table” です)。名前を付けると、Word で表のタイトルとして表示されます。PNG 画像として保存するコードもご紹介します。

# 表のタイトルに合わせ 'my table' を必要に応じて編集   
save_as_docx("my table" = my_table, path = "file.docx")

save_as_image(my_table, path = "file.png")

なお、flextable を画像として保存するには、 webshot または webshot2 パッケージが必要です。画像は背景が透明になることがあります。

flextable パッケージの出力の ‘live’ バージョンを意図したドキュメント形式で表示したい場合は、 print() を使用し、 preview =に以下のいずれかを指定します。ドキュメントは、指定したソフトウェアプログラムでコンピュータ上に「ポップアップ」して開きますが、保存はされません。この方法は表が1ページやスライドに収まるかを確認する場合や、別の文書に素早く表をコピーするような場合に便利です。引数の preview に “pptx” または “docx” を指定して print() を使用することができます。

print(my_table, preview = "docx") # Word文書の例
print(my_table, preview = "pptx") # Powerpointの例

R マークダウンで表を出力

この表は、表オブジェクトが R マークダウンチャンク内で呼び出された場合、自動化ドキュメントである R マークダウン出力に統合することができます。つまり、データが変更される可能性のあるレポートの一部としてテーブルを更新し、数値をリフレッシュすることができます。

詳細は、本ハンドブック R Markdown で作るレポート の章をご覧ください。

29.6 参考資料

flextable の完全解説版はこちら://ardata-fr.github.io/flextable-book/ The Github サイト こちら
flextable の全機能のマニュアルは こちらからご覧いただけます。

コード付きの美しい flextable の例を集めたギャラリーは こちらからご覧いただけます。