请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365825 新帖:32

基于RFM及K-means的用户价值度分析

爱汇小王子发表于:5 月 10 日 06:08回复(1)

1. 概述

金融行业、零售行业、电信行业等经常要对用户进行划分,以标记不同的标签,从而进行个性化的精准化营销行为。RFM模型,是以用户的实际交易或消费或购买或充值等(以下统称“交易”)一系列行为数据作为基础,从而进行用户群体的划分的,简单而又具有实际价值。RFM模型由三个指标组成,分别为:

  • Recency:用户最近一次交易时间的间隔。R值越大,表示客户交易发生的日期越久,反之则表示客户交易发生的日期越近。
  • Frequency:用户在最近一段时间内交易的次数。F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃。
  • Monetary:用户在最近一段时间内交易的金额。M值越大,表示客户价值越高,反之则表示客户价值越低。
    可以看到这三个指标都是通过用户的交易行为计算得出的,这些指标基本上代表了用户是否活跃,购买能力,忠诚度等信息。而运营人员的目标是通过对每个用户的RFM指标进行计算,将用户群体划分为不同的种类进行区分,以便运营人员进行分析和精准化营销。参考图1所示:

img2-5.jpg
(图1 用户分类)(*看到一张日文的描述得比较好的图,就採用了,应该能够看懂)

可以把R、F、M每个方向定义为:高、低,两个方向,我们找出R、F、M的中值,R=最近一次消费,高于中值就是高,低于中值就是低,这样就是222=8种用户分类,从而分析出高价值用户,重点发展用户,流失用户等群体进行针对性营销动作。制定运营策略既要结合各类用户在产品中的占比,也要结合产品的实际业务逻辑。参考表1所示:

(表1 不同用户分类的运营策略例)
策略.png

2.实证分析

2.1 数据预处理

从csv文件(某电商某段期间的真实运营数据)获取数据,包括用户ID,交易日期,交易金额等字段。数据概况如下:

  • 特征变量数:4(USERID、ORDERDATE、ORDERID、AMOUNTINFO)
  • 数据记录数:4442
  • 是否有NA值:无
  • (AMOUNTINFO)最大值:9188
  • (AMOUNTINFO)最小值:0.01

对原始数据做缺失值处理和异常值处理。由于本次数据没有缺失值,则不需要处理。(如有,可根据需要进行删除或补充处理。)对于交易金额为0.01的交易金额数据,经确认为运营测试数据,做删除处理。对交易日期(ORDERDATE)进行日期转换,以便后续进行时间间隔的计算。

2.2 RFM得分计算

1、指定最近时间截点
由于数据中,最近的交易日期为2018年12月5日,则指定该日期为最近时间截点,所有时间间隔的计算都以该日期为参考点。
2、R、F、M数值计算
以用户ID(USERID)为主键,分别对交易日期(ORDERDATE)求最大值、对交易日期(ORDERDATE)做计数统计、对交易金额(AMOUNTINFO)求和,结合上述1中得到的最近时间截点,得到R、F、M的数值。
对得到的R、F、M的数值使用分位法做区间划分,一般分为5份。同时通过labels标签指定区间标志。对R而言,数值越大,离最近时间截点越远,其区间标志越小。F、M则和R刚好相反。
经过计算后的RFM数据分布可参考图2、图3所示:

方法前-散点图.png
(图2 RFM-散点图分布)

方法前.png
(图3 RFM-3D图分布)

从图2、图3可知,该用户群的早期客户数较多,交易频度集中在50以内,交易金额普遍不是很高。

2.3 权重计算

一般来说,权重计算多采用层次分析法(AHP)。金融公司中常用于个人信用风险评级模型的开发,而零售行业中的借贷评级,也可以借鉴该方法。利用AHP方法,先通过两两比较确定各个因素的相对重要性,再通过求解相对重要性计算指标权重。指标权重需经过一致性检验,即随机一次性比率指标CR越小越好(一般要求CR<0.1)。专家评分矩阵可参考表2所示:

(表2 专家评分矩阵)
表2.png

最终确定权重为:[W_R 、W_F 、W_M] = [ 0.30、0.54、0.16 ](详细的计算过程省略)
由于本案例的指标只有3个,数量较少,不易混淆,所以也可以简单地直接设定。

2.4 基于RFM区间标志 分类规则的用户分类

第一种的分类方法是利用RFM区间标志 分类规则的方法(宋天龙,2017),可参考表1所示。以R为例,其中“RS分布”指RS的平均值,“高”指高于平均值。F、M雷同。其分类结果可参考图4所示:

方法1.png
(图4 基于方法一的分类结果)

从图4可知,该用户群主要由重要挽留用户 一般挽留客户组成,另外有少部分一般价值客户和一般保持用户,严重缺乏重要价值用户和重要发展客户。

2.5 基于RFM实际数值 K-means的用户分类

第二种方法是直接使用RFM的实际数值,结合K-means聚类方法进行分类,不使用RFM区间标签。在使用该方法的时候,要注意:

  • RFM是否存在离散值,是否均匀分布
  • RFM为不同量纲,需要进行标准化处理
  • K-means聚类方法按照几类进行分类

离散值和标准化处理可按照常规的处理办法。离散值处理可使用MAD法,也可简单地按照取对数的处理。标准化处理可用z-score方法。本案例中,只进行了标准化处理。至于按照几类进行分类,可通过elbow method(详细可参考https://en.wikipedia.org/wi/Determining_the_number_of_clusters_in_a_data_set)方法,来推断使用分类数,如图5所示,取图中曲线下降趋势缓和的节点即可,即k=4。

碎石检验.png
(图5 elbow method)

根据分类数=4进行K-means聚类分析。由于此时的分类类别不再是固定的8类,所以我们可将客户简单的分为k类星级客户。分类结果参考图6所示:

方法2最终排名.png
(图6 基于方法二的分类结果)

此时的用户分类只是简单地将用户进行分类,尚没有体现不同分类用户之间的区别。此时可取各分类的质心,并将其排名显示,结合了排名的分类结果可参考图7所示:

方法23d.png
(图7 基于方法二的分类 排名结果)

可通过概率密度图来查看各排名用户类别的RFM分布情况(watermelon12138,2019),以3星会员为例,参考图8所示:

概率密度图.png
(图8 3星会员的概率密度图)

由图8可知,该类用户访问天数在800天前后,其交易频率集中20附近,而其交易金额则分布较广,从15000到25000不等。

2.6 两种分类方法的比较

从两种不同的分类结果来看,虽然基本都是分为4类,但4类的分法各自不同。前者在分类规则上更容易按照运营人员的期望进行分类,后者更加注重利用机器学习的自动学习能力,把更多的任务交给K-mean聚类来完成。前者虽然也计算用户的价值分数,但前者的分类并不依赖用户价值分数,用户价值分数仅仅只是参考;而后者通过K-means进行聚类,但无法进行用户类别排名,用户类别排名主要依赖于用户价值分数及RFM权重。

参考文献:
1 宋天龙. 《Python数据分析与数据化运营》
2 HarveyLau.《RFM-Clustering》 https://github.com/HarveyLau/RFM-Clustering
3 watermelon12138.《K-means聚类算法、Pandas绘制概率密度图和TSNE展示聚类结果》
https://blog.csdn.net/watermelon12138/article/details/86549474
4 Wilame Lima Vallantin.《Apply RFM principles to cluster customers with K-Means》

# -*- coding: utf-8 -*-

'''
描述:基于RFM及K-means聚类的客户价值度分析
时间:2019-2-19
程序开发环境:JointQuant
程序输入:sales.csv(包含用户ID,订单日期,订单金额等客户属性)
程序输出:RFM得分数据写本地文件rfm_score.csv
参考文献:
1 宋天龙. 《Python数据分析与数据化运营》
2 HarveyLau.《RFM-Clustering》 https://github.com/HarveyLau/RFM-Clustering
3 watermelon12138.《K-means聚类算法、Pandas绘制概率密度图和TSNE展示聚类结果》
https://blog.csdn.net/watermelon12138/article/details/86549474
4 Wilame Lima Vallantin.《Apply RFM principles to cluster customers with K-Means》
'''

# 导入库
import time  # 导入时间库
import numpy as np  # 导入numpy库
import pandas as pd  # 导入pandas库
from scipy import stats
from sklearn.cluster import KMeans
from mpl_toolkits.mplot3d import Axes3D

#-- 参数初始化 --#
# input,output文件指定
inputfile = 'sales.csv'  # 销量及其他属性数据
outputfile = 'rfm_score.csv' # 保存结果的文件名

# 权重
W = [0.30, 0.54, 0.16]

# 原始数据参数
dtypes = {'ORDERDATE': object, 'ORDERID': object, 'AMOUNTINFO': np.float32}  # 设置每列数据类型_
index_column = 'USERID'
### 数据读取
data = pd.read_csv(inputfile, dtype=dtypes, index_col=index_column)  # 读取数据

# 原始数据审查和校验
# 原始数据概览
print ('Data Overview:')
print (data.head(4))  # 打印原始数据前4条
print ('-' * 60)
print ('Data before initial:')
print (data.describe())  # 打印原始数据基本描述性信息
print ('-' * 60)
#print ('Unique data:')
#print (data.nunique())  # 
#print ('-' * 60)
Data Overview:
        ORDERDATE      ORDERID  AMOUNTINFO
USERID                                    
135197  2016/1/21  1.50701E+13        80.0
135214  2016/1/21  1.50701E+13        43.0
135238  2016/1/22  1.50701E+13        38.0
135214  2016/1/22  1.50701E+13        75.0
------------------------------------------------------------
Data before initial:
        AMOUNTINFO
count  4442.000000
mean    142.146942
std     341.469452
min       0.010000
25%       5.280000
50%      51.500000
75%     116.379997
max    9188.000000
------------------------------------------------------------
### 数据初始化
# 1,缺失值处理
# 2,无效业务数据处理,比如订单金额<=1的订单
# 3,日期格式转换
#
def initial_data(raw_data):
    # 缺失值审查
    na_cols = raw_data.isnull().any(axis=0)  # 查看每一列是否具有缺失值
    print ('NA Cols:')
    print (na_cols)  # 查看具有缺失值的列
    print ('-' * 60)
    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)    
    # 缺失值处理
    data = raw_data.dropna()  # 丢弃带有缺失值的行记录
    data = data[data['AMOUNTINFO'] > 1]  # 丢弃订单金额<=1的记录
    data = data[data['AMOUNTINFO'] < 9000]  # 丢弃订单金额>=9000的记录    
    
    # 日期格式转换
    data['ORDERDATE'] = pd.to_datetime(data['ORDERDATE'], format='%Y-%m-%d')  # 将字符串转换为日期格
    
    return data
# 原始数据初始化
data = initial_data(data)
print ('Data after initial:')
print (data.describe())  # 打印原始数据基本描述性信息
print ('-' * 60)
NA Cols:
ORDERDATE     False
ORDERID       False
AMOUNTINFO    False
dtype: bool
------------------------------------------------------------
NA Recors:
Total number of NA lines is: 0
Empty DataFrame
Columns: [ORDERDATE, ORDERID, AMOUNTINFO]
Index: []
------------------------------------------------------------
Data after initial:
        AMOUNTINFO
count  3926.000000
mean    158.404785
std     328.920837
min       1.020000
25%      11.080000
50%      66.019997
75%     134.822498
max    4048.000000
------------------------------------------------------------
print('Most recent invoice is from:')
print(data['ORDERDATE'].max())
print ('-' * 60)
Most recent invoice is from:
2018-12-05 00:00:00
------------------------------------------------------------
# To calculate recency, we’ll look into the ‘ORDERDATE’. 
# Since the date of our last invoice was 2018–12–06, 
# we’ll consider it as the most recent one. 
# Then, we’ll subtract each day from the day after to calculate the other ‘recencies’.
deadline_date = pd.datetime(2018, 12, 5)  # 指定一个时间节点,用于计算其他时间与该时间的距离

def tran2rfm_pd(sales_data):
    # 数据转换
    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()  # 计算原始订单总金额
    
    # 计算RFM得分
    # 分别计算R、F、M得分
    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、F、M标签数据
    rfm_list = [r_score, f_score, m_score]  # 将r、f、m三个维度组成列表
    rfm_cols = ['R', 'F', 'M']  # 设置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数据框
    # R、F、M实际数据
    rfm_pd['recency'] = r_interval
    rfm_pd['frequency'] = frequency_value
    rfm_pd['monetary'] = monetary_value

    return rfm_pd
# RFM DataFrame做成
rfm_pd = tran2rfm_pd(data)   

# 打印输出和保存结果
# 打印结果
print ('RFM Scores Overview:')
print (rfm_pd.head())  
print ('-' * 60)
print ('RFM Scores Describe:')
print (rfm_pd.describe())    
print ('-' * 60)
RFM Scores Overview:
        R  F  M  recency  frequency      monetary
USERID                                           
135194  1  1  1      972          4    279.000000
135195  1  1  1      972          2    170.000000
135196  1  1  1      846          8   1784.760010
135197  4  1  2      383         73  24076.330078
135198  3  1  1      520          1     44.759998
------------------------------------------------------------
RFM Scores Describe:
                 R            F            M      recency    frequency  \
count  1157.000000  1157.000000  1157.000000  1157.000000  1157.000000   
mean      1.593777     1.004322     1.009507   831.893691     3.393258   
std       0.850949     0.121191     0.173742   185.216052    12.753047   
min       1.000000     1.000000     1.000000     0.000000     1.000000   
25%       1.000000     1.000000     1.000000   751.000000     1.000000   
50%       1.000000     1.000000     1.000000   892.000000     1.000000   
75%       2.000000     1.000000     1.000000   950.000000     3.000000   
max       5.000000     5.000000     5.000000  1045.000000   388.000000   

           monetary  
count   1157.000000  
mean     537.510864  
std     3792.002197  
min        1.120000  
25%       11.080000  
50%       38.000000  
75%      175.000000  
max    88395.390625  
------------------------------------------------------------
# 散点图展示
rfm_pd.plot.scatter(x='recency',y='frequency',c='monetary',cmap='viridis_r', s=50)
<matplotlib.axes._subplots.AxesSubplot at 0x7f3c30096048>
# 3D图展示
fig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(111, projection='3d')

ax.scatter(rfm_pd['recency'], rfm_pd['frequency'], rfm_pd['monetary'], s=50)
ax.set_xlabel('Recency')
ax.set_ylabel('Frequency')
ax.set_zlabel('Monetary')
Text(0.5,0,'Monetary')
# RFM总分计算
def compute(r,f,m):
    return r*W[0] + f*W[1] + m*W[2]
#------------------ 方法一:基于RFM区间标志 + 分类规则的分类  ------------------#
# 客户分类
H = [u'重要价值用户', #分类1
     u'重要发展用户',   #分类2
     u'重要保持用户',  #分类3
     u'重要挽留用户',  #分类4
     u'一般价值用户',  #分类5
     u'一般发展用户', #分类6
     u'一般保持用户', #分类7
     u'一般挽留用户'] #分类8

def set_rfm_level(rs):
    '''
    r_mean = rs['R'][100:-100].mean()        #剔除最高,前100 降低平均值
    f_mean = rs['F'][100:-1].mean()
    m_mean = rs['M'][100:-350].mean() #剔除最高,前350 降低平均值
    '''
    r_mean = rs['R'].mean() 
    f_mean = rs['F'].mean()
    m_mean = rs['M'].mean() 
   
    for index in rs.index:
        rs.loc[index, u'用户价值分数'] = compute(rs.loc[index]['R'],rs.loc[index]['F'],rs.loc[index]['M'])    
        if rs.loc[index]['R'] < r_mean:
            if rs.loc[index]['F'] > f_mean:
                if rs.loc[index]['M'] > m_mean:
                    rs.loc[index, u'用户分类'] = H[0]
                    rs.loc[index, u'用户分类id'] = 0
                else:
                    rs.loc[index, u'用户分类'] = H[1]
                    rs.loc[index, u'用户分类id'] = 1

            else:
                if rs.loc[index]['M'] > m_mean:
                    rs.loc[index, u'用户分类'] = H[2]
                    rs.loc[index, u'用户分类id'] = 2
                else:
                    rs.loc[index, u'用户分类'] = H[3]
                    rs.loc[index, u'用户分类id'] = 3
        else:
            if rs.loc[index]['F'] > f_mean:
                if rs.loc[index]['M'] > m_mean:
                    rs.loc[index, u'用户分类'] = H[4]
                    rs.loc[index, u'用户分类id'] = 4
                else:
                    rs.loc[index, u'用户分类'] = H[5]
                    rs.loc[index, u'用户分类id'] = 5
            else:
                if rs.loc[index]['M'] > m_mean:
                    rs.loc[index, u'用户分类'] = H[6]
                    rs.loc[index, u'用户分类id'] = 6
                else:
                    rs.loc[index, u'用户分类'] = H[7]
                    rs.loc[index, u'用户分类id'] = 7
    return rs
#
rs = set_rfm_level(rfm_pd)
print ('Fianl RFM Scores Overview:')    
print(rs.head(10))
print ('-' * 60)
Fianl RFM Scores Overview:
        R  F  M  recency  frequency      monetary  用户价值分数    用户分类  用户分类id
USERID                                                                   
135194  1  1  1      972          4    279.000000    1.00  重要挽留用户     3.0
135195  1  1  1      972          2    170.000000    1.00  重要挽留用户     3.0
135196  1  1  1      846          8   1784.760010    1.00  重要挽留用户     3.0
135197  4  1  2      383         73  24076.330078    2.06  一般保持用户     6.0
135198  3  1  1      520          1     44.759998    1.60  一般挽留用户     7.0
135199  1  1  1      889          8    815.869995    1.00  重要挽留用户     3.0
135201  2  1  1      748          2     16.830000    1.30  一般挽留用户     7.0
135207  1  1  1     1009          1    106.000000    1.00  重要挽留用户     3.0
135214  4  1  1      383         24   2380.979980    1.90  一般挽留用户     7.0
135215  1  1  1      898          7   1103.130005    1.00  重要挽留用户     3.0
------------------------------------------------------------
# 3D图表示
fig = plt.figure(figsize=(15,10))
dx = fig.add_subplot(111, projection='3d')
# 根据分类种类,定义区分颜色
colors = ['blue', 'white', 'red', 'black' , 'cyan', 'magenta' , 'yellow', 'green']
# c+: cyan + + 表示形式
# y-: yellow + - 表现形式

for i in range(0,8):
    '''
    dx.scatter(rs[rs[u'用户分类id'] == i].recency, 
               rs[rs[u'用户分类id'] == i].frequency, 
               rs[rs[u'用户分类id'] == i].monetary, 
               c = colors[i], 
               label = '分类 ' + str(i+1), 
               s=50)
    '''
    dx.scatter(rs[rs[u'用户分类id'] == i].recency, 
               rs[rs[u'用户分类id'] == i].frequency, 
               rs[rs[u'用户分类id'] == i].monetary, 
               c = colors[i], 
               label = H[i],
               s=50)

#dx.set_title('Clusters of clients')
dx.set_xlabel('Recency')
dx.set_ylabel('Frequency')
dx.set_zlabel('Monetary')
dx.legend()
<matplotlib.legend.Legend at 0x7f3c18a4c908>
#------------------ 方法二:基于RFM实际数值 + K-means的分类  ------------------#
def winsorize(rfm_pd):
    # 离散值处理
    rfm_pd['recency'] = np.log10(rfm_pd['recency'])
    rfm_pd['frequency'] = np.log10(rfm_pd['frequency'])
    rfm_pd['monetary'] = np.log10(rfm_pd['monetary'])
    return rfm_pd
# 离散值处理
rfm_pd = winsorize(rfm_pd)
rfm_pd.plot.scatter(x='recency',y='frequency',c='monetary',cmap='viridis_r', s=50)
def standardlize(rfm_pd):
    # 标准化处理
    rfm_pd['recency'] = stats.zscore(rfm_pd['recency'])
    rfm_pd['frequency'] = stats.zscore(rfm_pd['frequency'])
    rfm_pd['monetary'] = stats.zscore(rfm_pd['monetary'])
    return rfm_pd
# 标准化处理
rfm_pd = standardlize(rfm_pd)
rfm_pd.plot.scatter(x='recency',y='frequency',c='monetary',cmap='viridis_r', s=50)
<matplotlib.axes._subplots.AxesSubplot at 0x7f3c189a67b8>
def get_wcss_kmeans(X_train):
    wcss = []
    for i in range(1,8):
        kmeans = KMeans(n_clusters=i, init='k-means++', random_state=0)
        kmeans.fit(X_train)
        wcss.append(kmeans.inertia_)
    return wcss
X = rfm_pd.iloc[:,3:] # 取RFM实际数据作为训练集

plt.plot(range(1,8), get_wcss_kmeans(X))
#plt.title('Elbow graph')
plt.xlabel('Cluster number')
plt.ylabel('WCSS')
plt.show()
# K-means聚类
# Elbow graph(wcss) shows that an optimal number of clusters would be ?. 
# This is the point where line starts to do a soft descent. 
k = 4
iterations = 500
rank = pd.Series([4, 3, 2, 1])    #根据k值,设定排名,从到小

# 客户分类
H = [ #分类1
     u'4星会员',   #分类2
     u'3星会员',  #分类3
     u'2星会员',  #分类4
     u'1星会员'] 
def get_kmeans(X_train):
    model = KMeans(n_clusters=k, n_jobs=-1, max_iter=iterations)  # 分为k类
    #model = KMeans(n_clusters=k)  # 分为k类
    model.fit(X_train)  # 开始聚类
    return model
# 
def set_rfm_level(data,model):
    rfm = pd.concat([data, pd.Series(model.labels_, index=data.index)], axis=1)  # 详细输出每个样本对应的类别
    rfm.columns = list(data.columns) + [u'聚类类别']  # 重命名表头
    return rfm
kmeans = get_kmeans(X)
rfm_level_pd = set_rfm_level(rfm_pd,kmeans)
# 散点图表示
#rfm_level_pd.plot.scatter(x='recency',y='frequency',c=kmeans.labels_,cmap='viridis_r', s=50)
#rfm_level_pd.plot.scatter(x='recency',y='frequency',c='聚类类别',cmap='viridis_r', s=50)
# 3D图表示
fig = plt.figure(figsize=(15,10))
dx = fig.add_subplot(111, projection='3d')
# 根据分类种类,定义区分颜色
colors = ['blue', 'yellow', 'green', 'red', 'black']

for i in range(0,k):
    dx.scatter(rfm_level_pd[rfm_level_pd[u'聚类类别'] == i].recency, 
               rfm_level_pd[rfm_level_pd[u'聚类类别'] == i].frequency, 
               rfm_level_pd[rfm_level_pd[u'聚类类别'] == i].monetary, 
               c = colors[i], 
               label = '类别 ' + str(i+1), 
               s=50)

dx.set_title('Clusters of clients')
dx.set_xlabel('Recency')
dx.set_ylabel('Frequency')
dx.set_zlabel('Monetary')
dx.legend()
<matplotlib.legend.Legend at 0x7f3c18a4c9b0>
def clear_k_means(model):
    r1 = pd.Series(model.labels_).value_counts()  # 统计各个类别的数目
    r2 = pd.DataFrame(model.cluster_centers_)  # 找出聚类中心
    #r3 = pd.Series(r2[0] * W[0] + r2[1] * W[1] + r2[2] * W[2])
    r3 = pd.Series(compute(r2[0],r2[1],r2[2]))    
    r = pd.concat([r2, r1, r3], axis=1)  # axis=1 横向连接(0是纵向),得到聚类中心对应的类别下的数目
    r.columns = [u'R_质心'] + [u'F_质心'] + [u'M_质心'] + [u'类别数目'] + [u'分数']  # 重命名表头
    r = r.sort_values(by=[u'分数'])
    s = pd.Series(pd.DataFrame(r).index)
    s1 = pd.concat([s,rank],axis=1)
    s1.columns = [u'聚类类别'] + [u'排名']
    # rs = pd.concat([r, r4],axis=1)
    # rs.columns = [u'R_质心'] + [u'F_质心'] + [u'M_质心'] + [u'类别数目'] + [u'分数'] + [u'排名']
    return r,s1
r,s = clear_k_means(kmeans)
print(r)
print ('-' * 60)
print(s)
print ('-' * 60)
         R_质心        F_质心          M_质心  类别数目            分数
0  832.199291    2.552702    184.271284  1129    280.521652
3  857.421053   18.000000   6461.531995    19   1300.791435
2  776.285714   28.714286  18267.926897     7   3171.259732
1  611.500000  250.500000  81605.355469     2  13375.576875
------------------------------------------------------------
   聚类类别  排名
0     0   4
1     3   3
2     2   2
3     1   1
------------------------------------------------------------
# 设置排名
def init_clear_data(rs,s):
    rs['USERID'] = rs.index
    # 按照rs,s相同名字的列合并
    rs = pd.merge(rs,s,how='left')
    rs.sort_values(by='R')
    rs.index = rs['USERID']
    del rs['USERID']

    return rs
#rs = init_clear_data(data=rfm_level_pd,model=kmeans,s=s)
rs2 = init_clear_data(rs=rfm_level_pd,s=s)
print(rs2.head(10))
print ('-' * 60)
        R  F  M  recency  frequency      monetary  聚类类别  排名
USERID                                                     
135194  1  1  1      972          4    279.000000     0   4
135195  1  1  1      972          2    170.000000     0   4
135196  1  1  1      846          8   1784.760010     0   4
135197  4  1  2      383         73  24076.330078     2   2
135198  3  1  1      520          1     44.759998     0   4
135199  1  1  1      889          8    815.869995     0   4
135201  2  1  1      748          2     16.830000     0   4
135207  1  1  1     1009          1    106.000000     0   4
135214  4  1  1      383         24   2380.979980     0   4
135215  1  1  1      898          7   1103.130005     0   4
------------------------------------------------------------
# 3D图表示
fig = plt.figure(figsize=(15,10))
dx = fig.add_subplot(111, projection='3d')
# 根据分类种类,定义区分颜色
colors = ['blue', 'yellow', 'green', 'red']

for i in range(0,k):
    dx.scatter(rfm_pd[rs2[u'排名'] == i+1].recency, 
           rfm_pd[rs2[u'排名'] == i+1].frequency, 
           rfm_pd[rs2[u'排名'] == i+1].monetary, 
           c = colors[i], 
           label = H[i],
           s=50)

#dx.set_title('Clusters of clients')
dx.set_xlabel('Recency')
dx.set_ylabel('Frequency')
dx.set_zlabel('Monetary')
dx.legend()
<matplotlib.legend.Legend at 0x7f3c2836e1d0>
def density_plot(data): 
    import matplotlib.pyplot as plt 
    plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 
    plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 
    # subplots=True表示dataframe格式的数据中每一列绘制一幅子图 
    p = data.plot(kind='kde', linewidth=2, subplots=True, sharex=False) 
    # p[1]代表第1个子图 
    [p[i].set_ylabel(u'密度') for i in range(3)] 
    plt.legend() 
    return plt
# 取RFM实际数据
X2 = rfm_pd.iloc[:,3:]

pic_output = 'pd_' #概率密度图文件名前缀

for i in range(k):
    # rs[u'聚类类别'] == i返回的是index+boolen(注意:rs的index事先不能丢失)
    # X2[]返回的是只有true的行
    #density_plot(X2[rs[u'聚类类别'] == i]).show()
    density_plot(X2[rs2[u'排名']==i+1]).savefig(u'%s%s.png' %(pic_output, i))
#write_csv(rs_=rs)

全部回复

0/140

量化课程

    移动端课程