特徴量作成を楽にするライブラリいくつかまとめて試す②xfeat
この記事はなにか
機械学習の特徴量を作るときに色々とめんどくさい部分を楽にできるライブラリの紹介。
具体的には以下を紹介する、
- featuretools
- xfeat
①のfeaturetoolsの記事は以下。脳死で既存特徴量の四則演算や集約などを一括で楽に作れる。
今回は脳死ではなくある程度考えつつ特徴量を作りたいときに利用するxfeatを紹介する。
何ができるか
主な機能は以下
- 特徴量の変換
- 特徴量の加工
- GBDTの変数重要度を用いた特徴量選択(+optunaにより最適化)
となる。
featuretoolsも似た感じだったが、あっちは指定した加工ができそうな特徴量に対しては脳死で全特徴量の加工を一気に作成できた。xfeatは任意で指定した特徴量に対して楽に加工ができる、という使い分けかな?あとxfeatではカテゴリカル変数に対しての変換が楽にできる。
ちなみに、pandasではなくcuDF((pandasより高速のデータフレームを提供するライブラリっぽい。))という形式にも対応していて、そっちを使った方が爆速になるとのことだが今回は使わない。
なお、今回データとしてみんな大好きtitanicデータを使った以下の記事のコードをお借りする(一部変更してます)。
特徴量の変換
sklearn.piplineのように、Pipelineを使ってencode処理をつなげたencoder群を作り、それをfit_transformで適用していく。
その際のencoder objectとして以下を提供している。
Categorical encoder:
- xfeat.SelectCategorical
- xfeat.LabelEncoder
- xfeat.ConcatCombination
- xfeat.TargetEncoder
- xfeat.CountEncoder
- xfeat.UserDefinedLabelEncoder.
Numerical encoder:
- xfeat.SelectNumerical
- xfeat.ArithmeticCombination.
User-defined encoder:
- xfeat.LambdaEncoder
例えば、以下のようにすると、SelectCategorical→ConcatCombination→TargetEncoderという順で処理するencoderをdfに対して適用、という意味になる。
from xfeat import Pipeline, SelectCategorical, ConcatCombination, TargetEncoder encoder = Pipeline( [ SelectCategorical(), ConcatCombination(), TargetEncoder(), ] ) df_encoded = encoder.fit_transform(df)
特定型の特徴量のDFを作る
普通にやると以下のように①dtypesをみて手打ちでがんばる②指定した型の列listを作る、のいずれかで地味にめんどい。
train_df.dtypes # => 型一覧 categorical_features = [col for col in train_df.columns if train_df[col].dtype == 'object'] train_df[categorical_features].head()

xfeatでは以下のようにかける。
カテゴリカルデータ列のみのDFを作成
# カテゴリカルデータのみ from xfeat import SelectCategorical encoder = SelectCategorical() encoder.fit_transform(train_df).head() # 以下でも同じ # SelectCategorical().fit_transform(train_df).head()
数値データ列のみのDFを作成
# 数値データのみ from xfeat import SelectNumerical encoder = SelectNumerical() encoder.fit_transform(train_df).head() # 以下でも同じ # SelectNumerical().fit_transform(train_df).head()

特徴量の加工
カテゴリカル変数
機械学習ではカテゴリカル変数はそのままの文字列値で渡すことはできないので、なにかしらの変換が必要となる。
sklearn.preprocessingで変換処理はできるが、np.arrayで返ってくるのでDataFrameで色々やっていると対応がちぐはぐでめんどかったりするがxfeatではよしなにやってくれる。
ちなみに、Category Encodersというライブラリのも便利らしい。xfeatではOne-hot-Encodingみたいに多次元展開するものはなく、1次元変換のエンコーディングのみっぽいので他のも使いたい場合はCategory Encodersかな?
Label Encoding
from xfeat import Pipeline, SelectCategorical, LabelEncoder encoder = Pipeline([ SelectCategorical(exclude_cols=["Name", "Ticket"]), # Name,Ticketを除くカテゴリカル列のDFを作成 LabelEncoder(output_suffix=""), # 渡されたDFをlabel encodeする ]) encoded_df = encoder.fit_transform(train_df) encoded_df.head()

Count Encoding
from xfeat import CountEncoder # count encode from xfeat import Pipeline, SelectCategorical, LabelEncoder encoder = Pipeline([ SelectCategorical(exclude_cols=["Name", "Ticket"]), # Name,Ticketを除くカテゴリカル列のDFを作成 CountEncoder(output_suffix=""), # 渡されたDFをcount encodeする ]) encoded_df = encoder.fit_transform(train_df) encoded_df.head()

Target Encoding
# target encoding # 以下のHoldout TS # https://blog.amedama.jp/entry/target-mean-encoding-types from sklearn.model_selection import KFold from xfeat import TargetEncoder fold = KFold(n_splits=5, shuffle=False) encoder = TargetEncoder( input_cols=["Cabin"], # 変換対象 target_col="Survived", # target fold=fold, # foldの取り方 output_suffix="_re" # 変換後のsuffix ) encoded_df = encoder.fit_transform(train_df) encoded_df[["Survived", "Cabin", "Cabin_re"]].head()

列組み合わせ(文字列)
また、カテゴリカル変数同士を組み合わせて新たな特徴量を作成することもできる。
# 2変数の組み合わせ from xfeat import SelectCategorical, ConcatCombination, Pipeline encoder = Pipeline([ SelectCategorical(exclude_cols=["Ticket", "Name"]), ConcatCombination( # drop_origin=True, output_suffix="_combine", r=2), # 組み合わせは3つ ]) encoder.fit_transform(train_df).head()

# 3変数の組み合わせ from xfeat import SelectCategorical, ConcatCombination, Pipeline encoder = Pipeline([ SelectCategorical(exclude_cols=["Ticket", "Name"]), ConcatCombination( # drop_origin=True, output_suffix="_combine", r=3), # 組み合わせは3つ ]) encoder.fit_transform(train_df).head()

数値変数
集約関数
featuretoolsと同様に集約関数の適用ができる。ただし、こちらでは指定した列に対して適用する。以下ではAgeとPclass列それぞれをSex単位で集約している。
from xfeat import aggregation aggregated_df, aggregated_cols = aggregation(train_df, group_key="Sex", # group key group_values=["Age", "Pclass"], # 集約対象 agg_methods=["mean", "max"], # 集約関数 ) display(aggregated_cols, aggregated_df.head())

ちなみに、これはライブラリを使わない場合は以下で面倒
aggregated_df = train_df.copy() # 性別ごとの年齢の平均値を特徴量に追加 sex_mean_df = train_df.groupby('Sex')['Age'].mean() aggregated_df.loc[aggregated_df['Sex'] == 'female', 'agg_mean_Age_grpby_Sex'] = sex_mean_df['female'] aggregated_df.loc[aggregated_df['Sex'] == 'male', 'agg_mean_Age_grpby_Sex'] = sex_mean_df['male'] # 他の結合はagg特徴量は略 aggregated_df.head()
また、agg_methodsで自作関数を使うことはできるが処理後の列名がおかしくなるので少し工夫が必要とのこと。
列組み合わせ(数値)
これもfeaturetoolsにもあるが、こちらでは指定した列に対して適用する。
# 2変数の組み合わせ from xfeat import Pipeline, ArithmeticCombinations, SelectNumerical encoder = Pipeline([ SelectNumerical(), ArithmeticCombinations( # 兄弟/配偶者(SibSp)、両親/子供の数(Parch)を加算した特徴量を作成する input_cols=["SibSp", "Parch"], #drop_origin=True, output_suffix="_combine", operator="+", # 加算 r=2), ]) encoder.fit_transform(train_df).head()
Lambda処理
処理に対してLambda関数を書くことで自作の処理ができる。なお、この際のxは処理対象となるDFの全列(Pipelineの場合は直前までの処理結果)となるが、input_colsを使うと特定列のみが対象となる。
例えば、先程の列組み合わせ(数値)に対して更に四捨五入する処理を追加する。
# 2変数の組み合わせ + lambda処理 from xfeat import Pipeline, ArithmeticCombinations, SelectNumerical, LambdaEncoder encoder = Pipeline([ SelectNumerical(), ArithmeticCombinations( # 兄弟/配偶者、両親/子供の数を加算した特徴量を作成する input_cols=["Age", "Parch"], #drop_origin=True, output_suffix="_combine", operator="+", # 加算 r=2), LambdaEncoder( lambda x: round(float(x), -1), # 1の位で四捨五入 input_cols=["Age", "Fare"], output_suffix="_round", drop_origin=False,) ]) encoder.fit_transform(train_df).head()

もちろん、lambda処理なので文字列に対しても可能。