#user_id:用户id,order_dt:购买日期,order_products:购买产品数量,order_amount:购买金额 #数据时间:1997年1月-1998年6月的行为数据
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
%matplotlib inline
#在行内显示
plt.style.use('ggplot')#更改绘图风格
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']#设置字体
#导入数据
columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table('cd_ma.txt',names=columns,sep='s+') #sep=s's+'代表匹配任意空格
df
#1.日期格式需要转换 #2.存在一个用户一天内购买多次 df.describe()
df['order_date']=pd.to_datetime(df['order_dt'],format='%Y%m%d')
#format='%Y%m%d'按照指定格式转成数据列,%Y代表四位的年,%m代表两位月,%d代表两位日,%y代表两位年,%h代表两位小时,%M代表两位分钟
df['month']=df['order_date'].astype('datetime64[M]')
#将order_date转化成精度为月份的数据列,astype('datetime64[M]')精度转换,转成月份进度
df.head()
#按照月份统计购买产品数量,消费金额,消费次数,消费人数
plt.figure(figsize=(20,15))#单位时英寸
#每月产品购买数量
plt.subplot(2,2,1)#绘制在两行两列第一个图中
df.groupby('month')['order_products'].sum().plot()#默认折线图
plt.title('每月产品购买数量')
#每月的消费金额
plt.subplot(2,2,2)#绘制在两行两列第二个图中
df.groupby('month')['order_amount'].sum().plot()#默认折线图
plt.title('每月的消费金额')
#每月消费次数
plt.subplot(2,2,3)#绘制在两行两列第三个图中
df.groupby('month')['user_id'].count().plot()#默认折线图
plt.title('月消费次数')
#每月消费人数(根据'user_id'去重统计)
plt.subplot(2,2,4)#绘制在两行两列第三个图中
df.groupby(by='month')['user_id'].apply(lambda x:len(x.drop_duplicates())).plot()
#apply(lambda x:len(x.drop_duplicates()))去重作用,然后用len()长度计算,作为人数数量的统计
plt.title('月消费次数')
#分析结果:前三个月销量非常高,后面下降然后基本趋于平稳
用户个体的消费分析
#1.用户消费金额,消费次数(产品数量)描述统计 user_grouped=df.groupby(by='user_id').sum() user_grouped.describe() #用户数237570,每个用户平均购买7个,但是中位数只有3个,并且最大值1033个,平均值大于中位数,明显右偏
#绘制用户的产品的购买数量与消费金额的散点图 df.plot(kind='scatter',x='order_products',y='order_amount')
#用户的消费分布图
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.xlabel('每个订单的消费金额')
df['order_amount'].plot(kind='hist',bins=50)
#bins区间分数,影响柱子的宽度,值越大柱子越细
plt.subplot(1,2,2)
plt.xlabel('每个uid购买数量')
df.groupby(by='user_id')['order_products'].sum().plot(kind='hist',bins=50)
#每个用户购买数量非常少
#总结,购买量低,价格小于50的占绝大多数
用户的累积消费金额占比分析
#进行用户分组,取出消费金额,进行求和,排序,重置索引 user_cumsum=df.groupby(by='user_id')['order_amount'].sum().sort_values().reset_index() user_cumsum
#每个用户消费金额累积 #举例: #np.cumsum(a)#累加求和函数 user_cumsum['amount_cumsum']=np.cumsum(user_cumsum['order_amount']) #金额累加求和函数 user_cumsum
#消费金额总值 amount_total=user_cumsum['amount_cumsum'].max()#消费总金额 user_cumsum['amount_prop']=user_cumsum.apply(lambda x:x['amount_cumsum']/amount_total,axis=1) #前多少名客户贡献度,apply(lambda)代表遍历函数,第一个x是一行的每一个数据,axis=1是横向操作的意思 user_cumsum.tail()
user_cumsum['amount_prop'].plot()
plt.ylabel('贡献度')
plt.xlabel('用户人数',fontsize=16,color='b')
#总结分析,前20000名贡献度40%,后四千名用户贡献度60%
用户消费行为
1.用户的首购时间
#用户分组,取最小值,即为首购时间 df.groupby(by='user_id')['order_date'].min().value_counts().plot()
最后一次购买时间
#用户分组,取最大值,即为最后时间 df.groupby(by='user_id')['order_date'].max().value_counts().plot() #绝大部分客户最后一次消费集中在前三个月,缺乏忠诚客户。。。。。
一、RFM模型介绍
RFM是Rencency(最近一次消费)、Frequency(消费频率)、Monetary(消费金额)三个指标首字母组合,是衡量当前用户价值和进行用户分层的重要工具
(1)Rencency:最近一次消费是指客户在平台最近一次消费和当前的时间间隔,理论上R越小的客户是价值越高的客户
(2)Frequency:消费频率是指客户在固定时间内的购买次数
(3)Monetary:消费金额是指一段时间内的消费金额
二、使用RFM模型进行用户分类
(1)根据需求,获取RFM三个维度的历史数据
(2)确定RFM三个维度的中值(业务理解、整体数据中值、二八法则)
(3)根据RFM和中值,对用户进行分层
透视表
也许大多数人都有在Excel中使用数据透视表的经历,其实Pandas也提供了一个类似的功能,名为pivot_table。虽然pivot_table非常有用,但是我发现为了格式化输出我所需要的内容,经常需要记住它的使用语法。
在本文中,我将会跟踪一个销售渠道(也称为漏斗)。基本的问题是,一些销售周期很长(可以想一下“企业软件”、“资本设备”等),而管理者想更详细地了解它一整年的情况。
典型的问题包括:
- 本渠道收入是多少?
- 渠道的产品是什么?
- 谁在什么阶段有什么产品?
- 我们年底前结束交易的可能性有多大?
很多公司将会使用CRM工具或者其他销售使用的软件来跟踪此过程。虽然他们可能拥有有效的工具对数据进行分析,但肯定有人需要将数据导出到Excel,并使用一个透视表工具来总结这些数据。
使用Pandas透视表将是一个不错的选择,应为它有以下优点:
- 更快(一旦设置之后)
- 自行说明(通过查看代码,你将知道它做了什么)
- 易于生成报告或电子邮件
- 更灵活,因为你可以定义定制的聚合函数
用户分层
1.构建rfm模型
#透视表的使用(index:相当于groupby,values:取出的数据列,aggfunc:key值必须在values中,且必须跟随有效的聚合函数)
rfm=df.pivot_table(index='user_id',
values=['order_products','order_amount','order_date'],
aggfunc={
'order_date':'max',#最后一次购买
'order_products':'sum',#购买产品总数量
'order_amount':'sum' #消费总金额
})
rfm.head()
rfm['R']=-(rfm['order_date']-rfm['order_date'].max())/np.timedelta64(1,'D')
#每个用户最后一次购买时间-日期列中做大值,再转换成天数,精度保留1为小数
#‘/np.timedelta64(1,'D')转成天数,精度保留一位小数
rfm.rename(columns={'order_amount':'M','order_products':'F'},inplace=True)
rfm.head()
#rmf计算方式,每一列数据减去数据所在列的平均值,有正有负。根据结果值与1作比较,如果结果>=1设置为1,否则为0
def rmf_func(x):#x代表每一列函数
level= x.apply(lambda x:'1' if x>=1 else '0')
#print(level)
lable=level['R']+level['F']+level['M']#举例:100,001
d={
'111':'重要价值客户',
'011':'重要保持客户',
'101':'重要发展客户',
'001':'重要挽留客户',
'110':'一般价值客户',
'010':'一般保持客户',
'100':'一般发展客户',
'000':'一般挽留客户'
}
result=d[lable]
return result
#rfm['R']-rfm['R'].mean()
rfm['label']=rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rmf_func,axis=1)
#lambda x的x代表某一列,第二个x代表某一列的所有值
rfm.head()
新用户,活跃用户,回流用户流失分析
1.新用户的第一:第一次消费
2.活跃用户即老客户
3.不活跃的用户则是时间窗口没有消费国的老客
4.回流用户,相当于回头客的意思
5.用户回流:自主回流和人工回流
pivoted_counts=df.pivot_table(
index='user_id',
columns='month',
values='order_dt',
aggfunc='count'
).fillna(0)
pivoted_counts
#由于浮点数不直观,并且需要转成是消费过即可,用0,1表示 df_purchase=pivoted_counts.applymap(lambda x:1 if x>0 else 0) #apply():作用dataframe数据中的整行或整列 #applymap():作用于dataframe数据中每一个元素 #apply():本身seriers函数,在df中无法使用map函数。作用于seriers中每一个元素 df_purchase.head()
#判断是哪种用户
def active_status(data):#data:整行数据,共18列
status = []#负责存放18个月的状态:ureg/new/active/unactive/return/
for i in range(18):
if data[i] == 0:#这个月没有消费
#本月没有消费
if len(status)==0:#前面没有任何记录(97年1月份)
status.append('unreg')
else:#开始判断上一个月状态
if status[i-1] =='unreg':#一直未消费过
status.append('unreg')
else:#上个月是new/active/unactive/return/中一种,但是这个月没有消费
status.append('unactive')
else:#这个月有消费
if len(status)==0:##前面没有任何记录(97年1月份)
status.append('new')#第一次消费
else:
if status[i-1]=='unactive':
status.append('return')#回流
elif status[i-1]=='unreg':
status.append('new')
else:
status.append('active')
return pd.Series(status,df_purchase.columns)#值是status,列名df_purchase中列名
purchase_states = df_purchase.apply(active_status,axis=1)#得到用户的分层结果
purchase_states.head()
purchase_states_ct.T.fillna(0).plot.area()#行列转换
#回流用户占比 rate=purchase_states_ct.T.fillna(0).apply(lambda x: x/x.sum(),axis=1) plt.plot(rate['return'],label='return') plt.plot(rate['active'],label='active') plt.legend()
用户分层
1.构建rfm模型
#透视表的使用(index:相当于groupby,values:取出的数据列,aggfunc:key值必须在values中,且必须跟随有效的聚合函数)
rfm=df.pivot_table(index='user_id',
values=['order_products','order_amount','order_date'],
aggfunc={
'order_date':'max',#最后一次购买
'order_products':'sum',#购买产品总数量
'order_amount':'sum' #消费总金额
})
rfm.head()
rfm['R']=-(rfm['order_date']-rfm['order_date'].max())/np.timedelta64(1,'D')
#每个用户最后一次购买时间-日期列中做大值,再转换成天数,精度保留1为小数
#‘/np.timedelta64(1,'D')转成天数,精度保留一位小数
rfm.rename(columns={'order_amount':'M','order_products':'F'},inplace=True)
rfm.head()
#rmf计算方式,每一列数据减去数据所在列的平均值,有正有负。根据结果值与1作比较,如果结果>=1设置为1,否则为0
def rmf_func(x):#x代表每一列函数
level= x.apply(lambda x:'1' if x>=1 else '0')
#print(level)
lable=level['R']+level['F']+level['M']#举例:100,001
d={
'111':'重要价值客户',
'011':'重要保持客户',
'101':'重要发展客户',
'001':'重要挽留客户',
'110':'一般价值客户',
'010':'一般保持客户',
'100':'一般发展客户',
'000':'一般挽留客户'
}
result=d[lable]
return result
#rfm['R']-rfm['R'].mean()
rfm['label']=rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rmf_func,axis=1)
#lambda x的x代表某一列,第二个x代表某一列的所有值
rfm.head()
#客户分层可是化,散点图
for label,grouped in rfm.groupby('label'):
#print(label,grouped)
x=grouped['F']#每个用户购买数量
y=grouped['R']#最近一次购买时间有最大值相差天数
plt.scatter(x,y,label=label)
plt.legend()#显示图例
plt.xlabel('F')
plt.ylabel('R')
apply函数
apply函数是`pandas`里面所有函数中自由度最高的函数。该函数如下:
Dataframe.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)
该函数最有用的是第一个参数,这个参数是函数,相当于C/C++的函数指针。
这个函数需要自己实现,函数的传入参数根据axis来定,比如axis = 1,就会把一行数据作为Series的数据结构传入给自己实现的函数中,我们在函数中实现对Series不同属性之间的计算,返回一个结果,则apply函数会自动遍历每一行Dataframe的数据,最后将所有结果组合成一个Series数据结构并返回。
三、lambda 特性
lambda 函数是匿名的:
所谓匿名函数,通俗地说就是没有名字的函数。lambda函数没有名字。
lambda 函数有输入和输出:
输入是传入到参数列表argument_list的值,输出是根据表达式expression计算得到的值。
lambda 函数拥有自己的命名空间:
不能访问自己参数列表之外或全局命名空间里的参数,只能完成非常简单的功能。
常见的lambda函数示例:
lambda x, y: x*y # 函数输入是x和y,输出是它们的积x*y lambda:None # 函数没有输入参数,输出是None lambda *args: sum(args) # 输入是任意个数参数,输出是它们的和(隐性要求输入参数必须能进行算术运算) lambda **kwargs: 1 # 输入是任意键值对参数,输出是1
四、lambda 常见用法
由于lambda语法是固定的,其本质上只有一种用法,那就是定义一个lambda函数。
在实际中,根据这个lambda函数应用场景的不同,可以将lambda函数的用法扩展为以下几种:
1、将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数。
示例:
add = lambda x, y: x+y
相当于定义了加法函数lambda x, y: x+y,并将其赋值给变量add,这样变量add就指向了具有加法功能的函数。
这时我们如果执行add(1, 2),其输出结果就为 3。
2、将lambda函数赋值给其他函数,从而将其他函数用该lambda函数替换。
示例:
# 为了把标准库time中的函数sleep的功能屏蔽(Mock),我们可以在程序初始化时调用: time.sleep=lambda x: None # 这样,在后续代码中调用time库的sleep函数将不会执行原有的功能。 # 例如: time.sleep(3) # 程序不会休眠 3 秒钟,而是因为lambda输出为None,所以这里结果是什么都不做
3、将lambda函数作为参数传递给其他函数。
典型的用法就是下面我们常见的几种高阶函数。
五、lambda 用法之高阶函数
map() 函数:
描述:
map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
语法:
map(function, iterable, ...)
参数:
function ----> 函数
iterable ----> 一个或多个序列
返回值:
Python 2.x 版本返回的是列表
Python 3.x 版本返回的是迭代器
# ===========一般写法:=========== # 1、计算平方数 def square(x): return x ** 2 map(square, [1,2,3,4,5]) # 计算列表各个元素的平方 # 结果: [1, 4, 9, 16, 25] # ===========匿名函数写法:============ # 2、计算平方数,lambda 写法 map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 结果: [1, 4, 9, 16, 25] # 3、提供两个列表,将其相同索引位置的列表元素进行相加 map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]) # 结果: [3, 7, 11, 15, 19]



