该篇主要是对上篇分析报告的一个过程解读。
总共有110段代码:(以下操作均在IPython中进行)
(部分重复代码略、图表的标题、x轴、y轴、图列等图标设置代码略)
-------------------------------------
大纲:
引入及设置部分:1~6(6段)
Part 1 :7~28(22段)
Part 2 :29~45(17段)
Part 3 :46~61(16段)
Part 4 :62~106(45段)
用户生命周期 :62~73(12段)
用户购买周期 :74~79(6段)
回流率/流失率:80~99(20段)
复购率/回购率:100~106(7段)
Part 5 :补充4段,Part 1每月/日走势图
Let's go!
引入及设置部分
调用库和一些设置
In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: import matplotlib.pyplot as plt
In [4]: from datetime import datetime
In [5]: %matplotlib #使在交互软件中可视化
Using matplotlib backend: Qt5Agg
In [6]: plt.style.use('ggplot') #设置风格
Part 1数据总览
知识点及说明:
①描述性分析、用户的贡献情况、消费数量和金额关系
②groupby()函数:变换维度统计用户的情况,每天的销售情况以及每月的销售情况。
③info()函数:查看数据的基本信息类型、是否有缺失值
④describe()函数:查看数据的描述性分析(极值、均值、四分位等)
拿到一份csv文件格式的数据,先打开看看有什么信息。该数据没有表头,字符串是空格分割,一共有四列数据:用户ID、购买的日期、购买的数量,购买的金额。下面读取数据,并查看前五行以及统计描述、文件基本信息。
In [7]:columns=['user_id','order_dt','order_products','order_amount']
In [8]: df = pd.read_csv(r'C:\Users\55414\Desktop\CDNOW_master.txt',
names=columns,sep='\s+')
In [9]: df.head()
Out[9]:
user_id order_dt order_products order_amount
0 1 19970101 1 11.77
1 2 19970112 1 12.00
2 2 19970112 5 77.00
3 3 19970102 2 20.76
4 3 19970330 2 20.76
In [10]: df.describe()
Out[10]:
user_id order_dt order_products order_amount
count 69659.000000 6.965900e+04 69659.000000 69659.000000
mean 11470.854592 1.997228e+07 2.410040 35.893648
std 6819.904848 3.837735e+03 2.333924 36.281942
min 1.000000 1.997010e+07 1.000000 0.000000
25% 5506.000000 1.997022e+07 1.000000 14.490000
50% 11410.000000 1.997042e+07 2.000000 25.980000
75% 17273.000000 1.997111e+07 3.000000 43.700000
max 23570.000000 1.998063e+07 99.000000 1286.010000
In [11]: df.info()
RangeIndex: 69659 entries, 0 to 69658
Data columns (total 4 columns):
user_id 69659 non-null int64
order_dt 69659 non-null int64
order_products 69659 non-null int64
order_amount 69659 non-null float64
dtypes: float64(1), int64(3)
memory usage: 2.1 MB
观察以上数据发现:
①总用户数为23570人,总订单数为69659个,没有缺失值。
②每个订单平均有2.4个产品,35.89元。
③每个订单的产品数标准差为2.33,说明数据比较集中,用户每次购买的产品数量大多数在1~5个之间。
④订单的产品数量的极差为98个,上四分位为3个,说明订单产品数量超过3个的最多占到总订单数的25%。
⑤订单的金额最小值为0,可能存在优惠或减免的促销活动;上四分位只有43.7元,说明绝大部分的用户为低消费群体。符合二八原则期望。
⑥order_dt表示时间,但文件记录它只是年月日组合的一串整型数字,没有时间含义。
下面以用户维度进行转换
In [12]: df_user =df.groupby('user_id').sum()
In [13]: df_user.describe()
Out[13]:
order_dt order_products order_amount
count 2.357000e+04 23570.000000 23570.000000
mean 5.902627e+07 7.122656 106.080426
std 9.460684e+07 16.983531 240.925195
min 1.997010e+07 1.000000 0.000000
25% 1.997021e+07 1.000000 19.970000
50% 1.997032e+07 3.000000 43.395000
75% 5.992125e+07 7.000000 106.475000
max 4.334408e+09 1033.000000 13990.930000
In [14]: df_user.info()
Int64Index: 23570 entries, 1 to 23570
Data columns (total 3 columns):
order_dt 23570 non-null int64
order_products 23570 non-null int64
order_amount 23570 non-null float64
dtypes: float64(1), int64(2)
memory usage: 736.6 KB
从描述信息得知:
①总用户数量为23570
②每个用户平均消费产品7个,金额(客单价)106.08元。
③每个用户购买产品数标准差为16.9个,说明数据的离散程度比较大。
④用户所购买产品数和金额的平均值和上四分位比较接近,而且极差相差悬殊,说明存在小部分的高额消费用户。
消费总金额可以对order_amount列进行求和
In [15]: df.order_amount.sum()
Out[15]: 2500315.6300004106
其他不再赘述。不过再转换为时间维度前,先把order_dt转换为时间类型,如下:
In [16]: df['order_date'] =pd.to_datetime(df.order_dt,
format='%Y%m%d') #新增一列,将日期整型转日期格式
In[17]:df['month']=df.order_date.values.astype('datetime64[M]') #新增一列,提取每个日期的月份
In [18]: df.head()
Out[18]:
user_id order_dt order_products order_amount order_date month
0 1 19970101 1 11.77 1997-01-01 1997-01-01
1 2 19970112 1 12.00 1997-01-12 1997-01-01
2 2 19970112 5 77.00 1997-01-12 1997-01-01
3 3 19970102 2 20.76 1997-01-02 1997-01-01
4 3 19970330 2 20.76 1997-03-30 1997-03-01
接下来看用户的贡献情况,分别描述用户和消费金额、订单数量的关系。
In [19]: user_amount=df.groupby('user_id').order_amount.sum().sort_values().reset_index()
In [20]:user_amount['amount_cumsum']=user_amount.order_amount.cumsum()
In [21]: user_amount.head()
Out[21]:
user_id order_amount amount_cumsum
0 10175 0.0 0.0
1 4559 0.0 0.0
2 1948 0.0 0.0
3 925 0.0 0.0
4 10798 0.0 0.0
将消费金额进行求和并排序,再累加。查看前5行,发现都是0.0,说明可能是存在大力的促销减免活动
(这可能是前三个月用户数量暴涨的一个重要因素)。看看尾5行,如下:
In [22]: user_amount.tail()
Out[22]:
user_id order_amount amount_cumsum
23565 7931 6497.18 2463822.60
23566 19339 6552.70 2470375.30
23567 7983 6973.07 2477348.37
23568 14048 8976.33 2486324.70
23569 7592 13990.93 2500315.63
目的是查看用户的贡献情况,把数据转换成百分比更直观。接下来新增一行把累计情况转换成百分比。
In [23]: user_amount['prop']=user_amount.apply(lambdax:x.amount_cumsum/user_amount.amount_cumsum.max(),axis=1)
In [24]: user_amount.tail()
Out[24]:
user_id order_amount amount_cumsum prop
23565 7931 6497.18 2463822.60 0.985405
23566 19339 6552.70 2470375.30 0.988025
23567 7983 6973.07 2477348.37 0.990814
23568 14048 8976.33 2486324.70 0.994404
23569 7592 13990.93 2500315.63 1.000000
由图看出,前20000个用户贡献了40%的消费。后面3000多位用户贡献了60%,呈现二八倾向。
将以上代码中的order_amount改为order_dt,即可查看用户和订单数量的关系。
了解了用户的贡献之后,再看看订单订购的数量和用户消费金额之间的关系。引入seaborn模块做线性回归分析。
In [26]: import seaborn as sns
In [27]: sns.regplot(x='order_amount',y='order_products', data=df)
In [28]: sns.regplot(x='order_amount',y='order_products',data = df.groupby('user_id').sum())
分别从订单和用户两个维度进行分析,
订单维度:订单消费金额和订单商品量呈规律性,每个商品十元左右。订单的极值较少,超出1000的就几个
用户维度:用户消费金额和订购商品数量的规律性更强。说明销售的商品相对比较单一。
Part2用户分层——RFM模型
知识点及说明:
①R的参数:根据用户最近一次消费的时间与截止数据采集当天的间隔,计算出时间间隔均值,然后用每个用户的时间间隔与该均值比较,确定R的参数;
②F的参数:根据用户的总购买数量与所有用户的平均购买数量作比,确定F的参数;
③M的参数:根据用户的总购买金额与所有用户的平均购买金额作比,确定M的参数。
④np.timedelta64(1, 'D')将日期类型(timedelta)转换成数值类型
⑤这里使用均值做为判断条件,最近一次消费的时间小于均值时,R返回1,否则返回0;购买数量和金额大于均值时返回1,否则返回0。
In [29]: rfm = df.pivot_table(index='user_id',
...: values=['order_products', 'order_amount', 'order_date'],
...: aggfunc={'order_date': 'max','order_amount':'sum','order_products': 'sum'})
In [30]: rfm['R'] = (rfm.order_date -rfm.order_date.max()) / np.timedelta64(1, 'D')
In [31]:rfm.rename(columns={'order_products': 'F', 'order_amount': 'M'}, inplace=True) #重命名列
In [32]: rfm.head()
Out[32]:
M order_date F R
user_id
1 11.77 1997-01-01 1 -545.0
2 89.00 1997-01-12 6 -534.0
3 156.46 1998-05-28 16 -33.0
4 100.50 1997-12-12 7 -200.0
5 385.61 1998-01-03 29 -178.0
In [33]: def rfm_func(x):
...: level = x.apply(lambdax: '1' if x >= 0 else '0')
...: label = level.R +level.F + level.M # 字符串拼接
...: level_class = {
...: '111': '重要价值客户',
...: '011': '重要保持客户',
...: '101': '重要挽留客户',
...: '001': '重要发展客户',
...: '110': '一般价值客户',
...: '010': '一般保持客户',
...: '100': '一般挽留客户',
...: '000': '一般发展客户'}
...: result =level_class[label]
...: return result
...:
In [34]: rfm['label'] = rfm[['R', 'F','M']].apply(lambda x: x - x.mean()).apply(rfm_func, axis=1) #用均值作比,调用函数
In [35]: rfm.head()
Out[35]:
M order_date F R label
user_id
1 11.77 1997-01-01 1 -545.0 一般发展客户
2 89.00 1997-01-12 6 -534.0 一般发展客户
3 156.46 1998-05-28 16 -33.0 重要价值客户
4 100.50 1997-12-12 7 -200.0 一般挽留客户
5 385.61 1998-01-03 29 -178.0 重要价值客户
对用户分层完,进行绘制饼图,查看各层用户占比。
In [36]: use_c =rfm.groupby('label').count()
In [37]: plt.axis('equal')
In [38]: labels = ['一般价值客户','一般保持客户','一般发展客户','一般挽留客户
...: ','重要价值客户','重要保持客户','重要发展客户','重要挽留客户']
In [39]:plt.pie(use_c['M'],autopct='%1.1f%%',labels = labels,pctdistance=0.9,
...:labeldistance=1.1,radius=2.2,startangle = 180)
此时,显示的图表没有显示出汉字,需要对maplotlib进行设置,如下:
In [40]:plt.rcParams['font.sans-serif']=['SimHei'] #设置字体显示类型
In [41]: plt.rcParams['font.family']='sans-serif' #字体集
In [42]: plt.rcParams['axes.unicode_minus']= False #设置显示负号
接下来对RFM模型的各个指标进行查询,并在excel中进行绘制。
In [43]: -rfm.groupby('label').mean().R
Out[43]:
label
一般价值客户 142.951456
一般保持客户 471.363636
一般发展客户 493.947350
一般挽留客户 179.123636
重要价值客户 113.585200
重要保持客户 455.353240
重要发展客户 475.029046
重要挽留客户 171.105740
Name: R, dtype: float64
In [44]: rfm.groupby('label').sum().F
Out[44]:
label
一般价值客户 1712
一般保持客户 650
一般发展客户 29346
一般挽留客户 13977
重要价值客户 107789
重要保持客户 11121
重要发展客户 1263
重要挽留客户 2023
Name: F, dtype: int64
In [45]: rfm.groupby('label').sum().M
Out[45]:
label
一般价值客户 19937.45
一般保持客户 7181.28
一般发展客户 438291.81
一般挽留客户 196971.23
重要价值客户 1592039.62
重要保持客户 167080.83
重要发展客户 33028.40
重要挽留客户 45785.01
Name: M, dtype: float64
从RFM分层可知,六成左右的客户属于一般发展客户(该分层中的底层用户),最高层的重要价值客户占比19.3%。这一批重要价值客户(种子用户)贡献了63.6%的销售额,
在消费领域中,狠抓高质量用户是万古不变的道理。
Part 3 用户活跃度
知识点及说明:
①分类标准:
新用户new: 第一次消费。
活跃用户active: 在某一个时间窗口内有过消费的老客。
不活跃用户unactive: 在时间窗口内没有消费过的老客。
回流用户return:在上一个窗口中没有消费,而在当前时间窗口内有过消费。
潜在用户unreg: 截止当前窗口还没有消费过
以上的时间窗口都是按月统计。
比如某用户在
1月第一次消费,那么他在1月的分层就是新用户;
2月消费过,则是活跃用户;
3月没有消费,此时是不活跃用户;
4月再次消费,此时是回流用户;
5月还是消费,是活跃用户
②思路:对数据进行透视,然后用applymap函数将透视表的值转换成布尔值(0/1),最后定义一个函数对其进行判断、分类。
In [46]: pivoted_date =df.pivot_table(index='user_id', columns='month',
...: values='order_dt',aggfunc='count').fillna(0) #对df进行透视,用于计算的值及计算方法
In [47]: columns_month = df.month.sort_values().astype('str').unique()
In [48]: pivoted_date.columns =columns_month
In [49]: pivoted_date_bool =pivoted_date.applymap(lambda x: 1 if x > 0 else 0)
In [50]: def active_status(df):
...: status = []
...: for i in range(18):
...: #若本月没有消费
...: if df[i] == 0:
...: if len(status)> 0: #排除第一月
...: ifstatus[i-1] == 'unreg':
...: status.append('unreg')
...: else:
...: status.append('unactive')
...: else:
...: status.append('unreg') #<=0,说明是第一个月,未消费过
...: #若本月消费
...: else:
...: if len(status) ==0:
...: status.append('new')
...: else:
...: ifstatus[i-1] == 'unactive':
...: status.append('return')
...: elifstatus[i-1] == 'unreg':
...: status.append('new')
...: else:
...: status.append('active')
...: return status
...:
In [51]: user_level =pivoted_date_bool.apply(lambda x: active_status(x), axis=1)
In [52]: user_level.head()
Out[52]:
1997-01-01 1997-02-01 1997-03-01 1997-04-01 1997-05-01 1997-06-01 \
user_id
1 new unactive unactive unactive unactive unactive
2 new unactive unactive unactive unactive unactive
3 new unactive return active unactive unactive
4 new unactive unactive unactive unactive unactive
5 new active unactive return active active
1997-07-01 1997-08-01 1997-09-01 1997-10-01 1997-11-01 1997-12-01 \
user_id
1 unactive unactive unactive unactive unactive unactive
2 unactive unactive unactive unactive unactive unactive
3 unactive unactive unactive unactive return unactive
4 unactive return unactive unactive unactive return
5 active unactive return unactive unactive return
1998-01-01 1998-02-01 1998-03-01 1998-04-01 1998-05-01 1998-06-01
user_id
1 unactive unactive unactive unactive unactive unactive
2 unactive unactive unactive unactive unactive unactive
3 unactive unactive unactive unactive return unactive
4 unactive unactive unactive unactive unactive unactive
5 active unactive unactive unactive unactive unactive
潜在用户unreg截止当前窗口还没有消费过,是未来作为新客,在未发生交易之前,统计时应该去掉。
In [53]: user_level_del_unreg =user_level.replace('unreg', np.nan).apply(lambda x: pd.value_counts(x))
In [54]: user_level_del_unreg.T.fillna(0)
Out[54]:
active new return unactive
1997-01-01 0.0 7846.0 0.0 0.0
1997-02-01 1157.0 8476.0 0.0 6689.0
1997-03-01 1681.0 7248.0 595.0 14046.0
1997-04-01 1773.0 0.0 1049.0 20748.0
1997-05-01 852.0 0.0 1362.0 21356.0
1997-06-01 747.0 0.0 1592.0 21231.0
1997-07-01 746.0 0.0 1434.0 21390.0
1997-08-01 604.0 0.0 1168.0 21798.0
1997-09-01 528.0 0.0 1211.0 21831.0
1997-10-01 532.0 0.0 1307.0 21731.0
1997-11-01 624.0 0.0 1404.0 21542.0
1997-12-01 632.0 0.0 1232.0 21706.0
1998-01-01 512.0 0.0 1025.0 22033.0
1998-02-01 472.0 0.0 1079.0 22019.0
1998-03-01 571.0 0.0 1489.0 21510.0
1998-04-01 518.0 0.0 919.0 22133.0
1998-05-01 459.0 0.0 1029.0 22082.0
1998-06-01 446.0 0.0 1060.0 22064.0
新用户前部都在前三个月,前面数据展示部分订单的消费金额为零,可能是网站有减免活动。将数据以柱状图形式展示,查看各个时间窗口的值的差距。
In [55]: bar_values =user_level_del_unreg.T.div(user_level_del_unreg.T.sum(1),axis=0).fillna(0)#归1化
In [56]: bar_values.plot.bar(figsize=(16,8))
活跃用户和回流用户的占比和不活跃用户的占比相差十分悬殊。单独查看活跃用户和回流用户的占比。
In [57]: user_level_del_unreg.apply(lambdax: x / x.sum(), axis=0).fillna(0).T[['active', 'return']].plot()
In [58]: values =user_level_del_unreg.fillna(0).T[['active', 'return']].sum()
In [59]: plt.axis('equal')
In [60]: labels = ['active', 'return']
In [61]:plt.pie(values,autopct='%2.2f%%',labels=labels)
Part 4 用户画像
用户生命周期、用户购买周期、回流率、流失率、复购率、回购率、
查看用户周期为0天的占比,超过一半!说明大部分用户的粘性和忠诚度很低!
In [62]: user_life =df.groupby('user_id').order_date.agg(['min','max'])
In [63]: (user_life['min'] ==user_life['max']).value_counts()
Out[63]:
True 12054
False 11516
dtype: int64
In [64]: rate = (user_life['min'] ==user_life['max']).value_counts()
In [65]: labels = ['生命周期为0天','生命周期大于0天']
In [66]: plt.axis('equal')
In [67]: plt.pie(rate,labels=labels,
...: autopct='%2.1f%%',
...: startangle=90,
...: radius=1.5)
查看整体用户的生命周期
知识点及说明:
①统计逻辑:第一次消费时间至最后一次消费时间为整个用户生命周期
②思路:用用户最后一次消费的时间减去第一次消费的时间。
In [68]: life_seri =(user_life['max'] - user_life['min'])
...: /np.timedelta64(1,'D')
In[69]: life_seri.hist(bins=50)
In[70]: life_seri.mean()
Out[70]:134.8719558761137
图表呈现出长尾模型,说明大部分用户的生命周期比较短。接下来把非零的用户筛选出来分析。
In [71]: life_seri = (user_life['max'] -user_life['min']).reset_index()[0]/np.timedelta64(1,'D')
In [72]:life_seri[life_seri>0].hist(bins=50)
Out[72]:
In [73]: life_seri[life_seri > 0].mean()
Out[73]: 276.0448072247308
排除极值0天的影响之后,虽然仍旧有不少用户生命周期靠拢在0天,但数据呈现双峰图,有相当部分用户的生命周期在400天到500天之间,这已经属于忠诚用户。对于有过两次以上消费但生命周期在一个月内的而用户应当尽量引导,逐步培养用户的忠诚度。
再看看用户的购买周期。
知识点及说明:
①统计逻辑:求用户相邻两次消费的时间间隔;
②思路:先求出用户后续的所有产生消费的日期与其第一次消费的日期进行相减,求出差值,再使用一阶差分进行相减即可求得用户购买周期;
③merge()函数:将用户消费行为和第一次消费时间对应上,形成一个新的DataFrame。它和SQL中的join差不多;
④shift()函数:可以移动行或列的值,将前后的值相减可以进行错位求差值(一阶差分)
In [74]: df2=df[['user_id','order_products', 'order_amount', 'order_date']] #切片
In [75]: buy_df = pd.merge(left=df2, right= user_life['min'].reset_index(), #使用merge联结两个DF
...: how = 'inner', on = 'user_id', suffixes = ('', '_min'))
In [76]: buy_df['date_diff'] =(buy_df.order_date -buy_df['min'])/np.timedelta64(1,'D')
In [77]: buy_gap =buy_df.groupby('user_id').apply(lambda x :x.date_diff.shift(-1)-x.date_diff)
In [78]: buy_gap.hist(bins=100)
In [79]: buy_gap.mean()
Out[79]: 68.97376814424265
用户得购买平均周期为69天,想要召回用户,在60天左右的消费间隔是比较好的。直方图也是典型得长尾分布,大部分用户得消费间隔比较短。
接下来分析用户的留存率:
知识点及说明:
①统计逻辑:用户在第一次消费后,进行第二次消费的比率。(某个时间窗口,第二次消费数/总的一次消费数),
②思路:对用户购买周期的数据分区间进行统计;再透视相关数据,然后用applymap函数将透视表的值转换成布尔值(0/1),最后求DF.sum()/DF.count()即为所求。
③cut()将区间切为左开右闭区间(即date_diff=0并没有被划分入0~3天)
如果用户仅消费了一次或者在第一天内消费了多次但是往后没有消费,留存率都是是0。统计时剔除。
In [80]: bins = [0, 3, 7, 15, 30, 60, 90,180, 365]
In [81]: buy_df['date_diff_bins'] =pd.cut(buy_df.date_diff, bins=bins)
In [82]: pivoted_buy_df =buy_df.pivot_table(index='user_id', columns='date_d
...: iff_bins',values='order_amount', aggfunc=sum)
In [83]: pivoted_buy_df_bool =pivoted_buy_df.fillna(0).applymap(lambda x: 1 if x > 0 else 0)
In [84]: retention_rate =(pivoted_buy_df_bool.sum() / pivoted_buy_df_bool.count())
In [85]: retention_rate.plot()
In [86]: retention_rate.plot.bar()
In [87]: pivoted_buy_df.mean()
Out[87]:
date_diff_bins
(0, 3] 35.905798
(3, 7] 36.385121
(7, 15] 42.669895
(15, 30] 45.964649
(30, 60] 50.215070
(60, 90] 48.975277
(90, 180] 67.223297
(180, 365] 91.960059
dtype: float64
从用户在后续各阶段的平均消费金额效果看,用户第一次消费后的0~3天内,更可能消费更多。虽然后面时间段的金额高,但是它的时间范围也宽广。
用户流失率:
知识点及说明:
①统计逻辑:用户最后一次购买时间距离最后统计时间窗口的间隔大于一个统计窗口(这里设置为1个月)即为流失。累计所有流失用户数量除以当期用户的总数即为流失率
②思路:求每月发生购买的用户数/新用户数每月累加值,得到每月留存的用户比例,用1减去该比例即是流失率
In [88]: pivoted_data =df.pivot_table(index='user_id', columns='month', values='order_products',aggfunc=sum)
In [89]: pivoted_data_bool =pivoted_data.applymap(lambda x: 1 if x>0 else np.nan)
In [90]: month_buyers =pivoted_data_bool.count() #统计各列,用户id为统计对象,结果得到每个月的消费人数
In [91]: month_buyers.name = 'user_buy'
In [92]: month_new=df.groupby('user_id').month.min().value_counts()
#求首次消费的用户数(即新用户数量)在每个月的分布情况
In [93]: month_new.name = 'new_users'
In [94]: month_new.index.name = 'month'
In [95]: month_buyers_new =pd.merge(left=month_buyers.reset_index(), right=m
...: onth_new.reset_index(),how='left', on='month', suffixes=('','_min')).fillna(0)
In [96]: month_buyers_new['churn_rate'] =1-month_buyers_new.user_buy/month_buyers_new.new_users.cumsum()
In [97]: month_buyers_new.churn_rate.index= month_buyers_new.month.sort_values().astype('str')
In [98]: month_buyers_new.churn_rate.plot()
In [99]:month_buyers_new.churn_rate.plot.bar()
前三月,用户的流失率急剧上升,后续稳定在93%。
求用户的回购率
①统计逻辑:某一个时间窗口内消费的用户,在下一个时间窗口仍旧消费的占比。
②思路:将上期有消费且本期都有消费的用户,记为1;上期和本期只消费一次的记为0;都没有消费的记为NaN。然后统计DF.sum()和DF.count()的人数进行相除。
In [100]: def back_buy(data):
...: status=[]
...: for i in range(17):
...: if data[i]==1:
...: if data[i+1]==1:
...: status.append(1)
...: if data[i+1]==0:
...: status.append(0)
...: else :
...: status.append(np.nan)
...: else:
...: status.append(np.nan)
...: return status
...:
In [101]: pivoted_date_bool_2 =pivoted_date_bool.apply(back_buy,axis=1)
In [102]: back_buy_rate = (pivoted_date_bool_2.sum()/ pivoted_date_bool_2.count())
In [103]: back_buy_rate.plot()
复购率
①统计逻辑:在某时间窗口内消费两次及以上的用户在总消费用户中占比。
②思路:将数据转换未,消费两次及以上记为1,消费一次记为0,没有消费记为NaN。然后统计DF.sum()和DF.count()的人数进行相除。
In [104]: pivoted_date_bool_3 =pivoted_date.applymap(lambda x: 1 if x > 1 else np.NaN if x == 0 else 0)
In [105]: rebuy_rate =(pivoted_date_bool_3.sum() / pivoted_date_bool_3.count())
In [106]: rebuy_rate.plot()
Part 5 补充
In [1]:df.groupby('month').order_products.sum().plot(figsize=(12,6))
Out[1]: 0x21d31575b00> In [2]:df.groupby('month').order_amount.sum().plot(figsize=(12,6)) Out[2]: In [3]: df.groupby('month').order_products.count().plot(figsize=(12,6)) Out[3]: In [4]:df.groupby('month').user_id.count().plot(figsize=(12,6)) Out[4]: part 1每月/日销量、销售额、订单量、用户数的代码(直接将month->order_date即可得到每日走势图) 来一波彩蛋吧! 图表中显示汉字及负号: #中文乱码的处理(字体设置、负号问题) plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['font.family']='sans-serif' plt.rcParams['axes.unicode_minus'] =False 如果还出现乱码,需要修改一些默认的设置,参考: https://blog.csdn.net/minixuezhen/article/details/81516949 设置图表的标题、坐标轴标签及刻度值、字号的大小: plt.xlabel(x,fontsize=20)plt.ylabel(y,fontsize=20) plt.title(t,fontsize=20) plt.xticks(fontsize=20) plt.yticks(fontsize=20) 对坐标轴进行格式化,显示百分比: from matplotlib.tickerimport FuncFormatter def to_percent(temp,position): return '%2.1f' % (100 * temp) +'%' plt.gca().xaxis.set_major_formatter(FuncFormatter(to_percent)) plt.gca().yaxis.set_major_formatter(FuncFormatter(to_percent)) 图片存储及展示: plt.savefig(path, bbox_inches='tight', dpi=200)#一定要在show()前设置 plt.show() #主要是在其他编辑器,如pycharm等对图表进行可视化 来自小明传书,一起嗑数据。 公众号(小明传书)后台恢复:CD,可获取原始数据。 公众号(小明传书)后台恢复:CD分析,获取本人所整理的代码及分析过程。Part 6 总结