Python电商数据分析与随机森林销量预测全链路实战
做毕设这么多年带过不少人我发现在计算机类的题目里“电商数据分析销量预测”是最容易出活儿、也最容易翻车的一类。容易出活儿是因为方向明确谁都知道要做爬虫、要做模型、要做展示容易翻车是因为很多同学把这几个模块割裂着做爬虫跑完导出一份CSV画图脚本单独跑一次模型训练再看一眼准确率最后硬拼成一个系统答辩时一问整体链路就露馅。而“Python电商全维度商品数据分析与随机森林销量预测系统”这个题目恰好要求你把整条链路从数据采集、清洗、建模到可视化展示、Web系统集成全部打通是一个真正能体现工程能力的复合型选题。这篇文章我会从项目整体拆解开始把我实际做这套东西时的技术选型思路、关键代码实现、参数调节过程、以及那些文档里不会写的坑按真实开发的顺序完整过一遍。无论你是拿它当毕业设计还是想做一个能写进简历的数据分析项目这条链路都有很强的参考价值。1. 项目概述与整体设计思路1.1 核心需求与技术关键词拆解这个项目标题里的几个关键词不是随便堆的每拆一个出来都对应着一块具体的技术活“电商全维度商品数据分析”指的是数据维度不能只盯着价格或者销量至少要覆盖商品标题、价格区间、累计销量、评论数、店铺评分、上架时间、品牌/品类等多个维度。只有维度够全后续的分析和建模才有深度。“随机森林销量预测”是整个项目的模型担当目标是根据已知的商品特征和销售历史预测未来一段时间内的销量区间或销量值。“Django可视化”是把分析结果和预测结果整合成Web页面让用户能够在浏览器里交互查看报表和数据图表。“爬虫和数据分析”是数据来源与核心处理手段对应着数据采集和数据预处理两个阶段。“大模型”这个标签在毕设里更多是锦上添花可以把它用在商品评论的文本摘要、推荐理由的自然语言生成上或者直接作为将来的扩展方向不作为系统的依赖核心。有一个很重要的判断这个项目的核心主线是“数据质量→特征工程→模型效果”而不是“用什么高级算法”。随机森林之所以是这个题目的优选方案是因为它对于电商这类表格型数据几乎无需做复杂的特征缩放对异常值容忍度高还能输出特征重要性方便你在答辩时解释“为什么预测结果是这样”。1.2 技术选型与方案对比我实际操作下来技术栈的搭配直接决定了你后期写代码的舒适度模块推荐方案备选方案选择理由数据采集Scrapy RequestsBeautifulSoup requestsScrapy支持并发抓取和断点续爬数据量大时优势明显数据存储MySQLSQLite / CSVMySQL方便Django ORM直接读取事务和索引都更成熟数据分析Pandas NumPy纯Python处理Pandas的DataFrame操作是表格型数据分析的标准工具模型算法scikit-learn随机森林XGBoost / LightGBM随机森林参数少、调参空间直观新手也能拿到不错效果可视化ECharts pyechartsChart.js / MatplotlibECharts交互效果好适合嵌入Django模板展示Web框架DjangoFlask 单独前端Django自带ORM、Admin、模板系统全套功能开箱即用这里我想多说一句为什么不用深度学习做销量预测。电商销量预测本质上是结构化数据的回归/分类问题表格数据里的特征之间并没有明显的空间或时序关联深度学习模型在这种场景下不仅训练成本高而且调参难度大。随机森林作为集成学习中的Bagging代表通过多棵决策树投票/平均来降低过拟合风险在几千条到几万条商品数据量级上表现非常稳定。我自己实测下来在样本量不到2万条时随机森林的预测效果和XGBoost差距极小但代码量和对新手友好程度高出一大截。1.3 系统整体架构与数据流向整个系统的架构从数据流的角度看非常清晰我也建议你按这个顺序去搭建代码而不是按功能模块并行开发爬虫模块抓取电商商品数据写入MySQL商品主表。数据清洗脚本对缺失值、重复值、异常值做处理生成分析宽表。特征工程脚本从宽表中构造销量预测所需的特征集和标签列。模型训练脚本输出随机森林模型文件.pkl或.joblib同时输出特征重要性分析结果。Django应用读取MySQL中的数据页面通过ECharts展示多维分析图表和预测结果。预测功能通过调用训练好的模型文件对用户选择的商品特征实时预测销量。这个架构最大的好处是每个环节的产物都是独立的数据是数据、模型是模型、Web是Web。如果某个环节出问题比如爬虫被封IP不需要动到其他模块替换数据源重新跑一遍清洗和训练就行。我在实际做的时候刻意把爬虫输出和模型训练通过数据库解耦开后期换数据源、加数据维度都是直接改配置基本不动核心代码。2. 系统环境搭建与项目初始化2.1 开发环境与依赖清单这个项目的环境搭建并不复杂但有几处细节必须提前处理好不然后面会反复折腾。我推荐使用Python 3.9或3.10版本。太老的版本对Django 4.x的高版本支持不好太新的3.12在某些依赖库上可能出现源码编译问题尤其是Windows下安装scikit-learn或pandas时。用conda创建独立环境最省心conda create -n ecommerce python3.10 conda activate ecommerce核心依赖清单如下pip install django4.2 pip install scikit-learn1.3.2 pip install pandas2.0.3 pip install numpy1.24.3 pip install scrapy2.11 pip install pymysql pip install pyecharts2.0.5 pip install matplotlib3.7.2 pip install joblib这里有个版本坑值得提前说明pyecharts 2.x和Django模板的兼容性比1.x更好但2.x的API变化较大网上的旧教程大多是基于1.x写的如果你看到类似pyecharts.charts import Bar的写法实际上2.x已经是这样了而pyecharts.Chart这种写法是1.x的不要混着用。2.2 Django项目结构规划Django项目我建议按下面的目录结构来组织这种结构把“数据分析”和“Web展示”清晰地分开答辩时讲起来也顺ecommerce_system/ ├── manage.py ├── data_analysis/ # 数据分析与建模模块 │ ├── crawler/ # 爬虫代码 │ ├── preprocessing/ # 数据清洗与特征工程 │ ├── model_training/ # 模型训练与评估脚本 │ └── output/ # 模型文件与结果图表输出 ├── goods_analysis/ # Django主应用 │ ├── views.py # 视图函数 │ ├── urls.py # 路由配置 │ ├── models.py # ORM模型 │ └── templates/ # 前端模板 │ └── index.html └── db.sqlite3 / mysql配置一个容易忽略的点是Django的models是给Web系统查询展示数据用的它不一定需要覆盖原始数据的全部字段。我在实际项目里只把分析结果、统计聚合表、预测记录这三类数据纳入Django的ORM管理原始商品明细仍然放在MySQL的独立表里两者通过商品ID关联。这样做的好处是Web层轻量、查询快而且数据分析和Web开发可以并行推进。2.3 数据库设计与关键表结构数据库表设计是整个系统的基础设计得好后期能省很多事。核心表我分成了三张商品明细表存储爬虫采集的原始商品信息主要字段包括商品id、标题、类目、品牌、价格、累计评论数、销量、店铺名、店铺评分、上架时间、采集时间等。特征宽表存储清洗和特征工程后的建模数据包含价格区间编码、评论数区间、店铺评分、是否自营、类目编码、目标销量等。预测记录表存储用户在网页端提交预测请求时生成的结果记录字段包括用户输入的特征快照、预测结果、预测时间。这里要强调一个设计原则特征宽表不要和原始明细表混在一起。原始表的数据是“脏”的哪怕你清洗过也尽量不要动它因为你后续随时可能需要回溯排查数据问题。特征宽表是从原始表处理得到的建模专用数据它可以随时用清洗脚本重建保证可复现性。我自己在初期图省事直接在原始表上加了特征列结果清洗脚本跑两次字段就乱套了后来才拆开重做。3. 数据采集与预处理实战3.1 爬虫模块的实现要点与合规注意事项爬虫是这个项目的数据源头。我在实现时用的是Scrapy框架针对某电商平台公开的商品搜索页进行抓取。核心逻辑是构造搜索请求URL带上关键词和分页参数。解析返回的HTML或JSON接口提取商品标题、价格、评论数、店铺信息等字段。数据存入MySQL同时去重和增量更新。下面是一个简化版的Item定义和爬虫示例实际字段按你的需求扩展import scrapy class GoodsItem(scrapy.Item): goods_id scrapy.Field() title scrapy.Field() price scrapy.Field() comment_count scrapy.Field() shop_name scrapy.Field() shop_score scrapy.Field() category scrapy.Field() url scrapy.Field()爬虫写起来不难难的是稳定抓取和目标平台的限制。这里分享几个我实测有效的经验请求频率必须控制建议每个请求间隔1秒以上并发数控制在2-4之间。千万不要为了追求速度开高并发一单IP被限制整个采集进程都会中断。User-Agent和Cookie要模拟真实浏览器。只改UA还不够部分平台还会校验请求头顺序和TLS指纹如果你发现返回的数据字段明显缺失先检查是不是被识别成了爬虫。优先找页面里的JSON接口而不是解析HTML。电商平台的前端页面大多由异步接口渲染直接解析HTML不仅速度慢而且字段结构容易变。用浏览器的开发者工具抓包找到返回JSON数据的XHR接口解析效率和稳定性都高很多。必须提醒一点爬虫的使用要严格遵守目标平台的robots协议和服务条款只采集公开可见的信息控制合理频率不得用于任何商业用途或对目标平台造成压力。毕设项目里强调数据获取的合规性在答辩时也是加分项因为老师们其实很关注这一点。3.2 数据清洗的完整流程与关键操作爬下来的数据没法直接用这是所有数据分析人都懂的道理。我实际工作中最大的体会是清洗脚本的核心不是“怎么写”而是“每一步为什么这么做”。我的清洗流程分四步第一步是去重。同一商品可能被多个关键词的搜索结果重复抓取我以商品id为主键做去重保留采集时间最新的那条记录。这一步比较简单SQL里加个DISTINCT或者Pandas里drop_duplicates就行。第二步是缺失值处理。我在处理商品价格和评论数时发现缺失率最高的往往是最新款或预售款商品它们可能没有评论数。对于缺失值我采用的策略是价格缺失直接删除没有价格的数据无法用于价格区间分析评论数缺失用该类目评论数的中位数填充店铺评分缺失用全类目均值填充。用中位数而不是均值的原因是评论数分布极度右偏均值会被爆款商品拉高填充后会产生偏差。第三步是异常值处理。电商数据里有两种常见异常一种是价格异常比如某个商品价格标为0.01元这通常是凑单链接或测试商品另一种是销量异常比如一个链接同时刷了数千条评论评论数远高于正常水平。我的处理方式是定义一个合理区间超出区间上下限5倍的点直接剔除或者缩尾到边界值。实际操作里用Pandas的quantile方法很容易实现import pandas as pd df pd.read_sql(SELECT * FROM goods_detail, engine) # 价格异常处理剔除小于1元和大于类目95分位数的记录 for cat in df[category].unique(): subset df[df[category] cat] low, high subset[price].quantile([0.01, 0.95]) df.loc[(df[category] cat) (df[price] low), price] low df.loc[(df[category] cat) (df[price] high), price] high第四步是标准化。这一步需要把原始字段转为模型可用的数字特征我会在特征工程部分详述。3.3 特征工程从一个表到能用模型的宽表特征工程是整个项目里对预测效果影响最大的一步也是很多空有代码没有效果的项目最大的短板。我在这个项目里构造了以下几类特征价格类特征原始价格、价格/类目均价的比例、价格分位数区间编码。评论类特征评论数、评论数/销量的比值、评论数的对数变换。店铺类特征店铺评分、店铺评分与类目均值的差值。商品类特征标题长度、是否包含“旗舰店”“热卖”等词、类目one-hot编码。时间类特征上架距今天数、上架月份、是否在双11/618等大促周期内。为什么要做这么多特征因为随机森林虽然能处理非线性关系但它本质上是通过特征划分来学习的如果特征本身区分度不够再多的树也没用。举个例子直接用价格预测销量效果通常很差因为价格对不同类目的商品意义完全不同但当你把价格转换成“价格/类目均价比”这个相对特征后不同类目的商品就在同一个尺度上可比了这个特征的预测贡献明显提升。我建议你在建模前先用Pandas的corr()方法看看各个特征与销量之间的相关性把相关系数几乎为0的特征剔除掉这一步既能让模型更简洁也能让你在答辩时展示自己“有特征筛选意识”。4. 随机森林销量预测模型构建4.1 问题定义与评价指标选择在做模型之前先要把问题定义清楚。销量预测既可以做回归预测具体销量数值也可以做分类预测销量区间。我在实际做的过程中最终选择了回归方案因为分类需要手动切分销量区间区间边界的选择会影响结果的可解释性回归则直接给出预测数值便于直接和真实值对比。回归问题的核心评估指标是决定系数R²和均方根误差RMSE。R²代表模型解释了数据中多少比例的方差数值越大越好RMSE代表预测值和真实值之间的平均偏差数值越小越好。我当时定下的目标是R²不低于0.7RMSE控制在真实销量标准差的30%以内。为什么这么定因为电商销量数据本身的波动极大真要达到R²0.9以上需要非常丰富的时序信息和竞争信息这在毕设项目里不太现实。明确合理的指标预期避免后期为了追求数字好看而过度调参这是我想提醒所有做这个方向的人的一件事。4.2 数据集划分与训练流程数据划分上我用的是时间序列切分而非随机切分。这是很多人容易忽略的销量预测的场景下训练集和测试集如果随机打乱模型会用“未来的数据”预测“过去的数据”评估结果虚高但在真实应用里会翻车。正确做法是按时间切分取前80%时间范围内的数据作为训练集后20%作为测试集。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error import pandas as pd import joblib features [price_ratio, comment_log, shop_score_diff, days_since_online, title_length, promotion_flag] X df[features] y df[sales_volume] # 按时间排序后切分 df df.sort_values(online_days) split_idx int(len(df) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] model RandomForestRegressor( n_estimators300, max_depth20, min_samples_leaf3, random_state42 ) model.fit(X_train, y_train) y_pred model.predict(X_test) print(R2:, r2_score(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) joblib.dump(model, data_analysis/output/sales_model.pkl)这里的参数是我经过一轮初调后确定的。n_estimators设为300因为基学习器太少模型不稳太多训练时间不成比例增长max_depth设为20让树有一定深度来捕捉特征交互但不至于完全展开过拟合min_samples_leaf设为3相当于要求每个叶子节点至少3个样本才能继续分裂这个参数是抑制过拟合最有效的杠杆。4.3 超参数调优与特征重要性分析调参方面我没有用网格搜索全量跑因为组合空间太大耗时太长。我采用的是“先粗后细”的两轮策略第一轮先固定n_estimators200粗调max_depth在5、10、15、20、30中选一个用交叉验证观察R²的变化趋势。第二轮在选定max_depth的基础上调节min_samples_leaf在1、2、3、5中的取值。这样两轮跑完基本能找到稳定区间而且每轮10分钟以内能出结果比GridSearchCV动辄几小时的体验好太多。训练完成后一定要看特征重要性。随机森林自带feature_importances_属性直接输出各特征对预测的贡献占比。我跑出来的结果中评论数对数、价格占比和促销标志这三个特征贡献了大部分重要性这说明销量主要由商品的受欢迎程度、价格竞争力和促销力度驱动。这个结论本身就有分析价值在系统的“特征分析”页面里展示特征重要性柱状图答辩时老师问“为什么选这些特征”你可以直接拿这张图说话。需要注意的是特征重要性是相对值不是绝对值。它告诉你的是“在这个模型里哪个特征贡献大”而不是“哪个特征客观上更重要”。如果两个特征高度相关模型会把重要性分散到两者之间这是Bagging类模型的正常现象不必过于纠结。5. 可视化报表与Django系统集成5.1 可视化方案选型与图表设计可视化是这个项目的门面也是用户感知最强的部分。我选ECharts为核心解决方案原因很简单交互功能内置缩放、提示框、切换数据系列都不需要自己写JS事件中文本地化做得好导出图片方便答辩做PPT可以直接用系统截图。图表的设计要围绕“全维度分析”这个题眼展开我当时一共做了6类核心图表类目销量分布柱状图展示不同商品类目的销量总和排名一眼看出主营品类。价格区间与销量关系散点图横轴价格、纵轴销量、点大小代表评论数展示价格与销量的相关性。评论数与销量对应关系图用气泡图展示评论量级与销量水平的关系。商品标题词频柱状图统计标题中的高频词帮助理解用户搜索偏好。店铺评分分布箱线图展示主流店铺评分的集中区间和异常店铺分布。特征重要性排序图展示随机森林模型中各特征的贡献占比。这6类图表基本覆盖了“描述性分析”的全部模块配合预测功能形成了完整的闭环。5.2 Django视图与ECharts集成实现集成的技术路线其实不复杂Django视图从数据库读取聚合数据打包成JSON传给模板前端用ECharts渲染。关键是把数据查询逻辑写清楚不要在前端做复杂计算。# views.py 部分示例 from django.shortcuts import render from .models import CategoryStat def index(request): # 类目销量聚合数据 category_data ( CategoryStat.objects.values(category) .annotate(total_salesSum(sales_volume)) .order_by(-total_sales)[:10] ) categories [item[category] for item in category_data] sales [item[total_sales] for item in category_data] context { categories: categories, sales: sales, } return render(request, index.html, context)前端模板里把数据塞进ECharts的配置项var chart echarts.init(document.getElementById(chart1)); chart.setOption({ title: { text: 类目销量TOP10 }, xAxis: { type: category, data: {{ categories|safe }} }, yAxis: { type: value }, series: [{ type: bar, data: {{ sales|safe }}, itemStyle: { color: #5470c6 } }] });这里有个Django模板的坑我第一次踩到时很不解把列表变量直接传给ECharts的data数组时需要用|safe过滤器否则Django模板系统会把方括号转义成HTML实体图表只显示空白。数据量不大时也可以直接在视图里用json.dumps序列化好再传入。5.3 销量预测功能的前后端联调预测功能的实现逻辑是用户在前端页面选择或输入商品特征表单提交后Django视图读取请求参数经过和训练时完全相同的特征转换调用模型文件执行预测返回结果并存储到预测记录表。特征转换的一致性特别重要这是实战中最容易踩的雷。训练时你用的是“评论数对数”这个特征预测时用户输入的是原始评论数如果忘记做同样的对数变换模型输入分布就会和训练时不一致预测结果必然失真。我的解决方法是把特征转换逻辑抽成一个独立的函数训练和预测共用def build_model_features(raw_data): return [ raw_data[price] / category_mean_price[raw_data[category]], np.log1p(raw_data[comment_count]), raw_data[shop_score] - category_mean_score[raw_data[category]], (date.today() - raw_data[online_date]).days, len(raw_data[title]), is_promotion(raw_data[online_date]) ]这个函数是“一处定义、两处使用”的典型做法训练脚本和Django的视图函数都import它从根源上避免了前后逻辑不一致的问题。我见过不少项目在训练脚本里用一套特征变换在Web预测里用另一套甚至完全不做变换最后预测结果一塌糊涂基本可以断定就是这类问题导致的。5.4 页面布局与交互体验优化页面布局我采用典型的数据大屏风格顶部标题栏左侧放类目分析和价格分析两个图表右侧放评论分析和店铺评分两个图表中间主区域放销量预测模块。这种布局在答辩演示时很占便宜因为导师一眼就能看到项目的核心能力不用滚动页面找功能。交互上我做了两个细节一是图表tab切换让用户可以在“分析报表”和“预测功能”之间快速切换二是预测表单增加了“常见商品特征模板”下拉框用户选择“手机类目”等模板后自动填充典型参数一键出预测结果。这两个小改动不复杂但对整体作品完成度的提升非常明显。答辩时很多同学点开页面后不知道先干什么有交互引导就好很多。6. 常见问题与排查技巧6.1 环境与依赖相关的典型报错这个项目环境问题占了后端问题的很大比例。我把最常见的几类整理出来一是pandas和numpy版本兼容问题。如果安装时提示找不到numpy.core.multiarray多半是版本不匹配我用的是pandas 2.0.3配numpy 1.24.3这个组合经过验证是稳定的。还有新版本numpy合并C扩展后某些旧版pandas在Windows上会报module compiled错误解决方案就是像我上面那样锁定版本安装。二是pyecharts导入报错。pyecharts 2.x把底层渲染改成了单独包如果遇到pyecharts.render导入失败检查一下是否安装了simplejson和jinja2这两个是它的间接依赖。另外2.x版本中图表类的路径大多在pyecharts.charts下不要照抄1.x教程里的写法。三是MySQL连接问题。Django连接MySQL默认需要mysqlclient但在Windows上编译这个包经常失败。我的替代方案是用pymysql然后在项目__init__.py里加上import pymysql pymysql.install_as_MySQLdb()这样Django的ORM就能正常使用MySQL了。这个操作只影响启动时的数据库驱动加载逻辑不影响其他功能。6.2 爬虫与数据质量相关的坑爬虫方面最常见的坑就是抓取频率过高导致IP被限制。我一开始贪快设置了并发8、延迟0.5秒跑了半小时之后请求开始大量失败。后来我把延迟调到1.5秒、并发降到3稳定运行了两个小时都没问题。数据采集本来就是细水长流的活尤其是毕设数据量要求不高几千条有效数据完全够用没必要追求速度。另外一个隐蔽的坑是编码问题。电商平台的搜索接口返回的数据偶尔会有繁体字、表情符号或特殊字符直接写入MySQL可能在utf8编码下报错。我的处理方案是写入时统一转为utf8mb4编码Python侧读取时用errorsignore容错import pymysql conn pymysql.connect(hostlocalhost, userroot, password123456, charsetutf8mb4, databaseecommerce)同时在清洗阶段把文本字段里的非中英文字符统一剔除确保后续分析字段的干净度。6.3 模型效果不达预期的排查路径模型R²偏低是很多同学会来找我咨询的问题。根据我的排查经验按优先级从上到下检查第一条路径是数据质量。先查训练数据里是否混入了大量异常值或缺失值填充不当的记录尤其是评论数为0但销量极高的商品这种记录会严重干扰模型。我遇到过评论数为0、价格9.9元的商品月销8000多的特例最初模型总是预测偏低后来定位到是这类极端样本挤压了正常样本的空间。第二条路径是特征完备性。如果只有价格和评论数两个特征模型效果上限一定不高。至少要补充店铺评分、上架时长、促销标志这几个基本维度再往后可以加类目编码和标题文本特征。特征维度从5个加到10个之后R²明显提升了一个档次。第三条路径是参数过拟合或欠拟合。如果你发现训练集R²很高但测试集很低说明过拟合了把max_depth调小或把min_samples_leaf调大如果两边都低说明模型太弱或特征太少优先加特征而不是加树的数量。6.4 答辩演示时容易翻车的环节答辩演示环节我见过最多的翻车现场就是预测模块没有预置数据学生现场手动输入参数又正好输入了一个特征分布边缘的样本模型给出了一个看起来很离谱的预测值。我的经验是提前准备3个典型商品的复合特征作为预设模板演示时先点“手机类目模板”说明预测流程再手动改一个参数展示结果变化。这样既展示了功能又控制了输入范围。还有一个翻车点是图表加载缓慢。Django页面同时加载6张ECharts图表时如果每个图表的数据量过大首屏加载会非常卡顿。我的做法是每张图表的后端接口只返回TOP20的聚合数据或者在前端用dataZoom组件限制初始展示范围保证页面在2秒内能完整渲染。答辩时电脑性能不可控页面加载越轻越不容易出问题。写在最后从环境搭建到爬虫采集、数据清洗、特征工程、模型训练再到Django集成和可视化展示这一整套流程走下来我最大的感受是这个项目真正考验的不是某一个算法有多深而是你对整条数据处理链路的理解和工程实现能力。随机森林模型本身只是一个工具让这个项目出彩的是数据清洗时的细致程度、特征构造时的领域理解以及把分析结果用可视化和Web形式呈现出来的表达能力。即使在答辩时被问到“为什么不用深度学习”你也能有理有据地告诉老师在这个数据规模和业务场景下随机森林在效果、可解释性和工程复杂度之间取得了最好的平衡。如果后续你想继续扩展这个项目可以从两个方向入手一是接入大模型能力对商品标题做自动摘要或对评论做情感分析输出更丰富的文本维度特征二是引入商品销量时序数据改用Prophet或时序Transformer做短期销量预测和当前的特征化随机森林形成对比。那样的话这个项目就不仅仅是一个毕设而是可以真正作为数据分析完整案例写进作品集的东西了。