会员价值是以用户价值为出发点,通过模型或者方法将会员划分成几个群体或者层级,比如:支付宝将会员划分为钻石、黄金、白银等,又或者将会员价值评定为高、中和低。
通常,不同公司会采用不同的指标来判断会员的价值大小,常用的指标有如下几个:
价值度模型一般基于交易行为产生,衡量的是有实体转化价值的行为。常用的价值度模型是RFM。
RFM模型是根据会员最近一次购买时间R(Recency)、购买频率(Frequency)、购买金额(Monetary)计算得出RFM得分,通过这三个维度衡量客户的订单活跃价值,常用来做客户分群和价值区分。该模型常用于电子商务(即交易类)企业的会员分析。
RFM模型基于一个固定的时间点来做模型分析,因此今天做的RFM得分跟7天前做的结果可能不一样,原因是每个客户在不同的时间节点所得到的数据不同。
import pandas as pd
import numpy as np
import time
import pymysql
2.2 导入相关数据
在导入数据时,可直接对各列的数据类型进行定义,也可在导入数据之后,对各列的数据类型进行转换。这里选择读取数据时自定义数据库数据类型。同时将索引值设置为用户ID。
dtypes={'ORDERDATE':object,'ORDERID':object,'AMOUNTINFO':float}
data=pd.read_csv('sales.csv',dtype=dtypes,index_col='USERID')
2.3 数据审查和校验,主要包括数据概览、缺失值审查等
(1)数据概览
pandas的describe()方法可以很好的实现对数值型数据的分布做一个很好的描述,同时,还可查看数据的长度、组成规律、类型等信息。
print('DATA Overview:')
print(data.head())
print('\n')
print('Data desc:')
print(data.describe())
(2)缺失值审查
缺失值处理是数据预处理的重要步骤之一,缺失值对后续的计算会产生重大的影响。
#缺失值判断
na_cols=data.isnull().any(axis=0)
na_rows=data.isnull().any(axis=1)
print('总共缺失的行数(任意一列的缺失):',na_rows.sum())
print(data[na_rows])
以下为数据审查和校验结果:
DATA Overview:
ORDERDATE ORDERID AMOUNTINFO
USERID
142074 2016-01-01 4196439032 9399.0
56927 2016-01-01 4198324983 8799.0
87058 2016-01-01 4191287379 6899.0
136104 2016-01-01 4198508313 5999.0
117831 2016-01-01 4202238313 5399.0
Data desc:
AMOUNTINFO
count 86127.000000
mean 744.705249
std 1425.211176
min 0.500000
25% 13.000000
50% 59.000000
75% 629.000000
max 30999.000000
总共缺失的行数(任意一列的缺失): 10
ORDERDATE ORDERID AMOUNTINFO
USERID
75849 2016-01-01 4197103430 NaN
103714 NaN 4136159682 189.0
155209 2016-01-01 4177940815 NaN
139877 NaN 4111956196 6.3
54599 2016-01-01 4119525205 NaN
65456 2016-01-02 4195643356 NaN
122134 2016-09-21 3826649773 NaN
116995 2016-10-24 3981569421 NaN
98888 2016-12-06 3814398698 NaN
145951 2016-12-29 4139830098 NaN
由返回结果可知,ORDERDATE列和AMOUNTINFO列均有数据缺失,且缺失记录共有10行。由于10行在总共的86135中占比较小,这里可以直接删除。
2.4. 数据预处理准备工作,包括数据异常、格式转换和处理
(1)异常值处理
由于订单金额为0.5的订单是属于促销优惠券生成的订单,这些订单用来为用户消费时提供优惠券,没有实际意义,因此需要去除。除了0.5元的订单,所以低于1元的订单均有问题。
#删除异常数据,包括缺失值数据和非正常数据
sales_data=data.dropna()
sales_data=sales_data[sales_data['AMOUNTINFO']>1]
print('经过数据预处理后的记录数总共为:',sales_data.shape[0])
(2)日期格式转换
日期格式转换的目的是基于时间间隔的计算,这样才能算出R距离指定日期的天数。
#日期格式转换
sales_data['ORDERDATE']=pd.to_datetime(sales_data['ORDERDATE'],format='%Y-%m-%d')
print("数据的格式:",sales_data.dtypes)
2.5 计算RFM得分
(1)分别计算R,F,M的得分
deadline_date=pd.datetime(2017,1,1)
#计算间隔天数,以及R\F\M的得分
interval_days=(deadline_date-recency_value).dt.days
r_score=pd.cut(interval_days,5,labels=[5,4,3,2,1])
f_score=pd.cut(frequency_value,5,labels=[1,2,3,4,5])
m_score=pd.cut(monetary_value,5,labels=[1,2,3,4,5])
首先,指定一个时间节点,用于计算其他时间与该时间的间隔。对R、F、M三个变量使用分位数法做区间划分,使用了pd.cut方法,默认设置为5份,同时通过labels标签指定区间标志。主要是对于R而言,数值越大,意味着离指定日期越远,因此其区间划分后的值应该越小,所以该标签列顺序与其他相反。
(2)R、F、M数据合并
rfm_data=pd.DataFrame({'r_score':r_score,'f_score':f_score,'m_score':m_score},index=frequency_value.index,dtype=np.int32)
print('RFM Score overview:',rfm_data.head())
(3)RFM总得分计算
方法一:直接乘以特定权重得到总得分。根据业务方的需求建立模型。如果业务方更关注活跃度,认为访问的邻近度更为重要,认为访问的邻近度最重要,因此R的权重更高。其次是访问频率,最后是金额。假定权重分别为0.6,0.3,0.1(可由专家打分得到)
方法二:直接将RFM以字符串形式组合,组合的顺序根据RFM的重要程度排名。
#方法一:加权得分
rfm_data['total_score']=rfm_data['r_score']*0.6+rfm_data['f_score']*0.3+rfm_data['m_score']*0.1
#方法二:组合得分
rfm_data_tmp=rfm_data.copy()
rfm_data_tmp['r_score']=rfm_data_tmp['r_score'].astype('str')
rfm_data_tmp['f_score']=rfm_data_tmp['f_score'].astype('str')
rfm_data_tmp['m_score']=rfm_data_tmp['m_score'].astype('str')
rfm_data['final_score']=rfm_data_tmp['r_score'].str.cat(rfm_data_tmp['f_score']).str.cat(rfm_data_tmp['m_score'])
最后的RFM得分的结果如下:
print('final RFM Score overview:')
print(rfm_data.head())
print('\n')
#结果主要验证数据输出是否符合预期,主要是R、F、M的组合和加权计算,使用描述性统计简单看下数据型得分的区间分布是否在预计的[1,5]分之间
print('Final rfm score desc:')
print(rfm_data.describe())
final RFM Score overview:
f_score m_score r_score total_score final_score
USERID
51220 1 1 4 2.8 411
51221 1 1 2 1.6 211
51224 1 1 3 2.2 311
51225 1 1 4 2.8 411
51226 1 1 1 1.0 111
2.6 结果保存
(1)方法一:直接保存到本地csv文件
rfm_data.to_csv('rfm_model_data.csv')
(2)方法二:保存到Mysql数据库中
首先,需要实现数据库的连接,可用到pymysql中的connect()方法
import pymysql
conn=pymysql.connect(host='localhost',user='root',password='******',port=3306,db='employee',charset='utf8')
cursor=conn.cursor()
在将数据写入数据库之前,需要判断数据库中是否存在要写入的表,如果不存在需要新建数据库。
cursor.execute('show tables')
#采用游标的fetchall方法抓取全部数据
table_object=cursor.fetchall()
table_list=[]
for table in table_object:
table_list.append(table)
if not "sales_rfm_data" in table_list:
sql='Create table sales_rfm_data (\
userid varchar(20),\
r_score int(2),\
f_score int(2),\
m_score int(2),\
total_score decimal(10,2),\
final_score varchar(20),\
insert_data varchar(20))ENGINE=InnoDB DEFAULT CHARSET=utf8'
cursor.execute(sql)
经过上述步骤,能保证要写入的数据表已存在,接下来将数据插入数据库中。
user_id=rfm_data.index.astype('str')
total_score=rfm_data['total_score']
final_score=rfm_data['final_score']
#写库日期
timestamp=time.strftime('%Y-%m-%d',time.localtime(time.time()))
print('begin to insert data into table')
#依此循环将数据插入
for i in range(rfm_data.shape[0]):
insert_sql='insert into sales_rfm_data (userid,r_score,f_score,m_score,total_score,final_score,insert_data) values (%s,%s,%s,%s,%s,%s,%s)'
cursor.execute(insert_sql,(user_id[i],int(r_score.iloc[i]),int(f_score.iloc[i]),int(m_score.iloc[i]),float(total_score.iloc[i]),final_score.iloc[i],timestamp))
#由于需要批量写入操作,因此需要采用conn.commit方法提交命令
conn.commit()
完成数据库的插入操作之后,关闭游标和数据库。
cursor.close()
conn.close()
print('finish insertinng')
以上的数据分析案例摘自《Python数据分析与数据化运营》