分析过程--代码集

该篇主要是对上篇分析报告的一个过程解读。

总共有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()

<class 'pandas.core.frame.DataFrame'>

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()

<class 'pandas.core.frame.DataFrame'>

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]: <matplotlib.axes._subplots.AxesSubplotat 0x224561fdef0>

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]:<matplotlib.axes._subplots.AxesSubplot at

0x21d31575b00>

In [2]:df.groupby('month').order_amount.sum().plot(figsize=(12,6))

Out[2]:<matplotlib.axes._subplots.AxesSubplot at 0x21d31cdc780>

In [3]: df.groupby('month').order_products.count().plot(figsize=(12,6))

Out[3]:<matplotlib.axes._subplots.AxesSubplot at 0x21d30ae9198>

In [4]:df.groupby('month').user_id.count().plot(figsize=(12,6))

Out[4]:<matplotlib.axes._subplots.AxesSubplot at 0x21d31575b00>

每月销售产品数量
每月销售总金额
每月订单数量
每月发生交易的用户数


part 1每月/日销量、销售额、订单量、用户数的代码(直接将month->order_date即可得到每日走势图)


Part 6 总结


来一波彩蛋吧!

图表中显示汉字及负号:

#中文乱码的处理(字体设置、负号问题)

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分析,获取本人所整理的代码及分析过程。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • pyspark.sql模块 模块上下文 Spark SQL和DataFrames的重要类: pyspark.sql...
    mpro阅读 9,424评论 0 13
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,260评论 0 10
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查询集API 参...
    阳光小镇少爷阅读 3,802评论 0 8
  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,258评论 0 23
  • 真的是很讨厌他和他妈 人生不太完美的瑕疵点 唉,身体越来越发虚,得认真运动认真读书认真谈尤克里里 莫名觉得被绿 妈...
    小宝尼阅读 144评论 0 0