spark 总结 算子篇

1、创建工程

在这里添加 spark core包,添加bulid 插件。

<groupId>com.ypl.bigdata</groupId> // 这里添写包名称 
<artifactId>spark-200226</artifactId> // 这里是项目名称
<version>1.0-SNAPSHOT</version>//版本号 可以

<dependencies>
     <dependency>
         <groupId>org.apache.spark</groupId>
         <artifactId>spark-core_2.11</artifactId>
         <version>2.1.1</version>
     </dependency>
</dependencies>

<build>
       <finalName>WordCount</finalName>
       <plugins>
           <plugin>
               <groupId>net.alchim31.maven</groupId>
               <artifactId>scala-maven-plugin</artifactId>
               <version>3.2.3</version>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                           <goal>testCompile</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>


算子总结

map 算子
主要是做数据结构的转换,数据条数不变。

//创建
scala> var source = sc.parallelize(1 to 10)
// 打印
scala> source.collect()
// 将所有元素*2
scala> val mapadd = source.map(_ * 2)
// 打印最终结构
scala> mapadd.collect()

mapPartitions(func)
对分区数据进行转换。将某一个分区的所有数据拿过来形成一个可迭代的集合要求 返回可迭代的集合。提高效率会使用它。
应用场景:
只对分区内数据进行数据。缺点是不释放,可能导致oom。
当内存空间较大的时候建议使用 mapPartitions()以提高效率。

val rdd = sc.parallelize(Array(1,2,3,4))
rdd.mapPartitions(x=>x.map(_*2)) // 是每个元素 * 2组成新的rdd
res3.collect

mapPartitionsWithIndex(func)
获取分区索引,进行操作。

val rdd = sc.parallelize(Array(1,2,3,4))
val indexRdd = rdd.mapPartitionsWithIndex((index,items)=>(items.map((index,_)))) //使每个元素跟所在分区形成一个元组组成一个新的RDD
indexRdd.collect

flatMap
将一个整体拆成一个个 个体。扁平化

val sourceFlat = sc.parallelize(1 to 5)//(1,2,3,4,5)
sourceFlat.collect()
// (1->1, 2->1 ,2 ....5 -> 1,2,3,4,5)
val flatMap = sourceFlat.flatMap(1 to _)
flatMap.collect()
// Array(1,1,2,1,2,3,1,2,3,4,1,2,3,4,5)

glom
将一个分区形成一个数组,形成新的RDD类型时,RDD[Array[T]]
例如:创建一个4个分区的RDD,将每个分区的数据放到一个数组。
求分区的最大值,最小值等等。

val rdd = sc.parallelize(1 to 16 ,4)
//将每个分区的数据放到一个数组并收集到 Driver 端打印
rdd.glom().collect()

groupBy(func)
按照传入函数的返回值进行分组。相同的key对应的值放入一个迭代器。
例如:将元素模以2的值进行分组。

val rdd =sc.parallelize(1 to 4)
val group = rdd.groupBy(_%2)
group.collect

filter(func)
过滤,返回一个新的RDD,该RDD 由经过的 func 函数计算后返回值为true 的元素组成。
例如: 创建一个RDD(由字符串组成)过滤出一个新RDD(包含"xiao"字符串)

var sourceFilter = sc.parallelize(Array("zhang1","zhangy2","zhang3","lisi"))
val filter = sourceFilter.filter(_.contains("zhang"))
filter.collect()

sample(withReplacement,fraction,seed)
以指定的随机种子随机抽样出数量为fraction 的数据,withReplacement 表示抽出的数据是否放回,true 为有放回的抽样,false 为无放回的抽样,seed 用于指定随机数生成器的种子

val rdd = sc.parallelize(1 to 10)
rdd.collect()
var sample1 = rdd.sample(true,0.4,2)
sample1.collect()
// 不放回抽样
var sample2 = rdd.sample(false,0.2,3)
sample2.collect

disitnct([numPartitions])
对源RDD进行去重后返回一个新的RDD.

distinct task 是4/4 map 的task 是 2/2
首先是 write 任务-> 然后 read 任务
shuffer 会慢,然后有IO 的过程

val distinctRdd = sc.parallelize()
// 不指定并行度
val unionRDD = distinctRdd.distinct()
//打印去重后生成新的RDD
unionRDD.collect()
// 指定并行度
val unionRDD = distinctRdd.distinct(2)

coalesce(numPartitions)
缩减分区数,用于大数据集过滤后,提高小数据集的执行效率

val rdd = sc.parallelize(1 to 16 ,4)
// 查看rdd 分区数
rdd.partitions.size
// 对 RDD 重新分区
val coalesceRDD = rdd.coalesce(3)
// 查看rdd 分区数
coalesceRDD.partitions.size

repartition(numPartition)
根据分区数,重新通过网络随机洗牌所有数据
例如: 创建一个 4个分区的RDD,对其重新分区。

val rdd = sc.parallelize(1 to 16 ,4)
rdd.partitions.size
rdd.repartition(2)
rdd.partitions.size
rdd.glom.collect

coalesce 和repartition 的区别,coalesce 重新分区,可以选择是否 shuffle 过程,由参数 shuffle:Boolean = false /true 决定。
repartition 实际上是调用的coalesce ,默认是进行shuffle。

sortBy()
对数据进行排序

val rdd = sc.parallelize(List(2,1,3,5))
rdd.sortBy(x=>x).collect() // 降序
rdd.sortBy(x=>x ,false).collect() // 升序

双value类型交互

union(otherDataset)
对源RDD 和参数 RDD 求并集后返回一个新的RDD

val rdd1 = sc.parallelize(1 to 5)
val rdd2 = sc.parallelize(5 to 10)
val rdd3 = rdd1.union(rdd2)
rdd3.collect()

subtract(otherDataset) 案例
计算差的一种函数,去除两个RDD中相同的元素,不同的RDD 将保留下来

val rdd1 = sc.parallelize(3 to 8)
val rdd2 = sc.parallelize(1 to 5)
val rdd3 = rdd1.substract(rdd1).collect()

intersection(otherDataset) 案例
计算交集的一种函数,保留两个rdd 相同的元素

val rdd1 = sc.parallelize(1 to 7)
val rdd2 = sc.parallelize(5 to 10)
val rdd3 = rdd1.intersection(rdd2)

cartesian(otherDataset)
两个rdd 的笛卡尔积

val rdd1 = sc.parallelize(1 to 3 )
val rdd2 = sc.parallelize(2 to 5)
val rdd3 = rdd1.cartesian(rdd2).collect()

zip(otherDataset) 案例
对两个rdd 进行拉链操作,保证分区数和分区数里的数据量相同。

val rdd1 = sc.parallelize(Array(1,2,3),3 )
val rdd2 = sc.parallelize(Array("a","b","c"),3 )
val rdd3 = rdd1.zip(rdd2).collect

Key-Value类型交互

partitionBy
对pairRDD 进行分区操作,如果原有的partitionRDD和现有的partitionRDD是一致的的话,就不进行分区,否则生成shffuleRDD,即产生shuffle 过程。
例如: 创建一个有四个分区的rdd,对其重新分区。

val rdd1 = sc.parallelize(Array((1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")),4 )
rdd.partitions.size
var rdd2 = rdd.partitionBy(new org.aparche.spark.HashPartitioner(2))

// 自定义分区规则
val config:SparkConf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
// 创建Spark 上下文
val sc = newSpakrContext(config)
//
val listRDD = sc.makeRDD(List(("a",1),("b",2),("c",3)))

val partRDD = listRDD.patitionBy(new MyPartitioner(3))

partRDD.saveAsTextFile("output")

// 声明分区器
// 继承Partitioner 类
class MyPartitioner(partitions: Int) extends Partitioner{
	override def numPartitions: Int = {
		pattitions
	}
	override def getPartition(key:Any):Int = {
		1
	}
}


groupByKey
groupByKey 也是对每个key进行操作,但只生成一个 sequence。
创建一个pairRDD,将相同key值聚合到一个sequence中并计算相同key对应值的相加的结果。

val words = Array("1","2","2","3","3","3")
val wordPairsRDD = sc.parallelize(words).map(word => (word,1))
val group = wordPairsRDD.groupByKey()
group.collect()
group.map(t=> (t._1,t._2.sum)).collect

reduceByKey(func,[numTasks]) 案例
在一个(k,v)的rdd 上调用,返回一个(k,v)的rdd,使用指定的 reduce函数,将相同的key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。
例如:创建一个pairRDD 计算相同key对应值的相加的结果。

val rdd = sc.parallelize(List(("female",1),("male",5),("female",5),("male",2)))
val reduce = rdd.reduceByKey((x,y=> x + y))
reduce.collect()

reduceByKey 和 groupByKey 的区别:
区别
前者是按照key进行聚合在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v]。
按照key 进行分组,直接进行shuffle。
reduceByKey 比 groupByKey 用多,也要注意业务逻辑上的区别。

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对 输出。

zeroValue:给每一个分区中的每一个key一个初始值。
seqOp:分区内运算规则
combOp:分区间运算规则
例如:创建一个pairRDD,取出每个分区相同key 对应值的最大值,然后相加。

// 创建parirdd
val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
// 查看分区情况
rdd.glom.collect
val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_)
agg.collect

在这里插入图片描述

拓展:

val agg = rdd.aggregateByKey(10)(math.max(_._),_+_).collect
Array((a,10),(b,10),(c,20))

val agg = rdd.aggregateByKey(10)(_+_,_+_).collect

foldByKey
底层代码和 aggregateByKey 都调用了一个方法,,seqop和combop相同。

// 创建parirdd
val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
// 查看分区情况
rdd.glom.collect
val agg = rdd.foldByKey(0)(_+_)
agg.collect

combineByKey[C] 案例
参数:(createCombine:V=>C,mergeValue:(C,V)=>C,mergeCombiners:(C,C)=>C)
对于相同的K,把 V 合并成一个集合

  1. createCombiner:combineByKey() 会遍历分区中的所有元素,因此每个元素的键会被初始化,创建对于累加器的初始值。
    2)mergeValue:对之前分区内处理的键进行累加,和新的值进行合并。
    3)mergeCombiners: 对分区间的数据进行累加。
    在这里插入图片描述
val input = sc.parallelize(Array(("a",88),("b",95),("a",91),("b",93),("a",95),("b",98)),2) 
val combine = 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,))
	)
combine.collect
val result = combine.map{case(key,value)=>(key,value._1/value._2.toDouble)}
result.collect()

sortByKey([ascending],[numTasks]) 案例
作用在一个(k,v)的 RDD 上调用,K必须实现Order 接口,返回一个按照 key进行排序的(k,v) 的 RDD.

val rdd = sc.parallelize(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))
rdd.sortBykey(true).collect()
rdd.sortBykey(false).collect()

mapValues 案例
针对(k,v)形式的类型只对V 进行操作
例如创建一个pairRDD,并将value 添加字符串 “||||”

val rdd3 = sc.parallelize(Array(1,"a"),(1,"d"),(2,"b"),(3,"c"))
rdd3.mapValues(_+"|||").collect()

join(otherDataset,[numTasks]) 案例
在类型(k,v) 和 (k,w) 的RDD 上调用,返回一个相同key 对应的所有元素堆在一起的(k,(v,w))的 RDD
例如:创建两个pariRDD,并将相同的数据聚合到一个元组。

val rdd1 = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val rdd2 = sc.parallelize(Array((1,3),(2,4),(3,9)))
rdd1.join(rdd2).collect()
Array[(Int,(String,Int))] = Array((2,(b,5)),(1,(a,4)),(3,(c,6)))

cogroup 案例

在类型为(k,v)和(k ,w )的 rdd 上调用,返回一个(l,(Iterable,Iterable))类型的RDD
创建两个pairRDD,将key相同的数据聚合到一个迭代器。

val rdd1 = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val rdd2 = sc.parallelize(Array((1,3),(2,4),(3,9)))
rdd1.cogroup(rdd1).collect()

cogroup 可以把不是共同存在的元素,展示出来,join 不行。

案例,每个省份的广告统计。
样例:

package com.ypl.bigdata.spark

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Practice {

  def main(args: Array[String]): Unit = {
    
    // 1初始化spark 配置信息建立与spark的链接

    val sparkconf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("Test")
    
    val sc = new SparkContext(sparkconf)
    
    
    // 2、读取数据生成RDD : TS, Province,City,User,AD
    val line = sc.textFile("xxx/agent.log")


    // 3、按照最小力度进行聚合;((Province,AD),1)
    val proAdANDOne: RDD[((String, String), Int)] = line.map(x => {
      val fields: Array[String] = x.split(" ")
      ((fields(1), fields(4)), 1)
    }
    )


    // 4、 计算每个广告点击的总数 ;((Province,AD),sum)
    val proAdANDsum: RDD[((String, String), Int)] = proAdANDOne.reduceByKey(_ + _)
    
    // 5、 将省份作为 key,广告+ 点击次数为 value (province,(AD,sum))

    val proANDAdsum: RDD[(String, (String, Int))] = proAdANDsum.map(x => (x._1._1, (x._1._2, x._2)))

    // 6、将同一省的广告进行聚合
    val provinceGroup: RDD[(String, Iterable[(String, Int)])] = proANDAdsum.groupByKey()

    // 7 排序、取前三条、打印
    val provinceAdTop3: RDD[(String, List[(String, Int)])] = provinceGroup.mapValues(
      x => x.toList.sortWith((x, y) => x._2 > y._2).take(3)
    )

    provinceAdTop3.collect().foreach(println)
    
    // 关闭连接
    sc.stop()
    
  }
}

Action

reduce
通过func 函数聚集的RDD 中所有元素,先聚合分区内数据,再聚合分区间数据。
例:聚合RDD[Int] 、RDD[String] 中的所有元素

val rdd1 = sc.makeRDD(1 to 10 ,2)
rdd1.reduce(_+_)

val rdd1 = sc.makeRDD(Array(("a",1),("a",3),("c",3),("d",5)))
rdd2.reduce((x,y=>(x._1 + y._1, x._2+y._2)))
(adca,12

collect()
在驱动程序中,以数组的形式返回数据集中的所有元素

count()
返回rdd 中元素的个数。

first()
返回 rdd 第一个元素

take(n)
取出rdd中第第几个元素

takeOrdered(3)
排序完后取出第几个元素

aggregare()
创建一个rdd ,将所有元素相加

val rdd1 = sc.makeRDD(1 to 10,2)
rdd1.aggregate(0)(_+_,_+_) //55
rdd1.aggregate(10)(_+_,_+_) // 85 2 个分区 10 + 分区间计算 10

fold(num)(func) 案例
折叠操作,aggregare 的简化操作,seqop和combop 一样

val rdd1 = sc.makeRDD(1 to 10,2)
rdd.fold(0)(_+_)

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

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

saveAsObjectFile(path)
将RDD 中的元素序列化对象,存储到文件当中。

countByKey() 案例
针对(k,v) 类型的rdd 返回一个(k,Int) 的map ,表示每一个key对应的元素个数。

val rdd1 = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
rdd.countByKey

foreach(func) 案例
在数据集的每一个元素上,运行函数func 进行更新

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值