2023.8夏令营“用户新增预测”学习笔记(一)

本文介绍了如何使用Python和机器学习中的决策树方法,对用户数据进行分析,判断是否为新增用户,重点关注特征提取、时间序列处理和评估指标如精确率、召回率和F1_score的应用。
摘要由CSDN通过智能技术生成

        对赛事概要的部分理解以及baseline讲解课程中部分代码含义的知识点记录,准大一凭借浅显的python基础对赛事相关内容的理解实在有限,以下仅为个人理解。

主要目的

        根据平台给出的用户数据判断该用户是否为新增用户,若是,则在submit.csv的target一栏输出为1,反之,输出为0。尽量提高输出结果的精确率和召回率,以得到更高的f1_score。

背景理解

        尝试用机器学习的方法分析平台用户的使用场景并预测增长情况。用户在使用平台的场景中有各种各样的访问操作(即行为),如点击、浏览、退出等操作,用户本身也具有各种各样的属性,如性别、年龄、爱好等,它们被脱敏化处理后,在保护了用户隐私的前提下,抽象成了训练集数据,能够被程序提取特征,并以此对测试集数据进行分类判断。

数据理解

uuid--样本唯一标识:206784条测试集数据分别对应各个用户,uuid是每个用户的“身份证号”

eid--访问行为ID:每个行为的区分标识

udmap--行为属性:每个行为都有不同的属性,比如点击次数、浏览时长、操作顺序等,共分为key1~key9九类。它们封装入对应eid放入udmap

common_ts--应用访问记录发生时间:字面意思,行为发生的相关时间记录,以毫秒计

x1~x8--用户属性:每个用户的个人信息特征,被匿名处理成数据

target--预测目标:是否为新增用户

评估指标

f1_score为评估指标。

        真实值相当于标答,预测值相当于所填,两种可能正解和两种可能选择组成4个组合,即混淆矩阵。标答为正的样本为正例样本。

        精确率,即正例中答对的比例,体现模型的精准度,说哪个对,哪个就对。

precision=\frac{TP\frac{}{}}{TP+FP}

        召回率,即答对的题中正例的比例,体现一个模型的全面性,对的都能找出。

recall=\frac{TP}{TP+FN}

        f1_score,即两者的调和平均值,这要求上述两值不仅要尽量高,还要接近,才可能得到高分评价。

f1 = \frac{2\frac{}{}}{\frac{1}{precision}+\frac{1}{recall}}

代码解读(baseline)

# 导入库
import pandas as pd
import numpy as np

        导入了两个库,并规定了简写,使代码简洁。其中,前者用于数据处理和分析,后者用于科学计算和多位数组操作。

        在pycharm中使用需要提前载入,在cmd输入“pip install 库名”即可,出错按返回的建议操作,或在CSDN搜索解决方案。在ai studio中则可直接导入。

# 导入模型
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import precision_score, recall_score, f1_score

        从sklearn.tree模块导入DecisionTreeClassifier类,用于构建决策树分类模型。从sklearn.metrics模块导入三个指标的计算函数。

# 数据简略观测
train_data.head()
train_data.tail()
train_data.info()
train_data.columns

        前两个函数查看表格的前/末若干行,参数默认值为5;第三个函数用于查看数据总体属性;第四个函数用于查看表格字段列表和类型。

# 数据载入
train_data = pd.read_csv('E:/编程/用户新增预测大赛/train.csv')
test_data = pd.read_csv('E:/编程/用户新增预测大赛/test.csv')

        使用pd库的read_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')

        对于两个pd库的数据框对象,使用[<column_id>]标记了访问时间记录一列,使用pd.to_datetime()函数将时间戳列转换为 datetime 类型,单位为毫秒(unit参数使用关键字传递),eg.1678932546000->2023-03-15 15:14:16。 

        注意时间戳的长度, 如果是13位则unit 为毫秒, 如果是10位则为秒。

        由表格知,除了udmap字段的数据类型为字典或字符串、commom_ts字段的数据类型为datetime64[ns](以64位二进制数表示的以纳秒为单位的日期),其余字段的数据类型均为int64(以64位二进制数表示的整数)。

def udmap_onethot(d: str) -> np.ndarray:
    v = np.zeros(9)
    if d == 'unknown':
        return v
''' '{'key3': '67804', 'key2': '650'}' -> [0, 650, 67804, 0, 0, 0, 0, 0, 0]
    'unknown' -> [0, 0, 0, 0, 0, 0, 0, 0, 0]'''

    d = eval(d)
     # for...in...循环结构遍历range对象,将1~9的整数列依次赋给i进行循环
        if 'key' + str(i) in d:
    for i in range(1, 10): 
            v[i - 1] = d['key' + str(i)]

    return v


'''数据样例:
          udmap            key1  key2  key3  key4  key5  key6  key7  key8  key9
0           {'key1': 2}     2     0     0     0     0     0     0     0     0
1           {'key2': 1}     0     1     0     0     0     0     0     0     0
2  {'key1': 3, 'key2': 2}   3     2     0     0     0     0     0     0     0

    形如 {'key1': 3, 'key2': 2} 格式的为字典类型对象, 通过key-value键值对方式存储。
本数据集中, udmap以字符的形式存储, 处理时需用eval 函数将'udmap'解析为字典。'''

        定义一个函数:输入一个字符串,返回一个np数组(向量)。

        建立一个9维零向量[0, 0, 0, 0, 0, 0, 0, 0, 0]。输入‘unknown’,则返回9维零向量;输入其他字符串,则使用eval()函数,去掉内容是数或字典的字符串的引号,转化成对应类型,并将向量v中的i-1分量(相当于列表索引)的值赋给字典d中的key{i}键。

# 将'udmap'列进行 One-Hot 编码
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)))

        从内层开始分析,使用apply()函数,对数据集的udmap列的所有行调用前述udmap_onethot()函数,将udmap编码成九维向量,并使用np.vstack()函数将结果纵向堆叠成一个矩阵(数组),最后转化为数据框。

# 为新的特征 DataFrame 命名列名
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)]
'''或:
['key' + str(i) for i in range(1, 10)], \
 [i for i in range(1, 10)]'''

        将数据框各列转化为先前的key_num格式。 

  

# 将编码后的 udmap 特征与原始数据进行拼接,沿着列方向拼接
train_data = pd.concat([train_data, train_udmap_df], axis=1)
test_data = pd.concat([test_data, test_udmap_df], axis=1)
# axis=1:沿二维数组轴线连接,即连接各列

        使用pd.concat()函数将两次生成的数据框(data&udmap_df)连接,得到新数据框(data)。 

# 提取 eid 的频次特征
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())

        使用map()函数调用value_counts()函数,标记eid(行为ID)列,并对不同的数据值统计计数,写入eid_freq列,即各eid出现次数。

# 提取 eid 的标签特征
train_data['eid_mean'] = train_data['eid'].map(train_data.groupby('eid')['target'].mean()).astype(np.float32)
test_data['eid_mean'] = test_data['eid'].map(train_data.groupby('eid')['target'].mean()).astype(np.float32)

        使用map()函数调用groupby()函数按eid分组,调用mean()函数计算每组target列的平均值,并使用astype()函数将数据类型转为np库的float32类型(以32位二进制数表示的浮点数),写入eid_mean列。

train_data['eid_std'] = train_data['eid'].map(train_data.groupby('eid')['target'].std()).astype(np.float32)
test_data['eid_std'] = test_data['eid'].map(train_data.groupby('eid')['target'].std()).astype(np.float32)

        同理,计算所得标准差写入eid_std列。

train_data = train_data.fillna(0)
test_data = test_data.fillna(0)

        使用fillna()函数,将表中的空值NA替换为0。

# 编码udmap是否为空
train_data['udmap_isunknown'] = (train_data['udmap'] == 'unknown').astype(int)
test_data['udmap_isunknown'] = (test_data['udmap'] == 'unknown').astype(int)

        判断udmap各列的值是否为'unknown',并将输出的布尔值转化为0,1,填入udmap_isunknown列。

# 提取时间戳
train_data['common_ts_hour'] = train_data['common_ts'].dt.hour
test_data['common_ts_hour'] = test_data['common_ts'].dt.hour

train_data['common_ts_day'] = train_data['common_ts'].dt.day.astype(np.float32)
test_data['common_ts_day'] = test_data['common_ts'].dt.day.astype(np.float32)

        common_ts列的时间信息精确到了ms,但只需要提取出部分信息。使用.dt.函数提取 common_ts列的小时信息,填入common_ts_hour列。下面同理(数据转换可省略)。

train_data.drop(['udmap', 'common_ts', 'uuid', 'target'], axis=1).astype(np.float32).isna().sum(axis=0)

        删除udmap, common_ts, uuid, target列的所有行(去除非数字列),将表中数据类型全部转为float32,判断数据是否为NA并返回布尔值,对各行(axis=0)数据求和。

        这些列可能是特征或标签,取决于数据集的设置。'target'是训练数据集中的标签列,它包含了每个样本的目标值。

# 训练模型
clf = DecisionTreeClassifier()
clf.fit(
    train_data.drop(['udmap', 'common_ts', 'uuid', 'target'], axis=1).astype(np.float32),
    train_data['target']
)

        定义决策树分类器,并调用它的fit()函数拟合数据,拟合目标是target字段。

# 结果评估
pred = clf.predict(train_data.drop(['udmap', 'common_ts', 'uuid', 'target'], axis=1))

precision = precision_score(train_data['target'], pred)
recall = recall_score(train_data['target'], pred)
f1_score = f1_score(train_data['target'], pred)

print(f"precision:\t{precision},\nrecall:\t\t{recall},\nf1_score:\t{f1_score}")

        将拟合&预测结果输入三个函数来计算指标,并输出结果分数。

# 预测结果写入
pd.DataFrame({
    'uuid': test_data['uuid'],
    'target': clf.predict(test_data.drop(['udmap', 'common_ts', 'uuid'], axis=1))
}).to_csv('submit.csv', index=False)

        建立只有uuid和target两列的数据框,并填入测试集的用户ID和训练好的模型输出的预测结果,写入submit.csv文件,规定不将DataFrame的索引index写入文件中。

      对测试集的预测结果如上。可见先前使用的评估函数参考性不足,应选取更优的评估函数。

总结

        以上代码使用pandas库实现了对表格的编辑,使用numpy库实现了对数据的转化与统计整理,分类拟合过程仅需调用模块处理即可。故我们的任务重心是从原始数据中挖掘出更多的特征列表以供拟合,以及对数据的更多操作,无需对底层实现原理深究。

系列文章(持续更新)

2023.8夏令营“用户新增预测”学习笔记(二)

2023.8夏令营“用户新增预测”学习笔记(三)

2023.8夏令营“用户新增预测”学习笔记(四)

2023.8夏令营“用户新增预测”学习笔记(五)

2023.8夏令营“用户新增预测”学习笔记(六) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值