map(func)
通过函数func传递源的每个元素来形成一个新的分布式数据集
val arr=sc.parallelize(Array(("A",1),("B",2),("C",3)))
arr.map(x=>(x._1+x._2)).foreach(println)
print:
A1
B2
C3
mapValues
mapValues顾名思义就是输入函数应用于RDD中Kev-Value的Value,原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素。因此,该函数只适用于元素为KV对的RDD。
scala> val a = sc.parallelize(List("dog", "tiger", "lion", "cat", "panther", " eagle"), 2)
scala> val b = a.map(x => (x.length, x))
scala> b.mapValues("x" + _ + "x").collect
res5: Array[(Int, String)] = Array((3,xdogx), (5,xtigerx), (4,xlionx),(3,xcatx), (7,xpantherx), (5,xeaglex))
mapWith
mapWith是map的另外一个变种,map只需要一个输入函数,而mapWith有两个输入函数。它的定义如下:
def mapWith[A: ClassTag, U: ](constructA: Int => A, preservesPartitioning: Boolean = false)(f: (T, A) => U): RDD[U]
第一个函数constructA是把RDD的partition index(index从0开始)作为输入,输出为新类型A;
第二个函数f是把二元组(T, A)作为输入(其中T为原RDD中的元素,A为第一个函数的输出),输出类型为U。
举例:把partition index 乘以10,然后加上2作为新的RDD的元素。
val x = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10), 3)
x.mapWith(a => a * 10)((a, b) => (b + 2)).collect
res4: Array[Int] = Array(2, 2, 2, 12, 12, 12, 22, 22, 22, 22)
flatMap(func):
与map类似,但每个元素输入项都可以被映射到0个或多个的输出项,最终将结果”扁平化“后输出
val arr=sc.parallelize(Array(("A",1),("B",2),("C",3)))
arr.flatmap(x=>(x._1+x._2)).foreach(println)
print:
A
1
B
2
C
3
flatMapWith
flatMapWith与mapWith很类似,都是接收两个函数,一个函数把partitionIndex作为输入,输出是一个新类型A;另外一个函数是以二元组(T,A)作为输入,输出为一个序列,这些序列里面的元素组成了新的RDD。它的定义如下:
def flatMapWith[A: ClassTag, U: ClassTag](constructA: Int => A, preservesPartitioning: Boolean = false)(f: (T, A) => Seq[U]): RDD[U]
scala> val a = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)
scala> a.flatMapWith(x => x, true)((x, y) => List(y, x)).collect
res58: Array[Int] = Array(0, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 2, 7, 2,
8, 2, 9)
flatMapValues
flatMapValues类似于mapValues,不同的在于flatMapValues应用于元素为KV对的RDD中Value。每个一元素的Value被输入函数映射为一系列的值,然后这些值再与原RDD中的Key组成一系列新的KV对。
scala> val a = sc.parallelize(List((1,2),(3,4),(3,6)))
scala> val b = a.flatMapValues(x=>x.to(5))
scala> b.collect
res3: Array[(Int, Int)] = Array((1,2), (1,3), (1,4), (1,5), (3,4), (3,5))
上述例子中原RDD中每个元素的值被转换为一个序列(从其当前值到5),比如第一个KV对(1,2), 其值2被转换为2,3,4,5。然后其再与原KV对中Key组成一系列新的KV对(1,2),(1,3),(1,4),(1,5)。
mapPartitions(func):
将map函数作用与每个分区,函数类型为:Iterator[T] => Iterator[U]
一般在数据库操作中用到
var rdd1 = sc.makeRDD(1 to 5,2)
//rdd1有两个分区
scala> var rdd3 = rdd1.mapPartitions{ x => {
| var result = List[Int]()
| var i = 0
| while(x.hasNext){
| i += x.next()
| }
| result.::(i).iterator //要求输入和返回都Iterator[U]类型,故将i转换为iterator
| }}
rdd3: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[84] at mapPartitions at :23
//rdd3将rdd1中每个分区中的数值累加
scala> rdd3.collect
res65: Array[Int] = Array(3, 12)
scala> rdd3.partitions.size
res66: Int = 2
mapPartitionsWithIndex(func):
类似于mapPartitions,而且还提供FUNC与表示所述分区的索引的整数值,所以FUNC必须是类型(Int, Iterator[T]) => Iterator[U]
var rdd1 = sc.makeRDD(1 to 5,2)
//rdd1有两个分区
var rdd2 = rdd1.mapPartitionsWithIndex{
(x,iter) => {
var result = List[String]()
var i = 0
while(iter.hasNext){
i += iter.next()
}
result.::(x + "|" + i).iterator
}
}
//rdd2将rdd1中每个分区的数字累加,并在每个分区的累加结果前面加了分区索引
scala> rdd2.collect
res13: Array[String] = Array(0|3, 1|12)
filter(func):
增加过滤器,针对一维的数据
val rdd = sc.parallelize(1 to 5)
rdd.filter(_>3).collect().foreach(println)
4
5
sample(withReplacement,fraction,seed):
指定的随机种子随机抽样
withReplacement:抽出的数据是否放回,true为有放回的抽样,false为无放回的抽样
fraction :抽样的数量
seed:随机种子
val rdd = sc.parallelize(1 to 10)
val sample1 = rdd.sample(true,0.5,3)
sample1.collect.foreach(println)
9
union(ortherDataset):
将两个Rdd数据集合并返回
val rdd1 = sc.parallelize(1 to 3)
val rdd2 = sc.parallelize(3 to 5)
val unionRDD = rdd1.union(rdd2)
unionRDD.collect.foreach(println)
输出:
1
2
3
3
4
5
intersection(otherDataset):
返回两个Rdd的交集
val rdd1 = sc.parallelize(1 to 3)
val rdd2 = sc.parallelize(3 to 5)
val unionRDD = rdd1.intersection(rdd2)
unionRDD.collect.foreach(println)
输出:
3
distinct([ numTasks ])
对RDD数据集进行去重
val list = List(1,1,2,5,2,9,6,1)
val distinctRDD = sc.parallelize(list)
val unionRDD = distinctRDD.distinct()
unionRDD.collect.foreach(println)
1
9
5
6
2
groupByKey([ numTasks ])
当调用(K,V)对的数据集时,返回(K,Iterable )对的数据集。
一般优先选择reduceByKey,combineByKey,foldByKey
详情参照:
https://www.iteblog.com/archives/1357.html
val words = Array("one", "two", "two", "three", "three", "three")
val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
wordPairsRDD.groupByKey().map(t => (t._1, t._2.sum)).collect().foreach(println)
输出:
(two,2)
(one,1)
(three,3)
reduceByKey(func,[ numTasks ])
当调用(K,V)对的数据集时,返回(K,V)对的数据集,其中每个键的值使用给定的reduce函数func进行聚合,函数func必须是(V,V)=> V.像in一样groupByKey,reduce任务的数量可以通过可选的第二个参数来配置。
val words = Array("one", "two", "two", "three", "three", "three")
val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
val wordCountsWithReduce = wordPairsRDD
.reduceByKey(_ + _)
.collect().foreach(println)
输出:
(two,2)
(one,1)
(three,3)
aggregate(zeroValue,seq,comb,taskNums)
将初始值和第一个分区中的第一个元素传递给seq函数进行计算,然后将计算结果和第二个元素传递给seq函数,直到计算到最后一个值。第二个分区中也是同理操作。最后将初始值、所有分区的结果经过combine函数进行计算(先将前两个结果进行计算,将返回结果和下一个结果传给combine函数,以此类推),并返回最终结果。
>>> data = sc.parallelize((1,2,3,4,5,6),2)
>>> def seq(a,b):
... print 'seqOp:'+str(a)+"\t"+str(b)
... return min(a,b)
...
>>> def combine(a,b):
... print 'comOp:'+str(a)+"\t"+str(b)
... return a+b
...
>>> data.aggregate(3,seq,combine)
seqOp:3 1
seqOp:1 2
seqOp:1 3
seqOp:3 4
seqOp:3 5
seqOp:3 6
comOp:3 1
comOp:4 3
7
def main(args: Array[String]) {
System.setProperty("hadoop.home.dir", "D:\\hadoop-2.6.4\\hadoop-2.6.4")
val conf = new SparkConf().setAppName("Join").setMaster("local")
val sc = new SparkContext(conf)
val data = sc.parallelize(List((1, 3), (1, 2), (1, 4), (2, 3)),2)
def seq(a:Int, b:Int): Int = {
println("seq: " + a + "\t " + b)
math.max(a, b)
}
def comb(a:Int,b:Int):Int={
println("comb : "+a+"\t"+b)
a+b
}
val result = data.aggregateByKey(0)(seq,comb) //.collect()
result.collect().foreach(println)
print("==="+data.partitions.size)
}
从上面的代码的输出结果可以看出,1,2,3被分到一个分区中,4,5,6被分到一个分区中。3先和第一个元素1传给seq函数,返回最小值1,然后将1和第二个元素2传给seq函数,返回1,以此类推,最后返回第一个分区中的最小值1。第二个分区一样道理,最后结果返回最小值3.最后将初始值3和两个分区的结果经过combine函数进行计算,先将初始值3和第一个分区的结果1传给combine函数,返回4,然后将4和第二个分区结果3传给combine函数,返回最终结果7。
简单说就是先分区中使用seq(都带入初始值),在不同分区 使用comb函数(也带入初始值)
aggregateByKey(zeroValue)(seqOp,combOp,[ numTasks ])
当调用(K,V)对的数据集时,返回(K,U)对的数据集,其中使用给定的组合函数和中性的“零”值来汇总每个键的值。允许与输入值类型不同的聚合值类型,同时避免不必要的分配。
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
object AggregateByKeyOp {
def main(args:Array[String]){
val sparkConf: SparkConf = new SparkConf().setAppName("AggregateByKey").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data=List((1,3),(1,2),(1,4),(2,3))
val rdd=sc.parallelize(data, 2)
//合并不同partition中的值,a,b得数据类型为zeroValue的数据类型
def combOp(a:String,b:String):String={
println("combOp: "+a+"\t"+b)
a+b
}
//合并在同一个partition中的值,a的数据类型为zeroValue的数据类型,b的数据类型为原value的数据类型
def seqOp(a:String,b:Int):String={
println("SeqOp:"+a+"\t"+b)
a+b
}
rdd.foreach(println)
//zeroValue:中立值,定义返回value的类型,并参与运算
//seqOp:用来在同一个partition中合并值
//combOp:用来在不同partiton中合并值
val aggregateByKeyRDD=rdd.aggregateByKey("100")(seqOp, combOp)
sc.stop()
}
}
将数据拆分成两个分区
//分区一数据
(1,3)
(1,2)
//分区二数据
(1,4)
(2,3)
//分区一相同key的数据进行合并
seq: 100 3 //(1,3)开始和中立值进行合并 合并结果为 1003
seq: 1003 2 //(1,2)再次合并 结果为 10032
//分区二相同key的数据进行合并
seq: 100 4 //(1,4) 开始和中立值进行合并 1004
seq: 100 3 //(2,3) 开始和中立值进行合并 1003
将两个分区的结果进行合并
//key为2的,只在一个分区存在,不需要合并 (2,1003)
(2,1003)
//key为1的, 在两个分区存在,并且数据类型一致,合并
comb: 10032 1004
(1,100321004)
def main(args: Array[String]) {
System.setProperty("hadoop.home.dir", "D:\\hadoop-2.6.4\\hadoop-2.6.4")
val conf = new SparkConf().setAppName("Join").setMaster("local")
val sc = new SparkContext(conf)
val data = sc.parallelize(List(1,2,3,4,5,6),2)
def seq(a:Int, b:Int): Int = {
println("seq: " + a + "\t " + b)
math.min(a, b)
}
def comb(a:Int,b:Int):Int={
println("comb : "+a+"\t"+b)
a+b
}
println("====="+data.aggregate(3)(seq,comb))
}
简单说就是先分区中不同的key使用seq(不同key都带入初始值),在不同分区的key 使用comb函数(不带初始值)
sortByKey([ascending], [numTasks])
当调用K实现Ordered的(K,V)对的数据集时,按照布尔ascending参数中的指定,按照升序或降序返回按键排序的(K,V)对的数据集。
val d2 = sc.parallelize(Array(("cc",32),("bb",32),("cc",22),("aa",18),("bb",6),("dd",16),("ee",104),("cc",1),("ff",13),("gg",68),("bb",44)))
d2.sortByKey(false).collect
输出:
res60: Array[(String, Int)] = Array((gg,68), (ff,13), (ee,104), (dd,16), (cc,32), (cc,22), (cc,1), (bb,32), (bb,6), (bb,44), (aa,18))
join(otherDataset, [numTasks])
当(K,V)和(K,W)类型的数据集被调用时,返回(K,(V,W))对的每个键的所有元素对的数据集。外连接通过支持leftOuterJoin,rightOuterJoin和fullOuterJoin。
val a = sc.parallelize(Array(("123",4.0),("456",9.0),("789",9.0))
val b = sc.parallelize(Array(("123",8.0),("789",10)))
val c = a.join(b)
c.foreach(println)
/*
(123,(4.0,8.0))
(789,(9.0,10))
*/
cogroup(otherDataset, [numTasks])
当(K,V)和(K,W)类型的数据集被调用时,返回(K,(Iterable ,Iterable ))元组的数据集。这个操作也被称为groupWith。
val a = sc.parallelize(Array(("123",4.0),("456",9.0),("789",9.0))
val b = sc.parallelize(Array(("123",8.0),("789",10)))
val d = a.cogroup(b)
d.foreach(println)
/*
(456,(CompactBuffer(9.0),CompactBuffer()))
(123,(CompactBuffer(4.0),CompactBuffer(8.0)))
(789,(CompactBuffer(9.0),CompactBuffer(10)))
*/
pipe(command, [envVars])
通过shell命令管理RDD的每个分区,例如Perl或bash脚本。RDD元素被写入进程的stdin,输出到stdout的行被作为字符串的RDD返回。
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
object PipeTest {
def main(args: Array[String]) {
val sparkConf = new SparkConf().setAppName("pipe Test")
val sc = new SparkContext(sparkConf)
val data1 = Array[(String, Int)](
("K1", 1), ("K2", 2),
("U1", 3), ("U2", 4),
("W1", 3), ("W2", 4), )
val pairs = sc.parallelize(data1, 3)
val finalRDD = pairs.pipe("grep 2")
finalRDD.foreach(x => println("!!!!!!!! " + x + " !!!!!!!!"))//输出
//(K2,2)
//(U2,4)
//(W2,4)
sc.stop()
}
}
cartesian(otherDataset):
对两Rdd进笛卡尔积操作
val rdd1 = sc.parallelize(1 to 3)
val rdd2 = sc.parallelize(2 to 5)
val cartesianRDD = rdd1.cartesian(rdd2)
cartesianRDD.collect.foreach(println)
(1,2)
(1,3)
(1,4)
(1,5)
(2,2)
(2,3)
(2,4)
(2,5)
(3,2)
(3,3)
(3,4)
(3,5)
coalesce(numPartitions,shuffle):
对RDD的分区进行重新分区,shuffle默认值为false,当shuffle=false时,不能增加分区数
目,但不会报错,只是分区个数还是原来的
val rdd = sc.parallelize(1 to 16,4)
val coalesceRDD = rdd.coalesce(3) //当suffle的值为false时,不能增加分区数(即分区数不能从5->7)
println("重新分区后的分区个数:"+coalesceRDD.partitions.size)
输出
重新分区后的分区个数:3
repartition(numPartition):
是函数coalesce(numPartition,true)的实现
glom():
将RDD的每个分区中的类型为T的元素转换换数组Array[T]
val rdd = sc.parallelize(1 to 16,4)
val glomRDD = rdd.glom() //RDD[Array[T]]
glomRDD.foreach(rdd => println(rdd.getClass.getSimpleName))
randomSplit(weight:Array[Double],seed):
val rdd = sc.parallelize(1 to 10)
val randomSplitRDD = rdd.randomSplit(Array(1.0,2.0,7.0))
scala> randomSplitRDD(0).foreach(x => print(x +" "))
为空
scala> randomSplitRDD(1).foreach(x => print(x +" "))
8
scala> randomSplitRDD(2).foreach(x => print(x +" "))
9 3 1 6 2 4 10 5 7
repartitionAndSortWithinPartitions(partitioner)
根据给定的分区器对RDD进行重新分区,并在每个结果分区中按键分类记录。这比repartition在每个分区中调用然后排序更高效,因为它可以将排序推送到洗牌机器中。
repartitionAndSortWithinPartitions,官方建议,如果需要在repartition重分区之后,还要进行排序,建议直接使用repartitionAndSortWithinPartitions算子。因为该算子可以一边进行重分区的shuffle操作,一边进行排序。shuffle与sort两个操作同时进行,比先shuffle再sort来说,性能可能是要高的。
import org.apache.spark.{SparkContext, SparkConf}
/**
* Created by sunxufeng on 2016/6/18.
*/
class Student {
}
//创建key类,key组合键为grade,score
case class StudentKey(grade:String,score:Int)
// extends Ordered[StudentKey]{
// def compare(that: StudentKey) : Int = {
// var result:Int = this.grade.compareTo(that.grade)
// if (result == 0){
// result = this.student.compareTo(that.student)
// if(result ==0){
// result = that.score.compareTo(this.score)
// }
// }
// result
// }
//}
object StudentKey {
implicit def orderingByGradeStudentScore[A <: StudentKey] : Ordering[A] = {
// Ordering.by(fk => (fk.grade, fk.student, fk.score * -1))
Ordering.by(fk => (fk.grade, fk.score * -1))
}
}
object Student{
def main(args: Array[String]) {
//定义hdfs文件索引值
val grade_idx:Int=0
val student_idx:Int=1
val course_idx:Int=2
val score_idx:Int=3
//定义转化函数,不能转化为Int类型的,给默认值0
def safeInt(s: String): Int = try { s.toInt } catch { case _: Throwable => 0 }
//定义提取key的函数
def createKey(data: Array[String]):StudentKey={
StudentKey(data(grade_idx),safeInt(data(score_idx)))
}
//定义提取value的函数
def listData(data: Array[String]):List[String]={
List(data(grade_idx),data(student_idx),data(course_idx),data(score_idx))
}
def createKeyValueTuple(data: Array[String]) :(StudentKey,List[String]) = {
(createKey(data),listData(data))
}
//创建分区类
import org.apache.spark.Partitioner
class StudentPartitioner(partitions: Int) extends Partitioner {
require(partitions >= 0, s"Number of partitions ($partitions) cannot be negative.")
override def numPartitions: Int = partitions
override def getPartition(key: Any): Int = {
val k = key.asInstanceOf[StudentKey]
k.grade.hashCode() % numPartitions
}
}
//设置master为local,用来进行本地调试
val conf = new SparkConf().setAppName("Student_partition_sort").setMaster("local")
val sc = new SparkContext(conf)
//学生信息是打乱的
val student_array =Array(
"c001,n003,chinese,59",
"c002,n004,english,79",
"c002,n004,chinese,13",
"c001,n001,english,88",
"c001,n002,chinese,10",
"c002,n006,chinese,29",
"c001,n001,chinese,54",
"c001,n002,english,32",
"c001,n003,english,43",
"c002,n005,english,80",
"c002,n005,chinese,48",
"c002,n006,english,69"
)
//将学生信息并行化为rdd
val student_rdd = sc.parallelize(student_array)
//生成key-value格式的rdd
val student_rdd2 = student_rdd.map(line => line.split(",")).map(createKeyValueTuple)
//根据StudentKey中的grade进行分区,并根据score降序排列
val student_rdd3 = student_rdd2.repartitionAndSortWithinPartitions(new StudentPartitioner(10))
//打印数据
student_rdd3.collect.foreach(println)
}
}