第一次正式参加数据挖掘类的比赛,投入了三个星期。结果没有进入复赛,但是学到了许多经验。感谢技术圈和github的大佬们提供的baseline,让我少走了很多弯路。
第一次写博客,其一为了防止以后忘记,其二如果萌新能帮到萌新新们也是很开心啦。
思路:全文按照数据预处理、特征工程和模型融合讲解,并附有代码。
数据预处理
1 导入库和读取数据
1.1导入库:
import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import log_loss
from sklearn import preprocessing
import warnings
from sklearn.grid_search import GridSearchCV
warnings.filterwarnings("ignore")
from sklearn.model_selection import StratifiedKFold
import time
from itertools import product
import copy
import itertools
import seaborn as sns
1.2读取测试集和训练集:
train是训练数据,test是需要预测的测试集(这里用的是B榜的test_b)。
分析数据时会发现训练集中有少量样本'instance_id'字段重复,但在比赛B榜的test_b中也有重复样本,所以不进行去重(被注释的那一行是去重操作)。
train = pd.read_csv(r'C:\Users\Lee\Desktop\round\origin_data\round1_ijcai_18_train_20180301.txt', sep="\s+")
test = pd.read_csv(r'C:\Users\Lee\Desktop\round\origin_data\round1_ijcai_18_test_b_20180418.txt', sep="\s+")
data = pd.concat([train, test])
#data = data.drop_duplicates(subset='instance_id')
data = data.reset_index(drop = True)
2 数据预处理
2.1文本类特征处理
数据有3个文本类型特征item_category_list、item_property_list和predict_category_property,使用split分割。
item_category_list特征有3个字段,第一个字段所有数据一样,所以取range(1,3)
item_property_list特征有100个字段,各字段之间无从属关系,由于后面字段缺失值太多,所以取range(19)
predict_category_property特征有14个字段,有从属关系,同样是后面字段缺失值太多取range(5)
缺失值如果过多,样本不足,在对该特征做进一步的统计时,很容易造成误判,例如在52万条样本中该字段只有100条样本,里面有取值为'A'的3条样本全部点击了广告。那么在预测时就很有可能直接判定取值为'A'的广告会被点击。
并使用LabelEncoding进行编码。
lbl = preprocessing.LabelEncoder()
for i in range(1, 3):
data['item_category_list' + str(i)] = lbl.fit_transform(data['item_category_list'].map(
lambda x: str(str(x).split(';')[i]) if len(str(x).split(';')) > i else '')) # item_category_list的第0列全部都一样
for i in range(19):
data['item_property_list' + str(i)] = lbl.fit_transform(data['item_property_list'].map(lambda x: str(str(x).split(';')[i]) if len(str(x).split(';')) > i else ''))
for i in range(7):
data['predict_category_property' + str(i)] = lbl.fit_transform(data['predict_category_property'].map(
lambda x: str(str(x).split(';')[i]) if len(str(x).split(';')) > i else ''))
2.2时间戳处理
数据中的时间是以时间戳(context_timestamp)形式存在,进行如下处理提取日期和时间
data['realtime'] = data['context_timestamp'].apply(lambda x:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(x)))
data['realtime'] = pd.to_datetime(data['realtime'])
data['day'] = data['realtime'].dt.day
data['hour'] = data['realtime'].dt.hour
3特征工程
3.1长度特征
每条样本的3个文本特征的长度不一样,其中也许包含了一些信息。例如商品的属性列表很详细,可能是比较精细的产品,例如电脑和显卡两种商品,电脑参数包括CPU/显卡/内存等多项信息,显然比显卡复杂,那么电脑的属性长度可能为50,而显卡只有10,50和10就可以反映商品的一些信息。
因此长度特征可以描述商品的种类。
data['len_item_category'] = data['item_category_list'].map(lambda x: len(str(x).split(';')))
data['len_item_property'] = data['item_property_list'].map(lambda x: len(str(x).split(';')))
data['len_predict_category_property'] = data['predict_category_property'].map(lambda x: len(str(x).split(';')))
3.2基于商店质量的shop分段特征
shop中有4个连续型特征:shop_score_description、shop_score_delivery、
shop_score_service、shop_review_positive_rate
以shop_score_description为例,一条样本的取值是0.9632654,我们期望得知这个取值在总体样本中得分是处于什么样的一个水平。就需要进行分段,代码中0,1,2代表较差、一般、优秀3个等级,这里0.9632654被划分为较差,对应取值为0。
分段还有一个好处就是可以和其他特征进行组合,后面会提到。
data['shop_score_description0'] = data['shop_score_description'].apply(lambda x: 2 if x > 0.984 else x)
data['shop_score_description0'] = data['shop_score_description0'].apply(lambda x: 1 if 0.984 >= x > 0.97 else x)
data['shop_score_description0'] = data['shop_score_description0'].apply(lambda x: 0 if x <= 0.97 else x)
data['shop_score_delivery0'] = data['shop_score_delivery'].apply(lambda x: 2 if x > 0.979 else x)
data['shop_score_delivery0'] = data['shop_score_delivery0'].apply(lambda x: 1 if 0.979 >= x > 0.966 else x)
data['shop_score_delivery0'] = data['shop_score_delivery0'].apply(lambda x: 0 if x <= 0.966 else x)
data['shop_score_service0'] = data['shop_score_service'].apply(lambda x: 2 if x > 0.979 else x)
data['shop_score_service0'] = data['shop_score_service0'].apply(lambda x: 1 if 0.979 >= x > 0.967 else x)
data['shop_score_service0'] = data['shop_score_service0'].apply(lambda x: 0 if x <= 0.967 else x)
data['shop_review_positive_rate0'] = data['shop_review_positive_rate'].apply(lambda x: 2 if x == 1 else x)
data['shop_review_positive_rate0'] = data['shop_review_positive_rate0'].apply(lambda x: 1 if 1 > x > 0.98 else x)
data['shop_review_positive_rate0'] = data['shop_review_positive_rate0'].apply(lambda x: 0 if x <= 0.98 else x)
3.3基于历史点击率的分段特征
以user_age_level为例&