写在前面:第一次参加数据类竞赛,从第一次接触pandas到数据预处理,特征工程的构建,再到最终的模型融合,B榜成绩0.767,排名三百左右,做个简单的赛后总结。
赛题介绍:
主办方提供了(1)用户的标签数据(2)过去60天的交易行为(3)过去30天的APP行为数据,分成训练集和测试集合,最终需要预测用户违约的概率。评分标准为AUC。
以下分五个方面介绍流程:
一,EDA(探索性数据分析)
1.查看每个特征的取值范围以及不同取值的个数:
Train_data = pd.read_csv(path)
for col in Train_data.columns:
print(col)
#查看可能的取值
print(Train_data[col].unique())
#查看不同取值的个数
print(Train_data[col].value_counts)
2.查看拥有交易信息表和APP行为信息表的用户个数:
print(Train_data.groupby('id').count())
3.查看缺失值存在较多的用户情况:
print(Train_data.loc[(Train_data['atdd_type']=='\\N') & (Train_data['tot_ast_lvl_cd'] == '\\N')])
额外补充一点,pandas的.loc[:,:]函数用处很大,能提取行列值表达。
通过以上的分析,大概知道
1)有80%的用户有交易记录,仅有25% 的用户有APP行为数据。
2)有500个用户缺失值很多,可能是主办方刻意挖去数据。
该分析方法的缺点:缺少可视化,以后打比赛要增加可视化方面的直观感觉。
二,数据预处理
1,转化类型:
数据多数为object类型:
1)简单判断哪些可以转化为数字
2)根据类别的特征来转化对应类型
3)将缺失值‘\N’表示为np.nan(缺失值)
def tag_feature_pre(self,df, categorical_features, int_features, yuanben):
#将‘\\n’用缺失值表示
for col in yuanben:
df[col] = df[col].apply(lambda x: np.nan if x == '\\N' else x)
#对类型进行转码
for col in categorical_features:
df[col] = df[col].astype('category').cat.codes
#转成int类型
for col in int_features:
df[col] = df[col].apply(lambda x: int(x) if x != '\\N' else np.nan)
return df
当然也试过对类型进行onehot编码和labelencoder编码,但是效果都不佳,这里提供代码:
1)labelencoder:
from sklearn import preprocessing
def type_labelencoder(self,df):
# Trx_Cod1_Cd和Trx_Cod2_Cd保持类别编码
df = df.sort_values(['id', 'trx_tm']).reset_index(drop=True)
df['Trx_Cod1_Cd'] = df['Trx_Cod1_Cd'].fillna('-1')
#先调用函数
Trx_Cod1_Cd_le = preprocessing.LabelEncoder()
#这个过程其实是生成字典
Trx_Cod1_Cd_le = Trx_Cod1_Cd_le.fit(df.Trx_Cod1_Cd.values)
Trx_Cod2_Cd_le = preprocessing.LabelEncoder()
Trx_Cod2_Cd_le = Trx_Cod2_Cd_le.fit(df.Trx_Cod2_Cd.values)
#转化值
df['Trx_Cod1_Cd'] = Trx_Cod1_Cd_le.transform(df['Trx_Cod1_Cd'].values)
df['Trx_Cod2_Cd'] = Trx_Cod2_Cd_le.transform(df['Trx_Cod2_Cd'].values)
2)onehotlable编码:
df_pro = pd.get_dummies(df, prefix_sep="_", columns=df.columns[:-1])
也做过通过众数,平均数来填充空值的尝试,但由于xgb和lgb等有些树模型本身可以处理空值,所以就没管。
总结下:对数据类型进行了处理
三,特征工程
分三个部分讲:
一)用户tag标签数据:
将类别做分桶处理:
#对年龄段做分桶
bin = [i * 10 for i in range(1, 10)]
X_data['age_bin'] = pd.cut(X_data['age'], bin, labels=False)
对其他类别做各种交叉组合,然后根据模型的重要度进行排查。
最后发现:tag实在挖不倒什么有用特征,怎么做auc一直保持在0.68到0.70之间
二)trade用户交易表
后来看到高分的方案,才知道这是决定比赛上限的数据。
以下是自己做的特征工程,其实具体也是参考去年第一的方案来的:
2019招商银行FintechTop1方案
分别对Trx_Cod1_Cd和Trx_Cod2_cd交易类别来做的特征分析:
1.包括用户对这些类别:
1)交易最多的种类
2)对这些种类交易次数占据的比例,
3)对这些种类交易金额占据的比例
4)对这些种类不同的金额求和
2.对于时间trx_tm和金额cny_trx_amt所做的:
1)用户总交易额的均值、方差、最大最小值等数字统计
2)用户交易的第一天和最后一天
做到这里几乎都是照搬去年的方案:
我以为这里已经不能再继续深挖了,(但其实这就是为啥最终无法继续往前的原因)
赛后请教了高分的同学:(充分利用了时间金额的统计特性)
1)分了每周的统计金额,金额之和,金额平均
2)分了月份之间的差异,收支是否平衡,有比值有差值
还有同学:
1)粗粒度刻画:最后一次交易的时间是否晚于平均值,交易总次数,多少天有交易行为,多少天有交易行为,平均每天的交易金额,平均每次的交易金额等。
等等,该同学借鉴了RFM模型的思想。
三)用户APP上行为
由于此项数据仅有25%的用户拥有,加不加这里的特征对我最终结果无太大影响,所以没有加。
四)特征选择
特征选择用了两种方法:
1.xgboost模型自带输出特征的重要度,剔除重要度为1的特征
from lightgbm import LGBMRegressor,plot_importance
import matplotlib.pylab as plt
x_train,x_val,y_train,y_val = train_test_split(X_data,Y_data,test_size=0.3)
model = LGBMRegressor(num_leaves=127,n_estimators=150,learning_rate=0.01)
model = model.fit(x_train,y_train)
predict = model.predict(x_val)
roc = roc_auc_score(y_val,predict)
print('roc:',roc)
#画出模型前25重要的特征
plt.figure(figsize=(18,40))
plot_importance(model, max_num_features=25)
plt.title("Featurertances")
plt.show()
#输出每个特征的重要度
print(model.feature_importances_)
2.利用mlxtend模块种的SFS和SBS进行特征选择
mlxtend官方文档
from mlxtend.feature_selection import SequentialFeatureSelector as SFS
sfs1 = SFS(knn,
k_features=3,
forward=True,
floating=False,
verbose=2,
scoring='accuracy',
cv=0)
sfs1 = sfs1.fit(X, y)
利用贪心算法来跑,缺点就是速度非常慢,用实验室电脑跑的,最终效果还行。不建议用在更大型的数据处理中。
五)模型的选择和融合
模型采用xgb和lgb模型,用stacking做了模型融合:
heamy的官方文档
stacking对模型还是有微小的提升,虽然只有一点点,但是打比赛目的就是为了提分。
放个stacking的简单使用说明
# load boston dataset from sklearn
from sklearn.datasets import load_boston
data = load_boston()
X, y = data['data'], data['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=111)
# create dataset
dataset = Dataset(X_train,y_train,X_test)
# initialize RandomForest & LinearRegression
model_rf = Regressor(dataset=dataset, estimator=RandomForestRegressor, parameters={
'n_estimators': 50},name='rf')
model_lr = Regressor(dataset=dataset, estimator=LinearRegression, parameters={
'normalize': True},name='lr')
# Stack two models
# Returns new dataset with out-of-fold predictions
pipeline = ModelsPipeline(model_rf,model_lr)
stack_ds = pipeline.stack(k=10,seed=111)
# Train LinearRegression on stacked data (second stage)
stacker = Regressor(dataset=stack_ds, estimator=LinearRegression)
results = stacker.predict()
# Validate results using 10 fold cross-validation
results = stacker.validate(k=10,scorer=mean_absolute_error)
总结:
第一次参加数据挖掘的竞赛,觉得最重要的还是特征工程,能挖到更好的特征就意味着训练出更好的结果。本人在trade类别特征上并未有很多思考,一直在思考和实验tag型特征,也算是一次教训,不能投机蹊跷和思想上投篮,也希望下个比赛自己能做的更好。