<jupyter_start><jupyter_text>特徴量エンジニアリング（カテゴリ特徴量）<jupyter_code>!pip install -U pip -q
!pip install scikit-learn==0.23.1 -q
!pip install category_encoders -q
!git clone https://github.com/ironerumi/fe_workshop.git -q
import warnings

warnings.filterwarnings("ignore")

import numpy as np
import pandas as pd
from pandas import DataFrame, Series

pd.set_option("display.max_columns", 100)

from sklearn.preprocessing import OneHotEncoder, LabelEncoder
import category_encoders as ce

from google.colab import files
# collaboratoryにLendingClub50000.csvをアップロードする。
# uploaded = files.upload() # ローカルからアップロードできるが今回は使用しない。
# ファイルを読み込む
df = pd.read_csv("fe_workshop/dataset/LendingClub50000.csv")
df.head().T<jupyter_output><empty_output><jupyter_text>One-hoeエンコーディング<jupyter_code># pandasのget_dummiesでOne-hot Encodingする
df[["グレード"]].head().merge(
    pd.get_dummies(df["グレード"]).head(), left_index=True, right_index=True
)
# sklearnのOneHotEncoderでOne-hot Encodingする
ohe = OneHotEncoder()
ohe.fit_transform(df[["グレード"]]).A[:5, :]  # np.arrayで返ってくる
# category_encodersのOneHotEncoderでOne-hot Encodingする
ohe = ce.OneHotEncoder(
    handle_unknown="indicator", handle_missing=True, use_cat_names=True
)
# 空→nan、未知の値→-1として対応
df[["グレード"]].head().merge(
    ohe.fit_transform(df[["グレード"]]).head(), left_index=True, right_index=True
)
# 仮に1行目の'grade'を未知のデータに置き換えると
df.loc[0, "グレード"] = "Z"
df[["グレード"]].head().merge(
    ohe.transform(df[["グレード"]]).head(), left_index=True, right_index=True
)  # -1に割り当てられる
# 1行目'grade'の値を戻します
df.loc[0, "グレード"] = "A"
# では、'addr_state'も同様に処理できるだろうか？
df[["住所（州）"]].head()
# ユニーク数の多い'addr_state'に対してOne-hotを行うとカラム数が膨大になってしまう。
ohe = ce.OneHotEncoder(use_cat_names=True)
df[["住所（州）"]].head().merge(
    ohe.fit_transform(df[["住所（州）"]]).head(), left_index=True, right_index=True
)<jupyter_output><empty_output><jupyter_text>Labelエンコーディング<jupyter_code># sklearnのLabelEncoderを用いる
le = LabelEncoder()
le.fit_transform(df[["住所（州）"]].values.ravel())[:5]
# category_encodersのOrdinalEncoderを用いる。
# 割り振られるラベルは異なるが基本的には同じ。
# ただし、DataRobotのOrdinalEncodingとは大きく異なる点に注意。
oe = ce.OrdinalEncoder()
df[["住所（州）"]].head().merge(
    oe.fit_transform(df[["住所（州）"]]).head(),
    left_index=True,
    right_index=True,
    suffixes=("", "_変換後"),
)<jupyter_output><empty_output><jupyter_text>Frequency/Countエンコーディング<jupyter_code># 州ごとの観測数をカウントする。
summary = df.groupby(["住所（州）"])[["住所（州）"]].count()
summary.head()
# 集計結果をapplyする。
df[["住所（州）"]].head().merge(
    df["住所（州）"].apply(lambda x: summary.loc[x]).head(),
    left_index=True,
    right_index=True,
    suffixes=("", "_カウント"),
)<jupyter_output><empty_output><jupyter_text>Targetエンコーディング<jupyter_code># 'addr_state'について観測数の少ないカテゴリがどの程度か確認する。
df["住所（州）"].value_counts()
# category_encodersのTargetEncoderを用いる。
# min_samples_leafはvalue_countsを参考に100にしてみた。
# 州記号を州ごとの平均貸し倒れ率に置換したことになる。
te = ce.TargetEncoder(min_samples_leaf=100)
df[["住所（州）"]].head().merge(
    te.fit_transform(df[["住所（州）"]], df[["貸し倒れ"]]).head(),
    left_index=True,
    right_index=True,
    suffixes=("", "_カウント"),
)
# TargetEncoderの対象は予測ターゲットに限定されない。
# ここでは'annual_inc'を選択したため、州記号を州ごとの平均年収に置換したことになる。

# 特にインパクトの大きい特徴量がある場合に有効で、予測ターゲットを用いておらず、ターゲットリーケージの懸念がほぼない点で有用。
te = ce.TargetEncoder(min_samples_leaf=50)
result = te.fit_transform(df[["住所（州）"]], df[["年収"]])
df[["住所（州）"]].head().merge(
    result.head(), left_index=True, right_index=True, suffixes=("", "_カウント")
)
# 州記号を州ごとの平均年収を採用してみる。
df["住所（州）"] = result["住所（州）"]
# 結果を保存する。
df.to_csv("LendingClub50000_ave_income_state.csv", index=False)
# ダウンロードしたら、DataRobotでもう一度予測してみよう
files.download("LendingClub50000_ave_income_state.csv")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.17433213990636187, 'fraction_numerical': 0.011842467639768659, 'mean_word_length': 8.409326424870466, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '228436', 'license': 'MIT', 'n_tokens_mistral': 1897, 'n_tokens_neox': 1856, 'n_words': 169, 'path': 'Feature_Engineering_Categorical.ipynb', 'repo_name': 'ironerumi/fe_workshop', 'size': '3631'}