[Scala] Spark常用算子较详细说明及示例

Spark常用算子较详细说明及示例

直接上码

package com.ck.spark

import org.apache.spark.rdd.RDD
import org.apache.spark._

object SparkOperations {
  def main(args: Array[String]) {
    val config: SparkConf = new SparkConf().setMaster("local[*]").setAppName("Ha")

    val sc = new SparkContext(config)
    sc.setLogLevel("WARN")

    // Value类型

    // makeRDD的底层实现就是parallelize
    val arrayRDD: RDD[Int] = sc.parallelize(Array(5, 6, 7, 8))

    // textFile与上两个的默认核数有区别,上两个是机器核数与2取大值,下是取小值
    val file: RDD[String] = sc.textFile("in")

    // map(func)
    // 作用:返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成
    println("\nmap")
    val mapRDD: RDD[Int] = sc.makeRDD(1 to 5)
    mapRDD.map("X" + _).collect().foreach(println) // X1 X2 X3 X4 X5

    // mapPartitions(func)可对一个RDD中所有分区进行遍历
    // 作用:类似于map,但独立地在RDD的每一个分片上运行
    // 因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]
    // 假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区
    println("\nmapPartitions")
    val listRDD: RDD[Int] = sc.makeRDD(1 to 4)
    val mapPartitionsRDD: RDD[Int] = listRDD.mapPartitions(datas => {
      datas.map(_ * 2)
    })
    mapPartitionsRDD.collect().foreach(println) // Array[Int] = Array(2, 4, 6, 8)

    // mapPartitionsWithIndex(func)会额外多出分区的index信息
    // 作用:类似于mapPartitions,但func带有一个整数参数表示分片的索引值
    // 因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U]
    println("\nmapPartitionsWithIndex")
    val tupleRDD: RDD[(Int, String)] = listRDD.mapPartitionsWithIndex {
      case (num, datas) => datas.map((_, "分区号:" + num))
    }
    tupleRDD.collect().foreach(println) // (1,分区号:0) (2,分区号:1) (3,分区号:2) (4,分区号:3) (5,分区号:3)

    // glom
    // 作用:将每一个分区形成一个数组,形成新的RDD类型时RDD[Array[T]]
    println("\nglom")
    val glomRDD: RDD[Int] = sc.makeRDD(1 to 16, 4)
    // 将一个分区数据放到一个数组中
    glomRDD.glom().collect().foreach(array => {
      println(array.mkString(","))
    })
    // Array[Array[Int]] = Array(Array(1, 2, 3, 4), Array(5, 6, 7, 8), Array(9, 10, 11, 12), Array(13, 14, 15, 16))

    // groupBy(func)
    // 作用:分组,按照传入函数的返回值进行分组。将相同的key对应的值放入一个迭代器
    println("\ngroupBy")
    val groupByRDD: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
    val value: RDD[(Int, Iterable[Int])] = groupByRDD.groupBy(_ % 2)
    value.collect().foreach(println)
    // Array[(Int, Iterable[Int])] = Array((0,CompactBuffer(2, 4)), (1,CompactBuffer(1, 3)))

    // filter(func)
    // 作用:过滤。返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成
    println("\nfilter")
    val filterRDD: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4))
    filterRDD.filter(_ % 2 == 0).collect().foreach(println) // Array[Int] = Array(2, 4)

    // sample(withReplacement, fraction, seed)
    // 作用:以指定的随机种子随机抽样出数量为fraction的数据
    // withReplacement表示是抽出的数据是否放回,true为有放回的抽样,false为无放回的抽样,seed用于指定随机数生成器种子
    println("\nsample")
    val sampleRDD: RDD[Int] = sc.makeRDD(1 to 10).sample(false, 0.4, 1)
    sampleRDD.collect().foreach(println) // Array[Int] = Array(3, 5, 6, 8)

    // distinct([numTasks]))
    // 顺序会被打乱,因为中间发生了shuffle
    // 作用:对源RDD进行去重后返回一个新的RDD。默认情况下,只有8个并行任务来操作,但是可以传入一个可选的numTasks参数改变它
    println("\ndistinct")
    // distinct可添加分区参数,如distinct(2)
    val distinctRDD: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 2, 3)).distinct()
    distinctRDD.collect().foreach(println) // Array[Int] = Array(4, 1, 2, 3)

    // coalesce(numPartitions)
    // 作用:缩减分区数,用于大数据集过滤后,提高小数据集的执行效率,无shuffle
    println("\ncoalesce")
    val coalesceRDD: RDD[Int] = sc.makeRDD(1 to 16, 4).coalesce(3)
    println("分区数:" + coalesceRDD.partitions.length) // 分区数:3

    // repartition(numPartitions)
    // 作用:根据分区数,重新通过网络随机洗牌所有数据
    // coalesce可以选择是否进行shuffle过程,由参数shuffle: Boolean = false/true决定
    // repartition实际上是调用的coalesce,默认是进行shuffle的
    // def repartition... = withScope { coalesce(numPartitions, shuffle = true) }
    println("\nrepartition")
    val repartitionRDD: RDD[Int] = sc.makeRDD(1 to 16, 4).repartition(3)
    println(repartitionRDD.partitions.length) // 3

    // sortBy(func,[ascending], [numTasks])
    // 作用;使用func先对数据进行处理,按照处理后的数据比较结果排序,默认为正序
    println("\nsortBy")
    val sortByRDD: RDD[Int] = sc.makeRDD(List(2, 1, 3, 4))
    sortByRDD.sortBy(x => x).collect().foreach(println) // Array[Int] = Array(1, 2, 3, 4)
    sortByRDD.sortBy(x => x % 3).collect().foreach(println) // Array[Int] = Array(3, 1, 4, 2)

    // pipe(command, [envVars])
    // 作用:管道,针对每个分区,都执行一个shell脚本,返回输出的RDD
    // 略

    // 双Value类型

    // union(otherDataset)
    // 作用:对源RDD和参数RDD求并集后返回一个新的RDD
    println("\nunion")
    val unionRDD1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
    val unionRDD2: RDD[Int] = sc.makeRDD(List(5, 6, 7))
    val unionRDD3 = unionRDD1.union(unionRDD2)
    unionRDD3.collect().foreach(println) // Array[Int] = Array(1, 2, 3, 4, 5, 5, 6, 7)

    // subtract (otherDataset)
    // 作用:计算差的一种函数,去除两个RDD中相同的元素,不同的RDD将保留下来
    println("\nsubtract")
    val subtractRDD1: RDD[Int] = sc.makeRDD(3 to 8)
    val subtractRDD2: RDD[Int] = sc.makeRDD(1 to 5)
    subtractRDD1.subtract(subtractRDD2).collect().foreach(println) // Array[Int] = Array(8, 6, 7)

    // intersection(otherDataset)
    // 作用:对源RDD和参数RDD求交集后返回一个新的RDD
    println("\nintersection")
    val intersectionRDD1: RDD[Int] = sc.makeRDD(1 to 7)
    val intersectionRDD2: RDD[Int] = sc.makeRDD(5 to 10)
    val intersectionRDD3 = intersectionRDD1.intersection(intersectionRDD2)
    intersectionRDD3.collect().foreach(println) // Array[Int] = Array(5, 6, 7)

    // cartesian(otherDataset)
    // 笛卡尔积(尽量避免使用)
    println("\ncartesian")
    val cartesianRDD1: RDD[Int] = sc.makeRDD(1 to 3)
    val cartesianRDD2: RDD[Int] = sc.makeRDD(2 to 5)
    cartesianRDD1.cartesian(cartesianRDD2).collect().foreach(println)
    // Array[(Int, Int)] = Array((1,2), (1,3), (1,4), (1,5), (2,2), (2,3), (2,4), (2,5), (3,2), (3,3), (3,4), (3,5))

    // zip(otherDataset)
    // 作用:将两个RDD组合成Key/Value形式的RDD,这里默认两个RDD的partition数量以及元素数量都相同,否则会抛出异常
    println("\nzip")
    val zipRDD1: RDD[Int] = sc.makeRDD(Array(1, 2, 3), 3)
    val zipRDD2: RDD[String] = sc.makeRDD(Array("a", "b", "c"), 3)
    zipRDD1.zip(zipRDD2).collect().foreach(println) // Array[(Int, String)] = Array((1,a), (2,b), (3,c))

    // Key-Value类型

    // partitionBy
    // 作用:对pairRDD进行分区操作
    // 如果原有的partionRDD和现有的partionRDD是一致的话就不进行分区,否则会生成ShuffleRDD,即会产生shuffle过程
    println("\npartitionBy")
    val partitionByRDD1: RDD[(Int, String)] = sc
      .parallelize(Array((1, "aaa"), (2, "bbb"), (3, "ccc"), (4, "ddd")), 4)
    println(partitionByRDD1.partitions.length) // 4
    val partitionByRDD2 = partitionByRDD1.partitionBy(new HashPartitioner(2))
    println(partitionByRDD2.partitions.length) // 2

    // groupByKey
    // 作用:groupByKey也是对每个key进行操作,但只生成一个sequence
    println("\ngroupByKey")
    val groupByKeyRDD: RDD[(String, Int)] = sc
      .makeRDD(Array("one", "two", "two", "three", "three", "three"))
      .map((_, 1))
    val group = groupByKeyRDD.groupByKey()
    group.collect().foreach(println)
    // Array[(String, Iterable[Int])] = Array((two,CompactBuffer(1, 1)),
    //          (one,CompactBuffer(1)), (three,CompactBuffer(1, 1, 1)))
    group.map(t => (t._1, t._2.sum)).collect().foreach(println)
    // Array[(String, Int)] = Array((two,2), (one,1), (three,3))

    // reduceByKey(func, [numTasks])
    // 与groupByKey区别是r在shuffle前有combine操作,g没有,所以建议使用r
    // 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起
    // reduce任务的个数可以通过第二个可选的参数来设置
    println("\nreduceByKey")
    val reduceByKeyRDD: RDD[(String, Int)] = sc.makeRDD(List(("female", 1), ("male", 5), ("female", 5), ("male", 2)))
    val reduce = reduceByKeyRDD.reduceByKey((x, y) => x + y, 2)
    reduce.collect().foreach(println) // Array[(String, Int)] = Array((female,6), (male,7))

    // aggregateByKey
    // 参数:(zeroValue:U,[partitioner: Partitioner]) (seqOp: (U, V) => U,combOp: (U, U) => U)
    // 作用:在kv对的RDD中,按key将value进行分组合并,合并时,将每个value和初始值作为seq函数的参数,进行计算
    // 返回的结果作为一个新的kv对,然后再将结果按照key进行合并,最后将每个分组的value传递给combine函数进行计算
    // (先将前两个value进行计算,将返回结果和下一个value传给combine函数,以此类推),将key与计算结果作为一个新的kv对输出
    // 参数描述:
    // (1)zeroValue:给每一个分区中的每一个key一个初始值
    // (2)seqOp:函数用于在每一个分区中用初始值逐步迭代value
    // (3)combOp:函数用于合并每个分区中的结果
    println("\naggregateByKey")
    val aggregateByKeyRDD: RDD[(String, Int)] = sc
      .makeRDD(List(("a", 3), ("a", 2), ("c", 4), ("b", 3), ("c", 6), ("c", 8)), 2)
    aggregateByKeyRDD.aggregateByKey(0)(math.max(_, _), _ + _).collect().foreach(println)
    // Array[(String, Int)] = Array((b,3), (a,3), (c,12))

    // foldByKey
    // 参数:(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]
    // 作用:aggregateByKey的简化操作,seqop和combop相同
    println("\nfoldByKey")
    val foldByKeyRDD: RDD[(Int, Int)] = sc.makeRDD(List((1, 3), (1, 2), (1, 4), (2, 3), (3, 6), (3, 8)), 3)
    foldByKeyRDD.foldByKey(0)(_ + _).collect().foreach(println)
    // Array[(Int, Int)] = Array((3,14), (1,9), (2,3))

    // combineByKey
    // 参数:(createCombiner: V => C,  mergeValue: (C, V) => C,  mergeCombiners: (C, C) => C)
    // 作用:对相同K,把V合并成一个集合
    // 2.参数描述:
    // (1)createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就和之前的某个元素的键相同。如果这是一个新的元素,combineByKey()会使用一个叫作createCombiner()的函数来创建那个键对应的累加器的初始值
    // (2)mergeValue: 如果这是一个在处理当前分区之前已经遇到的键,它会使用mergeValue()方法将该键的累加器对应的当前值与这个新的值进行合并
    // (3)mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器, 就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并
    println("\ncombineByKey")
    val combineByKeyRDD: RDD[(String, Int)] = sc
      .parallelize(Array(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98)), 2)
    val combineResult = combineByKeyRDD.combineByKey((_, 1), (acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
      (acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2))
    combineResult.collect().foreach(println)
    // Array[(String, (Int, Int))] = Array((b,(286,3)), (a,(274,3)))
    val average: RDD[(String, Double)] = combineResult
      .map { case (key, value) => (key, value._1 / value._2.toDouble) }
    average.collect().foreach(println)
    // Array[(String, Double)] = Array((b,95.33333333333333), (a,91.33333333333333))

    // 注意aggregateByKey、foldByKey、combineByKey三者底层都是combineByKeyWithClassTag实现

    // sortByKey([ascending], [numTasks])
    // 作用:在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD
    println("\nsortByKey")
    val sortByKeyRDD: RDD[(Int, String)] = sc.parallelize(Array((3, "aa"), (6, "cc"), (2, "bb"), (1, "dd")))
    // 正序
    sortByKeyRDD.sortByKey(true).collect().foreach(println)
    // Array[(Int, String)] = Array((1,dd), (2,bb), (3,aa), (6,cc))
    // 倒序
    sortByKeyRDD.sortByKey(false).collect().foreach(println)
    // Array[(Int, String)] = Array((6,cc), (3,aa), (2,bb), (1,dd))

    // mapValues
    // 针对于(K,V)形式的类型只对V进行操作
    println("\nmapValues")
    val mapValuesRDD: RDD[(Int, String)] = sc.parallelize(Array((1, "a"), (1, "d"), (2, "b"), (3, "c")))
    mapValuesRDD.mapValues(_ + "|||").collect().foreach(println)
    // Array[(Int, String)] = Array((1,a|||), (1,d|||), (2,b|||), (3,c|||))

    // join(otherDataset, [numTasks])
    // 作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
    println("\njoin")
    val joinRDD1: RDD[(Int, String)] = sc.parallelize(Array((1, "a"), (2, "b"), (3, "c")))
    val joinRDD2: RDD[(Int, Int)] = sc.parallelize(Array((1, 4), (2, 5), (3, 6)))
    joinRDD1.join(joinRDD2).collect().foreach(println)
    // Array[(Int, (String, Int))] = Array((1,(a,4)), (2,(b,5)), (3,(c,6)))

    // cogroup(otherDataset, [numTasks])
    // 作用:在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的RDD
    println("\ncogroup")
    joinRDD1.cogroup(joinRDD2).collect().foreach(println)
    // Array[(Int, (Iterable[String], Iterable[Int]))] = Array((1,(CompactBuffer(a),CompactBuffer(4))),
    // (2,(CompactBuffer(b),CompactBuffer(5))), (3,(CompactBuffer(c),CompactBuffer(6))))

    // Action

    // reduce(func)
    // 作用:通过func函数聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据
    println("\nreduce")
    val reduceRDD1: RDD[Int] = sc.makeRDD(1 to 10, 2)
    println(reduceRDD1.reduce(_ + _)) // 55
    val reduceRDD2: RDD[(String, Int)] = sc.makeRDD(Array(("a", 1), ("a", 3), ("c", 3), ("d", 5)))
    println(reduceRDD2.reduce((x, y) => (x._1 + y._1, x._2 + y._2))) // (String, Int) = (adca,12)

    // collect()
    // 作用:在驱动程序中,以数组的形式返回数据集的所有元素
    println("\ncollect")
    val collectRDD: RDD[Int] = sc.parallelize(1 to 10)
    collectRDD.collect().foreach(println) // Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    // count()
    // 作用:返回RDD中元素的个数
    println("\ncount")
    val countRDD: RDD[Int] = sc.parallelize(1 to 10)
    println(countRDD.count()) // 10

    // first()
    // 作用:返回RDD中的第一个元素
    println("\nfirst")
    val firstRDD: RDD[Int] = sc.parallelize(1 to 10)
    println(firstRDD.first()) // 1

    // take(n)
    // 作用:返回一个由RDD的前n个元素组成的数组
    println("\ntake")
    val takeRDD: RDD[Int] = sc.parallelize(Array(2, 5, 4, 6, 8, 3))
    takeRDD.take(3).foreach(println) // Array[Int] = Array(2, 5, 4)

    // takeOrdered(n)
    // 作用:返回该RDD排序后的前n个元素组成的数组
    println("\ntakeOrdered")
    val takeOrderedRDD: RDD[Int] = sc.parallelize(Array(2, 5, 4, 6, 8, 3))
    takeOrderedRDD.takeOrdered(3).foreach(println) // Array[Int] = Array(2, 3, 4)

    // aggregate(zeroValue: U)(seqOp: (U, T) ⇒ U, combOp: (U, U) ⇒ U)
    // 作用:aggregate函数将每个分区里面的元素通过seqOp和初始值进行聚合
    // 然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作
    // 这个函数最终返回的类型不需要和RDD中元素类型一致
    println("\naggregate")
    val aggregateRDD: RDD[Int] = sc.makeRDD(1 to 10, 2)
    println(aggregateRDD.aggregate(0)(_ + _, _ + _)) // 55

    // fold(num)(func)
    // 作用:折叠操作,aggregate的简化操作,seqop和combop一样
    println("\nfold")
    val foldRDD: RDD[Int] = sc.makeRDD(1 to 10, 2)
    println(foldRDD.fold(0)(_ + _)) // 55

    // saveAsTextFile(path)
    // 作用:将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统
    // 对于每个元素,Spark将会调用toString方法,将它装换为文件中的文本
    // 略

    // saveAsSequenceFile(path)
    // 作用:将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下,可以使HDFS或者其他Hadoop支持的文件系统
    // 略

    // saveAsObjectFile(path)
    // 作用:用于将RDD中的元素序列化成对象,存储到文件中
    // 略

    // countByKey()
    // 作用:针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数
    println("\ncountByKey")
    val countByKeyRDD: RDD[(Int, Int)] = sc.parallelize(List((1, 3), (1, 2), (1, 4), (2, 3), (3, 6), (3, 8)), 3)
    println(countByKeyRDD.countByKey()) // scala.collection.Map[Int,Long] = Map(3 -> 2, 1 -> 3, 2 -> 1)

    // foreach(func)
    // 作用:在数据集的每一个元素上,运行函数func进行更新
    // 略

  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值