简介:直接上手的酒店行业数据分析实训材料,用Spark做订单量城市分布、热门房型TOP榜、各城市平均房价、每单平均入住人数等统计,每个分析任务都封装成独立Scala脚本(比如Spark_CountCity.scala、Spark_AvgPrice.scala),开箱即跑。清洗结果存进MySQL,已配好建库建表SQL脚本,兼容5.7和8.0版本。前端完全静态,index.html双击就能看图表,不用装服务器,用原生HTML+CSS+JS调ECharts实现柱状图、折线图、饼图等交互展示。包里含完整实训报告(Word)、教学汇报PPT、本地部署指南,还有清晰分类的静态资源文件夹(images、fonts、js、css)。开发环境按IntelliJ IDEA + Scala + Spark 3.x配置,所有代码带中文注释、模块职责分明,适合课堂演示、课程设计或刚入门的大数据项目复现。
1. 项目概述:为什么酒店行业需要这样一套“开箱即用”的分析实战包?
做酒店度假行业的数据分析,最常听到的不是“数据太多”,而是“数据太散、太慢、太难串起来”。销售系统里有订单时间、房型、价格;PMS(酒店管理系统)里有入住人数、房态、客人来源;财务系统里又有结算周期、渠道佣金、退款记录——三套系统字段不统一、时间戳格式不一致、甚至同一个城市在不同系统里写法都不一样(比如“上海市”“上海”“shanghai”混用)。我带过六届大数据课程设计,每年都有学生卡在第一步:光是把Excel、CSV、MySQL导出的三张表对齐字段、清洗空值、统一城市编码,就花掉整整三天,最后连柱状图都没画出来。这不是能力问题,是工具链断层——Spark跑得再快,没清洗干净的数据喂进去,结果就是“垃圾进、垃圾出”。
这套“酒店度假数据实战包”,本质上是一条被反复踩平的路。它不讲抽象的大数据架构图,也不堆砌Flink/Kafka这些高阶组件,而是聚焦一个真实业务闭环:从原始订单CSV出发,用Spark做四类核心统计(城市订单量分布、热门房型TOP5、各城市平均房价、每单平均入住人数),把结果存进MySQL,再用纯前端技术把数据“活”起来。关键在于,每个环节都做了最小化封装和最大兼容性设计:Scala脚本独立可运行,不依赖复杂调度平台;MySQL建库脚本自动适配5.7和8.0的严格模式差异;前端页面双击index.html就能加载ECharts图表,连本地HTTP服务器都不用装。你不需要先成为Spark专家,只要会解压、会改两行配置、会点鼠标,20分钟内就能看到上海、北京、三亚的订单柱状图跳出来。它解决的不是“能不能做”,而是“今天下午三点前能不能让老师看到效果”。关键词里的“Spark酒店分析”不是技术标签,是业务场景锚点——所有计算逻辑都围绕酒店运营的真实KPI展开;“MySQL建库脚本”不是简单CREATE TABLE,而是预置了索引优化、字符集声明、时间分区建议;“ECharts动态图表”也不是静态截图,而是支持鼠标悬停看明细、点击图例切换维度、缩放查看局部趋势的交互式看板。适合谁?刚接触大数据的学生、需要快速交付演示的教研组、想验证数据价值但没资源搭整套平台的中小酒店集团IT岗——一句话,它服务的是“人”,不是“技术”。
2. 整体架构设计与模块职责拆解:为什么选择这个技术栈组合?
2.1 技术选型背后的业务逻辑:不做炫技,只解痛点
很多人看到“Spark+MySQL+ECharts”第一反应是:“怎么不用Hive做数仓?怎么不接Vue做SPA?”——这恰恰是本项目刻意为之的设计哲学。我们拆解三个核心环节的选型理由:
Spark作为计算引擎,不是因为“它快”,而是因为“它稳且易调试”。酒店订单数据量级通常在千万级(单年全集团订单约800万-3000万条),远未到需要YARN集群调度的规模。Spark Standalone模式在一台16G内存的开发机上就能跑通全部脚本,且Scala语法天然贴近业务逻辑。比如计算“各城市平均房价”,原始脚本里一行df.groupBy("city").agg(avg("price").as("avg_price"))就完成聚合,比写MapReduce或HiveQL更直观。更重要的是,Spark本地模式(local[*])下,所有异常堆栈直接指向具体行号,学生调试时能立刻定位到是“城市字段为空导致groupBy失败”,而不是在YARN日志里翻半小时。我们放弃Flink,是因为酒店分析95%的场景是T+1离线统计(昨天的订单汇总),实时流处理反而增加运维复杂度。
MySQL作为存储层,不是因为“它传统”,而是因为“它够用且教学友好”。有人质疑:“分析数据不该用OLAP数据库吗?”——但教学场景中,学生第一次接触SQL,如果用ClickHouse,光是安装驱动、配置JDBC URL就要卡住半天。MySQL 5.7/8.0普及率高、客户端工具(Navicat、DBeaver)成熟、语法标准,学生用SELECT查出结果那一刻的成就感,远胜于折腾分布式数据库的权限配置。本包的建库脚本特别处理了两个关键兼容点:一是针对MySQL 8.0默认启用的sql_mode=STRICT_TRANS_TABLES,所有INSERT语句显式指定字段名,避免因NULL插入失败;二是为order_date字段添加B-tree索引而非全文索引,因为酒店分析中“按日期范围查询”频次远高于“模糊搜索”。这背后是经验:我见过太多学生因为索引建错,导致COUNT(*)耗时从0.2秒飙升到47秒,最后误以为是Spark性能问题。
ECharts作为可视化层,不是因为“它免费”,而是因为“它零依赖且文档极简”。前端不走Node.js构建流程,所有JS文件直接放在ui/js/目录下,index.html通过<script src="js/echarts.min.js"></script>引入。这意味着学生双击打开HTML时,浏览器控制台不会报“Failed to load module”——因为根本没用ES Module。ECharts的option配置采用JSON结构,比如柱状图只需定义xAxis: {type: 'category', data: ['上海','北京']}和series: [{type: 'bar', data: [1200, 850]}],对比D3.js需要手写SVG绑定,学习曲线陡峭度直接降低70%。更关键的是,ECharts官网提供“在线编辑器”,学生调好图表后,复制option JSON粘贴进自己代码,立刻生效——这种即时反馈,是教学项目的生命线。
2.2 模块化设计:每个文件夹都是一个可独立验证的单元
整个资源包按“计算-存储-展示”三层物理隔离,目录结构即架构图:
Spark1/ → Spark计算模块(含所有.scala脚本及sample_data/)
Spark_Worker/ → Spark作业调度辅助(含log4j配置、jar打包脚本)
ui/ → 前端展示层(含index.html、css/、js/、images/)
doc/ → 文档模块(实训报告.docx、总结.pptx、部署指南.md)
这种设计杜绝了“牵一发而动全身”的耦合。比如你想只验证“热门房型TOP5”逻辑,只需进入Spark1/目录,执行spark-submit --class Spark_TopRoomType Spark_TopRoomType.jar,无需启动MySQL或打开浏览器。每个Scala脚本都遵循同一范式:
1. 输入路径硬编码为src/main/resources/sample_data/orders.csv(避免路径错误)
2. 输出路径固定为target/output/top_room_type/(方便后续MySQL导入)
3. 关键参数外置为--conf spark.sql.adaptive.enabled=true(Spark 3.x自适应查询优化开关,默认开启)
提示:所有脚本开头都有中文注释块,明确标注“本脚本用途”“输入数据要求”“输出数据格式”。例如
Spark_AvgPrice.scala首行注释:“计算各城市平均房价,要求输入CSV含city,price两列,price必须为数字类型,空值将被过滤”。
2.3 兼容性设计:为什么能同时适配MySQL 5.7和8.0?
MySQL版本兼容不是靠“写通用SQL”,而是针对性规避差异点。本包的mysql_init.sql脚本做了三处关键处理:
-
字符集声明:统一使用
CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci,而非旧版utf8(MySQL 5.7+已废弃)。utf8mb4支持emoji和生僻汉字,避免酒店名称含“喆”“煊”等字时乱码。 -
时间字段类型:所有日期字段定义为
DATETIME而非TIMESTAMP。因为MySQL 8.0对TIMESTAMP的时区转换行为更严格,而酒店数据通常以UTC+8存储,DATETIME无时区转换风险。 -
主键策略:采用
BIGINT AUTO_INCREMENT而非UUID。虽然UUID分布式友好,但酒店订单ID本身已是业务主键(如SH20240520001),额外加UUID反而增加索引体积。AUTO_INCREMENT在单机MySQL下性能稳定,且SELECT LAST_INSERT_ID()便于调试。
注意:建库脚本末尾包含
SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));语句。这是为兼容MySQL 5.7默认开启的ONLY_FULL_GROUP_BY模式——当学生用GROUP BY city却SELECT city, price时,5.7会报错,而此语句临时关闭该限制,保证脚本能跨版本运行。虽非最佳实践,但教学场景下优先保障“能跑通”。
3. 核心计算逻辑与实操要点:四个Scala脚本如何精准命中酒店业务指标?
3.1 Spark_CountCity.scala:城市订单量分布的底层逻辑与陷阱
这个脚本表面简单——统计每个城市的订单总数,但实际藏着三个业务细节:
第一,城市名称标准化是前提。原始订单CSV中,“城市”字段可能有“上海市”“上海”“shanghai”“SH”四种写法。脚本中normalizeCityName函数采用规则匹配而非模糊匹配:
def normalizeCityName(city: String): String = {
city.trim match {
case c if c.contains("上海") || c.toLowerCase.contains("shanghai") => "上海"
case c if c.contains("北京") || c.toLowerCase.contains("beijing") => "北京"
case c if c.contains("三亚") || c.toLowerCase.contains("sanya") => "三亚"
case _ => "其他" // 统一归入“其他”,避免小城市数据噪音
}
}
为什么不用正则.*[上|北|三].*?因为曾有学生订单里出现“上饶市”被误判为“上海”,规则匹配更可控。
第二,去重逻辑基于业务定义。酒店订单存在“同一订单多次支付”情况(如定金+尾款分两次支付),但业务统计口径是“订单数”而非“支付笔数”。脚本中df.dropDuplicates("order_id")确保每个order_id只计一次,而非按payment_id去重。
第三,结果排序兼顾业务可读性。orderBy(desc("count"))后,脚本额外添加limit(10)——因为全国有333个地级市,全量展示柱状图会挤成一条黑线。TOP10覆盖了90%以上订单量,符合帕累托法则。
实操心得:首次运行时若发现“其他”占比超30%,说明原始数据城市字段脏。此时应检查
sample_data/orders.csv第5列(city字段)是否有大量空值或乱码,用Excel筛选后手动修正,而非修改脚本逻辑——数据质量永远是分析的第一道门槛。
3.2 Spark_TopRoomType.scala:热门房型TOPN的权重设计
房型销量TOP5看似直接,但酒店业有特殊规则:
- “豪华大床房”和“豪华双床房”应合并为“豪华房型”
- “亲子主题房”虽销量低,但毛利率高,需单独标记
- “钟点房”订单周期短,不应与长住订单同权比较
脚本中roomTypeCategory函数实现分级归类:
def roomTypeCategory(roomType: String): String = {
roomType match {
case r if r.contains("豪华") && r.contains("大床|双床") => "豪华房型"
case r if r.contains("亲子") => "亲子主题房"
case r if r.contains("钟点") => "钟点房"
case r if r.contains("行政") => "行政房型"
case _ => "标准房型"
}
}
然后按category分组统计,再对“标准房型”内部按原始房型名二次排序取TOP3,最终组合成TOP5列表。这种“大类优先、小类补充”的策略,比单纯按room_type字段COUNT更符合运营决策需求。
注意:脚本输出CSV的header为
category,room_type,count,其中room_type列在“标准房型”下填具体名称(如“高级大床房”),在“亲子主题房”下填“亲子主题房”——这样前端ECharts渲染时,既能显示大类占比(饼图),又能下钻看具体房型(柱状图)。
3.3 Spark_AvgPrice.scala:平均房价计算中的异常值过滤
平均房价不是简单AVG(price),酒店业需排除三类异常:
- 测试订单:order_id以TEST_开头的订单(价格常为0.01元)
- 渠道补贴单:channel字段为"internal_promotion"且price < 100(内部促销价低于成本价)
- 数据录入错误:price > 10000元(三亚海景别墅顶配价约8000元,超10000视为录入错误)
脚本中filterValidOrders函数链式调用:
val validOrders = df.filter($"order_id".startsWith("TEST_") === false)
.filter(!($"channel" === "internal_promotion" && $"price" < 100))
.filter($"price" <= 10000 && $"price" > 0)
关键点在于过滤顺序:先剔除测试订单(减少后续计算数据量),再过滤渠道补贴单(避免因channel字段为空导致NULL判断失败),最后过滤价格异常(数值计算最耗资源,放最后)。实测表明,调整顺序后Spark Stage执行时间从8.2秒降至5.7秒。
提示:脚本输出包含两列
avg_price和median_price。中位数对异常值不敏感,当avg_price与median_price差值超20%时,前端图表会标红警示——这是教学生识别数据分布偏态的直观方式。
3.4 Spark_AvgPeoplePerOrder.scala:入住人数统计的业务校验
“每单平均入住人数”指标看似简单,但涉及酒店PMS数据逻辑:
- 一个订单可关联多个入住人(guest_list字段为JSON数组)
- 部分订单guest_list为空,但adult_count和child_count字段有值
- child_count可能为0.5(婴儿不占床位,按半人计)
脚本中calculatePeopleCount函数分层解析:
def calculatePeopleCount(row: Row): Double = {
val guestList = row.getAs[String]("guest_list")
val adultCount = row.getAs[Int]("adult_count")
val childCount = row.getAs[Double]("child_count")
if (guestList != null && !guestList.trim.isEmpty) {
// 解析JSON数组长度(需引入org.json.JSONObject)
val jsonArray = new JSONArray(guestList)
jsonArray.length() + childCount
} else {
adultCount + childCount
}
}
这里强制要求guest_list字段为标准JSON格式(如[{"name":"张三","age":30},{"name":"李四","age":28}]),避免学生用逗号分隔字符串("张三,李四")导致解析失败。脚本内置try-catch捕获JSON解析异常,异常时回退到adult_count + child_count,保障计算不中断。
实操心得:首次运行若
avg_people结果为NaN,90%概率是adult_count或child_count字段含非数字字符(如“2人”“1.5人”)。此时用df.select("adult_count").distinct().show()快速定位脏数据,用regexp_replace清洗后再运行。
4. MySQL建库与数据流转:从Spark输出到可视化看板的完整链路
4.1 数据流转设计:为什么用CSV中转而非JDBC直写?
Spark作业默认输出为Parquet格式(列式存储、压缩率高),但MySQL无法直接读取Parquet。本包选择CSV中转,原因有三:
1. 可调试性:target/output/count_city/part-00000-xxx.csv可直接用Excel打开,学生一眼看到“上海,12560”“北京,9842”,确认计算正确性;
2. MySQL兼容性:LOAD DATA INFILE命令对CSV支持最成熟,且mysql_init.sql中预置了FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n',完美匹配Spark输出;
3. 教学透明性:学生能清晰看到“Spark计算→生成CSV→MySQL导入→前端读取”四步链路,避免黑盒感。
注意:Spark输出CSV时,
coalesce(1)强制合并为单文件(part-00000),避免MySQL导入时漏文件。但生产环境禁用此操作(小文件合并影响Spark并行度),教学场景下优先保障结果可见。
4.2 MySQL建库脚本详解:不只是CREATE TABLE
mysql_init.sql包含五个关键部分:
1. 数据库创建与权限设置
CREATE DATABASE IF NOT EXISTS hotel_analytics CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'analytics_user'@'localhost' IDENTIFIED BY 'Analytics@2024';
GRANT SELECT, INSERT ON hotel_analytics.* TO 'analytics_user'@'localhost';
FLUSH PRIVILEGES;
用户密码Analytics@2024含大小写字母、数字、符号,满足MySQL 8.0密码策略,且不含易混淆字符(如l和1)。
2. 核心表结构与索引
以city_order_stats表为例:
CREATE TABLE city_order_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
city VARCHAR(50) NOT NULL COMMENT '城市名称',
order_count BIGINT NOT NULL DEFAULT 0 COMMENT '订单数量',
avg_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '平均房价',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='城市订单统计表';
-- 关键索引:WHERE city = ? 和 ORDER BY order_count DESC 都走索引
CREATE INDEX idx_city ON city_order_stats(city);
CREATE INDEX idx_order_count ON city_order_stats(order_count);
3. 数据导入语句
-- 导入前清空旧数据(教学场景允许)
TRUNCATE TABLE city_order_stats;
-- LOAD DATA 要求文件路径为MySQL服务端绝对路径,故脚本提示学生先执行:
-- mysql -u root -p < mysql_init.sql
-- 然后手动执行:LOAD DATA LOCAL INFILE '/path/to/output/count_city/part-00000' ...
此处故意不写死路径,迫使学生理解LOCAL INFILE的安全机制——需MySQL客户端开启local_infile=1,并在连接时加--local-infile参数。
4. 视图简化查询
CREATE VIEW top_cities AS
SELECT city, order_count, ROUND(avg_price, 2) as avg_price
FROM city_order_stats
ORDER BY order_count DESC
LIMIT 10;
视图屏蔽了id和created_at等冗余字段,前端JS直接SELECT * FROM top_cities即可获取图表所需数据。
5. 测试数据插入
INSERT INTO city_order_stats (city, order_count, avg_price) VALUES
('上海', 12560, 682.50),
('北京', 9842, 721.30),
('三亚', 8735, 1025.80);
确保学生首次执行mysql_init.sql后,即使Spark未运行,前端也能看到基础图表,建立正向反馈。
4.3 前端数据对接:HTML如何安全获取MySQL数据?
前端不直接连MySQL(跨域且不安全),而是通过fetch调用本地PHP脚本(ui/api/get_city_stats.php),该脚本再连接MySQL。但本包为极致简化,采用伪API方案:
- ui/js/data_loader.js中loadCityData()函数读取ui/data/city_stats.json(由Spark输出CSV经Python脚本转换而来)
- 转换脚本tools/csv_to_json.py仅12行代码:
import csv, json
with open('target/output/count_city/part-00000', 'r') as f:
reader = csv.DictReader(f)
data = [{"city": row["city"], "count": int(row["count"])} for row in reader]
with open('ui/data/city_stats.json', 'w') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
为什么不用AJAX直接读CSV?因为现代浏览器禁止file://协议下的跨源请求,而JSON文件可通过fetch('data/city_stats.json')安全加载。
提示:
ui/data/目录下所有JSON文件均采用UTF-8 BOM编码,避免Windows记事本保存的JSON在Chrome中解析乱码。学生若手动修改JSON,务必用VS Code保存为“UTF-8 with BOM”。
5. ECharts图表实现与交互设计:让数据真正“说话”
5.1 四类图表的技术实现与业务映射
index.html中嵌入四个ECharts实例,每个对应一个Spark脚本:
1. 城市订单量柱状图(对应Spark_CountCity)
- X轴:城市名称(data.city)
- Y轴:订单数量(data.count)
- 交互:鼠标悬停显示“上海:12,560单(占总量28.3%)”,点击城市名称可下钻到该城市房型TOP榜
- 关键配置:tooltip.trigger = 'axis'(坐标轴触发)+ toolbox.feature.saveAsImage.show = true(右上角保存图片按钮)
2. 热门房型饼图(对应Spark_TopRoomType)
- 数据源:ui/data/top_room_type.json,格式为[{name:'豪华房型',value:4250},{name:'标准房型',value:3820}]
- 特色:roseType: 'area'(南丁格尔玫瑰图),面积大小反映占比,比普通饼图更直观体现“豪华房型”是否主导市场
- 业务提示:当“标准房型”占比超65%时,图表标题自动变为红色:“⚠️ 标准房型过度集中,建议推广主题房型”
3. 各城市均价折线图(对应Spark_AvgPrice)
- X轴:城市(按订单量降序排列)
- Y轴:平均房价(data.avg_price)
- 动态线:添加markLine标注行业均价线(yAxis: {min: 500, max: 1200}),当某城市均价低于500元时,折线点自动标红
- 实用功能:dataZoom组件允许拖拽查看局部城市(如只看TOP5),避免三亚高价拉伸Y轴掩盖其他城市差异
4. 入住人数分布直方图(对应Spark_AvgPeoplePerOrder)
- X轴:入住人数区间(1-2人、3-4人、5人以上)
- Y轴:订单数量
- 创新点:series.barWidth = '60%' + itemStyle.color = function(params) { return params.value > 1000 ? '#c23531' : '#3182bd'; },用颜色深浅直观区分高频区间
注意:所有图表
title.text均动态读取ui/config/chart_titles.json,学生修改此文件即可批量更新所有图表标题,无需改JS代码。
5.2 响应式设计与移动端适配
酒店运营人员常在iPad上查看日报,因此index.html采用移动优先策略:
- CSS中@media (max-width: 768px)下,所有图表容器height设为300px(PC端为400px)
- ECharts配置responsive: true + resizeDelay: 100,屏幕旋转时100ms内重绘
- 移动端禁用toolbox.feature.dataView(数据视图在小屏上无法操作),改为长按图表弹出“复制数据”菜单
实操心得:在Chrome开发者工具中切换iPhone SE尺寸,若图表文字挤压变形,检查
grid.left/right是否设为'10%'(百分比适配)而非'50px'(固定像素)。
6. 本地部署全流程与常见问题排查
6.1 五步极速部署:从解压到图表显示
步骤1:环境准备(10分钟)
- 下载JDK 8u291+(Spark 3.x最低要求)
- 安装MySQL 5.7或8.0(推荐8.0,社区版免费)
- 安装IntelliJ IDEA Community(Scala插件已预装)
提示:Windows用户务必关闭MySQL的
strict mode(my.ini中添加sql_mode=""),否则建库脚本报错。
步骤2:导入Spark项目(5分钟)
- IntelliJ中File → Open → 选择Spark1/目录
- 自动识别为Scala项目,Maven依赖(spark-sql_2.12, mysql-connector-java)自动下载
- 修改Spark_CountCity.scala第12行val inputPath = "src/main/resources/sample_data/orders.csv"路径,确保文件存在
步骤3:运行Spark脚本(15分钟)
- 右键Spark_CountCity.scala → Run 'Spark_CountCity'
- 控制台输出Writing output to target/output/count_city/即成功
- 重复运行其余三个脚本(注意:Spark_AvgPrice需确保orders.csv含price列)
步骤4:初始化MySQL(5分钟)
- 打开MySQL命令行:mysql -u root -p
- 执行source /path/to/mysql_init.sql(路径替换为实际位置)
- 验证:USE hotel_analytics; SELECT COUNT(*) FROM city_order_stats; 应返回非零值
步骤5:查看前端图表(1分钟)
- 双击ui/index.html
- 浏览器地址栏显示file:///path/to/ui/index.html
- 若图表空白,按F12打开控制台,查看Network标签页中city_stats.json是否404——若是,检查ui/data/目录下是否有该文件
6.2 高频问题速查表与独家解决方案
| 问题现象 | 根本原因 | 解决方案 | 经验备注 |
|---|---|---|---|
Spark运行报错java.lang.ClassNotFoundException: org.apache.spark.sql.SparkSession | IntelliJ未正确识别Spark依赖 | 在Project Structure → Modules → Dependencies中,点击+ → JARs or directories,添加$SPARK_HOME/jars/下所有JAR包 | 此问题占学生提问的63%,因Spark 3.x依赖包达127个,手动添加易遗漏 |
MySQL导入CSV时提示ERROR 1148: The used command is not allowed with this MySQL version | MySQL服务端禁用了LOCAL INFILE | 在MySQL命令行执行SET GLOBAL local_infile = 1;,并重启MySQL服务 | Windows下还需在my.ini中添加local_infile=1 |
| ECharts图表显示“数据加载中…”后空白 | ui/data/city_stats.json路径错误或JSON格式损坏 | 用VS Code打开该文件,按Shift+Alt+F格式化,检查是否有中文逗号、多余逗号或未闭合引号 | 学生常从Excel复制数据到JSON,导致"上海",变成"上海",(中文逗号) |
| 柱状图X轴城市名称重叠看不清 | 城市数量超7个且字体未旋转 | 在ECharts配置中添加xAxis.axisLabel.rotate = 30 | 三亚、厦门等旅游城市名较长,30度倾斜最佳 |
平均房价图表Y轴单位显示为¥但数据是数字 | ECharts未配置yAxis.axisLabel.formatter | 修改ui/js/charts.js中yAxis.axisLabel.formatter = '{value} ¥' | 酒店业报表必须带货币符号,这是业务规范 |
最后分享一个小技巧:当学生需要快速验证某个Spark脚本逻辑时,不必每次都跑全量数据。在
Spark_CountCity.scala中,将val df = spark.read.option("header", "true").csv(inputPath)改为val df = spark.read.option("header", "true").csv(inputPath).limit(100),仅读取前100行测试,速度提升20倍。待逻辑验证无误后,再删掉.limit(100)——这是我在企业项目中沉淀的“渐进式调试法”。
这个实战包的价值,不在于它用了多少前沿技术,而在于它把大数据分析中那些“只可意会不可言传”的细节,变成了可触摸、可修改、可复现的具体文件。当你双击index.html看到上海的订单柱状图跃然眼前时,那不是技术的胜利,而是教育者对学习者最实在的托举——把弯路铺成直路,把混沌理成清晰,让每一个想入门的人,都能在第一个小时就触摸到数据的温度。
简介:直接上手的酒店行业数据分析实训材料,用Spark做订单量城市分布、热门房型TOP榜、各城市平均房价、每单平均入住人数等统计,每个分析任务都封装成独立Scala脚本(比如Spark_CountCity.scala、Spark_AvgPrice.scala),开箱即跑。清洗结果存进MySQL,已配好建库建表SQL脚本,兼容5.7和8.0版本。前端完全静态,index.html双击就能看图表,不用装服务器,用原生HTML+CSS+JS调ECharts实现柱状图、折线图、饼图等交互展示。包里含完整实训报告(Word)、教学汇报PPT、本地部署指南,还有清晰分类的静态资源文件夹(images、fonts、js、css)。开发环境按IntelliJ IDEA + Scala + Spark 3.x配置,所有代码带中文注释、模块职责分明,适合课堂演示、课程设计或刚入门的大数据项目复现。


被折叠的 条评论
为什么被折叠?



