Spark-Transformation转换算子

Transformation转换算子

RDD整体上可以分为三种类型:

  1. Value类型
  2. 双Value类型
  3. Key-Value类型

1.Value类型

  1. Map算子

    1. 进去一行,出来一行
    2. 将一个RDD中的每个数据项,通过map中的函数映射变为一个新的元素。
    3. 有多少个输入分区,就会有多少个输出的分区
    4. 有可能改变数据项的数据类型
  2. MapMartitions算子

    1. 一次处理一个分区
    2. 工作中,内存足够的情况下,优先使用
    3. 是否保留上有RDD的分区信息,默认为false
    4. 把每一个分区中的元素放到迭代器中,所以在mappartition里面做的第一层操作时针对迭代器
  3. Map和MapMartitions的区别:

    1. map一次处理一个,mappartition一次处理一个分区
    2. map分区中的每个元素,计算完之后释放,mapPartition处理完一个分区之后才会释放资源
    3. 内存足够,优先选择
  4. MapPartitionsWithIndex算子

    1. 函数签名:f:
      (Int, Iterator[T]) => Iterator[U], // Int表示分区编号
    2. 第一个参数是分区号,第二个参数是迭代器
    3. 使用:val indexRdd =
      rdd.mapPartitionsWithIndex( (index,items)=>{items.map( (index,_) )} )
    4. 一般是需要对迭代器进行操作的
  5. FlatMap算子

    1. 函数签名:def
      flatMap【U: ClassTag】(f: T => TraversableOnce[U]): RDD[U]
    2. 功能说明:
    3. 进来一个,出去多个
    4. 需要对迭代器进行操作
    5. 演示:listRDD.flatMap(list=>list).collect.foreach(println)
  6. glom算子(不常用算子)

    1. 功能说明:分区转换数组
    2. 函数签名:def
      glom(): RDD[Array[T]]
    3. 没有参数,谁调用他,返回数组
    4. 将每一个分区变成一个数组,并放置在新的RDD中,数组中的元素和原来分区中元素类型保持一致
    5. 用的不多,因为每个分区中的数据很多,容易oom
    6. 演示:val maxRdd: RDD[Int]
      = rdd.glom().map(_.max)
    7. 遍历元素最大值:用的最多的就是map
    8. println(maxRdd.collect().sum)
  7. GroupBy分组

    1. 函数签名:

      f: T => K,
      numPartitions: Int)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] 
      
    2. ReducebuKey做了一定的优化,相比GroupBy用的更多

    3. groupby的结果就是(0,CompactBuffer(2,4))

    4. 使用的方式:rdd.groupBy(_.substring(0,1))

    5. groupby会存在shuffle过程

    6. 将不同的分区数据进行打乱重组

    7. 一定会落盘

    8. 案例:

    9. def main(args: Array[String]): Unit = {
      
              //1.创建SparkConf并设置App名称
              val conf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")
      
              //2.创建SparkContext,该对象是提交Spark App的入口
              val sc = new SparkContext(conf)
      
              //3具体业务逻辑
              // 3.1 创建一个RDD
              val strList: List[String] = List("Hello Scala", "Hello Spark", "Hello World")
              val rdd = sc.makeRDD(strList)
      
              // 3.2 将字符串拆分成一个一个的单词
              val wordRdd: RDD[String] = rdd.flatMap(str=>str.split(" "))
      
              // 3.3 将单词结果进行转换:word=>(word,1)
              val wordToOneRdd: RDD[(String, Int)] = wordRdd.map(word=>(word, 1))
      
              // 3.4 将转换结构后的数据分组
              val groupRdd: RDD[(String, Iterable[(String, Int)])] = wordToOneRdd.groupBy(t=>t._1)
      
              // 3.5 将分组后的数据进行结构的转换
              val wordToSum: RDD[(String, Int)] = groupRdd.map {
                  case (word, list) => {
                      (word, list.size)
                  }
              }
      
              wordToSum.collect().foreach(println)
      
              sc.stop()
          }
      
      
  8. Filter过滤算子

    1. 函数签名: def filter(f: T =>
      Boolean): RDD[T]
    2. 使用:rdd.filter(_%2==0)
  9. Sample()采样

    1. 函数签名:

      ​ def sample(

      ​ withReplacement: Boolean,

      ​ fraction: Double,

      ​ seed: Long = Utils.random.nextLong): RDD[T]

    2. 第一个参数输入false,就会选择伯努利算法,数据不放回(扔硬币,范围在0-1之间)如果大于第二个参数不打印,不大于全打印,第三个参数是会根据种子生成伪随机数;

    3. 第一个参数是true,选择泊松算法数据放回。第二个参数是希望出现次数的概率。

    4. 有什么用?在开发时候,不是很清楚key的分布,抽取一部分的数据进行分析。

  10. Distinct去重算子

    1. 去重的操作不适用Distinct有那些方式?
    2. set,groupBy,开窗
    3. 使用:rdd.distinct
    4. 这种是分布式的方式去重,比HashSet集合的方式不容易oom
    5. 去重可以修改分区个数
    6. 去重思路:在这里插入图片描述
  11. coalesce合并分区算子

    1. 配置shuffle

      1. rdd.coalesce(2,true)
    2. 不配置shuffle

      1. 功能:缩减分区

      2. val rdd = sc.makeRDD(Array(1,2,3,4),4)
        val coalesceRdd = rdd.coalesce(2)
        //打印查看对应分区的数据
        val indexRdd = coalesceRdd.mapPartitionsWithIndex(
            (index,datas)=>{
                //打印每个分区数据,并带上分区号
                datas.foreach(data=>{
                    println(index+"=>"+data)
                })
                //返回分区的数据
                datas
            }
        )
        
  12. repartition重新分区

    1. 内部执行的其实是coalesce操作,参数shuffle的默认是true/无论是将分区数多的rdd转换为分区数少的rdd,还是将分区少的rdd转换为分区数多的rdd,repartition操作都可以完成,因为无论如何都会经过shuffle过程。
  13. coalesce和partition的区别

    1. coalesce一般是缩减分区,如果扩大分区,不适用shuffle是没有意义的。
  14. sortby排序

    1. rdd.sortBy(num)
  15. pipe

    1. 函数签名: def pipe(command: String):
      RDD[String]
    2. 针对每个分区都调用一次shell脚本

2.双value类型交互

  1. itersection交集
    1. 函数签名:def intersection(other:
      RDD[T]): RDD[T]
    2. val rdd1 = sc.makeRDD(1 to 4)
    3. val rdd2 = sc.makeRDD(4 to 8)
    4. rdd1.intersection(rdd2).collection.foreach(println)
  2. union
    1. 函数签名:def union(other: RDD[T]):
      RDD[T]
    2. rdd1.union(rdd2).collection.foreach(println)
  3. substract
    1. 函数签名:def subtract(other: RDD[T]):
      RDD[T]
    2. rdd1.substract(rdd2).collection.foreach(println)
  4. zip
    1. 函数签名:def zip[U: ClassTag](other:
      RDD[U]): RDD[(T, U)]
    2. 必须分区数相同,并且几个元素的个数意义对应

3.Key-Value

  1. partitionBy按照k重新分区

    1. 函数签名:def partitionBy(partitioner:
      Partitioner): RDD[(K, V)]

    2. 如果原有的分区数和新的一致的话,就不进行分区,否则就会产生shuffle过程

    3. 传入的kv类型

    4. 自定义分区

      // 自定义分区
      class MyPartitioner(num: Int) extends Partitioner {
      
          // 设置的分区数
          override def numPartitions: Int = num
      
          // 具体分区逻辑
          override def getPartition(key: Any): Int = {
      
              if (key.isInstanceOf[Int]) {
      
                  val keyInt: Int = key.asInstanceOf[Int]
                  if (keyInt % 2 == 0)
                      0
                  else
                      1
              }else{
                  0
              }
          }
      }
      
      

      //4 打印查看对应分区数据
              val indexRdd = rdd3.mapPartitionsWithIndex(
                  (index, datas) => datas.map((index,_))
              )
      
              indexRdd.collect()
      
              //5.关闭连接
              sc.stop()
      
      
  2. reduceByKey()按照K聚合V

    1. def reduceByKey(func: (V, V)
      => V): RDD[(K, V)]
    2. def reduceByKey(func: (V, V)
      => V, numPartitions: Int): RDD[(K, V)]
    3. 功能说明:该操作可以让rdd中的元素按照相同的kv进行聚合。
    4. 和partitionby相比存在分区内和分区间的操作。会在分区内先进性一次combiner
    5. 和groupby的效果差不多,但是比groupby方便
  3. groupByKey()按照K重新分组

    1. 函数签名:def groupByKey(): RDD[(K,
      Iterable[V])]

    2. groupbykey只对每个key进行操作,但是只生成一个seq,并不进行聚合。

    3. 有shuflle

      val rdd = sc.makeRDD(List(("a",1),("b",2),("a",1)))
      //将相同的key都聚合到一个seq中
      val group = rdd.groupBykey().collect()>foreach(println)
      group.map(t=>(t._1,t._2.sum)).collect().foreach(println)
      
  4. reduceByKey和groupByKey区别

    1. reducebykey:按照key进行聚合,在shuffle之气那有combine操作,返回的结果是rdd[k,v]
    2. groupbykey:按照key进行分组,直接进行shuffle
    3. 开发指导:在不影响业务逻辑的前提下,优先使用reducebykey。求和操作不影响业务,求平均值影响业务逻辑
  5. aggregatebykey按照k处理分区内和分区间的逻辑

    1.  //3.1 创建第一个RDD
              val rdd: RDD[(String, Int)] = sc.makeRDD(List(("a", 3), ("a", 2), ("c", 4), ("b", 3), ("c", 6), ("c", 8)), 2)
      
              //3.2 取出每个分区相同key对应值的最大值,然后相加
              rdd.aggregateByKey(0)(math.max(_, _), _ + _).collect().foreach(println)
      
      
  6. foladbykey分区内和分区间相同的aggregateByKey

    1. 中间有shuffle

    2. val list: List[(String, Int)] = List(("a",1),("a",1),("a",1),("b",1),("b",1),("b",1),("b",1),("a",1))
              val rdd = sc.makeRDD(list,2)
      
              //3.2 求wordcount
              //rdd.aggregateByKey(0)(_+_,_+_).collect().foreach(println)
      
              rdd.foldByKey(0)(_+_).collect().foreach(println)
      
      
  7. combineByKey()转换结构后分区内和分区间操作

    1. def combineByKey[C](

    ​ createCombiner: V => C,

    ​ mergeValue: (C, V) => C,

    ​ mergeCombiners: (C, C) => C): RDD[(K, C)]

    //3.1 创建第一个RDD
            val list: List[(String, Int)] = List(("a", 88), ("b", 95), ("a", 91), ("b", 93), ("a", 95), ("b", 98))
            val input: RDD[(String, Int)] = sc.makeRDD(list, 2)
    
            //3.2 将相同key对应的值相加,同时记录该key出现的次数,放入一个二元组
            val combineRdd: RDD[(String, (Int, Int))] = input.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)
            )
    
            //3.3 打印合并后的结果
            combineRdd.collect().foreach(println)
    
            //3.4 计算平均值
            combineRdd.map {
                case (key, value) => {
                    (key, value._1 / value._2.toDouble)
                }
            }.collect().foreach(println)
    
    
    
  8. sortByKey()按照K进行排序

    1. 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD

    2. //3具体业务逻辑
              //3.1 创建第一个RDD
              val rdd: RDD[(Int, String)] = sc.makeRDD(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))
      
              //3.2 按照key的正序(默认顺序)
              rdd.sortByKey(true).collect().foreach(println)
      
              //3.3 按照key的倒序
              rdd.sortByKey(false).collect().foreach(println)
      
      
  9. mapValues()只对V进行操作

    1.  //3具体业务逻辑
              //3.1 创建第一个RDD
              val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (1, "d"), (2, "b"), (3, "c")))
      
              //3.2 对value添加字符串"|||"
              rdd.mapValues(_ + "|||").collect().foreach(println)
      
      
  10. join()连接

    1. 在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD

    2.  //3具体业务逻辑
              //3.1 创建第一个RDD
              val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c")))
      
              //3.2 创建第二个pairRDD
              val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1, 4), (2, 5), (4, 6)))
      
              //3.3 join操作并打印结果
              rdd.join(rdd1).collect().foreach(println)
      
      
  11. cogroup()类似全连接,但是在同一个RDD中对key聚合

    1. def
      cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]

    2. //3具体业务逻辑
              //3.1 创建第一个RDD
              val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1,"a"),(2,"b"),(3,"c")))
      
              //3.2 创建第二个RDD
              val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1,4),(2,5),(3,6)))
      
              //3.3 cogroup两个RDD并打印结果
              rdd.cogroup(rdd1).collect().foreach(println)
      
      

able[W]))]

2. ```scala
   //3具体业务逻辑
           //3.1 创建第一个RDD
           val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1,"a"),(2,"b"),(3,"c")))
   
           //3.2 创建第二个RDD
           val rdd1: RDD[(Int, Int)] = sc.makeRDD(Array((1,4),(2,5),(3,6)))
   
           //3.3 cogroup两个RDD并打印结果
           rdd.cogroup(rdd1).collect().foreach(println)
   
   ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值