发布时间:2024-09-01 11:01
提示:本项目分析仅用来学习使用
作为中国最受欢迎的在线旅游平台(OTP)之一,阿里巴巴集团旗下的飞猪通过提供百万级旅游相关产品(如机票、酒店、旅行团、 ETC)。平台拥有庞大的产品组合,平台积累用户的行为数据,该数据集是用户脱敏行为数据,包含基本属性用户信息和商品基本属性的脱敏信息。
阿里云天池数据集:飞猪出行平台用户行为数据推荐
数据由三部分组成:用户历史行为脱敏数据、用户基础属性脱敏数据、商品基础属性脱敏数据。
行为类型:(\'clk\', \'fav\', \'cart\',\'pay\')
user_item_behavior_history.csv
该数据集涵盖了2019年6月3日至2021年6月3日这两年期间约200万随机用户的所有行为(包括点击、收藏、添加和购买)。数据集的组织类似于MovieLens-20M,即每一行集合代表一个用户数据行为,由用户ID、产品ID、行为类型、日志组成。每列总结了被调查产品的详细信息。
原数据集共2亿多数据,本项目只选取了2021年5月3日至2021年6月3日一个月接近8000,000的数据。
user_profile.csv
该数据集主要包含约五百万随机用户的基本属性,如年龄、性别、职业、居住城市ID、人群标签等。数据集的每一行代表一条用户信息,以逗号分隔。
item_profile.csv
该数据文件主要包含约27万个产品的基本属性特征,如产品类别ID、产品城市、产品标签等。数据集的每一行代表一个产品的信息,以逗号分隔。
目标是从用户的行为数据中挖掘当前用户的行为特征,通过分析这些特征能够发现平台当前所面临的一些问题并分析原因,从而能出台一些针对性的解决措施,扩大用户的使用转化率。
用户维度
-pv/uv/pay:总量、平均人均(日期、时刻、星期)
-留存分析:次日、三日、五日、七日、十五日、三十日(用户粘度)
-用户画像:年龄、性别、职业、区域分布
行为维度
-复购率和跳失率情况
-用户行为转化率情况
-用户行为在时间上的分布
商品维度
-热门商品分析:点击、收藏、加购、购买前十
-商品购买路径分析:点击、收藏、加购——购买(转换率分析)
-商品画像:种类、地域、标签分布
导入数据’user_item_behavior_history.csv’
# 增加列名\'user_id\', \'item_id\', \'behavior_type\',\'timestamp\'
reader = pd.read_csv(\'./data/user_item_behavior_history.csv\', header=None, names = [\'user_id\', \'item_id\', \'behavior_type\',\'timestamp\'], iterator=True)
# 使用get_chunk方法获取数据
loop = True
chunkSize = 10000000 # 设置chunksize
chunks = []
import datetime
# start time
starttime = datetime.datetime.now()
# long running
while loop:
try:
chunk = reader.get_chunk(chunkSize)
chunks.append(chunk)
except StopIteration:
loop = False
print(\"Iteration is stopped.\") # 迭代完成
# 拼接chunks
df = pd.concat(chunks, ignore_index=True)
# end time
endtime = datetime.datetime.now()
# 共计数据获取时间
print(\'loop_time:\', (endtime - starttime).seconds)
原数据共计获取时间为304秒,也就是5分钟左右,数据共计2亿多行。
# timestamp转为datetime
df[\'timestamp\'] = pd.to_datetime(df[\'timestamp\'], unit=\'s\')
# 查看前五行数据
df.head()
start_date = pd.to_datetime(\'20210503\', format=\'%Y-%m-%d %H:%M:%S\') # 开始日期
end_date = pd.to_datetime(\'20210604\', format=\'%Y-%m-%d %H:%M:%S\') # 截止时间
df = df[(df[\'timestamp\'] >= start_date) & (df[\'timestamp\'] <= end_date)]
df = df.sort_values(by=\'timestamp\', ascending=True) # 时间按升序排列
df = df.reset_index(drop=True) # 重置索引
由此可见,此时的数据没有缺失值和异常值;
而重复数据在这里是正常的,因为用户是可能在同一时间进行多次相同行为的,所以对重复数据不做处理。
# 把列\'nehavior_type\'中的\'clk\',\'fav\',\'cart\',\'pay\'替换为1,2,3,4
\'\'\'
1——点击
2——收藏
3——加购
4——购买
\'\'\'
replace_values = {\'clk\':1, \'fav\':2, \'cart\':3, \'pay\':4}
df[\'behavior_type\'] = df[\'behavior_type\'].replace(replace_values)
# 时间格式转换,获取日期、时间、年、月、日、周几、小时
df[\'date\'] = df[\'timestamp\'].dt.date
df[\'time\'] = df[\'timestamp\'].dt.time
df[\'year\'] = df[\'timestamp\'].dt.year
df[\'month\'] = df[\'timestamp\'].dt.month
df[\'day\'] = df[\'timestamp\'].dt.day
df[\'weekday\'] = df[\'timestamp\'].dt.strftime(\"%w\")
df[\'hour\'] = df[\'timestamp\'].dt.hour
从用户维度和行为维度两个维度去分析
总量
uv = df[\'user_id\'].nunique() # 用户数量uv
item_num = df[\'item_id\'].nunique() # 商品数量
behavior_num = df[\'behavior_type\'].count() # 行为总数
clk_data = df[df[\'behavior_type\'] == 1] # 点击数据
fav_data = df[df[\'behavior_type\'] == 2] # 收藏数据
cart_data = df[df[\'behavior_type\'] == 3] # 加购数据
pay_data = df[df[\'behavior_type\'] == 4] # 支付数据
由以上得到的数据可以知道:点击>加购>收藏>支付
所以转化路径应为:点击—加购—收藏—支付
这里的点击clk相当于浏览pv
page_view = len(clk_data) # 总浏览量
pay_num = len(pay_data) # 总支付量
pv_avg = round(page_view/uv, 2) # 总平均浏览量
pay_avg = round(pay_num/uv, 2) # 总平均支付量
在这一个月内,总体来看,在1005061个用户中,平均每个人浏览7.19次,支付率为22%,说明用户活跃度不高且支付转化率不高。
日均
# 日访问量
pv_daily = clk_data.groupby(\'date\')[\'user_id\'].count().reset_index().rename(columns={\'user_id\': \'pv_daily\'})
# 日访客量
uv_daily = df.groupby(\'date\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'uv_daily\'})
# 拼接
daily_data = pd.merge(pv_daily, uv_daily, how=\'outer\', on=\'date\')
# 日平均访问量
daily_data[\'avg_pv_daily\'] = round(daily_data[\'pv_daily\']/daily_data[\'uv_daily\'], 2)
# 日成交量
pay_daily = pay_data.groupby(\'date\')[\'user_id\'].count().reset_index().rename(columns={\'user_id\': \'pay_daily\'})
# 拼接
daily_data = pd.merge(daily_data, pay_daily, on=\'date\')
# 日平均成交量
daily_data[\'avg_pay_daily\'] = round(daily_data[\'pay_daily\']/daily_data[\'uv_daily\'], 2)
# 创建图形
fig = plt.figure(figsize=(20,8),dpi=80)
plt.grid(True, linestyle=\"--\", alpha=0.5)
ax1 = fig.add_subplot(111)
ax1.plot(daily_data.date, daily_data.avg_pv_daily, label=\'日平均访问量\')
ax1.set_ylabel(\'日平均访问量\')
ax1.set_title(\'daily_data\', size=15)
ax1.legend(loc=\'upper left\')
ax2 = ax1.twinx() # 共享x轴
ax2.plot(daily_data.date, daily_data.avg_pay_daily, \'r\', label=\'日平均成交量\')
ax2.set_ylabel(\'日平均成交量\')
ax2.set_xlabel(\'日期\', size=15)
ax2.legend(loc=\'upper right\')
plt.show()
每天的人均访问量从上半月急剧下滑,到下半月持续走低,直至月底31号跌倒谷底,然后又突然上升;
每天的人均成交量从月初直线下降,然后缓慢回升,直至月底31号达到顶峰,随后突然回落。
时均
# 每小时访问量
pv_hour = clk_data.groupby(\'hour\')[\'user_id\'].count().reset_index().rename(columns={\'user_id\': \'pv_hour\'})
# 每小时访客量
uv_hour = df.groupby(\'hour\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'uv_hour\'})
# 拼接
hour_data = pd.merge(pv_hour, uv_hour, how=\'outer\', on=\'hour\')
# 每小时平均访问量
hour_data[\'avg_hour_pv\'] = round(hour_data[\'pv_hour\']/hour_data[\'uv_hour\'], 2)
# 每小时成交量
pay_hour = pay_data.groupby(\'hour\')[\'user_id\'].count().reset_index().rename(columns={\'user_id\': \'pay_hour\'})
# 拼接
hour_data = pd.merge(hour_data, pay_hour, on=\'hour\')
# 每小时平均成交量
hour_data[\'avg_hour_pay\'] = round(hour_data[\'pay_hour\']/hour_data[\'uv_hour\'], 2)
# 创建图形
fig = plt.figure(figsize=(20,8),dpi=80)
plt.grid(True, linestyle=\"--\", alpha=0.5)
ax1 = fig.add_subplot(111)
ax1.plot(hour_data.hour, hour_data.avg_hour_pv, label=\'每小时平均访问量\')
ax1.set_ylabel(\'每小时平均访问量\')
ax1.set_title(\'hour_data\', size=15)
ax1.legend(loc=\'upper left\')
ax2 = ax1.twinx() # 共享x轴
ax2.plot(hour_data.hour, hour_data.avg_hour_pay, \'r\', label=\'每小时平均成交量\')
ax2.set_ylabel(\'每小时平均成交量\')
ax2.set_xlabel(\'时刻\', size=15)
ax2.legend(loc=\'upper right\')
plt.show()
每时刻的人均访问量从零点开始上升,在凌晨3点之后缓慢回落,在12点之后又开始一直上升,直至下午2点达到顶点,之后急剧下降;
每时刻的人均成交量从零点开始上升,在凌晨3点达到顶点,之后不断回落,在中午12点之后急剧下滑。
周均
pv_weekday = clk_data.groupby(\'weekday\')[\'user_id\'].count().reset_index().rename(columns={\'user_id\': \'pv_weekday\'})
uv_weekday = df.groupby(\'weekday\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'uv_weekday\'})
weekday_data = pd.merge(pv_weekday, uv_weekday, how=\'outer\', on=\'weekday\')
weekday_data[\'avg_pv_weekday\'] = round(weekday_data[\'pv_weekday\']/weekday_data[\'uv_weekday\'], 2)
pay_weekday = pay_data.groupby(\'weekday\')[\'user_id\'].count().reset_index().rename(columns={\'user_id\': \'pay_weekday\'})
weekday_data = pd.merge(weekday_data, pay_weekday, on=\'weekday\')
weekday_data[\'avg_pay_weekday\'] = round(weekday_data[\'pay_weekday\']/weekday_data[\'uv_weekday\'], 2)
人均访问量最高是周一,最低是周日;
人均支付量最高是周一,最低是周四和周五。
总结
次日留存率=(某日新增的用户中,在注册的第2天还进行登录的用户数)/ 该日新增用户数
3日留存率=(某日新增用户中,在注册的第3天还进行登录的用户数)/ 该日新增用户数
7日留存率=(某日新增的用户中,在注册的第7天还进行登录的用户数)/ 该日新增用户数
30日留存率=(某日新增的用户中,在注册的第30天还进行登录的用户数)/ 该日新增用户数
# 创建n日留存函数
def cal_retention(data, n): # n为n日留存
user = []
date = pd.Series(data.date.unique()).sort_values()[:-n] # 时间截取至最后一天的前n天
retention_rates =[]
for i in date:
new_user = set(data[data.date == i].user_id.unique()) - set(user) # 识别新用户,设初始用户量为零
user.extend(new_user)
# 第n天留存状况
user_nday = data[data.date == i+timedelta(n)].user_id.unique() # 第n天登陆的用户状况
a = 0
for user_id in user_nday:
if user_id in new_user:
a += 1
retention_rate = a/len(new_user) # 计算该天第n日留存率
retention_rates.append(retention_rate) # 汇总n日留存数据
data_retention = pd.Series(retention_rates, index=date)
return data_retention
# 分别计算留存率
retention_2days = cal_retention(df, 1).apply(lambda x: format(x, \'.2%\')) # 次日留存
retention_3days = cal_retention(df, 2).apply(lambda x: format(x, \'.2%\')) # 3日留存
retention_7days = cal_retention(df, 6).apply(lambda x: format(x, \'.2%\')) # 7日留存
retention_15days = cal_retention(df, 14).apply(lambda x: format(x, \'.2%\')) # 15日留存
retention_30days = cal_retention(df, 29).apply(lambda x: format(x, \'.2%\')) # 30日留存
# 留存拼接
retention_data = pd.concat([retention_2days, retention_3days, retention_7days, retention_15days, retention_30days], axis=1)
# 改变列名
retention_data.columns = [\'次日\', \'3日\', \'7日\', \'15日\', \'30日\']
retention_data = retention_data.replace(np.nan, \'\')
print(retention_data)
# 存入CSV
retention_data.to_csv(\'retention.csv\', encoding=\'utf_8_sig\')
从不同的时间来看,留存率整体是比较低的,但这也可能跟多数用户出行频率不高的原因有关;
从另一个方面,可以加大活动优惠,提升产品品质,增强用户粘度。
只对支付用户进行分析
# 支付用户的消费时间
payuser_date = pay_data[[\'user_id\', \'hour\', \'weekday\']]
时刻
# 支付用户按小时分布
payuser_hour = payuser_date.groupby(\'hour\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'num\'})
# 创建图形
plt.figure(figsize=(20,8), dpi=80)
plt.bar(payuser_hour.hour, payuser_hour.num, label=\'每小时成交量\')
plt.plot(payuser_hour.hour, payuser_hour.num, \'ro-\', color=\'r\', alpha=0.8, linewidth=3)
# 添加标注
plt.xlabel(\'小时\',size=15)
plt.ylabel(\'成交量\',size=15)
#添加标题
plt.title(\'支付用户按小时分布\',size=15)
# 添加图例
plt.legend(loc=\'best\')
# 显示柱状-折线图
plt.show()
用户主要喜欢在凌晨支付,尤其是凌晨两点,在晚上支付最低,分析可能是用户在晚上时间主要在浏览选择自己感兴趣的东西,然后在凌晨才能下定决心支付,越晚越容易‘冲动’哈哈~
星期
# 支付用户按周几分布
payuser_weekday = payuser_date.groupby(\'weekday\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'num\'})
print(payuser_weekday)
# 创建图形
plt.figure(figsize=(10,5), dpi=80)
plt.bar(payuser_weekday.weekday, payuser_weekday.num, label=\'每周几成交量\')
# 添加标注
plt.xlabel(\'星期\',size=15)
plt.ylabel(\'成交量\',size=15)
#添加标题
plt.title(\'支付用户按星期分布\',size=15)
# 添加图例
plt.legend(loc=\'best\')
# 显示柱状图
plt.show()
# 读取user_profile.csv,并增加列名\'user_id\',\'age\',\'sex\',\'career\',\'use_city_id\',\'crowd_label\'
user_profile = pd.read_csv(\'./data/user_profile.csv\', header=None, names = [\'user_id\',\'age\',\'sex\',\'career\',\'use_city_id\',\'crowd_label\'], encoding=\'utf-8\')
# 根据user_id拼接user_profile数据
payuser_profile = pd.merge(payuser_date, user_profile, how=\'left\', on=\'user_id\')
年龄
# 最大年龄及最小年龄
print(payuser_profile[\'age\'].max(), payuser_profile[\'age\'].min())
\'\'\'
考虑到平台注册年龄限制,所以最小年龄设置为18岁;
对年龄异常数据进行处理;
\'\'\'
payuser_profile = payuser_profile[payuser_profile[\'age\']>=18]
print(payuser_profile[\'age\'].min())
# 年龄分箱
bins = [18,25,35,45,60,80,125]
labels = [\'[18-25)\',\'[25-35)\',\'[35-45)\',\'[45-60)\',\'[60-80)\',\'[80-125]\']
payuser_profile[\'age_cut\'] = pd.cut(x=payuser_profile.age, bins=bins, right=False, labels=labels)
print(payuser_profile.head())
# 年龄分布
payuser_age = payuser_profile[[\'user_id\', \'age_cut\']].groupby(\'age_cut\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'num\'})
print(payuser_age)
# 创建图形
plt.figure(figsize=(10,5), dpi=80)
plt.bar(payuser_age.age_cut, payuser_age.num, label=\'年龄段成交量\')
# 添加标注
plt.xlabel(\'年龄\',size=15)
plt.ylabel(\'成交量\',size=15)
#添加标题
plt.title(\'支付用户按年龄段分布\',size=15)
# 添加图例
plt.legend(loc=\'best\')
# 显示柱状图
plt.show()
支付用户的年龄主要集中在45~60岁年龄段,这个年龄的用户在退休年龄左右,相对年轻人和中年人来说,没有太大经济和生活压力,更懂得也有时间享受生活出去旅行。
性别
# 性别分布
payuser_sex = payuser_profile[[\'user_id\', \'sex\']].groupby(\'sex\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'num\'})
print(payuser_sex)
sex | num |
---|---|
1 | 962 |
2 | 27540 |
3 | 43487 |
# 画饼图
plt.pie(payuser_sex[\'num\'], labels=payuser_sex[\'sex\'], autopct=\'%1.2f%%\')
plt.title(\'支付用户按性别分布\')
plt.legend(loc=\'best\')
plt.show()
支付用户中女性是男性的1.5倍左右,说明现代女性更加独立自主,更喜欢享受生活喜欢旅行,男性经济方面压力大些,相对来说出行欲望较低。
职业
# 职业分布
payuser_career = payuser_profile.groupby(\'career\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'num\'})
payuser_career[\'percent\'] = payuser_career[\'num\']/payuser_career[\'num\'].sum()
支付用户的职业主要集中在职业3上,说明这个职业的人比较喜欢出行,也有可能是职业原因(需要出差等~)。
城市
# 城市分布
payuser_city = payuser_profile.groupby(\'use_city_id\')[\'user_id\'].apply(lambda x: x.drop_duplicates().count()).reset_index().rename(columns={\'user_id\': \'num\'})
payuser_city[\'percent\'] = payuser_city[\'num\']/payuser_city[\'num\'].sum()
payuser_city = payuser_city[payuser_city[\'percent\']>0.1]
print(payuser_city)
use_city_id | num | percent |
---|---|---|
224 | 8649 | 0.12 |
支付用户占比大于10%的城市只有一个:城市224,可能这个城市比较发达,人民相对富裕,除生活开销外,愿意负担出行的费用享受生活。
复购率=购买次数>1的用户/所有购买用户
# 统计每个用户支付次数
pay_times = pay_data[[\'user_id\', \'behavior_type\']].groupby(\'user_id\').agg(\'count\').reset_index().rename(columns={\'behavior_type\': \'paycount\'})
print(pay_times.head())
# 统计支付次数大于1的用户
repay_data = pay_times[pay_times[\'paycount\'] > 1]
user_id | paycount |
---|---|
32 | 6 |
105 | 1 |
257 | 4 |
323 | 1 |
441 | 1 |
复购率=2/5=40%
只购买一次的用户占购买用户总数的60%,有复购行为的人占40%,可以发现高复购次数的人占比较少,商家可以从商品质量、服务制度、物流体验、以及售后服务几个方面寻找原因,找到提高复购率的突破点。
跳失率=点击次数为1的用户/所有点击用户
# 统计每个用户的点击次数
clk_times = pay_data[[\'user_id\', \'behavior_type\']].groupby(\'user_id\').agg(\'count\').reset_index().rename(columns={\'behavior_type\': \'clkcount\'})
# 统计点击次数为1的用户
clk_onetime = clk_times[clk_times[\'clkcount\'] == 1]
# 计算跳失率
bounce_rate = round(clk_onetime.shape[0]/clk_times.shape[0], 2)
print(\'跳失率:\', bounce_rate)
跳失率:47%
复购率偏低,且跳失率较高,说明平台对用户没有足够的吸引力让用户停留。
用户从点击到最终支付的过程当中流失率(或转化率)状况
# 总体
pv_total = len(df)
clk_num = len(clk_data) # 点击总计
fav_num = len(fav_data) # 收藏总计
cart_num = len(cart_data) # 加购总计
pay_num = len(pay_data) # 支付总计
pv_total | clk_num | fav_num | cart_num | pay_num |
---|---|---|---|---|
7983450 | 7229031 | 229732 | 300665 | 224022 |
# 总体各个阶段的流失率计算
Wastage_rate1 = round(1-clk_num/pv_total,4)
Wastage_rate2 = round(1-cart_num/clk_num,4)
Wastage_rate3 = round(1-fav_num/cart_num,4)
Wastage_rate4 = round(1-pay_num/fav_num,3)
浏览—点击—加购—收藏—购买
总浏览到点击的流失率小说明转化率高,点击到加购流失率太高说明点击转化率低,用户进行点击行为后就没有后续了,收藏到购买的流失率很小,说明用户对这两个用户行为差不多,对于收藏的商品购买的可能性很大。
# 漏斗图--matplotlib
import seaborn as sns
from matplotlib.patches import Polygon # Polygon()可以用来传入按顺序组织的多边形顶点,从而生成出多边形
from matplotlib.collections import PatchCollection
plt.style.use(\'seaborn-dark\') # 设置主题
plt.rcParams[\'font.sans-serif\'] = [\'SimHei\'] # 用来正常显示中文标签
plt.rcParams[\'axes.unicode_minus\'] = False # 用来正常显示负号
data = [clk_num, cart_num, fav_num, pay_num]
phase = [\'点击次数\', \'加购次数\', \'收藏次数\', \'购买次数\']
data1 = [clk_num/2 - i/2 for i in data] # 用来覆盖柱形图左边
data2 = [i+j for i,j in zip(data, data1)] # 柱形图长度=data+覆盖柱形图长度
color_list = [ \'#00685A\', \'#1E786C\',\'#00A08A\',\'#34D0BA\'] # 柱子颜色
fig,ax = plt.subplots(figsize=(16, 9),facecolor=\'#f4f4f4\')
ax.set_title(\'用户转化漏斗图\',fontsize=18)
ax.barh(phase[::-1], data2[::-1], color = color_list, height=0.7) # 柱宽设置为0.7
ax.barh(phase[::-1], data1[::-1], color = \'#f4f4f4\', height=0.7) # 设置成背景同色,覆盖柱形图
ax.axis(\'off\')
polygons = []
for i in range(len(data)):
# 阶段
ax.text(
0, # 坐标
i, # 高度
phase[::-1][i], # 文本,phase[::-1]表示将列表倒序排列
color=\'black\', alpha=0.8, size=16, ha=\"right\")
# 数量
if i < 3:
ax.text(
data2[0] / 2,
i,
str(data[::-1][i]) + \'(\' + str(round(data[::-1][i] / data[::-1][i+1] * 100, 1)) + \'%)\',
color=\'black\', alpha=0.8, size=14, ha=\"center\")
else:
ax.text(
data2[0] / 2,
i,
str(data[::-1][i]),
color=\'black\', alpha=0.8, size=14, ha=\"center\")
if i < 3:
# 绘制过渡多边形
polygons.append(Polygon(xy=np.array([(data1[i + 1], 2 + 0.35 - i), # 因为柱状图的宽度设置成了0.7,所以一半便是0.35
(data2[i + 1], 2 + 0.35 - i),
(data2[i], 3 - 0.35 - i),
(data1[i], 3 - 0.35 - i)])))
# 使用add_collection与PatchCollection来向Axes上添加多边形
ax.add_collection(PatchCollection(polygons,
facecolor=\'#F56E8D\',
alpha=0.8));
plt.show()
# 建漏斗函数
def cal_funnel(data, datetime):
data1 = data.groupby([datetime, \'behavior_type\']).user_id.count().reset_index().rename(columns={\'user_id\': \'total\'})
# 不一样时间中,各用户行为的用户数量
lose_rates = [] #流失率
date2 = pd.Series(data1[datetime].unique())
for i in date2:
data2 = data1[data1[datetime] == i]
clk_to_cart = 1 - data2[data2.behavior_type == 3].reset_index().loc[0, \'total\']/data2[data2.behavior_type == 1].reset_index().loc[0,\'total\']
cart_to_fav = 1-data2[data2.behavior_type == 2].reset_index().loc[0,\'total\']/data2[data2.behavior_type == 3].reset_index().loc[0,\'total\']
fav_to_pay = 1-data2[data2.behavior_type == 4].reset_index().loc[0,\'total\']/data2[data2.behavior_type == 2].reset_index().loc[0,\'total\']
lose_rate = [clk_to_cart, cart_to_fav, fav_to_pay]
lose_rates.append(lose_rate)
data3 = pd.DataFrame(lose_rates, index=date2, columns=[\'clk_to_cart\', \'cart_to_fav\', \'fav_to_pay\'])
return data3
# 按日计算
a_day_lose=cal_funnel(df,\'date\')
# 创建图形
plt.figure(figsize=(20,8), dpi=80)
plt.plot(a_day_lose.index, a_day_lose.clk_to_cart, label=\'点击——加购\')
plt.plot(a_day_lose.index, a_day_lose.cart_to_fav, label=\'加购——收藏\')
plt.plot(a_day_lose.index, a_day_lose.fav_to_pay, label=\'收藏——购买\')
# 添加水平线
plt.axhline(y=0,ls=\"--\",c=\"k\", linewidth=1)
# 添加标注
plt.xlabel(\'日期\',size=15)
plt.ylabel(\'转化率\',size=15)
#添加标题
plt.title(\'用户行为转化流失率\',size=15)
# 添加图例
plt.legend(loc=\'best\')
# 显示折线图
plt.show()
由上图可以看出,部分日期的收藏—购买流失率处于负值区域,说明该天的成交量大于收藏量,说明成交量只有一小部分来源于收藏,所以不再适用于这种转化路径。
点击—收藏/加购—购买
# 建漏斗函数
def cal_funnel(data, datetime):
data1 = data.groupby([datetime, \'behavior_type\']).user_id.count().reset_index().rename(columns={\'user_id\': \'total\'})
# 不一样时间中,各用户行为的用户数量
lose_rates = [] #流失率
date2 = pd.Series(data1[datetime].unique())
for i in date2:
data2 = data1[data1[datetime] == i]
clk_to_favAndcart = 1 - (data2[data2.behavior_type == 2].reset_index().loc[0,\'total\'] + data2[data2.behavior_type == 3].reset_index().loc[0,\'total\'])/data2[data2.behavior_type == 1].reset_index().loc[0,\'total\']
favAndcart_to_pay = 1-data2[data2.behavior_type == 4].reset_index().loc[0,\'total\']/(data2[data2.behavior_type == 2].reset_index().loc[0,\'total\']+data2[data2.behavior_type == 3].reset_index().loc[0,\'total\'])
lose_rate = [clk_to_favAndcart, favAndcart_to_pay]
lose_rates.append(lose_rate)
data3 = pd.DataFrame(lose_rates, index=date2, columns=[\'clk_to_favAndcart\', \'favAndcart_to_pay\'])
return data3
# 按日计算
a_day_lose2=cal_funnel(df,\'date\')
# 创建图形
plt.figure(figsize=(20,8), dpi=80)
plt.plot(a_day_lose2.index, a_day_lose2.clk_to_favAndcart, label=\'点击——收藏/加购\')
plt.plot(a_day_lose2.index, a_day_lose2.favAndcart_to_pay, label=\'收藏/加购——购买\')
# 添加水平线
plt.axhline(y=0.5, ls=\"--\",c=\"k\", linewidth=1)
# 添加标注
plt.xlabel(\'日期\',size=15)
plt.ylabel(\'转化率\',size=15)
#添加标题
plt.title(\'用户行为转化流失率\',size=15)
# 添加图例
plt.legend(loc=\'best\')
# 显示折线图
plt.show()
在该月份期间,用户的点击—收藏/加购流失率一直居高不下,转化率极低,收藏/加购—购买的流失率大部分时间都大于50%,只有在月初的时候较低。
# 按日分布
clk_date = clk_data[[\'user_id\', \'date\']].groupby(\'date\')[\'user_id\'].agg(\'count\')
cart_date = cart_data[[\'user_id\', \'date\']].groupby(\'date\')[\'user_id\'].agg(\'count\')
fav_date = fav_data[[\'user_id\', \'date\']].groupby(\'date\')[\'user_id\'].agg(\'count\')
pay_date = pay_data[[\'user_id\', \'date\']].groupby(\'date\')[\'user_id\'].agg(\'count\')
# 按时分布
clk_hour = clk_data[[\'user_id\', \'hour\']].groupby(\'hour\')[\'user_id\'].agg(\'count\')
cart_hour = cart_data[[\'user_id\', \'hour\']].groupby(\'hour\')[\'user_id\'].agg(\'count\')
fav_hour = fav_data[[\'user_id\', \'hour\']].groupby(\'hour\')[\'user_id\'].agg(\'count\')
pay_hour = pay_data[[\'user_id\', \'hour\']].groupby(\'hour\')[\'user_id\'].agg(\'count\')
# 按周分布
clk_weekday = clk_data[[\'user_id\', \'weekday\']].groupby(\'weekday\')[\'user_id\'].agg(\'count\')
cart_weekday = cart_data[[\'user_id\', \'weekday\']].groupby(\'weekday\')[\'user_id\'].agg(\'count\')
fav_weekday = fav_data[[\'user_id\', \'weekday\']].groupby(\'weekday\')[\'user_id\'].agg(\'count\')
pay_weekday = pay_data[[\'user_id\', \'weekday\']].groupby(\'weekday\')[\'user_id\'].agg(\'count\')
# 创建图形
fig = plt.figure(figsize=(20,10),dpi=80)
plt.grid(True, linestyle=\"--\", alpha=0.5)
plt.xticks([]) #去掉横坐标值
plt.yticks([]) #去掉纵坐标值
ax1 = fig.add_subplot(311)
date = clk_date.index.tolist()
ax1.bar(date, clk_date.values, label=\'每日用户点击量\')
ax1.set_ylabel(\'数量\')
ax1.set_title(\'用户行为按天分布\', size=10)
ax1.legend(loc=\'upper left\')
ax2 = ax1.twinx()
ax2.plot(date, cart_date.values, \'r\', label=\'每日用户加购量\', linewidth=2)
ax2.plot(date, fav_date.values, \'k\', label=\'每日用户收藏量\', linewidth=2)
ax2.plot(date, pay_date.values, \'y\', label=\'每日用户购买量\', linewidth=2)
ax2.set_ylabel(\'数量\')
ax2.set_xlabel(\'日期\')
ax2.legend(loc=\'upper right\')
ax1 = fig.add_subplot(312)
hour = clk_hour.index.tolist()
ax1.bar(hour, clk_hour.values, label=\'每小时用户点击量\')
ax1.set_ylabel(\'数量\')
ax1.set_title(\'用户行为按时刻分布\', size=10)
ax1.legend(loc=\'upper left\')
ax2 = ax1.twinx()
ax2.plot(hour, cart_hour.values, \'r\', label=\'每日用户加购量\', linewidth=2)
ax2.plot(hour, fav_hour.values, \'k\', label=\'每日用户收藏量\', linewidth=2)
ax2.plot(hour, pay_hour.values, \'y\', label=\'每日用户购买量\', linewidth=2)
ax2.set_ylabel(\'数量\')
ax2.set_xlabel(\'时刻\')
ax2.legend(loc=\'upper right\')
ax1 = fig.add_subplot(313)
weekday = clk_weekday.index.tolist()
length = len(clk_weekday)
x = np.arange(length) # 横坐标
total_width, n = 0.6, 3 # 柱状总宽度,数据组数
width = total_width / n # 每个柱状的宽度
x1 = x - width / 3 # 第一组柱状横坐标起始位置
x2 = x1 + width # 第二组柱状横坐标起始位置
x3 = x2 + width # 第三组柱状横坐标起始位置
ax1.plot(weekday, clk_weekday.values, \'r\', linewidth=1, label=\'每周几用户点击量\')
ax1.set_ylabel(\'数量\')
ax1.set_title(\'用户行为按星期分布\', size=10)
ax1.legend(loc=\'upper left\')
ax2 = ax1.twinx()
ax2.bar(x1, cart_weekday.values, width=width, label=\'每周几用户加购量\')
ax2.bar(x2, fav_weekday.values, width=width, label=\'每周几用户收藏量\')
ax2.bar(x3, pay_weekday.values, width=width, label=\'每周几用户购买量\')
ax2.set_xticks(x, weekday)
ax2.set_ylabel(\'数量\')
ax2.set_xlabel(\'星期\')
ax2.legend(loc=\'upper right\')
plt.show()
# 读取数据\'item_profile.csv\'
item_profile = pd.read_csv(\'./data/item_profile.csv\', header=None, names = [\'item_id\',\'item_category_id\',\'item_city\',\'item_label\'], encoding=\'utf8\')
print(item_profile.head())
# 按商品分组统计商品被点击、加购、收藏、购买的数量
item_clk_num = clk_data.groupby(\'item_id\')[\'behavior_type\'].count().reset_index().rename(columns={\'behavior_type\':\'clk_num\'})
item_cart_num = cart_data.groupby(\'item_id\')[\'behavior_type\'].count().reset_index().rename(columns={\'behavior_type\':\'cart_num\'})
item_fav_num = fav_data.groupby(\'item_id\')[\'behavior_type\'].count().reset_index().rename(columns={\'behavior_type\':\'fav_num\'})
item_pay_num = pay_data.groupby(\'item_id\')[\'behavior_type\'].count().reset_index().rename(columns={\'behavior_type\':\'pay_num\'})
# 商品点击Top10
item_clk_num = item_clk_num.sort_values(by=\'clk_num\', ascending=False)
item_clk_Top10 = item_clk_num.head(10)
# 商品收藏+加购Top10
favcart_data = pd.concat([fav_data, cart_data], axis=0)
item_favcart_num = favcart_data.groupby(\'item_id\')[\'behavior_type\'].count().reset_index().rename(columns={\'behavior_type\':\'favcart_num\'})
item_favcart_num = item_favcart_num.sort_values(by=\'favcart_num\', ascending=False)
item_favcart_Top10 = item_favcart_num.head(10)
# 商品购买Top10
item_pay_num = item_pay_num.sort_values(by=\'pay_num\', ascending=False)
item_pay_Top10 = item_pay_num.head(10)
1.点击——购买:ctop_rate; 2.收藏/加购——购买:ftop_rate;
# Top10转化率
df1 = item_clk_Top10.merge(item_pay_Top10, on=\'item_id\').merge(item_favcart_Top10, on=\'item_id\')
# 点击——购买
df1[\'ctop_rate\'] = round(df1[\'pay_num\']/df1[\'clk_num\'],4)
# 收藏/加购——购买
df1[\'fctop_rate\'] = round(df1[\'pay_num\']/df1[\'favcart_num\'],4)
print(df1)
# 平均转化率
ctop_avg_rate = round(df1[\'pay_num\'].sum()/df1[\'clk_num\'].sum(),2)
fctop_avg_rate = round(df1[\'pay_num\'].sum()/df1[\'favcart_num\'].sum(),2)
# matplotlib可视化
import matplotlib.ticker as ticker
fig = plt.figure(figsize=(8,5),dpi=80)
colors = [\'#CC9999\',\'#FFFF99\',\'#666699\']
x = tuple(df1[\'item_id\'].apply(str)) #list transform to tuple
print(x)
# 点击-购买转化率
ax1 = fig.add_subplot(211)
ax1.set_title(\'点击Top10商品购买转化率\',fontsize=14)
ax1.bar(x,df1.clk_num,width=0.3,color=colors) # 柱状图显示点击量
ax1.set_xlabel(\'商品ID\', size = 10)
ax1.set_ylabel(\'点击量\', size = 10)
ax2 = ax1.twinx()
ax2.plot(x,df1.ctop_rate)
ax2.set_ylabel(\'点击-购买转化率\', size = 10)
ax2.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1, decimals=1))
ax2.axhline(y=ctop_avg_rate,c=\'r\',ls=\':\',label=\'平均购买转换率\') #添加水平直线
# 收藏-购买转化率
ax3 = fig.add_subplot(212)
ax3.set_title(\'收藏/加购Top10商品购买转化率\',fontsize=14)
ax3.bar(x,df1.favcart_num,color=colors,width=0.3)
ax3.set_xlabel(\'商品ID\', size = 10)
ax3.set_ylabel(\'收藏加购量\', size = 10)
ax4 = ax3.twinx()
ax4.axhline(y=fctop_avg_rate,c=\'r\',ls=\':\',label=\'平均购买转换率\') #添加水平直线
ax4.plot(scale_ls,df1.fctop_rate)
ax4.set_ylabel(\'收藏/加购-购买转化率\', size = 10)
ax4.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1, decimals=1))
plt.tight_layout() # 自动调整各子图间距
plt.show()
通过可视化分析,点击量、收藏加购量高的商品,购买转化率不一定高,相关性不成正比,尤其是收藏加购量最高的商品购买转化率反而是最低的,可以这类商品增加关注。
# item_pay_Top10根据item_id拼接item_profile
payitem_profile = pd.merge(item_pay_Top10, item_profile, how=\'left\', on=\'item_id\')
item_category_num = payitem_profile.groupby(\'item_category_id\')[\'pay_num\'].sum().reset_index().rename(columns={\'pay_num\':\'pay_sum\'})
item_city_num = payitem_profile.groupby(\'item_city\')[\'pay_num\'].sum().reset_index().rename(columns={\'pay_num\':\'pay_sum\'})
item_label_num = payitem_profile.groupby(\'item_label\')[\'pay_num\'].sum().reset_index().rename(columns={\'pay_num\':\'pay_sum\'})
# matplotlib可视化
fig = plt.figure(figsize=(9,5),dpi=80)
# 购买商品Top10种类分布
ax1 = fig.add_subplot(311)
ax1.set_title(\'购买商品Top10种类分布\',fontsize=14)
x1 = tuple(item_category_num[\'item_category_id\'].apply(str))
y1 = item_category_num[\'pay_sum\']
colors1 = [\'#CC9999\',\'#FFFF99\',\'#666699\']
ax1.barh(x1,y1,color=colors1)
ax1.set_xlabel(\'商品种类\', size = 10)
ax1.set_ylabel(\'成交量\', size = 10)
# 购买商品Top10区域分布
ax1 = fig.add_subplot(312)
ax1.set_title(\'购买商品Top10区域分布\',fontsize=14)
x1 = tuple(item_city_num[\'item_city\'].apply(str))
y1 = item_city_num[\'pay_sum\']
colors2 = [\'#FF9900\',\'#009999\',\'#CCCC99\', \'#CC3399\']
ax1.barh(x1,y1,color=colors2)
ax1.set_xlabel(\'城市\', size = 10)
ax1.set_ylabel(\'成交量\', size = 10)
# 购买商品Top10标签分布
ax1 = fig.add_subplot(313)
ax1.set_title(\'购买商品Top10标签分布\',fontsize=14)
x1 = tuple(item_label_num[\'item_label\'].apply(str))
y1 = item_label_num[\'pay_sum\']
colors3 = [\'#FF6666\',\'#0099CC\',\'#009966\']
ax1.barh(x1,y1,color=colors3)
ax1.set_xlabel(\'商品标签\', size = 10)
ax1.set_ylabel(\'成交量\', size = 10)
plt.tight_layout() # 自动调整各子图间距
plt.show()
购买商品Top10的种类主要集中在ID为4、13、30的种类;
购买商品Top10 的城市主要分布在ID为224、248、383、561的城市;
购买商品Top10 的标签主要是ID为-1、169、94的标签。
【Opencv】基于python-opencv的CV2实现图片OCR前的扫描摆正OCR识别【代码实现】
kalman滤波基础及matlab仿真_多传感器信息融合算法设计及仿真——信息融合算法分析...
Datenlord | Rust实现RDMA异步编程(二):async Rust 封装 UCX 通信库
除了 Qiankun, 这些微前端框架或许更适合你「建议收藏」
电脑商城项目总结-01用户管理模块(注册,登录,修改密码,个人信息,上传头像)
python web代码转换成app_python_webApp
VS code 安装VUE3 模板报错The template rootrequires exactly one element. eslint -plugin-vue