Apache Spark之RDD算子分析(章节三)

RDD 转换算子

map

将父类RDD的元素以此转换成新的RDD元素

scala> sc.parallelize(List(1,2,3,4,5),3).map(item=>item+2).collect()

flatMap

将集合的元素展开,并转换

scala> sc.textFile("hdfs:///demo/words")
        .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
        .collect()
       
filter

过滤掉集合中不满足要求的元素

scala> sc.parallelize(List(1,2,3,4,5),3).filter(item=>item % 2 == 0).collect()

mapPartitions

同map算子类似,但是不同的是该算子可以拿到父RDD的分区的整个数据集,在数据量不大的时候,可以使用。

scala> sc.parallelize(List("a","b","c"),3)
          .mapPartitions(ites => for(i<-ites) yield (i,1))
          .collect()

mapPartitionsWithIndex

同mapPartitions算子一样,只不过是在传入参数的时候传进去了父类RDD的分区数。

scala>sc.parallelize(List("a","b","c"),3)
        .mapPartitionsWithIndex((index,values)=>for(i<-values) yield (index,i) )
        .collect()

sample

使用给定的随机数生成器种子,在有或没有替换的情况下对数据的一小部分进行采样。

scala> sc.parallelize(List("a","b","c","d","e","f","g"),3).sample(true,0.9,1)

scala> sc.parallelize(List("a","b","c","d","e","f","g"),3).sample(true,0.9,1).collect()

scala> sc.parallelize(List("a","b","c","d","e","f","g"),3).sample(false,0.5,1).collect()

union

返回一个新数据集,其中包含源数据集和参数中元素的并集。

scala> var rdd1=sc.parallelize(Array(("张三",1000),("李四",100),("赵六",300)))

scala> var rdd2=sc.parallelize(Array(("张三",1000),("王五",100),("温七",300)))

scala> rdd1.union(rdd2).collect()

intersection

返回包含源数据集和参数中元素交集的新RDD。

scala> var rdd1=sc.parallelize(Array(("张三",1000),("李四",100),("赵六",300)))

scala> var rdd2=sc.parallelize(Array(("张三",1000),("王五",100),("温七",300)))

scala> rdd1.intersection(rdd2).collect()

distinct

返回包含源数据集的不同元素的新数据集。

scala> var rdd1=sc.parallelize(Array(("张三",1000),("李四",100),("赵六",300)))

scala> var rdd2=sc.parallelize(Array(("张三",1000),("王五",100),("温七",300)))

scala> rdd1.union(rdd2).distinct().collect()

groupByKey

在(K,V)对的数据集上调用时,返回(K,Iterable )对的数据集。注意:如果要对每个键执行聚合(例如总和或平均值)进行分组,则使用reduceByKey或aggregateByKey将产生更好的性能。默认情况下,输出中的并行级别取决于父RDD的分区数。您可以传递可选的numPartitions参数来设置不同数量的任务。

scala> sc.parallelize(List("a","b","a","c"),3)
         .map((_,1))
         .groupByKey(3)
         .map(t=> (t._1,t._2.sum))
         .collect()

reduceByKey

当调用(K,V)对的数据集时,返回(K,V)对的数据集,其中使用给定的reduce函数func聚合每个键的值,该函数必须是类型(V,V)=> V.groupByKey类似,reduce任务的数量可通过可选的第二个参数进行配置。

scala> sc.parallelize(List("a","b","a","c"),3)
        .map((_,1))
        .reduceByKey((v1,v2)=>v1+v2,3)
        .collect()

aggregateByKey

当调用(K,V)对的数据集时,返回(K,U)对的数据集,其中使用给定的组合函数和中性“零”值聚合每个键的值。允许与输入值类型不同的聚合值类型,同时避免不必要的分配。与groupByKey类似,reduce任务的数量可通过可选的第二个参数进行配置。

scala> sc.textFile("hdfs:///demo/words")
				 .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
				 .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
				 .collect()

sortByKey

当调用K实现Ordered的(K,V)对数据集时,返回按键升序或降序排序的(K,V)对数据集,如boolean升序参数中所指定。

scala> sc.textFile("hdfs:///demo/words")
				 .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
				 .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
				 .sortByKey(true,3)
				 .collect()
sortBy
scala> sc.textFile("hdfs:///demo/words")
        .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
        .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
        .sortBy(_._2,true,3)
        .collect()

join

当调用类型(K,V)和(K,W)的数据集时,返回(K,(V,W))对的数据集以及每个键的所有元素对。通过leftOuterJoin,rightOuterJoin和fullOuterJoin支持外连接。

scala> var rdd1=sc.parallelize(Array(("001","张三"),("002","李四"),("003","王五")))

scala> var rdd2=sc.parallelize(Array(("001",("apple",18.0)),("001",("orange",18.0))))

scala> rdd1.join(rdd2).collect()

cogroup

当调用类型(K,V)和(K,W)的数据集时,返回(K,(Iterable ,Iterable ))元组的数据集。此操作也称为groupWith。

scala> var rdd1=sc.parallelize(Array(("001","张三"),("002","李四"),("003","王五")))

scala> var rdd2=sc.parallelize(Array(("001","apple"),("001","orange"),("002","book")))

scala> rdd1.cogroup(rdd2).collect()

cartesian

当调用类型为T和U的数据集时,返回(T,U)对的数据集(所有元素对)。

scala> var rdd1=sc.parallelize(List("a","b","c"))

scala> var rdd2=sc.parallelize(List(1,2,3,4))

scala> rdd1.cartesian(rdd2)

scala> rdd1.cartesian(rdd2).collect()

coalesce

将RDD中的分区数减少为numPartitions。过滤大型数据集后,可以更有效地运行操作。

scala> sc.textFile("hdfs:///demo/words")
          .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
          .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2).sortBy(_._2,true,3)
          .coalesce(1).getNumPartitions

repartition

随机重新调整RDD中的数据以创建更多或更少的分区并在它们之间进行平衡。这总是随机播放网络上的所有数据。

scala> var rdd1=sc.parallelize(List("a","b","c"),3)
				  .mapPartitionsWithIndex((index,values)=>for(i<-values) yield (index,i) )

scala> rdd1.collect()

scala> var rdd2=rdd1.repartition(3)
					.mapPartitionsWithIndex((index,values)=>for(i<-values) yield (index,i))

scala> rdd2.collect()

RDD 动作算子

reduce(fun)

使用函数func(它接受两个参数并返回一个)来聚合数据集的元素。该函数应该是可交换的和关联的,以便可以并行正确计算。

scala> var rdd1=sc.parallelize(List("a","b","c"),3).reduce((v1,v2)=>v1+","+v2)
rdd1: String = a,c,b
collect

在驱动程序中将数据集的所有元素作为数组返回。在过滤器或其他返回足够小的数据子集的操作之后,这通常很有用。

scala> sc.textFile("hdfs:///demo/words")
        .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
        .collect()
count

返回数据集中的元素数。

scala> var rdd1=sc.parallelize(List("a","b","c"),3).count()
rdd1: Long = 3

first

返回数据集的第一个元素(类似于take(1))。

scala> var rdd1=sc.parallelize(List("a","b","c"),3).count()

scala> var rdd1=sc.parallelize(List("a","b","c"),3).take(1)

scala> var rdd1=sc.parallelize(List("a","b","c"),3).first()

take

返回包含数据集的前n个元素的数组。

scala> var rdd1=sc.parallelize(List("a","b","c"),3).take(3)
rdd1: Array[String] = Array(a, b, c)
takeSample

返回一个数组,其中包含数据集的num个元素的随机样本,有或没有替换,可选地预先指定随机数生成器种子。

scala> var rdd1=sc.parallelize(List("a","b","c"),3).takeSample(true,5,1)
rdd1: Array[String] = Array(b, c, c, b, a)

takeOrdered

使用自然顺序或自定义比较器返回RDD的前n个元素。

scala> var rdd=sc.parallelize(Array(("a",3),("b",1),("c",4)),5)

scala> val s=new Ordering[(String, Int)]{
     |     override def compare(x: (String, Int),y:(String,Int)):Int={
     |       return -1*(x._2-y._2)
     |     }
     |   }
     
scala> rdd.takeOrdered(2)(s)

saveAsTextFile

将数据集的元素作为文本文件(或文本文件集)写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统的给定目录中。Spark将在每个元素上调用toString,将其转换为文件中的一行文本。

scala> sc.textFile("hdfs:///demo/words")
          .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
          .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
          .sortBy(_._2,true,3)
          .map(t=>t._1+"\t"+t._2)
          .saveAsTextFile("hdfs:///results/")
          
saveAsSequenceFile

将数据集的元素作为Hadoop SequenceFile写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统中的给定路径中.这可以在实现Hadoop的Writable接口的键值对的RDD上使用。在Scala中,它也可以在可隐式转换为Writable的类型上使用(Spark包括基本类型的转换,如Int,Double,String等)。

scala> sc.textFile("hdfs:///demo/words")
        .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
        .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
        .sortBy(_._2,true,3)
        .saveAsSequenceFile("hdfs:///results-seq/")
        
saveAsObjectFile

使用Java序列化以简单格式编写数据集的元素,然后可以使用SparkContext.objectFile()加载。

scala> sc.textFile("hdfs:///demo/words")
      .flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
      .aggregateByKey(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
      .sortBy(_._2,true,3)
      .saveAsObjectFile("hdfs:///results-object/")
      
scala> sc.objectFile("hdfs:///results-object/").map((t:(String,Int)) => t).collect()

countByKey

仅适用于类型(K,V)的RDD。返回(K,Int)对的散列映射,其中包含每个键的计数。

scala> sc.textFile("hdfs:///demo/words")
					.flatMap(line=> for(i <- line.split("\\W+")) yield (i,1) )
					.countByKey()

foreach

在数据集的每个元素上运行函数func。这通常用于辅助工作,例如更新累加器或与外部存储系统交互。

scala> sc.objectFile("hdfs:///results-object/")
					.foreach((t:(String,Int)) => println(t))

共享变量

通常,当在远程集群节点上执行传递给Spark操作(例如map或reduce)的函数时,如果这些function需要用到Driver中定义的变量,spark会将这些定义在Driver中的变量拷贝到所有的worker节点,并且这些变量的修改的值并不会传递回来给Driver定义的变量。这样看来通常跨任务的读写共享变量效率不高,但是,Spark确实为两种常见的使用模式提供了两种有限类型的共享变量:广播变量累加器

广播变量

通常情况下,当一个RDD的很多操作都需要使用driver中定义的变量时,每次操作,driver都要把变量发送给worker节点一次,如果这个变量中的数据很大的话,会产生很高的传输负载,导致执行效率降低。使用广播变量可以使程序高效地将一个很大的只读数据发送给多个worker节点,而且对每个worker节点只需要传输一次,每次操作时executor可以直接获取本地保存的数据副本,不需要多次传输。

val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(SparkBroadcastDemo.getClass.getSimpleName())
val sc = new SparkContext(conf)
val userList = List(
  "001,张三,28,0",
  "002,李四,18,1",
  "003,王五,38,0"
)
val genderMap = Map("0" -> "女", "1" -> "男")
val genderMapBC:Broadcast[Map[String, String]] = sc.broadcast(genderMap)
val userRDD = sc.parallelize(userList)
val retRDD = userRDD.map(info => {
  val prefix = info.substring(0, info.lastIndexOf(","))   // "001,张三,28"
  val gender = info.substring(info.lastIndexOf(",") + 1)
  val genderMapValue = genderMapBC.value
  val newGender = genderMapValue.getOrElse(gender, "男")
  prefix + "," + newGender
})
retRDD.foreach(println)
sc.stop()

累加器

Spark提供的Accumulator,主要用于多个节点对一个变量进行共享性的操作。Accumulator只提供了累加的功能。但是确给我们提供了多个task对一个变量并行操作的功能。但是task只能对Accumulator进行累加操作,不能读取它的值。只有Driver程序可以读取Accumulator的值。

val conf = new SparkConf()
.setMaster("local[2]")
.setAppName(SparkAccumulatorDemo.getClass.getSimpleName())
val sc = new SparkContext(conf)

// 要对这些变量都*7,同时统计能够被3整除的数字的个数
val list = List(1, 2, 3, 4,6,9)
val listRDD:RDD[Int] = sc.parallelize(list)
var counter = 0
val counterAcc = sc.longAccumulator("count")
val mapRDD = listRDD.map(num =>  {
  counter += 1
  if(num % 3 == 0) {
    counterAcc.add(1)
  }
  num * 7
})
mapRDD.foreach(println)
println("counter:" + counter)
println("counterAcc:" + counterAcc.value)
sc.stop()

更多精彩内容关注

微信公众账号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值