Spark-shell交互式编程+HDFS操作,编写独立应用程序实现求二次排序问题

一、实验环境

Ubuntu 20.04 - VMware Workstation

java - openjdk version “1.8.0_312”

Scala code runner version 2.11.12

Spark 2.1.0

sbt 1.3.8

二、Spark-shell交互式编程+HDFS操作

  1.  实验内容

chapter5-data1.txt数据集包含了某大学计算机系的成绩,数据格式如下所示:

 

  1. HDFS操作:根据原始数据集中的姓名字段生成分班表class.txt,并将其存入HDFS中(地址自定)。按照姓名首字母分班:其中A-G为1班,H-N为2班,O-T为3班,U-Z为4班。class.txt每行包含2个字段,格式为(班级编号,姓名)
  2. Spark-shell交互式编程从HDFS中读取class.txt, 从本地读取chapter5-data1.txt,已知班级信息表与成绩表,对于每一门课,找出班级平均分>50的班级,并将结果写入HDFS中。最终输出的格式为:课程名,班级编号,平均分, 并按照课程名降序排列。

  1. 代码实现

使用命令:

cd /usr/local/spark

./bin/spark-shell

启动进入spark-shell

object Task1{

  def main(args:Array[String]) : Unit = {

    //chapter5-data1.txt的存放在本地,地址如下,用绝对路径调用

    val lines = sc.textFile("file:///usr/local/spark/mycode/t1/chapter5-data1.txt")

    //将原有成绩表格每一行按","分割,取第0位即学生的姓名,用map函数映射为(姓名,1)的rdd形式

    //用reduceByKey函数对Key去重,Value值全部变为1,随后去除Value单独取出学生姓名的String

    val name = lines.map(row=>(row.split(",")(0),1)).reduceByKey((x,y)=>1).map(_._1)

    //使用take函数取学生姓名首字母,用matches函数匹配后在filter函数中分别过滤出对应的学生

    //随后用map函数将班级编号作为key学生姓名作为Value,得到格式(班级编号,姓名)的rdd

    val f1 = name.filter(row=>row.take(1).matches("[A-G]")).map(row=>(1,row))

    val f2 = name.filter(row=>row.take(1).matches("[H-N]")).map(row=>(2,row))

    val f3 = name.filter(row=>row.take(1).matches("[O-T]")).map(row=>(3,row))

    val f4 = name.filter(row=>row.take(1).matches("[U-Z]")).map(row=>(4,row))

    //由于上述rdd格式相同,使用自带的合并方法 ++ 进行合并即可得到需要存储的class.txt的内容

    val f = f1 ++ f2 ++ f3 ++ f4

分班后的部分信息显示如下

 

//第二个终端

//cd /usr/local/hadoop

//./sbin/start-dfs.sh



    //打开HDFS后,将格式为(班级编号,姓名)的rdd存储在HDFS的/user/hadoop目录下,文件名为class.txt

f.saveAsTextFile("class.txt")

存储后,在第二个终端输入 ./bin/hdfs dfs -ls .

可以查看在HDFS的/user/hadoop目录,发现多了一个class.txt文件

 

    //读取HDFS/user/hadoop目录下的class.txt文件,获得分班表

    val classline = sc.textFile("hdfs://localhost:9000/user/hadoop/class.txt")

    //读取本地chapter5-data1.txt,获得学生科目成绩表

    val lines = sc.textFile("file:///usr/local/spark/mycode/t1/chapter5-data1.txt")

    //对分班表进行处理,使用map函数将原本读取的String处理成(姓名,班级编号)格式的rdd

    //其中使用split函数对","进行分割,分割出班级编号和名字;再使用filterNot函数去除String中的括号。班级信息用toInt转为Int型

    val classinfo = classline.map(row=>(row.split(",")(1).filterNot(c=>c==')'),row.split(",")(0).filterNot(c=>c=='(').toInt))

    //处理学生科目成绩表,用map函数将其映射成(姓名,(科目,成绩))的key-value的rdd形式

    val info = lines.map(row=>(row.split(",")(0),(row.split(",")(1),row.split(",")(2).toInt)))

    //处理后的分班信息表和科目成绩表的key都为学生姓名,用join函数将两个表合并,join后的格式为(姓名,((科目,成绩),班级编号))

    //由于之后需要对不同班级的不同学科进行统计,故将(科目,班级编号)作为key,用map函数将其处理成((科目,班级编号),成绩)的key-value形式的rdd

    val info1 = info.join(classinfo).map(x=>((x._2._1._1,x._2._2),x._2._1._2))

    //将value从成绩映射为(成绩,1)的形式后,用reduceByKey和mapValues的操作进行求平均,方法和练习一样

    //随后用map函数映射成(科目,班级编号,平均成绩)的形式,用filter函数过滤出平均成绩大于50的行

    //再用map函数映射成(科目,(班级编号,平均成绩))的key-value形式的rdd,用sortByKey(false)按照课程名降序排序,最后再map成(科目,班级编号,平均成绩)的形式

    info1.mapValues(x=>(x,1)).reduceByKey((x,y)=>(x._1+y._1,x._2+y._2)).mapValues(x=>(x._1/x._2)).map(x=>(x._1._1,x._1._2,x._2)).filter(x=>x._3>50).map(x=>(x._1,(x._2,x._3))).sortByKey(false).map(x=>(x._1,x._2._1,x._2._2)).collect()

  }

}

  1. 最终结果

 

可以看到最后的结果按照学科和班级编号进行了平均分的计算,并按照学科名降序显示出了平均分大于50的信息。

三、编写独立应用程序实现求二次排序问题

  1.  实验内容

每个输入文件表示班级学生某个学科的成绩,每行内容由4个字段组成,第一个是学生编号,第二个是学生名字,第三个是科目名称,第4个是学生成绩;要求编写 Spark 独立应用程序:将以下3个file中的成绩合并到同一个文件中,需要按照学生编号升序排序,若学生编号相同,则按照成绩降序排列,最后将结果输出到一个新文件中。下面是输入文件和输出文件的一个样例,供参考。

File1.txt

1 小明 Algorithm 92

2 小红 Algorithm 87

3 小新 Algorithm 82

4 小丽 Algorithm 90

File2.txt

1 小明 Database 95

2 小红 Database 81

3 小新 Database 89

4 小丽 Database 85

File3.txt

1 小明 Python 82

2 小红 Python 83

3 小新 Python 94

4 小丽 Python 91

输出文件:

1 小明 Database 95

1 小明 Algorithm 92

1 小明 Python 82

2 小红 Algorithm 87

2 小红 Python 83

2 小红 Database 81

3 小新 Python 94

3 小新 Database 89

3 小新 Algorithm 82

4 小丽 Python 91

4 小丽 Algorithm 90

4 小丽 Database 85

  1. 代码实现
import org.apache.spark.SparkContext

import org.apache.spark.SparkContext._

import org.apache.spark.SparkConf

import org.apache.spark.HashPartitioner

object JoinUp {

    def main(args: Array[String]) {

        val conf = new SparkConf().setAppName("JoinUp")

        val sc = new SparkContext(conf)

        //分别读取三个文件中的内容

        val dataFile1 = "file:///home/hadoop/hm2/File1.txt"

        val dataFile2 = "file:///home/hadoop/hm2/File2.txt"

        val dataFile3 = "file:///home/hadoop/hm2/File3.txt"

        val data1 = sc.textFile(dataFile1,3)

        val data2 = sc.textFile(dataFile2,3)

        val data3 = sc.textFile(dataFile3,3)

        //文件中读取后的格式为 “编号 学生名字 科目名称 学生成绩” 的String

        //用map函数按照空格进行分割,处理成(编号,学生名字,科目名称,学生成绩)的rdd,其中编号和成绩转为Int格式

        val info1 = data1.map(row=>(row.split(" ")(0).toInt,row.split(" ")(1),row.split(" ")(2),row.split(" ")(3).toInt))

        val info2 = data2.map(row=>(row.split(" ")(0).toInt,row.split(" ")(1),row.split(" ")(2),row.split(" ")(3).toInt))

        val info3 = data3.map(row=>(row.split(" ")(0).toInt,row.split(" ")(1),row.split(" ")(2),row.split(" ")(3).toInt))

        //三个表经过处理后的格式相同,用 ++ 进行合并

        val info = info1 ++ info2 ++ info3

        //根据题目要求,需要按照学生编号升序、成绩降序排序,使用sortBy函数,在降序列前加上负号即可

        //最后用map函数处理成和源文件格式相同的String写入文件

        val res = info.sortBy(x=>(x._1,-x._4)).map(x=>x._1.toString+" "+x._2+" "+x._3+" "+x._4.toString)

        res.saveAsTextFile("file:///home/hadoop/hm2/result")

 } }

  1. 具体操作
  1. 在终端进入目录/usr/local/spark/mycode/t2,mkdir -p src/main/scala,并新建一个task2.scala文件,将上述代码复制到其中
  2. 在目录/usr/local/spark/mycode/t2目录下新建task2.sbt,在其中写入

 

  1. 在目录/usr/local/spark/mycode/t2下执行打包程序

sudo /usr/local/sbt/sbt package

这里由于更新花费了不少时间,更新完成后重新输入出现信息

 

  1. 最后在目录/usr/local/spark/mycode/t2下执行命令提交程序

/usr/local/spark/bin/spark-submit --class "JoinUp" /usr/local/spark/mycode/t2/target/scala-2.11/task2-project_2.11-1.0.jar

  1. 在文件中指定的目录/home/hadoop/hm2/result 下即可得到结果文件,结果文件包括

 

  1. 结果文件

 

 

 

 

 

 

 

 

 

可以看到,结果文件为已经二次排序好的格式

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值