# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + [markdown] id="view-in-github" colab_type="text"
# <a href="https://colab.research.google.com/github/yukinaga/minnano_kaggle/blob/main/section_3/03_cross_validation.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>

# + [markdown] id="E-QmT8-Qhgt9"
# # 交差検証
# 「交差検証」により、過学習の問題に対処します。 

# + [markdown] id="sW2dwjT2kYqQ"
# ## データの準備
# 必要なライブラリの導入、データの読み込みと加工を行います。

# + id="Jwulz4eYsxZG"
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score

import lightgbm as lgb

train_data = pd.read_csv("train.csv")  # 訓練データ
test_data = pd.read_csv("test.csv") # テストデータ

test_id = test_data["PassengerId"]  # 結果の提出時に使用

data = pd.concat([train_data, test_data], sort=False)  # テストデータ、訓練データを結合

# カテゴリデータの変換
data["Sex"].replace(["male", "female"], [0, 1], inplace=True)
data["Embarked"].fillna(("S"), inplace=True)
data["Embarked"] = data["Embarked"].map({"S": 0, "C": 1, "Q": 2})

# 欠損値を埋める
data["Fare"].fillna(data["Fare"].mean(), inplace=True)
data["Age"].fillna(data["Age"].mean(), inplace=True)

# 新しい特徴量の作成
data["Family"] = data["Parch"] + data["SibSp"]

# 不要な特徴量の削除
data.drop(["Name", "PassengerId", "SibSp", "Parch", "Ticket", "Cabin"],
          axis=1, inplace=True)

# 入力と正解の作成
train_data = data[:len(train_data)]
test_data = data[len(train_data):]
t = train_data["Survived"]  # 正解
x_train = train_data.drop("Survived", axis=1)  # 訓練時の入力
x_test = test_data.drop("Survived", axis=1)  # テスト時の入力

x_train.head()

# + [markdown] id="mJypDLDg1lld"
# ## 交差検証
# scikit-learnの`StratifiedKFold`により交差検証を行います。  
# https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedKFold.html  
# `StratifiedKFold`を使えば検証用データ中の0と1の割合を一定に保つことができます。

# + id="oeA3OUzc0Qld"
y_valids = np.zeros((len(x_train),)) # 予測結果: 検証用データ
y_tests = []  # 予測結果: テスト用データ

skf = StratifiedKFold(n_splits=5, shuffle=True)

# ハイパーパラメータの設定
params = {
    "objective": "binary",  # 二値分類
    "max_bin": 300,  # 特徴量の最大分割数
    "learning_rate": 0.05,  # 学習率
    "num_leaves": 32  # 分岐の末端の最大数
}

categorical_features = ["Embarked", "Pclass", "Sex"]

for _, (ids_train, ids_valid) in enumerate(skf.split(x_train, t)):
    x_tr = x_train.loc[ids_train, :]
    x_val = x_train.loc[ids_valid, :]
    t_tr = t[ids_train]
    t_val = t[ids_valid]

    # データセットの作成
    lgb_train = lgb.Dataset(x_tr, t_tr, categorical_feature=categorical_features)
    lgb_val = lgb.Dataset(x_val, t_val, reference=lgb_train, categorical_feature=categorical_features)
    
    # モデルの訓練
    model = lgb.train(params, lgb_train, valid_sets=[lgb_train, lgb_val],
                      verbose_eval=20,  # 学習過程の表示間隔
                      num_boost_round=500,  # 学習回数の最大値
                      early_stopping_rounds=10)  # 連続して10回性能が向上しなければ終了

    # 結果を保持
    y_valids[ids_valid] = model.predict(x_val, num_iteration=model.best_iteration)
    y_test = model.predict(x_test, num_iteration=model.best_iteration)
    y_tests.append(y_test)

# + [markdown] id="wS5We0rU_vnG"
# ## 正解率
# 検証用データによる予測結果と正解を使い、正解率を計算します。

# + id="XYpY6tgX0uqm"
y_valids_bin = (y_valids>0.5).astype(int)  # 結果を0か1に
accuracy_score(t, y_valids_bin)  # 正解率の計算

# + [markdown] id="tHN33EZaAgwM"
# ## 提出用のデータ
# 提出量データの形式を整え、CSVファイルに保存します。

# + id="TI9FZwRnAgwN"
y_test_subm = sum(y_tests) / len(y_tests)  # 平均をとる
y_test_subm = (y_test > 0.5).astype(int)  # 結果を0か1に

# 形式を整える
survived_test = pd.Series(y_test_subm, name="Survived")
subm_data = pd.concat([test_id, survived_test], axis=1)

# 提出用のcsvファイルを保存
subm_data.to_csv("submission_titanic_cv.csv", index=False)

subm_data

StarcoderdataJupyterScriptsDedupFiltered