数据来源于:https://tianchi.aliyun.com/dataset/dataDetail?dataId=46&userId=1
~~~~ 用户在网上购物会产生了一系列的行为,个人的一次行为对用户个人来说仅仅是一次简单的操作,但他所代表的是一大类人群对商品的一类交互行为。我们通过追踪和记录用户的一系列包括点击、收藏、加入购物车、下单、付款等行为,以监控和研究商品购买过程中的问题与异常点的发掘,迅速锁定需要重点关注的用户,有利于运营方的精准运营并且对业务有更正确的理解和判断。
~~~~ 本数据集来源于天池关于淘宝APP在某一个月中的数据,数据包含了用户所浏览、收藏、加入购物车、和购买商品的行为,以及所对应的时间和商品种类。首先对数据进行整体的分析。
~~~~ 首先调用分析过程用到的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import timedelta
import random
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
读取数据
df = pd.read_csv('tianchi_mobile_recommend_train_user.csv')
df.head()
~~~~ 数据总共包含了六列,依次为用户id、商品id、用户行为类型、用户地理位置、商品大类id、时间。其中用户行为类型中1代表点击(当做pv),2代表collect(收藏),3代表cart(加入购物车)
在本次用户行为数据分析中,根据数据主要关注以下几个方面:
1)用户进行用户行为的时间趋势,熟悉用户行为的时间模式;
2)从pv到buy的路径分析,各个环节中的转化率分析,为精准运营作参考;
3)用户典型路径挖掘,一个购买过程中会产生多少次用户行为,以及购买路径的先后顺序;
4)根据用户特征进行聚类,根据不同类型用户进行行为分析,有利于锁定目标用户。
以下是整个分析过程
##数据处理
#对时间特征处理,添加每日时间段,星期特征
df.time = pd.to_datetime(df['time'])
df['daily'] = df['time'].dt.time
df['weekday'] = df['time'].dt.weekday
df['date'] = df['time'].dt.date
#将用户行为用特定行为名称描述
behavior = {1:'pv',2:'collect',3:'cart',4:'buy'}
df['behavior_type'] = df['behavior_type'].replace([1,2,3,4],['pv','collect','cart','buy'])
del df['user_geohash'] #删除地理位置数据,在本次分析中不会用到
##数据分析
####1)时间维度上的分析
table_time = df.groupby(['daily','behavior_type']).size().unstack()
table_time1 = df.pivot_table(index='daily',columns='behavior_type',aggfunc='count',margins=True,margins_name='Sum')
table_time1 = table_time1['date']
table_time.plot.bar(stacked=False,rot=45,figsize=(16,7))
plt.figure(figsize=(16,7))
sns.barplot(x=table_time1.index[:-1],y = table_time1['Sum'][:-1])
plt.xticks(rotation=45)
根据每日不同时间段进行汇总统计,在处于白天的时间,用户行为相对比较平稳,而从下午五点开始,用户行为激增并且一直持续到晚上十一点之前,表明在晚上是用户发生用户行为的高峰期。但是由于pv量显著高于其他用户行为的发生,因此需要根据不同行为进行研究。
for i in table_time1.columns:
plt.figure(figsize=(16,7))
sns.barplot(x=table_time1.index,y = table_time1[i])
plt.xticks(rotation=45)
~~~~ 可以看出,点击、收藏、加入购物车的数量都在晚上存在明显的提升,但是相对而言在购买上晚上的提升没有其他用户行为显著。这方面我认为可能存在以下解释:首先晚上可能用户有大量的时间来上app进行浏览等,但并不一定会立马下单付款;而在白天当中有部分需求来自于工作日常的需要,能够使得订单量增多。
接下来对一个月整个的情况进行分析
table_date = df.groupby(['date','behavior_type']).size().unstack()
table_date.plot(rot=45,figsize=(16,7))
for i in behavior:
plt.figure(figsize=(16,7))
plt.subplot(4,1,behavior.index(i)+1)
table_date[i].plot(rot=45)
plt.ylabel('Count of '+ i)
plt.axhline(table_date[i].mean(),linestyle='--',color='r')
~~~~ 可以看到双12这天用户行为暴增,其中buy是双十二之前都属于非常平稳然后在这一天全部激增,而在当时没有像现在这样存在双十一之前便可以预定付尾款的形式,因此所有购买行为全部会堆积到这一天。在12月初,收藏量便逐步增加,表明进入12月便开始了活动的预热。而加入购物差以及点击则在双十二前三日左右开始上升。
~~~~ 从这些数据可以粗略地了解当时的情景,并且能够有效为以后的活动运营提供一些建议,重点研究活动期间数据可以为后续提供指导,针对活动数据可以分析预热阶段收藏,加入购物车后在双十二的购买率,活动发放的优惠与购买率的联系等等。
接下来针对每周分析用户行为数据
table_week = df.groupby(['weekday','behavior_type']).size().unstack().reset_index()
table_week['weekday'] = table_week['weekday'].replace([0,1,2,3,4,5,6],['星期日','星期一','星期二','星期三','星期四','星期五','星期六'])
table_week = table_week.set_index('weekday')
table_week.plot.bar(rot=0,figsize=(16,7))
week_withoutpv = table_week.loc[:,['buy','collect','cart']]
week_withoutpv.plot.bar(rot=0,figsize=(16,7))
~~~~ 从图中可以看出在工作日当中pv量高于周末时候,这与原本预想的不太一样。双十二当天为星期四,对结果也造成了一定影响,可以看出星期四的购买量显著高于其余时刻,而当天收藏量减少也与双十二为星期四有关。
从时间维度我们可以得出以下几点:
1.用户对商品的浏览,收藏,加入购物一般在晚上7点到11点最多,但是在这期间购买量并没有比白天有显著的提升;
2.双十一活动促销明显改变了一段时间内的用户行为,根据活动类型使得双十二之前几天的用户浏览、收藏、加入购物车行为猛增,而在双十二当天购买量成倍数增长。
3.非工作日的购买量低于工作日。
对工作日白天晚上进行观察
####2)用户行为路径分析
def road_pv_buy(df,datetime):
df1 = df.groupby([datetime,'behavior_type']).size().unstack()
convs = []
for i in range(len(df1.index)):
pv2collect = df1.iloc[i,:]['collect']/df1.iloc[i,:]['pv']
pv2cart= df1.iloc[i,:]['cart']/df1.iloc[i,:]['pv']
collect2cart = df1.iloc[i,:]['cart']/df1.iloc[i,:]['collect']
collect2buy = df1.iloc[i,:]['buy']/df1.iloc[i,:]['collect']
cart2buy = df1.iloc[i,:]['buy']/df1.iloc[i,:]['cart']
conv = [pv2collect,pv2cart,collect2cart,collect2buy,cart2buy]
convs.append(conv)
data = pd.DataFrame(convs,index = df1.index,columns = ['pv2collect','pv2cart','collect2cart','collect2buy','cart2buy'])
return data
dict_road = road_pv_buy(df,'daily')
dict_road = road_pv_buy(df,'daily')
dict_road.loc[:,['collect2buy','cart2buy']].plot(figsize=(14,5))
plt.ylabel('conversion rate',size = 15)
plt.figure(figsize=(10,7))
dict_road.loc[:,['pv2collect','pv2cart','pv2buy']].plot(figsize=(14,5))
plt.ylabel('conversion rate',size = 15)
~~~~ 路径上做了粗略的分析,并没有考虑collect和cart之间的先后顺序,用户点击商品之后可能收藏,可能加入购物车都当成一类。
~~~~ 可以看到,从早上八点开始购买率上涨,随后在中午达到顶峰,然后下降直到下午五六点,在工作时期,由于企业公司的需求使得购买率上升,也许这时候用户往往选择的是他们需要的东西,在这期间如何迅速满足用户的需求是商品所应该关注的。在晚上,购买率下降,而收藏率,加入购物车的转化率上升,在大部分用户处于休息期间时候,浏览可能补充或者额外的东西是重点,这时候商品不仅仅关注需要方面,还需要根据不同商品研究这段期间用户的购买习惯从而发掘出规律,以对商品属性做指导。
dict_road_week = road_pv_buy(df,'weekday')
dict_road_week
同样由于受到双十二的影响,用户购买率远高于其余日期。
def road_finding(df):
pv2col = 0
pv2cart = 0
col2cart = 0
cart2col = 0
pv2buy = 0
item_dict = defaultdict(list)
data = df.sort_values(['user_id','date']).reset_index()
cur = data['user_id'][0]
count = 0
for i in range(len(data)):
#print(data['user_id'][i])
if data['user_id'][i] == cur:
behave = data['behavior_type'][i]
if behave == 'pv':
count+=1
if item_dict[data['item_id'][i]]:
if len(item_dict[data['item_id'][i]]) == 1 and list(item_dict[data['item_id'][i]])[0] == 'pv':
if behave == 'collect':
pv2col += 1
if behave == 'cart':
pv2cart += 1
if behave == 'buy':
pv2buy += 1
item_dict[data['item_id'][i]] = item_dict[data['item_id'][i]].add(data['behavior_type'][i])
else:
item_dict[data['item_id'][i]] = set([data['behavior_type'][i]])
else:
item_dict = defaultdict(list)
cur = data['user_id'][i]
item_dict[data['item_id'][i]] = set([data['behavior_type'][i]])
return [pv2col,pv2cart,pv2buy,count]
sample = random.sample(users,500)
dft = df[df['user_id'].isin(sample)]
road = road_finding(dft)
road
[5214, 6639, 2102, 556358]
[5949, 7607, 2206, 620156]
[4889, 6252, 2219, 555777]
~~~~ 由于之前是对路径作粗略地分析,这里做了更细致的处理,例如点击之后是收藏还是加入购物车;随机采取500个用户的样进行分析,这里可以发现用户点击之后若有后续行为则大部分是加入购物车,然后是加入收藏和购买。仍然有部分商品用户是点击浏览之后直接购买的,在实际中可以着重研究购买的这类产品是什么以及它们的特性,通过对产品的优化以及精准运营能够加强这部分的转化。另外还可以对收藏到购买以及加入购物到购买的转化分析,有利于监控业务以及精细化运营。
#用户在购买一个大类之前会加入多少个收藏和购物车
def before_buy(df):
users = set(df['user_id'].unique())
con = []
for i in users:
dfi = df[df['user_id'] == i]
dfi_buy = dfi[dfi['behavior_type'] == 'buy'].groupby('item_category').size().reset_index().rename(columns = {0:'buy_count'})
dfi_pv = dfi[dfi['behavior_type'] == 'pv'].groupby('item_category').size().reset_index().rename(columns = {0:'pv_count'})
dfi_tol = pd.merge(dfi_buy,dfi_pv,how = 'outer',on='item_category').reset_index()
dfi_tol = dfi_tol.fillna(0)
con.append(dfi_tol)
cons = pd.concat(con,axis=0,ignore_index=True)
cons_ = cons.groupby('item_category').sum().reset_index()
cons_['conv'] = cons_['buy_count']/cons_['pv_count']
return cons_
before_buy = before_buy(dft)
~~~~ 在用户购买一件商品之前会进行许多类似的点击收藏等行为,每个商品之间差别会比较大,因此在实际中需要针对某一商品进行深入的分析。
~~~~ 由于数据包含了不同种类的各种商品,因此无法进行更为精准的分析。从已有的分析可以看出,用户收藏加入购物车购买的行为仅占到总共的2.5%左右,大部分的用户行为还是对商品点击进行详情页访问,因此怎么加强点击到其他用户行为的转化是一个重点。APP可以优化商品推荐以及精准搜索使得用户能用更少的选择获得心仪的商品。另外,可以观察不同类型人群了解不同类型人群用户行为路径的差异,对转化率异常的人群进行调整。
repurchase=df.groupby(['item_id','user_id']).count()['behavior_type']
for i in range(1,10):
repurchase_rate = len(repurchase[repurchase>i])/len(repurchase)
print('重复',i,'次购买率:',repurchase_rate)
repurchase[repurchase>10].sort_values(ascending=False)
重复 1 次购买率: 0.7151618637804401
重复 2 次购买率: 0.3232069613544463
重复 3 次购买率: 0.16125676992744037
重复 4 次购买率: 0.09330871722569953
重复 5 次购买率: 0.06594886517837788
重复 6 次购买率: 0.04777055386668897
重复 7 次购买率: 0.03652283042281216
重复 8 次购买率: 0.02849066249276708
重复 9 次购买率: 0.022771321964350028
item_id user_id
343684208 5950556 128
127427928 59659116 124
41577581 8440456 120
320987891 71142425 113
109259240 123842164 112
353142251 31663890 102
113797961 70240139 101
25330046 137175187 99
343109466 82448685 99
~~~~ 71%的商品都存在重复购买的现象,而当月重复购买四次以上的比例便只有9%左右了。最高的重复购买能够达到128次,这些相当高次数的购买极有可能是企业公司等的行为,这同时也可以作为筛选用户的一个特征。另外,这类商品的属性以及形式是否能给与其他商品借鉴也是一个值得注意的点。
repurchase_cate = df.groupby(['item_category','item_id','user_id']).count()['behavior_type'].sort_values('item_category')
repurchase_cate = repurchase_cate.reset_index()
high_repeat = list(repurchase_cate[repurchase_cate['behavior_type']>80]['item_category'])
num = defaultdict(int)
lengths = defaultdict(list)
for i in high_repeat:
length = len(repurchase_cate[repurchase_cate['item_category']==i])
lengths[i] = length
num[i] += 1
value = []
for i in high_repeat:
value.append(num[i]/lengths[i])
result = pd.Series(value,index=high_repeat)
result
231 0.00036
436 0.00019
895 0.00018
1838 0.00005
1863 0.00001
1863 0.00001
2953 0.00006
3381 0.00003
4296 0.00007
4830 0.00012
5027 0.00002
5027 0.00002
5232 0.00002
5395 0.00003
5689 0.00002
5894 0.00002
5894 0.00002
6000 0.00002
6513 0.00001
6977 0.00011
7767 0.00010
10392 0.00003
10703 0.00015
10894 0.00002
12090 0.00010
12658 0.00120
12658 0.00120
13230 0.00002
13230 0.00002
13230 0.00002
~~~~ 筛选一些购买次数超过80次所处的大类,这类购买次数超过80次的商品在大类中所占的比列都非常低,因此并不存在由于大类下品种少所造成的选择不足。对于商家商品来说,分析这些商品的特性(属性,价格,品牌,优惠等)并且进行针对性分析相信能够带来销售量的提升。
####3)留存率与活跃度分析
#留存率分析,某日的留存率为用户在当日后N天的仍然在线的用户
def retention(df,n):
date = pd.Series(df.date.unique()).sort_values()[:-n]
retention_rates = []
user = []
for i in date:
new_user = set(df[df.date == i]['user_id'].unique()) - set(user)
user.extend(new_user)
user_n = df[df.date == i+timedelta(n)]['user_id'].unique()
a = 0
for j in user_n:
if j in new_user:
a+=1
#print(a,len(new_user))
retention_rate = a/len(new_user)
retention_rates.append(retention_rate)
#print(len(retention_rates),len(date))
total_retention_rate = pd.Series(retention_rates,index=date)
return total_retention_rate
def au(df,datetime):
#定义活跃度
group = df.groupby('behavior_type').count().reset_index()
user_id = list(set(df.user_id))
user = len(user_id)
pv_mean = float(group[group.behavior_type == 'pv']['user_id']/user)
coll_mean = float(group[group.behavior_type == 'collect']['user_id']/user)
cart_mean = float(group[group.behavior_type == 'cart']['user_id']/user)
buy_mean =float(group[group.behavior_type == 'buy']['user_id']/user)
user_group = df.groupby(['user_id','behavior_type'])['daily'].count().reset_index()
au_users = []
for i in range(user):
#计算pv_mean和buy_mean,大于两倍buy_mean和4倍pv_mean的定义为活跃用户
user_group_i = user_group[user_group['user_id'] == user_id[i]]
if not list(user_group_i[user_group_i['behavior_type']=='pv']['daily']):
continue
pv_mean_i = float(user_group_i[user_group_i['behavior_type']=='pv']['daily'])
if not list(user_group_i[user_group_i['behavior_type']=='buy']['daily']): continue
buy_mean_i = float(user_group_i[user_group_i['behavior_type']=='buy']['daily'])
if pv_mean_i > (pv_mean * 2):
au_users.append(user_id[i])
else:
if buy_mean_i > (2* buy_mean): au_users.append(user_id[i])
#筛选活跃用户
df_au = df[df['user_id'].isin(au_users)]
#计算活跃用户每个时间段的比例
date1 = pd.Series(df[datetime].unique())
data1 = df.groupby(datetime)['user_id'].nunique().reset_index()
data2 = df_au.groupby(datetime)['user_id'].nunique().reset_index()
data2 = data2.rename(columns = {'user_id':'user_id_au'})
data3 = pd.merge(data1,data2,on=datetime)
data3['activity_rate'] = data3['user_id_au']/data3['user_id']
return data3
retention3 = retention(df,3)
retention7 = retention(df,7)
au_user_date = au(df,'date')
(放宽活跃用户条件)
~~~~ 从留存率和活跃度可以得到以下启示:由于数据是一段截取数据,因此前一两天记录的用户可能大多数都是经常上APP的用户,所以留存较高,还从中间的数据可以看出留存率一般维持到40%到50%之间,而双十二作为活动日,大部分新用户都会在这天上线,所对应的留存率也较高。从活跃用户比例可以看出平均每日的活跃度在27%-28%,而双十二当天活跃用户比例下降到24.8%,表明这天普通用户的比例增多。通过更改活跃用户(放宽条件)的定义可以看出,双十二活跃用户比例下降更为明显,针对此类现象,重点研究是活动的某种属性刺激了非活跃用户在双十二这天的用户行为,例如优惠,玩法等。
####4)用户聚类
def cluster(df):
#根据购买行为
data_behavior = df.groupby(['user_id','behavior_type']).count()['item_id'].unstack().reset_index()
data_behavior = data_behavior.fillna(0)
data_behavior1 = data_behavior.copy()
data_behavior1.iloc[:,1:] = standscaler.fit_transform(data_behavior1.iloc[:,1:])
#根据购买时间,1:week(k=3);2:每日时间点(k=3)
data_daily = df.groupby(['user_id','daily']).count()['item_id'].unstack().reset_index()
data_daily = data_daily.fillna(0)
kmeans = KMeans(n_clusters = 3,max_iter = 300,tol = 0.001,init = 'k-means++')
kmeans.fit(data_daily.iloc[:,1:])
y_daily = kmeans.labels_
data_week = df.groupby(['user_id','weekday']).count()['item_id'].unstack().reset_index()
data_week = data_week.fillna(0)
kmeans = KMeans(n_clusters = 3,max_iter = 300,tol = 0.001,init = 'k-means++')
kmeans.fit(data_week.iloc[:,1:])
y_week = kmeans.labels_
data_behavior1['daily_feature'] = y_daily/3
data_behavior1['weekday_feature'] = y_week/3
data_behavior['daily_feature'] = y_daily
data_behavior['weekday_feature'] = y_week
kmeans = KMeans(n_clusters = 6,max_iter = 300,tol = 0.001,init = 'k-means++')
kmeans.fit(data_behavior1.iloc[:,1:])
data_behavior['cluster'] = kmeans.labels_
return data_behavior
~~~~ 在这里我先对聚类用到特征进行了筛选,首先选取了用户四类发生的次数,然后通过每日用户操作以及每周用户操作的用户行为预先做了聚类并将结果用作最终聚类的特征(根据聚类肘部规则将这两者都预先分为了3类),在最终聚类之前作了自定义的标准化操作,聚类类别设置为6类。
n = 6
pd.set_option('display.float_format', lambda x: '%.3f' % x)
for i in range(n):
data_i = data_behavior_total[data_behavior_total['cluster'] == i]
data_i_behavior = data_i[['pv','collect','cart','buy']]
print('第',i+1,'个类别人数为:',len(data_i))
print('第',i+1,'个类别用户行为次数的平均值为:',data_i_behavior.mean())
print('第',i+1,'个类别用户时间维度类别平均:',data_i[['daily_feature','weekday_feature']].mean())
print('总平均值为:',data_behavior_total[['pv','collect','cart','buy']].mean(),data_behavior_total[['daily_feature','weekday_feature']].mean())
#结果如下:
第 1 个类别人数为: 1282
第 1 个类别用户行为次数的平均值为: behavior_type
pv 1633.161
collect 36.981
cart 43.646
buy 17.563
第 1 个类别用户时间维度类别平均: behavior_type
daily_feature 0.541
weekday_feature 0.442
第 2 个类别人数为: 56
第 2 个类别用户行为次数的平均值为: behavior_type
pv 4523.696
collect 63.547
cart 377.321
buy 92.821
第 2 个类别用户时间维度类别平均: behavior_type
daily_feature 1.429
weekday_feature 1.357
第 3 个类别人数为: 2
第 3 个类别用户行为次数的平均值为: behavior_type
pv 22922.000
collect 2767.500
cart 177.500
buy 33.000
第 3 个类别用户时间维度类别平均: behavior_type
daily_feature 2.000
weekday_feature 2.000
第 4 个类别人数为: 344
第 4 个类别用户行为次数的平均值为: behavior_type
pv 3113.215
collect 41.205
cart 152.328
buy 37.930
第 4 个类别用户时间维度类别平均: behavior_type
daily_feature 1.148
weekday_feature 1.058
第 5 个类别人数为: 153
第 5 个类别用户行为次数的平均值为: behavior_type
pv 5199.196
collect 266.164
cart 62.483
buy 21.349
第 5 个类别用户时间维度类别平均: behavior_type
daily_feature 1.634
weekday_feature 1.529
第 6 个类别人数为: 3163
第 6 个类别用户行为次数的平均值为: behavior_type
pv 462.948
collect 12.396
cart 12.813
buy 5.713
第 6 个类别用户时间维度类别平均: behavior_type
daily_feature 0.017
weekday_feature 0.004
总平均值为: behavior_type
pv 1144.722
collect 37.036
cart 39.507
buy 13.226
daily_feature 0.296
weekday_feature 0.251
#将结果可视化出来
plt.figure(figsize = (16,7))
sns.barplot(x=plot['cluster'],y=plot['value'],hue='behavior_type',data = plot)
plt.figure(figsize = (16,7))
sns.barplot(x='cluster',y='value',hue='behavior_type',data = plot[plot['behavior_type'] != 'pv'])
plt.figure(figsize = (16,7))
sns.barplot(x='cluster',y='value',hue='behavior_type',data = plot[plot['behavior_type'] == 'buy'])
plot.groupby('cluster').count()['user_id'].plot.bar(rot = 0)
plt.ylabel('Number of Class ')
~~~~ 可以看出用户类别所占比例最高的为第一类和最后一类,人数分别是1282和3163,这两类人群都是属于用户行为较少的,并且最后一类的平均购买也远低于平均值,从而拉低了总体的平均值,第一类用户的购买仅高于最后一类。由此可以知道绝大部分用户都属于这一部分用户,可以类比与2/8法则中对应的8。
~~~~ 其次是第四类和第五类用户,人数分别是344,155。第四类用户加入购物车多而收藏少,第五类用户点击和收藏多而加入购物车少。
~~~~ 最后人数最少的为第二类和第三类用户,人数分别为56和2人。第二类用户用户行为较多,并且更为重要的是其购买行为明显高于其他类型的用户,可以说这类用户属于高价值用户,高价值需要与其建立稳固的关系,维持活跃度并且及时阻止流失。第三类属于比较极端型的类型,他们的点击浏览行为特别高但是并没有带来所匹配的购买量。
~~~~ 这里用的是随机选取的5000名用户的分类,当数据量增大时,每个群体的特征会更加明显。这样的用户分群能够为业务分析、运营、管理提供更多的信息,有利于精细化运营,并且能够从用户行为上迅速地抓住目标用户。
1)抓住用户的行为习惯,根据不同类型用户精准运营,进行个性化推荐;
2)根据时间维度进行有效地拉新促活活动,以及老用户的访问。根据复购率来刺激用户的持续消费,根据留存监控用户的持续用户行为,防止流失并且对用户行为给与一定的刺激;
3)对高价值用户需要提供优惠策略使其保持活跃度,针对不同用户行为人群制定针对性策略从而刺激进一步的用户行为,针对不活跃用户以及大部分的低购买用户采取相应措施刺激或者提高活跃度。