scikit-learnで単変量特徴量選択をやってみた

[特徴量の選択方法についてまとめてみました](https://qiita.com/insilicomab/items/cf67ffe1232feaab8d37)

特徴量の選択法の一つとして、単変量特徴量選択（univariate feature selection）という手法があり、scikit-learnの[SelectKBest](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html)と[SelectPercentile](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectPercentile.html)で実装が可能である。

[Kaggle Grandmasterに学ぶ 機械学習 実践アプローチ](https://book.mynavi.jp/ec/products/detail/id=123641)ではこれら2つの手法を組み込んだラッパークラスが紹介されており、使ってみた。

```python
# 単変量特徴量選択（univariate feature selection）のラッパークラスを作成
class UnivariateFeatureSelection:
    
    def __init__(self, n_features, problem_type, scoring):
        
        if problem_type == 'classification':
            valid_scoring = {
                'f_classif': f_classif,
                'chi2': chi2,
                'mutual_info_classif': mutual_info_classif
            }
        else:
            valid_scoring = {
                'f_regression': f_regression,
                'mutual_info_regression': mutual_info_regression
            }
        
        # 手法が対応していない場合の例外の発生
        if scoring not in valid_scoring:
            raise Exception('Invalid scoring function')
        
        if isinstance(n_features, int):
            self.selection = SelectKBest(
                valid_scoring[scoring],
                k=n_features
            )
        elif isinstance(n_features, float):
            self.selection = SelectPercentile(
                valid_scoring[scoring],
                percentile=int(n_features * 100)
            )
        else:
            raise Exception('Invalid type of feature')

    # fit関数
    def fit(self, X, y):
        return self.selection.fit(X, y)
    
    # transform関数
    def transform(self, X):
        return self.selection.transform(X)
    
    # fit_transform関数
    def fit_transform(self, X, y):
        return self.selection.fit_transform(X, y)
```

パラメータ
**n_features**：float型の場合は**SelectPercentile**、それ以外のときは**SelectKBest**を利用。

**problem_type**：分類（`'classification'`）or 回帰（`'regression'`）を文字列で。

**scoring**：
`problem_type`が`classification`の場合、`'f_classif'`（ANOVA F値）、`'chi2'`（カイ二乗値）、`'mutual_info_classif'`（相互情報量）のいずれかを文字列で。
`problem_type`が`regression`の場合、`'f_regression'`（回帰分析 F値）、`'mutual_info_regression'`（相互情報量）

# ボストンデータセットで試してみた

```python
# Bonstonデータセット
boston = load_boston()
X = boston.data
y = boston.target
```

ボストンデータセットの目的変数は住宅価格なので回帰分析（regression）。`scoring`は`f_regression`をせんたくしてみた。`'n_features'`は4とし、特徴量を4つ選択した。

```python
ufs = UnivariateFeatureSelection(n_features=4,
                                 problem_type='regression',
                                 scoring='f_regression'
)
ufs.fit(X, y) # X = boston.data, y = boston.target
X_transformed = ufs.transform(X)
mask = ufs.selection.get_support() # 各特徴量を選択した（True）、していない（False）

# 結果の確認（input: 特徴量、selected: 特徴量として選択されたかどうか）
selection_result = pd.DataFrame({'input': boston.feature_names, 'selected': mask})
print(selection_result)
```

```terminal
      input  selected
0      CRIM     False
1        ZN     False
2     INDUS      True
3      CHAS     False
4       NOX     False
5        RM      True
6       AGE     False
7       DIS     False
8       RAD     False
9       TAX     False
10  PTRATIO      True
11        B     False
12    LSTAT      True
```

今回のケースではINDUS（非小売業の割合）、RM（部屋数）、PTRATIO（生徒と先生の比率）、LSTAT（低所得者人口の割合）が選択された。