SparkSQL 之 RDD转为DataSet的两种方式

DataFrame可以从结构化文件、hive表、外部数据库以及现有的RDD加载构建得到。这里主要针对从现有的RDD来构建DataFrame进行实践与解析。

Spark SQL支持将现有的RDDs转换为DataSet的两种不同的方法。

 第一种方法是使用反射来推断包含特定对象类型的RDD的模式。在写Spark程序的同时,已经知道了模式,这种基于反射的方法可以使代码更简洁并且程序工作得更好。

第二种方法是通过一个编程接口来实现,这个接口允许构造一个模式,然后在存在的RDD上使用它。虽然这种方法代码较为冗长,但是它允许在运行期间之前不止列以及列的类型的情况下构造DataSet。

利用反射推断Schema

Spark SQL支持将javabean的RDD自动转换为DataFrame。使用反射获得的BeanInfo定义了表的模式。目前,Spark SQL不支持包含Map字段的javabean。但是支持嵌套的javabean和列表或数组字段。您可以创建一个实现Serializable的类并为其所有字段设置getter和setter,从而创建一个JavaBean。


1、JavaBean的创建

  public static class Person implements Serializable {
    private String name;
    private int age;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public int getAge() {
      return age;
    }

    public void setAge(int age) {
      this.age = age;
    }
  }

2、官网案例

  private static void runInferSchemaExample(SparkSession spark) {
    // Create an RDD of Person objects from a text file
    JavaRDD peopleRDD = spark.read()
      .textFile("examples/src/main/resources/people.txt")
      .javaRDD()
      .map(line -> {
        String[] parts = line.split(",");
        Person person = new Person();
        person.setName(parts[0]);
        person.setAge(Integer.parseInt(parts[1].trim()));
        return person;
      });

    // Apply a schema to an RDD of JavaBeans to get a DataFrame
    Dataset peopleDF = spark.createDataFrame(peopleRDD, Person.class);
    // Register the DataFrame as a temporary view
    peopleDF.createOrReplaceTempView("people");

    // SQL statements can be run by using the sql methods provided by spark
    Dataset teenagersDF = spark.sql("SELECT name FROM people WHERE age BETWEEN 13 AND 19");

    // The columns of a row in the result can be accessed by field index
    Encoder<String> stringEncoder = Encoders.STRING();
    Dataset<String> teenagerNamesByIndexDF = teenagersDF.map(
        (MapFunctionString>) row -> "Name: " + row.getString(0),
        stringEncoder);
    teenagerNamesByIndexDF.show();
    // +------------+
    // |       value|
    // +------------+
    // |Name: Justin|
    // +------------+

    // or by field name
    Dataset<String> teenagerNamesByFieldDF = teenagersDF.map(
        (MapFunctionString>) row -> "Name: " + row.<String>getAs("name"),
        stringEncoder);
    teenagerNamesByFieldDF.show();
    // +------------+
    // |       value|
    // +------------+
    // |Name: Justin|
    // +------------+
    // $example off:schema_inferring$
  }

编程指定Schema

如果不能提前定义JavaBean类(例如,记录的结构是在字符串中编码的,或者将对文本数据集进行解析,而对不同的用户将对字段进行不同的投影),那么可以通过三个步骤以编程方式创建DataSet< Row>。

1、从原始的RDD中创建行的RDD;
2、创建由StructType表示的模式,该结构匹配步骤1中创建的RDD中的行结构。
3、通过SparkSession提供的createDataFrame方法将模式应用到行RDD中。

官网案例

  private static void runProgrammaticSchemaExample(SparkSession spark) {
    // 1、创建一个RDD
    JavaRDD<String> peopleRDD = spark.sparkContext()
      .textFile("examples/src/main/resources/people.txt", 1)
      .toJavaRDD();

    // The schema is encoded in a string
    String schemaString = "name age";

    // 2、根据schema的字符串生成schema
    List fields = new ArrayList<>();
    for (String fieldName : schemaString.split(" ")) {
      StructField field = DataTypes.createStructField(fieldName, DataTypes.StringType, true);
      fields.add(field);
    }
    StructType schema = DataTypes.createStructType(fields);


    // 3、将JavaRDD的记录转换成JavaRDD
    JavaRDD rowRDD = peopleRDD.map((Function<String, Row>) record -> {
      String[] attributes = record.split(",");
      return RowFactory.create(attributes[0], attributes[1].trim());
    });

    ///4、将 schema 应用在JavaRDD ,创建 Dataset
    Dataset peopleDataFrame = spark.createDataFrame(rowRDD, schema);

    // Creates a temporary view using the DataFrame
    peopleDataFrame.createOrReplaceTempView("people");

    // SQL can be run over a temporary view created using DataFrames
    Dataset results = spark.sql("SELECT name FROM people");

    // The results of SQL queries are DataFrames and support all the normal RDD operations
    // The columns of a row in the result can be accessed by field index or by field name
    Dataset<String> namesDS = results.map(
        (MapFunctionString>) row -> "Name: " + row.getString(0),
        Encoders.STRING());
    namesDS.show();
    // +-------------+
    // |        value|
    // +-------------+
    // |Name: Michael|
    // |   Name: Andy|
    // | Name: Justin|
    // +-------------+
    // $example off:programmatic_schema$
  }

你可能感兴趣的:(spark)