我们的数据集为Data01.txt,该数据集包含了某大学计算机系的成绩,数据格式如下所示:
Tom,DataBase,80
Tom,Algorithm,50
Tom,DataStructure,60
Jim,DataBase,90
Jim,Algorithm,60
Jim,DataStructure,80
下面给上数据的链接
链接:https://pan.baidu.com/s/1807ykYwSLRuHnd9tYzEopw
提取码:2xdm
打开spark-shell
这里我们使用spark-shell连接上yarn
spark-shell --master yarn
连接上之后我们就可以进入交互界面了
scala>
然后导入我们实验用的数据,我的数据放在hdfs上面了所以用如下方式导入,具体导入可以根据自己文件放的位置决定(注意用HDFS的话路径为HDFS://ip:端口/路径,用本地文件是file:///路径,不然可能报错)
scala> val input=sc.textFile("HDFS://namenode:9000/Data01.txt")
input: org.apache.spark.rdd.RDD[String] = HDFS://namenode:9000/Data01.txt MapPartitionsRDD[3] at textFile at <console>:24
具体问题
(1) 该系总共有多少学生;
-
分析输入文件的格式,可以看到每行是一个字符串,RDD读入时,会将每行作为一个元素,我们要统计该系共有多少人,应该先从上表获得共有多少人的成绩,然后将其去重,最后count一下就可以得到总人数
input.map(line=>line.split(",")(0)).distinct().count() res0: Long = 265
-
map表示对该RDD中的每个元素做下面函数中的操作
-
line=>line.split(",")表示将每个元素用","分隔开转化为一个数组,然后将该数组的第一个值赋给新RDD的每个元素
-
distinct():对RDD中的元素进行去重
-
count():返回RDD中的元素个数
(2) 该系共开设来多少门课程;
-
该题和第一题思想是一样的,一个统计的是有多少不同的人,一个统计的是有多少不同的课程
-
代码
scala> input.map(line=>line.split(",")(1)).distinct().count() res4: Long = 8
(3) Tom同学的总成绩平均分是多少;
-
求平均分首先我们想到的做法是,找到所有叫Tom的人,将他所有的分数加起来,然后用总分除以总课程数就可以得到平均分
-
所以我们先找到叫Tom的人
scala> val Tom=input.filter(t => t.split(",")(0) == "Tom") Tom: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[14] at filter at <console>:25
-
然后我们构造一种数据结构(Tom,(成绩,1)),这样的话我们就可以得到Tom的总课程成绩以及总课程门数
scala> val Tom_1=Tom.map(t => (t.split(",")(0), (t.split(",")(2).toInt,1))) Tom_1: org.apache.spark.rdd.RDD[(String, (Int, Int))] = MapPartitionsRDD[4] at map at <console>:25
-
然后我们将上面得到的(成绩,1)全部加起来
scala> val Tom_2=Tom_1.reduceByKey((a,b)=>(a._1+b._1,a._2+b._2)) Tom_2: org.apache.spark.rdd.RDD[(String, (Int, Int))] = ShuffledRDD[5] at reduceByKey at <console>:25
-
最后我们只要将总成绩除以总课程数即可
scala> Tom_2.mapValues(a=>a._1/a._2).first() res6: (String, Int) = (Tom,30)
(4) 求每名同学的选修的课程门数;
- 类似上面第三题的思想,不用过滤人名了,这次将(课程,1)作为新构建的数据结构,同时本次只关心课程门数,所以课程那个位置放什么都可以,有了上面一题得到基础,这次我们可以直接写出完整的代码
- 代码
scala> input.map(t=>(t.split(",")(0), (t.split(",")(1),1))).reduceByKey((a,b)=>(a._1,a._2+b._2)).mapValues(a=>a._2).foreach(println) (Bartholomew,5) (Lennon,4) (Joshua,4) (Tom,5) ...
(5) 该系DataBase课程共有多少人选修;
-
本题思路很简单,过滤出课程名是"DataBase"的课程即可
-
代码
scala> input.filter(t => t.split(",")(1)=="DataBase").count() res16: Long = 126
(6) 各门课程的平均分是多少;
-
本题思路和求Tom的平均分一致,这里不过将Tom改成了课程名而已,同样的这里求所有课程的平均分,所以不需要过滤
-
代码
scala> input.map(t=>(t.split(",")(1), (t.split(",")(2).toInt,1))).reduceByKey((a,b)=>(a._1+b._1,a._2+b._2)).mapValues(a=>a._1/a._2).foreach(println) (Python,57) (OperatingSystem,54) (CLanguage,50) (Software,50) (Algorithm,48) (DataStructure,47) (DataBase,50) (ComputerNetwork,51)
(7)使用累加器计算共有多少人选了DataBase这门课。
-
首先,我们过滤出DataBase这门课,然后构造数据结构(“Database”,1),这样我们只要将所有元素的value相加即可
scala> val database=input.filter(t=>t.split(",")(1)=="DataBase").map(t=>(t.split(",")(1),1)) database: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[3] at map at <console>:25
-
然后我们构造一个累加器来进行对1的累加
scala> val counter=sc.longAccumulator("database_counter") counter: org.apache.spark.util.LongAccumulator = LongAccumulator(id: 1, name: Some(database_counter), value: 0)
-
进行累加得到结果
scala> database.values.foreach(a=>counter.add(a)) scala> counter.value res5: Long = 126
总结
RDD编程,灵巧方便,但由于可以使用的方法较多,要多熟悉给中方法的含义,面对问题先分析再进行编程,这样由于水平有限,只能做到这一步,后面还有更多的问题要去解决,还是需要持续的学习