spark-core rdd转换算子

一、单值类型

1、map(f) 映射转换

➢ 函数签名
def map[U: ClassTag](f: T => U): RDD[U] ➢ 函数说明
将处理的数据逐条进行映射转换,这里的转换可以是类型的转换,也可以是值的转换。
val dataRDD: RDD[Int] = sparkContext.makeRDD(List(1,2,3,4))
val dataRDD1: RDD[Int] = dataRDD.map(num => {num * 2})
val dataRDD2: RDD[String] = dataRDD1.map(num => {"" + num} )

2、mapPartitions(f) 分区

➢ 函数签名
def mapPartitions[U: ClassTag](
 f: Iterator[T] => Iterator[U],
 preservesPartitioning: Boolean = false): RDD[U]
➢ 函数说明
将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处
理,哪怕是过滤数据。
val dataRDD1: RDD[Int] = dataRDD.mapPartitions(datas => {datas.filter(_==2)} )

3、mapPartitionsWithIndex() 当前分区索引

➢ 函数签名
def mapPartitionsWithIndex[U: ClassTag](
 f: (Int, Iterator[T]) => Iterator[U],
 preservesPartitioning: Boolean = false): RDD[U]
➢ 函数说明
将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处
理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。
val dataRDD1 = dataRDD.mapPartitionsWithIndex(
 (index, datas) => {
 datas.map(index, _)
 } )

4、flatMap(f) 扁平化映射

➢ 函数签名
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
➢ 函数说明
将处理的数据进行扁平化后再进行映射处理,所以算子也称之为扁平映射
val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark"))
val flatRDD: RDD[String] = rdd.flatMap(s => {s.split(" ")})

5、glom() 封装数组

/**
 * def glom(): RDD[Array[T]]
➢ 函数说明
将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变
将同一分区的数据封装成一个数组
 * @param args
 */
def main(args: Array[String]): Unit = {

    val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
    val sc = new SparkContext(sparkConf)

    // TODO 算子 - glom
    val rdd : RDD[Int] = sc.makeRDD(List(1,2,3,4), 2)

    // List => Int
    // Int => Array
    val glomRDD: RDD[Array[Int]] = rdd.glom()

    glomRDD.collect().foreach(data=> println(data.mkString(",")))

6、groupBy(f) 分组

/**
     * ➢ 函数签名
def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]
➢ 函数说明
将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样
的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中
一个组的数据在一个分区中,但是并不是说一个分区中只有一个组
     */
    val rdd  = sc.makeRDD(List("Hello", "Spark", "Scala", "Hadoop"), 2)
    // 分组和分区没有必然的关系
    //charAt(0)得到第一个字母x=>x.charAt(0)
    //以首字母进行分区
    val groupRDD = rdd.groupBy(_.charAt(0))
    groupRDD.collect().foreach(println)

7、filter(f=>Boolean) 过滤

/**
 * ➢ 函数签名
def filter(f: T => Boolean): RDD[T]
➢ 函数说明
将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出
现数据倾斜
 */
val rdd = sc.makeRDD(List(1,2,3,4))
//对rdd数据进行过滤,保留奇数
val filterRDD: RDD[Int] = rdd.filter(num=>num%2!=0)
filterRDD.collect().foreach(println)

8、sample(Boolean,Double,Long) 抽样

val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
➢ 函数签名
        def sample(
         withReplacement: Boolean,
         fraction: Double,
         seed: Long = Utils.random.nextLong): RDD[T]
        ➢ 函数说明
        根据指定的规则从数据集中抽取数据
// TODO 算子 - sample
val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8,9,10))
// sample算子需要传递三个参数
// 1. 第一个参数表示,抽取数据后是否将数据返回 true(放回),false(不放回)
// 2. 第二个参数表示,
//         如果抽取不放回的场合:数据源中每条数据被抽取的概率,基准值的概念
//         如果抽取放回的场合:表示数据源中的每条数据被抽取的可能次数
// 3. 第三个参数表示,抽取数据时随机算法的种子
//                    如果不传递第三个参数,那么使用的是当前系统时间
//        println(rdd.sample(
//            false,
//            0.4
//            //1
//        ).collect().mkString(","))

println(rdd.sample(true,2,1).collect().mkString(","))
sc.stop()

9、distinct() 去重

val rdd = sc.makeRDD(List(1,2,3,4,1,2,3,4))/** * ➢ 函数签名        def distinct()(implicit ord: Ordering[T] = null): RDD[T]        def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]        ➢ 函数说明        将数据集中重复的数据去重 *///实现原理是先map后用reduceByKey做聚合// map(x => (x, null)).reduceByKey((x, _) => x, numPartitions).map(_._1)// (1, null),(2, null),(3, null),(4, null),(1, null),(2, null),(3, null),(4, null)// (1, null)(1, null)(1, null)// (null, null) => null// (1, null) => 1val rdd1: RDD[Int] = rdd.distinct()rdd1.collect().foreach(println)

10、coalesce(Int,Boolean) 减少分区

val rdd = sc.makeRDD(List(1,2,3,4,5,6), 3)/** * ➢ 函数签名def coalesce(numPartitions: Int, shuffle: Boolean = false, partitionCoalescer: Option[PartitionCoalescer] = Option.empty) (implicit ord: Ordering[T] = null) : RDD[T]➢ 函数说明根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少分区的个数,减小任务调度成本 */// coalesce方法默认情况下不会将分区的数据打乱重新组合// 这种情况下的缩减分区可能会导致数据不均衡,出现数据倾斜//val newRDD: RDD[Int] = rdd.coalesce(2)// 如果想要让数据均衡,可以进行shuffle处理,第二个参数设为trueval newRDD: RDD[Int] = rdd.coalesce(2,true)

11、repartition(Int) 扩大分区

val rdd = sc.makeRDD(List(1,2,3,4,5,6), 2)/** * 函数签名def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]➢ 函数说明该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition操作都可以完成,因为无论如何都会经 shuffle 过程。 */// coalesce算子可以扩大分区的,但是如果不进行shuffle操作,是没有意义,不起作用。// 所以如果想要实现扩大分区的效果,需要使用shuffle操作// spark提供了一个简化的操作// 缩减分区:coalesce,如果想要数据均衡,可以采用shuffle//val newRDD: RDD[Int] = rdd.coalesce(3, true)// 扩大分区:repartition, 底层代码调用的就是coalesce,而且肯定采用shuffleval newRDD: RDD[Int] = rdd.repartition(3)

12、sortBy(f,Boolean) 排序

val rdd = sc.makeRDD(List(("1", 1), ("11", 2), ("2", 3)), 2)/** * ➢ 函数签名def sortBy[K](f: (T) => K,ascending: Boolean = true,numPartitions: Int = this.partitions.length)    (implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]➢ 函数说明该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理的结果进行排序,默认为升序排列。排序后新产生的 RDD 的分区数与原 RDD 的分区数一致。中间存在 shuffle 的过程sortBy方法可以根据指定的规则对数据源中的数据进行排序,默认为升序,第二个参数可以改变排序的方式sortBy默认情况下,不会改变分区。但是中间存在shuffle操作 */val newRDD = rdd.sortBy(t=>t._1.toInt, false)

二、双值类型

1、intersection(rdd) 交集

val rdd1 = sc.makeRDD(List(1,2,3,4))val rdd2 = sc.makeRDD(List(3,4,5,6))val rdd7 = sc.makeRDD(List("3","4","5","6"))/** * ➢ 函数签名        def intersection(other: RDD[T]): RDD[T]        ➢ 函数说明        对源 RDD 和参数 RDD 求交集后返回一个新的 RDD */// 交集 : 【3,4】val rdd3: RDD[Int] = rdd1.intersection(rdd2)

2、union(rdd) 并集

/** * ➢ 函数签名        def union(other: RDD[T]): RDD[T]        ➢ 函数说明        对源 RDD 和参数 RDD 求并集后返回一个新的 RDD */// 并集 : 【1,2,3,4,3,4,5,6】val rdd4: RDD[Int] = rdd1.union(rdd2)println(rdd4.collect().mkString(","))

3、subtract(rdd) 差集

/** * ➢ 函数签名        def subtract(other: RDD[T]): RDD[T]        ➢ 函数说明        以一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集 */// 差集 : 【1,2】val rdd5: RDD[Int] = rdd1.subtract(rdd2)println(rdd5.collect().mkString(","))

4、zip(rdd) 拉链

/** * ➢ 函数签名        def zip[U: ClassTag](other: RDD[U]): RDD[(T, U)]        ➢ 函数说明        将两个 RDD 中的元素,以键值对的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD        中的元素,Value 为第 2 个 RDD 中的相同位置的元素 */// 拉链 : 【1-3,2-4,3-5,4-6】val rdd6: RDD[(Int, Int)] = rdd1.zip(rdd2)val rdd8 = rdd1.zip(rdd7)println(rdd6.collect().mkString(","))

三、key-value类型

1、partitionBy(Partitioner) 按指定分区器分区

val rdd = sc.makeRDD(List(1,2,3,4),2)/** * ➢ 函数签名def partitionBy(partitioner: Partitioner): RDD[(K, V)]➢ 函数说明将数据按照指定 Partitioner 重新进行分区。Spark 默认的分区器是 HashPartitioner *///必须要是k,v类型的val mapRDD:RDD[(Int, Int)] = rdd.map((_,1))// RDD => PairRDDFunctions// 隐式转换(二次编译)// partitionBy根据指定的分区规则对数据进行重分区val newRDD = mapRDD.partitionBy(new HashPartitioner(2))newRDD.partitionBy(new HashPartitioner(2))

2、reduceByKey(f) 按key对值聚合

/** * ➢ 函数签名def reduceByKey(func: (V, V) => V): RDD[(K, V)]def reduceByKey(func: (V, V) => V, numPartitions: Int): RDD[(K, V)]➢ 函数说明可以将数据按照相同的 Key 对 Value 进行聚合 */// reduceByKey : 相同的key的数据进行value数据的聚合操作// scala语言中一般的聚合操作都是两两聚合,spark基于scala开发的,所以它的聚合也是两两聚合// 【1,2,3】// 【3,3】// 【6】// reduceByKey中如果key的数据只有一个,是不会参与运算的。val reduceRDD: RDD[(String, Int)] = rdd.reduceByKey( (x:Int, y:Int) => {    //println(s"x = ${x}, y = ${y}")    //指定聚合逻辑    x + y} )

3、groupByKey() 按key对value分组

/** * ➢ 函数签名  def groupByKey(): RDD[(K, Iterable[V])]  def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])]  def groupByKey(partitioner: Partitioner): RDD[(K, Iterable[V])]  ➢ 函数说明  将数据源的数据根据 key 对 value 进行分组 */  // groupByKey : 将数据源中的数据,相同key的数据分在一个组中,形成一个对偶元组  //              元组中的第一个元素就是key,  //              元组中的第二个元素就是相同key的value的集合  val groupRDD: RDD[(String, Iterable[Int])] = rdd.groupByKey()

4、aggregateByKey(初始值)(分区内逻辑,分区间逻辑)

/** * ➢ 函数签名def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U, combOp: (U, U) => U): RDD[(K, U)]➢ 函数说明将数据根据不同的规则进行分区内计算和分区间计算 */// aggregateByKey存在函数柯里化,有两个参数列表// 第一个参数列表,需要传递一个参数,表示为初始值//       主要用于当碰见第一个key的时候,和value进行分区内计算// 第二个参数列表需要传递2个参数//      第一个参数表示分区内计算规则//      第二个参数表示分区间计算规则// (a,【1,2】), (a, 【3,4】)// (a, 2), (a, 4)// (a, 6)rdd.aggregateByKey(0)(    (x, y) => math.max(x, y),//分区内取最大值    (x, y) => x + y//分区间相加).collect.foreach(println)

5、foldByKey(初始值)(逻辑)

/** * ➢ 函数签名def foldByKey(zeroValue: V)(func: (V, V) => V): RDD[(K, V)]➢ 函数说明当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey *///rdd.aggregateByKey(0)(_+_, _+_).collect.foreach(println)// 如果聚合计算时,分区内和分区间计算规则相同,spark提供了简化的方法rdd.foldByKey(0)(_+_).collect.foreach(println)

7、mapValues(f) key保持不变,value进行转换

/** * mapValues:key保持不变,value进行转换 */val resultRDD: RDD[(String, Int)] = newRDD.mapValues {      //和/次数得到平均值    case (num, cnt) => {        num / cnt    }}

8、combineByKey(f,分区内逻辑,分区间逻辑)

/** * ➢ 函数签名      def combineByKey[C](       createCombiner: V => C,       mergeValue: (C, V) => C,       mergeCombiners: (C, C) => C): RDD[(K, C)]      ➢ 函数说明      最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于      aggregate(),combineByKey()允许用户返回值的类型与输入不一致 */  // combineByKey : 方法需要三个参数  // 第一个参数表示:将相同key的第一个数据进行结构的转换,实现操作  // 第二个参数表示:分区内的计算规则  // 第三个参数表示:分区间的计算规则  val newRDD : RDD[(String, (Int, Int))] = rdd.combineByKey(      v => (v, 1),//第一个参数,将第一个值变成一个元组      ( t:(Int, Int), v ) => {          (t._1 + v, t._2 + 1)      },      (t1:(Int, Int), t2:(Int, Int)) => {          (t1._1 + t2._1, t1._2 + t2._2)      }  )

9、sortByKey(Boolean) 按key进行排序

➢ 函数签名def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length) : RDD[(K, V)]➢ 函数说明在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口(特质),返回一个按照 key 进行排序的val dataRDD1 = sparkContext.makeRDD(List(("a",1),("b",2),("c",3)))val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(true)val sortRDD1: RDD[(String, Int)] = dataRDD1.sortByKey(false)

10、join(rdd) 连接

val rdd1 = sc.makeRDD(List(("a", 1), ("a", 2), ("c", 3)))val rdd2 = sc.makeRDD(List(("a", 5), ("c", 6),("a", 4)))/** * ➢ 函数签名    def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]    ➢ 函数说明    在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接在一起的    (K,(V,W))的 RDD */// join : 两个不同数据源的数据,相同的key的value会连接在一起,形成元组//        如果两个数据源中key没有匹配上,那么数据不会出现在结果中//        如果两个数据源中key有多个相同的,会依次匹配,可能会出现笛卡尔乘积,数据量会几何性增长,会导致性能降低。val joinRDD: RDD[(String, (Int, Int))] = rdd1.join(rdd2)

11、leftOuterJoin 左外连接

val rdd1 = sc.makeRDD(List(("a", 1), ("b", 2)//, ("c", 3)))
val rdd2 = sc.makeRDD(List(("a", 4), ("b", 5),("c", 6)))
/**
 * ➢ 函数签名
        def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
        ➢ 函数说明
        类似于 SQL 语句的左外连接
 */
//val leftJoinRDD = rdd1.leftOuterJoin(rdd2)

12、rightOuterJoin 右外连接

/**
 * ➢ 函数签名
        def rightOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
        ➢ 函数说明
        类似于 SQL 语句的右外连接
 */
//val leftJoinRDD = rdd1.leftOuterJoin(rdd2)
val rightJoinRDD = rdd1.rightOuterJoin(rdd2)

13、cogroup(rdd1,rdd2…)

val rdd1 = sc.makeRDD(List(("a", 1), ("b", 2),("c", 3)))
val rdd2 = sc.makeRDD(List(("a", 4), ("b", 5),("c", 6),("c", 7)))
/**
 * ➢ 函数签名
    def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]
    ➢ 函数说明
    在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的 RDD
 */
// cogroup : connect + group (分组,连接)
val cgRDD: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd1.cogroup(rdd2)