在开始训练模型之前,我们需要对数据进行特征工程的操作,一个好的数据质量决定预测结果的上限,而好的模型只是无限逼近这个上限。
一个好的数据质量,应该满足“完全合一”
- 完整性:数据是否存在空值,字段feature是否完善,是否有漏掉
- 全面性:观察某一列的全部数值和特征值,是否存在单位、字段名与数值不匹配
- 合法性:数据的类型、内容、大小的合法性
- 唯一性:数据是否存在重复记录
以下是特征工程操作的一些常见步骤:
-
建议将训练集和测试集concat到一起,方便后续操作。
-
数据探索,观测数据与label之间的关系、数据的缺失值,异常值,数据分布等。
data.info() #查看索引、数据类型和内存信息 data.describe() #查看数值型列的汇总统计,返回计数、均值、标准差、最小最大值、25%50%75%分位数,percentiles0.05,0.95分位数 data.unique() #快速查看数据列有哪些分类内容,类似groupby data.value_counts() #查看数据唯一值和计数 df.corr() #返回行与列之间的相关系数,但是如果特征字段是object类型的,系统会略过识别不出来 data.iloc[:] #数据索引切片 data.loc[:] #数据索引名称切片
-
对分类特征(文本特征等)、Label进行特征值编码 ==》OneHot Encoder / Label Encoder
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() data[feature] = le.fit_transform(data[[feature]]) #当对训练集进行fit_transform之后,对测试集只需要transform处理 from sklearn.preprocessing import OneHotEncoder enc = OneHotEncoder() res = enc.fit_transform(data[[feature]]).toarray() #此处编码结果形式有些奇怪,最好将其转换为二维数组 enc_feature = enc.get_feature_names() #获取onehot后的特征名称 #取出编码后的结果和名称构造到data中 for index,value in enumerate(enc_feature): data[value] = res[:,index]
-
去除对结果影响不大的特征,或者噪点特征
data.drop(columns = [useless_feature])
-
除了树模型,在其他模型训练之前,需要对数据进行归一化 / 标准化处理
from sklearn.preprocessing import StandardScaler #标准化处理 stander = StandardScaler() X = stander.fit_transform(X)
-
划分训练集和测试集
#1.简单数据划分-简单快捷 from sklearn.model_selection import train_test_split X_train,X_test,y_val,y_val = train_test_split(X,y,test_size = 0.3,random_state = 2021) #2.Kfold K折交叉验证-结果稳定,不会忽高忽低 from sklearn.model_selection import KFold from sklearn.metrics import accuracy_score kf = KFold(n_split = '折的次数',random_state = ,shuffle = ) score_list = [] for train_index,val_index in kf.split(X): X_train,X_val = X[train_index],X[val_index] y_train,y_val = y[train_index],y[vall_index] model.fit(X_train,y_train) #每折叠划分一次,训练一次模型 y_val_pred = model.predict(X_val) #每训练一次模型,做一次验证集的预测 score = accuracy_score(y_val,y_val_pred) #每预测一次算一次分数 score_list.append(score) #将每次预测分数放到一起 res = np.mean(score_list) #将所有预测的分数做一次平均,得到最终的分数
-
查看数据是否有缺失值或者异常值,用众数/平均数/中位数进行填充
- 对于数值特征 num_features,缺失值可以用中位数补全
- 对于类别特征 cat_features,缺失值可以用众数补全
data.isnull().sum() #检查数据是否存在空值 data.fillna(data[feature].median(),inplace=True) #填充缺失值-中位数 data.fillna(data[feature].mode()[0],inplace=True) #填充缺失值-众数 data.replace() #更改异常值
-
如果是回归问题,可以通过条形图画出类别型特征与label之间的关系,并尝试将类别型特征与label做聚合交叉,针对不同的特征对label统计其最大,最小,平均值等等
plt.bar(x,height,width) #画出特征与label的条形图,观察它们之间的关系 #划分出类别型特征 cat_columns = [c_1,c_2,c_3...] #遍历每个类别行特征,将其对应label的极大/小值、均值、std等等数据做个统计,并merge到data中作为新的特征 for col in cat_columns: t = data.groupby(col,as_index = False)[label].agg({col+'_count':'count',col+'_label_max':'max', col+'_label_median':'median', col+'_label_min':'min',col+'_label_sum':'sum',col+'_label_std':'std',col+'_label_mean':'mean'}) data = data.merge(t,on = col,how = 'left')
-
在匿名特征中,可以通过观察其与label之间的相关性,如果存在与label相关性很高的情况,可以将其两两之间做交叉组合
data.corr() #观察不同特征与label之间的相关性 ''' 通过高相关性的匿名特征之间做加减组合,构造新的匿名特征,查看效果 '''
-
对于时间特征,采用以下两种方法处理
#1.划分时间多尺度,构造函数,将时间数据划分为单独的年、月、日的独立特征 def func(x): #提取年月日 year = int(str(x)[:]) month = int(str(x)[:]) day = int(str(x)[:]) date = datetime(year,month,day) return date #2.将时间特征做时间差,构造新特征,一般时间差会与label存在一定线性关系 data['diff_time'] = data['time_1'] - data['time_2']
-
对于分类问题,不同类别特征,与label之间的关系,可以用sns.countplot()函数,以bar的形式展示每个类别的数量
import matplotlib.pyplot as plt plt.figure(figsize = (20,16)) sns.countplot(x = data['subGrade'],hue = data['isDefault'],ata = data)
- 在特征训练过程中,可以将预处理好的数据进行临时保存,在下次进行处理时可以直接调用,无需再跑一次程序,从而节省时间。
#数据保存为pickle文件
data.to_pickle('./')
调用,读取pickle文件
import pickle
with open('./') as file:
data = pickleload(file)
- 在查看dataframe过程中,可能会遇到column数据被折叠的现象(即中间列数据被省略号替代),可以用以下方法进行操作。
import pandas as pd
pd.set_option('max_columns,None)
- 在特征处理过程中,可以通过data.corr()指令查看特征之间得到相关性,如果特征之间的相关性很高>0.9(不包括label),应该考虑特征之间存在多重共线性,多重共线性会导致模型分析结果不稳定,使得本身是显著的特征变得不再显著。如果特征系数是正负相反,那么在计算过程会让两个特征的权重被抵消。