淺談自動特征構造工具Featuretools


簡介

特征工程在機器學習中具有重要意義,但是通過手動創造特征是一個緩慢且艱巨的過程。Python的特征工程庫featuretools可以幫助我們簡化這一過程。Featuretools是執行自動化特征工程的框架,有兩類特征構造的操作:聚合(aggregation)和 轉換(transform)。

官方文檔:https://docs.featuretools.com/en/stable/index.html
 

示例

版本說明

python 3.7.6
featuretools==0.13.4
scikit-learn==0.22.2.post1

首先,我們得先了解一下featuretools的3個基本組成

  • 實體集(EntitySet):把一個二維表看作一個實體,實體集是一個或多個二維表的集合
  • 特征基元(Feature Primitives):分為聚合和轉換兩類,相當於構造新特征的方法
  • 深度特征合成(DFS, Deep Feature Synthesis):根據實體集里的實體和特征基元創造新特征

單個數據表

max_depth=1

加載數據

from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
import featuretools as ft

dataset = load_iris()
X = dataset.data
y = dataset.target
iris_feature_names = dataset.feature_names

df = pd.DataFrame(X, columns=iris_feature_names)

用實體集表示數據集

import featuretools as ft
es = ft.EntitySet(id='single_dataframe')  # 用id標識實體集
# 增加一個數據框,命名為iris
es.entity_from_dataframe(entity_id='iris',         
             dataframe=df,
             index='index',
             make_index=True)

選擇特征基元並自動進行特征工程,我們這里采用加減乘除4個基元,max_depth控制“套娃”的深度,如果是1的話只在原特征上進行,大於1的話不僅會在原來的特征上,還會在其他基元生成的新特征上創造特征,數值越大,允許越深的“套娃”。

trans_primitives=['add_numeric', 'subtract_numeric', 'multiply_numeric', 'divide_numeric']  # 2列相加減乘除來生成新特征
# ft.list_primitives()  # 查看可使用的特征集元
feature_matrix, feature_names = ft.dfs(entityset=es, 
     target_entity='iris', 
     max_depth=1,    # max_depth=1,只在原特征上進行運算產生新特征
     verbose=1,
     trans_primitives=trans_primitives
)

我們知道加法和乘法滿足交換律,而減法和除法不滿足,以特征A和B為例,A+B的結果一定等於B+A,但是A-B不一定等於B-A。

按理說,不同基元操作后的總特征數:

加和乘的新特征數+原始特征數,feature_num*(feature_num-1)/2+feature_num,所以這里是4*3/2+4=10
減和除的新特征數+原始特征數,feature_num*(feature_num-1)+feature_num,所以這里是4*3+4=16
10*2+16*2-4*3=40,4*3減去重復的3原始特征3次

0.13.4版本的featuretools中,默認減法滿足交換律,因此實際生成的特征會少一些,只有34個特征。

下面是0.13.4版本的featuretools中的代碼,subtract_numeric默認開啟了交換律,我想因為 A-B = -(B-A) ,可以認為是一個特征,只不過一個是正相關一個是負相關。但是如果max_depth很深,差別會越來越大,如 A+B×(A-B) 和 A+B×(B-A) 

class SubtractNumeric(TransformPrimitive):
    name = "subtract_numeric"
    input_types = [Numeric, Numeric]
    return_type = Numeric

    def __init__(self, commutative=True):
        self.commutative = commutative
        ...

如果想要全部特征,可以創建一個subtract_numeric的實例,讓commutative參數為False,這時就會有40個特征了,這是預期的結果。

trans_primitives=['add_numeric', ft.primitives.SubtractNumeric(commutative=False), 'multiply_numeric', 'divide_numeric']
feature_matrix, feature_names = ft.dfs(entityset=es, 
     target_entity='iris', 
     max_depth=1, 
     verbose=1,
     trans_primitives=trans_primitives
)

自動特征工程后,可能會出現 np.inf 和 np.nan 這樣的異常值,我們需要處理這些異常數據。其中 np.inf 可能是由 n/0 導致的,np.nan 可能是由 0/0 導致的。

feature_matrix.replace([np.inf, -np.inf], np.nan)  # np.inf都用np.nan代替
feature_matrix.isnull().sum()

max_depth不為1

如果我們的max_depth不為1,要知道特征基元的順序是會帶來影響的。另外就是如果max_depth數值大,特征基元多的話特征工程后的維度會迅速膨脹。下面的兩個例子中,原來的特征只有4個,讓max_depth為2且只用2個特征基元后特征就有100+了。

先乘再除

feat_matrix, feat_names = ft.dfs(entityset=es, 
                     target_entity='iris', 
                     max_depth=2, 
                     verbose=1,
                     trans_primitives=['multiply_numeric', 'divide_numeric'],
)
# 乘法基元處理后特征數(包含原特征)一共有4*3/2+4=10個
# 除法基元會在乘法處理后的10個特征上,進行除法操作,所以這樣會有10*9+10=100個特征

先除再乘

feat_matrix, feat_names = ft.dfs(entityset=es, 
                     target_entity='iris', 
                     max_depth=2, 
                     verbose=1,
                     trans_primitives=['divide_numeric', 'multiply_numeric']
)
# 除法基元處理后特征數(包含原特征)一共有4*3+4=16個
# 同樣地,乘法在這16個特征上進行操作,會有16*15/2+16=136個特征

多個數據表

我們這里自定義2個數據表來表示。其中df_2中id就是df_1中的id

df_1 = pd.DataFrame({'id':[0,1,2,3], 'a':[1,2,2,3], 'b':[2,4,4,5]})
df_2 = pd.DataFrame({'id':[0,1,1,2,3], 'c':[1,3,3,2,5], 'd':[5,6,7,9,8]})

es = ft.EntitySet(id='double_dataframe')
es.entity_from_dataframe(entity_id='df_1',         # 增加一個數據框
             dataframe=df_1,
             index='id')
es.entity_from_dataframe(entity_id='df_2',         # 增加一個數據框
             dataframe=df_2,
             index='index',
             make_index=True)
# 通過 id 關聯 df_1 和 df_2 實體
relation = ft.Relationship(es['df_1']['id'], es['df_2']['id'])
es = es.add_relationship(relation)

聚合基元 sum 和 median 會將df_2中相同“id”的數據進行相加和取中位數的操作

trans_primitives=['add_numeric']
agg_primitives=['sum', 'median']
feature_matrix, feature_names = ft.dfs(entityset=es, 
                     target_entity='df_1', 
                     max_depth=1, 
                     verbose=1,
                     agg_primitives=agg_primitives,
                     trans_primitives=trans_primitives)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM