零售行业交易数据分析——RFM模型分类及可视化(Python实现)

news2024/11/16 17:47:33

内容简介

接上一篇文章《客户终身价值(CLTV)计算和回归预测模型》,本文继续分析一年的零售交易数据,从用户的角度,使用RFM模型对用户进行打分归类,并对结果进行可视化展示。

数据集介绍

数据集包含一家在英国注册的在线零售公司于 01/12/2010 和 09/12/2011 之间发生的所有交易。该公司主要销售各种场合的礼品,公司的许多客户都是批发商。

数据集一共包含8列:

  • InvoiceNo:发票编号。标称,为每笔交易唯一分配的 6 位整数。如果此代码以字母“c”开头,则表示取消。
  • StockCode:商品(商品)代码。标称,为每个不同的产品唯一分配的 5 位整数。
  • Description: 描述。 产品(项目)名称。
  • Quantity:数量。每笔交易每个产品(项目)的数量。
  • InvoiceDate:发票日期和时间,每笔交易产生的日期和时间。
  • UnitPrice:单价,单位产品价格(以英镑为单位)。
  • CustomerID:客户编号,一个唯一分配给每个客户的 5 位整数。
  • Country:国名,每个客户所在国家/地区的名称。

数据预处理

1. 导入包和封装预处理过程

先导入要使用的包和封装好的预处理过程。

import os
import datetime
import squarify
import warnings
import pandas as pd 
import numpy as np
import datetime as dt
from operator import attrgetter
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import plotly.graph_objs as go
from plotly.offline import iplot
from sklearn.metrics import (silhouette_score,
                             calinski_harabasz_score,
                             davies_bouldin_score)
from lifetimes import BetaGeoFitter, GammaGammaFitter
from lifetimes.plotting import plot_period_transactions
%matplotlib inline
#%load_ext nb_black
warnings.filterwarnings('ignore')
sns.set_style('whitegrid')
palette = 'Set2'

def replace_with_thresholds(dataframe, variable, q1 = 0.25, q3 = 0.75):
    
    '''
    Detects outliers with IQR method and replaces with thresholds 
    
    '''
    
    df_ = dataframe.copy()
    quartile1 = df_[variable].quantile(q1)
    quartile3 = df_[variable].quantile(q3)
    iqr = quartile3 - quartile1
    
    up_limit = quartile3 + 1.5 * iqr
    low_limit = quartile1 - 1.5 * iqr
    df_.loc[(df_[variable] < low_limit), variable] = low_limit
    df_.loc[(df_[variable] > up_limit), variable] = up_limit
    
    return df_

def ecommerce_preprocess(dataframe):
    df_ = dataframe.copy()
    
    #Missing Values
    df_ = df_.dropna()
    
    #Cancelled Orders & Quantity
    df_ = df_[~df_['InvoiceNo'].str.contains('C', na = False)]
    df_ = df_[df_['Quantity'] > 0]
    
    #Replacing Outliers
    df_ = replace_with_thresholds(df_, "Quantity", q1 = 0.01, q3 = 0.99)
    df_ = replace_with_thresholds(df_, "UnitPrice", q1 = 0.01, q3 = 0.99)
    
    #Total Price
    df_["TotalPrice"] = df_["Quantity"] * df_["UnitPrice"]
    
    return df_

2. 数据导入

导入的同时指定好个列的数据格式,就可以直接使用上面封装好的函数对数据进行预处理。

预处理的过程这里直接跳过,具体的处理过程在《客户终身价值(CLTV)计算和回归预测模型》这篇文章中由比较详细的解释。

#数据导入
df=pd.read_csv("data.csv",encoding="utf-8",
                 dtype = {'CustomerID': str,
                          'InvoiceID': str},
                 parse_dates = ['InvoiceDate'], 
                 infer_datetime_format = True)
                 
df = ecommerce_preprocess(df)
df.describe()

RFM模型分析

RFM模型介绍

RFM模型是客户关系管理(CRM)中被广泛使用,是衡量客户价值的重要工具。通过客户的近期交易行为、交易频率和交易金额三项指标,将客户划分为不同类型:

  • R(Recency): 计算最近的一次消费时间距离2017年12月3日有多久。消费间隔越小,表示R值越小,价值越高。
  • F(Frequency):消费频率,在这个时间段里,用户消费的次数。
  • M(Monetary): 消费金额,用户消费的总金额。

在SQL淘宝用户数据分析文章中,也使用到了RFM分析方法,是比较常用的用户分析模型。

1.分别计算R、F、M维度的值。

today_date = dt.datetime(2011,12,11)

rfm = df.groupby('CustomerID').agg({'InvoiceDate': lambda x: (today_date - x.max()).days,
                                    'InvoiceNo': lambda x: x.nunique(),
                                    'TotalPrice': lambda x: x.sum()})

rfm.columns = ['recency', 'frequency', 'monetary']
rfm= rfm[rfm['monetary'] > 0]
rfm = rfm.reset_index()

rfm.head()

在这里插入图片描述

2.汇总RFM分数并对用户分类

将计算好的RFM的值按照各自的分位数,分成1-5等分,然后组合成最终的RFM分数。
由于三个维度分别由5个等级,用户分数类别有555=125种不同的排列组合。为了简化过程,这里我们先使用R和F两个维度对用户进行简单的分类。

def get_rfm_scores(dataframe):
    
    df_ = dataframe.copy()
    df_['recency_score'] = pd.qcut(df_['recency'],5,labels = [5, 4, 3, 2, 1])
    df_['frequency_score'] = pd.qcut(df_['frequency'].rank(method = "first"), 5, labels = [1, 2, 3, 4, 5])
    df_['monetary_score'] = pd.qcut(df_['monetary'], 5, labels = [1, 2, 3, 4, 5])
    df_['RFM_SCORE'] = (df_['recency_score'].astype(str) + df_['frequency_score'].astype(str)+ df_['monetary_score'].astype(str))
    
    return df_

rfm = get_rfm_scores(rfm)

seg_map = {
    r'[1-2][1-2]': 'Hibernating',
    r'[1-2][3-4]': 'At Risk',
    r'[1-2]5': 'Can\'t Loose',
    r'3[1-2]': 'About to Sleep',
    r'33': 'Need Attention',
    r'[3-4][4-5]': 'Loyal Customers',
    r'41': 'Promising',
    r'51': 'New Customers',
    r'[4-5][2-3]': 'Potential Loyalists',
    r'5[4-5]': 'Champions'
}

rfm['segment'] = rfm['recency_score'].astype(str) +rfm['frequency_score'].astype(str)
rfm['segment'] = rfm['segment'].replace(seg_map, regex = True)

rfm.head()

在这里插入图片描述

3. RFM模型结果评估

#model evaluation
print(' RFM Model Evaluation '.center(70, '='))
X = rfm[['recency_score', 'frequency_score']]
labels = rfm['segment']
print(f'Number of Observations: {X.shape[0]}')
print(f'Number of Segments: {labels.nunique()}')
print(f'Silhouette Score: {round(silhouette_score(X, labels), 3)}')
print(f'Calinski Harabasz Score: {round(calinski_harabasz_score(X, labels), 3)}')
print(f'Davies Bouldin Score: {round(davies_bouldin_score(X, labels), 3)} \n{70*"="}')

在这里插入图片描述
对不同客户群体的RFM数值进行描述性分析

rfm[['recency','monetary','frequency','segment']]\
.groupby('segment')\
.agg({'mean','std','max','min'})

在这里插入图片描述

数据结果可视化

1.树状图

segments = rfm['segment'].value_counts().sort_values(ascending = False)
fig = plt.gcf()
ax = fig.add_subplot()
fig.set_size_inches(16, 10)
squarify.plot(sizes=segments,
              label=[label for label in seg_map.values()],
              pad = False,
              bar_kwargs = {'alpha': 1},
              text_kwargs = {'fontsize':15})
plt.title("Customer Segmentation Map", fontsize = 20)
plt.xlabel('Frequency', fontsize = 18)
plt.ylabel('Recency', fontsize = 18)
plt.show()

在这里插入图片描述

2.柱状图

plt.figure(figsize = (18, 8))
ax = sns.countplot(data = rfm,
                   x = 'segment',
                   palette = palette)
total = len(rfm.segment)
for patch in ax.patches:
    percentage = '{:.1f}%'.format(100 * patch.get_height()/total)
    x = patch.get_x() + patch.get_width() / 2 - 0.17
    y = patch.get_y() + patch.get_height() * 1.005
    ax.annotate(percentage, (x, y), size = 14)
plt.title('Number of Customers by Segments', size = 16)
plt.xlabel('Segment', size = 14)
plt.ylabel('Count', size = 14)
plt.xticks(size = 10)
plt.yticks(size = 10)
plt.show()

在这里插入图片描述

3.根据R、F、M三个维度查看客户分布情况

fig, axes = plt.subplots(3, 1, figsize=(16, 12))
fig.suptitle('RFM Segment Analysis', size = 14)
feature_list = ['recency', 'monetary', 'frequency']
for idx, col in enumerate(feature_list):
    sns.histplot(ax = axes[idx], data = rfm,
                 hue = 'segment', x = feature_list[idx],
                 palette= palette)
    if idx == 1:
        axes[idx].set_xlim([0, 400])
    if idx == 2:
        axes[idx].set_xlim([0, 30])
plt.tight_layout()
plt.show()

在这里插入图片描述

4. R、F、M分布情况

首先,分别查看三个维度分数的分布。

# plot the distribution of customers over R and F
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(15, 4))

for i, p in enumerate(['recency_score', 'recency_score', 'monetary_score']):
    parameters = {'recency_score':'recency', 'recency_score':'frequency','monetary_score':'monetary'}
    y = rfm[p].value_counts().sort_index()
    x = y.index
    ax = axes[i]
    bars = ax.bar(x, y, color='silver')
    ax.set_frame_on(False)
    ax.tick_params(left=False, labelleft=False, bottom=False)
    ax.set_title('Distribution of {}'.format(parameters[p]),
                fontsize=14)
    for bar in bars:
        value = bar.get_height()
        if value == y.max():
            bar.set_color('firebrick')
        ax.text(bar.get_x() + bar.get_width() / 2,
                value - 5,
                '{}\n({}%)'.format(int(value), int(value * 100 / y.sum())),
               ha='center',
               va='top',
               color='w')

plt.show()

在这里插入图片描述
结果显示,整体都是比较均匀地分布,都是在19%-20%。

接下来看对于不同的R和F,M的分布情况如何。

# plot the distribution of M for RF score
fig, axes = plt.subplots(nrows=5, ncols=5,
                         sharex=False, sharey=True,
                         figsize=(10, 10))

r_range = range(1, 6)
f_range = range(1, 6)
for r in r_range:
    for f in f_range:
        y = rfm[(rfm['recency_score'] == r) & (rfm['frequency_score'] == f)]['monetary_score'].value_counts().sort_index()
        x = y.index
        ax = axes[r - 1, f - 1]
        bars = ax.bar(x, y, color='silver')
        if r == 5:
            if f == 3:
                ax.set_xlabel('{}\nF'.format(f), va='top')
            else:
                ax.set_xlabel('{}\n'.format(f), va='top')
        if f == 1:
            if r == 3:
                ax.set_ylabel('R\n{}'.format(r))
            else:
                ax.set_ylabel(r)
        ax.set_frame_on(False)
        ax.tick_params(left=False, labelleft=False, bottom=False)
        ax.set_xticks(x)
        ax.set_xticklabels(x, fontsize=8)

        for bar in bars:
            value = bar.get_height()
            if value == y.max():
                bar.set_color('firebrick')
            ax.text(bar.get_x() + bar.get_width() / 2,
                    value,
                    int(value),
                    ha='center',
                    va='bottom',
                    color='k')
fig.suptitle('Distribution of M for each F and R',
             fontsize=14)
plt.tight_layout()
plt.show()

在这里插入图片描述
从上面图可以看出,数据主要集中在左上角和右下角:左上角是交易次数最少(F:1-2)并且交易金额也是集中在(M:1-2),表明有许多客户是一次性交易就离开了;右下角可见,这一年中花费最多的客户(M=5),大多是交易活动频繁的熟客(F和R都为4-5)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文带你搞懂 MySQL 事务

目录 1、事务的基本操作 2、事务的ACID属性 3、事务隔离级别 4、多版本并发控制&#xff08; MVCC &#xff09; 5、深入理解隔离级别 什么是事务&#xff1f; 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&…

基于OpenCV提供的人脸识别算法LBPH实现人脸识别

本文通过学习LBPH人脸识别算法&#xff0c;简要了解人脸识别技术的原理&#xff0c;实现人脸采集、训练人脸模型实现人脸识别。 文章目录一、 LBPH人脸识别算法概述二、 人脸识别技术原理三、 关键模块四、 实验准备1. 第三方库2. 新建相关文件夹3. 实验环境五、 人脸采集与检测…

【自学Java】Java开发环境搭建

Java开发环境搭建 Java开发环境搭建 Java 的开发环境主要使用 JDK。我们这边介绍的 JDK 版本是 1.8。我们可以直接从 Oracle 官网上面下载。JDK 下载地址&#xff1a; https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html如下图&#xff1a; 由于在…

如何使用css实现三角形?

​ 一、前言 ​ 通常情况下&#xff0c;我们会使用图片或者svg去完成三角形效果图&#xff0c;但如果单纯使用css如何完成一个三角形呢&#xff1f; 实现过程似乎也并不困难&#xff0c;通过边框就可完成 二、实现过程 在以前也讲过盒子模型&#xff0c;默认情况下是一个矩形…

Faster RCNN网络源码解读(Ⅳ) --- Faster R-CNN主体代码执行流程解析

目录 一、Faster R-CNN框架图 二、结合代码 &#xff08;faster_rcnn_framework.py&#xff09; 2.1 FasterRCNNBase类 2.2 FasterRCNN类 一、Faster R-CNN框架图 我们获取一张图片后将其输入特征提取网络Backbone中得到特征图&#xff0c;将特征图输入到RPN中得到一系列的…

Maven的基本使用方法

Maven Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 提供了一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布……&#xff09; 提供了一套依赖管理机制 标准化的项…

【JavaEE】Linux

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录一、Linux概述二、 云服务器安装Linux环境 Xshell登录三、Linux中常用指令介绍1. ls2. pwd 查看当前路径对应的绝对路径3. cd 切换当前的工作目录4. touch&#xff1a;创建一个空文件5. cat&#xff1a; 显示文件内…

23 种设计模式总结

title: 23 种设计模式总结 date: 2022-12-30 16:53:46 tags: 设计模式 categories:设计模式 cover: https://cover.png feature: false 文章目录1. 创建型1.1 单例模式&#xff08;Singleton Design Pattern&#xff09;1.1.1 概述与实现1.1.2 多例1.2 工厂模式&#xff08;Fa…

零信任与 K8s 环境实践

零信任的热度自然吸引了大量市场上的关注。尽管如此&#xff0c;零信任并不只是一个空洞的术语&#xff1a;它代表了对未来安全性的一些深刻变革的想法。那么&#xff0c;零信任具体是什么&#xff0c;为什么它会突然变得如此重要&#xff1f;零信任对 Kubernetes 用户来说具体…

【数据结构】顺序表(线性表)的实现

目录 一、什么是顺序表&#xff1f; 二、顺序表的动态实现 1、顺序表初始化 2、顺序表打印 3、顺序表检查空间 4、顺序表尾插 5、顺序表尾删 6、顺序表头插 7、顺序表头删 8、顺序表指定位置插入 9、顺序表指定位置删除 10、顺序表查找 11、顺序表销毁 三、源代码 1、SeqList.h…

hnu社交网络作业1

前言&#xff1a;上的是林剑新老师的课程&#xff0c;还是比较有意思的&#xff0c;此博客用来记录作业的学习情况&#xff0c;答案为老师提供的 一、对于图 1&#xff0c;请回答以下问题&#xff0c;并给出相应的计算过程&#xff1a; (1) 计算图 G 中每个顶点的 closeness 中…

Java重点源码回顾——HashMap1.7

1. 概述 public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, SerializableHashMap在我们的日常使用中非常多&#xff0c;所以今天来阅读下它的源码&#xff0c;了解它具体的设计思想&#xff0c;能够帮助我们扩宽视野。 H…

vmware vcp证书怎么考?vmware vcp证书通过率如何

可为您提供行业领先的虚拟化技术培训和认证服务&#xff0c;这些认证不但会考察您的知识掌握情况和经验水平&#xff0c;还将与您的实际工作职责挂钩。VMware认证按照不同解决方案划分&#xff0c;可分为四条路径&#xff1a;数据中心虚拟化、网络虚拟化、云计算管理和自动化、…

微信小程序项目转uniapp踩坑日记

本文目录一、前言二、转换方式三、后语四、其他&#xff1a;node报错1、包默认C盘存放&#xff0c;而不是安装目录E盘2、正确的环境变量添加3、npm install 命令报错4、npm install -g express报错没有权限一、前言 由于想要把之前完成的微信小程序项目转换成uniapp项目&#…

git入门指南

文章目录Git入门指南前言什么是版本控制系统&#xff08;VCS&#xff09;版本控制系统Git1、概述2、目前比较流行的Git和SVN&#xff0c;区别是什么3、Git安装4、Git的工作区、暂存区、本地仓库、远程仓库5、git的分支6、git的标签7、 实际操作下git常用命令准备操作git cloneg…

Kafka Cluster 扩容 添加副本 重分配分区

Kafka Cluster 扩容 针对kafka集群&#xff0c;可以通过向群集添加新节点来扩展群集。新节点将仅服务于新主题或新分区&#xff0c;现有分区将不会自动重新平衡以使用新节点。如果需要对现有的TOPIC进行重新分配分区&#xff0c;需要运维人员手动进行干预。今天学习下如何对已…

CAPL学习之路-测试功能集函数(故障注入函数)

TestDisableMsg 禁止发送消息,除非调用函数TestSetMsgEvent 使用TestEnableMsg重新启用消息。此函数影响分配CANoe交互层或CANopen仿真的仿真节点 这个函数可以在测试用例中控制Simulation Setup界面仿真节点报文的发送与停止 testcase TCExample() {testDisableMsg(LightSt…

Linux模块代码、编译、加载、卸载一条龙

最近要写一个Linux的内核模块&#xff0c;记录一下内核模块的代码编写、编译、加载和卸载的基本流程&#xff0c;以作备忘&#xff0c;也希望能帮到有需要的同学。 模块代码 //代码来自https://yangkuncn.cn/kernel_INIT_WORK.html //init_works.c #include <linux/kernel…

Docker-compose快速部署PostgreSQL

Docker-compose快速部署PostgreSQL&#xff1a; 利用docker-compose编排工具部署&#xff1a; docker-compose.yml 文件 version: "3.1" services:postgresql:image: postgres:12-alpinecontainer_name: postgresqlenvironment:POSTGRES_DB: postgresPOSTGRES_USE…

Python--数据容器总结

一、数据容器的分类 数据容器可以从一下视角进行简单的分类&#xff1a; 是否支持下标索引 支持&#xff1a;列表、元组、字符串 --序列类型不支持&#xff1a;集合、字典 --非序列类型是否支持重复元素 支持&#xff1a;列表、元组、字符串 --序列类型不支持&#xff1a;集…