Apache Spark:資料框,資料集和RDD之間的區別 - Baeldung

banq發表於2020-10-21

Apache Spark是一個快速的分散式資料處理系統。它執行記憶體中的資料處理,並使用記憶體中的快取和最佳化的執行,從而實現快速效能。它為流行的程式語言(例如Scala,Python,Java和R)提供了高階API。
在本快速教程中,我們將介紹Spark的三個基本概念:資料幀,資料集和RDD。
 

資料框DataFrame
從Spark 1.3開始,Spark SQL引入了表格形式的資料抽象,稱為DataFrame。從那時起,它已成為Spark中最重要的功能之一。當我們要處理結構化和半結構化的分散式資料時,此API很有用。
在第3節中,我們將討論彈性分散式資料集(RDD)。 DataFrame以比RDD更有效的方式儲存資料,這是因為它們使用RDD的不變,記憶體中,彈性,分散式和並行功能,但它們也將架構應用於資料。DataFrames還可以將SQL程式碼轉換為最佳化的低階RDD操作。
我們可以透過三種方式建立DataFrame:

  • 轉換現有的RDD
  • 執行SQL查詢
  • 載入外部資料

Spark團隊在2.0版中引入了SparkSession,它統一了所有不同的上下文,從而確保開發人員無需擔心建立不同的上下文:

SparkSession session = SparkSession.builder()
  .appName("TouristDataFrameExample")
  .master("local<li>")
  .getOrCreate();
 
DataFrameReader dataFrameReader = session.read();


我們將分析Tourist.csv檔案:

Dataset<Row> data = dataFrameReader.option("header", "true")
  .csv("data/Tourist.csv");


由於Spark 2.0 DataFrame成為Row型別的資料集,因此我們可以將DataFrame用作Dataset <Row>的別名。
我們可以選擇感興趣的特定列。我們還可以過濾和分組給定的列:

data.select(col("country"), col("year"), col("value"))
  .show();
 
data.filter(col("country").equalTo("Mexico"))
  .show();
 
data.groupBy(col("country"))
  .count()
  .show();

 

資料集Datasets
資料集是一組強型別的結構化資料。它們提供了熟悉的物件導向程式設計風格以及型別安全性的好處,因為資料集可以在編譯時檢查語法並捕獲錯誤。
資料集是DataFrame的擴充套件,因此我們可以將DataFrame視為資料集的無型別檢視。
Spark團隊在Spark 1.6中釋出了Dataset API,正如他們提到的那樣:“ Spark Datasets的目標是提供一個API,使使用者可以輕鬆地表達物件域上的轉換,同時還提供Spark SQL執行的效能和魯棒性優勢。發動機”。
首先,我們需要建立一個TouristData型別的類:

public class TouristData {
    private String region;
    private String country;
    private String year;
    private String series;
    private Double value;
    private String footnotes;
    private String source;
    // ... getters and setters
}

要將每個記錄對映到指定的型別,我們將需要使用編碼器。編碼器在Java物件和Spark的內部二進位制格式之間進行轉換:

// SparkSession initialization and data load
Dataset<Row> responseWithSelectedColumns = data.select(col("region"), 
  col("country"), col("year"), col("series"), col("value").cast("double"), 
  col("footnotes"), col("source"));
 
Dataset<TouristData> typedDataset = responseWithSelectedColumns
  .as(Encoders.bean(TouristData.class));

與DataFrame一樣,我們可以按特定的列進行過濾和分組:

typedDataset.filter((FilterFunction) record -> record.getCountry()
  .equals("Norway"))
  .show();
 
typedDataset.groupBy(typedDataset.col("country"))
  .count()
  .show();

我們還可以進行操作,例如按列匹配特定範圍進行過濾或計算特定列的總和,以獲取其總值:

typedDataset.filter((FilterFunction) record -> record.getYear() != null 
  && (Long.valueOf(record.getYear()) > 2010 
  && Long.valueOf(record.getYear()) < 2017)).show();
 
typedDataset.filter((FilterFunction) record -> record.getValue() != null 
  && record.getSeries()
    .contains("expenditure"))
    .groupBy("country")
    .agg(sum("value"))
    .show();

 

RDD
彈性分散式資料集或RDD是Spark的主要程式設計抽象。它代表了不可變,有彈性和分散式的元素的集合。
一個RDD封裝了一個大型資料集,Spark將自動在整個叢集中分佈RDD中包含的資料,並並行化我們對其執行的操作。
我們只能透過穩定儲存中的資料操作或其他RDD上的操作來建立RDD。
當我們處理大量資料並且資料分佈在群集計算機上時,容錯能力至關重要。RDD由於Spark內建的故障恢復機制而具有彈性。Spark依賴於以下事實:RDD會記住它們的建立方式,以便我們可以輕鬆地追溯沿襲來恢復分割槽。
我們可以對RDD執行兩種型別的操作:Transformations和Actions。

  • 轉變

我們可以將轉換應用於RDD以操縱其資料。執行完此操作後,我們將得到一個全新的RDD,因為RDD是不可變的物件。
我們將檢查如何實現Map和Filter,這是兩種最常見的轉換。
首先,我們需要建立一個JavaSparkContext並從Tourist.csv檔案中將資料作為RDD載入:

SparkConf conf = new SparkConf().setAppName("uppercaseCountries")
  .setMaster("local<li>");
JavaSparkContext sc = new JavaSparkContext(conf);
 
JavaRDD<String> tourists = sc.textFile("data/Tourist.csv");

接下來,讓我們應用map函式從每個記錄中獲取國家的名稱,並將名稱轉換為大寫。我們可以將此新生成的資料集儲存為磁碟上的文字檔案:

JavaRDD<String> upperCaseCountries = tourists.map(line -> {
    String[] columns = line.split(COMMA_DELIMITER);
    return columns[1].toUpperCase();
}).distinct();
 
upperCaseCountries.saveAsTextFile("data/output/uppercase.txt");

如果只想選擇一個特定國家/地區,則可以對原始遊客RDD應用過濾功能:

JavaRDD<String> touristsInMexico = tourists
  .filter(line -> line.split(COMMA_DELIMITER)[1].equals("Mexico"));
 
touristsInMexico.saveAsTextFile("data/output/touristInMexico.txt");

  • Action

在對資料進行一些計算之後,動作將返回最終值或將結果儲存到磁碟。
Spark中經常使用的兩個動作是Count和Reduce。
讓我們在CSV檔案中計算國家總數:

// Spark Context initialization and data load
JavaRDD<String> countries = tourists.map(line -> {
    String[] columns = line.split(COMMA_DELIMITER);
    return columns[1];
}).distinct();
 
Long numberOfCountries = countries.count();


現在,我們將按國家/地區計算總支出。我們需要過濾描述中包含支出的記錄。
代替使用JavaRDD,我們將使用JavaPairRDD。一對RDD是一種RDD,可以儲存鍵值對。接下來讓我們檢查一下:

JavaRDD<String> touristsExpenditure = tourists
  .filter(line -> line.split(COMMA_DELIMITER)[3].contains("expenditure"));
 
JavaPairRDD<String, Double> expenditurePairRdd = touristsExpenditure
  .mapToPair(line -> {
      String[] columns = line.split(COMMA_DELIMITER);
      return new Tuple2<>(columns[1], Double.valueOf(columns[6]));
});
 
List<Tuple2<String, Double>> totalByCountry = expenditurePairRdd
  .reduceByKey((x, y) -> x + y)
  .collect();



 綜上所述,當我們需要特定於域的API,需要聚合,求和或SQL查詢等高階表示式時,應使用DataFrames或Datasets。或者當我們想要在編譯時進行型別安全時。
另一方面,當資料是非結構化的並且不需要實現特定的架構時,或者在需要低階的轉換和操作時,我們應該使用RDD。
與往常一樣,所有程式碼示例都可以在GitHub上獲得




 

相關文章