【RFM模型】用python实现客户价值分析

文章导览

        目标定义

        数据获取

        数据探索性分析

        数据清洗

        RFM模型处理

一、目标定义

这里有一个关于欧洲某商家2010年12月-2011年12月的销售数据截取的部分片段。目标是根据RF模型对顾客进行划分。

二、数据获取

​​​​​​​RFM模型训练用 - Heywhale.comhttps://www.heywhale.com/mw/dataset/623f3a0b40f3c80018378be0/file数据集已挂在和鲸社区,链接如上

三、探索性分析

        3,1 分析方向

查看数据类型、字段、大小、缺失情况,异常情况等

## 导入相关依赖
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"]=["SimHei"]
plt.rcParams['axes.unicode_minus'] = False  # 解决中文显示的问题
from warnings import filterwarnings
filterwarnings('ignore')  # 忽略警告的问题
import seaborn as sns
!pip install brewer2mpl  # 解决画图库尴尬的配色问题
import brewer2mpl

# 导入数据
data = pd.read_csv("./data_sale.csv")
data.head(10)

# 查看基本信息
data.shape
data.isnull().sum()
data.info()

# 输出如下
(541909, 8)

InvoiceNo           0
StockCode           0
Description      1454
Quantity            0
InvoiceDate         0
UnitPrice           0
CustomerID     135080
Country             0
dtype: int64

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 541909 entries, 0 to 541908
Data columns (total 8 columns):
 #   Column       Non-Null Count   Dtype  
---  ------       --------------   -----  
 0   InvoiceNo    541909 non-null  object 
 1   StockCode    541909 non-null  object 
 2   Description  540455 non-null  object 
 3   Quantity     541909 non-null  int64  
 4   InvoiceDate  541909 non-null  object 
 5   UnitPrice    541909 non-null  float64
 6   CustomerID   406829 non-null  float64
 7   Country      541909 non-null  object 
dtypes: float64(2), int64(1), object(5)
memory usage: 33.1+ MB

# 数据集共54万1999条数据,8个字段
# InvoiceNo  发票编号  字符串  无缺
# StockCode  走势  字符串  无缺
# Description  描述  字符串 有缺
# Quantity  数量  数字型  无缺
# InvoiceDate  发票日期  字符串  无缺
# UnitPrice  单价  小数型  无缺
# CustomerID  顾客编号  小数型  有缺
# Country  国家  字符型  无缺

观察到Customer_ID一列数据类型是浮点小数,需要转换成字符型

# 转换类型
data["InvoiceDate"] = pd.to_datetime(data["InvoiceDate"])
data["CustomerID"] = data["CustomerID"].astype("object",copy=False)

四、数据清洗

## 数据清洗
# 去重
data = data.drop_duplicates()  # duplicates  (n)副本  默认行数据比较取出完全相同的
data.info()  # 去重后还剩53万+的数据

-------------------------------------
# 输出
<class 'pandas.core.frame.DataFrame'>
Int64Index: 536641 entries, 0 to 541908
Data columns (total 8 columns):
 #   Column       Non-Null Count   Dtype         
---  ------       --------------   -----         
 0   InvoiceNo    536641 non-null  object        
 1   StockCode    536641 non-null  object        
 2   Description  535187 non-null  object        
 3   Quantity     536641 non-null  int64         
 4   InvoiceDate  536641 non-null  datetime64[ns]
 5   UnitPrice    536641 non-null  float64       
 6   CustomerID   401604 non-null  object        
 7   Country      536641 non-null  object        
dtypes: datetime64[ns](1), float64(1), int64(1), object(5)
memory usage: 36.8+ MB
--------------------------------------
# 统计异常值
data.describe()  # 负值明显是错误的

# 查看负值的情况有多少
data[data["UnitPrice"]<0]["UnitPrice"].count()

data[data["Quantity"]<0]["Quantity"].count()


--------------------------------------------
# 输出
	Quantity	UnitPrice
count	536641.000000	536641.000000
mean	9.620029	4.632656
std	219.130156	97.233118
min	-80995.000000	-11062.060000
25%	1.000000	1.250000
50%	3.000000	2.080000
75%	10.000000	4.130000
max	80995.000000	38970.000000

2

10587
--------------------------------------------
# 删除不符合条件的行
data = data[data['UnitPrice']>=0]  
data = data[data['Quantity']>=0]
# 缺失删除的是整行数据,删完后还剩52万6千+的数据

# 统计缺失值有多少
data.isnull().sum()

# 统计缺失值的占比
data.isnull().sum()/data.shape[0]*100  # 顾客ID一栏缺失的比较多

# 删除CustomerID为空的数据
data = data[~data["CustomerID"].isnull()]  # 删除某些行时,可以用赋值语句。~代表取反
data = data.reset_index(drop=True)  # 删除之后还剩39万+行数据

五、数据分析及RFMM用户价值分析

## 数据分析
# 添加一列sales
data["sales"] = data["Quantity"] * data["UnitPrice"]
data

# 计算购买频率(每个顾客购买次数)
frequency_data = data.groupby("CustomerID")["InvoiceNo"].count()
frequency_data = pd.DataFrame(frequency_data)

# 计算每个顾客的购买总金额
frequency_data["sales"] = data.groupby("CustomerID")["sales"].sum()

# 计算顾客的最近购买行为与最终日期的相隔天数
frequency_data["dateDiff"] = (pd.to_datetime('2012-01-01') - data.groupby("CustomerID")["InvoiceDate"].max()).dt.days
data_rfm = frequency_data

# 重命名列
data_rfm.columns = ["frequency","sales","datediff"]
data_rfm

 

## 数据可视化
# 查看数据分布
sns.pairplot(data_rfm)  # pairplot函数查看数据两两之间的关系

# 绘制数据的直方图
plt.figure(1,figsize=(12,6))
n=0
for x in ['frequency','datediff','sales']:
    n+=1
    plt.subplot(1,3,n)
    plt.subplots_adjust(hspace=0.5,wspace=0.5)
    sns.distplot(data_rfm[x],bins=30)
    plt.title('{} 直方图'.format(x))
plt.show()

 

## RFM模型
# 计算出划分用户的阈值,通过分布直方图可以发现该份数据不适合用中位数来分层,因此这里用均值做分层
r_mean = data_rfm["datediff"].mean()
f_mean = data_rfm["frequency"].mean()
m_mean = data_rfm["sales"].mean()

# 添加一列数据type
data_rfm["cus_type"] = np.nan
data_rfm

 

# 编写循环填充表格type
for i in range(len(data_rfm)):
    if data_rfm.iloc[i,0] >= f_mean and data_rfm.iloc[i,1] >= m_mean and data_rfm.iloc[i,2] < r_mean:
        data_rfm.iloc[i,3]="重要价值用户"
    elif data_rfm.iloc[i,0] >= f_mean and data_rfm.iloc[i,1] >= m_mean and data_rfm.iloc[i,2] >= r_mean:
        data_rfm.iloc[i,3]="重要保持用户"
    elif data_rfm.iloc[i,0] < f_mean and data_rfm.iloc[i,1] >= m_mean and data_rfm.iloc[i,2] < r_mean:
        data_rfm.iloc[i,3]="重要发展用户"
    elif data_rfm.iloc[i,0] < f_mean and data_rfm.iloc[i,1] >= m_mean and data_rfm.iloc[i,2] >= r_mean:
        data_rfm.iloc[i,3]="重要挽留用户"
    elif data_rfm.iloc[i,0] >= f_mean and data_rfm.iloc[i,1] < m_mean and data_rfm.iloc[i,2] < r_mean:
        data_rfm.iloc[i,3]="一般价值用户"
    elif data_rfm.iloc[i,0] < f_mean and data_rfm.iloc[i,1] < m_mean and data_rfm.iloc[i,2] < r_mean:
        data_rfm.iloc[i,3]="一般发展用户"
    elif data_rfm.iloc[i,0] >= f_mean and data_rfm.iloc[i,1] < m_mean and data_rfm.iloc[i,2] >= r_mean:
        data_rfm.iloc[i,3]="一般保持用户"
    elif data_rfm.iloc[i,0] < f_mean and data_rfm.iloc[i,1] < m_mean and data_rfm.iloc[i,2] >= r_mean:
        data_rfm.iloc[i,3]="一般挽留用户"

data_rfm

# 计算个类型用户数量并可视化
Cus_type = data_rfm.groupby("cus_type")["cus_type"].count().sort_values(ascending=False)
sns.barplot(Cus_type.index,Cus_type)
plt.xticks(rotation=30)
plt.show()

​​​​​​​ 

 

# 计算不同用户的消费总额,可视化其份额比例
cus_sales = data_rfm.groupby("cus_type")["sales"].sum().sort_values(ascending=False)
cus_sales
plt.figure(figsize=(10,10))
explode01 = np.array([0.05]*8)
colors01 = brewer2mpl.get_map('Set3', 'qualitative', 8).mpl_colors  # 解决配色问题关键
plt.pie(cus_sales,labels=cus_sales.index,autopct='%.2f%%',textprops={'fontsize':14,'color':'k'},colors=colors01,explode=explode01)
plt.title('不同类型的客户销售份额',fontsize=15)
plt.show()


  • 5
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值