---
# System prepended metadata

title: 特徵工程

---

# 特徵工程

## 數值型資料
數值型資料的第一種檢查 : 
- 大小是否重要?
- 是否只需要知道他是正的還是負的?
- 或許只需要知道粗略的大小?

接著，考慮特徵的尺度 :
- 最大值和最小值是多少?

## 處理計數型資料
- 二元化 : 大於某一個數值定為1，小於某一個數值定為0
- 分箱 : 例如，利用年齡層來分組，
    - 0-9歲分到第一組，10-19歲分到第二組，以此類推
    - 另外也可以客製化年齡範圍，0-12歲、12-18歲、18-22歲等等
```
import numpy as np
np.floor_divide(你的list，10) # 取list裡面每個數字除以10過後的整數部分
```

- 數字跨越好幾個數量級，適合用10的次方進行分組，0-9、10-99、100-999、...
    
```
import numpy as np
# np.floor : 高斯符號[]，無條件捨去小數部分
# np.log10()，np.log()，np.log2 : 以10、e、2為底
np.floor(np.log10(你的list))
```
- 如果計數存在很大差距，導致許多箱子裡面沒有資料，則可以用"分位數分箱"，將資料分成 0.25 0.5 0.75 四等分。
```
import pandas as pd
pd.qcut(你的list，4，labels=False)
```
- Box-Cox 轉換
```
from scipy import stats
rc_bc,bc_params = stats.boxcox(你的list)
bc_params
```
## 特徵縮放
``` import sklearn.preprocseeing as preproc```
1. 最大最小縮放 ```preproc.minmax_scale(list)```
2. 標準化 ``` preproc.StandardScaler().fit_transform(list)```
3. 長度正規化 ```np.linalg.norm(list)```、```preproc.normalize(list,axis=0)```

## 交互作用(interaction term)
``` 
from sklearn.preprocessing import PolynomialFeatures
interaction = PolynomialFeatures(degree=2, interaction_only=False, include_bias=False)
X_inter=pd.DataFrame(interaction.fit_transform(X_train),columns=interaction.get_feature_names(input_features=X_train.columns))
``` 
## 類別變數編碼
- 一位有效編碼(one-hot encoding) : K種類別，K個變數，每個變數設為1，其他為0。

| One-hot encoding       | e1 | e2 | e3 |
| ------- | -- | -- | -- |
| Label 1 | 1  | 0  | 0  |
| Label 2 | 0  | 1  | 0  |
| Label 3 | 0  | 0  | 1  |
- 啞變數(dummy variables) : K種類別，K-1個變數設為1，剩餘1個變數做為參考變數全為0。

| Dummy variables       | e1 | e2 |
| ------- | -- | -- |
| Label 1 | 1  | 0  |
| Label 2 | 0  | 1  |
| Label 3 | 0  | 0  |
- 效果編碼(effect encoding) : 其中一種類別全部是 -1，其他都是設為1。

| Dummy variables       | e1 | e2 |
| ------- | -- | -- |
| Label 1 | 1  | 0  |
| Label 2 | 0  | 1  |
| Label 3 | -1  | -1  |

## 巨大類別變數
解決方法 : 
1. 使用簡單模型(Logistic、SVM)，搭配one-hot encoding.
2. 壓縮特徵
    (a). 特徵雜湊 
        a. 優點 : 計算上節省記憶體空間
        b. 缺點 : 雜湊後的特徵不可解讀
    (b). 分箱計數 : 使用條件機率(https://medium.com/@aks.chikara/machine-learning-handle-large-categorical-column-bin-counting-approach-cb9fadaf4e96)
        1. 計算 odds ratio
        2. 計算 log odds ratio
        3. 遇到罕見類別，使用**退縮法back-off**，將罕見類別的計數集中在一個特殊箱子，如果計數大於一個門檻值，那個類別才能獲得屬於他自己的計數統計，否則就使用來自退縮箱的統計資訊。
3. 防範資料洩漏
    實務上，加上一個Laplace(0,1)的小隨機雜訊，就可以掩蓋任何單一資料點引起的潛在洩漏。
4. 無範圍計數
    分箱計數無法處理線上學習，one-hot encoding 和 特徵雜湊可以處理線上學習。

## PCA
```
from sklearn.decomposition import PCA
pca_transformer = PCA(n_components=0.8) # 涵蓋80%總變異數
pca_X_train = pca_transformer.fit_transform(data)
pca_transformer.explained_variance_ratio_
```
PCA捨棄資訊，因此降維後的模型訓練較省時，但比較不準確。
PCA常用在time series的異常偵測，建立財務模型(Factor Analysis)

## ZCA
PCA的進一步白化，但ZCA沒有縮減維度。
ZCA常用於影像學習，相鄰的像素常常有相似的顏色，ZCA白化可以移除這種相關。

## 透過 K-means 進行非線性特徵化
(https://github.com/alicezheng/feature-engineering-book/blob/master/07.03-05_K-means_featurization.ipynb)
``` 
from sklearn.cluster import KMeans 
class KMeansFeaturizer:
    ``` 將每一個資料點轉換成最靠近的群集的ID```
    def __init__(self, k=100, target_scale=5.0, random_state=None):
        self.k = k
        self.target_scale = target_scale
        self.random_state = random_state
        self.cluster_encoder = OneHotEncoder().fit(np.array(range(k)).reshape(-1,1))
        
    def fit(self, X, y=None):
        if y is None:
            # No target variable, just do plain k-means
            km_model = KMeans(n_clusters=self.k, 
                              n_init=20, 
                              random_state=self.random_state)
            km_model.fit(X)

            self.km_model_ = km_model
            self.cluster_centers_ = km_model.cluster_centers_
            return self

        # There is target information. Apply appropriate scaling and include
        # into input data to k-means            
        data_with_target = np.hstack((X, y[:,np.newaxis]*self.target_scale))

        # Build a pre-training k-means model on data and target
        km_model_pretrain = KMeans(n_clusters=self.k, 
                                   n_init=20, 
                                   random_state=self.random_state)
        km_model_pretrain.fit(data_with_target)

        # Run k-means a second time to get the clusters in the original space
        # without target info. Initialize using centroids found in pre-training.
        # Go through a single iteration of cluster assignment and centroid 
        # recomputation.
        km_model = KMeans(n_clusters=self.k, 
                          init=km_model_pretrain.cluster_centers_[:,:2], 
                          n_init=1, 
                          max_iter=1)
        km_model.fit(X)
        
        self.km_model = km_model
        self.cluster_centers_ = km_model.cluster_centers_
        return self
        
    def transform(self, X, y=None):
        clusters = self.km_model.predict(X)
        return self.cluster_encoder.transform(clusters.reshape(-1,1))
    
    def fit_transform(self, X, y=None):
        """Runs fit followed by transform.
        """
        self.fit(X, y)
        return self.transform(X, y)
```
```
# k: integer, optional, default 100, the number of clusters to group data into.
kmf_hint = KMeansFeaturizer(k=optional,target_scale=10)
```
透過K-means得到的分類結果，得到群集特徵後，放入LR，可以使模型表現變好

