电子商务网站用户行为分析及服务推荐
来源:「Python 数据分析与挖掘实战」机械工业出版社
一、项目介绍
1.1 背景:
研究对象为北京某家法律网站,是一家电子商务类的大型法律资讯网站,致力于为用户提供丰富的法律信息与专业咨询服务,并为律师与律师事务所提供卓有成效的互联网整合营销解决方案。
目标:
- 按地域研究用户访问时间、访问内容和访问次数等分析主题,深入了解用户对访问网站的行为和目的以及关心的内容
- 借助大量的用户访问记录,发现用户的访问行为习惯,对不同需求的用户进行相关的服务页面的推荐
1.2 分析过程
- 从系统中获取用户访问网站的原始记录
- 对数据进行多维度分析,包括用户访问内容,流失用户分析以及用户分类分析
- 对数据进行预处理,包含数据去重、数据变换、和数据分类等处理过程
- 以用户访问后缀 html 的网页为关键条件,对数据进行处理
- 对比多种推荐算法进行推荐,通过模型评价,得到较好的智能推荐模型,通过模型对样本数据进行预测,获得推荐结果
二、数据探索
2.1 数据提取
将7law.sql 导入数据库,利用 python 提取 7law.sql 中 'all_gzdata' 的数据,使用到两个库文件,pymysql 库和 sqlalchemy 库。
1 from sqlalchemy import create_engine 2 engine=create_engine('mysql+pymysql://root:66@127.0.0.1:3306/test?charset=utf8') 3 sql=pd.read_sql('all_gzdata',engine,chunksize=10000)
导入数据库时遇到错误,解决办法:使用 Navicat Premium导入数据库文件,并且修改 max_allowed_packet 为原来的六百倍,原默认情况下应该是 1M,此处文件有 533 M。
2.2 网页类型分析
分析步骤:
- 对总体网页类型进行分析,观察类型占比情况
- 对某类型的网页进行进一步分析,比如与咨询相关(101)的网页进行分析
- 对特殊情况进行分析
- 网页带问号的类型
- 网页地址中不带 ’ .html ‘的类型,这种可视为用户在瞎逛,没有明确目标。
程序主要使用函数:
- 分类型计数 value_counts 函数
- 分组函数 groupby 函数
- 查询字符串是否包含某字符或字符串 contains 函数
- 在字符串中提取某字符或者字符串 extract 函数
- pandas 按列或者行排序函数 sort_values 函数 / sort_index 函数
- 正则表达式的使用
分析结果:
-
网页总数量约有 84 万条。
-
网页内容主要由咨询和知识相关类型内容构成,用户更偏向于咨询。
-
咨询相关中 101003 记录占比最多,该记录为咨询内容页,可以发现用户喜欢通过浏览问题的方式去寻找自己想要的答案,而不是提问的方式或者查看长篇内容知识去得到所需信息。
-
分析其他类型,发现 199 中有好多记录没有正确的分类,比如咨询经验和在线咨询应该是咨询相关内容,应将其进行重新分类。
-
网址中带有 “?”的数量达到 65492 条,占所有记录的7.8%,且其主要都是 1999001类型,占比 98.82%。
-
分析 1999001 类型数据,“法律快车-快车助手”是一个律师的登录界面,“咨询发布成功”是一个自动跳转页面,“快搜”和“免费发布咨询”类型复杂,占比较小。
-
发现一批没有任何点击的用户(没有 .html 记录),可以视为无效数据。
-
有许多网页与分析目标(用户访问行为习惯)无关
-
咨询发布成功页面
-
律师操作页面(法律快车-律师助手)
-
用户瞎逛(没有 .html 记录)
-
中间类型网页(带有 midques_)
-
重复数据(同一时间同一用户访问同一页面)
2.3 点击次数分析
分析步骤:
-
分析点击次数占比情况
-
分析点击次数为 1 的用户浏览网页类型情况
这是用户浏览一次统计排名前五的网页,进去可发现基本是知识页和咨询页。
-
http://www.lawtime.cn/info/shuifa/slb/2012111978933.html 1013条
-
http://www.lawtime.cn/info/hunyin/lhlawlhxy/20110707137693.html 501条
-
http://www.lawtime.cn/ask/question_925675.html 423条
-
http://www.lawtime.cn/info/shuifa/slb/2012111978933_2.html 367条
-
http://www.lawtime.cn/ask/exp/13655.html 301条
分析结果:
-
大概 25% 的人提供了 75% 的浏览量,大致满足二八定律
-
大部分的用户关注点都在知识或者咨询(提出的问题和回答页面,可以打开上面含 ask 的网页看一看)方面
-
针对只浏览一次网页的用户,发现网页类型基本为知识页和咨询页
-
用户流失,用户没有找到自己想找的内容
-
用户找到自己想要找的内容,浏览后退出
2.4 网页排名分析
对整体网页数据进行点击量排名分析,如图所示,单个网页点击量高的是法规专题然后是知识,最后才是咨询类。了解业务获悉,法规专题也是属于知识类的一种,故可知知识类单个点击量高的网页数是明显多余咨询类的,但由前面分析可知,咨询类网页总浏览量是大于知识类的,这是何原因呢?从下表中可以看出其是咨询类的点击率没有知识类的点击率高,大量用户浏览咨询类网页时比较分散,而不是集中于某些网页。
三、数据预处理
3.1 处理步骤
-
数据清洗 重复数据、与目标无关数据、占比较小的杂乱数据
-
数据变换 原始数据中分类不当的数据、翻页数据等
-
数据重分 原始的数据分类比较散乱,可以利用网址的构成对其进行重新分类
-
数据存储 整理完的数据重新存储进一个 sql 中
3.2 程序使用的方法:
-
数据清洗 先将满足清洗条件标记出来,然后根据标记清洗。如“中间类型网页”,利用 pandas创建一列为 0 的列,检测满足“中间类型网页”条件,将其置 1,依同样方法创建几个列分别处理其他几个条件,最后将这几个列相加,和为 0 的列则是我们需要留下来的列。
-
数据变换 利用 str.replace() 函数将翻页情况的网址末尾的“_2.html” 替换为“.html” 替换掉,然后去重。如:d['fullURL'] = d['fullURL'].str.replace('_d{0,2}.html', '.html')
-
数据重分 先查看网址中的关键词,对关键词进行汇总排序,取排名高的几类作为小类,建立新的标签。主要用到的就是正则表达式的知识对网址字符串进行提取。
-
数据存储 d.to_sql('splited_gzdata',engine,index=False,if_exists='append')
四、建模 & 评价
推荐系统是一种帮助用户快速发现有用信息的工具,推荐系统不需要用户提供明确的需求,而是通过分析用户的历史行为给用户的兴趣建模,从而主动给用户推荐能够满足他们兴趣和需求信息。
本例由于数据量大,只使用处理后的‘’婚姻类‘’数据做相关推荐,使用的是基于物品的协同过滤算法。
一般步骤:
-
建立用户-物品矩阵
-
利用用户-物品矩阵建立物品的相似度矩阵
-
相似度的计算方法有很多种,如夹角余弦、杰卡德相似系数、相关系数等等
-
-
根据物品的相似度和用户的历史行为给用户生成推荐列表
-
模型评价
4.1 数据前处理
由于本例中的物品即代表着不同的网址,网址过长不好利于展示,暂且使用数字代号代替各不同网址,程序如下:
1 class_mapping={label:idx for idx,label in enumerate(hunyin['fullURL'].unique())} 2 hunyin['net_label']=hunyin['fullURL'].map(class_mapping) 3 hunyin['net_label']=hunyin['net_label'].astype(str)
4.2 数据集划分
训练集和数据集的划分,把数据集划成 10 份,其中 9 份用来作为训练集,另外 1 份用来作为测试集。可以使用自己定义函数划分,也可以使用 python 中 sklearn.model_selection (train_test_split)来进行划分。另外存储时候选择了多层字典的形式(用户-物品倒排表),这将有利于后面的数据处理,减少计算量。
1 # 训练集测试集分开,分别用字典的方式表示,即用户对应点击了哪些网页多少次 2 train,test=train_test_split(hunyin,test_size=0.1,random_state=0) 3 train_df=train[['realIP','net_label','num']].groupby(['realIP','net_label']).sum() 4 test_df=test[['realIP','net_label','num']].groupby(['realIP','net_label']).sum() 5 6 train_dict={i:train_df.xs(i)['num'].to_dict() for i in train_df.index.levels[0]} # 将 df 结构改为用户-物品字典 7 test_dict={i:test_df.xs(i)['num'].to_dict() for i in test_df.index.levels[0]} # 将 df 结构改为用户-物品字典
4.3 求物品相似度(物品共现矩阵)
物品相似度公式:
分子表示同时喜欢物品 i 和 物品 j 的用户数,分母表示喜欢物品 i 和 物品 j 的用户数乘积的平方根。
有时候由于物品量或者用户量过多,一般的计算速度会很慢,这里采用一种物品共现矩阵的方式来加快计算,如图。
程序如下:
1 page_sim_mat={} 2 N=dict() 3 W=dict() 4 for user,pages in train_dict.items(): 5 for page1,count1 in pages.items(): 6 N.setdefault(page1, 0) 7 N[page1]+=1 8 page_sim_mat.setdefault(page1, {}) 9 for page2, count2 in pages.items(): 10 if page1==page2: 11 continue 12 page_sim_mat[page1].setdefault(page2, 0) 13 page_sim_mat[page1][page2]+=1 14 del page1,page2,user,count1,count2,pages 15 16 # 余弦相似矩阵 17 for page1,related_page in page_sim_mat.items(): 18 for page2,count in related_page.items(): 19 W.setdefault(page1, {}) 20 W[page1].setdefault(page2, 0) 21 W[page1][page2]=page_sim_mat[page1][page2]/math.sqrt(N[page1]*N[page2]) 22 del page1,page2,related_page,count
4.4 计算兴趣度
计算用户 u 对一个物品 j 的兴趣度:
N(u) 是用户当前喜欢的物品组合,是 S(j,k) 是和物品 j 最相似的 K 个物品组合,wji 是物品 j 和 i 的相似度,rui 是用户 u 对物品 i 的兴趣。和用户历史行为中越相似的物品越容易出现在推荐表中。
1 # 计算用户推荐字典,和用户历史上感兴趣的物品越相似的物品越有可能在用户的推荐列表中获得比较高的排名 2 # K 指的是与用户历史感兴趣物品i相似的物品数量,并非最后推荐物品推荐字典数量 3 def Recommendation(train,user_id,w,K): 4 rank=dict() 5 ru=train[user_id] 6 for i ,pi in ru.items(): 7 # 冷启动,跳过那些 test里面新出现的网页,在原始构建的网页矩阵中没有出现的 8 if i not in W.keys(): 9 continue 10 for j,wj in sorted(w[i].items(),key=operator.itemgetter(1),reverse=True)[0:K]: 11 if j in ru: 12 continue 13 rank.setdefault(j, 0) 14 rank[j]+=pi*wj 15 rank=sorted(rank.items(),key=operator.itemgetter(1),reverse=True) 16 17 return dict(rank)
4.5 评价
对用户 u 推荐 N 个物品(记为 R(u)),令用户在测试集上的物品集合为 T(u)
召回率计算如下:
准确率计算如下:
召回率反映的是推荐正确的物品占实际测试集物品的比重;
准确率反映的是推荐正确的物品占推荐物品集的比重。
1 def Recall(train,test,w,N): 2 hit=0 3 all=0 4 for user in test.keys(): 5 all += len(test[user]) 6 # 冷启动,跳过那些test里面新出现的用户 7 if user not in train.keys(): 8 continue 9 rank=Recommendation(train,user,w,N) 10 for item,pui in rank.items(): 11 if item in test[user]: 12 hit+=1 13 return hit/all 14 15 def Precision(train,test,w,N): 16 hit=0 17 all=0 18 for user in test.keys(): 19 # 冷启动,跳过那些test里面新出现的用户 20 if user not in train.keys(): 21 continue 22 rank=Recommendation(train,user,w,N) 23 all += len(rank) 24 for item,pui in rank.items(): 25 if item in test[user]: 26 hit+=1 27 return hit/all
随着 K 的增加,召回率在上升,而准确率在下降