1、导入所需要的库
numpy、pandas、matplotlib、seaborn和pyecharts
设置中文显示问题
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from datetime import datetime
from pyecharts.charts import Funnel
sns.set(style = 'darkgrid',font_scale = 1.5) # 设置sns的风格为darkgrid,曲线宽度为1.5
plt.rcParams['font.sans-serif'] = ['Simhei'] # 解决可视化过程中的中文显示问题
plt.rcParams['axes.unicode_minus'] = False
2、导入数据、查看、抽样
(1)读取、查看
# 读取数据
data_user = pd.read_csv("tianchi_mobile_recommend_train_user.csv",
encoding = "utf-8",dtype = str)
# 查看数据基础情况
data_user.info()
输出:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 12256906 entries, 0 to 12256905
Data columns (total 6 columns):
user_id object
item_id object
behavior_type object
user_geohash object
item_category object
time object
dtypes: object(6)
memory usage: 561.1+ MB
字段解释:
'''
字段解释:
user_id: 用户身份
item_id: 商品ID
behavior_type: 用户行为类型(点击、收藏、加购物车、支付---1,2,3,4表示)
user_geohash: 地理位置
item_category: 品类(商品所属的品类)
time: 用户行为发生的时间
'''
(2)抽样
# 整体数据量太大,随机抽取20%来操作
data_user = data_user.sample(frac = 0.2,replace = False)
# 抽取的数据情况:
data_user.info()
输出:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2451381 entries, 2927950 to 9650036
Data columns (total 6 columns):
user_id object
item_id object
behavior_type object
user_geohash object
item_category object
time object
dtypes: object(6)
memory usage: 130.9+ MB
# 数据前五行:
data_user.head()
输出:
3、数据预处理
(1)查看数据缺失情况
# 数据完整度
data_user.isnull().sum() # 使用 isnull()查看数据缺失情况
输出:
user_id 0
item_id 0
behavior_type 0
user_geohash 1666852
item_category 0
time 0
dtype: int64
可见数据缺失的主要是用户地理信息
计算缺失率
# 计算缺失率
data_user.apply(lambda x:sum(x.isnull())/len(x)) # 使用 lambda() 函数
输出:
user_id 0.000000
item_id 0.000000
behavior_type 0.000000
user_geohash 0.680321
item_category 0.000000
time 0.000000
dtype: float64
由于地理对于此次分析 影响不大,故忽略地理信息的缺失
(2)对日期进行处理
# 拆分time,使用切片:
data_user['date'] = data_user['time'].str[:10] # 提取出日期“yyy-mm-dd”格式的
data_user['hour'] = data_user['time'].str[11:] # 因为日期和时间中间有空格,故从11开始切片
# 查看数据类型
# data_user.dtypes
data_user.head()
输出:
对 time、date 和 hour进行数据类型的转换
# 转换日期的数据类型
data_user['time'] = pd.to_datetime(data_user['time']) # 使用 to_datetime()
data_user['date'] = pd.to_datetime(data_user['date'])
data_user['hour'] = data_user['hour'].astype(int)
# 再次查看数据类型
data_user.dtypes
输出:
user_id object
item_id object
behavior_type object
user_geohash object
item_category object
time datetime64[ns]
date datetime64[ns]
hour int32
dtype: object
(3)对数据进行排序
# 按 time 这一列进行升序排列,并对原数据进行操作
data_user.sort_values(by = 'time',ascending = True,inplace = True)
# 注意 inplace的用法,当 =True时,会直接在元数据 data_user里进行修改
data_user.tail()
输出:
可见最后的 time 是12月18号
(4)重置索引列
# 删除索引,并重置
data_user.reset_index(drop = True,inplace = True) # .reset_index()
data_user.head()
输出:
(5)数据快速统计摘要
data_user.describe(include = "all")
# 对数据进行简单评估,没有问题则数据清洗工作结束
输出:
4、构建数据模型
(1)pv和uv值的变化
从日期来看
1、日访问量pv
pv_daily = data_user.groupby('date').count()['user_id'] # 日访问量即每天的浏览量,对访客ID不必去重
display(pv_daily.head())
输出:
date
2014-11-18 73216
2014-11-19 72211
2014-11-20 70879
2014-11-21 67017
2014-11-22 72098
Name: user_id, dtype: int64
2、日访客量uv
uv_daily = data_user.groupby('date')['user_id'].apply(lambda x:x.drop_duplicates().count())
display(uv_daily.head())
输出:
date
2014-11-18 5660
2014-11-19 5688
2014-11-20 5632
2014-11-21 5558
2014-11-22 5474
Name: user_id, dtype: int64
3、合并pv和uv,并重新保存
pv_uv_daily = pd.concat([pv_daily,uv_daily],axis = 1) # 拼接使用 pd.concat()
# 在pandas中用concat()函数对两个表进行拼接,axis = 1,就是按列拼接
pv_uv_daily.columns = ['pv','uv'] # 设置字段名
pv_uv_daily.head()
输出:
4、查看pv和uv之间的相关性
使用系数相关矩阵 DataFrame.corr(method=‘pearson’, min_periods=1),计算列与列之间的相关系数,返回相关系数矩阵。
method:可选值为{‘pearson’, ‘kendall’, ‘spearman’}
pv_uv_daily.corr(method = 'pearson') # 越接近“1”,线性相关性越大
pv_uv_daily.corr(method = 'spearman')
输出:
可见pv和uv之间有正向关系
5、画pv和uv的图(日期)
plt.figure(figsize = (16,9)) # 设置画布大小,宽16,高9
figure函数用法:
''' figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True)num:图像编号或名称,数字为编号 ,字符串为名称 figsize:指定figure的宽和高,单位为英寸;
dpi参数:指定绘图对象的分辨率,即每英寸多少个像素,缺省值为80 1英寸等于2.5cm,A4纸是 21*30cm的纸张
facecolor:背景颜色
edgecolor:边框颜色
frameon:是否显示边框详解见:figure函数的用法
'''
plt.subplot(211) # 使用subplot绘制多个子图,211意思是两行一列,占用第一个
plt.plot(pv_daily,color = 'blue')
plt.ylabel('访问量') # y轴标签
plt.title('每日访问量') # 子图表头标题
plt.legend() # 图例
plt.subplot(212) # 使用subplot绘制多个子图,212意思是两行一列,占用第2个
plt.plot(uv_daily,color = 'red')
# pv_uv_daily['uv'].plot(color = 'red')
plt.xlabel('日期') # X轴标签
plt.ylabel('访客量') # y轴标签
plt.title('每日访客数') # 子图表头标题
plt.legend() # 图例
plt.suptitle('pv和uv变化趋势',fontsize = 25,color = 'blue') # 大图主表头标题,字号15,蓝色
plt.show()
# plt.show()
plt.savefig('PV和UV变化趋势.png', bbox_inches='tight')
plt.subplot(221) //分成2x2,占用第一个,即第一行第一列的子图
plt.subplot(222)//分成2x2,占用第二个,即第一行第二列的子图
plt.subplot(212)//分成2x1,占用第二个,即第二行
输出:
可见,在双12期间访问量和访客量都达到峰值
从小时来看
1、整理每日时间段的pv和uv数据(类比按日期的代码意思)
pv_hour = data_user.groupby('hour').count()['user_id']
uv_hour = data_user.groupby('hour')['user_id'].apply(lambda x:x.drop_duplicates().count())
pv_uv_hour = pd.concat([pv_hour,uv_hour],axis = 1)
pv_uv_hour.columns = ["pv","uv"]
pv_uv_hour.head()
输出:
2、查看相关性
# 查看pv_hour和 uv_hour 之间的相关系数
pv_uv_hour.corr("pearson")
pv_uv_hour.corr('spearman')
输出:
从相关性上来看,可以看出每小时访问量和每小时访客量呈正相关
3、绘制每小时的pv和uv 图(小时)
plt.figure(figsize = (16,9)) # 设置画布大小
pv_uv_hour['pv'].plot(color = 'r',label = "访问量")
plt.ylabel('访问量')
plt.xticks(range(0,24),pv_uv_hour.index)
# 对于xticks(locs, [labels], **kwargs)里面的各个参数locs是设置显示有几个刻度,
# [labels]是在每个刻度下显示的标签,
# **kwargs可用于设置标签字体倾斜度rotation和颜色等
plt.legend()
plt.show()
输出:
plt.figure(figsize = (16,9))
pv_uv_hour['uv'].plot(color = "blue",label = "访客量")
plt.ylabel('访客量')
plt.xticks(range(0,24),pv_uv_hour.index)
plt.legend()
# plt.show()
plt.savefig('uv(小时).png', bbox_inches='tight')
输出:
合并两个pv和uv图形在同一个坐标轴里
# 合并两个pv和uv图形在同一个坐标轴里
plt.figure(figsize = (16,9))
pv_uv_hour['pv'].plot(color = 'b',label = '访问量')
plt.ylabel('访问量')
plt.legend()
pv_uv_hour['uv'].plot(color = 'r',label = '访客量',secondary_y = True) # 应用在第二Y轴上
plt.ylabel('访客量')
plt.xticks(range(0,24),pv_uv_hour.index)
plt.legend(loc='upper center') # 位置中间
plt.grid(True)
# plt.show()
plt.savefig('每小时pv和uv图.png', bbox_inches='tight')
总结:
从上图看出:时段0-5点,pv和uv波动情况相同,并同时呈现下降趋势;
18点后用户的活跃度达到一天的峰值;
5-10点是活跃度攀升的时间段;
10-18点用户活跃度稳定。
(2)用户在一天不同时间段内的访问量及不同行为类型统计
pv_detail = pd.pivot_table(columns = 'behavior_type',index = 'hour',
data = data_user,aggfunc = np.size,values = 'user_id') # 数据透视
pv_detail.columns = ['点击','收藏','加购物车','支付'] # 改变字段的名称
display(pv_detail.head())
输出:
绘制图形:
# 由于点击量(1)和其他行为类型相差太大,不适合在统一坐标轴展示,此处提出点击量1
plt.figure(figsize = (16,9))
# sns.lineplot(data = pv_detail) # 全部显示
sns.lineplot(data = pv_detail.iloc[:,1:]) # 剔除点击量
# plt.show()
plt.savefig('一天内用户行为分布.png', bbox_inches='tight')
输出:
(3)将用户行为转化为漏斗计算
# 查看不同用户行为类型的访问量
user_type_count = data_user.groupby('behavior_type').count()
# user_hh = pd.DataFrame(list(user_type_count),index = [1,2,3,4],columns = ['行为','总数'])
user_type_count = user_type_count['user_id']
# user_type_count
user_hh = pd.DataFrame(list(user_type_count),index = ['点击','收藏','加购物车','支付'],columns = ['数量'])
user_hh
输出:
画漏斗模型,pyecharts制图
# funnel = Funnel( "漏斗图示例")
# name = "漏斗图示例"
# attr = ['点击','收藏','加购物车','支付']
# value = user_hh["数量"]
# funnel.add(
# name,
# attr,
# value,
# is_label_show=True,
# label_pos="outside",
# legend_orient="vertical",
# legend_pos="left",
# )
# funnel.render()
(4)计算流失率
# (1)点击量到收藏的流失率
rate1 = (user_type_count[0]-user_type_count[1])/user_type_count[0]
# (2)点击量到加购的流失率
rate2 = (user_type_count[0]-user_type_count[2])/user_type_count[0]
# (3)点击到支付的流失率
rate3 = (user_type_count[0]-user_type_count[3])/user_type_count[0]
# (4)加购到购买的流失率
rate4 = (user_type_count[2]-user_type_count[3])/user_type_count[2]
print(rate1)
print(rate2)
print(rate3)
print(rate4)
输出:
0.979064047403986
0.9704118937573873
0.9895603722620376
0.64716810016968
建议:
发现流失率太大,浏览到收藏或加购 这一过程流失很大一部分,可能是用户花费很长时间选购合适的商品,可以优化平台的推荐算法,或者优化筛选栏,尽量减少用户浏览商品花费的时间,并将流程指标再细化后进行分析,找出影响用户流失的关键问题
针对浏览量高而销量不高的这部分商品,商家可以从以下几个方面提高销售额:
1.诚信吸引用户,有的商家为吸引用户点击,在商品展示页投放的价格具有较强吸引力,而实际价格偏高,反而造成用户流失;2.从用户角度出发设计详情页信息流展示,便于用户获取信息;
3.优化商品展示的形式,可以采用视频等更直观的展示方式;
4.评论区评价管理,尤其对于差评区的用户反馈进行认真对待,分析自身劣势,并做出积极的回应和弥补。
(5)计算用户消费次数
用户消费次数
# 用户消费次数
per_buy = data_user[data_user.behavior_type == '4'].groupby('user_id').size()
per_buy.head(5)
输出:
user_id
100001878 3
100012968 3
100014060 3
100024529 8
100027681 15
dtype: int64
快速统计:
# 用户消费次数的分布情况--快速统计
per_buy.describe()
输出:
count 6644.000000
mean 3.599338
std 4.525364
min 1.000000
25% 1.000000
50% 2.000000
75% 4.000000
max 163.000000
dtype: float64
发现用户消费次数的平均数mean在小于4次,需要重点关注消费小于4次的用户群体
统计消费用户的日消费次数
# 统计每位消费用户的日消费次数
day_buy = data_user[data_user['behavior_type'] == '4'].groupby(['date','user_id']).count()['behavior_type']
day_buy = day_buy.reset_index().rename(columns = {'behavior_type':'total'}) # 重置索引为默认索引,并修改列名为toatl
day_buy.head()
输出:
其它消费数据
# 日消费次数
tt = day_buy.groupby('date')['total'].sum()
# 日消费人数
pp = day_buy.groupby('date')['user_id'].count()
# 每日人均消费次数
renjun_buy = tt/pp
display(renjun_buy.head())
输出:
date
2014-11-18 1.393474
2014-11-19 1.422509
2014-11-20 1.384000
2014-11-21 1.364465
2014-11-22 1.593607
dtype: float64
绘图每日人均消费次数
renjun_buy.describe() # 对每日人均消费次数快速统计
输出:
count 31.000000
mean 1.393146
std 0.074626
min 1.310476
25% 1.359393
50% 1.377320
75% 1.401906
max 1.705186
dtype: float64
绘图
plt.figure(figsize = (20,9))
sns.lineplot(data = renjun_buy,label = '每日人均消费次数')
plt.show()
输出:
# 用户活跃统计 ,同一天,不同用户不同类型的商品id数量统计
user_count = data_user.groupby(['date','user_id','behavior_type']).count()['item_id'].reset_index().rename(columns = {'item_id':'total'})
user_count.head()
输出:
(6)计算成交率
成交率 = 付费人数/活跃人数
# 每日用户活跃总次数
huoyue_count = user_count.groupby('date')['total'].sum()
# display(huoyue_count)
# 每日用户付费次数
fufei_count = user_count[user_count['behavior_type'] == '4'].groupby('date')['total'].sum()
# display(fufei_count)
# 一日内的付费用户占比:
zhanbi_rate = fufei_count/huoyue_count
plt.figure(figsize = (16,9))
zhanbi_rate.plot()
plt.xlabel("日期")
plt.ylabel('成交率')
plt.show()
输出:
可见在双12 成交率偏高
(7)计算复购率
复购率 = 二次消费以上用户/总消费用户
data_rebuy = data_user[data_user['behavior_type'] == '4'].groupby('user_id')['date'].apply(lambda x:len(x.unique()))
# .apply(lambda x:len(x.unique()))
# data_tebuy:用户在不同日期的购买次数,这段代码相当于数据透视表,
# 用户行为放筛选栏,选购买的;用户ID放“行”,“date”放"值",然后函数apply是对日期进行去重
data_rebuy[data_rebuy>=2].count()/data_rebuy.count() # 购买次数大于2的用户数量/所有的购买用户数量
输出:
0.6243997599039616
5、用户价值分层(RFM模型)
(1)计算R和F
由于数据缺少M(消费金额),因此暂时通过R(最近消费时间)和F(消费频率)的数据来对客户价值进行分层
计算 R,最近购买时间,以2014-12-20为最后日期,计算距上次消费时间多久
# 计算 R,最近购买时间,以2014-12-20为最后日期,计算距上次消费时间多久
recent_buy_time = data_user[data_user['behavior_type'] == '4'].groupby('user_id')['date'].apply(lambda x:datetime(2014,12,20) - x.sort_values().iloc[-1])
recent_buy_time.head()
输出:
user_id
100001878 2 days
100012968 2 days
100014060 2 days
100024529 8 days
100027681 7 days
Name: date, dtype: timedelta64[ns]
对输出进行处理,重置索引和修改字段名
recent_buy_time = recent_buy_time.reset_index().rename(columns = {'date':'recent'})
recent_buy_time['recent'] = recent_buy_time['recent'].map(lambda x:x.days) # 将天数提取出来使用 .map(lambda)
recent_buy_time.head()
结果:
计算F,计算这段时间里,用户的购买次数
# 计算F
buy_freq = data_user[data_user["behavior_type"] == '4'].groupby('user_id')['date'].count().reset_index().rename(columns = {'date':'freq'})
buy_freq.head()
输出:
将R和F的结果拼接,用.merge()
RFM = pd.merge(recent_buy_time,buy_freq,on = 'user_id')
# RFM = pd.merge(recent_buy_time,buy_freq,left_on = 'user_id',right_on = 'user_id')
RFM.head()
(2)数据分箱
# 进行数据分箱
RFM['recent_value'] = pd.qcut(RFM.recent,2,labels = ['1','0']) # 数据分箱 对recent进行二分,recent小的设为1,大的设为0
RFM['freq_value'] = pd.qcut(RFM.freq,2,labels = ['0','1']) # 数据分箱, 对freq进行二分,freq大的设为1,小的设为0
RFM.head()
RFM['RFM'] = RFM['recent_value'].str.cat(RFM['freq_value']) # pandas中多列拼接成一列的函数
RFM.sample(10)
查看RFM列的等级分布
# 查看RFM列的等级分布
RFM['RFM'].value_counts() # 查看某列中有哪些不同的值,并计算每个值有多少个重复值
输出:
11 2262
00 2132
10 1347
01 923
Name: RFM, dtype: int64
(3)划分等级
# 对不同等级进行 级别标记,重要性从高到低
def trans_value(x): # 注意括号里的X
if x == '11':
return '重要价值用户:近期多次消费'
elif x == '10':
return '重要发展客户:近期少量消费'
elif x == '01':
return '重要保持用户:历史多次消费'
elif x == '00':
return '一般价值用户:历史少量消费'
RFM['用户等级'] = RFM['RFM'].map(trans_value)
RFM.sample(5)
输出:
(3)客户等级的可视化处理
RFM['用户等级'].value_counts()
输出:
重要价值用户:近期多次消费 2262
一般价值用户:历史少量消费 2132
重要发展客户:近期少量消费 1347
重要保持用户:历史多次消费 923
Name: 用户等级, dtype: int64
对客户等级做可视化处理展示
# 对客户等级做可视化处理展示
plt.figure(figsize = (16,9))
RFM['用户等级'].value_counts().plot(kind = 'barh')
plt.xlabel('数量')
plt.show()
输出:
画出饼状图:
plt.figure(figsize = (16,9))
plt.pie(RFM['用户等级'].value_counts(),labels = ['重要价值用户:近期多次消费','一般价值用户:历史少量消费','重要发展客户:近期少量消费','重要保持用户:历史多次消费'],autopct = '%1.2f%%',colors = ('b','g','r','y'))
# 画饼图,pie(数据,数据对应的标签,百分数保留两位小数,颜色),labels = RFM['用户等级'].unique()
plt.title('客户等级占比')
plt.show()
输出:
6、总结和建议
对于重要价值用户:近期多次消费,这些人最优质的,要重点关注并且保持,应该提高满意度,增加留存
对于重要保持用户:最近有消费,但消费频率不高,可以通过活动来刺激消费,提高消费频次
对于重要发展用户:最近没有购买,但是历史上购买频次较高,可以做触达,防止客户流失
对于一般价值用户:由于历史消费较少,最近也没消费记录,因此需要防止流失,可以推送优惠券或者针对项活动,再次唤醒购买意愿
整体结论和建议:
1、客户流失率较高:优化搜索匹配度和商品内容详情,根据用户喜好推荐;诚信页面展示,降低负面评论;随机选取用户进行问问卷调查,采纳客户建议
2、根据pv和uv,日期和时段的分布情况:在双十二活动期间可以加大营销力度,另外在每天下午18点后的用户活跃高峰期,推出营销活动,使更多的人知道
3、根据回购率情况:应将重点放在培养转化老顾客经常回购的习惯,适当情况通过给老用户优惠的活动,培养顾客忠诚度
4、通过RFM模型:对不同等级的用户采取不同的营销策略,分为重要客户维护、较重要客户再开发、一般客户争取。一般通过各种活动,分层次进行营销。
网友评论