Spark算子記錄、實例

從這裏學習的,講的真好~ https://www.bilibili.com/video/av62992342/?p=41

單數據

MAP

1.作用

返回一個新RDD,該RDD由每個輸入元素經過func函數轉換後組成。

2.需求

創建一個1-10數組的RDD,將所有元素*2形成新的RDD

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("mapTest")
val sc = new SparkContext(conf)
val inclusive = 1 to 10
// 算子
val listRDD :RDD[Int] = sc.makeRDD(inclusive)
// 做了映射轉換後的RDD
val mapRDD = listRDD.map( _ * 2 )
//
mapRDD.collect().foreach(println)

map.png

mapPartitions(func)案例

1.作用

類似於map,但是獨立在RDD的每個分片(區)上運行,因此在類型爲T的RDD上運行時,func的函數類型必須是Iterator[T]=>Iterator[U]。假設有N個元素,有M個分區,那麼map函數將被調用N次,mapPartitions函數被調用M次,一個函數一次處理所有分區。

2.需求

創建一個RDD,使每個元素*2 組成新RDD

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("mapPartitionsTest")
    val sc = new SparkContext(conf)
    val inclusive = 1 to 10
    // 算子
    val listRDD: RDD[Int] = sc.makeRDD(inclusive)
    // 做了映射轉換後的RDD
    // mapPartitions可以對一個RDD中所有分區進行遍歷
    val mapPartitions = listRDD.mapPartitions(datas => {
      datas.map(data => data * 2)
    })
    mapPartitions.collect().foreach(println)

mapPartitions效率優於map算子,減少了發送到執行器執行交互的次數。
但是要注意!發送的是整個分區數據,可能會比較大。。。這裏回收機制和GC裏可達性分析類似,所以處理完的分區不會立即釋放,內存佔用高,可能會出現內存溢出OOM。
mapPartitions.png

mapPartitionsWithIndex(func)案例

1.作用

類似mapPartitions,但是func帶一個整數參數,表示分片的索引值。因此在類型爲T的RDD上運行,func的函數類型必須是(Int,Interator[T])=>Iterator[U];

2.需求

創建一個RDD,使每個元素跟所在分區形成一個元組組成一個新的RDD

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("mapPartitionsWithIndex")
val sc = new SparkContext(conf)

val listRDD = sc.makeRDD(1 to 10,2)

val indexRdd = listRDD.mapPartitionsWithIndex {
  case (num, datas) => {
    datas.map((_, "分區號:" + num))
  }
}

indexRdd.collect().foreach(println)

FlatMap(func)案例

1.作用

類似於map,但是每個輸入元素可以被映射爲0或多個輸出元素(所以func應該返回一個序列,而非單一元素)

2.需求

創建一個元素1-5的RDD,運用FlatMap創建一個新RDD,新RDD爲原RDD每個元素2倍

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("mapPartitionsWithIndex")
val sc = new SparkContext(conf)

val listRDD = sc.makeRDD(Array(List(1,2),List(3,4)))

// flatMap
// 1,2,3,4
val flatMapRDD = listRDD.flatMap(datas=>datas)

val value = flatMapRDD.map(_*2)

value.collect().foreach(println)

glom

1.作用

將每一個分區形成一個數組,組成新的RDD類型時RDD[Array[T]]

2.例子

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val listRDD: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,6,7,8),3)
// 將我們一個分區的數據放到一個數組中
val glomRDD: RDD[Array[Int]] = listRDD.glom()

glomRDD.collect().foreach(array=>{
  println(array.mkString(","))
})

groupBy

1.作用

分組,按照傳入函數的返回值進行分組。將相同的key 對應值放入一個迭代器中。

2.例子

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val listRDD: RDD[Int] = sc.makeRDD(List(1,2,3,4))

// 生成數據 按照指定規則進行分組
// 分組後的數據形成了對偶元組(k-V),k表示分組的key,value表示分組數據集合
val groupByRDD: RDD[(Int, Iterable[Int])] = listRDD.groupBy(i=>i%2)

groupByRDD.collect().foreach(println)

filter

1.作用

過濾,返回新的RDD,該RDD由經過func函數計算後返回值爲true的輸入元素組成。

2.需求

創建一個由字符組成的RDD,過濾出一個新的包含“xiao”子串的RDD

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val listRDD: RDD[String] = sc.makeRDD(Array("xiaoming","xiaojiang","xiaole","dazhi"))

val filterRDD: RDD[String] = listRDD.filter(x => {
  x.contains("xiao")
})
filterRDD

filterRDD.collect().foreach(println)

Sample(withReplacement,fraction,seed)案例

1.作用

抽樣,以指定隨機種子隨機抽樣出數量爲fraction的數據,withReplacement是表示抽出的數據是否放回,true爲放回,false無放回。seed用於指定隨機數生成器種子。

1.1 使用場景

大數量裏面進行採樣,進行大致分析。1億數據,不需要全部遍歷,採集不同部分數據進行多次分析。最後去平均值。

2.需求

創建一個RDD(1-10),從中選擇返回和不放回抽樣

3.解

 val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
    val sc = new SparkContext(conf)

    // 從指定的數據集合中抽樣處理,根據不同算法進行抽樣
    val listRDD: RDD[Int] = sc.makeRDD(1 to 10)
    // 不放回
    val sampleRDD: RDD[Int] = listRDD.sample(false,0.4,System.currentTimeMillis())
    // 放回
//  val sampleRDD: RDD[Int] = listRDD.sample(true,4,System.currentTimeMillis())

   sampleRDD.collect().foreach(println)

Distinct([numTasks])

1.作用

對源RDD進行去重,返回一個新的RDD。默認情況下,只有8個並行任務來操作,但是可以傳入一個可選的numTasks參數來改變。

2.需求

創建一個RDD,使用distinct()去重

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
    val sc = new SparkContext(conf)

    val listRDD: RDD[Int] = sc.makeRDD(List(1,2,1,5,2,9,6,1))

//    val distinctRDD: RDD[Int] = listRDD.distinct()
    
    // 結果存在兩個分區中
    // 使用distinct算子對數據去重,但是因爲去重後會導致數據減少,所以可以改變默認分區數量
    val distinctRDD: RDD[Int] = listRDD.distinct(2)

    // 我們發現順序被打亂了
//    distinctRDD.collect().foreach(println)
    distinctRDD.saveAsTextFile("output")

可以看到output目錄下,兩個文件,證明兩個分區。

Coalesce(numPartitions)

1.作用

縮減分區數,用於大數據集過濾後,提高小數據集的執行效率。

2.需求

創建一個4分區的RDD,對其縮減分區。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val listRDD: RDD[Int] = sc.makeRDD(1 to 16,4)

println("縮減分區前 = "+listRDD.partitions.size)

val coalesceRDD: RDD[Int] = listRDD.coalesce(3)

println("縮減分區前 = "+coalesceRDD.partitions.size)

所謂的縮減,其實是合併了。

Repartition(numerPartitions)

1.作用

根據分區數,重新通過網絡隨機洗牌所有數據。

coalesce和repartition的區別

coalesce可以選擇shuffle,但是reparation會調用coalesce而且shuffle一定是true

sortBy(func,[ascending],[numTasks])

1.作用

使用func先對數據進行處理,按照處理後的數據比較結果排序,默認正序。
ascending的參數 true表示升序,false表示降序。

2.需求

創建一個RDD,按照不同需求排序

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val listRDD: RDD[Int] = sc.makeRDD(List(2,1,3,4))

// 按照自身大小排序
val sortRDD: RDD[Int] = listRDD.sortBy(x=>x)
sortRDD.collect().foreach(println)
// 按照與3餘數大小排序
val sort3RDD: RDD[Int] = listRDD.sortBy(x=>x%3)
sort3RDD.collect().foreach(println)

多數據交互

#union(otherDataset)

1.作用

對源RDD和參數RDD求並集,然後返回新RDD

2.需求

創建兩個RDD

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd1: RDD[Int] = sc.makeRDD(5 to 10)
val rdd2: RDD[Int] = sc.makeRDD(1 to 5)

val unionRDD: RDD[Int] = rdd1.union(rdd2)

unionRDD.collect().foreach(println)

subtract(otherDataset)

1.作用

去除兩個RDD中相同的元素,留下不同的元素

2.案例

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd1: RDD[Int] = sc.makeRDD(3 to 8)
val rdd2: RDD[Int] = sc.makeRDD(1 to 5)

val subtractRDD: RDD[Int] = rdd1.subtract(rdd2)

subtractRDD.collect().foreach(println)

intersection(otherDataset)

1.作用

兩個RDD取交集,就不舉例了嗷!!!

cartesian(otherDataset)

1.作用

笛卡爾積(儘量別用!!!)不演示。

zip(otherDataset)

1.作用

將兩個RDD組成Key-Value形式的RDD,這裏默認兩個RDD的partition數量和元素都相同。否則會拋異常。

2.需求

創建兩個RDD,把他倆組合到一起形成k-v RDD

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd1: RDD[Int] = sc.makeRDD(Array(1,2,3),3)
val rdd2: RDD[String] = sc.makeRDD(Array("a","b","c"),3)

 val zipRDD: RDD[(Int, String)] = rdd1.zip(rdd2)

zipRDD.collect().foreach(println)

key-value類型

PartitionBy

1.作用

對pairRDD分區操作,,如果原partitionRDD和現有partitionRDD是一致的話就不進行分區,否則會生成shuffleRDD,即會產生shuffle過程。

2.需求

創建一個4個分區的RDD,對其重新分區。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

// 從指定的數據集合中抽樣處理,根據不同算法進行抽樣
val rdd: RDD[(Int, String)] = sc.makeRDD(Array((1, "aaa"), (2, "bbb"), (3, "ccc"), (4, "ddd")), 4)

println(rdd.partitions.size)

val rdd2: RDD[(Int, String)] = rdd.partitionBy(new org.apache.spark.HashPartitioner(2))

println(rdd2.partitions.size)

groupByKey

1.作用

對每個key進行操作,生成一個sequence

2.需求

創建一個pairRDD,將相同的key對應值聚合到一個sequence中,並計算相同key對應值的相加結果。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val words = Array("one","two","three","two","three","three")

val wordPairRDD: RDD[(String, Int)] = sc.parallelize(words).map(word=>(word,1))

val group: RDD[(String, Iterable[Int])] = wordPairRDD.groupByKey()

val result: RDD[(String, Int)] = group.map(t=>(t._1,t._2.sum))

result.collect().foreach(println)

reduceByKey(func[numTasks])

1.作用

在一個kv的RDD上調用,返回一個kv的RDD,使用指定的reduce函數,將相同的值聚合到一起,reduce任務的個數可以通過第二個可選的參數來設置。

2.需求

創建一個pairRDD,計算相同key對應值的相加結果。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val words = Array("one","two","three","two","three","three")

val wordPairRDD: RDD[(String, Int)] = sc.parallelize(words).map(word=>(word,1))

val result: RDD[(String, Int)] = wordPairRDD.reduceByKey(_+_)

result.collect().foreach(println)
reduceByKey和groupByKey的區別

兩個都是聚合。
1.reduceByKey:按照key進行聚合,在shuffle之前有combine操作,預聚合。返回結果是RDD[k,v]
2.groupByKey:按照key進行分組,直接進行shuffle
建議使用reduceByKey

aggregateByKey

1.作用

kv對的RDD中,按key將value進行分組合並,合併時,每個value和初始值作爲seq函數的參數,進行計算。返回的結果作爲一個新的kv對,然後再將結果按照key進行合併,最後將每個分組的value傳遞給combine函數去計算。將key與計算結果作爲一個新的kv對輸出。
參數
zeroValue:給每個分區的key 一個初始值
seqOp:函數用於在每個分區中用初始值逐步迭代value
combOp:函數用於合併每個分區中的結果

2.需求

創建一個pairRDD,取出每個分區相同key對應值的最大值,然後相加。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd: RDD[(String, Int)] = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)

val agg: RDD[(String, Int)] = rdd.aggregateByKey(0)(math.max(_,_),_+_)

agg.collect().foreach(println)

foldByKey

1.作用

aggregateByKey的簡化操作,seqop和combop相同

sortByKey

1.作用

在一個(K,V)的RDD上調用,K必須實現Ordered接口,返回一個按照key進行排序的(K,V)的RDD。

2.需求

創建一個pairRDD,按照key的正序和倒序進行排序

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd: RDD[(Int, String)] = sc.parallelize(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))

// 正序
val sortRDD: RDD[(Int, String)] = rdd.sortByKey(true)

sortRDD.collect().foreach(println)

mapValue

1.作用

針對k-v形式的類型,只對v進行操作

2.需求

創建一個pairRDD,並將value添加字符串"|||"

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd3: RDD[(Int, String)] = sc.parallelize(Array((1,"a"),(1,"d"),(2,"b"),(3,"c")))

val kvRDD: RDD[(Int, String)] = rdd3.mapValues(_+"|||")

kvRDD.collect().foreach(println)

join

1.作用

在類型k-v和k-w的RDD上調用,返回一個相同key對應的所有元素對在一起的(k,(v,w))的RDD

2.需求

創建兩個pairRDD,將key相同的數據聚合到一個元組。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd: RDD[(Int, Int)] = sc.parallelize(Array((1,4),(2,5),(3,6)))
val rdd1: RDD[(Int, String)] = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))

val joinRDD: RDD[(Int, (Int, String))] = rdd.join(rdd1)

joinRDD.collect().foreach(println)

cogroup

1.作用

在類型k-v和k-w的RDD上調用,返回一個(K,(Iterable,Iterable))類型的RDD

2.需求

創建兩個pairRDD,key相同的數據聚合到一個迭代器裏面。

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd1: RDD[(Int, Int)] = sc.parallelize(Array((1,4),(2,5),(3,6),(4,7)))
val rdd: RDD[(Int, String)] = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))

val cogroupRDD: RDD[(Int, (Iterable[String], Iterable[Int]))] = rdd.cogroup(rdd1)

cogroupRDD.collect().foreach(println)

舉栗子!!

1.數據結構

時間戳,省份,城市,用戶,廣告,中間字段用空格分隔
agent.log

1516609143867 6 7 64 16
1516609143869 9 4 75 18
1516609143869 1 7 87 12

2.需求

統計出每個省份廣告被點擊次數的top3

3.思路分析

思路

4.實現

package com.george.bigdata.agent

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: weicaijia
 * Date: 2019/11/1 17:41
 * Time: 14:15
 */
/**
 * 統計出每一個省份廣告被點擊次數的TOP3
 * 數據結構:時間戳,省份,城市,用戶,廣告,中間字段使用空格分割
 */
object Agent {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local[*]").setAppName("agent")
    val sc = new SparkContext(conf)

    //讀取文件到RDD
    val fileRDD = sc.textFile("input/data/agent/agent.log")
    //    查看
    //    fileRDD.collect().foreach(println)

    // 1.map 操作
    val provinceAdMap: RDD[((String, String), Int)] = fileRDD.map(datas => {
      val fields: Array[String] = datas.split(" ")
      //返回格式: ((省份-廣告),1)
      ((fields(1), fields(4)), 1)
    })
    //    查看
    //    proveinceAdMap.collect().foreach(println)

    // 2.reduceByKey 操作 返回格式 ((省份-廣告),sum)
    val provinceAdMapSum: RDD[((String, String), Int)] = provinceAdMap.reduceByKey(_ + _)
    //    查看
    //    provinceAdMapSum.collect().foreach(println)

    // 3.map 操作把格式轉換一下  ((省份-廣告),sum) ====> (省份,(廣告,sum))
    val provinceToAdSum: RDD[(String, (String, Int))] = provinceAdMapSum.map(datas => {
      // 返回 格式
      (datas._1._1, (datas._1._2, datas._2))
    })
    //    查看
    //    provinceToAdSum.collect().foreach(println)

    // 4.groupByKey 操作 返回格式 ((省份1,List((廣告1,sum1),(廣告2,sum2)...)),(省份2,List((廣告1,sum1),(廣告2,sum2)...)))
    val provinceAdGroup: RDD[(String, Iterable[(String, Int)])] = provinceToAdSum.groupByKey()
    //    查看
    //    provinceAdGroup.collect().foreach(println)


    // 5. 排名並取3條
    // 利用 mapValues 是隻對value進行操作的特性
    val result: RDD[(String, List[(String, Int)])] = provinceAdGroup.mapValues(datas => {
      val list: List[(String, Int)] = datas.toList
      list.sortWith((x, y) => {
        x._2 > y._2
      }).take(3)
    })

    result.collect().foreach(println)
    sc.stop()

  }

}

具體的日誌文件我就不提供了,可以去視頻下面拿別人Git存的。我沒有傳到我的Git
以上代碼和筆記都是手敲,結果也都對照過。
視頻沒寫這一集。。。

Action

reduce(func)

1.作用

通過func函數聚集RDD中的所有元素,先聚合分區內數據,再聚合分區間數據。

2.需求

創建一個RDD,將所有元素聚合得到結果

3.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd1: RDD[Int] = sc.makeRDD(1 to 20, 2)
// 聚合
val rdd1Result: Int = rdd1.reduce(_ + _)
println(rdd1Result)

val rdd2: RDD[(String, Int)] = sc.parallelize(Array(("a", 1), ("a", 3), ("c", 3), ("d", 5)))
// 聚合
val rdd2Result: (String, Int) = rdd2.reduce((x, y) => {
  (x._1 + y._1, x._2 + y._2)
})
println(rdd2Result)

collect()

1.作用

驅動程序中,以數組的形式返回數據集的所有元素。

count()

1.作用

返回RDD中元素個數

first()

1.作用

返回RDD中第一個元素

take(n)

1.作用

返回一個由RDD的前n個元素組成的數組。

takeOrdered(n)

1.作用

返回RDD排序後,前n個元素組成的數組

aggregate

1.參數

(zeroValue:U)(seqOp:(U,T)=>U,combOp(U,U)=>U)

2.作用

將每個分區裏面的元素,通過seqOp和初始值進行聚合。然後用combine函數將每個分區的結果和初始值(zeroValue)進行combine操作,這個函數最終返回的類型不需要和RDD中元素類型一致。

3.需求

創建RDD,所有元素相加得到結果。
4.解

val conf = new SparkConf().setMaster("local[*]").setAppName("glom")
val sc = new SparkContext(conf)

val rdd1: RDD[Int] = sc.makeRDD(1 to 10, 2)
// 初始值爲0  分區內相加   分區間相加
val result: Int = rdd1.aggregate(0)(_+_,_+_)
println(result)

fold(num)(func)

1.作用

摺疊操作,aggregate的簡化版。但是!返回值必須與rdd的數據類型相同。

saveAsTextFile

1.作用

將數據集的元素以textfile的形式保存到HDFS文件系統或者其他支持的文件系統。對於每個元素,Spark將會調用toString方法,轉換爲文件中的文本。

saveAsSequenceFile

1.作用

將數據集中的元素以Hadoop sequencefile的格式保存到指定目錄下,可以使HDFS或其他Hadoop支持的文件系統。

saveAsObjectFile

1.作用

將RDD中的元素序列化成對象,存到文件

countByKey

1.作用

針對kv類型RDD,返回一個(K,Int)的map,表示每個key對應的元素個數。

foreach(func)

1.作用

在數據集的每個元素上,運行函數func進行更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章