ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

校园一卡通消费数据分析与Python聚类建模实战

校园一卡通消费数据分析与Python聚类建模实战 简介这是一套围绕学生校园消费行为分析题的Python建模项目专为高校期末大作业、课程设计等场景打造。资源包共6个文件包含5个Python脚本和1个原始数据压缩包脚本按照不同任务模块编写如基本数据探查、消费特征提取、关联分析或预测建模等下载解压后无需额外修改即可运行并可直接作为答辩材料的基础工程。整个zip体积仅4.89MB目录层级简洁便于按任务顺序理解代码逻辑。目前已有618人学习下载是同类作业中热度较高的参考模板。读者可获得一整套可复用的分析流程框架、数据预处理与建模代码、任务拆解思路及结果展示方式尤其适合希望快速搭建项目骨架、降低排错成本的同学。1. 校园消费行为分析到底在做什么先把期末大作业的验收点摆出来晚上十点半的食堂刷卡机前排着刚下晚课的学生一张一卡通流水背后是几十万条可以建模的数据。这份期末大作业最容易踩的误区是以为把数据丢给 sklearn跑出一个聚类结果就完事。真实的验收点有三个——能不能把原始流水清洗成一张可分析的特征表能不能用合适的建模方法分出有业务含义的人群以及能不能在答辩现场十分钟内把「数据—特征—模型—结论」这条链路讲清楚。这里的 Python 建模落点不是比拼准确率而是比拼可解释性适合正在做数据分析课设、数学建模入门、或者想认真完成期末大作业的同学。2. 数据清洗与特征工程把一卡通流水变成能建模的二维表2.1 拿到原始流水后先别急着建模先检查字段和样本校园一卡通消费流水在不同学校的导出格式略有差异但核心字段基本稳定卡号或学号、交易时间、交易金额、商户名称、商户类型、收支标志。最好的习惯是先用 Excel 或 pandas 打开前 100 行肉眼看一遍数据长得什么样而不是直接开始写聚合代码。曾经有人拿到数据后直接按卡号分组求均值结果发现「人均消费」只有 0.15 元一查才知道有大量刷卡充值记录混在消费记录里充值金额都是整百把均值拉低了。我一般会先写一段极简的检查代码输出数据的行数、唯一卡数、时间范围和金额分布这几个指标能快速暴露数据集的真实规模和分析难度。重点是看三段信息交易时间是不是统一格式、金额列有没有负数、同一张卡同一秒同一商户是否存在重复记录。这三类问题如果不处理后续所有统计口径都会偏而且偏得悄无声息。import pandas as pd df pd.read_csv(card_flow.csv, parse_dates[trade_time]) print(总记录数:, len(df)) print(唯一卡号数:, df[card_no].nunique()) print(时间范围:, df[trade_time].min(), -, df[trade_time].max()) print(金额描述:) print(df[amount].describe()) print(负数金额条数:, (df[amount] 0).sum()) print(重复记录条数:, df.duplicated().sum())这段代码先读数据再把交易时间解析为 datetime 类型。nunique 用于数出去重后的卡号数量duplicated 用来判断全字段重复的行有多少。输出结果之后你会对本次建模的数据形态有个整体印象数据量是几万还是几十万时间跨度是三个月还是一整年负数是偶尔几笔还是大面积存在。先看全貌再动手避免后续在错误假设上堆代码。2.2 处理退款负数、充值记录和重复刷卡常见的坑有两个方向。第一个方向是负金额食堂窗口操作失误后的退款、超市退货、洗衣房余额退回都会造成负数交易。如果分析目标是「消费行为」我建议把负数交易单独拎出来只在退款分析里用不混进消费频率和消费金额的计算。第二个方向是充值记录一卡通充值本质是转账不是消费通常交易类型字段里会有「充值」「消费」的标记没有标记的话可以用商户类型或者金额规律来过滤。删除或标记这些记录后再处理重复刷卡。有些食堂闸机数据会同时写入两条相同记录时间戳精确到秒且金额一样这种情况直接用 drop_duplicates 删掉。但要注意如果刷完卡发现有退款退款记录和原消费记录成对出现此时不能简单按全字段去重否则会把正常的一进一出也删掉。处理时我通常只对「卡号 商户 时间 金额」四字段完全相同的行去重保留第一次出现。df df[df[amount] 0].copy() # 先排除退款和充值只看正向消费 df_clean df.drop_duplicates( subset[card_no, merchant, trade_time, amount], keepfirst ) print(清洗后记录数:, len(df_clean))这里的逻辑是先假设金额大于 0 都是消费需要你确认自己的数据里有没有负的消费、有没有正数充值。drop_duplicates 的 subset 参数非常关键它指定了「按哪些列判断重复」避免把同一时间在同一个商户买两笔相同金额商品的正常行为误删。keep 参数用 first 保留第一条在时间序列场景下等于默认选择先发生的记录。2.3 时间特征拆分星期、小时、早晚高峰一卡通流水里的交易时间是最有建模价值的字段它决定了你能不能在报告里画「一天消费曲线」和「一周消费热力图」。清洗完成后我会把时间拆成三个新列小时、星期、是否为工作日。小时用来判断早餐、午餐、晚餐、夜宵的高峰分布星期用来分析周末和上学日的差异工作日标记则帮助后续分群时识别作息规律。dt df_clean[trade_time] df_clean[hour] dt.dt.hour df_clean[weekday] dt.dt.weekday # 0周一, 6周日 df_clean[is_workday] df_clean[weekday].apply(lambda x: 1 if x 5 else 0)dt.hour 和 dt.weekday 是 pandas 的 datetime 访问器效率高且不容易写错。weekday 范围是 0 到 60 代表周一6 代表周日。is_workday 是自定义规则默认周一至周五算工作日。如果你的学校周六还有课或者周五下午就放假这个标记要按自己学校的校历调整不能照抄默认值。2.4 构建「以人为单位」的消费特征表建模时最常用的主体是「人」也就是每张卡对应一行特征。把流水按卡号分组统计消费总金额、总次数、平均金额、消费活跃天数、高频时段等字段最终得到一张宽表。这张表就是后续 KMeans、DBSCAN 的输入。daily df_clean.groupby([card_no]).agg( total_amount(amount, sum), total_count(amount, count), avg_amount(amount, mean), active_days(trade_time, nunique), avg_hour(hour, mean), ) daily[count_per_active_day] daily[total_count] / daily[active_days] print(daily.head())groupby 后面的 agg 是 pandas 推荐的聚合写法每一行指定新列名、原始列和聚合函数。total_amount 是总消费额total_count 是总刷卡次数active_days 是去重后的活跃天数用 nunique 统计。avg_hour 是消费时间的均值它有一个隐患跨午夜的作息会被平均到奇怪的位置比如晚上 23 点和凌晨 1 点的平均会得到 12 点。这种情况我用「圆形均值」处理或者干脆把它换成前三个固定时段的消费占比。2.5 衍生特征三餐占比、夜间消费比例、消费熵基础特征只能描述消费多少不能描述消费习惯。期末答辩时最能打动老师的是「这个簇的人早餐比例明显高」而不是「这个簇平均消费 12.3 元」。因此我额外构建三类衍生特征。第一类是三餐和夜宵占比用小时区间切分。第二类是夜间消费比例把 23 点到次日 5 点定义为夜宵时段统计夜宵金额占总金额的比例。第三类是消费熵衡量一个人刷卡金额的分散程度——如果一个人每顿都是 10 元整熵很低如果忽而 3 元忽而 40 元熵很高说明饮食结构波动大。def meal_type(hour): if 6 hour 10: return breakfast elif 10 hour 15: return lunch elif 15 hour 20: return dinner elif 20 hour 24 or hour 5: return night else: return other df_clean[meal] df_clean[hour].apply(meal_type) meal_cross pd.crosstab(df_clean[card_no], df_clean[meal], normalizeindex) def entropy(series): p series[series 0] return -(p * np.log(p)).sum()import numpy as np from scipy.stats import entropy as scipy_entropy def cal_entropy_by_group(group): val_counts group[amount].value_counts(normalizeTrue) return scipy_entropy(val_counts)第一个函数按小时区间把每笔交易归入早餐、午餐、晚餐或夜宵。pd.crosstab 配合 normalizeindex 得到每张卡在不同餐段的消费次数占比。第二个函数计算金额分布的熵我直接调用 scipy.stats.entropy它接受概率分布数组并返回非负熵值。熵越大代表不同金额的分布越分散越小代表消费金额集中在少数几个固定档位比如总是刷 8 元和 12 元套餐。2.6 标准化为什么用 StandardScaler不用 MinMaxScaler特征表建好后量纲差异非常明显消费总金额可能是几千早餐占比只有 0.1。如果直接把原始值喂给聚类算法金额会主导距离计算。常见做法是做标准化。StandardScaler 会把每个特征变成均值为 0、方差为 1 的分布适合 KMeans 和 DBSCAN 这种基于距离的模型。MinMaxScaler 会把数据压缩到 0~1 区间问题在于它很容易被极端值拉偏而校园消费里炒饭、夜宵或校外大额支出很容易产生极端值。from sklearn.preprocessing import StandardScaler feature_cols [total_amount, total_count, avg_amount, active_days, count_per_active_day, breakfast_ratio, dinner_ratio, night_ratio, entropy] X daily[feature_cols].fillna(0) scaler StandardScaler() X_scaled scaler.fit_transform(X)fillna(0) 是因为有些卡可能没有夜宵记录占比为空。fit_transform 在这段代码里直接完成了两步操作fit 计算均值和标准差transform 应用标准化。等到建模阶段你只需把 X_scaled 传入聚类算法。注意保留原始特征表 daily后面画图、打标签、写报告都需要它而不是直接使用标准化后的矩阵。3. 建模方案KMeans、DBSCAN、孤立森林怎么选3.1 先定目标再选模型不要先跑代码很多期末作业翻车的原因是拿到数据就开始找聚类代码跑完却讲不清每一类代表什么。建模之前必须回答一个问题这次分析到底要得出什么结论如果你的题目是「分析学生消费行为」最合理的目标是分群画像把学生分成规律干饭型、高消费夜宵型、低消费节俭型等若干类。如果你的题目是「识别异常消费」那应该用异常检测模型而不是聚类。如果你的题目是「根据前两个月消费预测后一个月」那就进入回归或时间序列的范畴不是同一套流程。在这份校园消费分析作业里最常见也最容易讲清楚的是「聚类 画像」。KMeans 能分出消费水平不同的群体每个群体的特征中心就是画像。DBSCAN 则适合发现不规则形状的群体还有机会把「每天只刷一顿饭」的极端群体标记为噪声点。孤立森林不是聚类它的用途是识别偏离正常消费模式的人。建议把它们放在同一份作业里分别回答「共性分群」和「异常个体」两个问题报告结构会很完整。3.2 聚类数怎么定肘部法则和轮廓系数KMeans 需要手动指定簇数 k这个 k 不能拍脑袋。业界用的两个指标分别是 SSE 肘部法则和轮廓系数。SSE 是簇内样本到中心点的距离平方和k 越大 SSE 越小但下降到某个点后收益骤减那个拐点就是肘部。轮廓系数则衡量每个样本和自身簇的相似度同类别紧凑、不同类别分离时系数高范围是 -1 到 1越接近 1 越好。from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score sse [] sil [] k_range range(2, 9) for k in k_range: km KMeans(n_clustersk, random_state42, n_init10) labels km.fit_predict(X_scaled) sse.append(km.inertia_) sil.append(silhouette_score(X_scaled, labels)) for k, s, si in zip(k_range, sse, sil): print(fk{k}, SSE{s:.1f}, silhouette{si:.4f})KMeans 的计算受初始中心点影响所以每一次迭代都指定 random_state42保证结果可复现。n_init10 表示用 10 组不同的初始中心运行算法最后返回 SSE 最小的一组避免随机性带来翻车。silhouette_score 在 X_scaled 上计算取值越大说明簇与簇之间分离越好。我的习惯是优先看肘部拐点再看 silhouette 是否超过 0.25如果超过 0.5 反而要警惕特征是否过于简单比如只靠金额一列劈成了两半。3.3 用 KMeans 跑通最小建模流程并打上人群标签确定 k 之后就可以跑最终的聚类。以 k4 为例把聚类结果合并回原始特征表就能看到每一类人的消费规律。这里还要注意一点KMeans 聚类是按距离切分的它不知道食堂、超市、奶茶店的业务含义打完标签后必须回看特征均值给每个簇起一个人类看得懂的名字。k 4 km KMeans(n_clustersk, random_state42, n_init10) cluster_labels km.fit_predict(X_scaled) daily[cluster] cluster_labels cluster_profile daily.groupby(cluster)[feature_cols].mean() print(cluster_profile.round(2))fit_predict 一次性完成训练和预测返回每个样本的簇编号。groupby 后取均值得到每类人的平均消费金额、平均活跃天数、早餐占比等。看到数据后再命名cluster 0 如果平均金额高、活跃天数多、夜宵占比高就命名为「活跃夜宵型」cluster 1 如果总金额低、早餐占比高、频次少就命名为「规律早餐型」。命名是报告的点睛之笔不要直接写「群组 0」。3.4 DBSCAN 的取舍噪点本身就是一种结果KMeans 会把每个样本都分到一个簇哪怕这个样本和其他任何样本都不像。DBSCAN 则会把这些样本直接标记为噪点也就是标签 -1。在校园消费场景里这很有价值低活跃、偶发消费、作息不规律的学生会在 DBSCAN 中被暴露出来。不过 DBSCAN 对参数非常敏感两个参数 eps 描述邻域半径min_samples 描述成为核心对象所需的最少样本数。from sklearn.cluster import DBSCAN db DBSCAN(eps0.8, min_samples10) db_labels db.fit_predict(X_scaled) n_clusters_db len(set(db_labels)) - (1 if -1 in db_labels else 0) print(DBSCAN 发现簇数:, n_clusters_db) print(噪点数量:, (db_labels -1).sum()) daily[dbscan_label] db_labelseps 表示如果某个样本的邻域内至少有 min_samples 个样本它才会被当作核心点。eps 设太小会把群体拆得零碎设太大又把所有人都划进一个大簇。对于标准化后的数据我一般把 eps 设在 0.5 到 1.2 之间min_samples 设在 10 到 20。这里的技巧是先跑一次看噪点比例是否在 5%~15%噪音比例太高说明参数过紧可以立刻调整。3.5 用孤立森林找异常消费不需要任何标签孤立森林是「无监督异常检测」算法它不依赖标签而是随机切分特征空间异常点因为分布稀疏只需要很少的切分次数就能被孤立出来。校园消费场景中它可以识别刷卡金额异常大、消费频率异常低或夜间消费异常多的人。这里要特别强调异常不等于坏人可能是校外人员借卡、访客临时消费也可能是有学生因病长时间不在校。from sklearn.ensemble import IsolationForest iso IsolationForest(contamination0.02, random_state42) anomaly_labels iso.fit_predict(X_scaled) daily[anomaly] anomaly_labels print(daily[anomaly].value_counts())contamination 参数表示你预估的数据集中异常值比例这里设 0.02 也就是假设 2% 的人消费模式异常。fit_predict 返回 1 表示正常-1 表示异常注意方向和聚类标签正好相反写报告前容易看反。IsolationForest 的核心优点是快几十万行数据也只需要几秒钟。缺点是结果不好解释它不会告诉你为什么异常需要回到原始特征表逐个看异常样本的数值。3.6 验证聚类不是「随机碰运气」聚类模型的验证是期末答辩时最容易被人追问的环节。老师说「你这个聚类结果会不会换一组初始值就完全变了」如果你答不上来前后就会陷入被动。我通常做两个验证第一个是稳定性验证把 random_state 改成其他值多次跑 KMeans比较每个簇的人数比例是否保持稳定第二个是样本验证随机抽取 80% 的样本重新聚类再把剩余 20% 预测到最近的簇中心观察整体分布是否一致。from sklearn.model_selection import train_test_split X_train, X_test train_test_split(X_scaled, test_size0.2, random_state7) km_temp KMeans(n_clusters4, random_state0, n_init10).fit(X_train) train_labels km_temp.predict(X_train) test_labels km_temp.predict(X_test) print(pd.Series(train_labels).value_counts(normalizeTrue).sort_index()) print(pd.Series(test_labels).value_counts(normalizeTrue).sort_index())这里用 train_test_split 把样本切分成 80% 训练、20% 测试。predict 方法会把新样本分配到距离最近的中心。如果两次的人群占比差距在 3% 以内说明结果不是偶然。如果差距很大优先检查特征表是否被少量极端值主导。4. 期末大作业避坑指南5 条踩过的数据与建模坑4.1 时间字段解析失败按天聚合结果全是 NaN现象用 pd.to_datetime 转换交易时间后发现小时列全部变成 NaN按天聚合后图像一片空白。 原因原始数据里时间格式不统一有的行是「2025-03-12 12:30:45」有的行是「2025/03/12 12:30」甚至还有「2025年3月12日」。to_datetime 无法自动识别所有格式解析失败的行变成了空值。 解决先用 errorscoerce 强制转换标记解析失败的行再检查失败样本的时间格式写一个自定义解析函数补齐。最简单的方式是让 to_datetime 的 format 参数明确指定格式。不要相信 Excel 导出的时间是「标准格式」必须有这一层检查。4.2 退款负数拉低均值消费金额变成负的现象早餐平均消费是 -3.2 元食堂抢了学生的钱。 原因退款记录被当作消费纳入聚合负金额抵消了正金额。 解决把金额列拆成正负两套逻辑。如果只做消费分析直接保留 amount 0需要研究退款时把负数的绝对值拿出来单独分析。注意充值记录通常是正数如果不按交易类型区分也会混进消费金额。建议先看交易类型字段的值分布再决定过滤条件。4.3 去重把正常购买记录误删了现象同一张卡在同一家店买了两次相同金额的饮料时间相差 1 秒结果被全字段去重删除。 原因没有指定去重列直接用 drop_duplicates() 把「卡号 商户 时间 金额」四列完全相同的记录当成了重复数据。其实一秒钟刷两次卡是合理的人类行为比如一次刷卡本人消费一次帮同学带饭。 解决只对高频重复类型做去重判断标准是「同一秒内同一商户同一金额且同一卡号」。如果数据里有交易流水号字段优先用流水号去重。没有流水号时subset 要加上三个以上维度避免误删。4.4 卡号字符串混进特征聚类结果被「身份证号」支配现象聚类出的四个簇分别对应卡号开头四个数字段业务上毫无意义。 原因把 card_no 或学号列当成了数值特征标准化后进入聚类模型。KMeans 基于欧氏距离卡号中任何一点数字差异都会被当成距离导致聚类完全偏离消费行为。 解决第一步是删除卡号、学号、姓名等身份标识列第二步检查特征表里是否有其他高基数列比如商户名称。需要用独热编码处理商户偏好字段或者直接不把商户名称作为聚类输入改用它构造「食堂消费占比」这类聚合特征。4.5 轮廓系数很高但人群画像完全解释不通现象k2 时轮廓系数高达 0.6看起来聚类效果很好但打开画像发现一群人全是总消费金额极高的夜宵大户另一群人全是几乎不刷卡的低频用户中间人群被强行切成了两半。 原因特征中存在极端值KMeans 对这种尖峰分布极其敏感少数高消费样本把整个簇中心拉走了。轮廓系数衡量的是几何分离程度不判断业务合理性。 解决先对特征做裁剪比如 95% 分位数截断极端消费金额或者使用分位数变换替代标准化。更重要的是必须回看每个簇的人数比例。如果人数最少那一类只占 2%它更可能是噪声而不是人群此时通过 W 检验调整 k 或改用 DBSCAN 处理离群点。5. 可视化与结果报告让期末答辩在 10 分钟内有底气5.1 三种能在一页 PPT 上讲清楚的图数据建模的成果需要用图形呈现评卷老师没有耐心一行行看 DataFrame。我通常画三张图第一张是所有学生的分时段消费折线图展示早中晚高峰第二张是周消费热力图横轴星期、纵轴小时用颜色深浅表示消费次数第三张是每个聚类小组的平均日消费金额分布图用来突出分群差异。import matplotlib.pyplot as plt import seaborn as sns plt.rcParams[font.sans-serif] [SimHei, PingFang SC, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False fig, axs plt.subplots(1, 3, figsize(16, 4)) hourly_count df_clean.groupby(hour).size() axs[0].plot(hourly_count.index, hourly_count.values, markero) axs[0].set_title(分时段消费次数) pivot df_clean.pivot_table(indexhour, columnsweekday, valuesamount, aggfunccount).fillna(0) sns.heatmap(pivot, axaxs[1], cmapYlOrRd) axs[1].set_title(每周消费规律热力图) daily_group daily.groupby(cluster)[total_count].mean() daily_group.plot(kindbar, axaxs[2], color[#4C72B0, #55A868, #C44E52, #8172B2]) axs[2].set_title(各簇平均消费次数) plt.tight_layout()先设置中文字体否则图表里的中文会变成方块这是 matplotlib 默认字体导致的。分时段曲线把「早中晚三个高峰」一句话讲完。热力图的 x 轴是周一到周日y 轴是 0 到 23 点能直观看到周末上午消费明显后移。第三张柱状图用于展示各簇画像差异柱子的高度对比就能支撑「人群可分」的结论。5.2 用表格把人群画像输出成报告素材图像适合口头表达书面报告里还需要一张精确的表格。我一般会把每个簇的核心特征整理成这种格式簇标签、自定义人群名、人数占比、平均日消费金额、平均日活跃次数、早餐占比、夜宵占比。这张表可以直接粘贴到期末报告的模型结果部分也能在答辩被追问时快速定位到对应数据。簇人群标签人数占比日均消费(元)日均次数早餐占比夜宵占比0规律早餐型28%9.62.142%5%1活跃夜宵型19%21.33.49%38%2节制消费型36%6.81.231%11%3周末集中型17%13.51.614%20%表格里的值来自 cluster_profile 的聚合结果不要手工填数也不要先把报告写好了再倒推数据。如果发现某种类型的占比太小比如只有 2%说明这个簇可能是异常值簇应该回去调整模型参数而不是硬搬进报告。5.3 报告结构抽掉代码也能自洽期末报告吓退同学的往往不是分析而是不知道按什么顺序写。我建议直接按项目流程组织第一部分写背景与目标说明为什么用校园消费数据做建模想解决什么问题第二部分写数据来源与预处理包括字段说明和清洗流程第三部分写特征工程解释你选择了哪几个特征、为什么没有选别的第四部分写模型选择与参数确定第五部分写分群结果与人群画像最后一段写对学生消费管理和个性化服务的建议。其中第三部分最容易被一笔带过。实际上老师最看重的是特征构造思路。你可以写「用 pandas 从原始流水中拆出小时、星期、餐段三类时间特征」也可以展开说明「消费熵衡量了消费习惯的稳定性」后者更能体现建模思考。但如果你的作业要求里没有要求写建议那最后一段不写也没问题不用为了凑篇幅硬编对策。5.4 被老师问「为什么选 KMeans」时这样回答不慌张答这种问题讲究「性能 业务」双线。先说业务这次分析的目的是给全校学生做消费人群分层KMeans 的每个簇可以用中心点描述天然适合输出画像。再说性能KMeans 在大样本下速度快算法复杂度接近线性咱们学校这条数据集有二十多万条流水跑一遍只需要几秒钟。更重要的是它只有 k 一个超参数调参路径清晰不像 DBSCAN 的 eps 和 min_samples 需要反复试。如果老师追问为什么不用 GMM 或层次聚类可以说 GMM 假设每个簇服从高斯分布校园消费金额分布实际上偏态明显这个假设不一定成立层次聚类的计算量随样本数增长较快面对几万张卡的特征表不划算。回答时不需要推倒重来只要把 KMeans 的优势和你对数据分布的观察讲清楚即可。6. 收尾一步用一个月做稳定性验证让报告再上一个等级期末作业交完后真正拉开差距的是验证部分。我最后一件事永远是「留一个月数据当验证集用前几个月的参数复跑一次」。具体操作是把整个数据集按月份切分比如用 3—5 月数据建模再用 6 月数据计算每个样本到已得簇中心的距离重新分配簇标签最后对比两轮聚类人数比例。如果一个簇的人数比例从 28% 跳到 64%说明建模只抓住了月份效应而不是消费习惯需要回到特征设计去加入「学期前半段 / 后半段」标记。另一个值得做的扩展是特征重要性验证。把聚类标签作为目标变量用随机森林训练一个分类器看哪些特征贡献度最高。如果早餐占比排第一说明你的分群主要靠早餐习惯切分报告里就要突出描述这个特征如果消费总金额排第一则要承认这是个「消费水平分层」模型而非「作息规律分层」模型。这一步不复杂却能让报告里所有结论都有依据。我当年做这份作业时最深的教训是急着跑结果跳过验证直接写结论。答辩时老师对着投影仪说「你把 5 月的数据抽出来跑一遍」结果簇分布完全变了当场冷场。后来我养成了先切一个月数据做稳定性验证的习惯再小的分析也按这个流程走。希望帮到你这 10 分钟会让你在讲台上的语气跟别人不一样。本文还有配套的精品资源点击获取
返回列表