提示:由于本人也是小白,本文可能会非常非常基础。本系列是基于Datewhale 23年暑期夏令营第三期赛题,赛题具体内容见下方链接。
赛题链接:
https://challenge.xfyun.cn/topic/info?type=subscriber-addition-prediction&ch=ymfk4uU
目录
前言
本文对baseline代码进行详细的解释。
一、数据集
赛题数据由约62万条训练集、20万条测试集数据组成,共包含13个字段。
其中uuid为样本唯一标识(用户编号),eid为访问行为ID,udmap为行为属性,其中的key1到key9表示不同的行为属性,如项目名、项目id等相关字段,common_ts为应用访问记录发生时间(毫秒时间戳),其余字段x1至x8为用户相关的属性,为匿名处理字段。target字段为预测目标,即是否为新增用户。
二、对Baseline的详解
#NO_1
#导入相关的库
import pandas as pd
import numpy as np
#使用pd.read_csv()函数读取数据
train_data = pd.read_csv('用户新增预测挑战赛公开数据/train.csv')
test_data = pd.read_csv('用户新增预测挑战赛公开数据/test.csv')
train_data['common_ts'] = pd.to_datetime(train_data['common_ts'], unit='ms')
test_data['common_ts'] = pd.to_datetime(test_data['common_ts'], unit='ms')
第一段代码:
- 导入了pandas和numpy两个常用的Python数据分析库
- 使用pandas的read_csv()方法分别读取了训练集train.csv和测试集test.csv
- 对训练集和测试集的common_ts这一列使用pd.to_datetime()进行了日期格式的转换,单位指定为ms(毫秒)
- 这样将common_ts这一列从原始的时间戳转换成了pandas的DateTime格式,便于后续进行时间序列分析和特征提取。
- 整体来说,这段代码完成了训练集和测试集的导入,并对时间戳列进行了预处理,为后续的分析建模做好了准备。
#NO_2
def udmap_onethot(d):
v = np.zeros(9) #创建长度为9的零数组
if d == 'unknown':
return v
d = eval(d)#将“udmap”解析为字典
for i in range(1, 10):#遍历“key1”到“key9”
if 'key' + str(i) in d:#如果当前键存在于字典中
v[i-1] = d['key' + str(i)]#将字典中的值存储在相应的索引上
return v
train_udmap_df = pd.DataFrame(np.vstack(train_data['udmap'].apply(udmap_onethot)))
test_udmap_df = pd.DataFrame(np.vstack(test_data['udmap'].apply(udmap_onethot)))
train_udmap_df.columns = ['key' + str(i) for i in range(1, 10)]
test_udmap_df.columns = ['key' + str(i) for i in range(1, 10)]
第二段代码:
1.定义了udmap_onethot()函数,目的是将udmap这一字典格式的特征转换为固定长度的向量表示。
2.对每个样本的udmap字典,如果是'unknown',返回全0向量。否则遍历字典的key1到key9,如果存在该key则将对应位置置1,可以理解为下图的形式(总结来说就是字典——>等长向量):
具体理解udmap_onethot()函数,d代表每个样本的udmap字典。'key' + str(i)构造了一个变量字典键的字符串,比如'key1','key2'等。然后使用in判断这个键是否在字典d中。如果存在,则将对应的值赋给结果向量v的第i-1个元素(因为key是从1开始,但是v[]数组里从0开始)。这样就实现了有选择地将udmap字典中的键值复制到定长结果向量v中。
3.train_udmap_df以及test_udmap_df目的是将训练集和测试集中的 udmap 字典特征转换为 DataFrame 格式。
具体来说,apply(udmap_onethot) 对 udmap 列应用自定义函数,将其转化为向量。np.vstack将向量垂直拼接,变成二维数组。pd.DataFrame是将这个二维数组转化为DataFrame
4.train_udmap_df.columns以及test_udmap_df.columns是设定列名为key1-key9。
train_data = pd.concat([train_data, train_udmap_df], axis=1)
test_data = pd.concat([test_data, test_udmap_df], axis=1)
第三段代码:
是将处理后的udmap特征加入到原始的训练集和测试集中。其中pd.concat()可以实现DataFrame的拼接,直白来说就像用胶带把我们刚刚生成的train_udmap_df和test_udmap_df粘到原始的数据集后面。(函数内参数axis=1就表示将列拼接在一起)
train_data['eid_freq'] = train_data['eid'].map(train_data['eid'].value_counts())
test_data['eid_freq'] = test_data['eid'].map(train_data['eid'].value_counts())
train_data['eid_mean'] = train_data['eid'].map(train_data.groupby('eid')['target'].mean())
test_data['eid_mean'] = test_data['eid'].map(train_data.groupby('eid')['target'].mean())
第四段代码:
理解这段代码首先理解value_counts()以及train_data.groupby().mean()
value_counts():这个函数的输入是一个序列,功能为统计序列中各个值出现的频率。
train_data.groupby().mean():这个看起来比较复杂,其实就是根据eid列进行分组,再计算target列的平均值(简单理解为相同eid值所对应的target的均值)
有了上面的基础,再理解map()这个函数:就是将我们刚刚算出来的频率,以及均值。对应到原始数据上,和第三段代码的思路异曲同工。
这种基于某个特征的分组统计,然后映射回每个样本的操作,是构造新的特征的常用技巧。它可以充分挖掘训练集中的信息,并扩展到测试集,为模型提供更多特征以提升效果。这样以eid
为桥梁构造的新特征,可以帮助模型学习eid
对目标的影响。
train_data['udmap_isunknown'] = (train_data['udmap'] == 'unknown').astype(int)
test_data['udmap_isunknown'] = (test_data['udmap'] == 'unknown').astype(int)
第五段代码:
由于udmap中含有unknown的数据:
可以通过上面的代码判断每一条的udmap是否为unknown,“是”则为“1”,“否”则为“0”(通过astype函数将布尔值转换为数值1或0)。这样针对unknown的数据缺失部分,通过上述方法,构造了一个新的二值特征,可以为模型提供额外的信息,充分挖掘并扩展了数据中的信息。
train_data['common_ts_hour'] = train_data['common_ts'].dt.hour
test_data['common_ts_hour'] = test_data['common_ts'].dt.hour
第六段代码:
这段相对简单,主要使用了.dt.hour()。在第一段代码中common_ts已经被转化为pandas的datetime类型。利用dt访问器,就可以获取各种时间相关的属性,如dt.hour、dt.day等。在这里是从common_ts列中提取了小时这个时间特征。
import lightgbm as lgb
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(
train_data.drop(['udmap', 'common_ts', 'uuid', 'target'], axis=1),
train_data['target']
)
第七段代码:
1.导入了三个库,lightbgm、sklearn.linear_model、sklearn.tree。
2.这段代码中主要用到了sklearn.tree中的DecisionTreeClassifier,并将它命名为clf。而fit函数,就是训练模型的核心方法,(具体而言,将训练特征矩阵X和目标向量y传递给模型;模型根据自己的算法,学习特征和目标之间的映射关系;通过迭代计算不断优化模型的参数,以最小化损失函数;得到一组最优的参数,建立特征到目标的映射模型;返回一个训练好的模型对象)
pd.DataFrame({
'uuid': test_data['uuid'],
'target': clf.predict(test_data.drop(['udmap', 'common_ts', 'uuid'], axis=1))
}).to_csv('submit.csv', index=None)
第八段代码:
整体来看是将结果转为DataFrame并写入submit.csv文件。内部为:将测试集中也删除训练集中删除的特征,调用clf.predict()对测试集进行预测,获得预测结果。最后将测试集的uuid(样本唯一标识)和预测结果写入submit.csv文件。
三、总结
可见本文分析的baseline代码思路清晰,通过数据挖掘、由现有数据扩展出数据内涵的隐藏信息,后使用决策树进行模型训练,相对简单明了。但跑分为0.62左右,满分为1,还有很大改进空间。
ps.
文中难免有疏漏或错误之处,如蒙各位读者能够提出宝贵意见,将不胜感激。