Python数据分析与数据化运营:会员数据化运营5-案例:基于RFM的用户价值度分析

浏览: 3535

作者介绍:宋天龙(TonySong),资深大数据技术专家,历任软通动力集团大数据研究院数据总监、Webtrekk(德国最大的网站数据分析服务提供商)中国区技术和咨询负责人、国美在线大数据中心经理。


本文来自《Python数据分析与数据化运营》配套书籍第5章节内容,机械工业出版社华章授权发布,未经允许,禁止转载!

此书包含 50个数据工作流知识点,14个数据分析和挖掘主题,8个综合性运营案例。涵盖了会员、商品、流量、内容4大数据化运营主题,360°把脉运营问题并贴合数据场景落地。

书籍购买链接:https://item.jd.com/12254905.html

课程学习链接:网站数据分析场景和方法——效果预测、结论定义、数据探究和业务执行https://edu.hellobi.com/course/221


往期回顾:Python数据分析与数据化运营:会员数据化运营1-概述与关键指标 

                 Python数据分析与数据化运营:会员数据化运营2-应用场景与分析模型

                 Python数据分析与数据化运营:会员数据化运营3-分析小技巧

                 Python数据分析与数据化运营:会员数据化运营4-“大实话”


5.7.1 案例背景

用户价值细分是了解用户价值度的重要途径,而销售型公司中对于订单交易尤为关注因此基于订单交易的价值度模型将更适合运营需求

对于用户价值度模型而言,由于用户的状态是动态变化的,因此一般需要定期更新,业务方的主要需求是至少每周更新一次由于兼顾历史状态变化,因此在每次更新时都需要保存历史数据,不同时间点下的数据将通过日期区分。

每次模型结果数据,一部分是要给运营直接做分析,一部分是要回吐数据库中,作为其他数据建模的基本数据维度,因此数据的输出需要有本地文件和数据库两种方式。

本节案例的输入源数据sales.csv源代码chapter5_code1.py位于“附件-chapter5”中,默认工作目录为“附件-chapter5”(如果不是,请cd切换到该目录下否则会“IOError: File sales.csv does not exist”)。程序RFM得分数据写本地文件sales_rfm_score.csvMySQL数据库目标数据表(sales_rfm_score

5.7.2 案例主要应用技术

案例没有使用现有成熟模型包,而是通过Python代码手动实现RFM模型,主要用到的库包括timenumpypandasmysql.connector。有关RFM模型更多内容,请查看“5.4.3 会员价值度模型

5.7.3 案例数据

案例数据某企业2016部分抽样数据,数据来源于销售系统,主要是用户订单记录以下数据概况:

  • 特征变量数:4
  • 数据记录数:86135
  • 是否NA:有
  • 是否有异常值

以下是本数据集4特征变量,包括

  • USERID用户ID每个用户的ID唯一,由纯数字组成。
  • ORDERDATE订单日期,格式YYYY-MM-DD例如2016-01-01
  • ORDERID:订单ID每个订单的ID唯一,由纯数字组成
  • AMOUNTINFO订单金额,浮点型数据。

5.7.4 案例过程

步骤1 导入用到的库

import time  # 导入时间库
import numpy as np  # 导入numpy库
import pandas as pd  # 导入pandas库
import mysql.connector  # 导入mysql连接库

该案例用到了四个库:timenumpypandasmysql.connector

  • time用来记录插入数据库时当前时间
  • numpy:用来做基本数据处理等
  • pandas:有关日期转换、数据格式化处理、主要RFM计算过程
  • mysql.connector:数据库连接工具,读写MySQL数据库

步骤2 读取原始数据

dtypes = {'ORDERDATE': object, 'ORDERID': object, 'AMOUNTINFO': np.float32}  # 设置每列数据类型
raw_data = pd.read_csv('sales.csv', dtype=dtypes, index_col='USERID')  # 读取数据文件

dtypes定义字典用于使用pd.read_csv读取数据时数据框数据类型的自定义,而非系统默认类型。本案例中将ORDERDATE(订单时间)和ORDERID(订单ID)都设置为object字符串对象)AMOUNTINFO(订单金额)设置为浮点型。

使用pd.read_csv读取数据文件过程中,默认的csv逗号作为分隔符,因此无需指定分隔符设置参数dtype上述自定义的类型字典同时指定列USERID(用户ID)为索引列,代替默认的数值索引

步骤3数据审查和校验,主要包括数据概览、缺失值审查等。

# 数据概览
print ('Data Overview:')
print (raw_data.head(4))  # 打印原始数据前4条
print ('-' * 30)
print ('Data DESC:')
print (raw_data.describe())  # 打印原始数据基本描述性信息
print ('-' * 60)

数据概览部分,通过数据框的head方法输出前4数据,通过数据框的describe方法输出其基本描述性统计信息。返回结果如下:

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
------------------------------
Data DESC:
         AMOUNTINFO
count  86127.000000
mean     744.705261
std     1425.211182
min        0.500000
25%       13.000000
50%       59.000000
75%      629.000000
max    30999.000000
------------------------------------------------------------

如何通过上述结果得到有效信息?

Data Overview部分主要查看不同数据列的数据格式,尤其是有特定转换操作之后是否符合源数据文件的格式或得到目标转换要求;另外,数据的长度、组成规律类型等是否与真实数据一致。

Data DESC部分主要分析数值型字段(案例中是AMOUNTINFO数值分布规律,包括记录数、极值、标准差、分位数结果等,可用于数据集的适用模型极值的处理等后续计算的辅助判断依据

本案例中,发现数据极值相差非常大,并且标准差也很大,说明数据波动非常明显。另外最大值和最小值似乎有些奇怪:最大值竟然有超过30000元、最小值却只有0.5元,两种状态都非常异常。经过与业务方沟通后确认,最大值的订单金额有效,为某客户一次性购买多个大家电商品;而订单金额为0.5的订单都属于促销优惠券生成的订单,这些订单用来为用户消费时提供优惠券,没有实际意义因此这些数据需要去掉。除了这些0.5的订单,所有低于1的订单均有这个问题。

# 缺失值审查
na_cols = raw_data.isnull().any(axis=0)  # 查看每一列是否具有缺失值
print ('NA Cols:')
print (na_cols)  # 查看具有缺失值的列
print ('-' * 30)
na_lines = raw_data.isnull().any(axis=1)  # 查看每一行是否具有缺失值
print ('NA Recors:')
print ('Total number of NA lines is: {0}'.format(na_lines.sum()))  # 查看具有缺失值的行总记录数
print (raw_data[na_lines])  # 只查看具有缺失值的行信息
print ('-' * 60)

缺失值对于后续的计算会产生重大影响,因此这里需要确认数据中是否含有缺失数据。先通过raw_data.isnull().any(axis=0)来判断所有列是否含有缺失信息,其中的isnull用来查看是否有缺失值,any用来判断数据记录中的任何一个位置出现缺失值都会计算在内,参数axis=0列为基础做查看。打印结果如下:

NA Cols:
ORDERDATE      True
ORDERID       False
AMOUNTINFO     True

返回结果显示了ODERDATEAMOUNTINFO都有缺失下一步结合每一行看下到底有多少条数据出现缺失。这次使用的方法跟判断列缺失一致,仅仅axis设为1用来表示按行查看。在na_lines,结果由TrueFalse组成,因此可以直接做数值计算,这里使用sum函数统计一共有多行为含有NA含有NA总记录数和详细行记录打印结果如下:

NA Recors:
Total number of NA lines is: 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

返回结果可知,一共有10数据含有NA,这些数据在整体样本集中占比非常小,因此这里可以直接删除。

步骤4 数据预处理准备工作,包括数据异常、格式转换和处理

# 异常值处理
sales_data = raw_data.dropna()  # 丢弃带有缺失值的行记录
sales_data = sales_data[sales_data['AMOUNTINFO'] > 1]  # 丢弃订单金额<=1的记录

对于异常值的处理,直接使用数据框的dropna方法删除;对于订单金额<=1的数据,我们也直接丢弃,选择订单金额>1的数据记录。

# 日期格式转换
sales_data['ORDERDATE'] = pd.to_datetime(sales_data['ORDERDATE'], format='%Y-%m-%d')  # 将字符串转换为日期格式
print ('Raw Dtypes:')
print (sales_data.dtypes)  # 打印输出数据框所有列的数据类型
print ('-' * 60)

日期转换目的是实现基于时间间隔的计算,这样才能算出R距离指定日期的天数。这一操作没有读取数据转换(在读取时的具体转换方法请参考“4.6.4 代码实操:Python时间序列分析部分),这里使用pd.to_datetime方法将ORDERDATE转换为pandas的日期类型(pd.datetime类型),format参数原始数据字符串的格式来写,只有格式对应上才能实现解析。解析完成后,使用数据框的dtypes打印输出Dtypes类型如下:

Raw Dtypes:
ORDERDATE     datetime64[ns]
ORDERID               object
AMOUNTINFO           float32
dtype: object

接下来需要分别计算RFM三个原始变量的数值主要使用的方式是数据框的groupby方法

# 数据转换
recency_value = sales_data['ORDERDATE'].groupby(sales_data.index).max()  # 计算原始最近一次订单时间
frequency_value = sales_data['ORDERDATE'].groupby(sales_data.index).count()  # 计算原始订单频率
monetary_value = sales_data['AMOUNTINFO'].groupby(sales_data.index).sum()  # 计算原始订单总金额

三行代码都是原始数据框的索引为主键(用户ID作为汇总维度)分别ORDERDATE最大值、对ORDERDATE计数统计、对AMOUNTINFO求和,得分RFM三个指标的原始值。

步骤5 计算RFM得分

# 分别计算R、F、M得分
deadline_date = pd.datetime(2017, 01, 01)  # 指定一个时间节点,用于计算其他时间与该时间的距离
r_interval = (deadline_date - recency_value).dt.days  # 计算R间隔
r_score = pd.cut(r_interval, 5, labels=[5, 4, 3, 2, 1])  # 计算R得分
f_score = pd.cut(frequency_value, 5, labels=[1, 2, 3, 4, 5])  # 计算F得分
m_score = pd.cut(monetary_value, 5, labels=[1, 2, 3, 4, 5])  # 计算M得分分

首先指定一个时间节点,用于计算其他时间与该时间的距离,这是计算R基础。这里定义2017-01-01通过数据框相减得到时间间隔天数对象,并对象使用dt.days方法获得天的数值。

下面对得到的RFM三个变量值使用分位数法做区间划分,这里使用了pd.cut方法,默认设置为5,同时通过labels标签指定区间标志。主要注意的是R(最近购买时间)而言,数值越大意味着离指定日期越远,因此其区间划分后的值应该越小,所以该标签列表顺序与其他两个相反。

提示 dt是pandas中Series时间序列datetime类属性的访问对象,除了代码用到的days(以外,还包括datedayofweekdayofyeardays_in_monthfreqhourmicrosecondminutemonthquartersecondtime、tz、weekweekdayweekday_nameweekofyearyear。这些提取Series时间数据的常用方法。

得到RFM各自得分后,将三维维度数据合并为一个数据框,便于在一起做展示以及后续的RFM得分计算。

# R、F、M数据合并
rfm_list = [r_score, f_score, m_score]  # 将r、f、m三个维度组成列表
rfm_cols = ['r_score', 'f_score', 'm_score']  # 设置r、f、m三个维度列名
rfm_pd = pd.DataFrame(np.array(rfm_list).transpose(), dtype=np.int32, columns=rfm_cols, index=frequency_value.index)  # 建立r、f、m数据框
print ('RFM Score Overview:')
print (rfm_pd.head(4))
print ('-' * 60)

先建立RFM三个维度值列表和名称列表,用于生成数据框时指定数据和标签。然后使用pd.DataFrame建立数据框。使用np.arrayRFM生成列表转换为矩阵,此时的矩阵形状(3, 59676)不符合我们需要的三列的需求,因此使用transpose方法对矩阵做转置处理,该方法也可以简写为T(注意大小写),np.array(rfm_list).transpose()等价于np.array(rfm_list).T;由于RFM值域是[1,5]整数,因此创建数据框时指定数据类型为整数型,通过dtype=np.int32实现,该方法也可以写成dtype='int32'二者是等价的然后设置列名指定索引列为用户ID由于RFM三个Series的索引都相同,因此这里随便指定为frequency_value.index。最后打印前4结果如下

RFM Score Overview:
        r_score  f_score  m_score
USERID                           
51220         4        1        1
51221         2        1        1
51224         3        1        1
51225         4        1        1
------------------------------------------------------------

完成RFM数据框创建后,可以基于该数据框计算RFM得分,这里使用两种方法:基于三个维度加权的RFM得分以及基于组成的总得分。

# 计算RFM总得分
# 方法一:加权得分
rfm_pd['rfm_wscore'] = rfm_pd['r_score'] * 0.6 + rfm_pd['f_score'] * 0.3 + rfm_pd['m_score'] * 0.1
# 方法二:RFM组合
rfm_pd_tmp = rfm_pd.copy()
rfm_pd_tmp['r_score'] = rfm_pd_tmp['r_score'].astype('string')
rfm_pd_tmp['f_score'] = rfm_pd_tmp['f_score'].astype('string')
rfm_pd_tmp['m_score'] = rfm_pd_tmp['m_score'].astype('string')
rfm_pd['rfm_comb'] = rfm_pd_tmp['r_score'].str.cat(rfm_pd_tmp['f_score']).str.cat(rfm_pd_tmp['m_score'])

方法一,直接取出数据框的三RFM通过乘以特定权重值得到总得分。由于业务关注活跃度,认为访问的邻近度最重要,因此R权重比较高,设置为0.6其次是访问频率F设置0.3;订单金额M设置为0.1。然后基于不同的列直接做加权相加,而无需通过循环读出各个元素再做计算。

提示 在设定权重值时需要与业务部门沟通,主要看业务关注哪个方面,再设置对应维度的值。而对于权重值的分配一般情况下总权重的和为1,这样划分更清晰并便于解释和管理。

方法二中,我们要将RFM三个值组合,要用到字符串的组合。由于原始数据框中为了做加权计算而设置为数值型,因此这里需要转换为字符串型。为了影响原始数据通过copy方法得到一份副本,然后副本三列使用astype方法将数值型转换为字符串型。

提示 设置数据类型时一般有两种思路:一种是在创建数据框时通过dtype指定(原始数据框就是这种方法),第二种是在创建好的数据框中使用astype方法将特定栏位转换为目标类型。

最后的合并过程中,使用pandas字符串处理库str中的cat方法做字符串合并,该方法可以将右侧的数据合并到左侧,在连续使用两个str.cat方法后得到总的RFM字符串组合。

相关知识点:使用str.cat方法将字符串合并

关于字符串的合并,之前在不同章节中主要用到了种方法:

  • 使用+(加号)组合字符串:例如输入X = 'a'+'b'得到X值是'ab'
  • 使用%占位符做字符串组合,主要用在变量输出上,例如输入X = 'I am %s'%'Tony',能得到X的值是'I am Tony'
  • 使用.join方法多个可迭代的对象合并,例如输入X = ' '.join(['I','am','tony'])得到X值是'I am tony'
  • 使用.format占位符将多个字符串合并,跟%用法类似但更强大,主要用在变量输出上,例如输入:X = 'I am {1} and {0} years old'.format(30,'Tony')得到X值是'I am Tony and 30 years old'

本节使用了pandas自带的字符串组合方法,该放在在str库中它用于字符串对象合并。该方法语法如下:cat(self, others=None, sep=None, na_rep=None)参数:

  • others:要合并的另外一个对象(右侧对象),如果为空则将左侧对象组合
  • sep:合并的分隔符,默认为空,可自定义,例如','、';'等na_rep:如果遇到NA(缺失值)时如果处理,默认为忽略
  • 需要注意的是:方法用于对Series做组合,而不能数据适用于一维数据字符串

示例

左侧对象组合

输入pd.Series(['a','b','c']).str.cat(sep=';')

输出'a;b;c'

将左侧对象和右侧对象组合

输入pd.Series(['a', 'b', 'c']).str.cat(['A', 'B', 'C'], sep=';')

输出

0    a;A
1    b;B
2    c;C

 

步骤6 打印输出和保存结果,该步骤中我们将数据打印出来并分别保存为本地csv数据文件以及将数据存入MySQL数据库。

经过上述计算过程,已经得到RFM结果,这里我们将其打印出来。

# 打印结果
print ('Final RFM Scores Overview:')
print (rfm_pd.head(4))  # 打印数据前4项结果
print ('-' * 30)
print ('Final RFM Scores DESC:')
print (rfm_pd.describe())

使用head方法指定打印前4结果以及RFM得分描述性统计结果如下:

Final RFM Scores Overview:
        r_score  f_score  m_score  rfm_wscore rfm_comb
USERID                                                
51220         4        1        1         2.8      411
51221         2        1        1         1.6      211
51224         3        1        1         2.2      311
51225         4        1        1         2.8      411
------------------------------
Final RFM Scores DESC:
            r_score       f_score       m_score    rfm_wscore
count  59676.000000  59676.000000  59676.000000  59676.000000
mean       3.299970      1.013439      1.000134      2.384027
std        1.402166      0.116017      0.018307      0.845380
min        1.000000      1.000000      1.000000      1.000000
25%        2.000000      1.000000      1.000000      1.600000
50%        3.000000      1.000000      1.000000      2.200000
75%        5.000000      1.000000      1.000000      3.400000
max        5.000000      5.000000      5.000000      5.000000

该过程主要验证数据输出是否符合预期,主要是RFM组合和加权计算;使用描述性统计简单看下数值型得分的区间分布(重点是极值)是否预期[1,5]之间。分析发现上述数据没问题。

注意 如果原始数据集中有NA值而没有经过处理,那么在使用分位数方法时会默认忽视NA的分位,即值为NA的数据点仍然是NA。这会导致在做后期加权得分和组合时都会遇到问题。通过描述性统计也能发现该问题是否存在。

接着RFM得分文件保存本地,便于业务做进一步分析应用。

  • # 保存RFM得分到本地文件
  • rfm_pd.to_csv('sales_rfm_score.csv')  # 保存数据为csv

直接使用数据框对象的to_csv方法将数据保存为csv格式的数据文件。除了保存为csv格式的文件,数据框对象还可以支持保存为excel文件。另外支持sqljsonstatapicklesparsehdfhtml等对象的输出。保存到本地csv文件部分数据截图如下:

 

image.png

5-4本地csv文件结果

由于RFM结果作为其他模型结果的输入维度,因此需要将RFM得分写入数据框。这里使用的是MySQL官方连接程序实现。

  • # 设置要写库的数据库连接信息
  • table_name = 'sales_rfm_score'  # 要写库的表名
  • # 数据库基本信息
  • config = {'host': '127.0.0.1',  # 默认127.0.0.1
  •           'user': 'root',  # 用户名
  •           'password': '123456',  # 密码
  •           'port': 3306,  # 端口,默认为3306
  •           'database': 'python_data',  # 数据库名称
  •           'charset': 'gb2312'  # 字符编码
  •           }

首先建立要写数据库的基本信息,通过table_name = 'sales_rfm_score'定义要写入的表的名称变量会在后续识别和写入用到。config定义了一个用于写入数据库的详细配置信息定义,包括主机host、用户user密码password、端口port、要写入的数据表所处的数据库名database、字符集charset这些信息“2.2.3 从关系型数据库MySQL读取运营数据”中已经详细介绍过,在此不再赘述。

配置信息定义完成后,下面建立数据连接。

con = mysql.connector.connect(**config)  # 建立mysql连接
cursor = con.cursor()  # 获得游标

使用mysql.connector.connect方法连接数据库,后续获得游标写库需要该有效连接;使用连接的con.cursor方法获得游标,后续的查询操作都基于对象实现。

在将数据写入数据库之前,需要先判断数据库中是否存在要写入的表。如果不存在需要新建数据

  • # 查找数据库是否存在目标表,如果没有则新建
  • cursor.execute("show tables")  #
  • table_object = cursor.fetchall()  # 通过fetchall方法获得所有数据
  • table_list = []  # 创建库列表
  • for t in table_object:  # 循环读出所有库
  •     table_list.append(t[0])  # 每个每个库追加到列表
  • if not table_name in table_list:  # 如果目标表没有创建
  •     cursor.execute('''
  •     CREATE TABLE %s (
  •     userid               VARCHAR(20),
  •     r_score               int(2),
  •     f_score              int(2),
  •     m_score              int(2),
  •     rfm_wscore              DECIMAL(10,2),
  •     rfm_comb              VARCHAR(10),
  •     insert_date              VARCHAR(20)
  •     )ENGINE=InnoDB DEFAULT CHARSET=gb2312
  •     ''' % table_name)  # 创建新表

使用游标的execute方法执行一个sql语句由于config中我们定义了数据库名,因此这里直接使用"show tables"显示当前数据库下所有的表列表而不是先使用use [数据库]然后"show tables"

游标执行后获得的对象是没有数据的,使用fetchall方法抓取全部数据并返回但返回的是一个数据列表对象[(u'order',), (u'test',)]列表中的每个元素都是一个元组,而数据表名处于的第一个值。因此接着通过一个循环将对象读取出来得到数据表列表table_list

使用if条件语句判断写入的数据表table_name是否在数据表列表中,如果该表不存则新建。这里仍然使用游标的execute方法执行一段SQL语句,只是由于创建表的SQL语句较长,使用'''表示多个段落的字符串。

相关知识点:Python的字符串标识符

Python的字符串标识符有三种,分别是'(单引号)、"(双引号)、'''(三个连续单引号)、"""(三个连续引号)。四种表示符号(注意都是英文状态下在大多数场景下作用是相同的,但是都要求成对出现。例如'ABC'等价于"ABC"也等价于'''ABC'''

但在某些场景下他们的用途是有区分的,主要体现在两个方面:

  • 字符串中出现子集字符串时,需要使用不同类型的字符串区分,例如'I said:"You can go!"'这时由于在外层字符串使用了'(单引号),内层就不能再使用相同表示方法了,所以使用引号。
  • 字符串非常长(通常超过1、或者字符串步长但是需要分段显示,就需要用到'''(连续三个单引号)或"""(三个连续双引号)来表示,上述代码就是这应用。除此以外这种长段落还经常用到字典定义、格式化段落文本输出、复杂功能的SQL语句例如SQL嵌套、关联查询)

 

经过上述步骤,能保证要写入的数据表已经存在,并且跟要写入的数据类型和格式相匹配。接下来开始写库操作。

# 将数据写入数据库
user_id = rfm_pd.index  # 索引列
rfm_wscore = rfm_pd['rfm_wscore']  # RFM加权得分列
rfm_comb = rfm_pd['rfm_comb']  # RFM组合得分列
timestamp = time.strftime('%Y-%m-%d', time.localtime(time.time()))  # 写库日期
print ('Begin to insert data into table {0}...'.format(table_name))  # 输出开始写库的提示信息
for i in range(rfm_pd.shape[0]):  # 设置循环次数并依次循环
    insert_sql = "INSERT INTO `%s` VALUES ('%s',%s,%s,%s,%s,'%s','%s')" % \
                 (table_name, user_id[i], r_score.iloc[i], f_score.iloc[i], m_score.iloc[i], rfm_wscore.iloc[i],rfm_comb.iloc[i], timestamp)  # 写库SQL依据
    cursor.execute(insert_sql)  # 执行SQL语句,execute函数里面要用双引号
    con.commit()  # 提交命令

写库时用到7数据,分别是useridr_scoref_scorem_scorerfm_wscorerfm_combinsert_date其中

  • userid可以从数据框的索引中取出,用于标识不同的用户,使用数据框的index获得索引列用户ID数据框
  • r_scoref_scorem_score三列值在分别计算RFM得分已经产生;
  • rfm_wscorerfm_comb可以分别使用数据框读取列名获得RFM加权得分和组合得分数据
  • insert_date用来记录每次写库时的日期,日期可以作为变化状态的判断依据,应用分析不同时间和周期下用户的活跃度变化,例如5.5.3 借助动态数据流关注会员状态的轮转”中的那种应用。而在跟其他模型做数据集成应用时,可根据实际情况取需要的时间对应的状态,例如取最新状态可取insert_date的最大值

在获取insert_date写库日期使用了3时间函数:

  • time.time():获取当前系统时间下相对于Epoch时间(1970-01-01 00:00:00 UTC,以浮点型数据形式表示,例如1495770037.572
  • time.localtime()将时间转换为本地时间,是一个包含了年、月、日、小时、分钟、秒、一周中第几天、一年中第几天等数据元组。例如将上述时间转换结果time.struct_time(tm_year=2017, tm_mon=5, tm_mday=26, tm_hour=11, tm_min=40, tm_sec=37, tm_wday=4, tm_yday=146, tm_isdst=0)
  • time.strftime():将不同形式的时间转换为特定格式和时间表示方法,方法在本书中多次用到,例如将上述结果转换为'2017-05-26'

经过上述功能转换后的日期是YYYY-MM-DD格式,例如2017-05-26接下来写库操作利用for循环,根据数据框的记录做读索引,然后使用游标执行方法运行SQL由于我们要做批量写入操作,因此需要使用on.commit方法提交命令。

写入操作完成后,关闭游标和数据库连接。

cursor.close()  # 关闭游标
con.close()  # 关闭数据库连接
print ('Finish inserting, total records is: %d' % (i + 1))  # 打印写库结果

得到总的写入数据库记录数如下:

Finish inserting, total records is: 59676

可使用Navicat打开对应的数据库表查看结果是否符合预期,以及总记录数是否一致。

 

5.7.5 案例数据结论

由于RFM划分时,区间划分为5,因此可以5区间分别定义了:、中、一般、差和非常差5级别,分别对应到RFM5/4/3/2/1

基于RFM得分业务方得到这样的结论:

  • 公司的会员中99%以上的客户消费状态都不容乐观主要体现在消费频率低R、消费金额低M——经过分析,这里主要由于其中有一个用户(ID74270消费金额非常高,导致5分位时收到最大值的影响,区间向大值域区偏移
  • 公司中有一些典型客户的整个贡献特征明显,重点RFM得分555的用户(ID74270,该用户不仅影响了订单金额高,而且其频率和购买新鲜度和消费频率都非常高应该引起会员管理部门的重点关注。
  • 本周表现处于一般水平以上用户的比例(RFM三个维度得分均在3以上的用户数)相对上周环比增长了1.3%。这种良好趋势体现了活跃度的提升。
  • 本周低价值RFM得分111以上)用户名单中,新增了1221新用户这些新用户的列表已经被取出

5.7.6 案例应用和部署

针对上述得到的分析结论,会员部门采取了以下措施:

  • ID74270用户加入大客户名单,并实现重点关怀和管理
  • 上周的会员工作有关低价值群体的用户并未减少,反新增了一些需要会员部门重点关注和处理。目前最主要的还是通过会员渠道拉动会员再次访问网站并订单,防止客户的流失和沉默,而订单的金额是次要因素

录入数据库RFM得分数据已经应用其他数据模型中成为建模输入的关键维度特征之一。同时,该模型已经作为定时任何每周一早晨上班前运行一次。

5.7.7 案例注意点

本案例中有以下几个点需要读者重点关注:

对于RFM区间的划分是一个离散化过程,具体需要划分为几个区间需要跟业务方确认。本案例是划分为5区间,实际上一般不会比这个区间更多,3~5比较好的选择。

RFM组合加权中关于权重的确认,不同时期的业务关注点可能不同,因此权重值可能是需要调整的;另外,权重值的打分一般使用专家打分法,即业务来权重打分,如果没有业务打分,那么大多数情况下三者的权重从大到小依次是:R>F>M

本案例中有一个ID74270用户,由于其方面特征非常显著,因此整个RFM得分的划分区间受其影响。这种极值的影响(包括案例开始时极小值的处理)需要跟业务部门沟通确认能进行处理,此的极大值的出现业务认可有效数据,因此没有去除;如果有其他极值可能需要特别处理,否则会影响区间划分。本例中虽然影响划分,但恰好发现了该用户的价值。

虽然订单数据库中的数据质量相对较高,但可能由于采集问题、数据库同步、ETL查询、误操作等问题还是会导致NA的出现NA的处理非常重要。

RFM三个维度的处理包括计算离散化组合、转换之前都需要注意其数据类型和格式,尤其是有关时间项的转换操作提前完成

5.7.8 案例引申思考

利用RFM模型划分用户群体并作价值度分析是统计分析非常基础有效的方法。模型几乎用不到任何专业的统计分析和挖掘知识,只需要具有基本的数据清洗处理和转换技能即可完成,因此几乎是各个企业都会用到的模型。更重要的是,该模型原理简单,业务方在理解和应用起来非常容易入手,大大提高了部署落地的可能性。

对于本案例的实施,读者可以从以下几方面做引申思考

  • 问题 按照RFM每个区间划分为5的原则,实际上出来的总得分可能性是5*5*5=125,为什么结果中只有14种组合得分
  • 问题二 如果不同周期下RFM权重会改变运营结果是否具有可比性连续
  • 问题三 RFM模型可以作为模型分析方法,也可以作为数据预处理方法,基于不同的维度通过计算组合得分或加权得分的方式获得新的数据,这是一种数据降维有效方法。使用组合得分方法和加权得分方法得到的两种降维方法在后续应用中有哪些不同?

 

笔者关于引申思考的想法

  • 问题 在案例注意点中也提到了,由于受到ID74270用户行为影响,极大值导致整个区间划分非常大,很多区间内没有数据。F为例该用户的F原始值14(一年中有14订单),其他用户最高的只有6订单,因此在做区间划分时从7-13没有一个用户符合条件,也就不会产生得分区间用户统计量。
  • 问题二 权重的变化影响的仅仅是加权得分,而不影响组合得分项因此其他建模应用中没有任何影响。在分析过程中,这种改变不可避免的会使加权得分产生规则发生变化,需要业务做分析加以提醒
  • 问题三 首先需要明确的是RFM两种总得分应该只选择其中一种加以应用,因为二者具有高度相关性。由于加权RFM得分是一个连续型变量适合直接参与后续回归建模;组合RFM得分是一个分类变量,更适合应用到分类建模中,同时需要注意的是该分类需要做标志转换具体过程参见“3.2 将分类数据和顺序数据转换为标志变量
推荐 0
本文由 Python爱好者社区 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册