RDD算子的使用

TransformationDemo.scala


import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}

import scala.collection.mutable.ListBuffer

object TransformationDemo extends App {

  val sparkConf = new SparkConf().setMaster("local").setAppName("TransformationDemo")
  val sc = new SparkContext(sparkConf)


  /** map */
  def map = {
    val list = List("小黄", "小白", "小蓝")
    val listRDD = sc.parallelize(list)
    listRDD.map(("hello", _)).foreach(println)
  }

  //  map
  //  (hello,小黄)
  //  (hello,小白)
  //  (hello,小蓝)


  /** flatMap */
  def flatMap = {
    val list = List("小黄", "小白", "小蓝")
    val listRDD = sc.parallelize(list)
    listRDD.flatMap(_.split(",")).map(("hello", _)).foreach(println)
  }

  //  flatMap
  //  (hello,小黄)
  //  (hello,小白)
  //  (hello,小蓝)


  /**
    * mapPartitions
    *
    * map:
    * 一条数据一条数据的处理(数据库、文件系统等)
    * mapPartitions:
    * 一次获取的是一个分区的数据(hdfs)
    * 正常情况下,mapPartitions是一个高性能的算子
    * 因为每次处理的是一个分区的数据,减少了去获取数据的次数。
    *
    * 但是,如果我们的分区如果设置得不合理,有可能导致每个分区里面的数据量过大。
    */
  def mapPartitions = {
    val list = List(1, 2, 3, 4, 5, 6)
    val listRDD = sc.parallelize(list)

    listRDD.mapPartitions(iterator => {
      val newList: ListBuffer[String] = ListBuffer()
      while (iterator.hasNext) {
        newList.append(s"hello ${iterator.next()}")
      }
      newList.toIterator
    }).foreach(println(_))
  }

  //  mapPartitions
  //  hello 1
  //  hello 2
  //  hello 3
  //  hello 4
  //  hello 5
  //  hello 6


  /**
    * mapPartitionsWithIndex
    * 每次获取和处理的就是一个分区的数据,并且可获知处理的分区的分区号
    *
    */
  def mapPartitionsWithIndex = {
    val list = List(1, 2, 3, 4, 5, 6, 7, 8)
    val listRDD = sc.parallelize(list)

    listRDD.mapPartitionsWithIndex((index, iterator) => {
      val listBuffer: ListBuffer[String] = new ListBuffer()
      while (iterator.hasNext) {
        //        listBuffer.append(s"${index}\t_${iterator.next()}")
        listBuffer.append(f"${index}\t_${iterator.next().toFloat}%3.2f")
        //        listBuffer.append(raw"${index}\t${iterator.next()}")
      }
      listBuffer.iterator
    }, true)
      .foreach(println(_))
  }

  mapPartitionsWithIndex

  //  ------------------------------------------
//  scala中插值器  s  f  raw ,它们都定义在 StringContext.scala 中
  //  s: 解析\t \n等特殊符号
  //  f: 格式化输出 (默认格式是 s)
  //  raw: 不解析\t \n等特殊符号
  //  ${}内可写任意字符串

  //   s        f                  raw
  //   0    _1    0 _1.00       0\t1
  //   0    _2    0 _2.00       0\t2
  //   0    _3    0 _3.00       0\t3
  //   0    _4    0 _4.00       0\t4
  //   0    _5    0 _5.00       0\t5
  //   0    _6    0 _6.00       0\t6
  //   0    _7    0 _7.00       0\t7
  //   0    _8    0 _8.00       0\t8
  //  ------------------------------------------

  /**
    * reduce其实就是将RDD中的所有元素进行合并,当运行call方法时,会传入两个参数,
    * 在call方法中将两个参数合并后返回,而这个返回值会和一个新的RDD中的元素再次传入call方法中,
    * 继续合并,直到合并到只剩下一个元素时。
    */
  def reduce = {
    val list = List(1, 2, 3, 4, 5, 6)
    val listRDD = sc.parallelize(list)
    println(listRDD.reduce(_ + _))
  }

  //  reduce
  //  21


  /**
    * reduceByKey仅将RDD中所有key-value键值对 中 key值相同的value进行合并
    */
  def reduceByKey = {
    val list = List(("小白", 1), ("小灰", 2), ("小灰", 3), ("小白", 4), ("小白", 5))
    val listRDD = sc.parallelize(list)
    listRDD.reduceByKey(_ + _).foreach(tuple => println(s"name: ${tuple._1} -> ${tuple._2}"))
  }

  //  reduceByKey
  //  name: 小白 -> 10
  //  name: 小灰 -> 5


  /** 累加 */
  def union = {
    val list1 = List(1, 2, 3, 4)
    val list2 = List(3, 4, 5, 6)
    val rdd1 = sc.parallelize(list1)
    val rdd2 = sc.parallelize(list2)

    rdd1.union(rdd2).foreach(x => print(s"$x ")) //.foreach(println)
  }

  //  union()
  //  name: 小白 -> 10
  //  name: 小灰 -> 5


  /**
    * groupByKey
    * union只是将两个RDD简单的累加在一起,而join则不一样,join类似与hadoop中的combin操作,只是少了排序这一段
    * groupByKey,因为join可以理解为union与groupByKey的结合:
    * groupByKey是将RDD中的元素进行分组,组名是call方法中的返回值
    *
    * 即groupByKey是将PairRDD中拥有相同key值得元素归为一组。
    */
  def groupByKey = {
    val list = List(("Spark", "sparkSQL"), ("Hadoop", "YARN"), ("Spark", "sparkStreming"), ("Hadoop", "HDFS"))
    val listRDD = sc.parallelize(list)
    val groupByKeyRDD = listRDD.groupByKey()
    groupByKeyRDD.foreach(t => {
      val name = t._1
      val iterator = t._2.iterator
      var include = ""
      while (iterator.hasNext) include = include + iterator.next() + " "
      println(s"name: $name include: $include")
    })
  }

  //  groupByKey
  //  name: Spark include: sparkSQL sparkStreming
  //  name: Hadoop include: YARN HDFS


  /** 连接 */
  def join = {
    val list1 = List((1, "小白"), (2, "小灰"), (3, "小蓝"))
    val list2 = List((1, 99), (2, 98), (3, 97))
    val list1RDD = sc.parallelize(list1)
    val list2RDD = sc.parallelize(list2)

    val joinRDD = list2RDD.join(list1RDD)
    joinRDD.foreach(t => println(s"学号: ${t._1} 姓名: ${t._2._1} 成绩: ${t._2._2}"))
  }

  //  join()
  //  list1RDD.join(list2RDD)       list2RDD.join(list1RDD)
  //  学号: 1 姓名: 小白 成绩: 99      学号: 1 姓名: 99 成绩: 小白
  //  学号: 3 姓名: 小蓝 成绩: 97      学号: 3 姓名: 97 成绩: 小蓝
  //  学号: 2 姓名: 小灰 成绩: 98      学号: 2 姓名: 98 成绩: 小灰


  /** 抽样 */
  def sample = {
    val list = 1.to(100)
    val listRDD = sc.parallelize(list)
    listRDD.sample(false, 0.1, 0).foreach(x => print(s"$x ")) //.foreach(println)
  }

  //  sample()
  //  10 39 41 53 54 58 60 80 89 98


  /** 笛卡尔积 */
  def cartesian = {
    val list1 = List("A", "B")
    val list2 = List(1, 2, 3)
    val list1RDD = sc.parallelize(list1)
    val list2RDD = sc.parallelize(list2)

    list2RDD.cartesian(list1RDD).foreach(x => println(s"${x._1} -> ${x._2}"))
  }

  //  cartesian
  //  list1RDD.cartesian(list2RDD)      list2RDD.cartesian(list1RDD)
  //  A -> 1                            1 -> A
  //  A -> 2                            1 -> B
  //  A -> 3                            2 -> A
  //  B -> 1                            2 -> B
  //  B -> 2                            3 -> A
  //  B -> 3                            3 -> B


  /** 过滤 */
  def filter = {
    val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    val listRDD = sc.parallelize(list)
    listRDD.filter(_ % 2 == 0).foreach(x => print(s"$x "))
  }

  //  filter
  //  2 4 6 8 10


  /** 去重 */
  def distinct = {
    val list = List(1, 1, 2, 2, 3, 4, 5)
    sc.parallelize(list).distinct().foreach(x => print(s"$x "))
  }

  //  distinct
  //  4 1 3 5 2


  /** 交叉点 */
  def intersect = {
    val list1 = List(1, 2, 3, 4)
    val list2 = List(3, 4, 5, 6)
    val list1RDD = sc.parallelize(list1)
    val list2RDD = sc.parallelize(list2)
    list1RDD.intersection(list2RDD).foreach(x => print(s"$x "))
  }

  //  intersect
  //  list1RDD.intersection(list2RDD)   list2RDD.intersection(list1RDD)
  //  4 3                                4 3


  /**
    * 分区数由  多 -> 少
    *
    * 默认分区数由parallelize中的第二个参数 numSlices控制
    */
  def coalesce = {
    val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
    sc.parallelize(list, 5).coalesce(5).foreach(x => print(s"$x "))
  }

  //  coalesce
  //  ------------------------------------------------------------------------------------
  //  numSlicens = 3
  //  coalesce(1)             coalesce(2)           coalesce(3) coalesce(4)....
  //  1 2 3 4 5 6 7 8 9       1 2 3                 1 2 3
  //                          4 5 6 7 8 9           4 5 6
  //                                                7 8 9

  //  numSliens = 5
  //  coalesce(4)          coalesce(5) coalesce(6)...   coalesce(1) coalesce(2) coalesce(3)
  //  1                      1
  //  2 3                    2 3                               1、2、3的结果同上
  //  4 5                    4 5
  //  6 7 8 9                6 7
  //                         8 9
  //  ------------------------------------------------------------------------------------


  /**
    * 进行重分区
    * 解决问题   本区数少  ->  增加分区数
    *
    * 提高并行度
    * 可以绝对控制parallelize中的numSlices数量
    */
  def replication = {
    val list = List(1, 2, 3, 4)
    sc.parallelize(list, 1).repartition(3).foreach(x => print(s"$x "))
  }

  //  replication
  //  ---------------------------------------
  //  numSlicens = 1
  //  repartition(1)        repartition(3)
  //  1 3                   3
  //  2 4                   1 4
  //                        2
  //  ---------------------------------------


  /**
    * repartitionAndSortWithinPartitions
    * 此函数是repartition函数的变种,与repartition函数不通的是,
    * 此函数在给定的partitioner内部进行排序,性能比repartition要高
    *
    * 分区数量由 HashPartitioner 来控制
    */
  def repartitionAndSortWithinPartitions = {
    val list = List(1, 4, 55, 66, 33, 48, 23)
    val listRDD = sc.parallelize(list, 1)
    listRDD.map(x => (x, x))
      .repartitionAndSortWithinPartitions(new HashPartitioner(4))
      .mapPartitionsWithIndex((index, iterator) => {
        val listBuffer: ListBuffer[String] = new ListBuffer
        while (iterator.hasNext) {
          //          listBuffer.append(s"$index${iterator.next()}")
          listBuffer.append(index + "" + iterator.next())
        }
        listBuffer.iterator
      }, false)
      .foreach(x => print(s"$x "))
  }

  //  repartitionAndSortWithinPartitions
  //  ------------------------------------------------------------
  //  numSlices = 1
  //  .repartitionAndSortWithinPartitions(new HashPartitioner(2))
  //  0(4,4) 0(48,48) 0(66,66)
  //  1(1,1) 1(23,23) 1(33,33) 1(55,55)

  //  .repartitionAndSortWithinPartitions(new HashPartitioner(3))
  //  0(33,33) 0(48,48) 0(66,66)
  //  1(1,1) 1(4,4) 1(55,55)
  //  2(23,23)

  //  .repartitionAndSortWithinPartitions(new HashPartitioner(4))
  //  0(4,4) 0(48,48)
  //  1(1,1) 1(33,33)
  //  2(66,66)
  //  3(23,23) 3(55,55)
  //  ------------------------------------------------------------


  /**
    * 对两个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合。
    * 与reduceByKey不同的是针对两个RDD中相同的key的元素进行合并。
    */
  def cogroup = {
    val list1 = List((1, "www"), (2, "bbs"))
    val list2 = List((1, "spark"), (2, "spark"), (3, "fink"))
    val list3 = List((1, "org"), (2, "org"), (3, "com"))

    val list1RDD = sc.parallelize(list1)
    val list2RDD = sc.parallelize(list2)
    val list3RDD = sc.parallelize(list3)

    list1RDD.cogroup(list2RDD, list3RDD)
      .foreach(tuple => println(s"${tuple._1} ${tuple._2._1} ${tuple._2._2} ${tuple._2._3}"))
  }

  //  cogroup
  //  ------------------------------------------------------------
  //  list1RDD.cogroup(list2RDD,list3RDD)
  //  1 CompactBuffer(www) CompactBuffer(spark) CompactBuffer(org)
  //  3 CompactBuffer() CompactBuffer(fink) CompactBuffer(com)
  //  2 CompactBuffer(bbs) CompactBuffer(spark) CompactBuffer(org)
  //  ------------------------------------------------------------


  /**
    * sortByKey函数作用与Key-Value形式的RDD,并对Key进行排序
    * 默认为true,升序;   flase,降序
    *
    */
  def sortByKey = {
    val list = List((99, "小白"), (44, "小紫"), (55, "小黑"), (22, "小蓝"))
    sc.parallelize(list).sortByKey(false)
      .foreach(tuple => println(s"${tuple._1} -> ${tuple._2}"))
  }

  //  sortByKey
  //  ------------------------------------------------------------
  //  sortByKey(false)降序        sortByKey() 默认为true,升序
  //  99 -> 小白                  22 -> 小蓝
  //  55 -> 小黑                  44 -> 小紫
  //  44 -> 小紫                  55 -> 小黑
  //  22 -> 小蓝                  99 -> 小白
  //  ------------------------------------------------------------


  /**
    * aggregateByKey
    * aggregateByKey函数对PairRDD中相同Key的值进行聚合操作,在聚合过程汇总同样使用了一个中立的初始值。
    * 和aggregateByKey函数类似,aggregateByKey返回值的类型不需要和RDD中value的类型一致。
    * 因为aggregateByKey是对相同Key中的值进行聚合操作,所以aggregateByKey函数最终返回的类型还是Pair RDD,
    * 对应的结果是Key和聚合好的值;而aggregate函数直接是返回非RDD的结果,这点需要注意。
    * 在实现过程中,定义了三个aggregateByKey函数原型,但最终调用的aggregateByKey函数都一致。
    *
    * 可设置中立值
    */
  def aggregateByKey = {
    val list = List("hello,world", "hello,spark")
    sc.parallelize(list).flatMap(_.split(",")).map((_, 1)).aggregateByKey(0)(_ + _, _ + _).foreach(tuple => println(s"${tuple._1} -> ${tuple._2}"))
  }

  //  aggregateByKey
  //  aggregateByKey(0)          aggregateByKey(10)
  //  spark -> 1                  spark -> 11
  //  hello -> 2                  hello -> 12
  //  world -> 1                  world -> 11


  //todo..


  sc.stop()
}

posted @ 2019-05-15 00:49  BBBone  阅读(325)  评论(0编辑  收藏  举报