kaggleトレーニングvol.1 ~Titanic: Machine Learning from Disaster~

#はじめに
[@wasabi_sugiさん](https://qiita.com/wasabi_sugi)が[kaggleの和訳記事](https://qiita.com/wasabi_sugi/items/0bc51e8da505c90061da)を書かれていて、自分も前からkaggleに興味を持っていたので記事を読んでみました。
ただ、自分で手と頭を動かさないと理解も進まないと思い、コードの写経と自分なりの解説を書いていくことに。
簡単に言うとパクリです、はい。

##目的
自分の勉強のためです。

##方針
kaggleのコンペからランダムで課題を選び、Kernel内のコードを自分なりに理解・解説しながら写経していきます。

##注意
勘違い、誤りが含まれているかもしれません。もし何かあればコメントでご指摘頂けると幸いです。




#本日のコンペとKernel
今回は、みんな大好きタイタニックから、[こちらのコード](https://www.kaggle.com/benhamner/random-forest-benchmark-r/code)を写経していきます。


##コンペの概要
1912年に起きたタイタニック号の沈没事件。多くの人が亡くなったが、女性、子供、上層階の客など、他の人より生き残る可能性が高いグループもあった。このコンペでは、どういう人が生き残る可能性が高いのか予測してみましょう。




#コード


```R
library(ggplot2)
library(randomForest)
```

ライブラリーの読み込み。
`randomForest`は全く使ったことがないですし、その理論もよくわかっていません。早速そこから検索。[このページ](https://mathwords.net/randomforest)が自分的にわかりやすかったです。
要は、判断基準（決定木）をたくさん作って、そこから良い感じの答えを一つ導き出そう、とかそんな感じだろうか。



```R
set.seed(1)
train <- read.csv('data/train.csv', stringsAsFactors = FALSE)
test  <- read.csv('data/test.csv', stringsAsFactors = FALSE)
```

`set.seed()`は、決められた乱数を作るためのもの（矛盾！）。
何かしらの乱数を生成する前に、`set.seed(1)`等としておくと、次回以降も同じ乱数が生成できるよ、というもの。

`read.csv`はkaggleからダウンロードしてきた2つのcsvデータの読み込みですね。



```R
extractFeatures <- function(data){
  features <- c('Pclass',
                'Age',
                'Sex',
                'Parch',
                'SibSp',
                'Fare',
                'Embarked')
  fea <- data[, features]
  fea$Age[is.na(fea$Age)] <- -1
  fea$Fare[is.na(fea$Fare)] <- median(fea$Fare, na.rm = TRUE)
  fea$Embarked[fea$Embarked == ''] = 'S'
  fea$Sex <- as.factor(fea$Sex)
  fea$Embarked <- as.factor(fea$Embarked)
  return(fea)
}
```

関数の定義。関数名`extractFeatures`は「特徴を抽出する」という意味。この関数では、読み込んだcsvデータの欠損値の処理方法を定義しています。

`features`オブジェクトに`Pclass`等を入れていますが、これは先程読み込んだ2つのcsvのカラム名になっています。

`fea <- data[, features]`
与えられた`data`に関して、`features`と一致するカラムを`fea`に入れる。

`fea$Age[is.na(fea$Age)] <- -1`
`Age`列の欠損値に対して、`-1`を代入。

`fea$Fare[is.na(fea$Fare)] <- median(fea$Fare, na.rm = TRUE)`
`Fare`（運賃）の欠損値に対して、`Fare`の欠損値を除く中央値を代入。

`fea$Embarked[fea$Embarked == ''] = 'S'`
`Embarked`（確か、乗客の乗船した港）の欠損値に対して、`S`(=Southampton)を代入

`fea$Sex <- as.factor(fea$Sex)`
`Sex`列をFactor型にする。

`fea$Embarked <- as.factor(fea$Embarked)`
`Embarked`列をFactor型にする。

`return(fea)`
欠損値の補間等を済ませた値を返す。


```R
rf <- randomForest(extractFeatures(train),
                   as.factor(train$Survived),
                   ntree = 100,
                   importance = TRUE)
```

`randomForest`は、ランダムフォレストによる、分類と回帰を行ってくれる関数。

最初の引数`extractFeatures(train)`
予測のためのトレーニングデータを指定しています。

2つ目`as.factor(train$Survived)`
トレーニングデータに含まれる目的変数を指定しています。

3つ目`ntree = 100`
生成する決定木の数。Rのヘルプを見ると、「小さすぎる数を指定しないように」と記述がありました。この数を大きくしたり小さくすることが、予測精度の高低に関わりそう。

4つ目`importance = TRUE`
「予測変数の重要度を出力しますか？」ということのようです。デフォルトは`FALSE`。ここは基本`TRUE`としておいたほうが、結果の判断に役立ちそうです。


```R
submission <- data.frame(PassengerId = test$PassengerId)
submission$Survived <- predict(rf, extractFeatures(test))
write.csv(submission, file = "1_random_forest_r_submission.csv", row.names=FALSE)
```

`submission <- data.frame(PassengerId = test$PassengerId)`
テスト用（予測する側）のデータから、乗客IDを抜き出す。

`submission$Survived <- predict(rf, extractFeatures(test))`
先程作成したモデル`rf`を、テストデータに当てはめ、その予測結果を出す。

`write.csv(submission, file = "1_random_forest_r_submission.csv", row.names=FALSE)`
予測結果をcsv出力。



```R
imp <- importance(rf, type=1)
featureImportance <- data.frame(Feature=row.names(imp), Importance=imp[,1])
```

`importance()`は、`randomForest`で計算された重要度を出力する関数。
typeはNULL,1,2のいずれかを指定できて、
NULL=下記両方
1=平均精度の低下
2=ノード不純物の平均減少
　※グーグル翻訳
の結果が得られます。正直なんのことやら...

`featureImportance <- data.frame(Feature=row.names(imp), Importance=imp[,1])`
重要度のグラフ化のために、新たなデータフレームの作成。


```R
p <- ggplot(featureImportance, aes(x = reorder(Feature, Importance), y = Importance)) +
  geom_bar(stat = 'identity', fill = '#53cfff') +
  coord_flip() +
  theme_light(base_size = 20) +
  xlab('') +
  ylab('Importance') +
  ggtitle('Random Forest Feature Importance\n') +
  theme(plot.title = element_text((size = 18)))
```

重要度のグラフ化。
私があまり使ったことなかった関数だけ記述します。
`reorder`
最初の引数を、2編目の引数の値に基づいて並び替えてくれる。つまり、昇順でのグラフ作成が可能。

`theme_light(base_size = 20)`
プロットエリア全体の体裁を変更する。便利だ。
`light`の他にも、`bw`や`linedraw`なんかもある。詳しくは、ヘルプ参照。

```R
ggsave("2_feature_importance.png", p)
```

作成したグラフの保存。
以上！



#疑問点、改善点

- `set.seed(1)`を使う理由。
コメントにありましたが、randomForestはランダム性を持っているため、これを行わないとこのコードを実行する度に結果が変わるから、とのこと。

- Ageの欠損値を-1で補完している。中央値、平均値などにしたら、もう少し精度が上がりそう。

- `ntree = 100`にしている。ここも200,300,400と変えたら、精度が変わるのではないか。



#おわりに
まだまだわからないことだらけ。
ただ、一歩一歩進めていくことが成長の近道だとも思うので、定期的に記事書いていきます。