航空公司客户价值分析Python源码实战:LRFMC模型与KMeans聚类
发布时间:2026/9/26 14:06:59来源:尧图网络
简介这份资源是面向数据分析与机器学习入门者的航空公司客户价值分析实战源码包围绕Python环境下的客户分群与价值预测展开适合希望把数据挖掘流程落到真实业务场景的学习者。包内共10个文件以5个xls和3个csv数据表为主覆盖原始数据、清洗后数据与标准化数据另有1个py脚本承载核心分析逻辑1个txt说明导入模块压缩包约17.13MB。已有891人学习下载。读者可借助完整数据与代码走通数据清洗、特征工程、K-means客户分群、客户生命周期价值估算及模型评估等环节理解从原始航空数据到客户价值结论的落地路径并参考目录组织方式与模块说明快速复现实验、排查数据格式与字段处理问题提升数据分析与机器学习实际应用能力。1. 航空公司客户价值分析Python源码从一份rar到一个能跑的RFM模型拿到「航空公司客户价值分析Python源码.rar」这个标题的人十有八九是两种情况一是课程设计或毕设卡在数据挖掘选题上想找一份能跑通、能改、能写进论文的代码二是已经在做航空常旅客运营手里有会员飞行记录想用Python把客户分层这件事从Excel里搬出来。不管哪种核心诉求都一样——把一堆航班流水、里程积分、票价折扣记录变成能指导营销动作的客户价值标签。这个方向在数据挖掘教学里属于经典案例因为它同时具备三个条件数据字段够丰富飞行里程、飞行次数、飞行时间、折扣系数、舱位等级、业务含义够清晰谁贡献收入、谁在流失、谁值得挽留、算法门槛够低聚类为主不需要深度学习。但「源码.rar」这三个字也意味着一个现实你拿到的很可能是一份能跑但看不懂、或者看懂了但跑不起来的代码。这篇笔记不假设我见过那份压缩包只按这个方向最常见的实现路径把从环境配置到模型落地的完整链路拆开讲让你拿到任何一份同类源码都能自己判断能不能用、哪里要改。适合的读者会一点Python基础语法知道pandas是什么但用得不多想通过一个完整案例把数据清洗、特征构造、KMeans聚类、雷达图可视化串起来的人。如果你已经能熟练写sklearn流水线这篇里关于航空业务特征的处理细节仍然值得看因为通用教程不会告诉你为什么折扣系数的均值比总和更有区分度。2. 航空客户价值分析到底在算什么LRFMC模型的来龙去脉2.1 为什么传统RFM在航空场景不够用RFM模型是客户价值分析的通用框架Recency最近一次消费、Frequency消费频率、Monetary消费金额。放到电商场景很好用因为一次下单就是一个清晰的消费事件。但航空公司的客户行为不是这样——一个乘客可能一年飞20次但每次都是公司出钱的折扣票另一个乘客一年只飞3次但每次都是全价头等舱。如果只看消费金额后者可能更高如果只看频次前者更活跃。两个维度打架分出来的客户群就没法指导运营。航空业常见的做法是在RFM基础上扩展成LRFMC模型多出来的两个维度是L和C。L是Length指客户入会时长或首次乘机到现在的月数用来衡量客户关系的深度C是Coefficient指平均折扣系数也就是实际票价除以全价票的比值用来衡量客户对价格的敏感程度。这两个维度加进来之后客户画像从「花了多少钱」变成「以什么价格、在多长时间里、飞了多少次」运营动作才能对应上——高折扣敏感的长龄客户适合推里程兑换低折扣敏感的新客户适合推升舱体验。2.2 LRFMC五个指标的计算口径与业务含义具体到代码实现五个指标的计算方式需要先定清楚否则后面聚类出来的结果没法解释。下面这张表是我在多个航空数据集上验证过的口径你可以直接对照手里的源码看它是不是这么算的指标字段来源计算方式业务含义L入会时间(当前日期 - 入会日期) / 30客户关系月数越大越忠诚R最近乘机日期(当前日期 - 最后乘机日期) / 30月数越小越活跃F飞行次数直接累加出行频次M飞行里程直接累加贡献总量C票价折扣平均(票价/全价)价格敏感度越小越不敏感这里有个容易翻车的地方R和C的方向。R是越小越好最近飞过C也是越小越好折扣低说明愿意付高价但F、M、L是越大越好。如果源码里没有做方向统一就直接丢给KMeans聚类结果的业务解释会完全乱掉。我一般会在标准化之前先把R和C取相反数或者用1减去归一化后的值让五个指标方向一致。2.3 从原始流水到客户宽表的聚合逻辑航空数据最常见的原始形态是一张航班流水表每行是一次乘机记录字段包括会员卡号、航班日期、飞行里程、票价、舱位代码等。要变成每个客户一行的宽表核心是一个groupby操作。但这里有个细节票价折扣系数不能简单用总票价除以总全价因为不同航线的全价票基数不同。正确做法是先算出每条航线的全价基准再对每个客户的每条记录算折扣最后取均值。import pandas as pd import numpy as np # 假设原始流水表字段member_id, flight_date, distance, fare, full_fare, join_date df pd.read_csv(flight_records.csv, parse_dates[flight_date, join_date]) # 计算每条记录的折扣系数避免用总价相除导致的辛普森悖论 df[discount] df[fare] / df[full_fare] # 以观察期末为基准日计算L和R snapshot_date df[flight_date].max() pd.Timedelta(days1) customer df.groupby(member_id).agg( L(join_date, lambda x: (snapshot_date - x.min()).days / 30), R(flight_date, lambda x: (snapshot_date - x.max()).days / 30), F(flight_date, count), M(distance, sum), C(discount, mean) ).reset_index()这段代码的关键在discount的计算位置——必须在groupby之前逐条算不能先sum再除。snapshot_date取最大航班日期加一天是为了避免R出现负数。如果你的源码里R有负值大概率就是基准日取错了。另外join_date用min是因为同一会员的入会日期应该一致用min可以容忍少量脏数据。3. 拿到源码后怎么在本地跑通环境、依赖与数据替换3.1 Python环境配置与依赖安装的最小步骤一份航空客户分析源码通常依赖这几个库pandas做数据处理、numpy做数值计算、scikit-learn做聚类和标准化、matplotlib和seaborn做可视化。如果你用的是Anaconda这些基本都自带了只需要确认版本。如果是从python.org下载的裸环境按下面步骤走# 创建独立环境避免和系统包冲突 python -m venv airline_env # Windows激活 airline_env\Scripts\activate # macOS/Linux激活 source airline_env/bin/activate # 安装核心依赖不指定版本让pip自动解析兼容版本 pip install pandas numpy scikit-learn matplotlib seaborn装完之后用python -c import sklearn; print(sklearn.__version__)验证一下。如果报ModuleNotFoundError检查是不是在正确的环境里执行。VS Code用户注意右下角解释器要选到airline_envPyCharm用户检查Project Interpreter设置。这一步看着简单但我见过太多人因为IDE解释器和终端环境不一致导致「终端能跑、IDE报错」的玄学问题。3.2 源码目录结构与数据文件的对应关系典型的源码包解压后大概长这样一个data文件夹放csv或xlsx一个main.py或analysis.ipynb是主入口可能还有utils.py放工具函数、config.py放路径配置。拿到之后先别急着运行按这个顺序检查第一打开主入口文件找read_csv或read_excel的路径参数。很多源码写的是作者本机的绝对路径比如D:\work\airline\data\customer.csv你需要改成相对路径./data/customer.csv。第二看数据文件名和实际文件是否一致有些压缩包里数据文件叫air_data.csv但代码里写的是data.csv。第三检查有没有encoding参数中文列名的csv在Windows上默认gbk代码里如果没写encodingutf-8会报UnicodeDecodeError。# 常见的路径配置写法建议统一放在文件开头 import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_PATH os.path.join(BASE_DIR, data, air_data.csv) # 读取时显式指定编码避免中文列名乱码 df pd.read_csv(DATA_PATH, encodingutf-8)用os.path拼接路径的好处是不管在Windows还是Linux上跑都不会因为斜杠方向出错。如果你的源码里是硬编码路径建议先改成这种写法再往下走。3.3 用自己的数据替换示例数据时要注意的字段映射源码自带的示例数据通常字段名是英文的比如MEMBER_NO、FLIGHT_DATE、DISTANCE。你手里的真实数据可能是中文列名或者字段含义对不上。替换时不要直接改代码里的列名而是加一层映射# 字段映射表把真实数据的列名映射到代码期望的列名 column_mapping { 会员卡号: member_id, 航班日期: flight_date, 飞行里程: distance, 票价: fare, 全价票价: full_fare, 入会日期: join_date } df df.rename(columnscolumn_mapping) # 检查映射后是否缺少关键字段 required [member_id, flight_date, distance, fare, full_fare, join_date] missing [c for c in required if c not in df.columns] if missing: raise ValueError(f缺少必要字段: {missing})这段检查逻辑建议保留它能在数据替换阶段就暴露问题而不是等到聚类跑完才发现结果不对。真实数据里full_fare经常没有现成字段需要根据舱位代码和航线去关联一张运价表这是替换数据时工作量最大的部分。4. 数据清洗与特征标准化聚类之前必须做的四件事4.1 缺失值与异常值的处理边界航空客户数据常见的缺失情况有三种票价为空可能是积分兑换票、飞行里程为空短途或系统未记录、入会日期为空早期会员数据迁移丢失。处理方式不能一刀切票价缺失但飞行里程正常的记录我一般保留把折扣系数设为该客户其他记录的中位数如果该客户只有这一条记录则整条剔除。飞行里程缺失且无法从航线表补全的直接剔除因为M是核心维度。入会日期缺失的用该客户最早航班日期减一年作为估计值或者用全体会员入会日期的中位数填充。异常值方面折扣系数理论上在0到1之间但数据里可能出现大于1的改签补差价或小于0的退款冲正。这些记录在算C均值时会拉偏结果建议先过滤到[0, 1]区间。飞行次数为1的客户是否保留要看分析目的——如果做高价值客户识别可以保留作为低价值群体如果做流失预警可能需要单独处理。4.2 标准化方法的选择Z-Score还是Min-MaxLRFMC五个指标的量纲差异很大L和R是月数通常在0到100之间F是次数可能1到200M是里程可能几百到几十万C是0到1的小数。不标准化直接聚类M会主导距离计算其他四个指标形同虚设。常见做法是Z-Score标准化也就是减均值除标准差。它适合数据近似正态分布的情况但航空数据里M和F往往是右偏的——少数高频旅客的里程数远超均值。这种情况下Z-Score会把大部分客户压缩在均值附近聚类效果反而不好。我一般会先对M和F做log变换再标准化from sklearn.preprocessing import StandardScaler # 对右偏严重的指标先取对数缓解长尾影响 customer[M_log] np.log1p(customer[M]) customer[F_log] np.log1p(customer[F]) # 方向统一R和C取相反数让所有指标都是越大越好 customer[R_adj] -customer[R] customer[C_adj] -customer[C] features [L, R_adj, F_log, M_log, C_adj] scaler StandardScaler() X_scaled scaler.fit_transform(customer[features])np.log1p是log(x1)避免M为0时报错。方向统一这一步很多源码会漏掉导致聚类结果里「高价值客户」的R值反而很大解释不通。标准化后的数据建议保存一份方便后面换聚类算法时复用。4.3 共线性检查与特征筛选L、F、M三个指标之间往往高度相关——入会时间长的客户飞行次数和里程通常也高。如果相关系数超过0.8在KMeans里相当于给这个维度加了权重聚类结果会偏向按「忠诚度」分群弱化折扣敏感度的影响。检查方法很简单算一下相关系数矩阵corr_matrix customer[features].corr() print(corr_matrix.round(2)) # 如果L和F相关系数超过0.85考虑只保留一个 # 或者用PCA降维但会损失业务解释性如果确实存在高相关我的做法是保留F去掉L因为飞行次数比入会时长更能反映当前活跃度。但这一步没有标准答案要看你的分析目标——如果重点是会员生命周期管理L就不能丢。5. 避坑与排查跑航空客户聚类时最容易翻车的五个地方5.1 聚类数K选错导致业务无法解释现象KMeans跑完每个簇的客户数量差不多但看簇中心发现五个指标的值都很接近说不出「这群人是什么客户」。原因K值选得太大或太小。K2只能分出高低价值K8又太细导致每个簇的特征不突出。航空客户分析通常K4或5比较合适对应「高价值常旅客」「价格敏感型」「新客户」「流失边缘」这几类。解决不要只看肘部法则的拐点结合业务预期定K。先跑K3到8对每个K输出簇中心表看哪个K的簇中心差异最明显。轮廓系数可以作为辅助指标但最终判断标准是「你能不能给每个簇起一个运营能听懂的名字」。5.2 标准化遗漏导致里程主导一切现象聚类结果里一个簇的M值极高其他四个指标和其他簇没区别整个分群变成按里程一刀切。原因忘记做标准化或者只对部分指标做了标准化。M的数值范围可能是其他指标的几百倍欧氏距离完全被它主导。解决确认StandardScaler或MinMaxScaler应用到了所有参与聚类的特征上。跑之前打印一下X_scaled的均值和标准差标准化后的数据均值应该接近0、标准差接近1。如果某个特征的std还是几百说明漏了。5.3 方向不一致让高价值客户被标成低价值现象聚类完成后你按业务逻辑判断应该是高价值的簇R值却很大很久没飞C值也很大折扣高。原因R和C的「越小越好」方向没有统一。KMeans基于距离聚类不关心方向但你在解释结果时会按「值大好」的直觉去读导致标签贴反。解决在标准化之前对R和C取相反数或者在输出簇中心后手动反转这两个指标的解读方向。我习惯在特征工程阶段就统一方向后面所有输出都按「值越大越优质」来读减少心智负担。5.4 数据泄露用全量数据做标准化再切训练测试现象如果源码里有训练集测试集划分标准化后的测试集表现异常好但换一批新数据预测就崩。原因先用全量数据fit了scaler再切分训练测试。scaler已经「看到」了测试集的均值和方差相当于泄露了信息。解决先切分再在训练集上fit_transform测试集只做transform。如果是无监督聚类没有测试集至少要在做客户分群时注意标准化参数应该基于当前批次数据计算换一批数据要重新fit不能把上一批的scaler直接套用。5.5 可视化雷达图标签重叠看不清现象用雷达图展示各簇特征时五个轴的标签挤在一起或者多个簇的线条重叠严重。原因雷达图的轴顺序和缩放没调好或者簇太多导致颜色区分度不够。解决把五个指标按业务逻辑排序比如L、R、F、M、C每个轴的范围统一到标准化后的[-2, 2]区间。簇数量超过5个时用分面图代替单张雷达图每个簇单独一张小图。颜色用matplotlib的tab10色板不要用默认的循环色。6. 从聚类结果到运营动作让分析报告真正被业务方用起来跑出簇标签只是第一步真正决定这份分析有没有价值的是你能不能把「簇0、簇1、簇2」翻译成运营能执行的动作。我一般会做一张对照表把每个簇的LRFMC特征描述、客户命名、建议动作写在一起。比如某个簇的L高、R低、F高、M高、C低那就是「高价值忠诚常旅客」建议动作是专属客服和里程加速另一个簇L低、R高、F低、M低、C高那就是「价格敏感流失边缘」建议动作是折扣券召回。验证聚类稳定性的一个实用技巧是换随机种子跑多次看每个簇的客户构成是否一致。如果换种子后某个客户的簇归属频繁跳变说明这个客户处于边界地带运营动作上可以暂缓投入。具体做法from sklearn.cluster import KMeans from sklearn.metrics import adjusted_rand_score labels_list [] for seed in [0, 42, 100, 2024]: km KMeans(n_clusters5, random_stateseed, n_init10) labels km.fit_predict(X_scaled) labels_list.append(labels) # 两两比较聚类结果的一致性 for i in range(len(labels_list)): for j in range(i1, len(labels_list)): score adjusted_rand_score(labels_list[i], labels_list[j]) print(fseed pair {i}-{j}: ARI {score:.3f})ARI接近1说明不同种子下聚类结果高度一致低于0.7就要警惕了可能是K选得不对或者特征区分度不够。n_init10是让KMeans用10组不同初始质心跑取最优结果这个参数在sklearn新版本里默认就是10但老版本默认是1显式写上更稳妥。最后说一个我自己的习惯每次做完客户分群我会随机抽10个客户手动核对他们的原始飞行记录和簇标签是否匹配。这个动作花不了十分钟但能抓住大部分数据清洗阶段的隐蔽错误。有次就是靠这个发现折扣系数算错了——把儿童票的半价当成了全价基准导致一批家庭客户的C值偏高被错误分到了价格敏感群。这种错误不看原始数据根本发现不了。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网