Heavy Watal

readr — 高速で柔軟なテーブル読み込み

タブ区切りテキストやCSVファイルを読み込んでdata.frameにするツール。 .gz.xz などの圧縮ファイルも透過的に読み書き可能。 標準でも read.table()read.csv() があるけど、それらと比べて

tidyverse に含まれているので、 install.packages('tidyverse') で一括インストール、 library(tidyverse) で一括ロード。 例えば:

library(tidyverse)
write_tsv(iris, 'iris.tsv.gz')
read_tsv('iris.tsv.gz')

主な関数

ファイル読み込み

read_delim(file, delim,
  quote = '"',
  escape_backslash = FALSE,
  escape_double = TRUE,
  col_names = TRUE,
  col_types = NULL,
  locale = default_locale(),
  na = c('', 'NA'),
  quoted_na = TRUE,
  comment = '',
  trim_ws = FALSE,
  skip = 0,
  n_max = Inf,
  guess_max = min(1000, n_max),
  progress = show_progress())

read_csv(...)read_tsv(...) は区切り文字 delim= 指定済みのショートカット。

read_table(...)
連続する空白文字をひとつの区切りと見なして処理
read_fwf(file, col_positions, ...)

fixed width file. 第二引数の指定方法は

  1. fwf_empty(infile, skip=0, col_names=NULL) で自動推定
  2. fwf_widths(widths, col_names=NULL) で幅指定
  3. fwf_positions(start, end, col_names=NULL) で開始・終了位置指定
read_lines(file, skip=0, n_max=-1L, ...), read_lines_raw(...)

1行を1要素とした文字列ベクタとして読み込む

read_file(file)

ファイルの内容まるごと文字列で返す

ファイル書き出し

write_delim(x, path,
  delim = ' ',
  na = 'NA',
  append = FALSE,
  col_names = !append)

write_csv(...)write_tsv(...) は区切り文字 delim= 指定済みのショートカット。

write_lines(x, path, na='NA', append=FALSE)
vectorやlistを1行ずつ書き出す。
write_file(x, path, append=FALSE)
文字列をそのまま書き出す。

文字列から別の型へ

parse_number(x, na=c('', 'NA'), locale=default_locale())
文字列で最初に登場する数値を抜き出す。 あるカラムでは末尾に単位が付いちゃってる、みたいな状況でのみ使う。 それ以外の複雑な判断はしないので、例えば '6e23' は単に6になる。
parse_double(x, ...), parse_integer(x, ...)
文字列をdouble/intの数値として解釈して返す。 '6e23' のような指数形式も大丈夫。 異物が混じっていた場合は警告してくれる。 (標準のas.integer()とかは黙って小数点以下を切り捨てたりする)
parse_logical(x, ...)
1/0/T/F/TRUE/FALSE を大文字小文字問わずlogicalに変換。

parse_factor(x, levels, ordered=FALSE, ...)

parse_date(x, format='', ...), parse_datetime(x, format='', ...), parse_time(x, format='', ...)

列の型を指定する

https://cran.r-project.org/web/packages/readr/vignettes/column-types.html

基本的には何も指定しなくても数値などを認識していい感じに設定してくれる。 標準の read.csv() などと違って暗黙のfactor変換はしない。 整数と実数は区別せずnumeric型で読む(1.2から)。

明示的に型を指定したい場合は col_types 引数に cols() 関数の値を渡す。 文字列で 'ccdi_' のように省略することも可能。

read_csv('mydata.csv', col_types='ccdi_')

colsp = cols(length=col_double(), count='i', .default='c')
read_csv('mydata.csv', col_types=colsp)

指定した列だけ読むには cols(..., .default=col_skip()) とするか cols_only(...) を使う。

Excelファイルを読み込む

https://github.com/tidyverse/readxl

自分のデータは絶対にExcel形式ではなくCSVやTSV形式で保存すべきだが、 人から受け取ったファイルや論文のサプリデータがExcelだったら仕方がない。 readxl というパッケージを利用すれば、 一旦Officeで開いてCSVに変換するという手間なしで直接Rに読み込める。

Rの中から install.packages('readxl') でインストールし、 使う前に library(readxl) でパッケージを読み込む。

excel_sheets(path)
ファイルに含まれるシートの名前を取得
read_excel(path, sheet=1, col_names=TRUE, col_types=NULL, na='', skip=0)
.xlsxlsx のどちらの形式でも読める。 sheet は番号でも名前でもいい。 それ以降の引数については readr の関数と同じ。

最新版をソースからインストールする

https://github.com/tidyverse/readr

devtools::install_github('tidyverse/readr')
# ...
ld: library not found for -lintl

見つからないと言われてる libintl.* はgettextの一部なのでそいつを入れる。 パス指定で楽をするため、keg-onlyだけど無理矢理シムリンクを張る (ホントは良くないかも)。

brew install gettext
brew link gettext --force

Homebrewを/usr/local/以外に入れている場合は、 それをRに見つけさせるため ~/.R/Makevars にオプションを書く。

LDFLAGS=-L${HOME}/.homebrew/lib

再びRで install_github('tidyverse/readr') を試みる。

tibble

tbl_df クラスが付与された改良版data.frameのことをtibbleと呼ぶ。 もともとは dplyr パッケージで扱っていたが、独立パッケージになった。 readr で読み込んだデータもこの形式になる。

tbl_iris = as_tibble(iris)
class(tbl_iris)
## [1] "tbl_df"     "tbl"        "data.frame"
class(iris)
## [1] "data.frame"

生のdata.frameとの違いは:

新しいtibble 1.4以降では pillar というパッケージが有効数字や欠損値などの表示形式を勝手にイジるようになってしまった。 見やすくない上にかなり遅いので print.tbl_df() 関数を上書きする。 標準の print.data.frame() でもいいけど、 data.table 1.11 以降がインストールしてある場合は print.data.table() が使いやすい。

# ~/.Rprofile
setHook(packageEvent("tibble", "attach"), function(...) {
  try({
    registerS3method("print", "tbl_df", data.table:::print.data.table)
  })
})

関数

tibble::tibble(...)
tibbleを新規作成。ちょっと昔までは dplyr::data_frame() だった。
base::data.frame() と違ってバグが混入しにくくて便利:
  • 勝手に型変換しない (stringsAsFactors=FALSEが基本)
  • 勝手に列名を変えない
  • 長さ1の変数以外はリサイクルしない
  • 引数の評価がlazyに行われるので前の列を利用して後の列を作ったりできる
  • tbl_df クラスを付加
  • ただし1.4以降のバージョンでは表示が遅くて見にくい
tibble::as_tibble(x)
既存のdata.frameやmatrixをtibbleに変換。 ちょっと昔までは dplyr::tbl_df() とか dplyr::as_data_frame() だった。 v2.0からは列名がちゃんとついてないとエラーになる。
tibble::new_tibble(x, ..., nrow, class = NULL)
tibbleのサブクラスを扱う開発者向け as_tibble() 。 検証なし、nrow 必須の代わりに高速。 クラスを先頭に追加できるのも便利。
tibble::enframe(x, name = "name", value = "value")
名前付きvectorとかlistを2列のtibbleに変換する。 tibble::deframe(x) はその逆。 c(a=1, b=2) %>% enframe() %>% deframe()
tibble::add_row(.data, ..., .before=NULL, .after=NULL)
既存のtibbleに新しいデータを1行追加する。
tibble::rownames_to_column(df, var='rowname')
行の名前をcharacter型で1列目の変数にする。dplyr::add_rownames()の後継。
tibble::rowid_to_column(df, var='rowid') はそれを整数で。
tibble::column_to_rownames(df, var='rowname') はその逆。
tibble::remove_rownames(df) は消すだけ。
tibble::glimpse(.data, width=NULL)
データの中身をざっと見る。 print() とか str() のようなもの。
pillar::type_sum(x)
オブジェクトの型
pillar::obj_sum(x)
type_sumとサイズ e.g., "data.frame [150 x 5]"

設定

表示される行数や幅を調節する項目には以下のようなものがある。 ~/.Rprofile に書いておけば起動時に勝手に設定される。

height = 30L  # for example
width = 160L
options(
  pillar.neg = FALSE,
  pillar.subtle = FALSE,
  datatable.print.class = TRUE,
  datatable.print.colnames = "top",
  datatable.print.nrows = height,
  datatable.print.topn = height %/% 2L,
  tibble.print_max = height,
  tibble.print_min = height,
  tibble.width = width,
  width = min(width, 10000L)
)

PAGERで全体を表示する

tibbleの全体を表示したい場合は print() 関数に指定が必要。 lessのようなPAGERで見たい場合は utils::page() が便利。

# tibble:::print.tbl_df をそのまま使う場合
as_tibble(iris) %>% print(n = nrow(.), width = 10000L)

# data.table:::print.data.table で上書きした場合
as_tibble(iris) %>% print(nrows = nrow(.))

iris %>% page('print')

オプションをいちいち設定しなくて済むように less() のような関数を定義しておくのもよい。

関連書籍