社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【Python】pandas实战:电商平台用户分析

机器学习初学者 • 2 年前 • 287 次点击  

分享一个《pandas进阶宝典》最新更新的实战项目,电商平台用户行为分析。以下是内容节选。

数据分析

1.行为概况

首先,我们要对用户的行为类型有一定的理解,了解每个行为所代表的含义。

  • 浏览:作为用户与商品接触的第一个行为,它的数量级与其他行为类型相比而言是非常庞大的,因为:
    • 用户购买之前需要货比三家,可能会浏览很多个商品最后只下单一个,此时就是多个浏览对应一个下单。
    • 但大部分用户可能只是浏览了很多商品,但最终没下单的,此时就是多个浏览对应零个下单。
  • 收藏:代表用户对商品有了一定程度的意向,但不一定有购买计划。
  • 加购物车:代表用户对商品的购买意向进一步加强,但由于某种原因还在犹豫没最终决定,这个环节的成单率极高。
  • 下单:代表交易达成,此时新用户已成为老用户,老用户继续产生价值。
  • 评论:代表用户对商品反馈的活跃程度。

需求1:对所有行为类型统计数量和占比

# 行为类型数量统计
ser_type_cnt = df['type_map'].value_counts()
# 行为类型数量占比
ser_type_pct = df['type_map'].value_counts(normalize=True)
# 合并
pd.concat([ser_type_cnt, ser_type_pct],axis=1).style.format("{:.2%}",subset=['proportion']).bar(subset='proportion')

观察结果:

  • 浏览行为占75.66%,印证了我们前面对的浏览行为的理解。
  • 加购物车占14.65%,下单仅占了6.97%,可以推出一些权益活动,吸引客户下单,提高下单率。
  • 此外用户评论和收藏的占比也较低,分别占2.46%和1.16%,应增强与用户之间的互动,提高评论数量和收藏率。

2.流量指标分析

指标设计可以很好的帮助我们了解业务状况、发现业务问题和异常。比如,xx企业将日活用户数作为北极星指标,针对该指标向下拆解各种细分指标,然后对各种细分指标进行优化提升,从而最终提升北极星指标,业绩大幅提升。

下面针对该项目数据中的行为类型设计一些常用的流量指标,用来监控用户行为的流量变化,比如浏览总量、浏览人数、日均访问量、人均访问数、消费用户人均访问量、消费用户人均消费次数、复购率等等。

访问量指标

针对浏览行为可以统计出一段时间内的访问量大小,即流量有多少。下面对数据中全部用户计算访问量的指标。

  • pv(page view)总访问量:一段时间内全部访问量的总和
  • uv(user view)总访客数:一段时间内全部访客的总和,注意用户是单独的个体因此需要计算唯一值
  • pv_per_day 日均访问量:一段时间内平均每天的访问量
  • pv_per_user 人均访问量:每个用户的平均访问量

需求2:计算出以上所有用户的访问量指标

# 总访问量
pv = df[df['type_map'] == '浏览']['user_id'].count()
print('总访问量:{}'.format(pv))

# 总访客数
uv = df['user_id'].nunique()
print('总访客数:{}'.format(uv))

# 日均访问量
pv_per_day = pv / df['date'].nunique()
print('日均访问量:%.0f' %pv_per_day)

# 人均访问量=总访问量/总访客数
pv_per_user = pv / uv
print('人均访问量:%.2f' %pv_per_user)
------
总访问量:2757485
总访客数:440373
日均访问量:393926
人均访问量:6.26

消费用户流量指标

以上是对全部用户的流量指标,如果我们想分析消费用户群体,可以针对消费用户设计相关的流量指标,比如有:

  • user_pay 消费用户数量:一段时间内有下单行为的总用户数量
  • pv_pay 消费用户总访问量:一段时间内所有消费用户的浏览数量总和
  • user_pay_rate 消费用户数占比:消费用户数占总用户数的比例
  • pv_per_buy_user 消费用户人均访问量:一段时间内每个消费用户浏览数量的均值

需求3:计算出以上消费用户的流量指标

# 消费用户的唯一id
user_pay = df[df['type_map'] == '下单']['user_id'].unique()
print('消费用户数量:{}'.format(len(user_pay)))

# 消费用户的总访问量
pv_pay = df[df['user_id'].isin(user_pay)]['type_map'].value_counts()['浏览']
print('消费用户总访问量:{}'.format(pv_pay))

# 消费用户数占比
user_pay_rate = len(user_pay) / uv
print('消费用户数占比:%.2f%%' %(user_pay_rate*100))

# 消费用户人均访问量
pv_per_buy_user = pv_pay / len(user_pay)
print('消费用户人均访问量:%.2f' %pv_per_buy_user)

# 消费用户人均消费次数
cnt_per_buy_user = df[df['type_map'] == '下单'].shape[0]/len(user_pay)
print('消费用户人均消费次数:%.2f' %cnt_per_buy_user)
------
消费用户数量:187549
消费用户总访问量:1400380
消费用户数占比:42.59%
消费用户人均访问量:7.47
消费用户人均消费次数:1.18

复购率

复购行为指的是一个客户的历史下单次数至少一次以上,即再第一次购买后,过了一段时间又有了第二次、第三次购买行为。

需求5:计算出下单次数一次以上的客户占总下单人数比例

注意:这里的复购逻辑是以人为维度,可以包括不同sku商品的下单,只要该用户总下单次数>1就算复购。因此我们对下单的用户分组统计总下单次数,然后筛选出下单次数>1的客户占总下单客户数的比例。

# 每个用户消费总次数
df_pay_num = df[df['type_map']=='下单'].groupby(['user_id'])['type'].count().to_frame(name='num')
# 消费次数>=2的客户数
repay_num = df_pay_num[df_pay_num['num']>1].shape[0]
# 消费总客户数
total_pay_num = df_pay_num.shape[0]
# 复购率
repay_rate =round(repay_num/total_pay_num,4)
print('用户维度的复购率:%.2f%%' %(repay_rate*100))
------
用户维度的复购率:11.55%

对于用户维度的复购率而言,11.55%是比较低的,有很大的提升空间。可以联合运营部门设计一些权益活动吸引老客下单,提升复购率。

需求6:计算出每个商品下单次数一次以上的客户占该商品总下单人数比例

以上的复购率口径是人的维度,只要在电商平台下单次数大于1次就算复购。如果我们从商品的角度出发,也可以分析每个商品的复购率。

# 对用户和商品id分组统计下单的次数
df_sku_user_pay = df[df['type_map']=='下单'].groupby(['sku_id','user_id'], as_index=False)['type'].count()
df_sku_user_pay.head()
# 筛选每个商品下用户下单次数大于1的
df_sku_user_repay = df_sku_user_pay[df_sku_user_pay['type']>1]
df_sku_user_repay.head()
# 对商品分组统计下单次数大于1的客户数
df_sku_repay_cnt = df_sku_user_repay.groupby(['sku_id'], as_index=False)['user_id'].count().sort_values(by='user_id',ascending=False)
# 对商品分组统计下单的总客户数
df_sku_pay_cnt = df.groupby(['sku_id'], as_index=False)['type'].count()
# 商品无复购客户的填充为0,计算复购率,按复购数量倒序排序
df_sku_repay_rate = (pd.merge(df_sku_pay_cnt,df_sku_repay_cnt, on='sku_id', how='left')
                        .fillna(0)
                        .rename(columns={'type':'pay_cnt','user_id':'repay_cnt'})
                        .assign(repay_rate=lambda x:x['repay_cnt']/x['pay_cnt'])
                        .sort_values(by='repay_cnt',ascending=False))
df_sku_repay_rate.head(10)

以上就得到了每个sku商品的复购率。下面通过可视化看下复购率大于0的分布状况,是明显的有偏分布,大多数sku的复购率都在10%以内,极少数高比如50%是因为总下单数量小,没有统计意义。

plt.subplots(figsize=(155))
df_sku_repay_rate.loc[df_sku_repay_rate['repay_rate']>0,'repay_rate'].hist(bins=50)
plt.show()
sku_repay_avg = df_sku_repay_rate.loc[df_sku_repay_rate['repay_rate']>0,'repay_rate'].mean()
sku_repay_med = df_sku_repay_rate.loc[df_sku_repay_rate['repay_rate']>0 ,'repay_rate'].median()
sku_repay_max = df_sku_repay_rate.loc[df_sku_repay_rate['repay_cnt']>0,'repay_rate'].max()
print('sku商品复购率大于0的均值:%.2f%%' %(sku_repay_avg*100))
print('sku商品复购率大于0的中位数:%.2f%%' %(sku_repay_med*100))
print('sku商品复购率的最大值:%.2f%%' %(sku_repay_max*100))
------
sku商品复购率大于0的均值:3.82%
sku商品复购率大于0的中位数:1.61%
sku商品复购率的最大值:50.00%

3.转化漏斗分析

浏览->收藏->加购物车->下单->评论,是一个用户从了解商品->交易产品->熟悉产品的过程。在这个过程中,由于每一个转化环节都会有一定流量的损失,因此流量最终会形成一个漏斗。

比如有10个用户浏览了商品,但最后加入购物车或者下单的只有7个,其他3个只是看看并没有后续行为,那么此时浏览到下单的转化率就是70%,其他环节的转化漏斗也是同理。

清楚地了解各环节转化率后,有助于我们发现薄弱环节并进行优化。同样地,各环节转化率也可以作为我们关注的指标,并按周期进行监测。

需求7:计算用户从浏览到下单的转化率及漏斗可视化

# 筛选用户的浏览数据
df_pv = df[df['type_map']=='浏览']
# 筛选客户的下单数据
df_pay = df[df['type_map']=='下单']

cols = ['user_id','sku_id','action_time','type_map']
# 将用户浏览和下单行为进行匹配
df_pv_pay = pd.merge(df_pv[cols], df_pay[cols], on=['user_id','sku_id'], how='inner', suffixes=('_pv''_pay'))
df_pv_pay.head()

以上通过内连接将同用户同商品下存在浏览和下单行为的匹配出来。

但此时的数据还不能直接统计使用,仍需要两个关键的步骤。

第一个是,因为浏览到下单是有先后顺序的,我们的条件是下单时间一定要晚于浏览时间,因此这里我们对这个时间进一步筛选。

# 筛选下单时间在浏览时间之后的数据
t_0 = df_pv_pay.shape[0]
df_pv_pay = df_pv_pay[df_pv_pay['action_time_pay'] > df_pv_pay['action_time_pv']]
t_1 = df_pv_pay.shape[0]
print('时间筛选前数量 %.0f'%t_0)
print('时间筛选后数量 %.0f'%t_1)
print('时间筛选后减少数量 %.0f'%(t_0-t_1))
------
时间筛选前数量 300673
时间筛选后数量 215070
时间筛选后减少数量 85603

第二个是,用户在下单一个商品之前可能有不止一次的浏览,即多个浏览对应一个下单,同理也存在一个浏览对应多个下单记录的情况(如下图所示)。

要分析从浏览到下单的转化率,我们需要以用户+商品的维度考虑,即对于同一用户同一商品下的多重行为记录要进行去重处理。举例说就是,同用户用商品下不论有多少次浏览行为,只要下单了那么都算作为一个转化,不应算作多个。

基于以上去重逻辑,我们使用groupby对同用户同商品的重复行为进行去重。

pv_pay_dict= {}
# 去重后的浏览用户数
pv_cnt = df_pv.groupby(['user_id','sku_id'])['type_map'].count().shape[0]
# 去重后的下单用户数
pay_cnt = df_pv_pay.groupby(['user_id','sku_id'])['type_map_pv'].count().shape[0]

pv_pay_rate = round(pay_cnt*100/pv_cnt,4)
pv_pay_dict['浏览'] = 100
pv_pay_dict['下单'] = pv_pay_rate
print('浏览用户数 %.0f'%pv_cnt)
print('浏览到下单的用户数 %.0f'%pay_cnt)
print('浏览->下单转化率 %.2f%%'%pv_pay_rate)
------
浏览用户数 2228233
浏览到下单的用户数 162772
浏览->下单转化率 7.30%

从浏览到下单的转化率仅为7.3%,还有很大的优化空间。

接下来,我们使用pyecharts第三方包漏斗可视化。

from pyecharts import options as opts
from pyecharts.charts import Funnel
from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB

pv_pay=(Funnel(opts.InitOpts(width="600px", height="300px"))
            .add(
            series_name="",
            data_pair=[[k,v] for  k,v in pv_pay_dict.items()],
            tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{b} : {c}%"),
            label_opts=opts.LabelOpts(is_show=True, position="inside"),
            itemstyle_opts=opts.ItemStyleOpts(border_color="#fff", border_width=1)
        ))
pv_pay.load_javascript()
pv_pay.render_notebook()

需求8:计算用户从浏览->加购物车->下单的转化率及漏斗可视化

多了一个环节,但分析过程与上面一样。

首先我们分别对浏览和加购物车、加购物车和下单做内连接的匹配。

这里注意一下:因为两次都是内连接,这意味着最终下单的客户一定是有浏览->加购物车->下单的行为轨迹的。但实际业务场景中,有的用户从浏览直接下单而不加入购物车,所以这种直接下单的用户群是没有包含在内的。

# 筛选用户的加购物车数据
df_cart = df[df['type_map']=='加购物车']
# 浏览用户与加购物车匹配
df_pv_cart = pd.merge(df_pv[cols], df_cart[cols], on=['user_id','sku_id'], how='inner', suffixes=('_pv''_cart'))
# 加购物车用户与下单匹配
df_cart_pay = pd.merge(df_cart[cols], df_pay[cols], on=['user_id','sku_id'], how='inner', suffixes=('_cart''_pay'))
# 加购物车用户与下单匹配
df_pv_cart_pay = pd.merge(df_pv_cart, df_cart_pay, on=['user_id','sku_id','action_time_cart','type_map_cart'], how='inner')

同样按照行为时间顺序进一步筛选。

# 加购物车时间大于浏览时间
t_0 = df_pv_cart.shape[0]
df_pv_cart = df_pv_cart[df_pv_cart['action_time_cart'] > df_pv_cart['action_time_pv']]
t_1 = df_pv_cart.shape[0]
print('时间筛选前数量 %.0f'%t_0)
print('时间筛选后数量 %.0f'%t_1)
print('时间筛选后减少数量 %.0f'%(t_0-t_1))
------
时间筛选前数量 895540
时间筛选后数量 569174
时间筛选后减少数量 326366

# 下单时间大于加购物车时间
t_0 = df_cart_pay.shape[0]
df_cart_pay = df_cart_pay[df_cart_pay['action_time_pay'] > df_cart_pay['action_time_cart']]
t_1 = df_cart_pay.shape[0]
print('时间筛选前数量 %.0f'%t_0)
print('时间筛选后数量 %.0f'%t_1)
print('时间筛选后减少数量 %.0f'%(t_0-t_1))
------
时间筛选前数量 196214
时间筛选后数量 182881
时间筛选后减少数量 13333

然后使用groupby对同用户同商品的重复行为进行去重。

pv_pay_cart_dict= {}
# 去重后浏览客户数
pv_cnt = df_pv.groupby(['user_id','sku_id'])['type_map'].count().shape[0]
# 去重后加购物车用户数
cart_cnt = df_pv_cart.groupby(['user_id','sku_id'])['type_map_pv'].count().shape[0]
# 去重后下单用户数
pay_cnt = df_cart_pay.groupby(['user_id','sku_id'])['type_map_cart'].count().shape[0]
# 浏览到加购物车转化率
pv_cart_rate = round(cart_cnt*100/pv_cnt,4)
# 浏览到加购物车到下单的转化率
pv_cart_pay_rate = round(pay_cnt*100/pv_cnt,4)
# 加购物车到下单的转化率
cart_pay_rate = round(pay_cnt*100/cart_cnt,4)

pv_pay_cart_dict['浏览'] = 100
pv_pay_cart_dict['加购物车'] = pv_cart_rate
pv_pay_cart_dict['下单'] = cart_pay_rate
print('浏览用户数 %.0f'%pv_cnt)
print('加购物车用户数 %.0f'%cart_cnt)
print('下单的用户数 %.0f'%pay_cnt)
print('浏览->加购物车转化率 %.2f%%'%pv_cart_rate)
print('浏览->加购物车->下单转化率 %.2f%%'%pv_cart_pay_rate)
print('加购物车->下单转化率 %.2f%%'%cart_pay_rate)
------
浏览用户数 2228233
加购物车用户数 354064
下单的用户数 137195
浏览->加购物车转化率 15.89%
浏览->加购物车->下单转化率 6.16%
加购物车->下单转化率 38.75%

同样用pyecharts进行漏斗可视化。

pv_cart_pay=(Funnel(opts.InitOpts(width="600px", height="400px"))
            .add(
            series_name="",
            sort_='none',
            data_pair=[[k,v] for  k,v in pv_pay_cart_dict.items()],
            tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{b} : {c}%")
        ))
pv_cart_pay.render_notebook()

我们观察到,需求7中得到的转化率为7.3%,而需求8中得到的转化率为6.16%,同样都是从浏览到下单的转化,为什么结果却是不一样的呢?

这是因为,加购物车与下单并不是一种包含关系,而是一种相交的关系。二者的关系有以下三种情况(如下图)。

  • 用户将商品加入购物车然后下单
  • 用户没有加购物车直接下单
  • 用户加入购物车但最后没下单

所以,需求7中的转化率更大,因为从浏览直接到下单,既包括了加购物车后下单也包括了直接下单;而需求8中的转化是从浏览,到加购物车,再到下单,是包含在需求7内的,所以转化率小。

4.消费时长分析

用户从浏览到下单的过程中,可能会有不同的路径,比如,浏览->下单,浏览->加购物车->下单,浏览->收藏->加购物车->下单。

不同的行为路径可能有不同的特性,比如消费时长。

为了更深入研究用户行为,下面我们分析不同路径用户的消费时长,即用户从浏览开始到下单之前所用的总时长,并统计时长的分布。

需求9:统计浏览->加购物车->下单的用户消费时长分布,并进行分布可视化

需求8中我们将浏览->加购物车->下单行为进行了内连接,下面对用户和商品进行分组统计浏览与下单的时间差。其中浏览时间筛选最早浏览时间,代表第一次接触商品,下单时间筛选最早的下单时间,代表浏览和加购物车后最近的下单。

pcp_interval = (df_pv_cart_pay.groupby(['user_id''sku_id'],as_index=False)
                              .apply(lambda x: (x.action_time_pay.min() - x.action_time_pv.min()))
                              .rename(columns={None:'interval'}))

以上我们得到了下单与浏览行为的时间间隔,但此时的interval变量是timedelta时间差类型,我们需要进一步转化成数值类型。

# 获取时间差成分,筛选小时数
pcp_interval['interval'] = pcp_interval['interval'].dt.components['hours']

这是使用了components方法将时间差分解,并最终转换为0-24小时的小时数值。最后使用seaborn对时间间隔进行了分布可视化。

# 时间间隔分布可视化
fig, ax = plt.subplots(figsize=[16,6])
sns.countplot(x='interval', data=pcp_interval, palette='Set1')
# 消费时长分布占比的注释
for p in ax.patches:
        ax.annotate('{:.2f}%'.format(100*p.get_height()/len(pcp_interval['interval'])), (p.get_x() + 0.1, p.get_height() + 100))
ax.set_yscale("log")
plt.title('消费时间间隔')

通过分布结果来看,大部分用户在一个小时内完成了商品下单,大部分用户的消费意愿比较明确。

以上是《pandas进阶宝典》中第四个实战项目的部分内容,目前仍在持续更新中,感兴趣的朋友可以戳👉《pandas进阶宝典》加入。

往期精彩回顾




Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/157398
 
287 次点击