Rで何かをしたり、読書をするブログ

政府統計の総合窓口のデータや、OECDやUCIやのデータを使って、Rの練習をしています。ときどき、読書記録も載せています。

東証の上場会社の資金調達額のデータの分析5 - 機械学習で株式公募の件数を予測する。

www.crosshyou.info

の続きです。前回までの分析で、株式公募や債券発行での資金調達には季節性があること、株式公募と債券発行には相関があることが確認できました。

今回は、趣向を変えて、機械学習で株式公募の件数を予測してみます。

採用するモデルは、線形モデル、Elastic-Netモデル、決定木モデル、ランダムフォレストモデルの4つです。

まず、tidymodelsパッケージの読み込みをします。

トレーニング用のデータとテスト用データを作成します。

レシピを作成します。

eqy_numをターゲット変数にして、eqY_val, bond_num, bond_val, monthを説明変数にします。step_dummy()関数でmonthをダミー変数にして、step_zv()関数で0分散の変数を削除し、step_normalize()関数で数値型変数を標準化しています。

各モデルを作成します。

線形モデルはlm, Elastic-Netモデルはglmnet, 決定木モデルはrpart, ランダムフォレストモデルはrangerをエンジンに採用しました。

ワークフローを作成します。

クロスバリデーションの設定をします。

チューニング・グリッドを作成します。grid_regula()関数を使いました。線形モデルはチューニングは不要です。

grid_tune()関数でチューニングを実行します。

決定木モデルとランダムフォレストモデルはチューニングに少し時間がかかりました。

select_best()関数で最適なパラメータを取り出します。

この最適なパラメータで最終ワークフローを作成します。finalize_workflow()関数を使います。

fit()関数でトレーニング用のデータを使って学習します。

predict()関数でテスト用のデータの予測をします。

metrics()関数でRMSEなどの評価指標を算出します。

それでは各モデルの評価指標を比較します。

ランダムフォレストモデルがRMSE、R2、MAEの3つの指標全てで一番良い結果でした。monthによってeqy_numの値がけっこう違うので、線形モデルは成績が悪かったですね。

最後にそれぞれの予測値と実際のeqy_numの散布図を描いてみます。

今回は以上です。

次回は、

www.crosshyou.info

です。

はじめから読むには、

www.crosshyou.info

です。

今回のコードは以下になります。

#
# tidymodelsの読み込み
library(tidymodels)
#
# eqy_numを被説明変数に、eqy_val, bond_num, bond_val, monthを説明変数
#
# 1. トレーニング用のデータ、テスト用のデータの作成(全モデル共通)
set.seed(9911)
split <- df |> 
  select(eqy_num:month) |> 
  initial_split(prop = 0.7)
train_data <- training(split)
test_data <- testing(split) 
train_data |> summary()
test_data |> summary()
#
# 2. レシピ作成(全モデル共通)
rec <- recipe(eqy_num ~ ., data = train_data) |> 
  step_dummy(month) |> 
  step_zv(all_predictors()) |> 
  step_normalize(all_numeric_predictors())
#
# 3. モデル設定(各モデル)
# 3-1. 線形モデル(lm)
lm_mod <- linear_reg() |> 
  set_engine("lm")
#
# 3-2. Elastic-Netモデル(glmnet)
glmnet_mod <- linear_reg(
  penalty = tune(),
  mixture = tune()
) |> 
  set_engine("glmnet")
#
# 3-3. 決定木モデル(rpart)
rpart_mod <- decision_tree(
  cost_complexity = tune(),
  tree_depth = tune(),
  min_n = tune()
) |> 
  set_engine("rpart") |> 
  set_mode("regression")
#
# 3-4. ランダムフォレストモデル(ranger)
ranger_mod <- rand_forest(
  mtry = tune(),
  min_n = tune(),
  trees = 1000
) |> 
  set_engine("ranger") |> 
  set_mode("regression")
#
# 4. ワークフロー作成(各モデル)
# 4-1. 線形モデル(lm)
lm_wf <- workflow() |> 
  add_model(lm_mod) |> 
  add_recipe(rec)
#
# 4-2 Elastic-Netモデル(glmnet)
glmnet_wf <- workflow() |> 
  add_model(glmnet_mod) |> 
  add_recipe(rec)
#
# 4-3. 決定木モデル(rpart)
rpart_wf <- workflow() |> 
  add_model(rpart_mod) |> 
  add_recipe(rec)
#
# 4-4. ランダムフォレストモデル(ranger)
ranger_wf <- workflow() |> 
  add_model(ranger_mod) |> 
  add_recipe(rec)
#
# 5. クロスバリデーションの設定(全モデル共通)
set.seed(2929)
folds <- vfold_cv(train_data, v = 10)
#
# 6. チューングリッドの作成(各モデル)
# 6-1. 線形モデル(glm)
# 線形モデルはチューニングは不要
#
# 6-2. Elastic-Netモデル(glmnet)
glmnet_grid <- grid_regular(
  penalty(range = c(-4, 0)),
  mixture(range = c(0.2, 0.8)),
  levels = c(penalty = 20, mixture = 7)
)
#
# 6-3. 決定木モデル(rpart)
rpart_grid <- grid_regular(
  cost_complexity(range = c(-4, -1)),
  tree_depth(range = c(2, 9)),
  min_n(range = c(2, 20)),
  levels = c(cost_complexity = 5, tree_depth = 4, min_n = 10)
)
#
# 6-4. ランダムフォレストモデル(ranger)
ranger_grid <- grid_regular(
  mtry(range = c(1, 4)),
  min_n(range = c(2, 30)),
  levels = c(mtry = c(4, min_n = 29))
)
#
# 7. チューニング(各モデル)
# 7-1. 線形モデルは不要
#
# 7-2. Elastic-Netモデル(glmnet)
glmnet_tuned <- tune_grid(
  glmnet_wf,
  resamples = folds,
  grid = glmnet_grid,
  metrics = metric_set(rmse, rsq, mae),
  control = control_grid(save_pred = TRUE)
)
#
# 7-3. 決定木モデル(rpart)
rpart_tuned <- tune_grid(
  rpart_wf,
  resamples = folds,
  grid = rpart_grid,
  metrics = metric_set(rmse, rsq, mae),
  control = control_grid(save_pred = TRUE)
)
#
# 7-4. ランダムフォレストモデル(ranger)
ranger_tuned <- tune_grid(
  ranger_wf,
  resamples = folds,
  grid = ranger_grid,
  metrics = metric_set(rmse, rsq, mae),
  control = control_grid(save_pred = TRUE)
)
#
# 8. 最適なパラメータ(各モデル)
# 8-1. 線形モデルは不要
# 8-2. Elastic-Netモデル(glmnet)
glmnet_params <- select_best(glmnet_tuned, metric = "rmse")
glmnet_params
#
# 8-3. 決定木モデル(rpart)
rpart_params <- select_best(rpart_tuned, metric = "rmse")
rpart_params
#
# 8-4. ランダムフォレストモデル(ranger)
ranger_params <- select_best(ranger_tuned, metric = "rmse")
ranger_params
#
# 9. 最終ワークフロー作成(各モデル)
# 9-1. 線形モデル(lm)
lm_wf_f <- lm_wf
#
# 9-2. Elastic-Netモデル(glmnet)
glmnet_wf_f <- finalize_workflow(glmnet_wf, glmnet_params)
#
# 9-3. 決定木モデル(rpart)
rpart_wf_f <- finalize_workflow(rpart_wf, rpart_params)
#
# 9-4. ランダムフォレストモデル(ranger)
ranger_wf_f <- finalize_workflow(ranger_wf, ranger_params)
#
# 10. train_dataで学習(各モデル)
# 10-1. 線形モデル(lm)
lm_fit <- fit(lm_wf_f, train_data)
#
# 10-2. Elastic-Netモデル(glmnet)
glmnet_fit <- fit(glmnet_wf_f, train_data)
#
# 10-3. 決定木モデル(rpart)
rpart_fit <- fit(rpart_wf_f, train_data)
#
# 10-4. ランダムフォレストモデル(ranger)
ranger_fit <- fit(ranger_wf_f, train_data)
#
# 11. test_dataで予測(各モデル)
# 11-1. 線形モデル(lm)
lm_pred <- test_data |> 
  select(eqy_num) |> 
  bind_cols(predict(lm_fit, test_data)) |> 
  mutate(model = "linear")
#
# 11-2. Elastic-Netモデル(glmet)
glmnet_pred <- test_data |> 
  select(eqy_num) |> 
  bind_cols(predict(glmnet_fit, test_data)) |> 
  mutate(model = "elastic_net")
#
# 11-3. 決定木モデル(rpart)
rpart_pred <- test_data |> 
  select(eqy_num) |> 
  bind_cols(predict(rpart_fit, test_data)) |> 
  mutate(model = "decision_tree")
#
# 11-4. ランダムフォレストモデル(ranger)
ranger_pred <- test_data |> 
  select(eqy_num) |> 
  bind_cols(predict(ranger_fit, test_data)) |> 
  mutate(model = "random_forest")
#
# 12. 評価(各モデル)
# 12-1. 線形モデル(lm)
lm_results <- lm_pred |> 
  metrics(truth = eqy_num, estimate = .pred) |> 
  mutate(model = "linear")
#
# 12-2. Elastic-Netモデル(glmnet)
glmnet_results <- glmnet_pred |> 
  metrics(truth = eqy_num, estimate = .pred) |> 
  mutate(model = "elastic_net")
#
# 12-3. 決定木モデル(rpart)
rpart_results <- rpart_pred |> 
  metrics(truth = eqy_num, estimate = .pred) |> 
  mutate(model = "decision_tree")
#
# 12-4. ランダムフォレストモデル(ranger)
ranger_results <- ranger_pred |> 
  metrics(truth = eqy_num, estimate = .pred) |> 
  mutate(model = "random_forest")
#
# 13. 評価の比較
# 13-1. 評価を合体
all_results <- lm_results |> 
  bind_rows(glmnet_results, rpart_results, ranger_results)
#
# 13-2. RMSEが一番小さいのは?
all_results |> 
  filter(.metric == "rmse") |> 
  arrange(.estimate)
# ランダムフォレストモデルが一番
#
# 13-3. R2が一番大きいのは?
all_results |> 
  filter(.metric == "rsq") |> 
  arrange(desc(.estimate))
# ランダムフォレストモデルが一番
#
# 13-4. MAEが一番小さいのは
all_results |> 
  filter(.metric == "mae") |> 
  arrange(.estimate)
# ランダムフォレストモデルが一番
#
# 14. 実際の値と予測値のグラフ
# 14-1. 予測値を統合
all_pred <- lm_pred |> 
  bind_rows(glmnet_pred, rpart_pred, ranger_pred)
#
# 14-2. グラフ
all_pred |> 
  ggplot(aes(x = .pred, y = eqy_num)) +
  geom_point(aes(color = model), alpha = 0.5, size = 3) +
  geom_abline(linewidth = 1) +
  theme_minimal()
#

 

(冒頭の画像は、Bing Image Creator で生成しました。プロンプトは、Landscape, beautiful long wide view of natural green fields, running a small creek, close up of blue Amaryllis flower, photo です。)