Spark-SQL之RDD转换为DataFrame

案例:(最下面)

一、以编程方式动态指定元数据,将RDD转换为DataFrame -->> RDD2DataFrameProgrammatically

二、使用反射的方式将RDD->>DataFrame -->> RDD2DataFrameReflect

RDD转换为DataFrame之后的话,我们就可以直接针对HDFS等任何可以构建为RDD的数据,使用Spark SQL进行SQL查询了。这个功能是无比强大的。这样就可以针对HDFS中的数据,直接就可以使用SQL进行查询。

Spark SQL支持两种方式来将RDD转换为DataFrame。

第一种方式,是使用反射来推断包含了特定数据类型的RDD的元数据。这种基于反射的方式,代码比较简洁,当你已经知道你的RDD的元数据时,是一种非常不错的方式。

第二种方式,是通过编程接口来创建DataFrame,你可以在程序运行时动态构建一份元数据,然后将其应用到已经存在的RDD上。这种方式的代码比较冗长,但是如果在编写程序时,还不知道RDD的元数据,只有在程序运行时,才能动态得知其元数据,那么只能通过这种动态构建元数据的方式。

使用反射方式推断元数据

Java版本:Spark SQL是支持将包含了JavaBean的RDD转换为DataFrame的。JavaBean的信息,就定义了元数据。Spark SQL现在是不支持将包含了嵌套JavaBean或者List等复杂数据的JavaBean,作为元数据的。只支持一个包含简单数据类型的field的JavaBean。

Scala版本:而Scala由于其具有隐式转换的特性,所以Spark SQL的Scala接口,是支持自动将包含了case class的RDD转换为DataFrame的。case class就定义了元数据。Spark SQL会通过反射读取传递给case class的参数的名称,然后将其作为列名。与Java不同的是,Spark SQL是支持将包含了嵌套数据结构的case class作为元数据的,比如包含了Array等。

使用编程方式指定元数据

Java版本:当JavaBean无法预先定义和知道的时候,比如要动态从一个文件中读取数据结构,那么就只能用编程方式动态指定元数据了。首先要从原始RDD创建一个元素为Row的RDD;其次要创建一个StructType,来代表Row;最后将动态定义的元数据应用到RDD<Row>上。

Scala版本:Scala的实现方式,与Java是基本一样的。

 

DataFrame转换为RDD:

DataFrame.javaRDD() 或 DataFrame.RDD() 

案例一:

java版本:

package Spark_SQL;

import org.apache.spark.SparkConf;
import org.apache.spark.SparkContext;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SQLContext;

import java.util.List;

/**
 * @Date: 2019/3/12 14:36
 * @Author Angle
 */

/*
* 使用反射的方式将RDD->>DataFrame
*
* */
public class RDD2DataFrameReflect {
    public static void main(String[] args){

        //创建普通的RDD
        SparkConf conf = new SparkConf().setAppName("RDD2DataFramReflect").setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);
        SQLContext sqlContext = new SQLContext(sc);

        JavaRDD<String> lines = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt");
        JavaRDD<Student> students = lines.map(new Function<String, Student>() {
            @Override
            public Student call(String line) throws Exception {
                String[] lineSpilt = line.split(",");

                Student stu = new Student();
                stu.setId(Integer.valueOf(lineSpilt[0].trim()));
                stu.setName(lineSpilt[1]);
                stu.setAge(Integer.valueOf(lineSpilt[2].trim()));
                return stu;
            }
        });

        //利用反射将RDD转化为DataFrame
        //将Student.class传入就是利用了反射,它是反射的一个应用
        //底层通过Student Class进行反射获取其中的field
        Dataset<Row> studentDF = sqlContext.createDataFrame(students, Student.class);

        //拿到DataFrame表后,将其注册为一个临时表,针对其数据进行SQL
        //这里要求JavaBean实现序列化接口Serializable
        studentDF.registerTempTable("student");

        //针对临时表操作SQL-->>>查询年龄小于18
        Dataset<Row> teenagerDF = sqlContext.sql("select * from student where age<=18");

        //查出来的DataFrame,转化成RDD
        JavaRDD<Row> teenagerRDD = teenagerDF.javaRDD();

        //将RDD的数据进行映射,映射为Student
        JavaRDD<Student> teeStudentRDD = teenagerRDD.map(new Function<Row, Student>() {
            @Override
            public Student call(Row row) throws Exception {
                //row中数据的顺序可能和期望不同
                Student stu = new Student();
                stu.setAge(row.getInt(0));
                stu.setId(row.getInt(1));
                stu.setName(row.getString(2));
                return stu;
            }
        });

        //将数据collect回来,打印出来
        List<Student> studentList = teeStudentRDD.collect();
        for (Student stu:studentList){
            System.out.println(stu);

        }
    }
}

scala版本 :

package SparkSQL_Scala

import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}

/**
  * @Date: 2019/3/12 16:48
  * @Author Angle
  */

/*
* 要用scala开发Spark程序
*在其中要实现基于反射RDD到DataFrame的转换,必须要用object extends App方式
* 而不能用def main()方式来运行,否则会报错:no typetag for...class
*
* */
object RDD2DataFrameReflect_s extends App {

  val conf = new SparkConf().setAppName("RDD2DataFrameReflect_S").setMaster("local")
  val sc = new SparkContext(conf)
  val sqlContext = new SQLContext(sc)

  //在Scala中使用反射进行RDD-->>DataFrame转换,需要手动导入隐式转换
  //import sqlContext.implicits._

  //样例类
  case class Student(id:Int,name:String,age:Int)

  val lines = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt")

  //拿到普通的Student的RDD(元素为case class)
  val students = lines.map(line => line.split(","))
    .map(arr => Student(arr(0).trim.toInt,arr(1),arr(2).trim.toInt))

  //创建DataFrame,传入RDD
  val studentDF = sqlContext.createDataFrame(students)

  //注册临时表
  studentDF.registerTempTable("students");

  val teenagerDF = sqlContext.sql("select * from students where age<=18")

  //转换成RDD
  val teenagerRDD = teenagerDF.rdd

  //顺序--> age-name-id
  //在scala中row中数据的顺序,是按照我们期望的排序的,和java不一样
  teenagerRDD.map(row => Student(row(0).toString.toInt,row(1).toString,row(2).toString.toInt))
    .collect()
    .foreach(stu => println(stu.id + ":" + stu.name + ":" + stu.age))


  //在scala中对row的使用比java的row更加丰富
  //scala中可以用row的getAs()方法,获取指定列名的列
  teenagerRDD.map(row => Student(
    row.getAs[Int]("id"), row.getAs[String]("name"),row.getAs[Int]("age")))
    .collect()
    .foreach(stu => println(stu.id + ":" + stu.name + ":" +stu.age))


  //可以通过row的getValuesMap()方法,获取指定几列的值,返回的是个map
  teenagerRDD.map(row => {
    val map = row.getValuesMap[Any](Array("id","name","age"))
    Student(map("id").toString.toInt,map("name").toString,map("age").toString.toInt)
  })
    .collect()
    .foreach(stu => println(stu.id + ":" + stu.name + ":" + stu.age))
}

案例二:

Java版本:

package Spark_SQL;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.types.DataType;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

/**
 * @Date: 2019/3/13 18:59
 * @Author Angle
 */

/*
* 以编程方式动态指定元数据,将RDD转换为DataFrame
*
* */
public class RDD2DataFrameProgrammatically {

    public static void main(String[] args){
        SparkConf conf = new SparkConf().setAppName("RDD2DataFrameProgrammatically").setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);
        SQLContext sqlContext = new SQLContext(sc);

        //一、创建普通的RDD,但要转换为RDD<row>的格式
        JavaRDD<String> linesRDD = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt");

        //往Row中塞数据的时候要注意:类型!!
        JavaRDD<Row> studentRDD = linesRDD.map(new Function<String, Row>() {
            @Override
            public Row call(String line) throws Exception {
                String[] lineSpilt = line.split(",");
                //后面要用到Integer类型,所以应该把String类型转化为Integer
                return RowFactory.create(Integer.valueOf(lineSpilt[0]),lineSpilt[1],Integer.valueOf(lineSpilt[2]));
            }
        });

        //二、动态构造元数据
        //如id,name等类型,可能在程序运行过程中,从mysql或配置文件加载,是不固定的--->>适合这种动态构造
        ArrayList<StructField> structFields = new ArrayList<StructField>();
        structFields.add(DataTypes.createStructField("id",DataTypes.IntegerType,true));
        structFields.add(DataTypes.createStructField("name",DataTypes.StringType,true));
        structFields.add(DataTypes.createStructField("age",DataTypes.IntegerType,true));
        StructType structType = DataTypes.createStructType(structFields);

        //三、将RDD转化为DataFrame
        Dataset<Row> stuDF= sqlContext.createDataFrame(studentRDD,structType);

        //注册为student_s表
        stuDF.registerTempTable("student_s");

        Dataset<Row> teenagerDF = sqlContext.sql("select * from student_s where age<=18");

        //数据拉取到本地
        List<Row> rows = teenagerDF.javaRDD().collect();

        for (Row row:rows){
            System.out.println(row);
        }


    }
}

scala版本:

package SparkSQL_Scala

import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}
import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * @Date: 2019/3/13 20:22
  * @Author Angle
  */
object RDD2DataFrameProgrammatically_s {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("RDD2DataFrameProgrammatically_s").setMaster("local")
    val sc = new SparkContext(conf)
    val sqlContext = new SQLContext(sc)

    //一、构造元素为Row的普通的RDD
    val lines = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt",1)
      .map{line => Row(line.split(",")(0).toInt,line.split(",")(1),line.split(",")(2).toInt)}

    //二、编程方式动态构造元数据
    val structType = StructType(Array(
          StructField("id",IntegerType,true),
          StructField("name",StringType,true),
          StructField("age",IntegerType,true)))

    //三、进行RDD到DataFrame的转换
    val studentDF = sqlContext.createDataFrame(lines,structType)

    //注册
    studentDF.registerTempTable("student_s")

    val teenagerDF = sqlContext.sql("select * from student_s where age<=19")

    val teenagerRDD = teenagerDF.rdd.collect()
      .foreach(row => println("id:" + row.get(0) + " name:" + row.get(1) + " age:" +row.get(2)))
  }

}

接下来还会慢慢更新Spark相关知识,有问题还请指正 

猜你喜欢

转载自blog.csdn.net/S_Running_snail/article/details/89280881
今日推荐