介绍:
Spark中RDD 的算子分为两类:
1、Transformation转换算子:返回一个新的 RDD
2、Action行动算子:返回值不是 RDD(无返回值或返回其他的)
Spark中的转换算子和Java中的Function非常的相似,也和Scala中的函数式编程类似,不过Spark中封装了更多的转换算子(真的是太多了(吐槽))
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5))
val rddLine: RDD[String] = sc.textFile("datas/1.txt")
函数签名:map(func)
函数说明:返回一个新的 RDD,该 RDD 由每一个输入元素经过 func 函数转换后组成
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
// 将每个数据*2
val mapRDD: RDD[Int] = rdd.map(_ * 2)
mapRDD.collect().foreach(println)
sc.stop()
}
函数签名:flatMap(func)
函数说明:类似于 map,但是每一个输入元素可以被映射为 0 或多个输出元素(所以 func 应该返回一个序列,而不是单一元素)
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[String] = sc.makeRDD(List("hello java", "hello scala"))
val flatMapRDD: RDD[String] = rdd.flatMap(_.split(" "))
flatMapRDD.collect().foreach(println)
sc.stop()
}
函数签名:glom(func)
函数说明:类似于 flatMap,将整体按照分区大小拆分为个体
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
// 整体 -> 个体
val glomRDD: RDD[Array[Int]] = rdd.glom()
// 获取每个分组中得最大值
val maxRDD: RDD[Int] = glomRDD.map(_.max)
maxRDD.collect().foreach(println)
sc.stop()
}
函数签名:mapPartitions(func)
函数说明:类似于 map,但独立地在 RDD 的每一个分片上运行,因此在类型为 T 的 RDD 上运行时,func 的函数类型必须是 Iterator[T] => Iterator[U]
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
// 将每个数据*2(这里得分隔符只会打印2次,说明这个函数只执行了2次,分别在2个分区中执行)
val mapPartitionsRDD: RDD[Int] = rdd.mapPartitions(iter => {
println("=================")
iter.map(_ * 2)
})
mapPartitionsRDD.collect().foreach(println)
sc.stop()
}
函数签名:mapPartitionsWithIndex(func)
函数说明:类似于 mapPartitions,但 func 带有一个整数参数表示分片的索引值,因此在类型为 T 的 RDD 上运行时,func 的函数类型必须是(Int, Interator[T]) => Iterator[U]
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
// 将每个数据*2
val mapPartitionsWithIndexRDD: RDD[Int] = rdd.mapPartitionsWithIndex((index, iter) => {
println(s"分区:${index},数据:${iter.toList}")
iter.map(_ * 2)
})
mapPartitionsWithIndexRDD.collect()
sc.stop()
}
函数签名:filter(func)
函数说明:返回一个新的 RDD,该 RDD 由经过 func 函数计算后返回值为 true 的输入元素组成,分区不变,但是分区内的数据可能不均衡,可能会出现数据倾斜
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
// 只保留偶数
val filterRDD: RDD[Int] = rdd.filter(_ % 2 == 0)
filterRDD.collect().foreach(println)
sc.stop()
}
函数签名:sample(withReplacement, fraction, seed)
函数说明:根据指定的规则从数据集中抽取数据
withReplacement:数据是否放回,fraction:数据被抽取的概率,seed: 用于指定随机数生成器种子(如果设置为1,则每次的结果都是一样的)
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
val sampleRDD: RDD[Int] = rdd.sample(withReplacement = false, 0.4)
sampleRDD.collect().foreach(println)
sc.stop()
}
函数签名:distinct([numTasks]))
函数说明:对源 RDD 进行去重后返回一个新的 RDD
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 4, 5, 3, 2))
val distinctRDD: RDD[Int] = rdd.distinct()
distinctRDD.collect().foreach(println)
sc.stop()
}
函数签名:coalesce(numPartitions)
函数说明:缩减分区,用于大数据集过滤之后,提高小数据集的执行效率
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
// 将分区缩减为2个
val coalesceRDD: RDD[Int] = rdd.coalesce(2)
coalesceRDD.collect().foreach(println)
sc.stop()
}
函数签名:repartition(numPartitions)
函数说明:重新分区
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
// 重新分区
val repartitionRDD: RDD[Int] = rdd.repartition(8)
repartitionRDD.collect().foreach(println)
sc.stop()
}
函数签名:sortBy(func,[ascending], [numTasks])
函数说明:根据指定规则进行排序
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(List(4, 2, 3, 4, 1, 3, 5, 7))
// 简单排序
val sortByRDD: RDD[Int] = rdd.sortBy(e => e)
// 复杂排序
val strRDD: RDD[(Int, String)] = sc.makeRDD(List((5, "tom"), (1, "jack"), (2, "jock")))
val strSortByRDD: RDD[(Int, String)] = strRDD.sortBy(e => e._1)
sortByRDD.collect().foreach(println)
strSortByRDD.collect().foreach(println)
sc.stop()
}
函数签名:intersection(otherDataset)
函数说明:交集
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
val rdd2: RDD[Int] = sc.makeRDD(List(5, 6, 1, 2))
// 交集(1, 2)
val intersectionRDD: RDD[Int] = rdd1.intersection(rdd2)
intersectionRDD.collect().foreach(println)
sc.stop()
}
函数签名:union(otherDataset)
函数说明:并集
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
val rdd2: RDD[Int] = sc.makeRDD(List(5, 6, 1, 2))
// 并集(1,2,3,4,5,6,1,2)
val unionRDD: RDD[Int] = rdd1.union(rdd2)
unionRDD.collect().foreach(println)
sc.stop()
}
函数签名:subtract(otherDataset)
函数说明:差集,去除rdd1中和rdd2相同的部分
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
val rdd2: RDD[Int] = sc.makeRDD(List(5, 6, 1, 2))
// 差集(3,4),去除rdd1中和rdd2相同的部分
val subtractRDD: RDD[Int] = rdd1.subtract(rdd2)
subtractRDD.collect().foreach(println)
sc.stop()
}
函数签名:zip(otherDataset)
函数说明:拉链,将相同位置的数据放到一块
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
val rdd2: RDD[Int] = sc.makeRDD(List(5, 6, 1, 2))
// 拉链(1,5)(2,6)(3,1)(4,2)
val zipRDD: RDD[(Int, Int)] = rdd1.zip(rdd2)
zipRDD.collect().foreach(println)
sc.stop()
}
函数签名:reduceByKey(func, [numTasks])
函数说明:相同key的数据进行聚合,相较于groupByKey,reduceByKey的性能要好些
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("b", 4)))
// 相同key的数据进行聚合,这里入参的(x,y)指的是第一个value和第二个value,不是指的key
val reduceByKeyRDD: RDD[(String, Int)] = rdd.reduceByKey((x, y) => {
println(s"x:${x},y:${y}")
x + y
})
reduceByKeyRDD.collect().foreach(println)
sc.stop()
}
函数签名:groupByKey([numTasks])
函数说明:相同key的数据分在一组中,形成一个对偶元组
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("b", 4)))
val groupByKey: RDD[(String, Iterable[Int])] = rdd.groupByKey()
groupByKey.collect().foreach(println)
sc.stop()
}
函数签名:groupBy(func)
函数说明:指定数据进行分组
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("b", 4)))
val groupByRDD: RDD[(String, Iterable[(String, Int)])] = rdd.groupBy(_._1)
groupByRDD.collect().foreach(println)
sc.stop()
}
函数签名:aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])
函数说明:将数据按照不用的规则进行分区内计算和分区间计算
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("b", 4)), 2)
// zeroValue:初始值,用于碰到第一个key的时候和value进行分区内计算
val aggregateByKeyRDD: RDD[(String, Int)] = rdd.aggregateByKey(0)(
// 分区内的计算规则
(x, y) => math.max(x, y),
// 分区间的计算规则
(x, y) => x + y
)
aggregateByKeyRDD.collect().foreach(println)
sc.stop()
}
函数签名:foldByKey(zeroValue)(seqOp, [numTasks])
函数说明:和aggregateByKey相似,当分区外和分区间的操作一样的时候可以使用foldByKey
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("a", 2), ("a", 3), ("b", 4)), 2)
val foldByKeyRDD: RDD[(String, Int)] = rdd.foldByKey(0)(_ + _)
foldByKeyRDD.collect().foreach(println)
sc.stop()
}
函数签名:join(otherDataset, [numTasks])
函数说明:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素对在一起的(K,(V,W))的 RDD
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)))
val rdd2: RDD[(String, Int)] = sc.makeRDD(List(("c", 4), ("a", 5), ("b", 6)))
val joinRDD: RDD[(String, (Int, Int))] = rdd1.join(rdd2)
joinRDD.collect().foreach(println)
sc.stop()
}
函数签名:cogroup(otherDataset, [numTasks])
函数说明:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素对在一起的(K,(V,W))的 RDD
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("map")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1: RDD[(String, Int)] = sc.makeRDD(List(("a", 1), ("b", 2), ("c", 3)))
val rdd2: RDD[(String, Int)] = sc.makeRDD(List(("c", 4), ("a", 5), ("b", 6)))
val cogroupRDD: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)
cogroupRDD.collect().foreach(println)
sc.stop()
}
函数签名:reduce(func)
函数说明:通过 func 函数聚集 RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据
val reduceResult: Int = rdd.reduce(_ + _)
函数签名:collect()
函数说明:将不同分区的数据按照分区顺序采集到内存中,形成数组
val collectResult: Array[Int] = rdd.collect()
函数签名:count()
函数说明:统计数据源数据的个数
val countResult: Long = rdd.count()
函数签名:first()
函数说明:返回 RDD 的第一个元素(类似于 take(1))
val firstResult: Int = rdd.first()
函数签名:take(n)
函数说明:返回一个由数据集的前 n 个元素组成的数组
val takeResult: Array[Int] = rdd.take(2)
函数签名:takeOrdered(n, [ordering])
函数说明:返回自然顺序或者自定义顺序的前 n 个元素
// 逆序取第一个
val takeOrderedResult: Array[Int] = rdd.takeOrdered(1)((x, y) => {
if (x < y) 1 else if (x == y) 0 else -1
})
函数签名:saveAsTextFile(path)
函数说明:将数据集的元素以 textfile 的形式保存到 HDFS 文件系统或者其他支持的文件系统,对于每个元素,Spark 将会调用 toString 方法,将它装换为文件中的文本
rdd.saveAsTextFile("output")
函数签名:foreach(func)
函数说明:在数据集的每一个元素上,运行函数 func 进行更新
rdd.foreach(println)
package com.xx.wc
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object WordCount {
def main(args: Array[String]): Unit = {
// 建立和Spark框架连接
val conf: SparkConf = new SparkConf().setMaster("local").setAppName("WordCount")
val sc = new SparkContext(conf)
// 1.数据
val rdd: RDD[String] = sc.makeRDD(List("hello scala", "hello spark"))
// 2.将行数据拆分
val words: RDD[String] = rdd.flatMap(_.split(" "))
this.wordCountByScala(words)
this.wordCountByReduceByKey(words)
this.wordCountByMapValues(words)
this.wordCountByCountByKey(words)
this.wordCountByCountByValue(words)
// 关闭连接
sc.stop()
}
/**
* 使用scala方式实现wordCount
*
* @param words 单词集合
*/
private def wordCountByScala(words: RDD[String]): Unit = {
// 3.分组
val group: RDD[(String, Iterable[String])] = words.groupBy(word => word)
// 4.转换
val wordToCount: RDD[(String, Int)] = group.map {
case (word, list) =>
(word, list.size)
}
println("==============使用scala实现WordCount===================")
wordToCount.collect().foreach(println)
}
/**
* 使用转换算子reduceByKey的方式实现wordCount
*
* @param words 单词集合
*/
private def wordCountByReduceByKey(words: RDD[String]): Unit = {
val wordToOne: RDD[(String, Int)] = words.map(word => (word, 1))
// 将相同的key进行reduce
val wordToCount: RDD[(String, Int)] = wordToOne.reduceByKey(_ + _)
println("==============使用转换算子reduceByKey实现WordCount===================")
wordToCount.collect().foreach(println)
}
/**
* 使用转换算子mapValues实现wordCount
*
* @param words 单词集合
*/
private def wordCountByMapValues(words: RDD[String]): Unit = {
val group: RDD[(String, Iterable[String])] = words.groupBy(word => word)
val wordCount: RDD[(String, Int)] = group.mapValues(iter => iter.size)
println("==============使用行动算子mapValues实现WordCount===================")
wordCount.collect().foreach(println)
}
/**
* 使用行动算子countByKey的方式实现wordCount
*
* @param words 单词集合
*/
private def wordCountByCountByKey(words: RDD[String]): Unit = {
val mapRDD: RDD[(String, Int)] = words.map((_, 1))
val wordCount: collection.Map[String, Long] = mapRDD.countByKey()
println("==============使用行动算子CountByKey实现WordCount===================")
println(wordCount)
}
/**
* 使用行动算子countByValue的方式实现wordCount
*
* @param words 单词集合
*/
private def wordCountByCountByValue(words: RDD[String]): Unit = {
val mapRDD: RDD[(String, Int)] = words.map((_, 1))
val wordCount: collection.Map[(String, Int), Long] = mapRDD.countByValue()
println("==============使用行动算子CountByValue实现WordCount===================")
println(wordCount)
}
}
数据:时间戳,省份,城市,用户,广告,中间使用空格分开
样例:
1516609143867 6 7 64 16
1516609143869 9 4 75 18
1516609143869 1 7 87 12
1516609143869 2 8 92 9
1516609143869 6 7 84 24
思路:
1、只保留有用的字段
2、将省份和广告作为一个整体,求得广告的数量
3、再将数据结构调整为省份为单独的key,广告和广告数量为一个整体
4、按照广告数量逆序排序并取前三条数据
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("transform")
val sc = new SparkContext(sparkConf)
// 获取数据
// 1516609143867 6 7 64 16
val dataRDD: RDD[String] = sc.textFile("datas/agent.log")
// 只保留需要的数据
// ((6,16),1),((9,18),1)
val mapRDD: RDD[((String, String), Int)] = dataRDD.map(line => {
val datas: Array[String] = line.split(" ")
((datas(1), datas(4)), 1)
})
// 将相同的key(省份,广告)分组,并将value值(广告数量)相加
// ((0,0),15),((0,1),18)
val reduceByKeyRDD: RDD[((String, String), Int)] = mapRDD.reduceByKey(_ + _)
// 调整数据格式
// (0,(0,15)),(0,(1,18))
val newMapRDD: RDD[(String, (String, Int))] = reduceByKeyRDD.map {
case ((prv, ad), sum) => (prv, (ad, sum))
}
// 将调整后的数据按照省份分组
val groupRDD: RDD[(String, Iterable[(String, Int)])] = newMapRDD.groupByKey()
// 按照广告数量进行逆序排序,最后取前三条数据
(0,List((2,29), (24,25), (26,24)))
val resultRDD: RDD[(String, List[(String, Int)])] = groupRDD.mapValues(iter => {
iter.toList.sortBy(_._2)(Ordering.Int.reverse).take(3)
})
resultRDD.collect().foreach(println)
sc.stop()
}