首页/新闻资讯/正文详情

航空公司客户价值分析Python实战:LRFMC模型与KMeans聚类

发布时间:2026/9/26 14:07:14 来源:云帆数科 栏目:资讯中心
航空公司客户价值分析Python实战:LRFMC模型与KMeans聚类
简介这份资源是面向数据分析与机器学习入门者的航空公司客户价值分析实战源码包围绕客户分群、客户生命周期价值预测等典型业务问题展开适合希望把Python数据分析技能落到真实场景的学习者。压缩包共10个文件以xls与csv数据表为主另含1个py脚本和1份模块说明txt整体约17.13MB数据文件可用于清洗、标准化与聚类建模脚本则串联起完整分析流程。目前已有891人学习下载说明该案例在同类练习中具有一定参考热度。读者可从中获得一套可运行的客户价值分析实现思路涵盖数据预处理、特征工程、K-means客户分群以及价值评估等环节并借助说明文档理解各模块职责与调用关系便于在此基础上替换数据、调整参数或迁移到其他行业的客户分析任务中。1. 从一份「航空公司客户价值分析Python源码.rar」说起它到底能算出什么拿到这个压缩包标题的人十有八九是两种情况一是做数据分析课程设计导师丢过来一句「做个客户价值分析」二是真在航司或OTA做会员运营想用Python把手里那堆飞行记录、里程、票价数据盘活。不管哪种核心诉求都一样——把「谁值得重点维护、谁快流失了、谁该被唤醒」这件事用数据算出来而不是拍脑袋。航空公司的客户价值分析和电商、零售有本质区别它的消费行为高度稀疏一年飞几次很正常、金额跨度极大经济舱和头等舱差几十倍、而且强绑定时间窗淡旺季、常旅客等级周期。所以直接套用RFM最近一次消费、消费频率、消费金额往往翻车行业里更常用的是LRFMC模型——L是入会时长LengthR是最近一次乘机间隔RecencyF是飞行次数FrequencyM是飞行总里程MileageC是平均折扣系数Coefficient。这套模型是航司客户价值分析里最稳的骨架源码包大概率也是围绕它展开。这篇文章不假装我拆过你手里那个rar而是把「航空公司客户价值分析」这件事从数据长什么样、指标怎么算、聚类怎么调、结果怎么读一路讲到落地时最容易踩的坑。适合会一点pandas、但没做过完整客户价值项目的人照着复现也适合做过RFM但被航司数据折磨过的老手对照参数边界。2. LRFMC五个指标怎么从原始乘机流水里算出来2.1 先搞清楚航司数据的三个典型表结构真实航司或公开竞赛数据集比如常见的航空客户价值数据集通常长这样一张客户信息表会员卡号、入会时间、性别、年龄、工作地一张乘机记录表会员卡号、航班号、乘机日期、舱位、飞行里程、折扣率有时还有一张积分兑换表。三张表靠会员卡号关联。关键点在于乘机记录表是一行一次飞行同一个人有多行。所以LRFMC不是直接读出来的而是分组聚合出来的。很多人第一步就错在把乘机记录当成客户表直接跑聚类结果每个样本是一次飞行而不是一个人聚类出来的「客户群」毫无意义。先做一次合并和去重检查这是所有后续计算的地基import pandas as pd import numpy as np # 读取三张表注意编码航司老系统导出常见gbk customer pd.read_csv(customer_info.csv, encodinggbk) flight pd.read_csv(flight_record.csv, encodinggbk) # 检查会员卡号是否有重复、空值 print(customer[会员卡号].duplicated().sum()) print(flight[会员卡号].isnull().sum()) # 统一卡号为字符串避免前导零丢失 customer[会员卡号] customer[会员卡号].astype(str) flight[会员卡号] flight[会员卡号].astype(str) # 乘机日期转datetime后续算R要用 flight[乘机日期] pd.to_datetime(flight[乘机日期])逻辑说明卡号转字符串这一步看着废话但航司系统导出的卡号经常带前导零pandas默认读成int会把00123变成123合并时直接对不上这是血泪经验。日期转datetime是为了后面做时间差字符串相减会报错。参数说明encodinggbk是航司老系统的常见编码如果报UnicodeDecodeError就换utf-8或gb18030。duplicated().sum()返回重复行数正常应该是0不是0说明客户表有脏数据得先去重。2.2 L、R、F、M、C五个指标的精确口径这五个指标每一个都有「看起来对但实际错」的写法逐个说清楚。L入会时长用观测窗口的结束日期减去入会日期单位是天或月。注意观测窗口要统一比如统一取数据里最大乘机日期作为窗口终点而不是用today()否则每次跑结果都不一样。R最近一次乘机间隔窗口终点减去该客户最后一次乘机日期。R越小说明越近价值越高。F飞行次数直接对乘机记录按卡号计数。但要注意往返算两次还是两次航班一般按航班段算一次中转算两段。M飞行总里程按卡号对里程求和。这里有个坑——里程字段如果有缺失直接sum()会跳过但如果你想要「缺失当0」得先fillna(0)。C平均折扣系数这是航司特有的等于平均折扣率。折扣率越低比如0.3说明买的是低价票价值相对低接近1说明全价或头等舱。计算时用mean()而不是sum()。# 确定观测窗口终点 snapshot_date flight[乘机日期].max() pd.Timedelta(days1) # 按卡号聚合出R、F、M、C agg flight.groupby(会员卡号).agg( R(乘机日期, lambda x: (snapshot_date - x.max()).days), F(航班号, count), M(飞行里程, sum), C(折扣率, mean) ).reset_index() # 合并客户信息算L df pd.merge(customer, agg, on会员卡号, howleft) df[入会时间] pd.to_datetime(df[入会时间]) df[L] (snapshot_date - df[入会时间]).dt.days # 没飞过的客户R/F/M/C补0或特定值 df[[R,F,M,C]] df[[R,F,M,C]].fillna(0)逻辑说明snapshot_date加一天是为了让「当天乘机」的R至少为1而不是0避免出现0值干扰后续log变换。howleft保证所有会员都保留没乘机记录的会员R/F/M/C为NaN这里填0——但要注意R填0其实语义是「最近」对没飞过的人是错的更严谨的做法是给一个很大的R值或单独标记。这是源码包里经常处理得含糊的地方。参数说明lambda x: (snapshot_date - x.max()).days里x.max()是该客户最后一次乘机日期。F用航班号计数如果航班号有缺失改用乘机日期计数更稳。2.3 标准化和log变换为什么不能直接丢进KMeansLRFMC五个指标量纲完全不同L可能几千天M可能几十万里程C在0到1之间。KMeans是基于欧氏距离的量纲大的指标会主导距离计算结果就是聚类几乎只按M分。所以必须标准化。但航司数据还有个特点M和F是右偏的少数人飞得极多直接标准化后极端值仍然拉偏均值。常见做法是先做log变换再标准化from sklearn.preprocessing import StandardScaler # 对右偏的L、F、M做log1p避免log(0) df[L_log] np.log1p(df[L]) df[F_log] np.log1p(df[F]) df[M_log] np.log1p(df[M]) features df[[L_log,R,F_log,M_log,C]].copy() scaler StandardScaler() features_scaled scaler.fit_transform(features)逻辑说明log1p等于log(1x)专门处理含0的数据。R和C本身分布相对均匀可以不做log。标准化用StandardScaler把每个指标变成均值0方差1这样五个指标在距离计算里权重才公平。参数说明如果你想让某个指标权重更高比如业务上更看重M可以在标准化后乘以权重系数但一般先跑默认权重看结果再调。3. KMeans聚类跑通之后怎么把簇翻译成业务语言3.1 聚类数K到底选几个肘部法和轮廓系数一起看KMeans最大的玄学就是K选几。教科书说肘部法但航司数据经常肘部不明显。实操里我一般同时看肘部法inertia和轮廓系数silhouette再结合业务能不能解释。from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt inertias, silhouettes [], [] K_range range(2, 9) for k in K_range: km KMeans(n_clustersk, random_state42, n_init10) labels km.fit_predict(features_scaled) inertias.append(km.inertia_) silhouettes.append(silhouette_score(features_scaled, labels)) for k, i, s in zip(K_range, inertias, silhouettes): print(fK{k}, inertia{i:.1f}, silhouette{s:.3f})逻辑说明inertia_是簇内平方和越小越紧凑但K越大必然越小所以看下降速度拐点。silhouette_score越接近1越好但计算量大K范围别开太大。random_state42固定随机种子保证每次结果一致否则你调完参数发现结果变了会怀疑人生。n_init10是跑10次不同初始化取最优新版sklearn默认就是10显式写出来更清楚。参数说明航司客户价值分析里K一般取4到6。K5经常对应「高价值常旅客、潜力客户、一般客户、流失客户、低价值客户」这种经典分群业务上好解释。如果轮廓系数在K5和K6差不多选5因为少一个群运营成本低。3.2 给每个簇打业务标签看质心而不是看名字聚类出来的0、1、2、3只是编号没有含义。要把它翻译成业务语言得看每个簇在原始指标上的均值质心反标准化后。# 把聚类标签贴回原数据 df[cluster] labels # 按簇看原始LRFMC均值 profile df.groupby(cluster)[[L,R,F,M,C]].mean().round(2) print(profile) # 看每个簇的人数 print(df[cluster].value_counts())逻辑说明groupby(cluster).mean()得到每个簇的画像。比如某个簇R很小、F和M很大、C接近1那就是高价值客户某个簇R很大、F很小那就是流失客户。人数分布也要看如果一个簇只有几个人可能是异常值考虑合并或剔除。参数说明round(2)只是显示好看不影响计算。如果某簇人数占比低于5%业务上通常不值得单独做运营策略可以并到最近的簇。3.3 用雷达图把五个簇的差异讲给业务方听技术人自己看表格能懂但给运营、市场同事汇报一张雷达图胜过十行数字。把质心标准化后画雷达图每个簇一条线五个轴是LRFMC。import matplotlib.pyplot as plt # 中文显示 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False # 质心标准化 centers df.groupby(cluster)[[L,R,F,M,C]].mean() centers_norm (centers - centers.min()) / (centers.max() - centers.min()) angles np.linspace(0, 2*np.pi, 5, endpointFalse).tolist() angles angles[:1] # 闭合 fig, ax plt.subplots(figsize(6,6), subplot_kwdict(polarTrue)) for idx, row in centers_norm.iterrows(): values row.tolist() [row.tolist()[0]] ax.plot(angles, values, labelf簇{idx}) ax.fill(angles, values, alpha0.1) ax.set_xticks(angles[:-1]) ax.set_xticklabels([L,R,F,M,C]) ax.legend(locupper right) plt.show()逻辑说明雷达图前先把质心做min-max归一化到0-1否则量纲差异会让图完全没法看。angles首尾闭合是matplotlib雷达图的标准写法。fill加透明度让重叠区域可读。参数说明SimHei是黑体Linux服务器上可能没有换成WenQuanYi Micro Hei或提前装字体。如果报字体警告但图能出可以忽略。4. 避坑与排查航司客户价值分析里最容易翻车的五件事4.1 现象聚类结果每次跑都不一样原因KMeans初始化是随机的没固定random_state或者n_init太小导致陷入局部最优。解决KMeans(n_clustersk, random_state42, n_init10)两个参数都写上。如果换了sklearn版本结果还是飘检查是不是标准化那步用了会变的统计量。4.2 现象某个簇人数特别少只有个位数原因数据里有极端值比如某个客户里程几百万KMeans为了它单独分一个簇。解决先看这个簇的原始指标如果是异常值比如测试账号、内部账号直接剔除如果是真实高价值客户考虑用RobustScaler替代StandardScaler或者对M做分位数截断比如99%分位封顶。4.3 现象R指标算出来是负数原因snapshot_date用了today()而数据里有未来日期的乘机记录数据录入错误或测试数据。解决snapshot_date统一用flight[乘机日期].max()并且检查有没有大于当前日期的记录有就过滤掉。4.4 现象合并后客户数变多了原因乘机记录表里有客户信息表里没有的卡号howleft不会增加行但如果用了howouter就会。或者客户表本身有重复卡号。解决合并前对客户表按卡号去重合并后用df.shape[0]和客户表行数对比不一致就查merge的indicator参数。4.5 现象C指标出现大于1的值原因折扣率字段口径不统一有的系统存的是百分比如85有的是小数0.85。解决先df[折扣率].describe()看分布如果最大值是100左右统一除以100。这个坑在跨系统取数时特别常见。5. 把分析结果变成可复用的打分卡一个不用聚类的轻量技巧聚类适合探索但生产环境里每次新数据都重跑KMeans、簇编号还可能变运营侧没法用。我一般会再补一步把聚类结果沉淀成一张LRFMC打分卡用规则给每个客户打分新数据来了直接查表。具体做法是对每个指标按分位数切成5档从高价值到低价值分别给5到1分然后加权求和。权重根据业务定比如M和F各0.3R和C各0.15L占0.1。这样每个客户得到一个0到5之间的价值分直接排序就能用。# 用分位数给每个指标打分R是越小越好所以反向 df[L_score] pd.qcut(df[L], 5, labels[1,2,3,4,5]).astype(int) df[R_score] pd.qcut(df[R], 5, labels[5,4,3,2,1]).astype(int) df[F_score] pd.qcut(df[F].rank(methodfirst), 5, labels[1,2,3,4,5]).astype(int) df[M_score] pd.qcut(df[M].rank(methodfirst), 5, labels[1,2,3,4,5]).astype(int) df[C_score] pd.qcut(df[C], 5, labels[1,2,3,4,5]).astype(int) # 加权总分 weights {L_score:0.1, R_score:0.15, F_score:0.3, M_score:0.3, C_score:0.15} df[value_score] sum(df[col]*w for col, w in weights.items()) # 按分数分档 df[value_tier] pd.cut(df[value_score], bins[0,2,3,4,5], labels[低价值,一般,潜力,高价值]) print(df[value_tier].value_counts())逻辑说明qcut按分位数切5档保证每档人数大致相等。R是反向指标所以标签顺序反过来。rank(methodfirst)是为了处理大量重复值比如很多客户F1直接qcut会因为边界重复报错。加权求和后cut分档运营直接按value_tier筛选人群。参数说明权重是业务可调的如果航司更看重里程把M_score权重提到0.4。分档边界[0,2,3,4,5]根据实际分数分布调别硬套。这套打分卡的好处是可解释、可复现、可增量新客户进来算一次分数就行不用重跑聚类。我自己的习惯是聚类用来发现「原来客户能分成这几种」打分卡用来做「每天给运营一张可执行名单」。两者配合比只交一个聚类模型实用得多。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

Java SpringBoot+Vue构建新农村风貌展示平台实战解析
Java SpringBoot+Vue构建新农村风貌展示平台实战解析

敲完最后一块代码、把前端页面在手机浏览器里缩放检查完,这个"Java基于SpringBootVue的新农村风貌展示平台"算是正式交付了。做这类项目的起因很简单:镇里想把各村的自然风光、老建筑、特色农产品和旅游路线集中放到一个页面上,让外… · 2026/9/26 14:07:14

全球植被碳储量变化空间分布数据:从栅格读取到碳汇判断的Python实践
全球植被碳储量变化空间分布数据:从栅格读取到碳汇判断的Python实践

简介:这份资源提供全球植被碳储量的变化空间分布数据,面向从事生态遥感、碳循环研究及地理信息分析的学习者与科研人员,可用于探究不同区域植被碳储量的增减格局、制作专题图件或作为论文与项目的空间数据支撑。压缩包共14个文件,… · 2026/9/26 14:07:14

Python实现NSGA-II多目标优化:Jupyter代码详解与调参避坑指南
Python实现NSGA-II多目标优化:Jupyter代码详解与调参避坑指南

简介:这份资源面向具备一定Python基础、希望深入理解多目标优化算法的学生与工程研究人员,围绕非支配排序遗传算法(NSGA-II)提供从原理到编程实现的完整学习材料。压缩包共10个文件,约518KB,以4个ipynb交互… · 2026/9/26 14:07:14

手把手搭建企业级RAG知识库:从原理到避坑指南
手把手搭建企业级RAG知识库:从原理到避坑指南

大模型时代,几乎每个团队都在尝试给自己的业务接入知识库。但只要你动手做一次RAG就会发现:网上教程很多,能跑通的Demo也不少,真正到了企业级场景,检索不准、引用不可信、上下文错乱、多轮对话失忆——问题一个接一个。… · 2026/9/26 14:49:22

机械臂避障路径规划仿真:从算法选型到工程落地
机械臂避障路径规划仿真:从算法选型到工程落地

简介:这份资源是面向机器人学学习者与机械臂控制方向研究者的避障路径规划仿真程序包,聚焦多自由度机械臂在三维复杂环境中安全、高效地从起点运动到目标点并规避障碍这一核心问题。压缩包共4个文件,约4KB,以mat数据文件、m脚本和… · 2026/9/26 14:49:22

RAG知识库从零到企业级落地:原理、选型与代码实践
RAG知识库从零到企业级落地:原理、选型与代码实践

最近不少读者在后台问我同一个问题:企业内部的文档越来越多,想让大模型直接回答各种规章制度、技术手册和项目沉淀,但试了几次都不理想。直接问模型,它要么一本正经地编答案,要么只能回答训练数据里那些过时的内容&… · 2026/9/26 14:49:22

Docling实战:开源文档解析工具如何让复杂PDF结构化提取更简单
Docling实战:开源文档解析工具如何让复杂PDF结构化提取更简单

文档解析这件事,这几年做RAG、知识库和文档智能处理的人应该都深有体会:PDF拿到手要转成结构化数据,真不是装个库调用一下就能完事的。尤其当文档里带着复杂表格、多栏排版、页眉页脚,或者干脆就是个扫描件,传统那套基… · 2026/9/26 14:49:22

SwinIR自定义训练测试代码:从数据准备到PSNR计算全流程
SwinIR自定义训练测试代码:从数据准备到PSNR计算全流程

简介:这份资源是面向图像恢复方向学习者与开发者的SwinIR自定义训练与测试代码实现,基于Swin Transformer架构,可完成图像超分辨率与图像去噪等任务。代码在官方源码基础上重新梳理了训练与测试流程,并补充了关键注释,… · 2026/9/26 14:49:22

二阶锥规划解主动配电网最优潮流:从建模到CPLEX实现
二阶锥规划解主动配电网最优潮流:从建模到CPLEX实现

简介:一套面向主动配电网最优潮流求解的毕业设计程序包,基于IEEE33节点系统,采用二阶锥规划(SOCP)方法,在MATLAB中调用CPLEX求解器,实现风电、并联电容器、SVG、有载调压变压器及储能设备的多时… · 2026/9/26 14:49:14

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码