RDD編程
Spark中的核心數據操作:
- 創建RDD
- 轉換已有的RDD
- 調用RDD操作進行求值
Note:
RDD是Spark數據操作的核心,它的主要特點是操作鏈,惰性求值。
RDD創建
創建RDD主要有兩種方法:
- 讀取外部數據集
JavaRDD<String> lines = sc.textFile("your file path")
- 在驅動程序中對一個集合進行並行化
JavaRDD<String> lines = sc.parralelize(Arrays.asList("pandas","i like pandas"));
RDD的操作
RDD的操作可以分爲兩種操作:1. 轉換操作,2. 行動操作。
- 轉換操作
RDD的轉換操作是在RDD的依賴操作鏈上新增一個操作,但是並不對RDD進行實際計算,而是直接返回一個新的RDD。(這個像函數式編程裏面的惰性計算,能有效的提高性能。)。
Note:
Spark裏面主要通過譜系圖(lineage graph)來記錄RDD的操作依賴鏈關係。通
過這種方式,Spark可以實現按需計算轉換操作,也可在持久化的RDD丟失部分數據的情況時恢復所丟失的數據。
常用的轉換操作有:
filter() //過濾數據集
map() //依次對數據集中每個數據進行操作
flatmap() //將函數應用於RDD的每個元素,將返回的迭代器的所有內容構成新的RDD,通常用來切分單詞
distinct() //去重
sample() //採樣
union() //求並集
intersection() //求交集
substract() //求子集
cartesian() //求笛卡爾子集
示例:
//filter
JavaRDD<String> errorsRDD = inputRDD.filter(
new Function<String, Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
}
);
- 行動操作
RDD的行動操作會對數據集進行實際計算,它會根據RDD的依賴操作鏈進行優化,然後根據優化後的依賴操作鏈上的轉換操作依次對數據進行轉換操作,最後計算出結果返回到驅動器程序或者寫入外部存儲系統中。
常用的行動操作有:
count() //返回結果總數
take(num) //返回一部分結果
collect() //返回所有結果
reduce() //聚合
aggregate() //累加
示例:
1.求和
Integer sum = rdd.reduce(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer x, Integer y) { return x+y; }
});
2.計算平均值
class AvgCount implements Serializable {
public AvgCount(int total, int num) {
this.total = total;
this.num = num;
}
public int total;
public int num;
public double avg() {
return total / (double) num;
}
};
Function2<AvgCount, Integer, AvgCount> addAndCount = new Function2<AvgCount,Integer, AvgCount>() {
public AvgCount call(AvgCount a, Integer x) {
a.total +=x;
a.num += 1;
return a;
}
};
Function2<AvgCount, AvgCount, AvgCount> combine = new Function2<AvgCount,AvgCount,AvgCount>() {
public AvgCount call(AvgCount a, AvgCount b) {
a.total += b.total;
a.num += b.num;
return a;
}
}
AvgCount initial = new AvgCount(0,0);
AvgCount result = rdd.aggreate(initial, addAndCount, combine);
Note:
除非我們對RDD的中間結果進行了持久化操作,每當我們調用一個新的行動操作時,整個RDD都會從頭計算。
- 函數傳遞
在Spark
中很多操作都需要依賴用戶傳遞的函數。用Java
傳遞用戶函數,主要實現Spark
中的org.apache.api.java.function
中任一函數接口的對象來進行傳遞的。該包中主要提供了三個接口。
函數名 | 實現方法 | 用途 |
---|---|---|
Function<T,R> | R call(T) | 接收一個輸入值並返回一個輸出值,用於類似map()和filter等操作中 |
Function2<T1,T2,R> | R call(T1,T2) | 接收兩個輸入值並返回一個輸出值,用於類似aggregate()和fold()等操作中 |
FlatMapFunction<T,R> | Iterable call(T) | 接收一個輸入值並返回任意個輸出,用於類似flatMap()這樣的操作中 |
具體的實現主要有三種方法:
- 匿名內部類
RDD<String> errors = lines.filter(new Function<String,Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
});
- 具名類
class ContainsError implements Function<String, Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
}
RDD<String> errors = lines.filter(new ContainsError());
3.lambda表達式
RDD<String> errors = lines.filter(s -> s.contains("error"));
- 示例
1.計算平方值
JavaRDD<Integer> rdd = sc.parrallelize(Arrays.asList(1,2,3,4));
JavaRDD<Integer> result = rdd.map(new Function<Integer, Integer>() {
public Integer call(Integer x) { return x*x; }
});
System.out.println(StringUtils.join(result.collect(),","));
- 將行數據切分爲單詞
JavaRDD<String> lines = sc.parallelize(Arrays.asList("hello world", "hi"));
JavaRDD<String> words = llines.flatMap(new FlatMapFunction<String, String>() {
public Iterable<String> call(String line) {
return Arrays.asList(line.split(" ")));
}
});
words.first();
集合操作
RDD也支持一些集合操作,但是並不是嚴格意義上的集合操作,它並不符合集合的唯一性。因爲有些操作RDD並不會對結果集合進行去重,需要手動進行distinct()
操作進行去重。
操作名 | 作用 | 是否進行數據混洗(去重) |
---|---|---|
distinct | 去重 | 是 |
union | 求並集 | 否 |
intersection | 求交集 | 是 |
substract | 求差集 | 是 |
cartesian | 求笛卡爾集 |
Note:
distinct()
,intersection()
和substract()
都需要進行數據混洗進行去重,開銷會比較大。
不同RDD類型之間轉換
鑑於有些函數只能用於特定類型,比如 mean()
和 variance()
只能用於數值,而 join()
只能用於鍵值對類型。Spark中除了提供通用的RDD,還提供了些特殊RDD,比如:DoubleRDD
(java中的JavaDoubleRDD
)和PairRDD
(java中的JavaPairRDD
)。在java
中使用spark
和scala
不同的是,spark
不會像在scala
中一樣對RDD
進行隱性轉換,我們需要在代碼顯性轉換RDD
的類型。
示例:
將上面的計算平均值改寫成爲一個JavaDoubleRDD
版本的計算每個元素的平方值的示例。
//將一個mapRDD類型轉換爲一個DoubleRDD類型
JavaDoubleRDD result = rdd.mapToDouble(new DoubleFunction<Integer>() {
public double call(Integer x) {
return (double) x*x;
}
});
System.out.println(result.mean());
其他相關類型之間的轉換:
方法 | 用途 |
---|---|
flatMapToDouble | 將flatMap類型轉換成DoubleRDD |
mapToDouble | 將一個map類型轉換成DoubleRDD |
flatMapToPair | 將flatMap類型轉換成PairRDD |
mapToPair | 將map類型轉換成PairRDD |
引用
- 《Spark快速大數據分析》