[Spark学习04]Spark Transformation和Action

4.1 Transformation算子

基本初始化

private val conf: SparkConf = new SparkConf().setAppName("TestTransformation").setMaster("local")
  private val sparkContext = new SparkContext(conf)

4.2 map、flatMap、mapParations、mapPartitionsWithIndex

Map

代码:

def map(): Unit ={

    val list = List("张无忌", "赵敏", "周芷若")

    val listRDD = sc.parallelize(list)

    val nameRDD = listRDD.map(name => "Hello " + name)

    nameRDD.foreach(name => println(name))

  }

运行结果:

总结:

可以看出,对于map算子,源listRDD的每个元素都会进行计算,由于是依次进行传参,所以他是有序的,新RDD的元素顺序与源RDD是相同的。而由有序又引出接下来的flatMap。

Flatmap

代码:

def flatMap(): Unit ={

    val list = List("张无忌 赵敏","宋青书 周芷若")

    val listRDD = sc.parallelize(list)

    val nameRDD = listRDD.flatMap(line => line.split(" ")).map(name => "Hello " + name)

    nameRDD.foreach(name => println(name))

  }

运行结果:

总结:

flatMap的特性决定了这个算子在对需要随时增加元素的时候十分好用,比如在对源RDD查漏补缺时。

map和flatMap都是依次进行参数传递的,但有时候需要RDD中的两个元素进行相应操作时(例如:算存款所得时,下一个月所得的利息是要原本金加上上一个月所得的本金的),这两个算子便无法达到目的了,这是便需要mapPartitions算子,他传参的方式是将整个RDD传入,然后将一个迭代器传出生成一个新的RDD,由于整个RDD都传入了,所以便能完成前面说的业务。

mapPartitions

代码:

def mapParations(): Unit ={

    val list = List(1,2,3,4,5,6)

    val listRDD = sc.parallelize(list,2)

    listRDD.mapPartitions(iterator => {

      val newList: ListBuffer[String] = ListBuffer()

      while (iterator.hasNext){

        newList.append("hello " + iterator.next())

      }

      newList.toIterator

    }).foreach(name => println(name))

  }

运行结果:

mapPartitionsWithIndex

每次获取和处理的就是一个分区的数据,并且知道处理的分区的分区号是啥?

代码:

def mapPartitionsWithIndex(): Unit ={

    val list = List(1,2,3,4,5,6,7,8)

    sc.parallelize(list).mapPartitionsWithIndex((index,iterator) => {

      val listBuffer:ListBuffer[String] = new ListBuffer

      while (iterator.hasNext){

        listBuffer.append(index+"_"+iterator.next())

      }

      listBuffer.iterator

    },true)

      .foreach(println(_))

  }

运行结果:

4.3 reduce、reduceByKey

Reduce

reduce其实是将RDD中的所有元素进行合并,当运行call方法时,会传入两个参数,在call方法中将两个参数合并后返回,而这个返回值回合一个新的RDD中的元素再次传入call方法中,继续合并,直到合并到只剩下一个元素时。

代码:

def reduce(): Unit ={

    val list = List(1,2,3,4,5,6)

    val listRDD = sc.parallelize(list)

    val result = listRDD.reduce((x,y) => x+y)

    println(result)

  }

运行结果:

reduceByKey

reduceByKey仅将RDD中所有K,V对中K值相同的V进行合并。

代码:

def reduceByKey(): Unit ={

    val list = List(("武当", 99), ("少林", 97), ("武当", 89), ("少林", 77))

    val mapRDD = sc.parallelize(list)

    val resultRDD = mapRDD.reduceByKey(_+_)

    resultRDD.foreach(tuple => println("门派: " + tuple._1 + "->" + tuple._2))

  }

运行结果:

4.4 union,join和groupByKey

Union

当要将两个RDD合并时,便要用到union和join,其中union只是简单的将两个RDD累加起来,可以看做List的addAll方法。就想List中一样,当使用union及join时,必须保证两个RDD的泛型是一致的。

代码:

def union(): Unit ={

    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(println(_))

  }

运行结果:

groupByKey

在说join之前说说groupByKey,因为join可以理解为union与groupByKey的结合:groupBy是将RDD中的元素进行分组,组名是call方法中的返回值,而顾名思义groupByKey是将PairRDD中拥有相同key值得元素归为一组。即:

代码:

def groupByKey(): Unit ={

    val list = List(("武当", "张三丰"), ("峨眉", "灭绝师太"), ("武当", "宋青书"), ("峨眉", "周芷若"))

    val listRDD = sc.parallelize(list)

    val groupByKeyRDD = listRDD.groupByKey()

    groupByKeyRDD.foreach(t => {

      val menpai = t._1

      val iterator = t._2.iterator

      var people = ""

      while (iterator.hasNext) people = people + iterator.next + " "

      println("门派:" + menpai + "人员:" + people)

    })

  }

运行结果:

Join

join是将两个PairRDD合并,并将有相同key的元素分为一组,可以理解为groupByKey和Union的结合

代码:

def join(): Unit = {

    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 = list1RDD.join(list2RDD)

    joinRDD.foreach(t => println("学号:" + t._1 + " 姓名:" + t._2._1 + " 成绩:" + t._2._2))

  }

运行结果:

4.5 sample、cartesian

Sample

Sample(withReplacement : scala.Boolean, fraction : scala.Double,seed scala.Long)

参数:

  • withReplacement:表示抽出样本后是否在放回去,true表示会放回去,这也就意味着抽出的样本可能有重复
  • fraction:抽出多少,这是一个double类型的参数,01之间,eg:0.3表示抽出30%
  • seed:表示一个种子,根据这个seed随机抽取

代码:

def sample(): Unit ={
    val list = 1 to 100
    val listRDD = sc.parallelize(list)
    listRDD.sample(false,0.1,0).foreach(num => print(num + " "))
  }

运行结果:

Cartesian

cartesian是用于求笛卡尔积的

代码:

def cartesian(): Unit ={

    val list1 = List("A","B")

    val list2 = List(1,2,3)

    val list1RDD = sc.parallelize(list1)

    val list2RDD = sc.parallelize(list2)

    list1RDD.cartesian(list2RDD).foreach(t => println(t._1 +"->"+t._2))

  }

运行结果:

4.6 filter、distinct、intersection

Filter

过滤出偶数

代码:

 def filter(): Unit ={
    val list = List(1,2,3,4,5,6,7,8,9,10)
    val listRDD = sc.parallelize(list)
    listRDD.filter(num => num % 2 ==0).foreach(print(_))
  }

运行结果:

Distinct

去重

代码:

def distinct(): Unit ={
    val list = List(1,1,2,2,3,3,4,5)
    sc.parallelize(list).distinct().foreach(println(_))
  }

运行结果:

Intersection

代码:

def intersection(): Unit ={

    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(println(_))

  }

运行结果:

4.7 coalesce、repartition、repartitionAndSortWithinPartitions

Coalesce

分区数由多  -》 变少

代码:

def coalesce(): Unit = {

    val list = List(1,2,3,4,5,6,7,8,9)

    sc.parallelize(list,3).coalesce(1).foreach(println(_))

  }

运行结果:

Replication

进行重分区,解决的问题:本来分区数少  -》 增加分区数

代码:

def replication(): Unit ={

    val list = List(1,2,3,4)

    val listRDD = sc.parallelize(list,1)

    listRDD.repartition(2).foreach(println(_))

  }

运行结果:

repartitionAndSortWithinPartitions

repartitionAndSortWithinPartitions函数是repartition函数的变种,与repartition函数不同的是,repartitionAndSortWithinPartitions在给定的partitioner内部进行排序,性能比repartition要高。

代码:

def repartitionAndSortWithinPartitions(): Unit ={

    val list = List(1, 4, 55, 66, 33, 48, 23)

    val listRDD = sc.parallelize(list,1)

    listRDD.map(num => (num,num))

      .repartitionAndSortWithinPartitions(new HashPartitioner(2))

      .mapPartitionsWithIndex((index,iterator) => {

        val listBuffer: ListBuffer[String] = new ListBuffer

        while (iterator.hasNext) {

          listBuffer.append(index + "_" + iterator.next())

        }

        listBuffer.iterator

      },false)

      .foreach(println(_))

  }

运行结果:

4.8 cogroup、sortBykey、aggregateByKey

Cogroup

对两个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合。与reduceByKey不同的是针对两个RDD中相同的key的元素进行合并。

代码:

def cogroup(): Unit ={

    val list1 = List((1, "www"), (2, "bbs"))

    val list2 = List((1, "cnblog"), (2, "cnblog"), (3, "very"))

    val list3 = List((1, "com"), (2, "com"), (3, "good"))

    val list1RDD = sc.parallelize(list1)

    val list2RDD = sc.parallelize(list2)

    val list3RDD = sc.parallelize(list3)

    list1RDD.cogroup(list2RDD,list3RDD).foreach(tuple =>

      println(tuple._1 + " " + tuple._2._1 + " " + tuple._2._2 + " " + tuple._2._3))

  }

运行结果:

 

sortBykey

sortByKey函数作用于Key-Value形式的RDD,并对Key进行排序。它是在org.apache.spark.rdd.OrderedRDDFunctions中实现的,实现如下

def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.size)

    : RDD[(K, V)] =

{

  val part = new RangePartitioner(numPartitions, self, ascending)

  new ShuffledRDD[K, V, V](self, part)

    .setKeyOrdering(if (ascending) ordering else ordering.reverse)

}

从函数的实现可以看出,它主要接受两个函数,含义和sortBy一样,这里就不进行解释了。该函数返回的RDD一定是ShuffledRDD类型的,因为对源RDD进行排序,必须进行Shuffle操作,而Shuffle操作的结果RDD就是ShuffledRDD。其实这个函数的实现很优雅,里面用到了RangePartitioner,它可以使得相应的范围Key数据分到同一个partition中,然后内部用到了mapPartitions对每个partition中的数据进行排序,而每个partition中数据的排序用到了标准的sort机制,避免了大量数据的shuffle。下面对sortByKey的使用进行说明:

代码:

def sortByKey(): Unit ={

    val list = List((99, "张三丰"), (96, "东方不败"), (66, "林平之"), (98, "聂风"))

    sc.parallelize(list).sortByKey(false).foreach(tuple => println(tuple._2 + "->" + tuple._1))

  }

运行结果:

aggregateByKey

aggregateByKey函数对PairRDD中相同Key的值进行聚合操作,在聚合过程中同样使用了一个中立的初始值。和aggregate函数类似,aggregateByKey返回值的类型不需要和RDD中value的类型一致。因为aggregateByKey是对相同Key中的值进行聚合操作,所以aggregateByKey函数最终返回的类型还是Pair RDD,对应的结果是Key和聚合好的值;而aggregate函数直接是返回非RDD的结果,这点需要注意。在实现过程中,定义了三个aggregateByKey函数原型,但最终调用的aggregateByKey函数都一致。

代码:

def aggregateByKey(): Unit ={

    val list = List("you,jump", "i,jump")

    sc.parallelize(list)

      .flatMap(_.split(","))

      .map((_, 1))

      .aggregateByKey(0)(_+_,_+_)

      .foreach(tuple =>println(tuple._1+"->"+tuple._2))

  }

运行结果:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值