make_column_selectorで型・正規表現からカラムのサブセットを選択する

2021.09.06

データアナリティクス事業本部の鈴木です。

今回は、sklearn.composeモジュールから、ColumnTransformerで扱うカラムを選択するのに便利なmake_column_selectorを紹介します。

make_column_selectorとは

make_column_selectorは、カラムのデータ型や正規表現で指定したカラム名に基づいてカラムを選択する、呼び出し可能なインスタンスを作ります。カラム名は複数条件指定した場合は全ての条件に合うカラムを取得します。

sklearn.compose.make_column_selector — scikit-learn 0.24.2 documentation

ColumnTransformerではtransformerを適用するカラムの指定方法として、カラムが何番目かを表す整数のリストや、DataFrameであればカラム名のリストを渡すことができますが、make_column_selectorを使うと、より柔軟な指定を行うことができます。

準備

検証した環境

  • コンテナ:jupyter/datascience-notebook
  • scikit-learn:0.24.2

データの作成

今回は、sklearn.datasetsモジュールのアイリスデータセットを使います。アイリスデータセットは言わずと知れた有名なデータセットで、3種のアヤメの情報が合計150サンプル分入っています。

make_column_selectorの入力とするため、アイリスデータのデータフレームを作成します。

import pandas as pd
from sklearn import datasets

# アヤメのデータをsklearnから読み出す。
iris = datasets.load_iris()

# pandasデータフレームに変換する。
# カラム名にスペースが入っていたので、扱いやすい名前に変えた。
feature_names = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
df_iris = pd.DataFrame(iris["data"], columns=feature_names)

# 各行が何の種類なのか取り出す。
df_iris['species'] = iris.target_names[iris["target"]]

make_column_selectorでは、カラムのデータ型や名前を使うため、これらを確認しておきます。

df_iris.info()

## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 150 entries, 0 to 149
## Data columns (total 5 columns):
##  #   Column        Non-Null Count  Dtype  
## ---  ------        --------------  -----  
##  0   sepal_length  150 non-null    float64
##  1   sepal_width   150 non-null    float64
##  2   petal_length  150 non-null    float64
##  3   petal_width   150 non-null    float64
##  4   species       150 non-null    object 
## dtypes: float64(4), object(1)
## memory usage: 6.0+ KB

やってみる

型を指定する

dtype_include引数で、指定した型のカラムを指定することができます。

from sklearn.compose import ColumnTransformer
from sklearn.compose import make_column_selector
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler


ct = ColumnTransformer(
          transformers=[
           ('num', StandardScaler(), make_column_selector(dtype_include=np.number)),
           ('cat', OneHotEncoder(), make_column_selector(dtype_include=object))
      ])

ct.fit_transform(df_iris)  
## array([[-0.90068117,  1.01900435, -1.34022653, ...,  1.        ,
##       0.        ,  0.        ],
## ...

また、dtype_exclude引数で、指定した型を除いたカラムを指定することも可能です。

ct = ColumnTransformer(
          transformers=[
           ('num', StandardScaler(), make_column_selector(dtype_exclude=object)),
           ('cat', OneHotEncoder(), make_column_selector(dtype_include=object))
      ])

ct.fit_transform(df_iris)  
## array([[-0.90068117,  1.01900435, -1.34022653, ...,  1.        ,
##       0.        ,  0.        ],
## ...

名前を指定する

pattern引数に正規表現パターンを渡すと、そのパターンにマッチしたカラムを選択できます。

ct = ColumnTransformer(
          transformers=[
           ('sepal', 
            StandardScaler(), 
            make_column_selector(pattern="sepal_*")
           )
      ])

ct.fit_transform(df_iris)  
## array([[-0.90068117,  1.01900435],
## ...

パイプラインに組み込む

パイプライン内で利用してみます。ColumnTransformerをパイプラインで利用する例は、以前に書いたこちらの記事もご参照ください。

from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier

# パイプラインの作成
ct = ColumnTransformer(
          transformers=[
           ('num', StandardScaler(), make_column_selector(dtype_include=np.number)),
           ('cat', OneHotEncoder(), make_column_selector(dtype_include=object))
      ])
pipe = Pipeline([("column_transformer", ct),  
                 ("Classifier", HistGradientBoostingClassifier())])

作成したパイプラインをダイアグラムでも確認しておきましょう。

from sklearn import set_config
set_config(display='diagram')   
pipe

構築したパイプライン

参考までにfitscoreを実行し、学習と推論ができることを確認しておきます。

from sklearn.model_selection import train_test_split

# 訓練・テストデータの作成
X = df_iris.drop("species", axis=1)
y = df_iris["species"]
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

# 学習
pipe.fit(X_train, y_train)

# テストデータに対するaccuracyを計算する。
pipe.score(X_test, y_test)
## 1.0

make_column_selectorが使えない場合

make_column_selectorの__call__ではDataFrameを受け取るように作られており、NumPy配列のようなDataFrame以外のものを渡すとエラーになります。

ct = ColumnTransformer(
          transformers=[
           ('num', StandardScaler(), make_column_selector(dtype_include=np.number)),
           ('cat', OneHotEncoder(), make_column_selector(dtype_include=object))
      ])
ct.fit_transform(df_iris.values) 
## ---------------------------------------------------------------------------
## ValueError 
## ...
## ValueError: make_column_selector can only be applied to pandas dataframes

そのため、機械学習パイプライン内では、例えばDictVectorizerなどDataFrameではないインスタンスを返すtransformerの後には、直接置けないことに注意が必要です。

最後に

make_column_selectorを使って、型や正規表現からColumnTransformerで扱うカラムのサブセットを選択する方法を紹介しました。

ColumnTransformerを使って処理を適用したいカラムのサブセットは、データ型やカラム名で分けられることが多いので、カラム名のリストやカラムの位置のリストを使って指定する代わりに、make_column_selectorが活躍するケースも多そうですね。