1、Spark SQL支持两种将现有rdd转换为Datasets的方法。第一种方法使用反射来推断包含特定类型对象的RDD的schema。在编写Spark应用程序时,如果您已经了解了schema,那么这种基于反射的方法可以产生更简洁的代码。
2、创建Datasets的第二种方法是通过一个编程接口,该接口允许您构造一个schema,然后将它应用到现有的RDD。虽然此方法更加详细,但当列及其类型直到运行时才知道时,它允许您构造Datasets。
1、反射推断
Spark SQL的Scala接口支持将包含case类的RDD自动转换为DataFrame。case类定义了表的schema。使用反射读取case类的参数名称,并将其作为列的名称。Case类也可以嵌套或包含复杂类型,如Seqs或Arrays。这个RDD可以隐式转换为一个DataFrame,然后注册为一个表。表可以在后续的SQL语句中使用。
采用反射的方式推断出Schema信息,将RDD转换为Dataset和DataFrame,具体步骤:
1、RDD[CaseClass]
2、caseclassRDD.toDF() / caseclassRDD.toDS()
将RDD中数据类型变为CaseClass样例类,直接调用toDF或toDS即可转换。
// TODO: 构建SparkSession实例对象, 对SparkContext封装,得层还是SparkContext
val spark: SparkSession = SparkSession
.builder() // 使用建造者模式构建对象
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[3]")
.getOrCreate()
// For implicit conversions like converting RDDs to DataFrames
import spark.implicits._
// 读取电影评分数据u.data, 每行数据有四个字段,使用制表符分割
// user id | item id | rating | timestamp.
val ratingsRDD: RDD[String] = spark.sparkContext.textFile("datas/ml-100k/u.data", minPartitions = 2)
// 转换数据
val mrsRDD: RDD[MovieRating] = ratingsRDD
.filter(line => null != line && line.trim.split("\t").length == 4)
.mapPartitions{iter =>
iter.map{line =>
// 拆箱操作, Python中常用
val Array(userId, itemId, rating, timestamp) = line.trim.split("\t")
// val arr = line.trim.split("\t")
// 返回MovieRating实例对象
MovieRating(userId, itemId, rating.toDouble, timestamp.toLong)
}
}
// 将RDD转换为DataFrame和Dataset
val mrsDF: DataFrame = mrsRDD.toDF()
mrsDF.printSchema()
mrsDF.show(10)
val mrsDS = mrsRDD.toDS()
mrsDS.printSchema()
mrsDS.show(10)
2、自定义Schema
当case类不能提前定义时(例如,记录的结构被编码在字符串中,或者文本数据集将被解析,字段将被不同的用户以不同的方式投影),可以创建一个DataFrame
可以手动编程定义Schema信息,应用到已存在的RDD上,转换为DataFrame,具体步骤:
1、Create an RDD of Rows from the original RDD;
RDD[Row]
2、Create the schema represented by a StructType matching the structure of Rows in the RDD created in Step 1.
schema
3、Apply the schema to the RDD of Rows via createDataFrame method provided by SparkSession.
调用sparkSession中createDataFrame函数,传递rowsRDD和schema信息
// TODO: 构建SparkSession实例对象, 对SparkContext封装,得层还是SparkContext
val spark: SparkSession = SparkSession
.builder() // 使用建造者模式构建对象
.appName(this.getClass.getSimpleName.stripSuffix("$"))
.master("local[3]")
.getOrCreate()
// For implicit conversions like converting RDDs to DataFrames
import spark.implicits._
// 读取电影评分数据u.data, 每行数据有四个字段,使用制表符分割
// user id | item id | rating | timestamp.
val ratingsRDD: RDD[String] = spark.sparkContext.textFile("datas/ml-100k/u.data", minPartitions = 2)
// a. RDD[Row]
val rowsRDD: RDD[Row] = ratingsRDD.mapPartitions{ iter =>
iter.map{line =>
// 拆箱操作, Python中常用
val Array(userId, itemId, rating, timestamp) = line.trim.split("\t")
// 返回Row实例对象
Row(userId, itemId, rating.toDouble, timestamp.toLong)
}
}
// b. schema
val rowSchema: StructType = StructType(
Array(
StructField("userId", StringType, nullable = true),
StructField("itemId", StringType, nullable = true),
StructField("rating", DoubleType, nullable = true),
StructField("timestamp", LongType, nullable = true)
)
)
// c. 应用函数createDataFrame
val ratingDF: DataFrame = spark.createDataFrame(rowsRDD, rowSchema)
ratingDF.printSchema()
ratingDF.show(10, truncate = false)
// TODO: 将DataFrame转换为Dataset,指定泛型
val ratingsDS: Dataset[MovieRating] = ratingDF.as[MovieRating]
ratingsDS.show()