一、創建RDD
1.由外部存儲系統的數據集創建,由外部存儲系統的數據集創建,包括本地的文件系統,還有所有Hadoop支持的數據集,比如HDFS、Cassandra、HBase等
val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt")
2.通過已有的RDD經過算子轉換生成新的RDD
val rdd2=rdd1.flatMap(_.split(" "))
3.由一個已經存在的Scala集合創建
val rdd3 = sc.parallelize(Array(1,2,3,4,5,6,7,8))
或者
val rdd4 = sc.makeRDD(List(1,2,3,4,5,6,7,8))
makeRDD方法底層調用了parallelize方法
二、RDD的方法/算子分類
RDD的算子分爲兩類:
1.Transformation轉換操作:返回一個新的RDD
2.Action動作操作:返回值不是RDD(無返回值或返回其他的)
RDD不實際存儲真正要計算的數據,而是記錄了數據的位置在哪裏,數據的轉換關係(調用了什麼方法,傳入什麼函數)
RDD中的所有轉換都是惰性求值/延遲執行的,也就是說並不會直接計算。只有當發生一個要求返回結果給Driver的Action動作時,這些轉換纔會真正運行。
之所以使用惰性求值/延遲執行,是因爲這樣可以在Action時對RDD操作形成DAG有向無環圖進行Stage的劃分和並行優化,這種設計讓Spark更加有效率地運行。 |
Transformation轉換算子
轉換 |
含義 |
map(func) |
返回一個新的RDD,該RDD由每一個輸入元素經過func函數轉換後組成 |
filter(func) |
返回一個新的RDD,該RDD由經過func函數計算後返回值爲true的輸入元素組成 |
flatMap(func) |
類似於map,但是每一個輸入元素可以被映射爲0或多個輸出元素(所以func應該返回一個序列,而不是單一元素) |
mapPartitions(func) |
類似於map,但獨立地在RDD的每一個分片上運行,因此在類型爲T的RDD上運行時,func的函數類型必須是Iterator[T] => Iterator[U] |
mapPartitionsWithIndex(func) |
類似於mapPartitions,但func帶有一個整數參數表示分片的索引值,因此在類型爲T的RDD上運行時,func的函數類型必須是 (Int, Interator[T]) => Iterator[U] |
sample(withReplacement, fraction, seed) |
根據fraction指定的比例對數據進行採樣,可以選擇是否使用隨機數進行替換,seed用於指定隨機數生成器種子 |
union(otherDataset) |
對源RDD和參數RDD求並集後返回一個新的RDD |
intersection(otherDataset) |
對源RDD和參數RDD求交集後返回一個新的RDD |
distinct([numTasks])) |
對源RDD進行去重後返回一個新的RDD |
groupByKey([numTasks]) |
在一個(K,V)的RDD上調用,返回一個(K, Iterator[V])的RDD |
reduceByKey(func, [numTasks]) |
在一個(K,V)的RDD上調用,返回一個(K,V)的RDD,使用指定的reduce函數,將相同key的值聚合到一起,與groupByKey類似,reduce任務的個數可以通過第二個可選的參數來設置 |
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) |
|
sortByKey([ascending], [numTasks]) |
在一個(K,V)的RDD上調用,K必須實現Ordered接口,返回一個按照key進行排序的(K,V)的RDD |
sortBy(func,[ascending], [numTasks]) |
與sortByKey類似,但是更靈活 |
join(otherDataset, [numTasks]) |
在類型爲(K,V)和(K,W)的RDD上調用,返回一個相同key對應的所有元素對在一起的(K,(V,W))的RDD |
cogroup(otherDataset, [numTasks]) |
在類型爲(K,V)和(K,W)的RDD上調用,返回一個(K,(Iterable<V>,Iterable<W>))類型的RDD |
cartesian(otherDataset) |
笛卡爾積 |
pipe(command, [envVars]) |
對rdd進行管道操作 |
coalesce(numPartitions) |
減少 RDD 的分區數到指定值。在過濾大量數據之後,可以執行此操作 |
repartition(numPartitions) |
重新給 RDD 分區 |
Action動作算子
動作 |
含義 |
reduce(func) |
通過func函數聚集RDD中的所有元素,這個功能必須是可交換且可並聯的 |
collect() |
在驅動程序中,以數組的形式返回數據集的所有元素 |
count() |
返回RDD的元素個數 |
first() |
返回RDD的第一個元素(類似於take(1)) |
take(n) |
返回一個由數據集的前n個元素組成的數組 |
takeSample(withReplacement,num, [seed]) |
返回一個數組,該數組由從數據集中隨機採樣的num個元素組成,可以選擇是否用隨機數替換不足的部分,seed用於指定隨機數生成器種子 |
takeOrdered(n, [ordering]) |
返回自然順序或者自定義順序的前 n 個元素 |
saveAsTextFile(path) |
將數據集的元素以textfile的形式保存到HDFS文件系統或者其他支持的文件系統,對於每個元素,Spark將會調用toString方法,將它裝換爲文件中的文本 |
saveAsSequenceFile(path) |
將數據集中的元素以Hadoop sequencefile的格式保存到指定的目錄下,可以使HDFS或者其他Hadoop支持的文件系統。 |
saveAsObjectFile(path) |
將數據集的元素,以 Java 序列化的方式保存到指定的目錄下 |
countByKey() |
針對(K,V)類型的RDD,返回一個(K,Int)的map,表示每一個key對應的元素個數。 |
foreach(func) |
在數據集的每一個元素上,運行函數func進行更新。 |
foreachPartition(func) |
在數據集的每一個分區上,運行函數func |
統計操作
算子 |
含義 |
count |
個數 |
mean |
均值 |
sum |
求和 |
max |
最大值 |
min |
最小值 |
variance |
方差 |
sampleVariance |
從採樣中計算方差 |
stdev |
標準差:衡量數據的離散程度 |
sampleStdev |
採樣的標準差 |
stats |
查看統計結果 |
三、常用算子操作:
集羣模式啓動
/export/servers/spark/sbin/start-all.sh
啓動spark-shell
/export/servers/spark/bin/spark-shell \
--master spark://node01:7077 \
--executor-memory 1g \
--total-executor-cores 2
或本地模式啓動
/export/servers/spark/bin/spark-shell
1、創建RDD
val rdd1 = sc.parallelize(List(5,6,4,7,3,8,2,9,1,10))
val rdd2 = sc.makeRDD(List(5,6,4,7,3,8,2,9,1,10))
2、查看該RDD的分區數量
sc.parallelize(List(5,6,4,7,3,8,2,9,1,10)).partitions.length
//沒有指定分區數,默認值是2
sc.parallelize(List(5,6,4,7,3,8,2,9,1,10),3).partitions.length
//指定了分區數爲3
sc.textFile("hdfs://node01:8020/wordcount/input/words.txt").partitions.length
//2
RDD分區的數據取決於哪些因素?
RDD分區的原則是使得分區的個數儘量等於集羣中的CPU核心(core)數目,這樣可以充分利用CPU的計算資源,但是在實際中爲了更加充分的壓榨CPU的計算資源,會把並行度設置爲cpu核數的2~3倍。RDD分區數和啓動時指定的核數、調用方法時指定的分區數、如文件本身分區數 有關係
分區原則
1.啓動的時候指定的CPU核數確定了一個參數值: spark.default.parallelism=指定的CPU核數(集羣模式最小2) 2.對於Scala集合調用parallelize(集合,分區數)方法, 如果沒有指定分區數,就使用spark.default.parallelism, 如果指定了就使用指定的分區數(不要指定大於spark.default.parallelism) 3.對於textFile(文件,分區數) defaultMinPartitions 如果沒有指定分區數sc.defaultMinPartitions=min(defaultParallelism,2) 如果指定了就使用指定的分區數sc.defaultMinPartitions=指定的分區數 rdd的分區數 對於本地文件: rdd的分區數 = max(本地file的分片數, sc.defaultMinPartitions) 對於HDFS文件: rdd的分區數 = max(hdfs文件的block數目, sc.defaultMinPartitions) 所以如果分配的核數爲多個,且從文件中讀取數據創建RDD,即使hdfs文件只有1個切片,最後的Spark的RDD的partition數也有可能是2 |
3、map
對RDD中的每一個元素進行操作並返回操作的結果
//通過並行化生成rdd
val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
//對rdd1裏的每一個元素
rdd1.map(_ * 2).collect //collect方法表示收集,是action操作
4、filter
函數中返回True的被留下,返回False的被過濾掉
val rdd2 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
val rdd3 = rdd2.filter(_ >= 10)
rdd3.collect //10
5、flatmap
對RDD中的每一個元素進行先map再壓扁,最後返回操作的結果
val rdd1 = sc.parallelize(Array("a b c", "d e f", "h i j"))
//將rdd1裏面的每一個元素先切分再壓平
val rdd2 = rdd1.flatMap(_.split(' '))
rdd2.collect
//Array[String] = Array(a, b, c, d, e, f, h, i, j)
6、sortBy
val rdd1 = sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
val rdd2 = rdd1.sortBy(x=>x,true) // x=>x 表示按照元素本身進行排序,True表示升序
rdd2.collect //1,2,3,.....
val rdd2 = rdd1.sortBy(x=>x+"",true)//x=>x+""表示按照x的字符串形式排序變成了字符串,結果爲字典順序
rdd2.collect//1,10,2,3...
7、交集、並集、差集、笛卡爾積
類型要一致
val rdd1 = sc.parallelize(List(5, 6, 4, 3))
val rdd2 = sc.parallelize(List(1, 2, 3, 4))
//union不會去重
val rdd3 = rdd1.union(rdd2)
rdd3.collect
//去重
rdd3.distinct.collect
//求交集
val rdd4 = rdd1.intersection(rdd2)
rdd4.collect
//求差集
val rdd5 = rdd1.subtract(rdd2)
rdd5.collect
//笛卡爾積
val rdd1 = sc.parallelize(List("jack", "tom"))//學生
val rdd2 = sc.parallelize(List("java", "python", "scala"))//課程
val rdd3 = rdd1.cartesian(rdd2)//表示所有學生的所有選課情況
rdd3.collect
//Array[(String, String)] = Array((jack,java), (jack,python), (jack,scala), (tom,java), (tom,python), (tom,scala))
8、join
join(內連接)聚合具有相同key組成的value元組
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 2), ("kitty", 3)))
val rdd2 = sc.parallelize(List(("jerry", 9), ("tom", 8), ("shuke", 7), ("tom", 2)))
val rdd3 = rdd1.join(rdd2)
rdd3.collect
//Array[(String, (Int, Int))] = Array((tom,(1,8)), (tom,(1,2)), (jerry,(2,9)))
val rdd4 = rdd1.leftOuterJoin(rdd2) //左外連接,左邊的全留下,右邊的滿足條件的才留下
rdd4.collect
//Array[(String, (Int, Option[Int]))] = Array((tom,(1,Some(2))), (tom,(1,Some(8))), (jerry,(2,Some(9))), (kitty,(3,None)))
val rdd5 = rdd1.rightOuterJoin(rdd2)
rdd5.collect
//Array[(String, (Option[Int], Int))] = Array((tom,(Some(1),2)), (tom,(Some(1),8)), (jerry,(Some(2),9)), (shuke,(None,7)))
val rdd6 = rdd1.union(rdd2)
rdd6.collect
//Array[(String, Int)] = Array((tom,1), (jerry,2), (kitty,3), (jerry,9), (tom,8), (shuke,7), (tom,2))
9、groupbykey
groupByKey()的功能是,對具有相同鍵的值進行分組。
比如,對四個鍵值對("spark",1)、("spark",2)、("hadoop",3)和("hadoop",5),
採用groupByKey()後得到的結果是:("spark",(1,2))和("hadoop",(3,5))。
//按key進行分組
val rdd6 = sc.parallelize(Array(("tom",1), ("jerry",2), ("kitty",3), ("jerry",9), ("tom",8), ("shuke",7), ("tom",2)))
val rdd7=rdd6.groupByKey
rdd7.collect
//Array[(String, Iterable[Int])] = Array((tom,CompactBuffer(1, 8, 2)), (jerry,CompactBuffer(2, 9)), (shuke,CompactBuffer(7)), (kitty,CompactBuffer(3)))
10、cogroup
cogroup是先RDD內部分組,在RDD之間分組
val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
val rdd3 = rdd1.cogroup(rdd2)
rdd3.collect
// Array((tom,(CompactBuffer(1, 2),CompactBuffer(1))), (jerry,(CompactBuffer(3),CompactBuffer(2))), (shuke,(CompactBuffer(),CompactBuffer(2))), (kitty,(CompactBuffer(2),CompactBuffer())))
11、groupBy
根據指定的函數中的規則/key進行分組
val intRdd = sc.parallelize(List(1,2,3,4,5,6))
val result = intRdd.groupBy(x=>{if(x%2 == 0)"even" else "odd"}).collect
//Array[(String, Iterable[Int])] = Array((even,CompactBuffer(2, 4, 6)), (odd,CompactBuffer(1, 3, 5)))
12、reduce
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))
//reduce聚合
val result = rdd1.reduce(_ + _)
// 第一_ 上次一個運算的結果,第二個_ 這一次進來的元素
reduceByKey是Transformation還是Action? --Transformation
reduce是Transformation還是Action? --Action
13、reducebykey
reducebykey是轉換算子
reduceByKey(func)的功能是,使用func函數合併具有相同鍵的值。
比如,reduceByKey((a,b) => a+b),有四個鍵值對("spark",1)、("spark",2)、("hadoop",3)和("hadoop",5)
對具有相同key的鍵值對進行合併後的結果就是:("spark",3)、("hadoop",8)。
可以看出,(a,b) => a+b這個Lamda表達式中,a和b都是指value,
比如,對於兩個具有相同key的鍵值對("spark",1)、("spark",2),a就是1,b就是2。
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2), ("shuke", 1)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))
val rdd3 = rdd1.union(rdd2) //並集
rdd3.collect
//Array[(String, Int)] = Array((tom,1), (jerry,3), (kitty,2), (shuke,1), (jerry,2), (tom,3), (shuke,2), (kitty,5))
//按key進行聚合
val rdd4 = rdd3.reduceByKey(_ + _)
rdd4.collect
//Array[(String, Int)] = Array((tom,4), (jerry,5), (shuke,3), (kitty,7))
14、repartition
改變分區數
val rdd1 = sc.parallelize(1 to 10,3) //指定3個分區
//利用repartition改變rdd1分區數
//減少分區
rdd1.repartition(2).partitions.length //新生成的rdd分區數爲2
rdd1.partitions.length //3 //注意:原來的rdd分區數不變
//增加分區
rdd1.repartition(4).partitions.length
//減少分區
rdd1.repartition(3).partitions.length
//利用coalesce改變rdd1分區數
//減少分區
rdd1.coalesce(2).partitions.size
rdd1.coalesce(4).partitions.size
repartition可以增加和減少rdd中的分區數,
coalesce默認減少rdd分區數,增加rdd分區數不會生效。
不管增加還是減少分區數原rdd分區數不變,變的是新生成的rdd的分區數
15、collect
val rdd1 = sc.parallelize(List(6,1,2,3,4,5), 2)
rdd1.collect
16、count
count統計集合中元素的個數
rdd1.count //6
求RDD中最外層集合裏面的元素的個數
val rdd3 = sc.parallelize(List(List("a b c", "a b b"),List("e f g", "a f g"), List("h i j", "a a b")))
rdd3.count //3
17、distinct
val rdd = sc.parallelize(Array(1,2,3,4,5,5,6,7,8,1,2,3,4), 3)
rdd.distinct.collect
18、top
//取出最大的前N個
val rdd1 = sc.parallelize(List(3,6,1,2,4,5))
rdd1.top(2)
19、take
//按照原來的順序取前N個
rdd1.take(2) //3 6
//需求:取出最小的2個
rdd1.sortBy(x=>x,true).take(2)
20、first
//按照原來的順序取前第一個
rdd1.first
21、keys、values
val rdd1 = sc.parallelize(List("dog", "tiger", "lion", "cat", "panther", "eagle"), 2)
val rdd2 = rdd1.map(x => (x.length, x))
rdd2.collect
//Array[(Int, String)] = Array((3,dog), (5,tiger), (4,lion), (3,cat), (7,panther), (5,eagle))
rdd2.keys.collect
//Array[Int] = Array(3, 5, 4, 3, 7, 5)
rdd2.values.collect
//Array[String] = Array(dog, tiger, lion, cat, panther, eagle)
22、mapValues
mapValues表示對RDD中的元素進行操作,Key不變,Value變爲操作之後
val rdd1 = sc.parallelize(List((1,10),(2,20),(3,30)))
val rdd2 = rdd1.mapValues(_*2).collect //_表示每一個value ,key不變,將函數作用於value
//(1,20),(2,40),(3,60)
23、collectAsMap
轉換成Map
val rdd = sc.parallelize(List(("a", 1), ("b", 2)))
rdd.collectAsMap
//scala.collection.Map[String,Int] = Map(b -> 2, a -> 1)
24、foreach和foreachPartition
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
rdd1.foreach(x => println(x*100)) //x是每一個元素
rdd1.foreachPartition(x => println(x.reduce(_ + _))) //x是每個分區
注意:foreach和foreachPartition都是Action操作,但是以上代碼在spark-shell中執行看不到輸出結果,
原因是傳給foreach和foreachPartition的計算函數是在各個分區執行的,即在集羣中的各個Worker上執行的
應用場景:
比如在函數中要將RDD中的元素保存到數據庫
foreach:會將函數作用到RDD中的每一條數據,那麼有多少條數據,操作數據庫連接的開啓關閉就得執行多少次
foreachPartition:將函數作用到每一個分區,那麼每一個分區執行一次數據庫連接的開啓關閉,有幾個分區就會執行數據庫連接開啓關閉
import org.apache.spark.{SparkConf, SparkContext}
object Test {
def main(args: Array[String]): Unit = {
val config = new SparkConf().setMaster("local[*]").setAppName("WordCount")
val sc = new SparkContext(config)
//設置日誌輸出級別
sc.setLogLevel("WARN")
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
//Applies a function f to all elements of this RDD.
//將函數f應用於此RDD的所有元素
rdd1.foreach(x => println(x*100))
//把函數傳給各個分區,在分區內循環遍歷該分區中的元素
//x每個元素,即一個一個的數字
println("==========================")
//Applies a function f to each partition of this RDD.
//將函數f應用於此RDD的每個分區
rdd1.foreachPartition(x => println(x.reduce(_ + _)))
//把各個分區傳遞給函數執行
//x是每個分區
}
}
25、map和mapPartitions
將每一個分區傳遞給函數
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 3)
rdd1.mapPartitions(x=>x.map(y=>y*2)).collect //x是每一個分區,y是分區中的元素
26、mapPartitionsWithIndex(同時獲取分區號)
功能:取分區中對應的數據時,還可以將分區的編號取出來,這樣就可以知道數據是屬於哪個分區的
val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)
//該函數的功能是將對應分區中的數據取出來,並且帶上分區編號
// 一個index 分區編號
// 一個iter分區內的數據
val func = (index: Int, iter: Iterator[Int]) => {
iter.map(x => "[partID:" + index + ", val: " + x + "]")
}
rdd1.mapPartitionsWithIndex(func).collect
//Array[String] = Array(
[partID:0, val: 1], [partID:0, val: 2], [partID:0, val: 3],
[partID:1, val: 4], [partID:1, val: 5], [partID:1, val: 6],
[partID:2, val: 7], [partID:2, val: 8], [partID:2, val: 9]
)
27、aggregate
val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7,8,9), 3)
//0表示初始值
//第一個_+_,表示區內聚合,第一個_表示歷史值,第二個_表示當前值
//第二個_+_,表示區間聚合,第一個_表示歷史值,第二個_表示當前值
val result1: Int = rdd1.aggregate(0)(_+_,_+_) //45 ==> 6 + 15 + 24 = 45
//10表示初始值,每個分區有初始值,區間聚合的時候也有初始值
val result2: Int = rdd1.aggregate(10)(_+_,_+_) //85 ==> 10+ (10+6 + 10+15 + 10+24)=85
28、combineByKey
val rdd1 = sc.textFile("hdfs://node01:8020/wordcount/input/words.txt").flatMap(_.split(" ")).map((_, 1))
//Array((hello,1), (me,1), (hello,1), (you,1), (hello,1), (her,1))
//x => x,表示key不變
//(a: Int, b: Int) => a + b:表示區內聚合
//(m: Int, n: Int) => m + n:表示區間聚合
val rdd2 = rdd1.combineByKey(x => x, (a: Int, b: Int) => a + b, (m: Int, n: Int) => m + n)
//val rdd2 = rdd1.combineByKey(x => x, _+_, _+_)//注意這裏簡寫錯誤,原則:能省則省,不能省則不要偷懶
rdd2.collect
//Array[(String, Int)] = Array((hello,3), (me,1), (you,1), (her,1))
val rddData1: RDD[(String, Float)] = sc.parallelize(
Array(
("班級1", 95f),
("班級2", 80f),
("班級1", 75f),
("班級3", 97f),
("班級2", 88f)),
2)
val rddData2 = rddData1.combineByKey(
grade => (grade, 1),
(gc: (Float, Int), grade) => (gc._1 + grade, gc._2 + 1),
(gc1: (Float, Int), gc2: (Float, Int)) => (gc1._1 + gc2._1, gc1._2 + gc2._2)
)
val rddData3 = rddData2.map(t => (t._1, t._2._1 / t._2._2))
rddData3.collect
29、aggregateByKey
val pairRDD = sc.parallelize(List( ("cat",2), ("cat", 5), ("mouse", 4),("cat", 12), ("dog", 12), ("mouse", 2)), 2)
def func(index: Int, iter: Iterator[(String, Int)]) : Iterator[String] = {
iter.map(x => "[partID:" + index + ", val: " + x + "]")
}
pairRDD.mapPartitionsWithIndex(func).collect
//Array(
[partID:0, val: (cat,2)], [partID:0, val: (cat,5)], [partID:0, val: (mouse,4)],
[partID:1, val: (cat,12)], [partID:1, val: (dog,12)], [partID:1, val: (mouse,2)]
)
pairRDD.aggregateByKey(0)(math.max(_, _), _ + _).collect
// Array[(String, Int)] = Array((dog,12), (cat,17), (mouse,6))
//100表示區內初始值,區間聚合沒有
pairRDD.aggregateByKey(100)(math.max(_, _), _ + _).collect
//Array[(String, Int)] = Array((dog,100), (cat,200), (mouse,200))
pairRDD.aggregateByKey(5)(math.max(_, _), _ + _).collect
//Array[(String, Int)] = Array((dog,12), (cat,17), (mouse,10))
pairRDD.aggregateByKey(10)(math.max(_, _), _ + _).collect
//Array[(String, Int)] = Array((dog,12), (cat,22), (mouse,20))
val rddData1 = sc.parallelize(
Array(
("用戶1", "接口1"),
("用戶2", "接口1"),
("用戶1", "接口1"),
("用戶1", "接口2"),
("用戶2", "接口3")),
2)
val rddData2 = rddData1.aggregateByKey(collection.mutable.Set[String]())(
(urlSet, url) => urlSet += url,
(urlSet1, urlSet2) => urlSet1 ++= urlSet2)
rddData2.collect
30、小練習
給定一個鍵值對RDD
val rdd = sc.parallelize(Array(("spark",2),("hadoop",6),("hadoop",4),("spark",6)))
key表示圖書名稱,
value表示某天圖書銷量,
請計算每個鍵對應的平均值,也就是計算每種圖書的每天平均銷量。
最終結果:("spark",4),("hadoop",5)
val rdd1 = rdd.groupByKey
rdd1.collect
//Array((spark,CompactBuffer(6, 2)), (hadoop,CompactBuffer(4, 6)))
val rdd2 = rdd1.mapValues(v => v.sum / v.size)
rdd2.collect
答案
val rdd = sc.parallelize(Array(("spark",2),("hadoop",6),("hadoop",4),("spark",6)))
val rdd2 = rdd.groupByKey()
rdd2.collect
//Array[(String, Iterable[Int])] = Array((spark,CompactBuffer(2, 6)), (hadoop,CompactBuffer(6, 4)))
val rdd3 = rdd2.map(t=>(t._1,t._2.sum /t._2.size))
rdd3.collect
//Array[(String, Int)] = Array((spark,4), (hadoop,5))
四、總結
分類
RDD的算子分爲兩類,一類是Transformation轉換操作,一類是Action動作操作
●如何區分Transformation和Action
返回值是RDD的爲Transformation轉換操作,延遲執行/懶執行/惰性執行
返回值不是RDD(如Unit、Array、Int)的爲Action動作操作
●面試題:
1.Transformation操作的API有哪些? --map/flatMap/filter....
2.Action操作的API有哪些? --collect/reduce/saveAsTextFile....
3.reduceByKey是Transformation還是Action? --Transformation
4.reduce是Transformation還是Action? -- Action
5.foreach和foreachPartition的區別? foreach作用於每個元素,foreachPartition作用於每個分區
●注意:
RDD不實際存儲真正要計算的數據,而只是記錄了RDD的轉換關係(調用了什麼方法,傳入什麼函數,依賴哪些RDD,分區器是什麼,數量塊來源機器列表)
RDD中的所有轉換操作都是延遲執行(懶執行)的,也就是說並不會直接計算。只有當發生Action操作的時候,這些轉換纔會真正運行。