Spark算子

介绍:
Spark中RDD 的算子分为两类:
1、Transformation转换算子:返回一个新的 RDD
2、Action行动算子:返回值不是 RDD(无返回值或返回其他的)
Spark中的转换算子和Java中的Function非常的相似,也和Scala中的函数式编程类似,不过Spark中封装了更多的转换算子(真的是太多了(吐槽))

一、RDD

1.1、基于内存创建RDD

val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5))

1.2、基于文件创建RDD

val rddLine: RDD[String] = sc.textFile("datas/1.txt")

二、转换算子

2.1、map

函数签名: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()
  }

2.2、flatMap

函数签名: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()
  }

2.3、glom

函数签名: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()
  }

2.4、mapPartitions

函数签名: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()
  }

2.5、mapPartitionsWithIndex

函数签名: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()
  }

2.6、filter

函数签名: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()
  }

2.7、sample

函数签名: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()
  }

2.8、distinct

函数签名: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()
  }

2.9、coalesce

函数签名: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()
  }

2.10、repartition

函数签名: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()
  }

2.11、sortBy

函数签名: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()
  }

2.12、intersection

函数签名: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()
  }

2.13、union

函数签名: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()
  }

2.14、subtract

函数签名: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()
  }

2.15、zip

函数签名: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()
  }

2.16、reduceByKey

函数签名: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()
  }

2.17、groupByKey

函数签名: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()
  }

2.18、groupBy

函数签名: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()
  }

2.19、aggregateByKey

函数签名: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()
  }

2.20、foldByKey

函数签名: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()
  }

2.21、join

函数签名: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()
  }

2.22、cogroup

函数签名: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()
  }

三、行动算子

3.1、reduce

函数签名:reduce(func)
函数说明:通过 func 函数聚集 RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据

val reduceResult: Int = rdd.reduce(_ + _)

3.2、collect

函数签名:collect()
函数说明:将不同分区的数据按照分区顺序采集到内存中,形成数组

val collectResult: Array[Int] = rdd.collect()

3.3、count

函数签名:count()
函数说明:统计数据源数据的个数

val countResult: Long = rdd.count()

3.4、first

函数签名:first()
函数说明:返回 RDD 的第一个元素(类似于 take(1))

val firstResult: Int = rdd.first()

3.5、take

函数签名:take(n)
函数说明:返回一个由数据集的前 n 个元素组成的数组

val takeResult: Array[Int] = rdd.take(2)

3.6、takeOrdered

函数签名:takeOrdered(n, [ordering])
函数说明:返回自然顺序或者自定义顺序的前 n 个元素

    // 逆序取第一个
    val takeOrderedResult: Array[Int] = rdd.takeOrdered(1)((x, y) => {
      if (x < y) 1 else if (x == y) 0 else -1
    })

3.6、saveAsTextFile

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

rdd.saveAsTextFile("output")

3.6、foreach

函数签名:foreach(func)
函数说明:在数据集的每一个元素上,运行函数 func 进行更新

rdd.foreach(println)

四、案例

4.1、WordCount

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)
  }

}

4.2、统计每个省份每个广告被点击数量Top3

数据:时间戳,省份,城市,用户,广告,中间使用空格分开
样例:
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()
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值