定义
class Dataset[T](sparkSession: SparkSession, queryExecution: QueryExecution, encoder: Encoder[T])
extends Serializable
数据集是特定于域的对象的强类型集合,可以使用函数或关系操作并行转换这些对象。每个数据集还有一个称为DataFrame的非类型化视图,它是Row的数据集。
数据集上可用的操作分为转换和操作。转换是生成新数据集的转换,而操作是触发计算并返回结果的操作。示例转换包括map、filter、select和aggregate(groupBy)。示例操作计数、显示或将数据写入文件系统。
数据集是“惰性”的,也就是说,只有在调用操作时才会触发计算。在内部,数据集表示一个逻辑计划,该计划描述生成数据所需的计算。当一个动作被调用时,Spark的查询优化器会优化逻辑计划并生成一个物理计划,以便以并行和分布式的方式高效地执行。要探索逻辑计划和优化的物理计划,请使用解释功能。
为了有效地支持特定于域的对象,需要一个编码器。编码器将域特定的类型T映射到Spark的内部类型系统。例如,给定一个具有两个字段name(string)和age(int)的Person类,编码器将用于告诉Spark在运行时生成代码,以将Person对象序列化为二进制结构。这种二进制结构通常具有较低的内存占用,并且针对数据处理的效率进行了优化(例如,以列格式)。要理解数据的内部二进制表示,请使用schema函数。
利用List,Seq,Array创建Dataset 对象
在创建Dataset的时候,需要注意数据的格式,必须使用case class,或者基本数据类型,同时需要通过import spark.implicts._来完成数据类型的编码,而抽取出对应的元数据信息,否则编译无法通过
def createDataset[T : Encoder](data: Seq[T]): Dataset[T]
object DataSetDemo1 extends App {
val spark: SparkSession = SparkSession.builder()
.appName("SparkSQL")
.master("local")
.getOrCreate()
import spark.implicits._
private val girls = List(
//因为是case class,所以可以不用new关键字构建对象
Girl(1, "lw", 1, 180.0),
Girl(2, "wsw", 2, 179.0),
Girl(3, "chh", 1, 183.0),
Girl(4, "mnn", 0, 168.0)
)
val frame= spark.createDataset(girls)
frame.show()
spark.stop()
}
//创建case class
case class Girl(id: Int, name: String, sex: Int, height: Double)
运行结果如下
+---+----+---+------+
| id|name|sex|height|
+---+----+---+------+
| 1| lw| 1| 180.0|
| 2| wsw| 2| 179.0|
| 3| chh| 1| 183.0|
| 4| mnn| 0| 168.0|
+---+----+---+------+
其中createDataset() 方法传入List,Array,Seq均可.
private val girls = Seq(
Girl(1, "lw", 1, 180.0),
Girl(2, "wsw", 2, 179.0),
Girl(3, "chh", 1, 183.0),
Girl(4, "mnn", 0, 168.0)
)
再比如
val ds= spark.createDataset(0 to 10)
ds.show()
运行结果如下
+-----+
|value|
+-----+
| 0|
| 1|
| 2|
| 3|
| 4|
| 5|
| 6|
| 7|
| 8|
| 9|
| 10|
+-----+
利用RDD创建DataSet对象
相当于把上面例子中的List,Array,Seq用RDD包装一下
object rdd2DataSet extends App {
val spark: SparkSession = SparkSession.builder()
.appName("SparkSQL")
.master("local")
.getOrCreate()
import spark.implicits._
private val value: RDD[Girl] = spark.sparkContext.parallelize(List(
Girl(1, "lw", 1, 180.0),
Girl(2, "wsw", 2, 179.0),
Girl(3, "mhh", 1, 183.0),
Girl(4, "mnn", 0, 168.0)
))
//rdd转dataset
//Creates a Dataset from an RDD of a given type.
private val dataset: Dataset[Girl] = spark.createDataset(value)
dataset.show()
}
其中,RDD转DS还有简便写法,直接调用toDS方法就可以.
private val dataset: Dataset[Girl] = value.toDS()
dataset.show()
Dataset转RDD
接上述
private val rdd: RDD[Girl] = dataset.rdd
rdd.foreach(println)
运行结果
Girl(1,lw,1,180.0)
Girl(2,wsw,2,179.0)
Girl(3,chh,1,183.0)
Girl(4,mnn,0,168.0)
Dataset转DataFrame
由于type DataFrame = Dataset[Row]
. 因此ds转df也就是改个泛型的事情.
private val dataFrame: DataFrame = dataset.toDF()
dataFrame.show()
来看一把源码,就是改了Dataset的泛型而已.
def toDF(): DataFrame = new Dataset[Row](sparkSession, queryExecution, RowEncoder(schema))
总结
- dataset可通过Seq创建,也可以通过RDD创建
- dataset可以和DataFrame和RDD相互转换