1、需求:
在用户行为分析中重要的一环是对用户行为路径的分析,如先访问什么再访问什、现购买什么再购买什么。找到用户的访问或购买路径,有助于我们对商业流程的理解,也可以帮助我们改善和优化企业产品。
2、题设:
假设我们是一家家电电商网站,拥有所有客户的购买每种家电的详细记录(包括客户id,购买产品,购买日期)。
我们希望通过这个数据分析出社会用户在购买家电先后顺序的行为路径。
这个结果的实际用途,例如客户在装修新房后购置抽油烟机,那么我们很可能希望给他推荐热水器或者空调等。我们总不至于像某东网站一样,客户购买了一个冰箱后还给推荐另一个冰箱吧?
3、结果示例:
希望能够得到类似下图的结果:
这是百度echarts的Sankey(桑基图)图示例,我们将通过Python+R实现类似的效果。
4、技术路线:
如标题所述,使用Python进行数据处理,形成R绘图直接可用的数据结构;再使用R绘制Sankey图(之所以不使用Python直接绘图,是因为Python在绘制桑基图方面还不够完善——好吧,是我不会!)。
先来看R绘制桑基图networkD3官方给出的示例代码:
#R code
# Load energy projection data
# Load energy projection data
URL <- paste0(
"https://cdn.rawgit.com/christophergandrud/networkD3/",
"master/JSONdata/energy.json")
Energy <- jsonlite::fromJSON(URL)
# Plot
sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 12, nodeWidth = 30)
访问上述url指向的地址,可以看到数据文件格式,这是一个json文件,结构如下:
#R networkD3绘制桑基图所需的数据格式
{"nodes":[
{"name":"Agricultural 'waste'"},
{"name":"Bio-conversion"},
……
],
"links":[
{"source":0,"target":1,"value":124.729},
{"source":1,"target":2,"value":0.597},
{"source":1,"target":3,"value":26.862}],
……
]}
数据主要包含两个部分:nodes的每条记录表示桑基图中每个节点位置的名称;links的每条记录表示从某个节点到另一个节点出现的次数。source和target后面所跟的数字顺序和nodes后面的数据顺序一致。如0表示节点名字是Agricultural 'waste',1表示节点Bio-conversion,{"source":0,"target":1,"value":124.729}表示从Agricultural 'waste'到Bio-conversion有124.729大的量。
因此,剩下的工作就是如何将上述分笔交易数据转化成上述符合要求的格式。
5、数据整理:
大致分析一下,我们首先需要找到购买了两笔及以上的客户。然后将这些客户中,每个客户成交商品的顺序进行处理。
如一个客户先后购买了:冰箱——洗衣机——电视机,整理出的关系则是:
source target value
冰箱 洗衣机 1
洗衣机 电视机 1
(1)首先,需要将购买有2次记录及以上的所有数据筛选出来备用。
#Python code
import pandas as pd
#读取所有数据
base_data=pd.read_excel('C:/Users/Administrator/Desktop/家电购买记录.xlsx')
#找出购买了两次及以上的用户(通过user_id统计次数再作筛选)
buy_times=base_data.groupby('user_id')['product'].count()
re_buy_userid=buy_times[buy_times>1].reset_index()
#筛选所有购买两次及以上用户的购买记录
rebuy_data=base_data[base_data['user_id'].isin(re_buy_userid['user_id'])]
(2)第二步,分析购买先后次序。思路是对每个客户的购买记录进行分析,形成第5部分开头所示的明细列表,然后再对这个明细列表进行汇总,计算出每种对应关系的汇总结果。
#Python code
#分析流向
source_name=[]
target_name=[]
num=[]
#遍历每个购买两次及以上的用户id
for user_id in re_buy_userid['user_id']:
#取出每个用户购买记录明细,按时间升序排列,重建索引,以便后面取结果时使用
each_buy=rebuy_data[rebuy_data['user_id']==user_id].sort_values(by='buytime',ascending=True).reset_index()
#按顺序对上述明细数据进行遍历,分别取当前记录的product和下一条记录的product,即可构成购买先后关系
for i in range(len(each_buy)-1):
each_source=each_buy['product'][i]
each_target=each_buy['product'][i+1]
source_name.append(each_source)
target_name.append(each_target)
num.append(1)
#对结果进行整理,形成一个表格
st_result=pd.DataFrame([source_name,target_name,num],index=['source_name','target_name','num']).T
#获取统计结果
group_result=st_result.groupby(['source_name','target_name']).count().reset_index().sort_values(by='num',ascending=False)
这里我们得到的结果如下:
实际上,到这里,基本上已经是我们R所需要的数据了,只需要将其转换成json格式即可。
同时,我们可以得出结论:我们的客户通常情况下买了电视机会接着买空调,买了电饭锅通常会买电磁炉,买了空调通常会买微波炉(千万别拿这个当决策,前面说了数据都是我瞎编的)...
(3)第三步,将上述数据转换成json格式,并存储成文件。
#Python code
#转化json所需格式
#定义names,以确定nodes的name顺序
names=group_result['source_name'].append(group_result['target_name']).drop_duplicates().reset_index(drop=True)
#将确定好的name顺序固定,生成一个对应表,用以对group_result里面的中文补充相应的name顺序代号
names_order=names.reset_index().reset_index().rename(columns={'level_0':'source','index':'target',0:'product'})
#添加source_name对应的name代号
mid_data=pd.merge(group_result,names_order[['source','product']],left_on='source_name',right_on='product',how='left')
#添加target_name对应的name代号
path_data=pd.merge(mid_data,names_order[['target','product']],left_on='target_name',right_on='product',how='left')
#生成nodes
name_list=[]
for i in names:
each_name={'name':i}
name_list.append(each_name)
#生成links
source_target=[]
for i in range(len(path_data)):
each_links={'source':path_data['source'][i],'target':path_data['target'][i],'value':path_data['num'][i]}
source_target.append(each_links)
#生成json格式
rebuy_dict={"nodes":name_list,"links":source_target}
rebuy_json=str(rebuy_dict).replace("'",'"')
#存储为json格式
f=open("d:/rebuy.json","w",encoding="utf-8")
f.write(str(rebuy_json))
f.close()
(4)第四步,使用R绘图
#R code
library(networkD3)
rebuy <- jsonlite::fromJSON('D:/rebuy.json')
sankeyNetwork(Links = rebuy$links, Nodes = rebuy$nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 30, nodeWidth = 30,
fontFamily = "微软雅黑")
来看结果吧:
不能更丑了,已经。