R

トップページ
Google
WWWを検索
サイト内を検索

Rでデータを読み書きするときは、CSVのような構造化されたテキストファイルを使うときが多いと思いますが、Rにもバイナリ・データの操作のためにraw型と、そのための関数が用意されています。

raw型にはpack形式とunpack形式の使い方があります。numeric型やcharacter型とpack形式raw型は、相互に変換できます。integer型で表される数値文字参照(コード番号)から、pack形式もしくはunpack形式のraw型にできます。character型で表される16進数値文字参照(16進コード番号)から、pack形式もしくはunpack形式のraw型にできます。バイナリファイルはpack形式raw型の塊として取り扱いますが、numeric型やinteger型と看做して入力できます。numeric型やinteger型のベクトルをpack形式raw型の塊として書き込めば、バイナリファイルになります。本稿ではこれらのことを、実際に操作でなぞります。

Rは対話型インターフェイスなので、慣れてきたらバイナリ・エディタの代わりに使えるかも知れません。

1 raw型の扱い方

raw型ベクトルも他の型と同様にコピーや変更ができますが、やはり他の型との変換が肝になります。

1.1 pack形式raw型への変換

writeBinでraw型に変換するのは意外かも知れません。

# character型はcharToRawで変換できる
(p_raw_char <- charToRaw("a"))
[1] 61
# integer型はas.rawで変換できる/数値文字参照(コード番号)入力
(p_raw_int <- as.raw(123)) # 123の代わりに0x7bと書いてもよい
[1] 7b
# character型で表される16進数値文字参照(16進コード番号)
(p_raw_int_hex <- as.raw(as.hexmode("7b")))
[1] 7b
# numeric型に限らずwriteBinはどの型も変換できる
(p_raw_numeric <- writeBin(c(1.23, 4.56), raw()))
 [1] ae 47 e1 7a 14 ae f3 3f 3d 0a d7 a3 70 3d 12 40

1.2 pack形式raw型からの変換

readBinでraw型から戻せるのも以外かも知れません。

# character型にはcharToRawで変換
rawToChar(p_raw_char)
[1] "a"
# integer型の数値文字参照(コード番号)にはas.integer
as.integer(p_raw_int)
[1] 123
# integer型にしてから16進数表記にする
sprintf("%x", as.integer(p_raw_int_hex))
[1] "7b"
# 型と長さnの指定が必要
readBin(p_raw_numeric, numeric(), n = 2)
[1] 1.23 4.56

1.3 unpack形式への変換

アセンブラ使いだった人以外、使い道が思いつかないかも知れませんが、0と1の配列にできます。

# 数値文字参照(コード番号)からの変換
(up_raw_int <- intToBits(123))
 [1] 01 01 00 01 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[26] 00 00 00 00 00 00 00
# pack形式raw型からの変換
rawToBits(p_raw_int)
[1] 01 01 00 01 01 01 01 00

長さが異なるのは、integer型は32bitsで、raw型は8bitsだからです。

1.4 unpack形式からの変換

0と1の配列から、まとめたものにもできます。

packBits(up_raw_int, "raw") # raw型にする
[1] 7b 00 00 00
packBits(up_raw_int, "integer") # integerとdouble(numeric)は直接戻せる
[1] 123

2 バイナリファイルの扱い方

エラーは起きないと見なせて、ファイルフォーマットが決まっていて、データをすべてまとめて読み書きできるのであれば、煩雑なことは無いです。

2.1 書き出し

numeric型の乱数列をつくってファイルに書き出します。

n <- 10
fname <- "vector.dat"
(v <- round(runif(n, min=0.5, max=9.5), 2))
 [1] 0.93 2.28 5.82 5.74 8.00 7.52 5.03 2.10 3.93 9.02
os <- file(fname, "wb")
writeBin(v, os)
close(os)

2.2 読み込み

つくったバイナリ・ファイルを読み込んでみましょう。 n <- 10としてもよいのですが、ファイルサイズからベクトルに含まれるnumeric型の値の個数を計算します。

fname <- "vector.dat"
fs <- file.info(fname)[, "size"]
size_of_numeric <- length(writeBin(numeric(1), raw())) # Cのsizeofの同等物が無い
n <- fs / size_of_numeric

ファイルを開いてnnumeric型を読み込みます。

is <- file(fname, "rb", blocking = FALSE)
(v2 <- readBin(is, numeric(), n = n)) # 変数の型と読み込む個数を指定する
 [1] 0.93 2.28 5.82 5.74 8.00 7.52 5.03 2.10 3.93 9.02
close(is)

書き出したvと読み込んだv2が同じか確認します。

if(all(v = v2)) print("v2 is the same as v.") else print("v2 is diffrent from v.")
Warning in all(v = v2): 'double' 型の引数を論理型に変換します
[1] "v2 is the same as v."

無事、同じでしたね。

3 逐次読み出しとランダムアクセス

Rのファイル入出力はC標準のファイル入出力と大きな差異はないです。一気に全体を読まない場合は、C言語のfeofに該当するisIncompleteTRUEになるまで繰り返し処理するコードを書きます。また、ランダムアクセス可能かはisSeekableで確認でき、可能な場合はseektruncateができます。一方で、flockは標準では提供されません。

4 Fortranで書いたバイナリファイルを読む

数値解析の速度は申し分がないFortranですが、プロットなどのI/O周りの使い勝手は悪いです。Fortranの計算結果をファイルに書き出して、Rに読ますのもひとつの方法です。簡単な例でやり方を確認しておきましょう。

4.1 Fortranのコード

3行3列の行列をFortranで作ってバイナリ・ファイルに保存します。

program makeBin
  implicit none
  double precision, dimension(3, 3) :: m = reshape((/1,2,3,4,5,6,7,8,9/),(/3,3/))

  open(17,file='data_seq.dat', form='unformatted', status='replace')
  write(17) m
  close(17)

  open(17,file='data_str.dat', form='unformatted', access = "stream", status='replace')
  write(17) m
  close(17)
end program makeBin

2つファイルを作っていますが、オプションで形式が変わるからです。Fortranのバイナリー形式(unformatted)のデフォルト(access = "sequential")のファイル書き出しだと、writeの度に前後にそれぞれ4バイトを足してきます1。Fortran 2003以降で使えるaccess = "stream"ではつきません。

4.2 Rのコード

access = "sequential"data_seq.datを読むコードを示します。

size <- c(3, 3)
fname <- "data_seq.dat"
fs <- file.info(fname)[, "size"]
is <- file(fname, "rb", blocking = FALSE)
buf <- readBin(is, "raw", fs)
# 先頭と末尾の4バイトを除外する
# access = "stream"で書いたならば不要
buf <- buf[-c(1:4, (fs - 3):fs)]
# raw型からnumeric型に変更
v <- readBin(buf2, numeric(), n = prod(size));
close(is)
m <- matrix(v, size[1], size[2])

5 Cで書いたバイナリ・ファイルを読む

Fortranのaccess = "sequential"のようにヘッダーとフッターをつけては来ませんが、C言語の二次元配列は行ごとに続けて値が入るので、Rのmatrixで行列に戻すときにはbyrow=TRUEが引数に要ります。

6 応用時の注意

Rに限った話ではありませんが、応用上はバイナリ・ファイルの構造の把握が大事です。昔の非Intelのシステムで読み書きするバイナリファイルでは、エンディアンが今のシステムの逆の場合もあります。readBinwriteBinendianを引数に取れますが、bigを指定することになります。


  1. この頭と尻尾の4バイトが何になるかはFortranコンパイラごとに異なり、gfortranだと中身のファイルサイズになります。ただし、gfortranでwriteしたsequentialのバイナリーファイルの頭と尻尾を書き換えて、gfortranにreadさせても問題なく読み取れます。実際のところ、何の機能も果たしていないようです。↩︎