本文へスキップ
からだにいいもの

Rのトピックスを中心に『まだ、まだ、知らない、役に立つ情報?』を発信します。

Rで解析:WordとPowerPointをコードで操作する「officer」パッケージの紹介

officerは、Microsoft Word(.docx)とPowerPoint(.pptx)をRだけで生成・編集できるパッケージです。ファイルの内部形式であるOffice Open XMLを直接操作するため、Microsoft Office本体はもちろん外部ソフトを一切必要とせず、Windows・macOS・Linuxのいずれでも同じコードが動きます。空の文書をゼロから作るだけでなく、既存ファイルを雛形として読み込み、見出し・段落・表・画像・グラフの追加から文字列の一括置換までをコードで完結できます。分析のたびに手作業で資料を作り直す必要がなくなるため、集計結果や定型レポートを毎回同じ体裁で自動生成したい場面で特に威力を発揮します。flextableやrvg、mschartと組み合わせれば、体裁の整った表やベクター画像、ネイティブなグラフの埋め込みも可能です。

パッケージバージョンは0.7.6。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。

パッケージのインストール

下記コマンドを実行してください。

# officerパッケージをインストールする(初回のみ実行すればよい)
install.packages("officer")
# インストール済みのパッケージを読み込み、関数を使えるようにする
library("officer")
スポンサーリンク

コマンド例

詳細はコメント、パッケージのヘルプを確認してください。

Word文書オブジェクトを新規作成する:read_docxコマンド

read_docxはWord文書の操作の起点となる関数です。引数pathを省略すると空の新規文書を、既存の.docxや雛形の.dotxを渡すとそのファイルを読み込んだ状態のオブジェクトを取得します。ここでは新規作成した文書に、見出しや本文(body_add_par)、書式付きの段落(body_add_fpar・fpar・ftext)、データフレームの表(body_add_table)、Rの作図結果(plot_instr・body_add_plot)を順に追加し、1つの読書記録レポートを作成する例を示します。保存先は作業フォルダでTest.docxが作成されます。

オプション意味初期値
pathドキュメントの基礎となるdotxファイルのパスNULL
# テキスト・書式付き段落・表・グラフをセクションごとに構成した
# 文書を作成する例
# read_docx()で新規のWord文書オブジェクトを作成する
# 以降はこのdocに要素を追加しながら文書を組み立てていく
doc <- read_docx()
 
## 文字を追加する:body_add_parコマンド
# 文書の先頭にメインタイトルを「heading 1」スタイルで追加する
doc <- body_add_par(doc, "読書記録レポート", style = "heading 1")
 
# fparとftextで、一部だけ色や太字にした書式付きの導入文を作る
# ftextで装飾したい語句を、fp_text_liteでその書式(太字・色など)を指定する
intro_text <- fpar(
  "この文書は、今年の",
  ftext(
    "月別読書冊数の集計",
    fp_text_lite(bold = TRUE, color = "#C32900")
  ),
  "をまとめたものです。以下のセクションで詳細な傾向を紹介します。"
)
# 作成した書式付き段落をbody_add_fparで文書に追加する
doc <- body_add_fpar(doc, intro_text)
 
## セクション1:読書データ ----
# セクションの見出しを「heading 2」スタイルで追加する
doc <- body_add_par(doc, "月別読書冊数", style = "heading 2")
 
# 表の前に置く説明文を通常段落として追加する
doc <- body_add_par(
  doc,
  "下記の表はジャンルごとの読書冊数をまとめたものです。"
)
 
# 表の元になるデータフレームを作成する(月別の冊数・ページ数・前月比)
reading_data <- data.frame(
  Month = c("4月", "5月", "6月", "7月"),
  Books = c(3, 5, 4, 7),
  Pages = c(620, 980, 750, 1240),
  Growth = c("−", "58.1%", "-23.4%", "65.3%"),
  stringsAsFactors = FALSE
)
# body_add_tableでデータフレームを表として追加する(styleで表の体裁を指定)
doc <- body_add_table(doc, value = reading_data, style = "table_template")
 
# 数値の一部を強調した補足コメントを書式付き段落で作成する
comment <- fpar(
  "特筆すべき点:",
  ftext(
    "7月は最もページ数が多かった月",
    fp_text_lite(bold = TRUE, font.size = 11)
  ),
  "で、前月比",
  ftext("65.3%増加", fp_text_lite(color = "#006699", bold = TRUE)),
  "しました。"
)
doc <- body_add_fpar(doc, comment)
 
## セクション2:グラフ ----
# 2つ目のセクションの見出しを追加する
doc <- body_add_par(doc, "ページ数の推移", style = "heading 2")
 
# グラフの前に置く説明文を追加する
doc <- body_add_par(
  doc,
  "図1は月ごとのページ数の推移を示しています。"
)
 
# plot_instrの中にRの作図コードを書くと、その描画結果を図として取り込める
# ここでは月別ページ数の棒グラフを作成する
pages_plot <- plot_instr({
  months <- c("4月", "5月", "6月", "7月")
  pages <- c(620, 980, 750, 1240)
  barplot(
    pages,
    names.arg = months,
    col = "#4472C4",
    border = NA,
    main = "月別ページ数",
    ylab = "ページ数",
    xlab = "月",
    ylim = c(0, 1500)
  )
  # 目盛りを見やすくする補助線(横線のみ)を引く
  grid(nx = NA, ny = NULL, col = "gray90", lty = 1)
})
# 作成した棒グラフをbody_add_plotで文書に追加する(width・heightはインチ単位)
doc <- body_add_plot(doc, pages_plot, width = 6, height = 4)
 
## セクション3:分布図 ----
# 3つ目のセクションの見出しを追加する
doc <- body_add_par(doc, "1日あたりの読書時間の分布", style = "heading 2")
 
# 2つ目のグラフの説明文を書式付き段落で作成する
analysis_intro <- fpar(
  "下記の分布図は、",
  ftext("1日あたりの読書時間のばらつき", fp_text_lite(italic = TRUE)),
  "を示しています。生活リズムの変化を把握するのに役立ちます。"
)
doc <- body_add_fpar(doc, analysis_intro)
 
# 1日あたりの読書時間の密度プロット(分布図)を作成する
duration_plot <- plot_instr({
  # 説明用に、1日あたりの読書時間(分)の疑似データを生成する
  # set.seedで乱数を固定し、毎回同じ結果になるようにする
  set.seed(123)
  daily_minutes <- c(
    # 4〜6月:平均20分程度
    rnorm(90, mean = 20, sd = 8),
    # 7月:夏休みで増加傾向、平均35分程度
    rnorm(30, mean = 35, sd = 10)
  )
  # densityで推定した分布を折れ線で描画する
  plot(
    density(daily_minutes),
    main = "1日あたりの読書時間の分布",
    xlab = "読書時間(分)",
    ylab = "密度",
    col = "#C32900",
    lwd = 2
  )
  # 分布曲線の下側を半透明の色で塗りつぶす
  polygon(density(daily_minutes), col = rgb(0.76, 0.16, 0, 0.2), border = NA)
  # 平均値の位置に縦の破線を引く
  abline(v = mean(daily_minutes), col = "#006699", lwd = 2, lty = 2)
  # 平均値の破線の凡例を右上に表示する
  legend("topright", legend = "平均値", col = "#006699", lty = 2, lwd = 2)
})
doc <- body_add_plot(doc, duration_plot, width = 6, height = 4)
 
## まとめ ----
# 最後に結び(まとめ)のセクションを追加する
doc <- body_add_par(doc, "まとめ", style = "heading 2")
conclusion <- fpar(
  "全体を通して",
  ftext("読書量の増加傾向", fp_text_lite(bold = TRUE, color = "#006699")),
  "が見られ、特に7月は大きく伸びました。",
  "夏休み期間に読書時間が増えたことが要因と考えられます。"
)
doc <- body_add_fpar(doc, conclusion)
 
# printのtargetに保存先パスを渡し、作業フォルダにTest.docxとして書き出す
print(doc, target = paste0(getwd(), "/","Test.docx"))

パワーポイントの特定のIDのプレースホルダーに文字を挿入する:ph_location_idコマンド

プレースホルダー(仮の文字や入力枠)をidで指定するための関数です。プレースホルダーのidは同一レイアウト内で一意に割り振られており、「layout_properties」や「plot_layout_properties」で確認できます。

オプション意味初期値
idプレースホルダーのID
newlabelプレースホルダーに関連付ける新しいラベルNULL
## ph_location_idを使う例:スライドの各枠にIDを指定して文字を入れる
# read_pptxで新規のPowerPointオブジェクトを作成する
doc <- read_pptx()
 
# add_slideで「Comparison(比較)」レイアウトのスライドを1枚追加する
doc <- add_slide(doc, "Comparison")
 
# plot_layout_propertiesでレイアウトを図示し、各枠のIDを確認する
# (各ボックスの右上にIDが表示される)
plot_layout_properties(doc, "Comparison")
 
# ph_location_id(id = 2)でID2の枠を指定し、ph_withでその枠に文字を入れる
doc <- ph_with(doc, "第2四半期の比較", location = ph_location_id(id = 2))
 
# ID3の枠に文字を入れる
doc <- ph_with(doc, "国内売上", location = ph_location_id(id = 3))
 
# ID4の枠に文字を入れる
doc <- ph_with(doc, "前年同期比 +8%", location = ph_location_id(id = 4))
 
# ID8の枠に文字を入れる
doc <- ph_with(doc, "社内資料", location = ph_location_id(id = 8))
 
# 作業フォルダにTest.pptxとして書き出す
print(doc, target = paste0(getwd(), "/","Test.pptx"))

・レイアウトをプロットして確認

・作業フォルダにTest.pptxとして書き出す

複数のブロックを連結して新しい文書を作成:block_listコマンド

複数のブロックをまとめて操作したい場合に使用します。

# ブロックの部品を準備する
# R同梱のサンプル画像(Rロゴ)のパスを取得する
img.file <- file.path(R.home("doc"), "html", "logo.jpg")
# 青色・太字のテキスト書式を定義する
fpt_blue_bold <- fp_text(color = "#006699", bold = TRUE)
# 赤色・斜体のテキスト書式を定義する
fpt_red_italic <- fp_text(color = "#C32900", italic = TRUE)
 
## この例はWord文書の出力でのみ利用できます
## (pptxはテキストと画像を組み合わせた段落に対応していないため)
## (指定自体は可能ですが、pptx出力では画像は表示されません)
# block_listで複数のfpar(段落)をひとまとめのブロックにする
value <- block_list(
  # 1段落目:青太字のあいさつ
  fpar(ftext("おはようございます", fpt_blue_bold)),
  # 2段落目:青太字と赤斜体を組み合わせた文
  fpar(ftext("今日は", fpt_blue_bold), " ",
       ftext("晴れです", fpt_red_italic)),
  # 3段落目:赤斜体の文と画像を同じ段落に配置する
  fpar(
    ftext("良い一日を", fpt_red_italic),
    external_img(
      src = img.file, height = 1.06, width = 1.39)))
 
# 新規のWord文書を作成する
doc <- read_docx()
 
# body_addでブロックリストの内容をまとめて文書に追加する
doc <- body_add(doc, value)
 
# 作業フォルダにTest_2.docxとして書き出す
print(doc, target = paste0(getwd(), "/","Test_2.docx"))

セクションを区切るためのコマンド:block_sectionコマンド

オプション意味初期値
propertyprop_section()で定義したセクションプロパティ
# prop_sectionでセクションのページ設定(向き・余白・種類)をまとめて定義する
prop_section(
  # 用紙の向きを横向き(ランドスケープ)に指定する
  page_size = page_size(orient = "landscape"),
  # 上部の余白を2インチに設定する(page_marの単位はインチ)
  page_margins = page_mar(top = 2),
  # セクションの種類。"continuous"は改ページせず続ける設定
  type = "continuous"
)
 
$type
[1] "continuous"
 
$page_size
$width
[1] 11.69306
 
$height
[1] 8.268056
 
$orient
[1] "landscape"
 
attr(,"class")
[1] "page_size"
 
$page_margins
$header
[1] 0.4916667
 
$bottom
[1] 0.9840278
 
$top
[1] 2
 
$right
[1] 0.9840278
 
$left
[1] 0.9840278
 
$footer
[1] 0.4916667
 
$gutter
[1] 0
 
attr(,"class")
[1] "page_mar"
 
attr(,"class")
[1] "prop_section" "prop"   

いろいろなコンテンツを文書に追加する:body_addコマンド

オプション意味初期値
xrdocxオブジェクト
valueドキュメントに追加するオブジェクト(文字列ベクトル、data.frame、グラフ、書式付き段落のブロックなど)
style段落スタイル名NULL
format_fun値のフォーマットに使う関数formatC
header表のヘッダーを表示するかどうかTRUE
tcf表の条件付き書式設定table_conditional_formatting()
alignment列の配置。列数と長さが一致する必要があり、値は「l」(左)「r」(右)「c」(中央)のいずれかNULL
width画像サイズ(unit引数で指定する単位)6
height画像サイズ(unit引数で指定する単位)5
respng画像の解像度(ppi)300
scale乗算スケーリングファクター。ggsaveと同様1
unit長さの単位。「in」「cm」「mm」のいずれか“in”
# body_add()の汎用性を示す例
# 見出し・目次・改ページ・表・段落・図・セクション区切りを、すべてbody_addだけで追加する

# 新規のWord文書を作成する
doc <- read_docx()

# 目次ページ用の見出しを「heading 1」スタイルで追加する
doc <- body_add(doc, "目次", style = "heading 1")

# block_tocで自動目次を挿入する
# この時点では枠だけが入り、Wordで開いてフィールドを更新すると見出し一覧が表示される
doc <- body_add(doc, block_toc())

# run_pagebreakで改ページを挿入し、本文を次のページから始める
doc <- body_add(doc, run_pagebreak())

# 本文の大見出しを追加する
doc <- body_add(doc, "読書記録サンプル", style = "heading 1")

# 表として差し込むデータフレームを作成する
reading_data <- data.frame(
  Month = c("4月", "5月", "6月", "7月"),
  Books = c(3, 5, 4, 7),
  Pages = c(620, 980, 750, 1240),
  stringsAsFactors = FALSE
)

# データフレームを表として追加する
# head()は既定で先頭6行を返す関数で、行数の多いデータで表が長くなりすぎるのを防ぐ
# (この例のデータは4行のため、結果は全4行になる)
doc <- body_add(doc, head(reading_data), style = "table_template")

# 別の見出しを追加する
doc <- body_add(doc, "読書ジャンル一覧", style = "heading 1")

# 文字列ベクトルを段落として追加する
# body_addに長さ2以上のベクトルを渡すと、要素ごとに独立した段落が作られる
# (ここでは5つのジャンルがそれぞれ「Normal」スタイルの段落になる)
genres <- c("小説", "エッセイ", "ビジネス書", "技術書", "歴史書")
doc <- body_add(doc, genres, style = "Normal")

# block_sectionでセクション区切り(continuous)を挿入する
# 改ページせず同じページのまま、以降のセクション設定を切り替えられる
doc <- body_add(doc, block_section(prop_section(type = "continuous")))

# plot_instrに作図コードを渡し、Rの基本グラフィックスの図を追加する
xdoc <- body_add(doc, plot_instr(code = barplot(1:5, col = 2:6)))

# 以降のセクションを横向き(ランドスケープ)に切り替える
# ページの向きだけが異なる新しいセクションが作られる
doc <- body_add(doc,
  block_section(prop_section(page_size = page_size(orient = "landscape"))))

# 作業フォルダにTest_3.docxとして書き出す
print(doc, target = paste0(getwd(), "/","Test_3.docx"))

特定の文字列を置き換えるコマンド:body_replace_all_textコマンド

指定されたテキストをドキュメント中で全て置き換えることができます。例えば、誤った情報を正しいものに更新する際に便利です。

オプション意味初期値
x対象のdocxオブジェクト
old_value置き換え対象の値
new_value置き換え後の値
only_at_cursor現在のカーソル位置でのみ検索置換するかどうかFALSE
warn古い値が見つからない場合に警告するかTRUE
# 置換の動作を確認するため、仮の文書を用意する
doc <- read_docx()
doc <- body_add_par(doc, "仮タイトルその1")
doc <- body_add_par(doc, "仮タイトルその2")

# docx_show_chunkで現在のカーソル位置にあるテキストを表示する
# 直前に追加した段落が対象なので、出力結果は「仮タイトルその2」
docx_show_chunk(doc)

# カーソル位置のみを対象に、正規表現を無効(fixed = TRUE)にして単純置換する
doc <- body_replace_all_text(
  doc,
  old_value = "仮タイトル",
  new_value = "本タイトル",
  only_at_cursor = TRUE,
  fixed = TRUE)

# カーソル位置の段落だけが置換され、出力結果は「本タイトルその2」
docx_show_chunk(doc)

# 大文字小文字を区別せず(ignore.case = TRUE)、文書全体を対象に同じ置換を行う
doc <- body_replace_all_text(
  doc,
  old_value = "仮タイトル",
  new_value = "本タイトル",
  only_at_cursor = FALSE,
  ignore.case = TRUE)

# cursor_backwardでカーソルを1つ前の段落に戻す
doc <- cursor_backward(doc)

# 戻した先の段落を確認する。出力結果は「本タイトルその1」
docx_show_chunk(doc)

# 正規表現で、末尾の数字を丸括弧付きの番号に変換する(\\1は後方参照)
doc <- body_replace_all_text(doc, "([0-9])$", "(\\1)")

# 末尾の「1」が「(1)」になり、出力結果は「本タイトルその(1)」
docx_show_chunk(doc)# 置換の動作を確認するため、仮の文書を用意する
doc <- read_docx()
doc <- body_add_par(doc, "仮タイトルその1")
doc <- body_add_par(doc, "仮タイトルその2")

# docx_show_chunkで現在のカーソル位置にあるテキストを表示する
# 直前に追加した段落が対象なので、出力結果は「仮タイトルその2」
docx_show_chunk(doc)

# カーソル位置のみを対象に、正規表現を無効(fixed = TRUE)にして単純置換する
doc <- body_replace_all_text(
  doc,
  old_value = "仮タイトル",
  new_value = "本タイトル",
  only_at_cursor = TRUE,
  fixed = TRUE)

# カーソル位置の段落だけが置換され、出力結果は「本タイトルその2」
docx_show_chunk(doc)

# 大文字小文字を区別せず(ignore.case = TRUE)、文書全体を対象に同じ置換を行う
doc <- body_replace_all_text(
  doc,
  old_value = "仮タイトル",
  new_value = "本タイトル",
  only_at_cursor = FALSE,
  ignore.case = TRUE)

# cursor_backwardでカーソルを1つ前の段落に戻す
doc <- cursor_backward(doc)

# 戻した先の段落を確認する。出力結果は「本タイトルその1」
docx_show_chunk(doc)

# 正規表現で、末尾の数字を丸括弧付きの番号に変換する(\\1は後方参照)
doc <- body_replace_all_text(doc, "([0-9])$", "(\\1)")

# 末尾の「1」が「(1)」になり、出力結果は「本タイトルその(1)」
docx_show_chunk(doc)

ggplotオブジェクトを文書に埋め込む:block_ggコマンド

オプション意味初期値
valueggplotオブジェクト
fp_p段落書式設定プロパティ。fp_par()を参照fp_par()
width画像サイズ(unit引数で指定する単位)6
height画像サイズ(unit引数で指定する単位)5
respng画像の解像度(ppi)300
scale乗算スケーリングファクター。ggsaveと同様1
unit長さの単位。「in」「cm」「mm」のいずれか“in”
# ggplot2が使える場合のみ実行する
if (require("ggplot2")) {
  # 乱数を固定し、毎回同じグラフになるようにする
  set.seed(2)
  # 新規のWord文書を作成する
  doc <- read_docx()
  
  # body_append_start_contextで、要素をまとめて追記するための作業コンテキストを開始する
  z <- body_append_start_context(doc)
  
  # 3枚分のグラフを繰り返し作成して追記する
  for (i in seq_len(3)) {
    # 気温と湿度の疑似観測データを生成する
    df <- data.frame(
      temperature = rnorm(10, mean = 25, sd = 3),
      humidity = rnorm(10, mean = 60, sd = 10)
    )
    # 気温と湿度の散布図を作成する
    gg <- ggplot(df, aes(x = temperature, y = humidity)) + geom_point()
    
    # write_elements_to_contextで、block_ggにしたグラフをコンテキストに書き込む
    write_elements_to_context(
      context = z,
      block_gg(
        value = gg
      )
    )
  }
  
  # body_append_stop_contextで追記を確定し、文書オブジェクトに反映する
  doc <- body_append_stop_context(z)
  
  # 作業フォルダにTest_4.docxとして書き出す
  print(doc, target = paste0(getwd(), "/","Test_4.docx"))
}


この記事が誰かの役に立ちますように。

スポンサーリンク
価格および配送状況は変更される場合があります。購入時は商品ページをご確認ください。
当サイトに表示されている商品情報はAmazonから提供されたものであり、更新または削除される場合があります。
karada-goodはAmazonアソシエイトとして、適格販売により収入を得ています。