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