You are on page 1of 38

分布式程序设计

实验报告

专 业 班 级
智能科学与技术 20-1
学生姓名及学号
安柯锦 2020212309
课程教学班号 0500020X--001

任 课 教 师 张赞

实验指导教师 张赞

实 验 地 点 翡翠湖校区科教楼 C 座机房

2022~2023 学年第 一 学期
说 明
实验报告是关于实验教学内容、过程及效果的记录和总结,因此,
应注意以下事项和要求:
1.每个实验单元在 50 页的篇幅内完成一份报告。“实验单元”
指按照实验指导书规定的实验内容。若篇幅不够,可另附纸。
2、各实验的预习部分的内容是进入实验室做实验的必要条件,请
按要求做好预习。
3.实验报告要求:书写工整规范,语言表达清楚,数据和程序
真实。理论联系实际,认真分析实验中出现的问题与现象,总结经验。
4.参加实验的每位同学应独立完成实验报告的撰写,其中程序
或相关的设计图纸也可以采用打印等方式粘贴到报告中。严禁抄袭或
拷贝,否则,一经查实,按作弊论取,并取消理论课考试资格。
5.实验报告作为评定实验成绩的依据。
实验序号及名称:实验 一 Spark 本地版安装
实验时间∶ 2022 年 11 月 28

预习内容
一、实验目的和要求∶
安装 Hadoop 和 Spark。
按照实验指导书的步骤,进入 Linux 系统,完成 Hadoop 伪分布式模式的安装,再
安装 Spark(Local 模式)。

二、实验任务∶
1.安装 VMware16Pro
2.VMware 安装 Ubuntu
3.安装 vm-tools 实现虚拟机和本地主机的文件共享
4.安装 Hadoop
5.安装 Spark

三、实验准备方案,包括以下内容:
(硬件类实验:实验原理、实验线路、设计方案等)
(软件类实验:所采用的系统、组件、工具、核心方法、框架或流程图、程序清
单等)

实验参考:“第 3 章-Spark 本地版安装教程”

软件配置:
操作系统:win10
虚拟机:Vmware Workstation 15 pro
其余软件:jdk8,Hadoop-3.3.1,spark

实验内容
一、实验用仪器、设备:
实验仪器:LAPTOP-3PDMCA1H
软件配置:
操作系统:win10
虚拟机:Vmware Workstation 15 pro
其余软件:jdk8,Hadoop-3.3.1,spark
Ubuntu 虚拟机配置:

图 1 虚拟机配置

二、实验内容与步骤(过程及数据记录):
事先已经安装好 VMware Workstation Pro,如下图所示:

图 2 Vmware Workstation 安装截图

二、VMware 安装 Ubuntu

创建虚拟机
这部分实验,我没有完全按照实验指导书上面的内容来,原因是之前创建虚拟机
都是直接先导入光盘映像,没有在意实验指导书上的具体细节,所以在虚拟机创
建完成后。键盘输入法和系统的语言都不是中文,我把改成中文的步骤附在后面。
截图仅选取部分内容。

设置时间截图:

图 3 时间设置截图 1

图 4 时间设置截图 2

② 设置语言
图 5 安装语言截图

图 6 语言选择截图 1
图 7 语言选择截图 2

图 8 网络通畅截图
如上图所示,语言及时间修改正确,网络连接成功,在 VM 下安装 Ubuntu 虚
拟机成功。

虚拟机和本地主机的文件共享
图 9 Ubuntu 安装软件截图
如上图所示,软件安装成功。

三、Hadoop 安装与配置教程

创建新用户,赋予权限。

图 10 创建新用户 Hadoop
安装 vim
图 11 安装 vim 截图
实现 ssh 免密登录

图 12 ssh 免密登录截图 1
图 13 ssh 免密登录截图 2

安装 jdk

图 14 安装 jdk 截图
设置路径
图 15 设置路径截图
验证 Java 环境安装成功

图 16 验证 Java 环境安装成功
Hadoop 版本信息如下图所示:
图 17 Hadoop 版本信息截图

执行 grep 例子,结果如下图所示:

图 28 执行 grep 截图
修改配置文件
图 19 配置文件修改截图 1

图 20 配置文件修改截图 2

执行 NameNode 的格式化成功,如下图所示:
图 21 NameNode 格式化截图

执行 jps 命令,成功启动,如下图所示:

图 22 执行 jps 截图
访问 Web 界面 http://localhost:9870
图 23 访问 web 截图

查看文件列表

图 24 文件列表截图
进行验证
图 35 验证截图

四、Spark 的安装与配置(本地模式)

下载 Spark

图 26 Spark 下载截图
Spark 安装成功,如下图所示:

图 27 spark 安装成功截图
执行 python 程序,如下图所示:

图 28 python 程序执行截图
三、实验结果分析、思考题解答∶
如图 34 所示,实验顺利完成,Spark 安装成功。

四、感想、体会、建议∶
通过本次实验,我对 Spark 有了基本的了解,并且通过自己的实践且依据实
验指导书,成功在本地安装了 Spark。

实验成绩∶

指导教师签名:
年 月 日
实验序号及名称:实验 二 Spark 算子
实验时间∶ 2022 年 11 月 28 日
预习内容
一、实验目的和要求∶
掌握 Spark 算子。Spark 的算子可分为:
Transformation 变换/转换算子:这种变换并不触发提交作业,完成作业中
间过程处理。Transformation 操作是延迟计算的,也就是说从一个 RDD 转换生成
另一个 RDD 的操作不是马上执行,需要等到有 Action 操作的时候才会真正触发运
算。
Action 行动算子:这类算子会触发 SparkContext 提交 Job 作业。Action 算子
会触发 Spark 提交作业(Job),并将数据输出 Spark 系统。
二、实验任务∶
1. 使用 Spark 的 map 算子按照相关需求完成任务。
2. 使用 Spark 的 mapPartitions 算子按照相关需求完成任务。
3. 使用 Spark 的 filter 算子按照相关需求完成任务。
4. 使用 Spark 的 flatMap 算子按照相关需求完成任务。
5. 使用 Spark 的 distinct 算子按照需求完成任务。
6. 使用 Spark 的 SortBy 算子按照相关需求完成任务。
7. 使用 Spark 的 sortByKey 算子按照相关需求完成任务。
8. 使用 Spark 的 mapValues 算子按照相关需求完成任务。
9. 使用 Spark 的 reduceByKey 算子按照相关需求完成任务。
10. 使用 Spark 的 Action 常用算子按照相关需求完成任务。

三、实验准备方案,包括以下内容:
登录头歌平台,根据老师上课所讲的 Spark 算子相关知识点,完成 10 关的测
试。

实验内容
一、实验用仪器、设备:
实验仪器:联想小新 LAPTOP-3PDMCA1H
实验环境:头歌实验平台

二、实验内容与步骤(过程及数据记录):
1、第一关:Transformation - map
Map: 将原来 RDD 的每个数据项通过 map 中的用户自定义函数 f 映射转变为
一个新的元素。

图 4 map 操作
每个方框表示一个 RDD 分区,左侧的分区经过自定义函数 f:T->U 映射为右侧的
新 RDD 分区。但是,实际只有等到 Action 算子触发后,这个 f 函数才会和其
他函数在一个 Stage 中对数据进行运算。
# 1.初始化 SparkContext,该对象是 Spark 程序的入口
SC = SparkContext("local", "Simple App")
# 2.创建一个 1 到 5 的列表 List
Date_list = [1,2,3,4,5]
# 3.通过 SparkContext 并行化创建 rdd
rdd = SC.parallelize(Date_list)
# 4.使用 rdd.collect() 收集 rdd 的元素。
print(rdd.collect())

"""
使用 map 算子,将 rdd 的数据 (1, 2, 3, 4, 5) 按照下面的规则进行转换
操作,规则如下:
需求:
偶数转换成该数的平方
奇数转换成该数的立方
"""
# 5.使用 map 算子完成以上需求
rdd_map = rdd.map(lambda x: x * x if x % 2 == 0 else x * x * x)
# 6.使用 rdd.collect() 收集完成 map 转换的元素
print(rdd_map.collect())
# 7.停止 SparkContext
SC.stop()

2、第二关:Transformation - mapPartitions
mapPartitions:mapPartitions 函数获取到每个分区的迭代器,在函数中通
过这个分区整体的迭 代器对整个分区的元素进行操作。

图 5 mapPartitions 操作
图中每个方框表示一个 RDD 分区,左侧的分区经过自定义函数 f:T->U 映射为右
侧的新 RDD 分区。mapPartitions 算子比 map 算子效率高。
if __name__ == "__main__":
#********** Begin **********#

# 1.初始化 SparkContext,该对象是 Spark 程序的入口


SC = SparkContext("local", "Simple App")

# 2. 一个内容为("dog", "salmon", "salmon", "rat", "elephant")的列


表 List
Date_list = ["dog", "salmon", "salmon", "rat", "elephant"]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)
# 4.使用 rdd.collect() 收集 rdd 的元素。
print(rdd.collect())

"""
使用 mapPartitions 算子,将 rdd 的数据 ("dog", "salmon", "salmon",
"rat", "elephant") 按照下面的规则进行转换操作,规则如下:
需求:
将字符串与该字符串的长度组合成一个元组,例如:
dog --> (dog,3)
salmon --> (salmon,6)
"""

# 5.使用 mapPartitions 算子完成以上需求


partitions = rdd.mapPartitions(f)

# 6.使用 rdd.collect() 收集完成 mapPartitions 转换的元素


print(partitions.collect())

# 7.停止 SparkContext
SC.stop()
3、第三关:Transformation – filter
filter :filter 函数功能是对元素进行过滤,对每个元素应用 f 函数,返
回值为 true 的元素在 RDD 中保留,返回值为 false 的元素将被过滤掉。内部实现
相当于生成。

图 6 filter 操作
每个方框代表一个 RDD 分区, T 可以是任意的类型。通过用户自定义的过滤函
数 f,对每个数据项操作,将满足条件、返回结果为 true 的数据项保留。例如,
过滤掉 V2 和 V3 保留了 V1,为区分命名为 V’1。
# 1.初始化 SparkContext,该对象是 Spark 程序的入口
SC = SparkContext("local", "Simple App")

# 2.创建一个 1 到 8 的列表 List


Date_list = [1,2,3,4,5,6,7,8]
# 3.通过 SparkContext 并行化创建 rdd
rdd = SC.parallelize(Date_list)

# 4.使用 rdd.collect() 收集 rdd 的元素。


print(rdd.collect())

"""
使用 filter 算子,将 rdd 的数据 (1, 2, 3, 4, 5, 6, 7, 8) 按照下面的
规则进行转换操作,规则如下:
需求:
过滤掉 rdd 中的奇数
"""
# 5.使用 filter 算子完成以上需求
rdd_filter = rdd.filter(lambda x: x % 2 == 0)

# 6.使用 rdd.collect() 收集完成 filter 转换的元素


print(rdd_filter.collect())

# 7.停止 SparkContext
SC.stop()
4、第四关:Transformation - flatMap
flatMap:将原来 RDD 中的每个元素通过函数 f 转换为新的元素,并将生成的
RDD 中每个集合的元素合并为一个集合,内部创建。

图 7 flatmap 操作
RDD 的一个分区,进行 flatMap 函数操作,flatMap 中传入的函数为 f:T->U,T 和
U 可以是任意的数据类型。将分区中的数据通过用户自定义函数 f 转换为新的数
据。外部大方框可以认为是一个 RDD 分区,小方框代表一个集合。V1、V2、V3 在一个
集 合 作 为 RDD 的 一 个 数 据 项 , 可 能 存 储 为 数 组 或 其 他 容 器 , 转 换 为
V’1、V’2、V’3 后,将原来的数组或容器结合拆散,拆散的数据形成 RDD 中的数
据项。
# 1.初始化 SparkContext,该对象是 Spark 程序的入口
SC = SparkContext("local", "Simple App")
# 2.创建一个[[1, 2, 3], [4, 5, 6], [7, 8, 9]] 的列表 List
Date_list = [[1,2,3],[4,5,6],[7,8,9]]
# 3.通过 SparkContext 并行化创建 rdd
rdd = SC.parallelize(Date_list)
# 4.使用 rdd.collect() 收集 rdd 的元素。
print(rdd.collect())
"""
使用 flatMap 算子,将 rdd 的数据 ([1, 2, 3], [4, 5, 6], [7, 8,
9]) 按照下面的规则进行转换操作,规则如下:
需求:
合并 RDD 的元素,例如:
([1,2,3],[4,5,6]) --> (1,2,3,4,5,6)
([2,3],[4,5],[6]) --> (1,2,3,4,5,6)
"""
# 5.使用 filter 算子完成以上需求
rdd_flatMap = rdd.flatMap(lambda x: x)
# 6.使用 rdd.collect() 收集完成 filter 转换的元素
print(rdd_flatMap.collect())
# 7.停止 SparkContext
SC.stop()
5、第五关:Transformation - distinct
Distinct:distinct 将 RDD 中的元素进行去重操作。

图 8 distinct 操作
上图中的每个方框代表一个 RDD 分区,通过 distinct 函数,将数据去重。 例
如,重复数据 V1、 V1 去重后只保留一份 V1 。
# 1.初始化 SparkContext,该对象是 Spark 程序的入口
SC = SparkContext("local", "Simple App")

# 2.创建一个内容为(1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1)的列表 List


Date_list = [1,2,3,4,5,6,5,4,3,2,1]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)

# 4.使用 rdd.collect() 收集 rdd 的元素


print(rdd.collect())

"""
使用 distinct 算子,将 rdd 的数据 (1, 2, 3, 4, 5, 6, 5, 4, 3, 2,
1) 按照下面的规则进行转换操作,规则如下:
需求:
元素去重,例如:
1,2,3,3,2,1 --> 1,2,3
1,1,1,1, --> 1
"""
# 5.使用 distinct 算子完成以上需求
rdd_distinct = rdd.distinct()

# 6.使用 rdd.collect() 收集完成 distinct 转换的元素


print(rdd_distinct.collect())

# 7.停止 SparkContext
SC.stop()

6、第六关:Transformation - sortBy
sortBy:sortBy 函数是在 org.apache.spark.rdd.RDD 类中实现的。
该函数最多可以传三个参数:
 (1)第一个参数是一个函数,排序规则;
 (2)第二个参数是 ascending ,这参数决定排序后 RDD 中的元素是升序还
是降序,默认是 true ,也就是升序;
 (3)第三个参数是 numPartitions ,该参数决定排序后的 RDD 的分区个
数 , 默 认 排 序 后 的 分 区 个 数 和 排 序 之 前 的 个 数 相 等 , 即 为
this.partitions.size。
# 1.初始化 SparkContext,该对象是 Spark 程序的入口
SC = SparkContext("local", "Simple App")

# 2.创建一个内容为(1, 3, 5, 7, 9, 8, 6, 4, 2)的列表 List


Date_list = [1,3,5,7,9,8,6,4,2]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)

# 4.使用 rdd.collect() 收集 rdd 的元素


print(rdd.collect())

"""
使用 sortBy 算子,将 rdd 的数据 (1, 3, 5, 7, 9, 8, 6, 4, 2) 按照
下面的规则进行转换操作,规则如下:
需求:
元素排序,例如:
5,4,3,1,2 --> 1,2,3,4,5
"""
# 5.使用 sortBy 算子完成以上需求
rdd_sortBy = rdd.sortBy(lambda x: x)

# 6.使用 rdd.collect() 收集完成 sortBy 转换的元素


print(rdd_sortBy.collect())

# 7.停止 SparkContext
SC.stop()
7、第七关:Transformation - sortByKey
sortByKey : ascending 参 数 是 指 排 序 ( 升 序 还 是 降 序 ) , 默 认 是 升
序。numPartitions 参数是重新分区,默认与上一个 RDD 保持一致。keyfunc 参数是
排序规则。
# 1.初始化 SparkContext,该对象是 Spark 程序的入口
SC = SparkContext("local", "Simple App")

# 2.创建一个内容为[(B',1),('A',2),('C',3)]的列表 List
Date_list = [("B",1), ("A",2), ("C",3)]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)

# 4.使用 rdd.collect() 收集 rdd 的元素


print(rdd.collect())

"""
使用 sortByKey 算子,将 rdd 的数据 ('B', 1), ('A', 2), ('C', 3)
按照下面的规则进行转换操作,规则如下:
需求:
元素排序,例如:
[(3,3),(2,2),(1,1)] --> [(1,1),(2,2),(3,3)]
"""
# 5.使用 sortByKey 算子完成以上需求
rdd_sortByKey = rdd.sortByKey()

# 6.使用 rdd.collect() 收集完成 sortByKey 转换的元素


print(rdd_sortByKey.collect())

# 7.停止 SparkContext
SC.stop()
8、第八关:Transformation - sortByKey
mapValues:针对(Key, Value)型数据中的 Value 进行 Map 操作,而不
对 Key 进行处理。

图 9 mapValues 操作
上图中的方框代表 RDD 分区。 a=>a+2 代表对 (V1,1) 这样的 Key Value
数据对,数据只对 Value 中的 1 进行加 2 操作,返回结果为 3。

# 1.初始化 SparkContext,该对象是 Spark 程序的入口


SC = SparkContext("local", "Simple App")

# 2.创建一个内容为[("1", 1), ("2", 2), ("3", 3), ("4", 4), ("5",


5)]的列表 List
Date_list = [("1",1), ("2",2), ("3",3), ("4",4), ("5",5)]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)

# 4.使用 rdd.collect() 收集 rdd 的元素


print(rdd.collect())

"""
使用 mapValues 算子,将 rdd 的数据 ("1", 1), ("2", 2), ("3",
3), ("4", 4), ("5", 5) 按照下面的规则进行转换操作,规则如下:
需求:
元素(key,value)的 value 进行以下操作:
偶数转换成该数的平方
奇数转换成该数的立方
"""
# 5.使用 mapValues 算子完成以上需求
rdd_mapValues = rdd.mapValues(lambda x: x * x if x % 2 == 0 else x
* x * x)

# 6.使用 rdd.collect() 收集完成 mapValues 转换的元素


print(rdd_mapValues.collect())

# 7.停止 SparkContext
SC.stop()

9、第九关:Transformations - reduceByKey
reduceByKey:reduceByKey 算子,只是两个值合并成一个值,比如叠加。

图 10 reduceByKey 算子
方框代表 RDD 分区。通过自定义函数 (A,B) => (A + B) ,将相同 key 的数据
(V1,2) 和 (V1,1) 的 value 做加法运算,结果为( V1,3)。

# 1.初始化 SparkContext,该对象是 Spark 程序的入口


SC = SparkContext("local", "Simple App")

# 2. 创 建 一 个 内 容 为 [("python", 1), ("scala", 2), ("python", 3),


("python", 4), ("java", 5)]的列表 List
Date_list = [("python", 1), ("scala", 2), ("python", 3), ("python",
4), ("java", 5)]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)

# 4.使用 rdd.collect() 收集 rdd 的元素


print(rdd.collect())

"""
使用 reduceByKey 算子,将 rdd 的数据[("python", 1), ("scala",
2), ("python", 3), ("python", 4), ("java", 5)] 按照下面的规则进行转换操
作,规则如下:
需求:
元素(key-value)的 value 累加操作,例如:
(1,1),(1,1),(1,2) -->
(1,4)
(1,1),(1,1),(2,2),(2,2)
--> (1,2),(2,4)
"""
# 5.使用 reduceByKey 算子完成以上需求
rdd_reduceByKey = rdd.reduceByKey(lambda x,y: x+y)

# 6.使用 rdd.collect() 收集完成 reduceByKey 转换的元素


print(rdd_reduceByKey.collect())

# 7.停止 SparkContext
SC.stop()

10、第十关:Actions - 常用算子
count():返回 RDD 的元素个数。
first():返回 RDD 的第一个元素(类似于 take(1))。
take(n):返回一个由数据集的前 n 个元素组成的数组。
reduce():通过 func 函数聚集 RDD 中的所有元素,该函数应该是可交换的和关
联的,以便可以并行正确计算。
collect():在驱动程序中,以数组的形式返回数据集的所有元素。

# 1.初始化 SparkContext,该对象是 Spark 程序的入口


SC = SparkContext("local", "Simple App")

# 2.创建一个内容为[1, 3, 5, 7, 9, 8, 6, 4, 2]的列表 List


Date_list = [1,3,5,7,9,8,6,4,2]

# 3.通过 SparkContext 并行化创建 rdd


rdd = SC.parallelize(Date_list)

# 4.收集 rdd 的所有元素并 print 输出


print(rdd.collect())

# 5.统计 rdd 的元素个数并 print 输出


print(rdd.count())

# 6.获取 rdd 的第一个元素并 print 输出


print(rdd.first())

# 7.获取 rdd 的前 3 个元素并 print 输出


print(rdd.take(3))

# 8.聚合 rdd 的所有元素并 print 输出


print(rdd.reduce(lambda x,y: x+y))

# 9.停止 SparkContext
SC.stop()
三、实验结果分析、思考题解答∶
第一关结果:

第二关结果:

第三关结果:

第四关结果:
第五关结果:

第六关结果:

第七关结果:
第八关结果:

第九关结果:

第十关结果:

四、感想、体会、建议∶
通过本次实验的学习,让我对 Spark 的十个算子有了更深的理解。对分布式
RDD 操作理解更加透彻,也增加了自己对于分布式的学习能力,使自己的综合能
力更上一层楼。

实验成绩∶

指导教师签名:
年 月 日
实验序号及名称:实验 三 企业 spark 案例:出租车轨迹分析
实验时间∶ 2022 年 11 月 28 日
预习内容
一、实验目的和要求∶
将出租车轨迹数据规整化,清洗掉多余的字符串。了解掌握数据清洗。使用
SparkSQL 完成数据分析
二、实验任务∶
1.将出租车轨迹数据规整化,清洗掉多余的字符串。
2.使用 SparkSQL 完成数据分析
三、实验准备方案,包括以下内容:
环境:头歌平台
工具:spark
方法:SparkSQL 读取 CSV
SparkSQL 内置字符串处理函数
正则表达式
SparkSQL UDF

实验内容
一、实验用仪器、设备:
实验仪器:LAPTOP-3PDMCA1H
实验环境:头歌实验平台
二、实验内容与步骤(过程及数据记录):
1、第一关:SparkSql 数据清洗
 SparkSQL 读取 CSV:
具体代码如下:
spark = SparkSession.builder.appName("demo").master("local").getOrCreate()
spark.read.option("header", True).option("delimiter", "CSV 分隔符").csv("csv 路径")

header 为 true 将 CSV 的第一行数据作为 SparkSQL 表的字段;delimiter 分隔


符,例如,CSV 文件默认以英文逗号进行字段分隔,那么 delimiter 为英文逗号,
如果文件以分号进行字段分隔,那么 delimiter 为分号。
 正则表达式清洗:
正则可以使用限定符匹配字符出现的次数,提高了灵活度。正则表达式的限定符
用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或
? 或 {n} 或 {n,} 或 {n,m} 共 6 种
2、第二关:SparkSql 数据分析
 将时间戳转换成时间,并将列名重命名为 TIME

具体函数如下:
def time_convert(string):
timeArray = time.localtime(float(string))
otherStyleTime = time.strftime("%Y-%m-%d", timeArray)
return otherStyleTime
def local_convert0(string):
a = string.split(',')
try:
temp = a[0]+', '+a[1]+']'
return temp
except:
return None
def local_convert1(string):
a = string.split(',')
try:
temp = '['+a[-2]+', '+a[-1]
return temp
except:
return None
def time_len(string):
temp = len(string.split(','))/2*15-15
return '['+str(temp)+']'
 计算每个行程总时长,以秒为单位,并将其作为新列,列名为 TIMELEN,分离
出起始位置与目的位置作为新列,起始位置列名为 STARTLOCATION,目的位置
列名为 ENDLOCATION

 统计每天各种呼叫类型的数量并以 CALL_TYPE, TIME 升序排序

三、实验结果分析、思考题解答∶
第一关结果:
第二关结果:
四、感想、体会、建议∶
通过本次实验,对数据清洗以及正则表达式有了更深刻的理解与掌握,明白
了数据清洗的方法及应用。利用正则表达式洗掉多余的字符串,效果比较显著,
并且使用 SparkSQL 完成数据分析,效果较为不错,能够达到实验目的,完成实
验需求,同时使自己的能力更上一层楼。

实验成绩∶

指导教师签名:
年 月 日

You might also like