数据分析项目——电商平台用户画像分析
pre:数据集导入及说明
# 导入数据集
df = pd.read_excel("order_data.xlsx")
# 导入用户详情数据集
df_user = pd.read_excel("user_data.xlsx")
包括用户行为数据和用户基本信息数据:
- 用户行为数据(用户id,行为类型[购买、浏览、收藏、加购],商品id,商品种类,行为日期),共258179条
- 用户基本信息数据(用户id,性别,年龄,城市,省份,婚姻情况,教育程度,工作等),共222条。
- 时间跨度:2014-11-18 到 2014-12-18
分析思路
- 基于用户粒度:对用户按照某种规则打标,便于精准式营销
-
基于群体粒度:分析一个月在该平台活跃的用户整体情况
整个分析思路如下图:
基于用户粒度分析.png
基于群体粒度分析.png
一、数据预处理
重复值处理
缺失值处理
数据格式处理:
日期格式的转换 astype()
1、重复值处理
查看有无重复值:首先看使用df.info查看各个字段的记录数,再看去除重复值后的数量。以此判断有无重复数据。
去重重复值可以使用函数drop_duplicates()
2、数据格式转换
通过df.info查看数据格式
df.info()
#结果
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 user_id 258179 non-null int64
1 item_id 258179 non-null int64
2 behavior_type 258179 non-null int64
3 item_category 258179 non-null int64
4 time 258179 non-null object
看到time是object类型的,不适合处理。同时我们一般将数据分为日期和时间格式,因此对其进行拆分处理。
- 处理1:将time拆分为两个字段,一个是日期,一个是时间。方便我们打标。
- 处理2:转换格式。
日期格式,可进行日期运算。时间转换为整型,可进行分类。 - 使用的方法:series的str属性(可以类比hql中的substr函数,对其进行切割),to_datetime函数,astype函数(类比hql中的cast函数)
为各个时段打标,将时段分为'凌晨'、'上午'、'中午'、'下午'、'晚上'
知识点:pd.cut函数,区间分割
df['hour'] = pd.cut(df.time, bins=[-1, 5, 10, 13, 18, 23],labels=['凌晨','上午','中午','下午','晚上'])
日期处理结果.png
3、空值的处理
查看空值,并计算数量。可以看到并无空值。
df.isnull().sum()
#返回
user_id 0
item_id 0
behavior_type 0
item_category 0
time 0
dtype: int64
二、用户消费习惯分析
- 用户浏览活跃时间段:该用户喜欢在哪个时间段活跃?
- 用户购物活跃时间段:该用户喜欢在哪个时间段购物?
1、用户浏览/购物活跃时间段
在订单表中按照(用户,时段)分组,取每个时段的记录数(订单数):
# 修改列名item_id为hour_counts
time_browse.rename(columns={'item_id':'hour_counts'},inplace=True)
# 统计每个用户浏览次数最多的时刻
time_browse_max = time_browse.groupby(['user_id']).hour_counts.max().reset_index()
# 进行关联
time_browse = pd.merge(time_browse, time_browse_max, how='inner', on=['user_id','hour')
# 选取各用户浏览次数最多的时刻,如有并列,用逗号连接
time_browse_hour = time_browse.loc[:,'hour'].groupby(time_browse['user_id']).agg(lambda x:','.join(x)).reset_index()
这里的操作流程与hive-sql类似,我们需要先把max算出来,再去关联。
将生成的标签加入标签表:
用户浏览活跃时间.png
同理,可统计出用户购买的活跃时间段,在此不赘述,仅展示结果
用户购物活跃时间.png
2、用户最喜欢的类目
分析用户最喜欢的类目,从而便于我们为其进行推荐。
# 先获取需要的数据集
df_browse = df.loc[df['behavior_type']==1,['user_id','item_id','item_category']]
# 计算每个类目的出现次数
df_cate_browse = df_browse.groupby(['user_id','item_category']).item_id.count().reset_index()
df_cate_browse.rename(columns={'item_id':'cate_counts'},inplace=True)
# 按照user_id计算出现频次最高的类目
df_cate_browse_max = df_cate_browse.groupby('user_id').cate_counts.max().reset_index()
# 关联每个用户的最频繁访问类目
df_cate_browse = pd.merge(df_cate_browse,df_cate_browse_max, how='inner',on=['user_id','cate_counts'])
# 可能存在浏览次数相同的类目,对其进行合并,首先对类型进行转换
df_cate_browse['item_category'] = df_cate_browse['item_category'].astype(str)
df_cate_browse = df_cate_browse.loc[:,'item_category'].\
groupby(df_cate_browse.user_id).agg(lambda x:','.join(x)).reset_index()
# 将其加入用户标签表
labels = pd.merge(labels, df_cate_browse, how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_browse'},inplace=True)
最终得到标签表:
各用户最喜欢类目.png
三、RFM分析
- 用户近30天活跃天数(活跃:有浏览、收藏、加购、购物四种行为之一)
- 用户最近一次购物距今天数:判断用户最近是否活跃
- 用户最近两次消费间隔时间:判断用户的消费频率
1、用户近30天行为分析
通过分析用户分析最近的行为,来判断该用户是否活跃、流失
# 用户近30天的购买次数
df_counts_30_buy = df[df.behavior_type==4].groupby('user_id').item_id.count().reset_index()
# 加入标签表
labels = pd.merge(labels,df_counts_30_buy,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_30_buy'},inplace=True)
用户近30天的购买次数.png
分析近30天的活跃天数,只要有浏览、收藏、加购和购物四种行为之一就认为是活跃。
# nunique表示不同日期的天数,去重计数
counts_30_active = df.groupby('user_id')['date'].nunique()
# 加入标签表
labels = pd.merge(labels,counts_30_active,how='left',on='user_id')
labels.rename(columns={'date':'counts_30_active'},inplace=True)
结果如下:
用户近30天活跃天数.png
我们可以通过查看所有用户30天活跃天数的分布情况,来确认一个分类的标准,判断某个用户是否活跃。
用户30天活跃天数分布图如图:
用户30天活跃天数分布情况.png
总体上看,访问天数多的访客比访问天数少的访客数量多,且以20次左右为拐点,因此定义访问天数小于20次的为低活跃,访问天数大于等于20次的定义为高活跃。此定义只是从用户的分布角度出发,工作中当从业务出发定义是否活跃。
labels['buy_active_level'] = '高'
labels.loc[labels['counts_30_buy']<=19,'buy_active_level'] = '低'
同理,可分析用户近7天的行为,包括其购物次数和活跃天数。
与上述操作类似,展示结果:
用户近7天行为分析.png
2、最后一次行为距今天数
明确今天的日期,在本项目中,“今天”指“2014-12-19”,然后统计订单表中用户某种行为的最大日期与当前日期的差值。
使用的函数是:datetime.strptime: new datetime parsed from a string (like time.strptime()) ,将字符串转为date格式,以方便我们做日期运算
days_buy = df[df['behavior_type']==4].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2014-12-19','%Y-%m-%d')-x).days)
#加入标签表
labels = pd.merge(labels,days_buy,how='left',on='user_id')
labels.rename(columns={'date':'days_buy'},inplace=True)
结果如图:
用户最后一次行为距今天数.png
3、最后两次购买行为间隔天数
使用日期的diff函数
# 首先对user_id和date进行分组去重
df_interval_buy = df[df.behavior_type==4].groupby(['user_id','date']).item_id.count().reset_index()
# 然后对date降序排列,对其进行差分,去空后保留第一行
df_interval_buy = df_interval_buy.groupby('user_id').date.apply(lambda x: x.sort_values().diff(1).dropna().head(1)).reset_index()
df_interval_buy['date'] = df_interval_buy['date'].apply(lambda x: x.days)
df_interval_buy.drop('level_1',axis=1,inplace=True)
df_interval_buy.rename({'date':'interval_buy'},inplace=True)
# 将其加入标签
labels = pd.merge(labels,df_interval_buy,how='left',on='user_id')
labels.head()
返回结果
用户最后两次购买行为间隔天数png
4、RFM分组
RFM分组是指按照最近一次消费 (Recency)、消费频率 (Frequency)和消费金额(Monetary)进行分组
最近一次消费,我们按照最后一次购物距今的天数进行分组,同样是观看用户的一个分布情况,然后看如何进行分类。
消费频率,这里放宽要求,按照用户的30天活跃天数进行分类
消费金额,本次不涉及金额的统计。
因此,我们可以根据活跃度分类和最近一次消费将用户划分为四类:
- 最近一次消费距今天数(近)活跃度(高):重要价值客户
- 最近一次消费距今天数(远)活跃度(高):重要唤回客户。历史活跃度比较高但是最近没买,说明存在流失的可能,要唤回他。
- 最近一次消费距今天数(近)活跃度(低):重要深耕客户。最近购买了,但是活跃度不是很高。是我们要努力留住的客户。
- 最近一次消费距今天数(远)活跃度(低):即将流失客户。历史活跃度比较低且最近没购买,可能用户就要跑路了。
首先看一下最近一次购物距今天数用户的分布情况,看一下怎么分类。
last_buy_days = labels['days_buy'].value_counts().sort_index()
# 图形绘制
plt.figure(figsize=(16,9))
last_buy_days.plot(title='最后一次购买距今天数与人数的关系',fontsize=18)
plt.ylabel('购买人数',fontsize=14)
plt.xlabel('距今天数',fontsize=14)
结果如图
最后一次消费距今天数分布情况.png
可以看到在第8天前后,用户有明显的分层,因此按照8天对其进行划分,最后一次消费距今天数小于8天的,认为最近有消费,大于8天的认为最近无消费
labels['buy_days_level'] = '高'
labels.loc[labels['days_buy']>8,'buy_days_level'] = '低'
#利用series的字符串属性对值进行拼接
labels['rfm_value'] = labels['buy_active_level'].str.cat(labels['buy_days_level'])
#自定义函数
def trans_value(x):
if x == '高高': ## 高活跃且最近购买
return '重要价值客户'
elif x == '低高': ## 低活跃且最近购买(要继续保持,所以要深耕)
return '重要深耕客户'
elif x == '高低': ##高度活跃但是最近没购买,要唤回
return '重要唤回客户'
else: ##不活跃且最近没有购买
return '即将流失客户'
labels['rfm'] = labels['rfm_value'].apply(trans_value)
返回结果:
用户RF分层.png
通过value_counts查看每个值的出现次数:
labels['rfm'].value_counts()
#返回
重要深耕客户 114
重要价值客户 67
即将流失客户 40
重要唤回客户 1
四、潜在购物行为分析
- 是否浏览未下单
- 是否加购未下单
提取bahavior=1或者behavior=4的数据,根据用户id、商品id以及用户行为来进行分组,统计每个用户的浏览情况和购买情况,每一个用户对于每一件商品,如果浏览了但是未购买,则将其记为1。
该标签基于的粒度是(用户,商品),若用户对某一商品浏览未下单,可相应地进行运营策略的投放。
下面的方法使用了数据透视表,数据透视表可以实现类似于列转行的功能,将原来的数据变为维度,如在下列的方法中,我们将behavior_type变为了维度。
#提取浏览和购买的行为数据
df_browse_buy = df.loc[(df.behavior_type==1) | (df.behavior_type==4),['user_id','item_id','behavior_type','time']]
#使用数据透视表的方法打标浏览未购买的行为
df_browse_buy.head()
browse_not_buy = pd.pivot_table(df_browse_buy,index=['user_id','item_id'], columns=['behavior_type'],values=['time'],aggfunc=['count'])
上述返回的结果值是:
1628926422.png
如果值为空,说明无相应的行为,对浏览未购买的数据记录进行打标
以上的步骤到商品粒度,下面对用户进行统计,即只要用户有浏览未下单的行为就对其进行打标
browse_not_buy = browse_not_buy.groupby('user_id').browse_not_buy.sum().reset_index()
labels = pd.merge(labels, browse_not_buy,how='left',on='user_id')
返回的结果如图:
用户浏览未购买分析.png
加购未下单同上述步骤
五、用户的基本属性分析
分析平台用户的年龄、学历、性别、城市、职业、婚姻情况等,以便于我们去分析我们的典型用户是什么。
1、性别
user_sex = df.groupby('gender').user_id.count()
# autopct设置显示百分比,explode 表示各项距离圆心n个半径
user_sex.plot.pie(y="user_id",figsize=(10,5),explode=[0.01,0.01],autopct='%1.1f%%',title="性别分布图")
用户性别比例.png
从上图可以看出,平台用户中男性居多,女性较少。
2、年龄
值的分层可以使用pd.cut函数
# 用户的年龄分布
bins = [0,20,25,30,35,40,45,50,55,200]
labels =["(0-20]","(20-25]","(25-30]","(30-35]","(35-40]","(40-45]","(45-50]","(50-55]","55岁以上"]
df['age_group'] = pd.cut(df.age, bins=bins, labels=labels)
# 绘制柱状图
age_group = df.groupby('age_group').user_id.count()
age_group.plot.bar(figsize=(10,5),x='age_group',y='user_id',title='用户年龄分布',rot=0)
用户年龄分布.png
从年龄分布图中,可以看出平台的用户以25岁-40岁的用户居多。青年人和中老年用户较少。
3、地域分布
# 地域分布
province_df = df.groupby('province').user_id.count().reset_index().sort_values(by='user_id',ascending=True)
province_df.plot.barh(figsize=(10,5),x='province',y='user_id',legend=False)
# 根据城市排序
city_df = df.groupby('city').user_id.count().reset_index().sort_values(by='user_id',ascending=True)
city_df.plot.barh(x='city', y='user_id',legend=False)
返回结果如下图,从结果中可以看到来自北上广深的用户占绝大部分比例。
省份分布.png
城市分布.png
4、婚姻
从婚姻分布情况中可以看到,平台用户中有超过60%的用户为已婚。
婚姻分布.png
5、学历
使用该平台的用户大部分拥有大学及以上的学历
学历分布.png
6、职业
使用该平台的用户大部分为互联网从业人群
职业分布.png
六、用户购物活跃分析
6.1 用户购物时间
首先查看这一个月的时间跨度用户每天的购物情况:
每天下单用户数.png
从上图可以看出,该平台每天的下单用户数比较平稳,有一些周期性因素在其中,可能是每周周中和周末的购物情况不同。同时,注意到12.12这一天购物用户激增,可能是12.12购物节的因素。
再分析下用户喜欢在周几下单,哪个时间下单
购物日期分布.png
购物时间分布.png
从图中我们可以看到用户的喜欢的购物时间:周五,晚上9点。上午购物的人数都比较少,主要集中在下午和晚上。
6.2 下单用户分层
通过用户的下单情况,我们可以对每周的下单用户进行分层,将其分为活跃用户、不活跃用户和回流用户。
- 活跃用户:上周消费,本周也消费
- 回流用户:上周没消费,本周消费
- 不活跃用户:上周没消费,本周没消费
首先使用date中的weekofyear函数判断当前日期是本年的第几周,然后根据上一周和本周的活跃情况对用户进行分层。判断分层的代码如下:
def active_status(data):
status = []
for i in range(5):
# 本周有活跃
if data[i+47] ==1:
if len(status) > 0:
if status[i-1] == 'unactive':
status.append('return')
else:
status.append('active')
else:
status.append('active')
# 本周不活跃
else:
status.append('unactive')
最终结果
用户活跃分层.png
通过面积图看一下每周不同类型用户的比例情况:
不同类型用户比例.png
从图中可以看到,每周活跃的用户数比较平稳
6.3每周复购率分析
复购率:一周内购买多次(>1)的用户占总用户的比例
每周复购率.png
从中可以看到,5周的复购用户都在50%以上,47周和51周的复购率较低,可能是因为数据不完整的原因。
7基于品类的分析
7.1 用户最喜欢品类分析
用户最喜欢购买的品类?
用户最多收藏的品类?
用户购物最多品类TOP5.png
用户收藏最多品类TOP5.png
从结果可见,用户最喜欢购买的品类前五名是6344、12189、5232、1863和4370,用户收藏最多的品类是13230、5894、1863、6513和5027。
7.2 各商品的浏览/购物转换率
转换率=浏览该商品的用户数/购买该商品的用户数
最终结果如图:
各品类转换率.png
查看转换率的分布情况:
转换率情况.png
由于数据原因,存在一人浏览一人购买的情况,所以有部分的转换率为100%。除去此部分数据,大部分的商品转换率在0.5以下。