【R】エクセルのファイルをロードしたい
データっていろんなフォーマットで保存されてますが、まぁ一般的なのがエクセル(.xls, .xlsx)ですね。
データ分析とか仕事でしてる人からしたら「は?」って感じかもしれないですが、未だに政府系のデータとかエクセルフォーマットのみで記録されてたり、個人的なつながりでもらったデータは全部エクセルだったりすることは珍しくないです。
このエクセルデータ、どうやってRで読もうかという話になるんですが、いくつか方法があります。
大まかに分けると
という感じですね。
1. エクセルでファイル変換してから読む
文字通りです。まず該当するファイルをエクセルで開いて、「名前をつけて保存」。そのときに、ファイル形式をテキスト形式に変更して保存(例えばcsvなど)
df = read.csv("data1.csv", header = TRUE)
2. コピペしてRで読む
データの一部だけ読みたいなら、コピペで読み込ませることも可能。
エクセルでデータを開いて、ほしい箇所をコピーします。この状態でクリップボードに該当箇所が一時保存されているので、
df = read.table("clipboard")
としてやれば、該当箇所がデータフレームの形でロードされます。
3. Rパッケージを使う
大量のエクセルファイルを読み込む場合は上のような方法だと手間がかかりすぎますよね。
私はインドネシア人の友達のデータ処理を手伝ったときに、インドネシア政府のデータで月ごとに約20年分(12 x 20 = 240 個のファイル)のエクセルファイルを読み込むという作業をやりました。しかも途中から形式がxlsからxlsxに変わるという・・・とてもじゃないけど手作業では無理です。
エクセルファイルを読み込むパッケージはいくつかあります。知っている限りで、
- RODBC
- xlsReadWrite
- XLConnect
- gdata
- xlsx
- openxlsx
- readxl
RODBCというのが最初に流行っていたみたいですが、Windowsのみで、しかも32bitしかサポートしてないみたいです。
xlsReadWriteパッケージは、速いけど、xlsxはサポートしてないようです。XLConncetは遅いらしい。
結論から言うと、特にこだわりがない場合、現時点ではreadxlを使うのがいいのではないだろうか。
他のパッケージはperlとかjavaとかに依存していたりして、環境によってエラーがでるので面倒だったりする。
Hadley Wickham教ならreadxl一択ですが、過去のコードの直しなどで必要な場合のために、エクセル読み込みの経験をまとめておきます。
なお、それぞれ読みこむ関数の名前がパッケージによってread.xlsとかread.xlsxなど同じで混乱する可能性があるので、パッケージ名を::でつないでわざわざ書いてます。
gdata
gdataパッケージはxlsもxlsxも読んでくれます。私はWindowsとMac両方で使いましたが、Linuxもいけるみたいです。
エクセルを読む関数で忘れてはいけないのが、どのシートを読み込むかです。gdata::read.xlsではsheet引数で指定します。
ちなみにskipは空白行を除いた最初のn行をスキップします。ネ申エクセルなんかを読むときに重宝しますね。
library(gdata) df = gdata::read.xls("data1.xls", sheet = 1, skip = 3)
gdataを使おうとしてよく起こるエラーがperlに関するエラーではないかな。
おっしゃ読むぞ!と思って走らせると
Error in findPerl(verbose = verbose) : perl executable not found. Use perl= argument to specify the correct path. Error in file.exists(tfn) : invalid 'file' argument
というエラーが出ることがあります。
これは、windowsではperlは最初からインストールされていないので、こういうところからperlをダウンロードして、読み込むときにperlのファイルがあるところのパスを指定してやるとうまくいきます。
df = read.xls("data1.xlsx", perl = "C:\\Perl64\\bin\\perl.exe")
xlsx
名前そのまんまのパッケージ。読むだけじゃなくて書き出すこともできるようです。
シートを指定するときに、sheetIndexで何枚目のシートか指定する他に、sheetNameでシートの名前でも指定できます。
df = xlsx::read.xlsx("data1.xlsx", sheetName = "Sheet1")
ただし、少し遅いみたいです。
代替法として、xlsx::read.xlsx2という関数があります。
これは早くなるかわりに、それぞれの変数(列)のクラスを手で指定してやる必要があります。
下の例だと、1列目が文字列、2列目がファクター、3列目が数値として指定しています。
df = xlsx::read.xlsx2("data1.xlsx", sheetName = "Sheet1", colClasses = c("character", "factor", "numeric") )
xlsxパッケージを走らせようとすると、Javaが必要になるのですが、
rJavaをインストールしようとすると、
library("rJava", lib.loc="C:/Program Files/R/R-3.0.3/library") Error : .onLoad failed in loadNamespace() for 'rJava', details: call: inDL(x, as.logical(local), as.logical(now), ...) error: unable to load shared object 'C:/Program Files/R/R-3.0.3/library/rJava/libs/x64/rJava.dll': LoadLibrary failure: The specified module could not be found.
というエラーが出ます。
これはシンプルにJavaがインストールされていないためにでるので、
https://www.java.com/en/download/manual.jsp
からJavaをダウンロードしてインストールすることで解決します。
openxlsx
xlsxもインストールできて、さぁ読み込もう!と思ったら今度はデータが大きすぎて、メモリ容量オーバーで読み込めないって言われたんですよ・・・。
まぁ60列ぐらいで、20万行ぐらいあるデータだったので、エクセルでも開くの時間かかりますし。
そこで調べたところ、openxlsxを使えばよいっぽいことがわかった。
このread.xlsx関数はsheetの引数が数字なら単に何枚目かを示し、文字列なら名前を指定していることになるので、区別がなく便利。
何行スキップするかではなく、何行目から読み始めるかをstartRowで指定する。
library(openxlsx) df = openxlsx::read.xlsx("data1.xlsx", sheet = "Sheet1", startRow = 2)
readxl
シンプルに速いし、データ容量の問題も今の所起こってないです。
特に何も理由がなければ、これを使うのが一番ストレス少ないと思います。
こちらもsheetで数字(何枚目か)と文字列(シート名)のどちらでも指定できます。
df = readxl::read_excel("data1.xlsx", sheet = 1)
おまけ:大量・複数のExcelファイルを読み込む
多くのファイルを一気に読み込みたい場合ってありますよね。
おそらく現状ではpurrr::mapとreadxlを組み合わせるのが良いと思います。
同じフォルダにすべてのエクセルファイルがあるという前提です。
library(readxl) library(purrr) file.list <- list.files(pattern='*.csv') # ファイル名の一覧を取得 file.list <- setNames(file.list, file.list) # 各行にファイル名をIDとして名付ける。 data = map_df(file.list, ~read_excel(.x, sheet = 1), .id = "id")
二行目は、各行にファイル名をIDとしてつけるために必要です。
たとえば、2000年から2020年で、1年毎に分けられたエクセルファイル hogehoge_2000.xlsx からhogehoge_2020.xlsxを読み込みたいが、
各ファイルの中身は同じな場合、まとめてしまうとどのデータがどの年かわからなくなってしまいます。
それを防ぐために、各ファイルを読みこんだときに、名前をつけておいて、どのファイルから読み込まれたデータかわかるようにしておきます。
あと、エクセルでありがちなのが、各ファイルによって変数名が違ったり、変数の数がまちまちだったりする場合です。*1
こういう場合は読み込んでから手作業で対処する必要があります。
map_dfの代わりにmapを使って、リスト形式として読み込んでから、一つずつ手作業で直してから結合するのがいいかと思います。
data = map(file.list, ~read_excel(.x, sheet = 1))
data1とすれば1つ目のデータファイル、data2とすれば2つ目、というふうに取り出せるので、一つずつチェックして行くことになります。(ご苦労さまです・・・)
データの処理についてはこれらに本が参考になると思います。
*1:変数名が全角の年と半角の年があったりな!!