Spark Transformation转换算子

RDD转换算子整体上分为:Value类型、双Value类型和Key-Value类型


一.Value类型

顾名思义是对单个value值进行运算的算子类型。下面主要从函数签名、功能、案例+图解三个方法介绍这几类算子。

1.map():映射

1)函数签名:

def map[U: ClassTag](f: T => U): RDD[U]

2)功能说明

参数f是一个函数,它可以接收一个参数。当某个RDD执行map方法时,会遍历该RDD中的每一个数据项,并依次应用f函数,从而产生一个新的RDD。即,这个新RDD中的每一个元素都是原来RDD中每一个元素依次应用f函数而得到的。

map方法的部分源代码:

def map[U: ClassTag](f:T=> U): RDD[U]= withScope {
	val clea nF= sc. clea n(f)
	new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.m ap(cleanF))
}
private[spark] class MapPartitionsRDD[U: ClassTag T: ClassTag](
	...
	override def getPartitions: Array[Partition]= firstParent[T].partit ions,
	....
}
protected[spark] def firstParent[U; ClassTag] RDD[U]={
	dependencies. head.rdd. asInst anceOf[RDD[U]]
}

3)案例:创建一个1-4数组的RDD,两个分区,将所有元素*2形成新的RDD

在这里插入图片描述

object TestMap {
   
  def main(args: Array[String]): Unit = {
   
    //1.创建SparkConf并设置App名称
    val conf = new SparkConf().setAppName("CT").setMaster("local[*]")
    //2.创建SparkContext,该对象是提交Spark App的入口
    val sc: SparkContext = new SparkContext(conf)

    //3具体业务逻辑
    // 3.1 创建一个RDD
    val rdd: RDD[Int] = sc.makeRDD(1 to 4,2)
    // 3.2 调用map方法,每个元素乘以2
    val mapRdd: RDD[Int] = rdd.map(_ * 2)

    // 3.3 打印修改后的RDD中数据
    mapRdd.collect().foreach(println)
    
    //4.关闭连接
    sc.stop()
  }
}

说明:由于具体的业务逻辑代码只有第3部分几行,剩下的关于SparkContext对象的创建及资源关闭,后面的案例将直接省略这部分代码。

2.mapPartitions():以分区为单位执行map

mapPartitions:以分区为单位,对RDD中的元素进行映射。一般适用于批处理的操作,比如:将RDD中的元素插入到数据库中,需要数据库连接,如果每一个元素都创建一个连接,效率很低;可以对每个分区的元素,创建一个连接。

1)函数签名:

def mapPartitions[U: ClassTag](
f: Iterator[T] => Iterator[U],   
preservesPartitioning: Boolean = false): RDD[U])    

参数说明:

  • f函数:把每一个分区的数据,分别放入到迭代器中,进行批处理。传入的是一个迭代器,也就是该分区的整体数据。
  • preservePartitioning: 是否保留上游RDD的分区信息,默认flase

2)功能说明: Map是一次处理一个元素,而mapPartitions一次处理一个分区数据

3)案例:创建–个RDD,4个元素,2个分区,使每个元素*2组成新的RDD

在这里插入图片描述

//3具体业务逻辑
// 3.1 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(1 to 4, 2)

// 3.2 调用mapPartitions方法,每个元素乘以2
val rdd1 = rdd.mapPartitions(x=>x.map(_*2))
//注意与map处理数据的区别:
//val mapRdd: RDD[Int] = rdd.map(_ * 2)

// 3.3 打印修改后的RDD中数据
rdd1.collect().foreach(println)
*map()和mapPartitions()区别

在这里插入图片描述

mapPartitions一般适用于批处理的操作,比如:将RDD中的元素插入到数据库中,需要数据库选接,如果每一个元素都创建一个连接, 效率很低——可以对每个分区的元素,创建一个连接

3.mapPartitionsWithIndex():带分区号的2

mapPartitionsWithIndex:以分区为单位,对RDD中的元素进行映射,并且带分区编号

1)函数签名:

def mapPartitionsWithIndex[U: ClassTag](
	f: (Int, Iterator[T]) => Iterator[U],   
	preservesPartitioning: Boolean = false): RDD[U]
)

Int即表示分区编号.

2)功能说明:类似于mapPartitions,比mapPartitions多一个整数参数表示分区号

3)案例:创建一个RDD,使每个元素跟所在分区号形成一个元组,组成一个新的RDD

在这里插入图片描述

//3具体业务逻辑
// 3.1 创建一个RDD
val rdd: RDD[Int] = sc.makeRDD(1 to 4,2)
// 3.2 调用map方法,每个元素乘以2
val mapRdd = rdd.mapPartitionsWithIndex(
  (index,items) => {
   
    items.map(a => (index,a))
  }
)

// 3.3 打印修改后的RDD中数据
mapRdd.collect().foreach(println)

4.flatMap():扁平化

faltMap:对集合中的元素进行扁平化处理

1)函数签名:

def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U])

2)功能说明

  • 与map操作类似,将RDD中的每一个元素通过应用f函数依次转换为新的元素,并封装到RDD中

区别:

  • 在flatMap操作中,f函数的返回值是一个集合并且会将每一个该集合中的元素拆分出来放到新的RDD中

3)案例:创建一个集合,集合里面存储的还是子集合,把所有子集合中数据取出放入到一个大的集合中

在这里插入图片描述

//3具体业务逻辑
// 3.1 创建一个RDD
val listRDD=sc.makeRDD(List(List(1,2),List(3,4),List(5,6),List(7)), 2)

// 3.2 把所有子集合中数据取出放入到一个大的集合中
listRDD.flatMap(list=>list).collect.foreach(println)

注意:如果匿名函数输入和输出相同,那么不能简化

  • listRDD.flatMap(list=>list)

5.glom():分区转数组

glom:将RDD一个分区中的元素,组合成一个新的数组

1)函数签名:

def glom(): RDD[Array[T]]

2)功能说明

  • 该操作将RDD中每一个分区变成一个数组,并放置在新的RDD中,数组中元素的类型与原分区中元素类型一致

3)案例:创建一个2个分区的RDD,并将每个分区的数据放到一个数组,求出每个分区的最大值

在这里插入图片描述

// 3.1 创建一个RDD
val rdd = sc.makeRDD(1 to 4, 2)

// 3.2 求出每个分区的最大值  0->1,2   1->3,4
//glom求出的是Array,map遍历的就是Array
val maxRdd: RDD[Int] = rdd.glom().map(_.max)

// 3.3 求出所有分区的最大值的和 2 + 4
println(maxRdd.collect().sum)

6.groupBy():分组

groupBy:按照指定的规则,对RDD中的元素进行分组

1)函数签名:

def groupBy[K](f: T => K)(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])]

2)功能说明

  • 分组,按照传入函数的返回值进行分组;
  • 将相同的key对应的值放入一个迭代器:如 1,2,3,4 按照奇偶数分组
    • (0,CompactBuffer(2, 4))
    • (1,CompactBuffer(1, 3))

3)案例:创建一个RDD,按照元素模以2的值进行分组。

在这里插入图片描述

// 3.1 创建一个RDD
val rdd = sc.makeRDD(1 to 4, 2)

// 3.2 将每个分区的数据放到一个数组并收集到Driver端打印
rdd.groupBy(_ % 2).collect().foreach(println)
//(0,CompactBuffer(2, 4))
//(1,CompactBuffer(1, 3))

// 3.3 创建一个RDD
val rdd1: RDD[String] = sc.makeRDD(List("hello","hive","hadoop","spark","scala"))

// 3.4 按照首字母第一个单词相同分组
rdd1.groupBy(str=>str.substring(0,1)).collect().foreach(println)
WordCount案例

在这里插入图片描述

val rdd: RDD[String] = sc.makeRDD(List("Hello Scala", "Hello Spark", "Hello World"))

//算子中使用case模式匹配元组时,需要用大括号,否则会报错
rdd.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).map{
   
  case (str, tuples) => {
   
    (str,str.size)
  }
}.collect().foreach(println)

复杂版:

//复杂版思路:("Hello Scala", 2)==>(hello,2),(Scala,2)     ("Hello Spark", 3)==>(hello,3),(Spark,3)
val rdd: RDD[(String, Int)] = sc.makeRDD(List(("Hello Scala", 2), ("Hello Spark", 3), ("Hello World", 2)))

//1)对RDD中的元素进行扁平映射
val flatMapRDD: RDD[(String, Int)] = rdd.flatMap {
   
  case (words, count) => {
   
    words.split(" ").map(word => (word, count))
  }
}

//2)按照单词对RDD中的元素进行分组     (Hello,CompactBuffer((Hello
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值