井出草平の研究ノート

Rでのデータ読み込み・書き出しはrioパッケージを使うべし[R]

rioパッケージの解説翻訳をした。

cran.r-project.org

データファイルのインポート、エクスポート、コンバート

rio の背景にある考えは、R へのデータのインポートと R からのデータのエクスポートのプロセスを簡素化することだ。このプロセスは、おそらく不必要なほど、R の初級ユーザにとって非常に複雑である。実際、Rはデータのインポート/エクスポートのプロセスを説明したマニュアルを提供している。そして、そのような文章にもかかわらず、説明されているパッケージのほとんどは(程度の差こそあれ)時代遅れなものばかりだ。rio は、データの入出力(インポートとエクスポート)を、import()export() という二つの簡単な関数に統一することを目的としており、初心者や経験豊富な R ユーザーが、R データの読み書きに最適な方法を二度(あるいは一度)考える必要がないようにするものだ。

rio の核となる利点は、ユーザがおそらく喜んで行うであろう仮定を行うことだ。具体的には、rio はファイル名の拡張子から、それがどのようなファイルであるかを判断する。これは、例えばWindows OSが、与えられたファイルタイプに関連するアプリケーションを判断する際に使用するのと同じロジックだ。rioは、初心者が認識できないようなファイル形式と特定のインポート/エクスポート機能を手作業で照合する必要をなくすことで、一般的なデータ形式のほとんどすべてを同じ機能で読み込むことを可能にした。

インポートとエクスポートを簡単にすることで、次のステップとして、Rを単純なデータ変換ユーティリティとしても使うことは明らかだ。様々な独自フォーマット間でデータファイルを転送することは常に苦痛であり、しばしば費用もかかる。そこで、変換機能はimportexportを組み合わせて、ファイル形式を簡単に変換する(したがって、Stat/TransferSledgehammerのようなプログラムのFOSS代替となるものである)。

対応するファイル形式

rio は、インポートおよびエクスポートのために様々な異なるファイル形式をサポートしている。パッケージのスリム化を図るため、必須でないフォーマットはすべて "Suggests" パッケージでサポートされており、これらはデフォルトではインストール (またはロード) されないようになっている。rio が完全に機能するように、rio を初めて使うときにこれらのパッケージをインストールしてほしい。

install_formats()

対応フォーマットの一覧は以下の通りだ。

フォーマット 代表的な拡張子 インポートパッケージ エクスポートパッケージ デフォルトでインストールされているか
Comma-separated data .csv data.table data.table Yes
Pipe-separated data .psv data.table data.table Yes
Tab-separated data .tsv data.table data.table Yes
CSVY (CSV + YAML metadata header) .csvy data.table data.table Yes
SAS .sas7bdat haven haven Yes
SPSS .sav haven haven Yes
SPSS (compressed) .zsav haven haven Yes
Stata .dta haven haven Yes
SAS XPORT .xpt haven haven Yes
SPSS Portable .por haven Yes
Excel .xls readxl Yes
Excel .xlsx readxl openxlsx Yes
R syntax .R base base Yes
Saved R objects .RData, .rda base base Yes
Serialized R objects .rds base base Yes
Epiinfo .rec foreign Yes
Minitab .mtp foreign Yes
Systat .syd foreign Yes
“XBASE” database files .dbf foreign foreign Yes
Weka Attribute-Relation File Format .arff foreign foreign Yes
Data Interchange Format .dif utils Yes
Fortran data no recognized extension utils Yes
Fixed-width format data .fwf utils utils Yes
gzip comma-separated data .csv.gz utils utils Yes
Apache Arrow (Parquet) .parquet arrow arrow No
EViews .wf1 hexView No
Feather R/Python interchange format .feather feather feather No
Fast Storage .fst fst fst No
JSON .json jsonlite jsonlite No
Matlab .mat rmatio rmatio No
OpenDocument Spreadsheet .ods readODS readODS No
HTML Tables .html xml2 xml2 No
Shallow XML documents .xml xml2 xml2 No
YAML .yml yaml yaml No
Clipboard default is tsv clipr clipr No
Google Sheets as Comma-separated data

さらに、rio がサポートしていないが R の実装が知られている形式は、パッケージとインポートまたはエクスポート関数を示す情報付きのエラーメッセージを生成する。認識できない形式は、単純に "Unrecognized file format" エラーを出力する。

データのインポート

"rio"では、1つの通常は単一引数の関数を使用して、ほとんどすべての形式のファイルをインポートできる。import () は、ファイルの拡張子からファイル形式を推測し、適切なデータインポート関数を呼び出して、単純なdata.frameを返す。これは、上記の形式の任意のに対して機能する。

library("rio")

x <- import("mtcars.csv")
y <- import("mtcars.rds")
z <- import("mtcars.dta")

# 同一のものであることを確認
all.equal(x, y, check.attributes = FALSE)
## [1] TRUE
all.equal(x, z, check.attributes = FALSE)
## [1] TRUE

何らかの理由でファイルに拡張子がない、あるいは実際の種類と一致しない拡張子を有している場合、手動でファイル形式を指定して、形式推論ステップを上書きすることができる。例えば、拡張子を持たないCSVファイルを読み込むには、csvを指定する。

head(import("mtcars_noext", format = "csv"))

データリストのインポート

import()は単一のデータフレームを返すだけだが、import_list()はファイル名のベクトルをRにインポートするのに使用できる。

str(import_list(dir()), 1)

同様に、いくつかの単一ファイル形式(Excel ワークブック、Zip ディレクトリ、HTML ファイルなど)には、複数のデータセットが含まれることがある。import() は型安全で、常にデータ・フレームを返すので、これらのフォーマットからのインポートには、どのデータ・セット(ワークシート、ファイル、テーブルなど)をインポートするかを指示するために import() に which 引数を指定する必要がある(デフォルトは which = 1)。しかし、import_list() を使用すると、これらのタイプのファイルからデータオブジェクトのすべて(または指定されたサブセットのみ、これも which を介して)をインポートすることができる。

データエクスポート

rio のエクスポート機能は、様々な R パッケージで様々な関数が利用可能であることや、インポート関数はしばしば他のアプリケーションからのデータを利用するために書かれ、他のアプリケーションで使用される形式にエクスポートする関数は開発の優先順位が決して高くないことから、インポート機能よりもやや限定的なものとなっている。とはいえ、rioは現在、以下のフォーマットをサポートしている。

library("rio")

export(mtcars, "mtcars.csv")
export(mtcars, "mtcars.rds")
export(mtcars, "mtcars.dta")

また、Rパイプの一部としてexport()を使用することも簡単である(magrittrまたはdplyrから)。例えば、次のコードは、単純なデータ変換の結果を保存するためにexport()を使用している。

library("magrittr")
mtcars %>% subset(hp > 100) %>%  aggregate(. ~ cyl + am, data = ., FUN = mean) %>% export(file = "mtcars2.dta")

ファイルフォーマットによっては(ExcelワークブックやRdataファイルなど)、1つのファイルに複数のデータオブジェクトを格納できるものもある。export()は、これらのタイプのファイルへの複数のオブジェクトの出力をネイティブでサポートする。

# Excelワークブックのシートにエクスポートする
export(list(mtcars = mtcars, iris = iris), "multi.xlsx")
# .Rdataファイルへのエクスポート
## 名前付きリストとして
export(list(mtcars = mtcars, iris = iris), "multi.rdata")

## 文字ベクトルとして
export(c("mtcars", "iris"), "multi.rdata")

また、新しい関数 export_list() (v0.6.0 以降) を使って、ファイル名のベクトルかファイルパターンを使って、データフレームのリストを複数のファイルに書き出すことができる。

export_list(list(mtcars = mtcars, iris = iris), "%s.tsv")

ファイル変換

convert() 関数は、インポートされたファイルからデータフレームを構築し、直ちにディスクに書き戻すことで、import() と export() を連携さ せる。 convert() はエクスポートされたファイルのファイル名を見えないように返すので、それを使って新しいファイルにプログラム的にアクセ スできる。

convert()import()export() の薄いラッパーに過ぎないので、非常に簡単に使用することができる。たとえば、次のように変換できるようになる。

# 変換するファイルを作成する
export(mtcars, "mtcars.dta")

# StataからSPSSへの変換
convert("mtcars.dta", "mtcars.sav")

convert() は、インポート (in_opts) とエクスポート (out_opts) を制御するための引数のリストも受け取ることができる。これは、インポートやエクスポートのメソッドに追加の引数を渡すのに便利だ。これは、例えば、固定幅のフォーマットファイルを読み込んで、それをカンマ区切りの値ファイルに変換するのに便利だろう。

# 未定義のファイルを作成する
fwf <- tempfile(fileext = ".fwf")
cat(file = fwf, "123456", "987654", sep = "\n")

# ファイルを読み込むための2つの方法を見る
identical(import(fwf, widths = c(1,2,3)), import(fwf, widths = c(1,-2,3)))
## [1] FALSE
# CSVに変換する
convert(fwf, "fwf.csv", in_opts = list(widths = c(1,2,3)))
import("fwf.csv") # 変換をチェックする
##   V1 V2  V3
## 1  1 23 456
## 2  9 87 654

メタデータが豊富なファイル形式(Stata、SPSSSASなど)では、オープンなテキスト区切り形式に変換する際に、インポートしたデータをcharacterize()やfactorize()に通すことも有用である。characterize()は、「ラベル」属性を持つデータフレーム内の単一の変数またはすべての変数を、値のラベルへのマッピングに基づいて文字ベクトルに変換する(例:export(characterize(import("file.dta"), "file.csv"))-). 別の方法として、CSVY形式へのエクスポートがある。これは、CSVファイルの冒頭にあるYAML形式のヘッダーにメタデータを記録するものである。

また、Rscript に -e (expression) 引数を付けて呼び出すことで、コマンドラインから rio を使用することも可能だ。例えば、Stataファイル(.dta)をカンマ区切り値(.csv)に変換するには、次のようにするだけでよい。

パッケージの哲学

rioの核となる利点は、ユーザーがおそらく望んでいるであろう仮定を行うことだ。このうち8つが重要だ。

  • 1.rioはファイル名の拡張子を使って、それがどのような種類のファイルであるかを決定する。これは、例えばWindows OSが、与えられたファイルタイプに関連するアプリケーションを判断する際に使用するのと同じロジックだ。初心者が認識できないようなファイル形式と特定のインポート/エクスポート機能を手作業で照合する必要をなくすことで、rioはほとんどすべての一般的なデータ形式を同じ機能で読み込むことができるようになった。また、ファイル拡張子が正しくない場合、ユーザーは format引数を指定することで、特定のインポート方法を強制することができる。他のパッケージでもこのようなことは可能だが、rio は各パッケージよりも完全で一貫性のあるものを目指している。

  • readerは、特定のテキスト形式と R バイナリファイルを扱うことができます。

  • ioはカスタムフォーマットのセットを提供
  • ImportExportは特定のバイナリ形式(Excel, SPSS, Accessファイル)に焦点を当て、Shinyインタフェースを提供する。
  • SchemaOnReadは、1つのメソッドが成功するまで、多数の可能なインポートメソッドを繰り返し実行する

  • 2.rioはテキスト区切りファイルに対して data.table::fread() を使って、拡張子に関係なくファイル形式を自動的に判断する。そのため、実際にはタブ区切りである CSV も正しくインポートさ れる。また、ものすごく速い。

  • 3.rioは可能な限り、文字列を要因としてインポートしないようにしている。
  • 4.rioは、SSL (HTTPS) URL、短縮 URL、適切な拡張子を持たない URL、(公開)Google Documents Spreadsheets など、ウェブベースのインポートをネイティブでサポートしている。
  • 5.rioは、単一のファイルである .zip および .tar アーカイブから、明示的に解凍することなく自動的にインポートする。圧縮されたディレクトリへのエクスポートもサポートされている。
  • 6.rioは、基本 R やforeignパッケージが提供するものよりも高速で、より合理化された様々な I/O パッケージを包んでいる。また、data.tableによる区切り形式、HavenによるSAS、Stata、SPSSのファイル、よりスマートで高速な固定幅ファイルのインポートとエクスポートルーチン、readxlとopenxlsxによるExcelワークブックの読み込みと書き出しが利用可能になっている。
  • 7.rioは、リッチなファイルフォーマット(SPSS、Stataなど)からのメタデータを、ファイルタイプやインポート機能に関係なく、一貫した形で変数レベルの属性として保存する。これらの属性は次のように識別さ れる。

  • label: 変数の説明

  • labels: 数値とその値が表す文字列を対応させたベクトル
  • format: オリジナルファイルにおける変数の保存形式を表す文字列

gather_attrs()関数は、変数レベルの属性をデータフレームレベルに簡単に移動さ せることができる(そしてspread_attrs()は、その収集プロセスを逆転させる)。これらは、特に、ファイル変換の際に、ファイルフォーマット間で異なって扱われる属性をより簡単に変更するのに便利だ。例として、次のイディオムはSPSSの値ラベルをStataで許される最大32文字に切り詰めるために使用することができる。

dat <- gather_attrs(rio::import("data.sav"))
attr(dat, "labels") <- lapply(attributes(dat)$labels, function(x) {
    if (!is.null(x)) {
        names(x) <- substring(names(x), 1, 32)
    }
    x
})
export(spread_attrs(dat), "data.dta")

さらに、2つの関数(v0.5.5 で追加)が、これらの "labels" 属性から文字変数と因子変数を簡単に作成する方法を提供する。characterize()は、 "labels" 属性を持つ単一の変数またはデータフレーム内のすべての変数を、値のラベルへのマッピングに基づいて文字ベクターに変換する。characterize()は、これらのリッチなファイル形式をオープンな形式に変換する際に特に役立つ(例:export(characterize(import("file.dta")), "file.csv")。

rioは内部のS3クラスインフラに基づいてファイルのインポートとエクスポートを行う。これは、他のパッケージがS3メソッドを登録することによって、rio拡張機能を含むことができることを意味する。これらのメソッドは.import.rio_X().export.rio_X()という形式を取る必要があり、ここでXはファイルタイプの拡張子である。例として、[rio.dbパッケージ}(https://github.com/leeper/rio.db)で提供されている。