ROpenSciの実験的パッケージリポジトリropenscilabsで、便利そうなパッケージを見つけた。{tabulizer}
というものだ。このパッケージはTabulaというオープンソースツールの機能を利用して、PDF中に含まれる表から、値を取り出すというもの。
政府や企業の報告書はPDFであることが多く(二次利用を想定していないのだろうが)、表としてデータが収められていることがしばしばある。PDFからのデータ取得方法として、Rでは {tm}
を使う方法や{pdftools}
を利用する方法がそれぞれあるが、{pdftools}
ではテキストベースでの抽出となるため、表データの抽出からRの特徴とも言えるデータフレーム形式への変換が困難であった。そこでこの{tabulizer}
を使うことで、PDFに含まれる表データの抽出が可能となり、Rで扱えるデータの幅が広がることが考えられる。
使ってみた感じ、PDFからのデータ取得の決定版となりそうな大変便利なパッケージであることがわかったので、その機能を試しておく。
パッケージは現在CRANには登録されていないのでGitHubのリポジトリからダウンロードしてくる。利用の際に{tabulizerjars}
というパッケージも必要になるのでこちらもインストール({rJava}
に依存しているのでWindows環境では面倒かも)。なおGitHubリポジトリ上のインストールの際は{githubinstall}
を利用するのが楽。
library(githubinstall)
githubinstall(c("tabulizerjars", "tabulizer"))
GitHubのvignettesに解説記事があるが一通り紹介しておく。
library(tabulizer)
メインとなる関数がextract_tables()
。この関数では引数に与えたPDFファイル(ローカル上でもウェブ上でも良い)に含まれる表からデータを抽出する。引数がいくつか用意されていて、PDFへのパス以外何も指定しない場合にはすべてのPDFのページを対象に表データの抽出を実行する。返り値は行列のリストが初期値となっている。
例として、{tabulizer}
パッケージインストール時に付属するPDFファイルを対象に操作してみる。なおこのファイルは https://github.com/leeper/tabulizer/raw/master/inst/examples/data.pdf にあるものと同じなので、実行結果を比較してみたい方はリンク先のPDFを見てもらうと良いだろう。
path2pdf <- system.file("examples", "data.pdf", package = "tabulizer")
out <- extract_tables(path2pdf)
class(out) # リストクラス
## [1] "list"
# 表データの取り出し
out[[1]] %>% head()
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear"
## [2,] "21.0" "6" "160.0" "110" "3.90" "2.620" "16.46" "0" "1" "4"
## [3,] "21.0" "6" "160.0" "110" "3.90" "2.875" "17.02" "0" "1" "4"
## [4,] "22.8" "4" "108.0" "93" "3.85" "2.320" "18.61" "1" "1" "4"
## [5,] "21.4" "6" "258.0" "110" "3.08" "3.215" "19.44" "1" "0" "3"
## [6,] "18.7" "8" "360.0" "175" "3.15" "3.440" "17.02" "0" "0" "3"
Rで利用する場合はデータフレームであると何かと都合が良いので、データフレームとして取り出すオプションとして、引数methodが用意されている。*method*="data.frame"
でリストの中身がデータフレームとして与えられるようになる。
out <- extract_tables(path2pdf,
pages = 2,
method = "data.frame")
out
## [[1]]
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
##
## [[2]]
## X Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 145 6.7 3.3 5.7 2.5 virginica
## 2 146 6.7 3.0 5.2 2.3 virginica
## 3 147 6.3 2.5 5.0 1.9 virginica
## 4 148 6.5 3.0 5.2 2.0 virginica
## 5 149 6.2 3.4 5.4 2.3 virginica
## 6 150 5.9 3.0 5.1 1.8 virginica
今回はpages引数で対象ページを指定した。ページ内に複数の表が存在する場合にはデータフレームも2つになる。
引数methodは既定値でmatrixだが、data.frameやcharacterも選べるようになっている。またさらなるオプションとして、取得結果をRのオブジェクトではなくcsvやtsv、json形式で保存するようにも設定できる。保存の際は作業ディレクトリではなくPDFが位置するディレクトリに作成される(ウェブ上のPDFを対象にすることはできない)。
extract_tables(path2pdf,
pages = 3,
method = "csv")
# [1] "/Library/Frameworks/R.framework/Versions/3.3/Resources/library/tabulizer/examples"
表の部分的な抽出もarea引数の指定により可能となっている。これは1つのページから複数のテーブルデータを得る場合などに役立つ。areaはlist()
でPDFの座標位置(上左下右の順番)を与える必要があるので闇雲に対象のデータがある座標を探すことになってしまうが、その際は次のlocate_areas()
を利用すると良いだろう。
extract_tables(path2pdf,
pages = 2,
area = list(c(126, 149, 212, 462)),
method = "data.frame")
## [[1]]
## X Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 3.9 1.7 0.4 setosa
Shiny Widgetsの機能を利用したlocate_areas()
またはextract_areas()
の実行により、インタクティブに取得範囲を指定できるようになる。これは対象のPDFをブラウザ上に描画させ、データ取得を実行する範囲をユーザーで指定する(ドラックで範囲指定)というもの。
locate_areas(path2pdf, pages = 2, widget = "shiny")
# [[1]]
# top left bottom right
# 123.6069 159.5029 208.3006 287.6879
返り値の座標をextract_tables()
のarea引数の値として与えてみると
extract_tables(path2pdf,
pages = 2,
method = "data.frame",
area = list(c(120, 150, 210, 280)))
## [[1]]
## Sepal.Length Sepal.Width
## 1 5.1 3.5
## 2 4.9 3.0
## 3 4.7 3.2
## 4 4.6 3.1
## 5 5.0 3.6
見事に該当箇所のデータが取得できた!
もう一つのextract_areas()
関数の方は、指定箇所のデータを直接取得するというもの。
extract_areas(path2pdf, pages = 2)
# [[1]]
# [,1]
# [1,] "Petal.Length"
# [2,] "1.4"
# [3,] "1.4"
# [4,] "1.3"
# [5,] "1.5"
# [6,] "1.4"
# [7,] "1.7"
関数extract_metadata()
は対象PDFのメタ情報(タイトルやページ数、作成日など)を取得する関数となっている。PDFの概要を知りたい場合に便利だろう(pdftools::pdf_info()
と同様の機能)。
# extract_metadata関数によって取得されるメタデータ
extract_metadata(path2pdf) %>% names()
## [1] "pages" "title" "author" "subject" "keywords" "creator"
## [7] "producer" "created" "modified" "trapped"
これはextract_tables()
での引数areaの指定に役立つと思われる。
get_page_dims(path2pdf)
## [[1]]
## [1] 612 792
##
## [[2]]
## [1] 612 792
##
## [[3]]
## [1] 612 792
get_n_pages(path2pdf) # ページ数
## [1] 3
PDFを分割したり、複数のPDFを結合する関数が用意されている。
# 一時フォルダに各ページのPDFが保存される
(sf <- split_pdf(path2pdf))
# [1] "/var/folders/8f/s_lbgwks6q7g3lz52q93ngph0000gn/T//Rtmpx4BCAz/data1.pdf" "/var/folders/8f/s_lbgwks6q7g3lz52q93ngph0000gn/T//Rtmpx4BCAz/data2.pdf"
# [3] "/var/folders/8f/s_lbgwks6q7g3lz52q93ngph0000gn/T//Rtmpx4BCAz/data3.pdf"
# PDFを結合
merge_pdfs(sf, "merge.pdf")
pdftools::pdf_render_page()
と同じように、PDFの各ページについてサムネイル画像を生成する関数が用意されている。make_thumbnails()
にPDFのパスを与えると、PDFがあるフォルダにpng形式の画像ファイルが生成される(出力先や画像ファイルの拡張子は引数の指定により変更可能。)
make_thumbnails(path2pdf,
pages = 1,
format = "png",
resolution = 72L,
outdir = "inst/")
© Shinya Uryu, 2016.
The contents on this site are licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.