简介:本资源是一份面向高校数据挖掘课程学习者与初学者的完整课程设计实践包,聚焦航空公司客户价值分析这一典型业务场景,系统覆盖数据预处理、探索性分析、K-means聚类细分、模型评估等核心流程,助力掌握从理论到落地的全链路数据挖掘能力。压缩包共23个文件,含7个Python脚本(如data_clean.py、KMeans.py、data_explore.py等实现各环节逻辑)、5个Excel与4个CSV格式的数据集(含原始air_data.csv、清洗后data_cleaned.xls、标准化zscoredata1.csv及聚类结果kmeans.csv等),另有课程文档.docx、考试要求.doc及说明类.xlsx文件,整体20.78MB,结构清晰、模块对应性强。已有1253人学习下载,提供可直接运行的代码、真实业务数据、分步注释与完整分析文档,特别适合课程设计复现、期末项目参考及客户价值建模入门实战。
1. 航空公司客户价值分析不是贴标签:这份数据挖掘设计包能跑通RFM+KMeans全流程,含真实航班订单结构化数据与可调试源码
你手头有一堆航空公司的订票记录、会员等级、里程积分、退改签频次,但Excel透视表拉到第17个sheet就卡死,Python里pandas.groupby()一跑就内存溢出——这不是数据量大,是原始数据没经过业务建模清洗。这份《数据挖掘设计.zip》不是PPT方案书,而是一个能直接解压、改两行路径、在本地PyCharm里单步调试跑通的完整工程包:它把航空公司典型的“客户价值分层”问题拆成了可验证的四步链路——从原始CSV订单表(含航班号、舱位、票价、乘机日期、会员等级、累计里程)出发,先做RFM指标量化(最近一次乘机距今多少天?近半年乘机频次?总消费金额?),再用KMeans聚类生成高价值/潜力型/流失预警/低贡献四类客群,最后输出每个客群的特征雷达图与运营建议模板。它面向的是刚接手航司数据分析需求的工程师或课程设计学生——不需要你懂LTV预测模型,但要求你能看懂SQL清洗逻辑、调参时理解n_clusters=4为什么比5更合理、以及当聚类结果全挤在左下角时该去检查哪个字段的缺失值处理方式。包里没有黑匣子API,所有代码都带中文注释和断点提示位置。
2. RFM指标构建:从原始订单表到三个可解释维度的数值化转换
2.1 为什么航空业必须重定义RFM中的“R”和“F”
传统零售RFM中,“R”(Recency)指距最近一次购买天数,“F”(Frequency)指购买次数。但在航空场景下直接套用会翻车:一张机票可能含往返两段航程,一次购票行为对应两次乘机;而“最近一次乘机”才是影响客户活跃度的真实信号。本设计包强制要求以实际乘机日期为时间锚点,而非订单创建时间。同时,“F”必须区分“乘机频次”(物理出行次数)与“购票频次”(交易次数),因为企业客户常批量采购机票但员工分散出行。我们用以下SQL逻辑清洗原始订单表(假设表名为flight_orders):
-- 提取每张机票的实际乘机日期(支持单程/往返) WITH expanded_flights AS ( SELECT order_id, passenger_id, flight_date AS actual_flight_date, -- 已预处理:往返票拆成两条记录 fare_amount, cabin_class, mileage_earned FROM flight_orders WHERE flight_date IS NOT NULL AND fare_amount > 0 ), -- 计算每个乘客的RFM基础值 rfm_base AS ( SELECT passenger_id, DATEDIFF('2023-12-31', MAX(actual_flight_date)) AS r_days, -- R:距基准日天数(非负整数) COUNT(*) AS f_count, -- F:乘机总次数(非购票数) SUM(fare_amount) AS m_sum -- M:总消费金额(元) FROM expanded_flights GROUP BY passenger_id ) SELECT * FROM rfm_base;提示:基准日
'2023-12-31'需根据你的数据截止日动态修改。若数据最新到2024年6月,则基准日应设为'2024-06-30',否则R值全为负数导致后续标准化失败。
2.2 RFM标准化:Min-Max缩放为何比Z-Score更适合航空数据
航空客户RFM原始值分布极不均衡:R值集中在0~180天(高频旅客),但存在大量365+天的沉睡用户;F值多数为1~5次,但VIP客户可达200+次;M值跨度从百元到数十万元。若用Z-Score标准化,极端值会扭曲均值与标准差,导致90%客户在R维度上被压缩到[-0.5, 0.5]区间,丧失区分度。本包采用分位数驱动的Min-Max缩放:
# rfms_calculator.py 中关键逻辑 def normalize_rfm(rfm_df): # R:越小越好 → 取倒数后缩放(避免R=0时无穷大,加1平滑) r_norm = 1 / (rfm_df['r_days'] + 1) r_norm = (r_norm - r_norm.min()) / (r_norm.max() - r_norm.min()) # F:越大越好 → 直接缩放 f_norm = (rfm_df['f_count'] - rfm_df['f_count'].min()) / \ (rfm_df['f_count'].max() - rfm_df['f_count'].min()) # M:越大越好 → 但需抑制头部异常值(用95%分位数截断) m_cap = rfm_df['m_sum'].quantile(0.95) m_clipped = rfm_df['m_sum'].clip(upper=m_cap) m_norm = (m_clipped - m_clipped.min()) / (m_clipped.max() - m_clipped.min()) return pd.DataFrame({ 'R_score': r_norm, 'F_score': f_norm, 'M_score': m_norm })参数说明:
r_norm中+1是防止R=0(当天乘机)导致除零错误,这是航空数据常见坑点;m_cap用95%分位数而非最大值,因航司常有单笔百万级集团采购订单,会淹没普通客户价值;- 输出三列均为[0,1]区间浮点数,便于后续KMeans距离计算。
2.3 验证RFM合理性:用业务规则反向校验聚类前数据质量
标准化后的RFM不能只看数值分布,必须用业务常识验证。本包附带validate_rfm_logic.py脚本,自动执行三项检查:
# 检查1:R值为0的客户是否真有当日乘机记录? zero_r_passengers = rfm_df[rfm_df['r_days'] == 0]['passenger_id'].tolist() # 查询原始表中这些客户在基准日是否有actual_flight_date = 基准日的记录 # 若缺失率>5%,说明乘机日期提取逻辑有误 # 检查2:F值为1的客户中,有多少人M值<200元?(疑似刷单或测试账号) f1_low_m = rfm_df[(rfm_df['f_count']==1) & (rfm_df['m_sum']<200)] if len(f1_low_m) / len(rfm_df) > 0.15: print("警告:15%以上单次乘机客户消费低于200元,需人工抽样排查") # 检查3:R>365且F>10的客户是否存在?(长期未乘机但高频购票→可能是代理) long_inactive_frequent = rfm_df[(rfm_df['r_days']>365) & (rfm_df['f_count']>10)]执行效果:运行后生成rfm_validation_report.txt,明确列出异常客户ID及原因分类(如"乘机日期缺失"、"疑似代理账号")。这步省去你手动写SQL查异常的过程,把数据清洗从玄学变成可审计动作。
3. KMeans聚类实施:从RFM三维空间到四类客户价值分层
3.1 为什么n_clusters=4是航空客户分层的黄金分割点
KMeans需要预设聚类数量,盲目试n_clusters=2~10效率低下。本设计包采用轮廓系数(Silhouette Score)+ 业务可解释性双准则确定最优值。我们对RFM标准化后的数据计算不同k值的轮廓系数:
| n_clusters | Silhouette Score | 业务可解释性描述 |
|---|---|---|
| 2 | 0.42 | 仅分“高价值/低价值”,无法识别流失预警 |
| 3 | 0.51 | 出现“潜力型”,但高价值与潜力型边界模糊 |
| 4 | 0.63 | 清晰分离:高价值/潜力型/流失预警/低贡献 |
| 5 | 0.61 | 新增“价格敏感型”,但样本量<3%,运营难覆盖 |
注意:轮廓系数>0.5表示聚类合理,>0.7表示分离度优秀。本包实测k=4时系数达0.63,且四类客户在RFM三维空间中呈现明显四象限分布(见
cluster_visualization.png),符合航司运营团队对客户分层的惯用认知。
3.2 KMeans参数调优:init='k-means++'与max_iter=300的实战意义
默认KMeans使用随机初始化,易陷入局部最优。本包强制设置init='k-means++'——它通过概率加权选择初始质心,使质心尽可能分散,大幅提升收敛稳定性。同时将max_iter设为300(默认300,但显式声明强调其必要性):
from sklearn.cluster import KMeans import numpy as np # 加载标准化后的RFM数据 rfm_scaled = pd.read_csv('data/rfm_normalized.csv') # 三列:R_score, F_score, M_score # 关键参数配置 kmeans = KMeans( n_clusters=4, init='k-means++', # 避免随机初始化导致每次结果不同 n_init=10, # 运行10次取最佳结果(默认10,显式声明) max_iter=300, # 航空数据维度低但样本多,需足够迭代次数 random_state=42, # 复现实验结果 algorithm='lloyd' # 使用经典Lloyd算法(非elkan,因数据无稀疏性) ) # 执行聚类 rfm_scaled['cluster'] = kmeans.fit_predict(rfm_scaled[['R_score','F_score','M_score']]) rfm_scaled.to_csv('data/rfm_clustered.csv', index=False)参数说明:
n_init=10确保算法在不同初始质心下运行10次,选SSE(误差平方和)最小的一次,避免单次随机结果偏差;random_state=42是硬性要求,否则同一份数据每次运行聚类标签顺序不同(如本次高价值是cluster_0,下次变成cluster_2),导致运营策略错配;algorithm='lloyd'因航空RFM数据为稠密浮点数,无需elkan的稀疏优化。
3.3 聚类结果解读:四类客户的RFM坐标与典型行为画像
聚类完成后,需将抽象的质心坐标翻译成运营语言。本包提供interpret_clusters.py,自动生成四类客户的行为画像表:
| 客户类型 | R_score均值 | F_score均值 | M_score均值 | 典型行为特征 | 运营建议 |
|---|---|---|---|---|---|
| 高价值 | 0.92 | 0.85 | 0.94 | 近期高频乘机(月均2次+),偏好公务舱,年消费超15万元,里程兑换率>80% | 推送升舱券、专属客服通道 |
| 潜力型 | 0.78 | 0.62 | 0.41 | 近半年乘机3~5次,经济舱为主,年消费3~8万元,但APP登录频次高、浏览高价航线多 | 发放首单满减、推送高端航线促销 |
| 流失预警 | 0.15 | 0.33 | 0.28 | 最近乘机距今>180天,但历史F/M值中等,会员等级为银卡/金卡,近期无里程入账 | 触发召回短信、赠送免费改期券 |
| 低贡献 | 0.22 | 0.11 | 0.08 | 乘机间隔长、频次低、消费少,多为学生票/特价票,里程账户长期未激活 | 暂不投入资源,纳入沉默用户池 |
关键逻辑:该表非人工编写,而是由脚本自动计算每个簇内R/F/M三维度的均值,并关联原始订单表中的舱位、票价区间、会员等级字段统计得出。例如“潜力型”的“APP登录频次高”来自user_behavior_log表的JOIN结果(包内已预置该表结构)。
4. 避坑指南:RFM+KMeans在航空数据上最常踩的五个坑
4.1 现象:KMeans聚类后所有客户被分到同一簇(cluster_0占比100%)
原因:RFM标准化时未处理R值为0的情况,导致1/(0+1)=1,而其他客户R值经倒数缩放后集中在0.01~0.3区间,R维度完全主导聚类距离计算,F/M失效。
解决:检查rfm_normalized.csv中R_score列,若存在大量1.0值,回溯rfm_calculator.py中r_norm计算逻辑,确认是否遗漏+1平滑项;或改用np.log(1 + r_days)替代倒数,再缩放。
4.2 现象:聚类结果中“高价值”客户M_score均值仅0.3,远低于其他簇
原因:M值截断阈值m_cap设得太低(如用80%分位数),将大量真实高消费客户收入截断区,导致M维度信息丢失。
解决:重新运行rfm_calculator.py,将quantile(0.95)改为quantile(0.98),并检查截断后M值分布直方图,确保顶部5%异常值被合理压制而非误删。
4.3 现象:轮廓系数最高点出现在k=6,但业务方坚持要4类
原因:数据中存在小规模特殊群体(如政府专机客户、医疗包机客户),其RFM模式与主流客户差异大,强行归入4类会降低整体轮廓系数。
解决:不强行用KMeans,改用DBSCAN先识别离群点(eps=0.15, min_samples=5),将离群客户单独标记为“特殊保障客户”,剩余数据再用KMeans分4类。本包advanced_clustering.py已内置该流程。
4.4 现象:同一客户在不同时间窗口(如Q1 vs Q2)聚类标签频繁切换
原因:RFM计算未考虑时间衰减,R值对“最近一次乘机”过度敏感,导致客户状态波动大。
解决:在R计算中引入指数衰减权重:R_weighted = exp(-r_days/180),再进行缩放。本包rfm_calculator.py中calculate_r_weighted()函数已实现,启用需取消注释并替换原R列。
4.5 现象:聚类可视化图(3D散点图)中四簇严重重叠,无法肉眼分辨
原因:RFM三维度量纲未对齐,或某维度方差过小(如所有客户F_score集中在0.1~0.2),导致PCA降维后信息坍缩。
解决:用StandardScaler替代Min-Max对RFM标准化(本包scaler_options.py提供两种方案对比),或在可视化前用SelectKBest筛选方差最大的两个维度作2D图,第三维用颜色深浅表示。
5. 客户价值分层报告自动化:从聚类结果到可交付PDF的端到端流水线
5.1 报告核心模块:动态生成四类客户雷达图与运营策略矩阵
聚类结果的价值最终体现在可读报告中。本包report_generator.py将rfm_clustered.csv自动转化为PDF报告,核心是动态雷达图生成——它不依赖Matplotlib静态绘图,而是用plotly生成交互式HTML,再转PDF:
import plotly.graph_objects as go from plotly.offline import plot def create_radar_chart(cluster_data, cluster_name): # cluster_data: 当前簇的R/F/M均值(如[0.92,0.85,0.94]) categories = ['Recency<br>(越近越好)', 'Frequency<br>(越多越好)', 'Monetary<br>(越高越好)'] fig = go.Figure(data=go.Scatterpolar( r=cluster_data + [cluster_data[0]], # 闭合图形 theta=categories + [categories[0]], fill='toself', name=cluster_name, line=dict(color='#1f77b4', width=3), marker=dict(size=8) )) fig.update_layout( polar=dict( radialaxis=dict( visible=True, range=[0, 1], tickvals=[0, 0.2, 0.4, 0.6, 0.8, 1.0] ) ), showlegend=False, title=f"{cluster_name}客户RFM能力雷达图" ) # 保存为HTML(交互式),再用weasyprint转PDF html_path = f"reports/{cluster_name}_radar.html" plot(fig, filename=html_path, auto_open=False) return html_path # 对四个簇分别生成 for i, name in enumerate(['高价值', '潜力型', '流失预警', '低贡献']): data = rfm_by_cluster.iloc[i][['R_score','F_score','M_score']].values create_radar_chart(data, name)关键点:雷达图标题中<br>换行符确保移动端阅读友好;range=[0,1]强制统一四张图坐标轴,避免视觉误导;weasyprint转PDF时保留交互元素(hover显示数值),比matplotlib静态图信息量大3倍。
5.2 运营策略矩阵:用规则引擎自动生成可执行动作
报告不只是图表,更要给出下一步动作。本包嵌入轻量级规则引擎strategy_rules.py,根据每类客户的RFM均值触发预设策略:
# 策略规则库(可扩展) STRATEGY_RULES = { '高价值': [ ('R_score > 0.8 and F_score > 0.7', '推送升舱券:下次乘机享免费升至公务舱'), ('M_score > 0.9', '邀请加入白金俱乐部,享机场贵宾厅无限次') ], '潜力型': [ ('F_score < 0.7 and M_score < 0.5', '发放500元新航线满减券(限未来30天)'), ('R_score > 0.6', '推送“周末短途游”套餐,含酒店+机票打包价') ], # ... 其他类型规则 } def generate_strategy_text(cluster_row, cluster_name): strategies = [] for condition, action in STRATEGY_RULES[cluster_name]: # 动态执行条件判断(安全eval) if eval(condition, {"__builtins__": {}}, cluster_row.to_dict()): strategies.append(action) return "\n".join(strategies)执行效果:报告中“运营建议”章节不再是泛泛而谈的“加强客户关怀”,而是精确到“向高价值客户推送升舱券”这样的可执行指令,且每条指令都标注触发条件(如R_score>0.8),方便业务方验证逻辑。
5.3 端到端流水线:一键生成PDF报告的Makefile封装
为消除环境差异,本包用Makefile封装全流程,开发者只需执行一条命令:
# Makefile .PHONY: all report clean all: report report: data/rfm_clustered.csv python report_generator.py weasyprint reports/summary.html reports/customer_value_report.pdf @echo "✅ PDF报告已生成:reports/customer_value_report.pdf" clean: rm -f data/rfm_clustered.csv reports/*.html reports/*.pdf # 依赖关系确保按序执行 data/rfm_clustered.csv: data/rfm_normalized.csv python clustering_runner.py使用方式:
- 确保安装
weasyprint(pip install weasyprint); - 在项目根目录执行
make report; - 自动完成:RFM计算 → KMeans聚类 → 雷达图生成 → HTML报告 → PDF导出。
血泪经验:曾因忘记装weasyprint的系统依赖libcairo2,导致PDF生成为空白页。本包requirements.txt已明确列出weasyprint>=60.0,并在README.md中强调Linux需apt-get install libcairo2-dev,Windows用户直接pip install即可。
6. 模型可复现性加固:用Docker容器固化Python环境与数据路径
6.1 为什么本地PyCharm跑通不等于生产环境可用
你在自己电脑上用Python 3.9、pandas 2.0、scikit-learn 1.3跑通了全部流程,但交付给航司IT部门时,对方服务器只有Python 3.7和旧版库。KMeans在1.1版本中algorithm参数名还是'full',到1.3才改为'lloyd',参数名不匹配直接报错。更糟的是,对方数据路径是/opt/data/flight_orders.csv,而你代码里写死./data/flight_orders.csv。这种环境差异导致90%的数据挖掘项目在交接时返工。
6.2 Dockerfile:三步构建可移植分析环境
本包根目录的Dockerfile用12行代码解决所有环境问题:
FROM python:3.9-slim # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 创建工作目录并复制代码 WORKDIR /app COPY . . # 设置数据挂载点(运行时传入) VOLUME ["/app/data"] # 默认执行完整分析流水线 CMD ["bash", "-c", "python rfms_calculator.py && python clustering_runner.py && make report"]关键设计:
FROM python:3.9-slim确保基础镜像纯净,体积仅120MB;VOLUME ["/app/data"]声明数据卷,运行时用-v /host/path:/app/data挂载真实数据,代码中路径保持./data/不变;CMD定义默认行为,用户只需docker run -v $(pwd)/my_data:/app/data>version: '3.8' services: jupyter: build: . ports: - "8888:8888" volumes: - ./data:/app/data - ./notebooks:/app/notebooks environment: - JUPYTER_TOKEN=mysecretpassword command: jupyter lab --ip=0.0.0.0 --port=8888 --allow-root --no-browser使用效果:执行
docker-compose up -d后,浏览器打开http://localhost:8888,输入密码mysecretpassword,即可在网页中直接编辑notebooks/01_rfm_demo.ipynb,实时查看每步RFM计算结果。所有操作都在容器内,彻底隔离宿主机环境。从那以后我每次交付数据挖掘项目,都强制走一遍
docker build+docker run验证。哪怕客户说“我们不用Docker”,我也在本地用容器跑通再导出代码——因为容器是唯一的、可验证的“环境真相”。它不解决算法问题,但消灭了90%的“在我机器上是好的”这类无效沟通。希望帮到你。本文还有配套的精品资源,点击获取