赞
踩
文章导览
目标定义
数据获取
数据探索性分析
数据清洗
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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。