Kmeans算法来实现RFM指标计算步骤
K-Means(K均值)是一种经典的无监督聚类算法,主要用于将数据集划分为 KKK 个不同的簇(Cluster)。
它基于最小化簇内样本的平方误差,即最小化数据点与簇中心的距离之和。
1. K-Means 算法原理
(1) 主要步骤
-
初始化
选择 KKK 个初始聚类中心(可以随机选取,也可以使用K-Means++优化选择)。 -
分配样本到最近的簇中心
对于数据集中的每个点,将其分配给最近的聚类中心(使用欧几里得距离、曼哈顿距离等度量)。 -
更新簇中心
计算每个簇中所有点的均值,并将该均值作为新的簇中心。 -
重复步骤 2 和 3
直到聚类中心不再发生变化或者达到最大迭代次数。
(2) 目标函数
K-Means 试图最小化以下目标函数(平方误差):


2. K-Means 的特点
(1) 优势
- 计算速度快,时间复杂度接近 O(nKT)O(nKT)O(nKT)(其中 TTT 是迭代次数)。
- 适用于大规模数据集,易于并行化(可以结合MapReduce/Spark实现)。
- 聚类结果可解释性强,适用于数据分析、推荐系统、用户分群等场景。
(2) 局限性
- K值需要人为指定,如果选择不当,可能会导致聚类效果不佳。
- 对初始簇中心敏感,可能陷入局部最优(K-Means++ 可以优化初始中心选择)。
- 对异常点敏感,受离群点影响较大。
- 适用于凸形簇,对非球形分布的数据表现较差。
3. K-Means Demo示例
# 使用机器学习算法实现rfm
from utils.base import BasicTag
from pyspark.sql import SparkSession,functions as F
from pyspark.sql.types import *
from pyspark.ml.feature import VectorAssembler
from pyspark.ml.clustering import KMeansclass RFMTag(BasicTag):def make_tag(self):rule_dict = self.data_dict# 1-计算获取RFM的值# 消费周期计算recency = F.datediff(F.current_date(), F.from_unixtime(F.max('finishtime'))).alias('diff_dt').alias('recency')# 订单量frequency = F.countDistinct('ordersn').alias('frequency')# 订单金额monetary = F.sum('orderamount').alias('monetary')df_rfm = self.df_es.groupby('memberid').agg(recency,frequency,monetary)# 2-根据计算的结果,将数据划分5个等级进行打分
# -----------------------------R数据打分----------------------------------------------------------------------------------# 2-1 特征工程将数据转为向量(只有转化为坐标向量(VectorAssembler),才能在坐标轴上计算方向及两点之间的距离,再求平均距离)v_r = VectorAssembler(inputCols=['recency'],outputCol='v_r')# 添加转化数据,将上面订单金额加入进行转化df_r_fit = v_r.transform(df_rfm)# 2-2 使用kmeas算法打分,# k参数负责划分几个分数段;# featuresCol负责传入向量值用于聚类的特征向量的列名;# predictionCol 用于存储聚类结果的列名。KMeans算法会在训练数据上进行聚类分析,并将每个数据点分配到一个聚类。这个聚类结果会存储在predictionCol指定的列中。#+---+-----------------+ +---+-----------------------+#|id | v_r | |id | v_r | k_r |#+---+-----------------+ +---+-----------------+-----+#| 1 | [1.0, 2.0, 3.0] | 执行KMeans聚类后: | 1 | [1.0, 2.0, 3.0] | 0 |#| 2 | [4.0, 5.0, 6.0] | ————————————————————————> | 2 | [4.0, 5.0, 6.0] | 1 |#| 3 | [7.0, 8.0, 9.0] | | 3 | [7.0, 8.0, 9.0] | 2 |#| 4 |[10.0, 11.0, 12.0]| | 4 |[10.0, 11.0, 12.0]| 3 |kmeans = KMeans(k=5,featuresCol='v_r',predictionCol='k_r')# 添加数据----kmeans.fit( )用于训练模型,训练完生成一个KMeans模型对象k_r_fitk_r_fit = kmeans.fit(df_r_fit)# 转化数据,对训练结果进行预测,将预测结果添加到数据框:df_k_r中df_k_r = k_r_fit.transform(df_r_fit)df_k_r.show()# 计算k值中心判断k值的大小--clusterCenters()获取每个聚类的中心点(质心),存储在cluster变量中。cluster = k_r_fit.clusterCenters()# 遍历k值(质点)中心,计算k值的和# cluster_dict = {}:创建一个空字典,用于存储每个聚类中心点的索引和其特征值的和。# enumerate:是 Python 的一个内置函数,用于在遍历可迭代对象(如列表、元组、字符串等)时,同时获取每个元素的索引和值。它返回一个枚举对象,该对象生成一系列包含索引和值的元组。# 示例列表:#------------------------------------------+# fruits = ['apple', 'banana', 'cherry'] |# #使用 enumerate 进行遍历 |# for index, value in enumerate(fruits): |# rint(index, value) |#--------输出------------------------------+# 0 apple |# 1 banana |# 2 cherry |#-----------------------------------------+cluster_dict = {}for i, v in enumerate(cluster):cluster_dict[i] = sum(v)print(cluster_dict) # 输出结果:{0: 1445.0, 1: 1446.0, 2: 1447.0}# 中心点排序,对cluster_dict按特征值的和进行升序排序--并强制类型转化为字典格式cluster_dict_sort = dict(sorted(cluster_dict.items(), key=lambda x: x[1]))# 对k中心替换大小# count = 0:初始化计数器。# for k, v in cluster_dict_sort.items():遍历排序后的字典cluster_dict_sort。# cluster_dict_sort[k] = count:将排序后的中心点索引替换为从0开始的递增值。# count += 1:计数器递增。然后使用cluster_dict_sort[k] 进行值的替换count = 0for k, v in cluster_dict_sort.items():cluster_dict_sort[k] = countcount += 1# 替换原来df中的k值编号@F.udf(returnType=IntegerType())def repace_data(k):return cluster_dict_sort.get(k)df_replace_r = df_k_r.select(df_k_r.memberid,df_k_r.recency,df_k_r.frequency,df_k_r.monetary,repace_data('k_r').alias('k_r'))#-----------------------------F数据打分----------------------------------------------------------------------------------# 2-1 特征工程将数据转为向量f_r = VectorAssembler(inputCols=['frequency'], outputCol='f_r')# 添加转化数据df_f_fit = f_r.transform(df_replace_r)# 2-2 kmeas算法打分kmeans = KMeans(k=5, featuresCol='f_r', predictionCol='k_f')# 添加数据f_r_fit = kmeans.fit(df_f_fit)# 转化数据df_f_r = f_r_fit.transform(df_f_fit)# 计算k值中心判断k值的大小cluster = f_r_fit.clusterCenters()# 遍历k值中心,计算k值的和cluster_dict = {}for i, v in enumerate(cluster):cluster_dict[i] = sum(v)# 中心点排序cluster_dict_sort = dict(sorted(cluster_dict.items(), key=lambda x: x[1]))# 对k中心替换大小count = 0for k, v in cluster_dict_sort.items():cluster_dict_sort[k] = countcount += 1# 替换原来df中的k值编号@F.udf(returnType=IntegerType())def repace_data1(k):return cluster_dict_sort.get(k)df_replace_f = df_f_r.select(df_f_r.memberid, df_f_r.recency, df_f_r.frequency, df_f_r.monetary,df_f_r.k_r,repace_data1('k_f').alias('k_f'))print('F计算完成')
# -----------------------------M数据打分----------------------------------------------------------------------------------# 2-1 特征工程将数据转为向量# TODO:inputColss输入字段修改 outputCol字段修改m_r = VectorAssembler(inputCols=['monetary'], outputCol='m_r')# 添加转化数据# TODO: 替换上一步的df数据df_f_fit = m_r.transform(df_replace_f)# 2-2 kmeas算法打分# TODO:inputColss输入字段修改 outputCol字段修改kmeans = KMeans(k=5, featuresCol='m_r', predictionCol='k_m')# 添加数据f_r_fit = kmeans.fit(df_f_fit)# 转化数据df_f_r = f_r_fit.transform(df_f_fit)df_f_r.show()print('M评分')# 计算k值中心判断k值的大小cluster = f_r_fit.clusterCenters()# 遍历k值中心,计算k值的和cluster_dict = {}for i, v in enumerate(cluster):cluster_dict[i] = sum(v)# 中心点排序cluster_dict_sort = dict(sorted(cluster_dict.items(), key=lambda x: x[1]))# 对k中心替换大小count = 0for k, v in cluster_dict_sort.items():cluster_dict_sort[k] = countcount += 1# 替换原来df中的k值编号@F.udf(returnType=IntegerType())def repace_data2(k):return cluster_dict_sort.get(k)# TODO:df_replace_m = df_f_r.select(df_f_r.memberid, df_f_r.recency, df_f_r.frequency, df_f_r.monetary, df_f_r.k_r, df_f_r.k_f,repace_data2('k_m').alias('k_m'))df_replace_m.show()print('M计算完成')# 3-计算所有用户的平均打分--结果为一行数据df_rfm_score_avg = df_replace_m.select(F.avg('k_r').alias('r_avg'),F.avg('k_f').alias('f_avg'),F.avg('k_m').alias('m_avg'))# +-----+-----+-----+# |r_avg|f_avg|m_avg|# +-----+-----+-----+# | 2.0 | 3.0 | 4.0 |# +-----+-----+-----+# 将平均分转为row对象可以获第一行取值(实际上只有一行)# row 是一个 Row 对象,它允许你像访问属性一样访问列值。row = df_rfm_score_avg.first()# 4-通过平均值判断rfm的高低df_rfm_num = df_replace_m.select(df_replace_m.memberid,# 大于为1 否则为0F.when(df_replace_m.k_r >= row['r_avg'],1).otherwise(0).alias('rn'),F.when(df_replace_m.k_f > row['f_avg'],1).otherwise(0).alias('fn'),F.when(df_replace_m.k_m > row['m_avg'],1).otherwise(0).alias('mn'),)# 5-标签匹配# df_rfm_num 是一个包含 RFM 分数的 DataFrame。# select 方法选择了 memberid 列和通过 concat 方法连接的 RFM 分数(即 rn、fn、mn)。# F.concat 将 R、F、M 分数转化为字符串并连接成一个新的字符串。# 最后,alias('rfm') 为这个新字符串列命名为 rfmdf_rfm_str = (df_rfm_num.select(df_rfm_num.memberid,F.concat # F.concat 将 R、F、M 分数转化为字符串并连接成一个新的字符串。并且alias('rfm') 为这个新字符串列命名为 rfm(df_rfm_num.rn.cast('string'),df_rfm_num.fn.cast('string'),df_rfm_num.mn.cast('string')).alias('rfm')))df_rfm_str.show()# 这是一个用户定义函数(UDF),用于根据 rule_dict 字典将 RFM 字符串映射到相应的标签。# @F.udf(returnType=StringType()) 装饰器用于将 Python 函数转化为 PySpark UDF,并指定返回类型为 StringType()。@F.udf(returnType=StringType())def match(data):return rule_dict.get(data)# 应用 match 函数并创建新 DataFrame:self.df_new_tag = (df_rfm_str.select(df_rfm_str.memberid.alias('userID'),match(df_rfm_str.rfm).alias('tagsID')))# 创建对象
ss = SparkSession.builder.config('spark.sql.shuffle.partitions','6').getOrCreate()
rfm = RFMTag()
# 执行
rfm.action('RFM', ss, 'tfec_tags', 'tbl_basic_tag')
相关文章:
Kmeans算法来实现RFM指标计算步骤
K-Means(K均值)是一种经典的无监督聚类算法,主要用于将数据集划分为 KKK 个不同的簇(Cluster)。 它基于最小化簇内样本的平方误差,即最小化数据点与簇中心的距离之和。 1. K-Means 算法原理 (1) 主要步骤 …...
LeetCode 1745.分割回文串 IV:动态规划(用III或II能直接秒)
【LetMeFly】1745.分割回文串 IV:动态规划(用III或II能直接秒) 力扣题目链接:https://leetcode.cn/problems/palindrome-partitioning-iv/ 给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,…...
Vue2-3 优雅的在子组件修改父组件传递过来的v-model
在子组件修改父组件传递过来的v-model,这样会破坏单向数据流,造成屎山代码,为了避免这个问题,需要给一个中间层来相对舒服的使用v-model。方法就是用computed去拦截v-model,然后在computed 里面去触发 emit 事件来修改父组件传来的…...
threejs:用着色器给模型添加光带扫描效果
第一步:给模型添加光带 首先创建一个立方体,不进行任何缩放平移操作,也不要set position。 基础代码如下: 在顶点着色器代码里varying vec3 vPosition;vPosition position;获得threejs自动计算的顶点坐标插值(也就…...
1.从0搭建前端Vue项目工程
我们通过vue官方提供的脚手架Vue-cli来快速生成一个Vue的项目模板。 **注意:**需要先安装NodeJS,然后才能安装Vue-cli。 环境准备好了,接下来我们需要通过Vue-cli创建一个vue项目,然后再学习一下vue项目的目录结构。Vue-cli提供了…...
开放鸿蒙OpenHarmony 5.0.0 Release 兼容性测试实战经验分享
OpenHarmony 5.0版本的发布时间是2024年12月20日至21日。这个版本带来了许多新特性和改进。现在5.0出了两个release 版本,分别是5.0.0和5.0.1。 就在5.0版本发布不到2周的时间内,2025年01月01日起,不支持新产品基于老分支(OpenHar…...
Chromium_src源码
Chromium_src源码 码云上有一个OpenHarmony-TPC/chromium_src项目,目前已经停止维护了,迁移到GitCode上了,源代码项目地址为:openharmony-tpc/chromium_chrome 特此记录一下老的项目的相关软件架构 Chromium 简介 软件架构 软…...
深度学习的正则化深入探讨
文章目录 一、说明二、学习目标三、什么是机器学习中的正则化四、了解过拟合和欠拟合五、代价函数的意义六、什么是偏差和方差?七、机器学习中的正则化? 一、说明 在训练机器学习模型时,模型很容易过拟合或欠拟合。为了避免这种情况…...
《OpenCV》——dlib(人脸应用实例)
文章目录 dlib库dlib库——人脸应用实例——表情识别dlib库——人脸应用实例——疲劳检测 dlib库 dlib库的基础用法介绍可以参考这篇文章:https://blog.csdn.net/lou0720/article/details/145968062?spm1011.2415.3001.5331,故此这篇文章只介绍dlib的人…...
tauri2+typescript+vue+vite+leaflet等的简单联合使用(一)
项目目标 主要的目的是学习tauri。 流程 1、搭建项目 2、简单的在项目使用leaflet 3、打包 准备项目 环境准备 废话不多说,直接开始 需要有准备能运行Rust的环境和Node,对于Rust可以参考下面这位大佬的文章,Node不必细说。 Rust 和…...
本地部署阿里万象2.1文生视频模型(Wan2.1-T2V)完全指南
在生成式AI技术爆发式发展的今天,阿里云开源的万象2.1(Wan2.1)视频生成模型,为创作者提供了从文字/图像到高清视频的一站式解决方案。本文针对消费级显卡用户,以RTX 4060 Ti 16G为例,详解本地部署全流程与性能调优方案,涵盖环境配置、多模型选择策略、显存优化技巧及实战…...
# [Linux] [Anaconda]解决在 WSL Ubuntu 中安装 Anaconda 报错问题
在 Windows 10 中安装了 WSL(Windows Subsystem for Linux)并使用 Ubuntu 后,你可能会下载 Anaconda 的 Linux 版本进行安装。但在安装过程中,可能会遇到 tar (child): bzip2: Cannot exec: No such file or directory 这样的错误…...
ES怎么查询大于10000条数据
在Elasticsearch(ES)中,默认情况下,查询结果的最大返回条数是10,000条。如果你需要查询超过10,000条数据,可以通过以下几种方式来实现: 1. 使用 scroll API scroll API 适用于需要处理大量数据的场景&…...
【Vue CLI脚手架开发】——3.组件交互props配置
文章目录 前言一、props数据接收方式二、代码实现1. 父组件2.子组件 三、分析 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习…...
FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解
FPGA之Usb数据传输 Usb 通信 你也许会有疑问,明明有这么多通信方式和数据传输(SPI、I2C、UART、以太网)为什么偏偏使用USB呢? 原因有很多,如下: 1. 高速数据传输能力 高带宽:USB接口提供了较高的数据传…...
【Office-Word】如何自动生成中英文目录
1.目录介绍 Word这个自动生成目录非常强大,涉及的功能很琐碎,想要完美的生成目录不仅仅是只会目录这么简单,前后涉及到的大纲级别、目标样式和域代码等操作是比较头疼的。 下面就一步一步开始介绍 2.多级标题级别编号设置 目录想要设置好…...
Oracle删除重复数据保留其中一条
Oracle删除重复数据保留其中一条 在Oracle数据库中,要删除重复数据并保留其中一条记录,可以使用多种方法。这里介绍两种常见的方法:使用ROWID或使用ROW_NUMBER()窗口函数。 方法1:使用ROWID ROWID是Oracle中用来唯一标识表中每…...
CentOS 7 安装Nginx-1.26.3
无论安装啥工具、首先认准了就是官网。Nginx Nginx官网下载安装包 Windows下载: http://nginx.org/download/nginx-1.26.3.zipLinxu下载 wget http://nginx.org/download/nginx-1.26.3.tar.gzLinux安装Nginx-1.26.3 安装之前先安装Nginx依赖包、自行选择 yum -y i…...
家政预约小程序用例图分析
在和客户进行需求沟通的时候,除了使用常规的问答的形式,我还使用图形化工具更深入的沟通。比如借助UML的用例图来开展系统分析,并且按照角色详细拆解了家政预约小程序的各个用例。在分析阶段思考的越多,沟通的越多,在系…...
112页精品PPT | DeepSeek行业应用实践报告
这份文件是一份关于DeepSeek行业应用实践的报告,以PPT形式呈现,共112页,详细介绍了DeepSeek及其核心产品DeepSeek-R1的技术特点、市场表现、应用路径以及在多领域的实践案例。报告展示了DeepSeek在市场上的快速崛起,包括其日活用户…...
计算机毕业设计SpringBoot+Vue.js航空机票预定系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
C语言学习笔记-初阶(27)操作符详解1:位操作
1. 操作符的分类 上述的操作符,我们已经学过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作符,今天继续介绍⼀部分,操作符中有一些操作符和二进制有关系,我们先铺垫一下二进制的和进制转换的知识。 2. 二进制、…...
网络安全需要学多久才能入门?
网络安全是一个复杂且不断发展的领域,想要入行该领域,我们需要付出足够多的时间和精力好好学习相关知识,才可以获得一份不错的工作,那么网络安全需要学多久才能入门?我们通过这篇文章来了解一下。 学习网络安全的入门时间因个人的…...
20250304学习记录
第一部分,先来了解一下各种论文期刊吧,毕竟也是这把岁数了,还什么都不懂呢 国际期刊: EI收集的主要有两种, JA:EI源刊 CA:EI会议 CPCI也叫 ISTP 常说的SCI分区是指,JCR的一区、…...
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架 1. 引言 本教程旨在帮助嵌入式开发小白从零开始,学习如何在STM32F407微控制器上实现一个基于串口的数据接收程序。该程序能够通过判断数据头来接收一串数据,并将其存储到缓冲区中…...
文件上传复现
文件上传漏洞的概念 在现代互联网的web应用程序中,上传文件是一种常见的功能,因为它有助于提高业务效率,比如社交 网站中,允许用户上传图片、视频、头像和许多其他类型的文件。然而向用户提供的功能越多, web应 用受到…...
Redis——缓存穿透、击穿、雪崩
缓存穿透 什么是缓存穿透 缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库…...
HMC7043和HMC7044芯片配置使用
一,HMC7043芯片 MC7043独特的特性是对14个通道分别进行独立灵活的相位管理。所有14个通道均支持频率和相位调整。这些输出还可针对50 Ω或100 Ω内部和外部端接选项进行编程。HMC7043器件具有RF SYNC功能,支持确定性同步多个HMC7043器件,即确保所有时钟输出从同一时钟沿开始…...
STM32程序的加密与破解以及烧录方法
STM32程序的加密与破解,以及烧录方法。 盗取他人的PCB和烧录文件,可以节省大大开发成本,何乐而不为呢。因此,就滋生了一些协助他人盗版的公司。为了防止被盗版和复制,单片机工程师也是煞费苦心,对硬件和软…...
Redis和MySQL的实时数据同步方案
针对 Redis 和 MySQL 的实时数据同步,需根据业务场景选择不同的技术方案,核心目标是保障数据一致性、降低延迟、提升系统可靠性。以下是几种典型方案及其适用场景: 方案一:基于 MySQL Binlog 的异步同步 原理 监听 MySQL 的 Bin…...
