Rでメタアナリシス:kableExtraパッケージで表を作る
1. 始めに
1.1. この記事の趣旨
こんにちは、nekometaと申します。「Rでメタアナリシス」では、GNU R(以下、R)で要約データを用いたランダム化比較試験のメタアナリシスを行い、代表的な図表を作成する方法を説明します。今回はRで臨床試験の特徴を要約する表を作る方法を説明します。ポイントは以下の2つです。
- 表作成は
kableExtra
パッケージが便利 - 文字列結合用の二項演算子を定義すると便利
1.2. 背景
通常、メタアナリシスの論文では解析に使用した臨床試験の特徴を表形式で要約します。Microsoft Wordなどで作表してもよいのですが、Rで出力できたら手作業を減らせて楽ですね。そこで、Rの kableExtra
パッケージを利用します。
kableExtra
は knitr
パッケージの表作成関数 kable()
を強化し、複雑な作表を可能にするパッケージです。knitr
はQuartoやR Markdownといったドキュメント生成システムの一部です。今回は、Quartoを使用する場合を想定して説明します。
1.3. 目標
Microsoft Wordで作成した表の例を示します。これとできるだけ近い表をRで出力することを目指します。
1.4. 環境
- OS:Windows 10 Pro(バージョン:21H2、OSビルド19044.1645)
- WSL2-Ubuntu 20.04.4 TLS
- Docker Desktop 4.7.1(WSL2バックエンド)
- Docker Engine version 20.10.14
- Docker Compose version v2.4.1
- rocker/rstudio:4.2.0
- installed R packages:
checkpoint
,pacman
,rmarkdown
- checkpoint date: 2022-04-21
この記事で使用した解析プロジェクトの構成は以下の通りです。
【解析プロジェクトのテンプレート】
【主要な設定ファイルの説明】
2. 解析コード
2.1. セットアップチャンク
2.1.1. コードの例
analysis.qmdというファイルに解析コードを記載します。冒頭に記載する一連のコード(通称:セットアップチャンク)の例を示します。
```{r}
#| label: setup
#| code-fold: false
# set working directory
setwd("~/project/codes")
# change processing of missing values in data frames
options(na.action = "na.pass")
# save default graphics parameters
oldpar <- par(no.readonly = TRUE)
# attach the package 'checkpoint'
library(checkpoint)
# set checkpoint date
checkpoint("2022-04-21", checkpoint_location = "../", scan_now = FALSE)
# attach packages
library(pacman)
p_load(kableExtra)
# load my scripts
source("functions.R")
# load data sets
df_00 <- read.csv("datasets/00_char.csv", na.strings = c("", "NULL"),
colClasses = "character")
```
2.1.2. コードの説明
# set working directory
setwd("~/project/codes")
analysis.qmdを配置した ~/project/codes
ディレクトリをワーキングディレクトリに設定しています。
# change processing of missing values in data frames
options(na.action = "na.pass")
# save default graphics parameters
oldpar <- par(no.readonly = TRUE)
今回の記事には関係ありませんが、ここで作図用の設定をしています。
-
options(na.action = "na.pass")
-
na.action
で欠損値がある研究の扱いを変更できます。既定値の"na.omit"
だと、欠損値のある研究が図から除外されます。ここでは、"na.pass"
とすることで、欠損値のある研究も図に含めるよう設定しています。
-
-
oldpar <- par(no.readonly = TRUE)
-
oldpar
というオブジェクトに、デフォルトの作図関連のパラメーターを退避します。図によってpar
の設定が変わるので、毎回描画が終わるタイミングでpar(oldpar)
とし設定を復旧することで、描画トラブルを防ぎます。
-
# attach the package 'checkpoint'
library(checkpoint)
# set checkpoint date
checkpoint("2022-04-21", checkpoint_location = "../", scan_now = FALSE)
checkpoint
パッケージをアタッチしてチェックポイント日を指定し、以後呼び出すパッケージのバージョンを固定します。
# attach packages
library(pacman)
p_load(kableExtra, metafor, robvis)
pacman
パッケージをアタッチし、p_load()
で解析に使用するパッケージを読み込みます(パッケージがインストールされていない場合は、インストールされます)。
# load my scripts
source("functions.R")
~/project/codes
ディレクトリにfunctions.R
というファイルを作成し、自作関数を記載して読み込みます。今回使用する自作関数は以下の通りです。
# define a binary operator to concatenate strings
"%cs%" <- function(x, y) paste0(x, y)
Rではあらかじめさまざまな二項演算子が定義されていますが、自分で二項演算子を定義することもできます。詳細は "R Language Definition" の "3.1.4 Operators"を参照してください。ここでは2つのオブジェクトを文字列に変換したうえで結合し、文字列ベクトルとして返す関数 paste0()
を利用して、二項演算子 %cs%
を定義しています。
使い方は単純で、"A" %cs% "B" %cs% "C"
とすれば、文字列"ABC"を得ることができます。paste0()
でも関数を入れ子にすれば同じ結果を得ることができますが、二項演算子のほうがシンプルに書けます。
# load data sets
df_00 <- read.csv("datasets/00_char.csv", na.strings = c("", "NULL"),
colClasses = "character")
セットアップチャンクの最後に、データセットを読み込みます。
- df_00
- 臨床試験の特徴を要約する情報を含みます。
- 統計解析には使用しません。
-
colClasses = "character"
とすることで、すべての情報を文字列として読み込みます。
2.2. 文字列の処理
2.2.1. コードの例
データフレーム df_00
の中身は以下のようになっています。
構造は単純で、臨床試験ごとに、被験薬群(test)と対照薬群(control)別に、各要素の平均値と標準偏差(SD)が格納されています。たとえば、各群の被験者の年齢は、以下の4つの列に格納されています。
- test_age
- test_age_sd
- control_age
- control_age_sd
これを、%cs%
を使って結合し、表出力用のデータフレームを作ります。各セルの1行目に被験薬群の平均値±SD、2行目に対照薬群の平均値±SDが来るように、スラッシュと改行記号も入れます。
57.0±9.0/
58.0±11.0
コードは以下のとおりです。
```{r}
#| label: df_char
# create Table 1
## combine strings
c_N <- (as.numeric(df_00$test_number) +
as.numeric(df_00$control_number)) %cs% " [" %cs%
df_00$test_number %cs% "/" %cs%
df_00$control_number %cs% "]"
c_age <- df_00$test_age %cs% "±" %cs%
df_00$test_age_sd %cs% "/\n" %cs%
df_00$control_age %cs% "±" %cs%
df_00$control_age_sd
c_baseline_HbA1c <- df_00$test_baseline_HbA1c %cs% "±" %cs%
df_00$test_baseline_HbA1c_sd %cs% "/\n" %cs%
df_00$control_baseline_HbA1c %cs% "±" %cs%
df_00$control_baseline_HbA1c_sd
c_followup_HbA1c <- df_00$test_followup_HbA1c %cs% "±" %cs%
df_00$test_followup_HbA1c_sd %cs% "/\n" %cs%
df_00$control_followup_HbA1c %cs% "±" %cs%
df_00$control_followup_HbA1c_sd
c_FPG <- df_00$test_FPG %cs% "±" %cs%
df_00$test_FPG_sd %cs% "/\n" %cs%
df_00$control_FPG %cs% "±" %cs%
df_00$control_FPG_sd
c_BMI <- df_00$test_BMI %cs% "±" %cs%
df_00$test_BMI_sd %cs% "/\n" %cs%
df_00$control_BMI %cs% "±" %cs%
df_00$control_BMI_sd
c_T2DM <- df_00$test_T2DM %cs% "±" %cs%
df_00$test_T2DM_sd %cs% "/\n" %cs%
df_00$control_T2DM %cs% "±" %cs%
df_00$control_T2DM_sd
## create a data-frame for Table 1
df_char <- data.frame(df_00$study, df_00$design, df_00$country, c_N, c_age,
c_baseline_HbA1c, c_followup_HbA1c, c_FPG, c_T2DM,
df_00$test_arm, df_00$control_arm, df_00$weeks)
colnames(df_char) <- c("Study", "Design", "Country",
"Number of patients\n[Experimental vs. Control]",
"Baseline age (years)\n[Experimental vs. Control]",
"Baseline HbA1c (%)\n[Experimental vs. Control]",
"Follow-up HbA1c (%)\n[Experimental vs. Control]",
"Baseline FPG (mg/dL)\n[Experimental vs. Control]",
"Type2 DM duration (years)\n[Experimental vs. Control]",
"Experimental\narm", "Control\narm",
"Therapy duration (weeks)")
## overwrite cells with missing values
df_char[6, 9] <- as.character("NA")
```
2.2.2. コードの説明
前処理部分は繰り返しが多いので、一部を抜粋します。
c_N <- (as.numeric(df_00$test_number) +
as.numeric(df_00$control_number)) %cs% " [" %cs%
df_00$test_number %cs% "/" %cs%
df_00$control_number %cs% "]"
数値処理がある場合は、as.numeric()
で数値型(numeric)に変換して計算します。ここでは各群の合計を計算しています。
結合した文字列ベクトルを c_N
というオブジェクトに格納しています。表に出力すると、以下のように表示されます。
398 [199/199]
c_age <- df_00$test_age %cs% "±" %cs%
df_00$test_age_sd %cs% "/\n" %cs%
df_00$control_age %cs% "±" %cs%
df_00$control_age_sd
数値処理がない場合は、文字列を %cs%
で結合するだけです。\n
は改行を指示するエスケープ処理です。
結合した文字列ベクトルを c_age
というオブジェクトに格納しています。表に出力すると、以下のように表示されます。
57.0±9.0/
58.0±11.0
## create a data-frame for Table 1
df_char <- data.frame(df_00$study, df_00$design, df_00$country, c_N, c_age,
c_baseline_HbA1c, c_followup_HbA1c, c_FPG, c_T2DM,
df_00$test_arm, df_00$control_arm, df_00$weeks)
colnames(df_char) <- c("Study", "Design", "Country",
"Number of patients\n[Experimental vs. Control]",
"Baseline age (years)\n[Experimental vs. Control]",
"Baseline HbA1c (%)\n[Experimental vs. Control]",
"Follow-up HbA1c (%)\n[Experimental vs. Control]",
"Baseline FPG (mg/dL)\n[Experimental vs. Control]",
"Type2 DM duration (years)\n[Experimental vs. Control]",
"Experimental\narm", "Control\narm",
"Therapy duration (weeks)")
文字列の結合が終わったら、データフレームを作成します。
-
data.frame()
で表を出力するためのデータフレームdf_char
を作成します。前処理をせずに使える項目はdf_00$study
のようにdf_00
から列名を指定して読み込み、前処理をした項目はc_N
のように結合した文字列ベクトルを格納したオブジェクトを読み込みます。 -
colnames()
で列名をつけます。ここが、後ほど表のヘッダーになります。
## overwrite cells with missing values
df_char[6, 9] <- as.character("NA")
欠損値があるセルをそのままにすると、以下のように表示されます。
NA±NA/
NA±NA
ここを単に "NA" と表示するように、データフレームの当該要素を as.character("NA")
で上書きします。
2.3. 表の出力
2.3.1. コードの例
前処理が終わったので、df_char
の内容を表に出力します。コードは以下の通りです。
```{r}
#| label: charastaristics
#| tbl-cap: "Table 1: Charastaristics of the seven studies"
## output Table 1
kbl(df_char, align = "l") |>
kable_classic(html_font = "Times New Roman",
font_size = 11, full_width = FALSE) |>
footnote(general = c(
"Data are mean ± SD unless otherwise stated. In the study of Vanderheiden et al. (2016), the type 2 DM duration among all patients was 17.9 ± 8.4 years.",
"Abbreviations: SD: standard deviation; R: randomized; DB: double-blinded; PC: placebo-controlled; HbA1c: hemoglobin A1c; FPG: fasting plasma glucose;",
"BMI: body mass index; DM: diabetes mellitus; IDegLira: fixed-ratio combination of insulin degludec and liraglutide; NA: not available."),
general_title = "")
```
2.3.2. コードの説明
#| label: charastaristics
#| tbl-cap: "Table 1: Charastaristics of the seven studies"
Quartoのチャンクオプション tbl-cap
に、表のタイトルを記述します。既定値ではタイトルの位置は表の上ですが、QuartoのYAMLヘッダーで tbl-cap-location
の値を指定することで変更可能です。詳細は公式ドキュメントを参照してください。
## output Table 1
kbl(df_char, align = "l") |>
kable_classic(html_font = "Times New Roman",
font_size = 11, full_width = FALSE) |>
footnote(general = c(
"Data are mean ± SD unless otherwise stated. In the study of Vanderheiden et al. (2016), the type 2 DM duration among all patients was 17.9 ± 8.4 years.",
"Abbreviations: SD: standard deviation; R: randomized; DB: double-blinded; PC: placebo-controlled; HbA1c: hemoglobin A1c; FPG: fasting plasma glucose;",
"BMI: body mass index; DM: diabetes mellitus; IDegLira: fixed-ratio combination of insulin degludec and liraglutide; NA: not available."),
general_title = "")
-
kbl()
でデータフレームを読み込んで作表します。 -
kable_classic()
で論文向きのスタイルを当てます。 -
footnote()
で脚注を追加します。リスト形式で値を渡すと、各要素を行として認識します。
その他、利用可能なオプションについてはCreate Awesome HTML Table with knitr::kable and kableExtraを参照してください。
2.3.3. 出力結果
kableExtra
パッケージによる表の出力結果は以下の通りです。
便利なことに、体裁を保ったままMicrosoft Wordに表を貼り付けることができます。これでほぼ目的としたレベルの表が作成できました(今回、対応を忘れていましたが、R側で "±" の前後にnon-breaking spaceを入れてもよいかもしれません)。あとはMicrosoft Wordで以下の処理をすれば完成です(頑張ればRでもできますが、効率が悪いのでオススメしません)。
- スタイルの割り当て
- 各行の幅調整
- 引用文献の追加(Zoteroなどの文献管理ソフトウェアと連携)
3. まとめ
今回は文字列結合用の二項演算子を定義したうえで、kableExtra
パッケージで臨床試験の特徴を要約する表を作成しました。今回作成した表はこのまま査読を通過するレベルですが、よりスマートな列の構成を練ってもよいでしょう。紙幅が許せば、ヘッダー部分に "Experimental" と "Control" という小分類を追加してもよいかもしれません。そのような複雑な表も、kableExtra
パッケージで作成可能です。
Author And Source
この問題について(Rでメタアナリシス:kableExtraパッケージで表を作る), 我々は、より多くの情報をここで見つけました https://zenn.dev/nekometa/articles/220508_rma_char著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol