Spark RDD Transformation 练习

在过去的几个月里,我断断续续写了Spark,Spark SQL, Spark Streaming相关的文章,自己也对Spark有了一个基本的认识。 但是仅仅这样不能算学会了spark。
回想过去一年里看过的书,只有那些写过博客的内容才印象深刻,其他的书甚至都忘了讲什么的。
再加上最近我一直在思考怎么才算掌握spark,用spark 能解决什么问题? 正所谓纸上得来终觉浅,绝知此事要躬行。所以我写这篇Spark RDD 练习,帮助我们加深对Spark知识的理解。

这篇博客涉及到的知识点有:

  1. 如何反转pair RDD中的key和value
  2. 如何通过Map实例创建一个RDD
  3. 如何连接两个RDD
  4. 如何利用Spark RDD找最大/小值
  5. 如何进行RDD的自连接

重温Spark RDD的知识

Spark RDD 分为普通的Spark RDD和Spark Pair RDD.
Spark RDD 有10个Transformations(参见Spark RDD(一) RDD的创建和转化操作), 9 Actions(参见Spark RDD(二) RDD的相关行为(Action)操作).
Spark Pair RDD 有4个Transformations, 3个Actions.(参见Spark RDD(三) 键值对RDD相关的转换和动作操作
)

实际练习

我们有movies.tsv和movie-ratings.tsv两个文件。在这两个文件中通过Tab分隔列。
movies文件中每一行数据包含3列,分别是演员名, 电影名, 年份。一行数据就代表了一名演员参演了一部电影。如果一部电影有十个演员对应的就会有10行数据。

movies.tsv:

McClure, Marc (I)       Freaky Friday   2003

movie-ratings.tsv 中则每一条数据包含3列分别是评分,电影名,年份。
movies-rating.tsv

1.6339  'Crocodile' Dundee II   1988
练习一

计算每年的电影数。输出应该包含两列:年份和电影数量,并且按电影数量降序排序。

解决方法一利用reduceByKey()

// step1: 根据movies.tsv创建RDD
var movies=spark.sparkContext.textFile("movies.tsv") 

// step2: 创建pair RDD 因为我们只需要统计年份和电影,所以构建一个(year, 1)的pair RDD
var pairMovies=movies.map(row=>(row.split("\t")(2),1))
pairMovies.first()

// step3: 利用reduceByKey()累加每年对应的电影数
var yearCount=pairMovies.reduceByKey((total,value)=>total+value)

// step4: 反转key/vaule pair RDD
var result=yearCount.map(item=>(item._2, item._1))

// step5: 降序排序
result.sortByKey(false).collect()

输出为

Array[(Int, String)] = Array((2078,2006), (2005,2004), (1986,2007), (1960,2005), (1926,2011), (1892,2008), (1890,2009), (1843,2010), (1834,2002), (1687,2001), (1652,2003), (1639,2000), (1584,1997), (1401,1999), (1372,1998), (916,1996), (601,2012), (498,1995), (358,1994), (304,1992), (270,1993), (214,1990), (183,1991), (174,1986), (152,1989), (149,1984), (133,1985), (126,1987), (119,1983), (111,1988), (103,1982), (53,1981), (47,1980), (40,1977), (37,1979), (30,1978), (12,1972), (5,1975), (5,1973), (2,1967), (2,1961))

解决方法二利用countByKey()计数每年对应的电影数,但是countByKey的输出是Map类型

// step1: 根据movies.tsv创建RDD
var movies=spark.sparkContext.textFile("movies.tsv") 

// step2: 创建pair RDD 因为我们只需要统计年份和电影,所以构建一个(year, 1)的pair RDD

var pairMovies=movies.map(row=>(row.split("\t")(2),1))
pairMovies.first()

// step3: 利用countByKey()计算每年对应的电影数
var yearCountMap=pairMovies.countByKey()

输出为:

yearCountMap: scala.collection.Map[String,Long] = Map(2003 -> 1652, 1983 -> 119, 1978 -> 30, 1961 -> 2, 1997 -> 1584, 1986 -> 174, 1975 -> 5, 1989 -> 152, 2002 -> 1834, 1993 -> 270, 1982 -> 103, 1991 -> 183, 1980 -> 47, 2007 -> 1986, 2004 -> 2005, 1996 -> 916, 2011 -> 1926, 2001 -> 1687, 1979 -> 37, 2008 -> 1892, 2012 -> 601, 1999 -> 1401, 1981 -> 53, 2005 -> 1960, 1988 -> 111, 1992 -> 304, 2010 -> 1843, 2009 -> 1890, 1977 -> 40, 2000 -> 1639, 1995 -> 498, 1985 -> 133, 1984 -> 149, 1998 -> 1372, 1967 -> 2, 1972 -> 12, 1987 -> 126, 1994 -> 358, 1990 -> 214, 2006 -> 2078, 1973 -> 5)

这里需要根据map重新构造一个RDD

var yearCount=spark.sparkContext.parallelize(yearCountMap.toSeq)

然后同样的执行反转排序输出

// step4: 反转key/vaule pair RDD
var result=yearCount.map(item=>(item._2, item._1))

// step5: 降序排序
result.sortByKey(false).collect()

练习二

计算每一个演员参演的电影数。输出应该包含两列:演员和参演电影数量,并按电影数量降序排序

因为movies.tsv的数据格式就是一行数据就对应一名演员和其参演的电影
解决方法类似, 因为每一个Tran’s’formation的结果都是RDD所以我们可以写成下面这种形式更简洁

var actorMovies=movies
.map(row=>(row.split("\t")(0),1))
.reduceByKey((total,value)=>total+value)
.map(row=>(row._2,row._1))
.sortByKey(false)

actorMovies.take(10)

输出为

Array[(Int, String)] = Array((38,Welker, Frank), (38,Tatasciore, Fred), (32,Jackson, Samuel L.), (31,Harnell, Jess), (27,Willis, Bruce), (27,Damon, Matt), (26,Cummings, Jim (I)), (25,Lynn, Sherry (I)), (25,Bergen, Bob (I)), (25,McGowan, Mickie))

从结果我们可以看出“Welker, Frank”参演电影最多,演了38部电影

练习三

计算每年评分最高的电影并包含此电影的所有演员。输出应该包含4列:年份,电影名, 评分, 用分号分隔的演员名。
这个练习要求连接movies.tsv和movie-ratings.tsv两个文件,并且有两种思路。第一种是先找出评分最高的电影,再连接相关的演员。第二种思路先执行连接操作再去查找评分最高的电影和演员。

解决方法一:先找出每年评分最高的电影

// step1 创建普通RDD
var movieRatings=spark.sparkContext.textFile("movie-ratings.tsv")

// step2先创建Pair RDD (year, (rating, movie_name)), 然后利用reduceByKey找出评分最高的电影
var highestRatingMoviePerYear=movieRatings
.map(row=>{var temp=row.split("\t"); (temp(2).toLong,(temp(0).toFloat,temp(1)))})
.reduceByKey((a,b)=>{if (a._1>b._1) a else b})

// step3 创建((year,movie),rating)的PairRDD 以便后续执行join操作
var yearNameRating=highestRatingMoviePerYear.map(row=>((row._1, row._2._2), row._2._1))

//接下来是操作movies 这个RDD 把同一部电影的演员合并起来
var movieYearActor=movies.map(row=>{var tmp=row.split("\t"); (tmp(1), (tmp(0), tmp(2).toLong))})
var yearNameMovie=movieYearActor
.reduceByKey((a,b)=>{((a._1+";"+b._1),a._2)})
.map(row=>{((row._2._2, row._1), row._2._1)})

// 执行join后,调整格式
var result=yearNameRating.join(yearNameMovie).map(row=>{(row._1._1, row._1._2, row._2._1, row._2._2)})

练习四

查找合作次数最多的一对演员。输出应该包含三列:演员1,演员2和数量, 并以数量降序排序。提示,这个问题需要自连接操作。

// 创建PairRDD
var movieYearActor=movies.map(row=>{var tmp=row.split("\t"); (tmp(1), (tmp(0), tmp(2).toLong))})

// 自联结并过滤自身,结果为((actor1, actor2),1)
var combineActors= movieYearActor.join(movieYearActor).map(row=>{((row._2._1._1, row._2._2._1),1)}).filter(row=>{ row._1._1!=row._1._2}) 

// 反转key/value 统计并降序展示
var result=combineActors.reduceByKey((a,b)=>{a+b}).map(row=>(row._2,row._1)).sortByKey(false)
result.take(10)

经过这几个练习我对RDD的Transformation加深了理解。对Spark的方便之处也有了初步的体会。

你可能感兴趣的:(spark,大数据)