spark transformation算子

一、什麼是RDD算子

答:所謂RDD算子,就是RDD中定義的函數,可以對RDD中的元素進行轉換和操作。

 

二.算子的分類

算子分爲兩類:轉換算子(Transformation)和行動算子(Action)。

  1. 轉換算子(Transformation):操作時延遲計算的,也就是一個RDD轉換爲另外一個RDD不是馬上執行的,需要等到行動算子(Action)執行的時候,纔會真正觸發。
  2. 行動算子(Action):Action算子的執行會觸發Spark提交作業。

三.導包

本地導入目前spark最新版本,spark1.6進行測試

 

Java代碼 

 收藏代碼

  1. <dependency>  
  2.     <groupId>org.apache.spark</groupId>  
  3.     <artifactId>spark-core_2.10</artifactId>  
  4.     <version>1.6.0</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>org.apache.spark</groupId>  
  8.     <artifactId>spark-sql_2.10</artifactId>  
  9.     <version>1.6.0</version>  
  10. </dependency>  
  11. <dependency>  
  12.     <groupId>org.apache.hadoop</groupId>  
  13.     <artifactId>hadoop-client</artifactId>  
  14.     <version>2.5.2</version>  
  15. </dependency>  

 

 

 

四.轉換算子(Transformation)

溫馨提示:這裏演示使用javaAPI來使用算子,在javaAPI中目前沒有處理key-value的算子,只有處理value數據類型的算子,也就是說如下API沒有提供

  • mapValues()
  • combineByKey()
  • reduceByKey()
  • partitionBy()
  • Cogroup()
  • Join()

4.1.輸入分區與輸出分區一對一型

4.1.1.map()

將原來RDD的每個數據項通過map中的用戶自定義函數f映射轉變爲一個新的元素。源碼中的map算子相當於初始化一個RDD,新RDD叫作MappedRDD(this, sc.clean(f))。

圖(4-1-1)中的每個方框表示一個RDD分區,左側的分區經過用戶自定義函數f:T->U映射爲右側的新的RDD分區。但是實際只有等到Action算子觸發後,這個f函數纔會和其他函數在一個Stage中對數據進行運算。V1輸入f轉換輸出V’1。



圖(4-1-1) 

演示代碼如下:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * 通過Map算子,將RDD中json字符串對象轉換爲java對象 
  3.  *  
  4.  * @author Ickes 
  5.  * 
  6.  */  
  7. public class MapDemo {  
  8.     public static void main(String[] args) {  
  9.         SparkConf sparkConf = new SparkConf().setAppName("map").setMaster(  
  10.                 "local");  
  11.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  12.         List<String> data = Arrays.asList(  
  13.                 "{'id':1,'name':'xl1','pwd':'xl123','sex':2}",  
  14.                 "{'id':2,'name':'xl2','pwd':'xl123','sex':1}",  
  15.                 "{'id':3,'name':'xl3','pwd':'xl123','sex':2}");  
  16.         JavaRDD<String> rddData = sc.parallelize(data);  
  17.         rddData.map(new Function<String, User>() {  
  18.             @Override  
  19.             public User call(String v) throws Exception {  
  20.                 Gson gson = new Gson();  
  21.                 return gson.fromJson(v, User.class);  
  22.             }  
  23.         }).foreach(System.out::println);  
  24.     }  
  25. }  

 

 打印結果如下:

 

 

Java代碼 

 收藏代碼

  1. User [id=1, name=xl1, pwd=xl123, sex=2]  
  2. User [id=2, name=xl2, pwd=xl123, sex=1]  
  3. User [id=3, name=xl3, pwd=xl123, sex=2]  

 

 

 

4.1.2.flatMap()

將原來RDD中的每個元素通過函數f轉換爲新的集合,並將生成的RDD的每個集合中的元素合併爲一個集合。內部創建 FlatMappedRDD(this, sc.clean(f))。

如下圖(4-1-2)中所示:



圖(4-1-2)

演示代碼如下:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * 將rdd中的元素,通過逗號分隔; 
  3.  * 原始RDD中僅有三個元素,通過flatMap後,新的RDD中有9個元素 
  4.  * @author Ickes 
  5.  * 
  6.  */  
  7. public class FlatMapDemo {  
  8.     public static void main(String[] args) {  
  9.         SparkConf sparkConf = new SparkConf().setAppName("flatMap").setMaster(  
  10.                 "local");  
  11.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  12.         List<String> data = Arrays.asList(  
  13.                 "aa,bb,cc",  
  14.                 "cxf,spring,struts2",  
  15.                 "java,C++,javaScript");  
  16.         JavaRDD<String> rddData = sc.parallelize(data);  
  17.         rddData.flatMap(new FlatMapFunction<String,String>() {  
  18.             @Override  
  19.             public Iterable<String> call(String t) throws Exception {  
  20.                 List<String> list= Arrays.asList(t.split(","));  
  21.                 return list;  
  22.             }  
  23.         }).foreach(System.out::println);  
  24.     }  
  25. }  

 返回結果如下:

 

 

Java代碼 

 收藏代碼

  1. aa  
  2. bb  
  3. cc  
  4. cxf  
  5. spring  
  6. struts2  
  7. java  
  8. C++  
  9. javaScript  

 

 

4.1.3.mapPartitions()

mapPartitions函數獲取到每個分區的迭代器,在函數中通過這個分區整體的迭代器對整個分區的元素進行操作。內部實現是生成MapPartitionsRDD。圖(4-1-3)中的方框代表一個RDD分區。

 

圖(4-1-3)中,用戶通過函數f (iter )=>iter.filter(_>=3)對分區中的所有數據進行過濾,>=3的數據保留。一個方塊代表一個RDD分區,含有1、2、3的分區過濾只剩下元素3。



圖(4-1-3)

演示代碼如下:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * MapPartitions 算子 
  3.  * @author Ickes 
  4.  */  
  5. public class MapPartitionsDemo {  
  6.     public static void main(String[] args) {  
  7.         SparkConf sparkConf = new SparkConf().setAppName("MapPartitions").setMaster(  
  8.                 "local");  
  9.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  10.         List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8);  
  11.         JavaRDD<Integer> rddData = sc.parallelize(data);  
  12.         rddData.mapPartitions(new FlatMapFunction<Iterator<Integer>,Integer>() {  
  13.             /** 
  14.              * 其實他跟map的作用一樣,區別在於他的輸入是RDD中分區的迭代器。 
  15.              */  
  16.             @Override  
  17.             public Iterable<Integer> call(Iterator<Integer> t) throws Exception {  
  18.                 List<Integer> list = new ArrayList<Integer>();  
  19.                 while(t.hasNext()){  
  20.                     int num = t.next();  
  21.                     if(num > 3){  
  22.                         list.add(num);  
  23.                     }  
  24.                 }  
  25.                 return list;  
  26.             }  
  27.          }).foreach(System.out::println);  
  28.     }  
  29. }  

 返回結果:

 

 

Java代碼 

 收藏代碼

  1. 4  
  2. 5  
  3. 6  
  4. 7  
  5. 8  

 

 

4.2.輸入分區與輸出分區多對一型 

4.2.1.union()

使用union函數時需要保證兩個RDD元素的數據類型相同,返回的RDD數據類型和被合併的RDD元素數據類型相同,並不進行去重操作,保存所有元素。如果想去重,可以使用distinct()。

 

圖(4-2-1)中左側的大方框代表兩個RDD,大方框內的小方框代表RDD的分區。右側大方框代表合併後的RDD,大方框內的小方框代表分區。含有V1,V2…U4的RDD和含有V1,V8…U8的RDD合併所有元素形成一個RDD。V1、V1、V2、V8形成一個分區,其他元素同理進行合併。



 圖(4-2-1)

演示代碼如下:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * Union算子,合併算子 
  3.  * @author Ickes 
  4.  */  
  5. public class UnionDemo {  
  6.     public static void main(String[] args) {  
  7.         SparkConf sparkConf = new SparkConf().setAppName("Union").setMaster(  
  8.                 "local");  
  9.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  10.         List<Integer> data1 = Arrays.asList(1,2,3,4,5);  
  11.         List<Integer> data2 = Arrays.asList(4,5,6,7,8);  
  12.         JavaRDD<Integer> rddData1 = sc.parallelize(data1);  
  13.         JavaRDD<Integer> rddData2 = sc.parallelize(data2);  
  14.           
  15.         rddData1.union(rddData2).foreach(System.out::println);  
  16.     }  
  17. }  

 返回結果如下:

 

 

Java代碼 

 收藏代碼

  1. 1  
  2. 2  
  3. 3  
  4. 4  
  5. 5  
  6.   
  7. 4  
  8. 5  
  9. 6  
  10. 7  
  11. 8  

 

 

4.2.2.cartesian()

對兩個RDD內的所有元素進行笛卡爾積操作。操作後,內部實現返回CartesianRDD。圖(4-2-2)中左側的大方框代表兩個RDD,大方框內的小方框代表RDD的分區。右側大方框代表合併後的RDD,大方框內的小方框代表分區。

圖(4-2-2)中的大方框代表RDD,大方框中的小方框代表RDD分區。例如,V1和另一個RDD中的W1、W2、Q5進行笛卡爾積運算形成(V1,W1)、(V1,W2)、(V1,Q5)。



 圖(4-2-2)

演示代碼如下:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * Cartesian 算子,或者笛卡爾積算子 
  3.  * @author Ickes 
  4.  */  
  5. public class CartesianDemo {  
  6.     public static void main(String[] args) {  
  7.         SparkConf sparkConf = new SparkConf().setAppName("Cartesian").setMaster(  
  8.                 "local");  
  9.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  10.         List<Integer> data1 = Arrays.asList(1,2,3);  
  11.         List<String> data2 = Arrays.asList("aa","bb","cc");  
  12.         JavaRDD<Integer> rddData1 = sc.parallelize(data1);  
  13.         JavaRDD<String> rddData2 = sc.parallelize(data2);  
  14.           
  15.         rddData1.cartesian(rddData2).foreach(System.out::println);  
  16.     }  
  17. }  

 返回結果如下:

 

 

Java代碼 

 收藏代碼

  1. (1,aa)  
  2. (1,bb)  
  3. (1,cc)  
  4. (2,aa)  
  5. (2,bb)  
  6. (2,cc)  
  7. (3,aa)  
  8. (3,bb)  
  9. (3,cc)  

 

 

4.3.輸入分區與輸出分區多對多型

4.3.1.groupBy()

將元素通過函數生成相應的Key,數據就轉化爲Key-Value 格式,之後將Key相同的元素分爲一組。

圖(4-3-1)中的方框代表一個RDD分區,相同key的元素合併到一個組。例如,V1,V2合併爲一個Key-Value對,其中key爲“V”,Value爲“V1,V2”,形成V,Seq(V1,V2)。



 

圖(4-3-1)

演示代碼如下所示:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * GroupBy算子:分組算子 
  3.  * @author Ickes 
  4.  * 
  5.  */  
  6. public class GroupByDemo {  
  7.     public static void main(String[] args) {  
  8.         SparkConf sparkConf = new SparkConf().setAppName("GroupBy").setMaster(  
  9.                 "local");  
  10.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  11.         List<Integer> data1 = Arrays.asList(1,2,3,1,2,1);  
  12.         JavaRDD<Integer> rddData = sc.parallelize(data1);  
  13.         //jdk1.7  
  14.         rddData.groupBy(new Function<Integer,String>() {  
  15.             @Override  
  16.             public String call(Integer v) throws Exception {  
  17.                 String s = "key"+v;  
  18.                 return s;  
  19.             }  
  20.         }).foreach(System.out::println);  
  21.         //jdk1.8  
  22.         rddData.groupBy(e -> {return "key"+e;}).foreach(System.out::println);  
  23.     }  
  24. }  

 返回結果如下所示:

 

 

Java代碼 

 收藏代碼

  1. (key2,[2, 2])  
  2. (key3,[3])  
  3. (key1,[1, 1, 1])  

 

 

 

4.4.輸出分區爲輸入分區子集型

4.4.1.filter()

filter的功能是對元素進行過濾,對每個元素應用f函數,返回值爲true的元素在RDD中保留,返回爲false的將過濾掉。內部實現相當於生成FilteredRDD(this,sc.clean(f))。

 

圖4-4-1中的每個方框代表一個RDD分區。T可以是任意的類型。通過用戶自定義的過濾函數f,對每個數據項進行操作,將滿足條件,返回結果爲true的數據項保留。例如,過濾掉V2、V3保留了V1,將區分命名爲V1'。



 圖(4-4-1)

 

演示代碼如下:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * Filter算子,過濾算子 
  3.  *  
  4.  * @author Ickes 
  5.  * 
  6.  */  
  7. public class FilterDemo {  
  8.     public static void main(String[] args) {  
  9.         SparkConf sparkConf = new SparkConf().setAppName("GroupBy").setMaster(  
  10.                 "local");  
  11.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  12.         List<Integer> data = Arrays.asList(1, 2, 3, 7, 4, 5, 8);  
  13.         JavaRDD<Integer> rddData = sc.parallelize(data);  
  14.         // 將RDD中小於3的元素進行過濾  
  15.         // jdk1.8以下  
  16.         rddData.filter(new Function<Integer, Boolean>() {  
  17.             @Override  
  18.             public Boolean call(Integer v) throws Exception {  
  19.                 if (v >= 3) {  
  20.                     return true;  
  21.                 }  
  22.                 return false;  
  23.             }  
  24.         }).foreach(System.out::println);  
  25.         // jdk1.8  
  26.         rddData.filter(e -> e >= 3).foreach(System.out::println);  
  27.     }  
  28. }  

 返回結果如下所示:

 

 

Java代碼 

 收藏代碼

  1. 3  
  2. 7  
  3. 4  
  4. 5  
  5. 8  

 

 

4.4.2.distinct()

distinct將RDD中的元素進行去重操作。圖(4-4-2)中的方框代表RDD分區。

圖(4-4-2)中的每個方框代表一個分區,通過distinct函數,將數據去重。例如,重複數據V1、V1去重後只保留一份V1。



 

 圖(4-4-2)

演示代碼如下所示:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * distinct算子,去重操作 
  3.  * @author Ickes 
  4.  * 
  5.  */  
  6. public class DistinctDemo {  
  7.     public static void main(String[] args) {  
  8.         SparkConf sparkConf = new SparkConf().setAppName("Distinct").setMaster(  
  9.                 "local");  
  10.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  11.         List<Integer> data = Arrays.asList(1,2,3,1,2,1);  
  12.         JavaRDD<Integer> rddData = sc.parallelize(data);  
  13.         rddData.distinct().foreach(System.out::println);  
  14.     }  
  15. }  

 返回結果如所示:

 

 

Java代碼 

 收藏代碼

  1. 1  
  2. 3  
  3. 2  

 

 

4.4.3.subtract()

subtract相當於進行集合的差操作,RDD 1去除RDD 1和RDD 2交集中的所有元素。

圖(4-4-3)中左側的大方框代表兩個RDD,大方框內的小方框代表RDD的分區。右側大方框代表合併後的RDD,大方框內的小方框代表分區。V1在兩個RDD中均有,根據差集運算規則,新RDD不保留,V2在第一個RDD有,第二個RDD沒有,則在新RDD元素中包含V2。



 圖(4-4-3)

演示代碼如下所示:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * Subtract算子,用於求兩個集合的差集,要求兩個集合中的元素類型保持一致 
  3.  * @author Ickes 
  4.  * 
  5.  */  
  6. public class SubtractDemo {  
  7.     public static void main(String[] args) {  
  8.         SparkConf sparkConf = new SparkConf().setAppName("Subtract").setMaster(  
  9.                 "local");  
  10.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  11.         List<Integer> data1 = Arrays.asList(1,2,3,4,5);  
  12.         List<Integer> data2 = Arrays.asList(4,5,6,7,8);  
  13.         JavaRDD<Integer> rddData1 = sc.parallelize(data1);  
  14.         JavaRDD<Integer> rddData2 = sc.parallelize(data2);  
  15.           
  16.         rddData1.subtract(rddData2).foreach(System.out::println);  
  17.     }  
  18. }  

 返回結果如下所示:

 

 

Java代碼 

 收藏代碼

  1. 1  
  2. 2  
  3. 3  

 

 

4.4.4.sample()

sample將RDD這個集合內的元素進行採樣,獲取所有元素的子集。用戶可以設定是否有放回的抽樣、百分比、隨機種子,進而決定採樣方式。

* @第一個參數:withReplacement

* true:表示有放回的抽樣;false:表示無放回的抽樣;

* @第二個參數:fraction

* 抽取的百分比,例如0.5就是抽取的50%的數據;

* @第三個參數:seed

* 隨機種子;

圖(4-4-4)中的每個方框是一個RDD分區。通過sample函數,採樣50%的數據。V1、V2、U1、U2、U3、U4採樣出數據V1和U1、U2,形成新的RDD。



 圖(4-4-4)

演示代碼如下所示:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * Sample算子,抽取樣本的算子 
  3.  * @author Ickes 
  4.  *  
  5.  */  
  6. public class SampleDemo {  
  7.     public static void main(String[] args) {  
  8.         SparkConf sparkConf = new SparkConf().setAppName("Sample").setMaster(  
  9.                 "local");  
  10.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  11.         List<Integer> data = Arrays.asList(1,2,3,4,5,6);  
  12.         JavaRDD<Integer> rddData = sc.parallelize(data);  
  13.         /* 
  14.          * @第一個參數:withReplacement 
  15.          * true:表示有放回的抽樣;false:表示無放回的抽樣; 
  16.          * @第二個參數:fraction 
  17.          * 抽取的百分比,例如下面的0.5就是抽取的50%的數據; 
  18.          * @第三個參數:seed 
  19.          * 隨機種子; 
  20.          */  
  21.         rddData.sample(true,0.5,9).foreach(System.out::println);  
  22.     }  
  23. }  

 返回結果如下所示:

 

 

Java代碼 

 收藏代碼

  1. 1  
  2. 1  
  3. 3  
  4. 5  

 

 

4.4.5.takeSample()

takeSample()函數和上面的sample函數是一個原理,但是不使用相對比例採樣,而是按設定的採樣個數進行採樣,同時返回結果不再是RDD,而是相當於對採樣後的數據進行Collect(),返回結果的集合爲單機的數組。

圖(4-4-5) 中左側的方框代表分佈式的各個節點上的分區,右側方框代表單機上返回的結果數組。通過takeSample對數據採樣,設置爲採樣一份數據,返回結果爲V1。



 圖(4-4-5)

演示代碼如下所示:

 

Java代碼 

 收藏代碼

  1. /** 
  2.  * TakeSample算子 
  3.  * @author Ickes 
  4.  */  
  5. public class TakeSampleDemo {  
  6.     public static void main(String[] args) {  
  7.         SparkConf sparkConf = new SparkConf().setAppName("TakeSample").setMaster(  
  8.                 "local");  
  9.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  10.         List<Integer> data = Arrays.asList(1,2,3,4,5,6);  
  11.         JavaRDD<Integer> rddData = sc.parallelize(data);  
  12.         /* 
  13.          * @第一個參數:withReplacement 
  14.          * true:表示有放回的抽樣;false:表示無放回的抽樣; 
  15.          * @第二個參數:num 
  16.          * 抽取樣本的個數 
  17.          */  
  18.         rddData.takeSample(true,2).forEach(System.out::println);  
  19.     }  
  20. }  

 返回結果如下所示:

 

 

Java代碼 

 收藏代碼

  1. 6  
  2. 1  

 

 

4.5.Cache型

4.5.1.cache()

cache將RDD元素從磁盤緩存到內存,相當於persist(MEMORY_ONLY)函數的功能。

 

4.5.2.persist()

persist函數對RDD進行緩存操作。數據緩存在哪裏由StorageLevel枚舉類型確定。有以下幾種類型的組合,如圖(4-5-2),DISK代表磁盤,MEMORY代表內存,SER代表數據是否進行序列化存儲。

 圖(4-5-2)

例如,MEMORY_AND_DISK_SER代表數據可以存儲在內存和磁盤,並且以序列化的方式存儲。其他同理。

圖(4-5-3)中的方框代表RDD分區。disk代表存儲在磁盤,mem代表存儲在內存。數據最初全部存儲在磁盤,通過persist(MEMORY_AND_DISK)將數據緩存到內存,但是有的分區無法容納在內存,例如:圖(4-5-3)中將含有V1,V2,V3的RDD存儲到磁盤,將含有U1,U2的RDD仍舊存儲在內存

圖(4-5-3)


緩存的演示代碼如下所示:

Java代碼 

 收藏代碼

  1. /** 
  2.  * Cache算子,緩存算子 
  3.  * @author Ickes 
  4.  * 
  5.  */  
  6. public class CacheDemo {  
  7.     public static void main(String[] args) {  
  8.         SparkConf sparkConf = new SparkConf().setAppName("Cache").setMaster(  
  9.                 "local");  
  10.         JavaSparkContext sc = new JavaSparkContext(sparkConf);  
  11.         List<Integer> data = Arrays.asList(1,2,3,4,5,6);  
  12.         JavaRDD<Integer> rddData1 = sc.parallelize(data);  
  13.         JavaRDD<Integer> rddData2 = sc.parallelize(data);  
  14.         //cache緩存  
  15.         rddData1.cache().foreach(System.out::println);  
  16.         //persist緩存  
  17.         rddData2.persist(StorageLevel.MEMORY_AND_DISK()).foreach(System.out::println);  
  18.     }  
  19. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章