データ分析をちゃんと管理しよう【コーディング編】
今月10記事も書けるのか?
ううう……がんばりましゅ……
フォルダ構成編 socinuit.hatenablog.com
ファイル名編 socinuit.hatenablog.com
ここまででできるようになったこと
- データを扱うプロジェクトはフォルダ構成が重要。
- 入力データを保存するフォルダ
input
- 出力結果を保存するフォルダ
output
- 分析に使ったコードを保存するフォルダ
source
- 入力データを保存するフォルダ
- 基本的にそのプロジェクトフォルダの中で全ての作業が完結する。
- フォルダへのアクセスについてのRの関数
- フォルダを「作る」
dir.create
- フォルダ内のファイルのリストを表示する
list.files
- フォルダを「作る」
- フォルダ名・ファイル名に規則性を設ける
- 自分の成果をコンピュータを使って探しやすくするため
- いつやったかの記録
Sys.Date
- 文字列の結合
paste
とpaste0
- 文字列のマッチング
grep
使いこなせずとも「Rだけでこのあたりの話が完結する」ということが分かってくれたら嬉しいなあと思います。
今回できるようになりたいこと
- Rのコードを「タスクや工程によって分ける」こと。
基本思想
1つのコードで前処理・分析・結果の出力の全てを記述すると、
修正や編集が必要となった時に、様々な部分を同時に変更しなければならず、
分析結果の再現性を担保することが難しくなります。
例えばシンプルに「前処理用コード」「分析用コード」「結果確認・出力用コード」に分けるとすると、
例えば前処理の追加編集のためには「前処理用コード」を編集すればよく、
解析のためのパラメータの編集をする場合には「分析用コード」を編集すればよい、
という形で「どこを編集するべきなのか」を限定することができます。
結果の管理は前回まででファイル名やフォルダの階層で行えばよいので、
コード内で使う変数名やアウトプットの名前をいちいち変更する必要はありません。
これを実行できれば、効率的で再現性のある形での分析ワークが進むと思いませんか!?
それを実現するために、Rにはsource
関数という関数があります。
この関数は自分で作成したRコードを、別のRコードで読み込むことができる関数です。
こんな感じでやっていきをしよう
修士学生だったきぬいとは、こんなコードで分析をしていました。
ファイル名: 分析.R # ライブラリ読み込み library(reshape) library(lme4) library(data.table) # データ読み込み data_1 <- fread("dat1.csv") data_2 <- fread("dat2.csv") ## ....(中身の確認・EDAムーブで数十行) # データ前処理 data_1 <- hoge(data_1) data_2 <- hoge(data_2) #### ...(数十行) # 分析 reg1 <- regfunc(y~., data = data_1) summary(reg1) reg1$coefficients # ...(確認の行が数十行) # 出力 write.csv(reg1$coefficients, "結果.csv") ##(合計300行以上)
……かなり省略しましたが、このコードの何が問題だったのか。
- データの読み込み・前処理・分析・結果の確認・出力の全てが1ファイル
- 修正・実行は各工程を見直して適宜編集しなければならない
- 前処理後のテーブルを出力していない
- 分析に使うデータを再現できない
- 変数名が抽象的すぎる
data_1
はなんのデータなのか全くわからない
こんなコードを明日から書かなくていいようにしよう*1。
ファイル名: 前処理.R # ライブラリも前処理で使うものだけを入れる。 library(tidyverse) library(data.table) research_data <- data.table::fread("./input/research_data.csv", encoding = "unknown") geom_data <- data.frame::fread("./input/geometry_data.csv", encodinf = "UTF-8") # hoge関数は前処理に関する任意のいろいろな手続きです。 # みんなでいろいろ書いてみましょう。 for_regression_data <- hoge(research_data, geom_data)
注意するべきことに、source
関数は日本語を含むマルチバイト文字が入っているRコードの読み込みでエラーを履くことがあります。
これは文字コードのエラーだったりするので、encoding = "UTF-8"
などと設定すると良いです*2。
ファイル名: 回帰分析.R # ライブラリは必要なものだけを入れる library(lme4) library(glmnet) # これで for_regression_data を読み込むことができる。 source("./source/前処理.R", encoding = "UTF-8") nb_regression <- lme4::glmer.nb(y~., data = for_regression_data) l1_regression <- glmnet::glmnet(y = y, x = x, ...)
同様に、結果の確認や出力のスクリプトはこんな感じです。
ファイル名: 結果確認.R source("./source/回帰分析.R", encoding = "UTF-8") ## ...(結果の確認やアウトプットについての記述)
……というような形で、それぞれでsource
関数を呼び出すことで、
それまでの手続きの継承が可能です。
上記の例だと回帰分析.R
を呼び出すことで、連鎖的に前処理.R
を呼び出されているのですが、
例えば前処理.R
のfor_regression_data
を中間データとして出力し、
回帰分析.R
のsource
部分をread.csv(中間データ名)
のように編集すれば、
連鎖的な呼び出しのない形で呼び出しが可能です。
こういうスタイルだと、例えばEDAのパートを分析の実行時に呼び出さずに構成可能ですし、
結果の確認と出力を分ければ、なんか変な結果になったときにも立ち戻ることも楽にできます。
次回予告
まだつづくのか。
次回は「エラーから逃げるな」という話をします。