在线销售数据集分析:基于Python的RFM数据分析方法实操训练

news2025/3/12 23:35:37

一、前言

个人练习,文章用于记录自己的学习练习过程,分享出来和大家一起学习。
数据集:在线销售数据集
分析方法:RFM分析方法

二、过程

1.1 库的导入与一些必要的初始设置

import pandas as pd
import datetime
import matplotlib.pyplot as ax[0]

# 设置中文字体,避免可视化时中文标题出现乱码
ax[0].rcParams['font.sans-serif'] = ['Microsoft YaHei']

数据导入与预览:

data = pd.read_csv(r"C:\Users\31049\Desktop\在线零售数据集\Online_Retail.csv")
data.head(5)
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62012/1/10 8:262.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62012/1/10 8:263.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82012/1/10 8:262.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62012/1/10 8:263.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62012/1/10 8:263.3917850.0United Kingdom

1.2 数据的初步分析

data.shape
(541909, 8)
data.info()
<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
data.isnull().sum()
InvoiceNo           0
StockCode           0
Description      1454
Quantity            0
InvoiceDate         0
UnitPrice           0
CustomerID     135080
Country             0
dtype: int64
# 缺失值占比
(data.isnull().sum() / data.shape[0]).round(3)
InvoiceNo      0.000
StockCode      0.000
Description    0.003
Quantity       0.000
InvoiceDate    0.000
UnitPrice      0.000
CustomerID     0.249
Country        0.000
dtype: float64

初步分析结果:

  • ~数据集共541909行和8个属性列。

  • ~属性InvoiceDate是object型,需要修改为datetime型;属性CustomerID是float型,需要修改为str型。

  • ~Description列包含1454个缺失值,CustomerID列包含135080个缺失值,不过缺失值占整个数据的比例都不足0.5%(即较少缺失值)

1.3 数据清洗

# 删除InvoiceNo列中以字母‘c’开头的数据
data = data[data['InvoiceNo'].str.startswith('c') == False]
# 删除重复数据
data.drop_duplicates(inplace=True)
# 删除缺失值
data.dropna(subset=['CustomerID'], inplace=True)
data.shape
(401604, 8)
# 格式转换
data['InvoiceDate'] = pd.to_datetime(data['InvoiceDate'], format='mixed', dayfirst=False)
data['CustomerID'] = data['CustomerID'].astype(str)

1.4 数据探索性分析(EDA)————RFM分析

# 加入消费金额列
data['Total'] = data['UnitPrice'] * data['Quantity']
# 设定最新的时间作为计算标准
latest = data['InvoiceDate'].max() + datetime.timedelta(days=1)

# 开始计算R、F、M
rfm = data.groupby(['CustomerID']).agg({
    'InvoiceDate': lambda x: (latest-x.max()).days,     # R:最近一次消费距离laetest多少天了
    'InvoiceNo': 'count',     # F: 消费频次
    'Total': 'sum'    # M: 所有消费总金额
})

# 设置分组计算结果的列索引
rfm.rename(columns={'InvoiceDate': 'R','InvoiceNo': 'F','Total': 'M'}, inplace=True)

rfm.head(10)
RFM
CustomerID
12346.069320.00
12347.01531824310.00
12348.0443311797.24
12349.0386731757.55
12350.0395617334.40
12352.0440951545.41
12353.0571489.00
12354.0600581079.40
12355.0264813459.40
12356.0390592811.43
# 计算RFM分数,计算前使用rank()进行排名,避免重复值出现在分箱边界而分箱出错

# R分数: R值比较特殊,R值越小越好(用户质量越高),打分时,R值越小,给分越高,即r_s越大越好
rfm['r_s'] = pd.qcut(rfm['R'].rank(method='first'), q=5, labels=[5, 4, 3, 2, 1]).astype(int)

# F分数:F值越大,给分越高
rfm['f_s'] = pd.qcut(rfm['F'].rank(method='first'), q=5, labels=[1, 2, 3, 4, 5]).astype(int)

# M分数:F值越大,给分越高
rfm['m_s'] = pd.qcut(rfm['M'].rank(method='first'), q=5, labels=[1, 2, 3, 4, 5]).astype(int)
# 基于评分的平均值进行分值评价

def low_high(score, mean):
    if score>mean:
        return '高'
    else:
        return '低'

# 评分高or低
rfm['rs高低'] = rfm['r_s'].apply(lambda x: low_high(x, rfm['r_s'].mean()))
rfm['fs高低'] = rfm['f_s'].apply(lambda x: low_high(x, rfm['f_s'].mean()))
rfm['ms高低'] = rfm['m_s'].apply(lambda x: low_high(x, rfm['m_s'].mean()))

rfm.head(10)
RFMr_sf_sm_srs高低fs高低ms高低
CustomerID
12346.069320.00111
12347.01531824310.00555
12348.0443311797.24334
12349.0386731757.55444
12350.0395617334.40122
12352.0440951545.41344
12353.0571489.00211
12354.0600581079.40234
12355.0264813459.40112
12356.0390592811.43445

1.5 用户分类



def classify(r, f, m):
    if r=='高' and f=='高' and m=='高':
        return '重要价值用户'
    elif r=='高' and f=='低' and m=='高':
        return '重要发展用户'
    elif r=='低' and f=='高' and m=='高':
        return '重要保持用户'
    elif r=='低' and f=='低' and m=='高':
        return '重要挽留用户'
    elif r=='高' and f=='高' and m=='低':
        return '一般价值用户'
    elif r=='高' and f=='低' and m=='低':
        return '一般发展用户'
    elif r=='低' and f=='高' and m=='低':
        return '一般保持用户'
    elif r=='低' and f=='低' and m=='低':
        return '一般保留用户'
    
# 分类
rfm['用户分类'] = rfm.apply(lambda x: classify(x['rs高低'], x['fs高低'], x['ms高低']), axis=1)
rfm.head(10)
RFMr_sf_sm_srs高低fs高低ms高低用户分类
CustomerID
12346.069320.00111一般保留用户
12347.01531824310.00555重要价值用户
12348.0443311797.24334重要挽留用户
12349.0386731757.55444重要价值用户
12350.0395617334.40122一般保留用户
12352.0440951545.41344重要保持用户
12353.0571489.00211一般保留用户
12354.0600581079.40234重要挽留用户
12355.0264813459.40112一般保留用户
12356.0390592811.43445重要价值用户
1.6 可视化
# R的分布图
ax[0].hist(rfm['R'], bins=30, edgecolor='black')
ax[0].xticks(range(0, max(rfm['R'])+1, 500))
ax[0].xlabel('R值')
ax[0].ylabel('频数')
ax[0].title('R的分布图')
ax[0].show()

在这里插入图片描述

# F的分布图
ax[0].hist(rfm['F'], bins=30, edgecolor='black')
ax[0].xlabel('F值')
ax[0].ylabel('频数')
ax[0].title('F的分布图')
ax[0].show()

在这里插入图片描述

# M的分布图
ax[0].hist(rfm['M'], bins=30, edgecolor='black')
ax[0].xlabel('M值')
ax[0].ylabel('频数')
ax[0].title('M的分布图')
ax[0].show()

在这里插入图片描述

# 不同用户数量的可视化

categories = rfm.groupby('用户分类')['用户分类'].count().sort_values(ascending=False)

# 柱状图
fig, ax = plt.subplots(2, 1, figsize=(6, 11))
ax[0].bar(categories.index, categories.values, color='skyblue')
for i in range(len(categories)):
    ax[0].text(categories.index[i], categories.iloc[i], s=str(categories.iloc[i]), ha='center', va='bottom')
ax[0].tick_params(axis='x', rotation=45)
ax[0].set_title('不同种类的用户数')
ax[0].set_ylabel('数量')

# 饼图
ax[1].pie(categories, labels=categories.index, radius=1.1, autopct=lambda pct: f'{int(pct/100*categories.sum())}, {pct: .1f}%')
ax[1].set_title('各种用户占比')

fig.tight_layout()
plt.show()

在这里插入图片描述

因为需要结合具体的业务,所以数据分析中提建议的部分就略了,本文到此结束。

# 文章如有错误,欢迎大家指正。我们下期见叭!

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

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

相关文章

小程序设计和开发:要如何明确目标和探索用户需求?

一、明确小程序的目标 确定业务目标 首先&#xff0c;需要明确小程序所服务的业务领域和目标。例如&#xff0c;是一个电商小程序&#xff0c;旨在促进商品销售&#xff1b;还是一个服务预约小程序&#xff0c;方便用户预订各类服务。明确业务目标有助于确定小程序的核心功能和…

【C语言深入探索】:指针高级应用与极致技巧(二)

目录 一、指针与数组 1.1. 数组指针 1.2. 指向多维数组的指针 1.2.1. 指向多维数组元素的指针 1.2.2. 指向多维数组行的指针 1.3. 动态分配多维数组 1.4. 小结 二、指针与字符串 2.1. 字符串表示 2.2. 字符串处理函数 2.3. 代码示例 2.4. 注意事项 三、指针与文件…

手写MVVM框架-构建虚拟dom树

MVVM的核心之一就是虚拟dom树&#xff0c;我们这一章节就先构建一个虚拟dom树 首先我们需要创建一个VNode的类 // 当前类的位置是src/vnode/index.js export default class VNode{constructor(tag, // 标签名称&#xff08;英文大写&#xff09;ele, // 对应真实节点children,…

【Blazor学习笔记】.NET Blazor学习笔记

我是大标题 我学习Blazor的顺序是基于Blazor University&#xff0c;然后实际内容不完全基于它&#xff0c;因为它的例子还是基于.NET Core 3.1做的&#xff0c;距离现在很遥远了。 截至本文撰写的时间&#xff0c;2025年&#xff0c;最新的.NET是.NET9了都&#xff0c;可能1…

C++11中的bind

官方文档对于bind接口的概述解释&#xff1a;Bind function arguments 在C11中&#xff0c;std::bind 是一个非常有用的工具&#xff0c;用于将函数、成员函数或函数对象与特定的参数绑定在一起&#xff0c;生成一个新的可调用对象。std::bind 可以用于部分应用函数参数、改变…

鼠标拖尾特效

文章目录 鼠标拖尾特效一、引言二、实现原理1、监听鼠标移动事件2、生成拖尾元素3、控制元素生命周期 三、代码实现四、使用示例五、总结 鼠标拖尾特效 一、引言 鼠标拖尾特效是一种非常酷炫的前端交互效果&#xff0c;能够为网页增添独特的视觉体验。它通常通过JavaScript和C…

金山打字游戏2010绿色版,Win7-11可用DxWnd完美运行

金山打字游戏2010绿色版&#xff0c;Win7-11可用DxWnd完美运行 链接&#xff1a;https://pan.xunlei.com/s/VOIAYCzmkbDfdASGJa_uLjquA1?pwd67vw# 进入游戏后&#xff0c;如果输入不了英文字母&#xff08;很可能是中文输入状态&#xff09;&#xff0c;就按一下“Shift”键…

爬虫学习笔记之Robots协议相关整理

定义 Robots协议也称作爬虫协议、机器人协议&#xff0c;全名为网络爬虫排除标准&#xff0c;用来告诉爬虫和搜索引擎哪些页面可以爬取、哪些不可以。它通常是一个叫做robots.txt的文本文件&#xff0c;一般放在网站的根目录下。 robots.txt文件的样例 对有所爬虫均生效&#…

(10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例

&#xff08;1&#xff09; 先介绍下后面的代码里要用到的基础函数&#xff1a; 以及&#xff1a; &#xff08;2&#xff09; 接着给出现代版的 读写 socket 参数的系统函数 &#xff1a; 以及&#xff1a; &#xff08;3&#xff09; 给出 一言的 范例代码&#xff0c;获取…

【玩转 Postman 接口测试与开发2_016】第13章:在 Postman 中实现契约测试(Contract Testing)与 API 接口验证(上)

《API Testing and Development with Postman》最新第二版封面 文章目录 第十三章 契约测试与 API 接口验证1 契约测试的概念2 契约测试的工作原理3 契约测试的分类4 DeepSeek 给出的契约测试相关背景5 契约测试在 Postman 中的创建方法6 API 实例的基本用法7 API 实例的类型实…

day8-面向对象

目录 面向对象1、面向对象介绍2、类和对象类和对象类的几个补充注意事项 3、封装 面向对象 1、面向对象介绍 ⭐️面向对象介绍&#xff1a; 面向&#xff1a;拿、找对象&#xff1a;能干活的东西面向对象编程&#xff1a;拿东西过来做对应的事情 面向对象编程的例子&#x…

【Java】位图 布隆过滤器

位图 初识位图 位图, 实际上就是将二进制位作为哈希表的一个个哈希桶的数据结构, 由于二进制位只能表示 0 和 1, 因此通常用于表示数据是否存在. 如下图所示, 这个位图就用于标识 0 ~ 14 中有什么数字存在 可以看到, 我们这里相当于是把下标作为了 key-value 的一员. 但是这…

【自然语言处理(NLP)】生成词向量:GloVe(Global Vectors for Word Representation)原理及应用

文章目录 介绍GloVe 介绍核心思想共现矩阵1. 共现矩阵的定义2. 共现概率矩阵的定义3. 共现概率矩阵的意义4. 共现概率矩阵的构建步骤5. 共现概率矩阵的应用6. 示例7. 优缺点优点缺点 **总结** 目标函数训练过程使用预训练的GloVe词向量 优点应用总结 个人主页&#xff1a;道友老…

如何获取sql数据中时间的月份、年份(类型为date)

可用自带的函数month来实现 如&#xff1a; 创建表及插入数据&#xff1a; create table test (id int,begindate datetime) insert into test values (1,2015-01-01) insert into test values (2,2015-02-01) 执行sql语句,获取月份&#xff1a; select MONTH(begindate)…

NSSCTF Pwn [NISACTF 2022]ezpie 题解

下载 exeinfo checksec 32位 NX PIE开启 查看main函数&#xff1a; 后门函数&#xff1a; 发现已经给出了main函数地址 因为开启了PIE 所以IDA中 后门函数减去main函数的后三位就是偏移 给出的函数加上这个数就是后门函数地址 exp&#xff1a; from pwn import *p remote(…

【01】共识机制

BTF共识 拜占庭将军问题 拜占庭将军问题是一个共识问题 起源 Leslie Lamport在论文《The Byzantine Generals Problem》提出拜占庭将军问题。 核心描述 军中可能有叛徒&#xff0c;却要保证进攻一致&#xff0c;由此引申到计算领域&#xff0c;发展成了一种容错理论。随着…

文字加持:让 OpenCV 轻松在图像中插上文字

前言 在很多图像处理任务中,我们不仅需要提取图像信息,还希望在图像上加上一些文字,或是标注,或是动态展示。正如在一幅画上添加一个标语,或者在一个视频上加上动态字幕,cv2.putText 就是这个“文字魔术师”,它能让我们的图像从“沉默寡言”变得生动有趣。 今天,我们…

【R语言】环境空间

一、环境空间的特点 环境空间是一种特殊类型的变量&#xff0c;它可以像其它变量一样被分配和操作&#xff0c;还可以以参数的形式传递给函数。 R语言中环境空间具有如下3个特点&#xff1a; 1、对象名称唯一性 此特点指的是在不同的环境空间中可以有同名的变量出现&#x…

惰性函数【Ⅱ】《事件绑定的自我修养:从青铜到王者的进化之路》

【Ⅱ】《事件绑定的自我修养&#xff1a;从青铜到王者的进化之路》 1. 代码功能大白话&#xff08;给室友讲明白版&#xff09; // 青铜写法&#xff1a;每次都要问浏览器"你行不行&#xff1f;" function addEvent青铜版(element, type, handler) {if (window.add…

Vue3的el-table-column下拉输入实时查询API数据选择的实现方法

由于本人对el-table-column有下拉输入选择的要求&#xff0c;根据网上搜索的资料及本人优化&#xff0c;推出我比较满意的方法&#xff0c;供各位读者参考使用。 效果图 el-table-column写法 <el-table-columnlabel"货品编号"align"center"prop"…