atm 08 

atmacupに初参戦することとなった
実質稼働日数は3日 ＆　ド素人だがとりあえず出てみることにした
親切なことに主催者の方々が初心者向けの講座をyoutubeに挙げていたのでそれをまとめて理解しようと思う

まず初めにどういうデータを取り扱って何を予測してその結果がどのように評価されるのかについての理解を深める

一番初めにやることはコードを書くことではなくデータのスキーマと実際にどういうデータが入っているのかを確認すること。自分が使える情報は何か、それはどのような構造を持っているか実際にどういう値が入っているかをしっかりと把握する

現象を理解する　データがどのように作られているかについて考える

実際に眺める　エクセルで開いてデータを確認する。
例えばnullがめっちゃ起こっている→特定の場合には二つの情報が同時に欠落するようなオペレーションになっているかも？テキストのカラムでの表記ゆれなど。

pandas profilingというライブラリがありこれを使うとデータフレームの統計量を一気に確認することができる。簡単な使い方はProfileReport classに可視化したいデータフレームを渡してreportを作りto_fileを呼び出してhtmlファイルとして保存する方法です。
保存したhtmlをブラウザで開くと以下のような統計量をカラムごとに確認出来て便利
1,ユニークな値の数 2,頻度の高い値 3,意味のないカラムやNullが多いカラムかどうか...etc

```python
pandas_profiling.ProfileReport(train_df)
```
学習用データと予測するデータの差分を知る
学習用データと予測データは別のデータなので、差分が強すぎるとよくない
差分をvenn図で見定める

```python
c="Year_of_Release"
fig,ax=plt.subplots(figsize=(5,3))
venn2(
    subsets=(set(train_df[c].unique()),set(test_df[c].unique())),
    set_labels=("Train","Test"),
    ax=ax
)
ax.set_title(c)
```
year_of_Release以外のデータも見てみる

```python
columns = test_df.columns
n_figs = len(columns)
n_cols = 4
n_rows = n_figs // n_cols + 1

fig, axes = plt.subplots(figsize=(n_cols * 3, n_rows * 3), ncols=n_cols, nrows=n_rows)

for c, ax in zip(columns, axes.ravel()):
    venn2(
        subsets=(set(train_df[c].unique()), set(test_df[c].unique())),
        set_labels=('Train', 'Test'),
        ax=ax
    )
    ax.set_title(c)

fig.tight_layout()
```
モデリングのための特徴量作成、アルゴリズムが理解しやすい特徴量を作ることを特徴量エンジニアリングという

連続変数の特徴量　連続変数の場合そのままモデルが解釈できるため基本的にはそのまま用いられる

欠損値の処理　通常欠損値があると欠損のことを考慮する必要があります。その場合平均値や中央値などで欠損を埋める必要がありますが、lightgbmなどのgbdtは欠損をそのまま自然に扱えるので基本的に気にする必要がない

連続変数とみなす場合にはその大小関係に意味があると思っていることと同値であることに注意する。大小関係に意味がない値の場合には連続変数ではなくてカテゴリ変数とみなして処理を行ったほうが適切な場合がある。勾配ブースティングは連続値とみなしてもある程度うまくやってくれるが線形モデルのように値の大小と予測結果が線形に比例するようなモデルでは予測性能が悪化する場合がある

```python
def preprocess(input_df: pd.DataFrame) -> pd.DataFrame:
    output_df = input_df.copy()
    output_df['User_Score'] = input_df['User_Score'].replace('tbd', None).astype(float)
    return output_df

def create_continuous_features(input_df):
    input_df = preprocess(input_df)
    use_columns = [
        # 連続変数
        'Critic_Score',
        'Critic_Count', 
        'User_Score', 
        'User_Count',
        'Year_of_Release'
    ]
    return input_df[use_columns].copy()
assert len(create_continuous_features(train_df)) == len(train_df)
assert create_continuous_features(train_df.head()).equals(create_continuous_features(train_df.head()))
```
booleanの特徴量
特定の条件を満たしているかどうかを表す特徴量。例えばテキストカラムに特定の文字列が存在しているかどうかなどが該当する

```python
def create_boolean_feature(input_df):
    output_df=pd.DataFrame()
    texts=[
        "japan","nintendo"
    ]
    for t in texts:
        output_df[f'Developer_has_{t}']=input_df["Developer"].fillna('').str.lower().str.contains(t).astype(int)
    return output_df
```

```python
# https://github.com/nyk510/vivid/blob/master/vivid/utils.py
from contextlib import contextmanager
from time import time

@contextmanager
def timer(logger=None, format_str='{:.3f}[s]', prefix=None, suffix=None):
    if prefix: format_str = str(prefix) + format_str
    if suffix: format_str = format_str + str(suffix)
    start = time()
    yield
    d = time() - start
    out_str = format_str.format(d)
    if logger:
        logger.info(out_str)
    else:
        print(out_str)
from tqdm import tqdm

def to_feature(input_df):
    processors = [
        create_continuous_features,
        create_boolean_feature
    ]

    out_df = pd.DataFrame()

    for func in tqdm(processors, total=len(processors)):
        with timer(prefix='create ' + func.__name__ + ' '):
            _df = func(input_df)

        assert len(_df) == len(input_df), func.__name__
        out_df = pd.concat([out_df, _df], axis=1)

    return out_df
train_feat_df = to_feature(train_df)
test_feat_df = to_feature(test_df)
```
lightgbmによる学習
特徴量を作成したので次にlightgbmによる学習を行っていく交差検証という考え方が大事

cross validationとは　学習用データセットを複数に分割してそれぞれの分割で学習検証のデータセットを作りモデルの性能を見積もる枠組み。学習データの中で今の枠組みの性能を評価したい。

一番ナイーブな方法はkfold これは何も考えずにとにかくランダムに学習データを分割する。
その他にターゲット(目的変数)の分布が同じになるように分割するStratifiedと呼ばれる方法もある。groupkfoldは訓練データと検証データに同じグループが現れないように分ける方法