一个编程实战案例,手把手教你构建电商用户画像 数据分析
1# 导入所需的库
2
3%matplotlib inline
4
5import numpy as np
6
7import pandas as pd
8
9from matplotlib import pyplot as plt
10
11from datetime import datetime
- %matplotlib inline:一个魔法函数,由于%matplotlib inline的存在,当输入plt.plot()后,不必再输入plt.show(),图像将自动显示出来。
- datetime:用来显示时间的模块。
1# 导入数据集
2
3df_orginal = pd.read_csv("./taobao_persona.csv")
4
5# 抽取部分数据
6
7df = df_orginal.sample(frac=0.2,random_state=None)
1# 查看其中是否有缺失值,统计各字段缺失值
2
3df.isnull().any().sum()
4
5# 发现只有user_geohash有缺失值,且缺失的比例很高,无统计分析的意义,将此列删除
6
7df.drop("user_geohash",axis=1,inplace=True)
8
9# 将time字段拆分为日期和时段
10
11df["date"] = df["time"].str[0:10]
12
13df["time"] = df["time"].str[11:]
14
15df["time"] = df["time"].astype(int)
16
17# date用str方法取0-9位的字符,time取11位到最后一位,将time转化成int类型。
18
19# 将时段分为"凌晨","上午","中午","下午","晚上"
20
21df["hour"] = pd.cut(df["time"],bins=[-1,5,10,13,18,24],labels=["凌晨","上午",
22
23 "中午","下午","晚上"])
1# 生成用户标签表,制作好的标签都加入这个表中
2
3users = df["user_id"].unique()
4
5labels = pd.DataFrame(users,columns=["user_id"])
- pd.DataFrame():其中数据填充的是users,列名为user_id。
1# 对用户和时段分组,统计浏览次数
2
3time_browse = df[df["behavior_type"]==1].groupby(["user_id","hour"]).item_
4
5 id.count().reset_index()
6
7time_browse.rename(columns={"item_id":"hour_counts"},inplace=True)
8
9# 统计每个用户浏览次数最多的时段
10
11time_browse_max = time_browse.groupby("user_id").hour_counts.max().reset_index()
12
13time_browse_max.rename(columns={"hour_counts":"read_counts_max"},inplace=True)
14
15time_browse = pd.merge(time_browse,time_browse_max,how="left",on="user_id")
16
17# 之前已经按照user_id和hour进行了浏览物品次数的计数统计,现在借用浏览次数统计user_id在
18
19# 哪个时间段浏览次数最多,并将其作为该用户的浏览时间标签的代表。
20
21# 选取各用户浏览次数最多的时段,如有并列最多的时段,用逗号连接
22
23time_browse_hour=time_browse.loc[time_browse["hour_counts"]==time_browse["read_
24
25 counts_max"],"hour"].groupby(time_browse["user_id"]).aggregate(lambda
26
27 x:",".join(x)).reset_index()
28
29time_browse_hour.head()
30
31# 将用户浏览活跃时间段加入用户标签表中
32
33labels = pd.merge(labels,time_browse_hour,how="left",on="user_id")
34
35labels.rename(columns={"hour":"time_browse"},inplace=True)
36
37# labels相当于一张考试卷纸,上面展示的都是最后处理好的结果
- groupby(["key1","key2"]):多列聚合,分组键为列名。
- reset_index():默认drop=False,可以获得新的index,原来的index变成数据列保留下来,第一列会添加计数的数字,不会使用数据中的index。
- rename():进行重命名,此处将item_id替换成hour_counts,inplace为是否原地填充。
- pd.merge():将两个表合并在一起,横向合并,on代表通过某个主键,how指左合并,每行一一对应。
- loc函数:通过行索引Index中的具体值来取指定数据。
- aggregate函数:groupby分组之后会返回多个子数据帧,该函数可以实现数据聚合,可以得到每个子数据帧的某些列的某些信息。
- lambda函数:可以定义一个匿名函数,lambda [arg1[, arg2, … argN]]: expression,其中参数是函数的输入,是可选的,后面的表达式则为输出,此处和join()函数一起用,其中每个x值能被“,”隔开;使用类似的代码可以生成浏览活跃时间段,此处就不再赘述。
1df_browse = df.loc[df["behavior_type"]==1,["user_id","item_id","item_category"]]
2
3df_collect = df.loc[df["behavior_type"]==2,["user_id","item_id","item_category"]]
4
5df_cart = df.loc[df["behavior_type"]==3,["user_id","item_id","item_category"]]
6
7df_buy = df.loc[df["behavior_type"]==4,["user_id","item_id","item_category"]]
1# 对用户与类目进行分组,统计浏览次数
2
3df_cate_most_browse = df_browse.groupby(["user_id","item_category"]).item_id.count().
4
5 reset_index()
6
7df_cate_most_browse.rename(columns={"item_id":"item_category_counts"},inplace=
8
9 True)
10
11# 统计每个用户浏览次数最多的类目
12
13df_cate_most_browse_max=df_cate_most_browse.groupby("user_id").item_category_
14
15 counts.max().reset_index()
16
17df_cate_most_browse_max.rename(columns={"item_category_counts":"item_category_
18
19 counts_max"},inplace=True)
20
21df_cate_most_browse = pd.merge(df_cate_most_browse,df_cate_most_browse_max,
22
23 how="left",on="user_id")
24
25# 将item_category的数字类型改为字符串型
26
27df_cate_most_browse["item_category"] = df_cate_most_browse["item_category"].
28
29 astype(str)
30
31# 选取各用户浏览次数最多的类目,如有并列最多的类目,用逗号连接
32
33df_cate_browse=df_cate_most_browse.loc[df_cate_most_browse["item_category_
34
35 counts"]==
36
37df_cate_most_browse["item_category_counts_max"],"item_category"].groupby(df_
38
39 cate_most_browse["user_id"]).aggregate(lambda x:",".join(x)).reset_index()
40
41# 将用户浏览最多的类目加入用户标签表中
42
43labels = pd.merge(labels,df_cate_browse,how="left",on="user_id")
44
45labels.rename(columns={"item_category":"cate_most_browse"},inplace=True)
46
47labels.head(5)
1# 将购买行为按用户进行分组,统计次数
2
3df_counts_30_buy = df[df["behavior_type"]==4].groupby("user_id").item_id.
4
5 count().reset_index()
6
7labels = pd.merge(labels,df_counts_30_buy,how="left",on="user_id")
8
9labels.rename(columns={"item_id":"counts_30_buy"},inplace=True)
1# 将加购行为按用户进行分组,统计次数
2
3df_counts_30_cart = df[df["behavior_type"]==3].groupby("user_id").item_id.
4
5 count().reset_index()
6
7labels = pd.merge(labels,df_counts_30_cart,how="left",on="user_id")
8
9labels.rename(columns={"item_id":"counts_30_cart"},inplace=True)
1# 对用户进行分组,统计活跃的天数,包括浏览、收藏、加购、购买
2
3counts_30_active = df.groupby("user_id")["date"].nunique()
4
5labels = pd.merge(labels,counts_30_active,how="left",on="user_id")
6
7labels.rename(columns={"date":"counts_30_active"},inplace=True)
8
9这里pd.nunique()是指返回的是唯一值的个数。
1days_browse = df[df["behavior_type"]==1].groupby("user_id")["date"].max().apply
2
3(lambda x:(datetime.strptime("2014-12-19","%Y-%m-%d")-x).days)
4
5labels = pd.merge(labels,days_browse,how="left",on="user_id")
6
7labels.rename(columns={"date":"days_browse"},inplace=True)
- datetime.strptime("2014-12-19","%Y-%m-%d")-x).days:该部分属于lambda中的函数表达式部分,即计算规则,此处最后取相减后的天数总和。
- apply():格式为apply(func,*args,**kwargs),当一个函数的参数存在于一个元组或者一个字典中时,可间接调用这个函数,并将元组或者字典中的参数按照顺序传递给该函数,返回值就是func函数的返回值。相当于循环遍历,起到处理每一条数据的效果。
1df_interval_buy = df[df["behavior_type"]==4].groupby(["user_id","date"]).item_
2
3 id.count().reset_index()
4
5interval_buy = df_interval_buy.groupby("user_id")["date"].apply
6
7(lambda x:x.sort_values().diff(1).dropna().head(1)).reset_index()
8
9interval_buy["date"] = interval_buy["date"].apply(lambda x : x.days)
10
11interval_buy.drop("level_1",axis=1,inplace=True)
12
13interval_buy.rename(columns={"date":"interval_buy"},inplace=True)
14
15labels = pd.merge(labels,interval_buy,how="left",on="user_id")
1df_browse_buy=df.loc[(df["behavior_type"]==1)|(df["behavior_type"]==4),
2
3["user_id","item_id","behavior_type","time"]]
4
5browse_not_buy=pd.pivot_table(df_browse_buy,index=["user_id","item_id"],
6
7columns=["behavior_type"],values=["time"],aggfunc=["count"])
8
9browse_not_buy.columns = ["browse","buy"]
10
11browse_not_buy.fillna(0,inplace=True)
12
13# 添加了一列browse_not_buy,初始值为0。
14
15browse_not_buy["browse_not_buy"] = 0
16
17# 浏览数>0,购买数=0的数据输出1.
18
19browse_not_buy.loc[(browse_not_buy["browse"]>0) & (browse_not_buy["buy"]==0),
20
21 "browse_not_buy"] = 1
22
23browse_not_buy=browse_not_buy.groupby("user_id")["browse_not_buy"].sum().reset_
24
25 index()
26
27labels = pd.merge(labels,browse_not_buy,how="left",on="user_id")
28
29labels["browse_not_buy"] = labels["browse_not_buy"].apply(lambda x: "是" if x>0
30
31 else "否")
- |:在Python语句中表示或,&表示且。
- pd.pivot_table():透视表功能,df_browse_buy为data块,values可以对需要的计算数据进行筛选,aggfunc参数可以设置我们对数据聚合时进行的函数操作。
- fillna:会填充NaN数据,返回填充后的结果,inplace=True代表原地填充。
1df_cart_buy=df.loc[(df["behavior_type"]==3)|(df["behavior_type"]==4),["user_
2
3 id","item_id","behavior_type","time"]]
4
5cart_not_buy=pd.pivot_table(df_cart_buy,index=["user_id","item_id"],columns=
6
7 ["behavior_type"],values=["time"],aggfunc=["count"])
8
9cart_not_buy.columns = ["cart","buy"]
10
11cart_not_buy.fillna(0,inplace=True)
12
13cart_not_buy["cart_not_buy"] = 0
14
15cart_not_buy.loc[(cart_not_buy["cart"]>0) & (cart_not_buy["buy"]==0),"cart_not_
16
17 buy"] = 1
18
19cart_not_buy = cart_not_buy.groupby("user_id")["cart_not_buy"].sum().reset_index()
20
21labels = pd.merge(labels,cart_not_buy,how="left",on="user_id")
22
23labels["cart_not_buy"] = labels["cart_not_buy"].apply(lambda x: "是" if x>0
24
25 else "否")
1buy_again = df[df["behavior_type"]==4].groupby("user_id")["item_id"].count().
2
3 reset_index()
4
5buy_again.rename(columns={"item_id":"buy_again"},inplace=True)
6
7labels = pd.merge(labels,buy_again,how="left",on="user_id")
8
9labels["buy_again"].fillna(-1,inplace=True)
10
11# 未购买的用户标记为"未购买",有购买未复购的用户标记为"否",有复购的用户标记为"是"
12
13labels["buy_again"] = labels["buy_again"].apply(lambda x: "是" if x>1 else
14
15 "否" if x==1 else "未购买")
1user_active_level = labels["counts_30_active"].value_counts().sort_index(ascending=
2
3 False)
4
5plt.figure(figsize=(16,9))
6
7user_active_level.plot(title="30天内访问次数与访问人数的关系",fontsize=18)
8
9plt.ylabel("访问人数",fontsize=14)
10
11plt.xlabel("访问次数",fontsize=14)
12
13# 用于显示中文
14
15plt.rcParams["font.sans-serif"] = ["SimHei"]
16
17plt.rcParams["axes.unicode_minus"] = False
18
19# 先将user_active_level全部设置成高,再搜索数值<16的部分,设置成低
20
21labels["user_active_level"] = "高"
22
23labels.loc[labels["counts_30_active"]<=16,"user_active_level"] = "低"
- value_counts():查看表格某列中有多少个不同值,并计算每个不同值在该列中有多少重复值。
- sort_index():按照某一列的大小进行排序,ascending=False是按照从大到小排序。
- plt.figure(figsize=(a,b)):创建画板,figsize代表宽为a,高为b的图形,单位为英寸。
- plt.ylabel:设置y轴,fontsize是字体大小。
- plt.xlabel:设置x轴。
1buy_active_level = labels["counts_30_buy"].value_counts().sort_index(ascending=
2
3 False)
4
5plt.figure(figsize=(16,9))
6
7buy_active_level.plot(title="30天内购买次数与购买人数的关系",fontsize=18)
8
9plt.ylabel("购买人数",fontsize=14)
10
11plt.xlabel("购买次数",fontsize=14)
12
13labels["buy_active_level"] = "高"
14
15labels.loc[labels["counts_30_buy"]<=14,"buy_active_level"] = "低"
1buy_single=df[df["behavior_type"]==4].groupby("user_id").item_category.nunique()
2
3.reset_index()
4
5buy_single.rename(columns={"item_category":"buy_single"},inplace=True)
6
7labels = pd.merge(labels,buy_single,how="left",on="user_id")
8
9labels["buy_single"].fillna(-1,inplace=True)
10
11labels["buy_single"] = labels["buy_single"].apply(lambda x: "是" if x>1 else
12
13 "否" if x==1 else "未购买" )
1last_buy_days = labels["days_buy"].value_counts().sort_index()
2
3plt.figure(figsize=(16,9))
4
5last_buy_days.plot(title="最后一次购买距今天数与购买人数的关系",fontsize=18)
6
7plt.ylabel("购买人数",fontsize=14)
8
9plt.xlabel("距今天数",fontsize=14)
1labels["buy_days_level"] = "高"
2
3labels.loc[labels["days_buy"]>8,"buy_days_level"] = "低"
4
5labels["rfm_value"] = labels["buy_active_level"].str.cat(labels["buy_days_level"])
6
7def trans_value(x):
8
9 if x == "高高":
10
11 return "重要价值客户"
12
13 elif x == "低高":
14
15 return "重要深耕客户"
16
17 elif x == "高低":
18
19 return "重要唤回客户"
20
21 else:
22
23 return "即将流失客户"
24
25labels["rfm"] = labels["rfm_value"].apply(trans_value)
26
27# 此处的apply()调用了一个自己定义(def)的函数
28
29labels.drop(["buy_days_level","rfm_value"],axis=1,inplace=True)
30
31labels["rfm"].value_counts()
- str.cat()是指将两个独立的字符串拼接,此处将
- "buy_active_level‘和"buy_days_level"拼接。如果要在两个合并的列中间加一个分隔符号,可在cat括号内加:sep="-",用-连接合并内容。