Dataset 的作用和常见操作
目标
1,理解 Dataset 是什么
2,理解 Dataset 的特性
Dataset 是什么?
@Test
def dataset1(): Unit ={
//1.创建SparkSession.Builder
val spark = new SparkSession.Builder()
.master("local[6]")
.appName(this.getClass.getSimpleName)
.getOrCreate()
//2.导入隐式转换
import spark.implicits._
//3.演示
val sourceRDD: RDD[Person] = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), (Person("lisi", 15))))
val dataSet = sourceRDD.toDS() //toDS 必须导入隐式转换
//dataSet 支持RDD (强类型) API
dataSet.filter( x => x.age > 10).show()
//dataSet 支持弱类型 API
dataSet.filter( 'age> 10 ).show()
dataSet.filter( $"age" > 10).show()
//dataSet 支持 直接写 SQL 表达式
dataSet.filter("age > 10").show()
}
问题1: People 是什么?
People 是一个强类型的类
问题2: 这个 Dataset 中是结构化的数据吗?
非常明显是的, 因为 People 对象中有结构信息, 例如字段名和字段类型
问题3: 这个 Dataset 能够使用类似 SQL 这样声明式结构化查询语句的形式来查询吗?
当然可以, 已经演示过了
问题4: Dataset 是什么?
Dataset 是一个强类型, 并且类型安全的数据容器, 并且提供了结构化查询 API 和类似 RDD 一样的命令式 API
即使使用 Dataset 的命令式 API, 执行计划也依然会被优化
Dataset 具有 RDD 的方便, 同时也具有 DataFrame 的性能优势, 并且 Dataset 还是强类型的, 能做到类型安全.
@Test
def dataset2(): Unit ={
//TODO 屏蔽日志信息
Logger.getLogger("org").setLevel(Level.ERROR)
//1.创建SparkSession.Builder
val spark = new SparkSession.Builder()
.master("local[6]")
.appName(this.getClass.getSimpleName)
.getOrCreate()
//2.导入隐式转换
import spark.implicits._
//3.演示
val sourceRDD: RDD[Person] = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), (Person("lisi", 15))))
val dataSet = sourceRDD.toDS() //toDS 必须导入隐式转换
//优化
dataSet.explain(true)
//无论DataSet 放置的是什么类型对象,最终执行计划中的RDD 上都是InteranlRow
val executionRDD: RDD[InternalRow] = dataSet.queryExecution.toRdd
}
Dataset 的底层是什么?
Dataset 最底层处理的是对象的序列化形式, 通过查看 Dataset 生成的物理执行计划, 也就是最终所处理的 RDD, 就可以判定 Dataset 底层处理的是什么形式的数据
@Test
def dataset3(): Unit ={
//TODO 屏蔽日志信息
Logger.getLogger("org").setLevel(Level.ERROR)
//1.创建SparkSession.Builder
val spark = new SparkSession.Builder()
.master("local[6]")
.appName(this.getClass.getSimpleName)
.getOrCreate()
//2.导入隐式转换
import spark.implicits._
//3.演示
// val sourceRDD: RDD[Person] = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), (Person("lisi", 15))))
// val dataSet = sourceRDD.toDS() //toDS 必须导入隐式转换
val dataSet: Dataset[Person] = spark.createDataset(Seq(Person("zhangsan", 10), Person("lisi", 15)))
//优化
dataSet.explain(true)
//无论DataSet 放置的是什么类型对象,最终执行计划中的RDD 上都是InternalRow
//直接获取到已经分析和解析过的DataSet的执行计划,从中拿到RDD
val executionRDD: RDD[InternalRow] = dataSet.queryExecution.toRdd
//通过将DataSet 底层的RDD[InternalRow] 通过Decoder 转成了DataSet一样的类型RDD
val typeRDD: RDD[Person] = dataSet.rdd
println(executionRDD.toDebugString)
println("-------------------------")
println(typeRDD.toDebugString)
}
dataset.queryExecution.toRdd 这个 API 可以看到 Dataset 底层执行的 RDD, 这个 RDD 中的范型是 InternalRow, InternalRow 又称之为 Catalyst Row, 是 Dataset 底层的数据结构, 也就是说, 无论 Dataset 的范型是什么, 无论是 Dataset[Person] 还是其它的, 其最底层进行处理的数据结构都是 InternalRow
所以, Dataset 的范型对象在执行之前, 需要通过 Encoder 转换为 InternalRow, 在输入之前, 需要把 InternalRow 通过 Decoder 转换为范型对象
可以获取 Dataset 对应的 RDD 表示
在 Dataset 中, 可以使用一个属性 rdd 来得到它的 RDD 表示, 例如 Dataset[T] → RDD[T]
val dataset: Dataset[People] = spark.createDataset(Seq(People("zhangsan", 9), People("lisi", 15)))
/*
(2) MapPartitionsRDD[3] at rdd at Testing.scala:159 []
| MapPartitionsRDD[2] at rdd at Testing.scala:159 []
| MapPartitionsRDD[1] at rdd at Testing.scala:159 []
| ParallelCollectionRDD[0] at rdd at Testing.scala:159 []
*/
//(1)
println(dataset.rdd.toDebugString) // 这段代码的执行计划为什么多了两个步骤?
/*
(2) MapPartitionsRDD[5] at toRdd at Testing.scala:160 []
| ParallelCollectionRDD[4] at toRdd at Testing.scala:160 []
*/
//(2)
println(dataset.queryExecution.toRdd.toDebugString)
(1)使用 Dataset.rdd 将 Dataset 转为 RDD 的形式
(2)Dataset 的执行计划底层的 RDD
可以看到 (1) 对比 (2) 对了两个步骤, 这两个步骤的本质就是将 Dataset 底层的 InternalRow 转为 RDD 中的对象形式, 这个操作还是会有点重的, 所以慎重使用 rdd 属性来转换 Dataset 为 RDD
总结
1,Dataset 是一个新的 Spark 组件, 其底层还是 RDD
2,Dataset 提供了访问对象中某个特定字段的能力, 不用像 RDD 一样每次都要针对整个对象做操作
3,Dataset 和 RDD 不同, 如果想把 Dataset[T] 转为 RDD[T], 则需要对 Dataset 底层的 InternalRow 做转换, 是一个比价重量级的操作
DataFrame 的作用和常见操作
目标
1,理解 DataFrame 是什么
2,理解 DataFrame 的常见操作
DataFrame 是什么?
DataFrame 是 SparkSQL 中一个表示关系型数据库中 表 的函数式抽象, 其作用是让 Spark 处理大规模结构化数据的时候更加容易. 一般 DataFrame 可以处理结构化的数据, 或者是半结构化的数据, 因为这两类数据中都可以获取到 Schema 信息. 也就是说 DataFrame 中有 Schema 信息, 可以像操作表一样操作 DataFrame.
DataFrame 由两部分构成, 一是 row 的集合, 每个 row 对象表示一个行, 二是描述 DataFrame 结构的 Schema.
DataFrame 支持 SQL 中常见的操作, 例如: select, filter, join, group, sort, join 等
case class Person(name:String,age:Int)
val spark: SparkSession = new sql.SparkSession.Builder()
.appName("hello")
.master("local[6]")
.getOrCreate()
import spark.implicits._
val peopleDF: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()
/*
+---+-----+
|age|count|
+---+-----+
| 15| 2|
+---+-----+
*/
peopleDF.groupBy('age)
.count()
.show()
case class Person(name:String,age:Int)
@Test
def dataFrame1(): Unit ={
Logger.getLogger("org").setLevel(Level.ERROR)
//创建SparkSession
val spark = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[6]")
.getOrCreate()
//创建DataFrame
import spark.implicits._
val dataFrame: DataFrame = Seq(Person("zhangsan", 10), Person("lisi", 15)).toDF
//看看新花样
dataFrame.where('age > 10)
.select('name,'age)
.show()
}
/*
+----+---+
|name|age|
+----+---+
|lisi| 15|
+----+---+
*/
通过隐式转换创建 DataFrame
case class Person(name:String,age:Int)
@Test
def dataFrame2(): Unit = {
Logger.getLogger("org").setLevel(Level.ERROR)
//创建SparkSession
val spark = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[6]")
.getOrCreate()
//创建隐式转换
import spark.implicits._
val personList: Seq[Person] = Seq(Person("zhangsan", 15), Person("lisi", 20))
/**
* 创建的DataFrame 的三种方式
* 1,toDF
* 2,createDataFrame
* 3,DataFrameReader
*/
//toDF
val df1 = personList.toDF()
val df2 = spark.sparkContext.parallelize(personList).toDF()
//createDataFrame
val df3 = spark.createDataFrame(personList)
//DataFrameReader(read)
val df4 = spark.read.csv("input\\BeijingPM20100101_20151231_noheader.csv")
df4.show()
}
根据源码可以知道, toDF 方法可以在 RDD 和 Seq 中使用
通过集合创建 DataFrame 的时候, 集合中不仅可以包含样例类, 也可以只有普通数据类型, 后通过指定列名来创建
val spark: SparkSession = new sql.SparkSession.Builder()
.appName("hello")
.master("local[6]")
.getOrCreate()
import spark.implicits._
val df1: DataFrame = Seq("nihao", "hello").toDF("text")
/*
+-----+
| text|
+-----+
|nihao|
|hello|
+-----+
*/
df1.show()
val df2: DataFrame = Seq(("a", 1), ("b", 1)).toDF("word", "count")
/*
+----+-----+
|word|count|
+----+-----+
| a| 1|
| b| 1|
+----+-----+
*/
df2.show()
通过外部集合创建 DataFrame
val spark: SparkSession = new sql.SparkSession.Builder()
.appName("hello")
.master("local[6]")
.getOrCreate()
val df = spark.read
.option("header", true)
.csv("input\\BeijingPM20100101_20151231.csv")
df.show(10)
df.printSchema()
不仅可以从 csv 文件创建 DataFrame, 还可以从 Table, JSON, Parquet 等中创建 DataFrame, 后续会有单独的章节来介绍
案例
在 DataFrame 上可以使用的常规操作
需求: 查看每个月的统计数量
数据准备:准备100条数据
命名为:(BeijingPM20100101_20151231.csv)
No,year,month,day,hour,season,PM_Dongsi,PM_Dongsihuan,PM_Nongzhanguan,PM_US_Post,DEWP,HUMI,PRES,TEMP,cbwd,Iws,precipitation,Iprec
1,2010,1,1,0,4,NA,NA,NA,NA,-21,43,1021,-11,NW,1.79,0,0
2,2010,1,1,1,4,NA,NA,NA,NA,-21,47,1020,-12,NW,4.92,0,0
3,2010,1,1,2,4,NA,NA,NA,NA,-21,43,1019,-11,NW,6.71,0,0
4,2010,1,1,3,4,NA,NA,NA,NA,-21,55,1019,-14,NW,9.84,0,0
5,2010,1,1,4,4,NA,NA,NA,NA,-20,51,1018,-12,NW,12.97,0,0
6,2010,1,1,5,4,NA,NA,NA,NA,-19,47,1017,-10,NW,16.1,0,0
7,2010,1,1,6,4,NA,NA,NA,NA,-19,44,1017,-9,NW,19.23,0,0
8,2010,1,1,7,4,NA,NA,NA,NA,-19,44,1017,-9,NW,21.02,0,0
9,2010,1,1,8,4,NA,NA,NA,NA,-19,44,1017,-9,NW,24.15,0,0
10,2010,1,1,9,4,NA,NA,NA,NA,-20,37,1017,-8,NW,27.28,0,0
11,2010,1,1,10,4,NA,NA,NA,NA,-19,37,1017,-7,NW,31.3,0,0
12,2010,1,1,11,4,NA,NA,NA,NA,-18,35,1017,-5,NW,34.43,0,0
13,2010,1,1,12,4,NA,NA,NA,NA,-19,32,1015,-5,NW,37.56,0,0
14,2010,1,1,13,4,NA,NA,NA,NA,-18,30,1015,-3,NW,40.69,0,0
15,2010,1,1,14,4,NA,NA,NA,NA,-18,28,1014,-2,NW,43.82,0,0
16,2010,1,1,15,4,NA,NA,NA,NA,-18,26,1014,-1,cv,0.89,0,0
17,2010,1,1,16,4,NA,NA,NA,NA,-19,25,1015,-2,NW,1.79,0,0
18,2010,1,1,17,4,NA,NA,NA,NA,-18,30,1015,-3,NW,2.68,0,0
19,2010,1,1,18,4,NA,NA,NA,NA,-18,35,1016,-5,NE,1.79,0,0
20,2010,1,1,19,4,NA,NA,NA,NA,-17,35,1017,-4,NW,1.79,0,0
21,2010,1,1,20,4,NA,NA,NA,NA,-17,38,1017,-5,cv,0.89,0,0
22,2010,1,1,21,4,NA,NA,NA,NA,-17,38,1018,-5,NW,1.79,0,0
23,2010,1,1,22,4,NA,NA,NA,NA,-17,38,1018,-5,NW,2.68,0,0
24,2010,1,1,23,4,NA,NA,NA,129,-17,41,1020,-5,cv,0.89,0,0
25,2010,1,2,0,4,NA,NA,NA,148,-16,38,1020,-4,SE,1.79,0,0
26,2010,1,2,1,4,NA,NA,NA,159,-15,42,1020,-4,SE,2.68,0,0
27,2010,1,2,2,4,NA,NA,NA,181,-11,63.5,1021,-5,SE,3.57,0,0
28,2010,1,2,3,4,NA,NA,NA,138,-7,85,1022,-5,SE,5.36,0,0
29,2010,1,2,4,4,NA,NA,NA,109,-7,85,1022,-5,SE,6.25,0,0
30,2010,1,2,5,4,NA,NA,NA,105,-7,92,1022,-6,SE,7.14,0,0
31,2010,1,2,6,4,NA,NA,NA,124,-7,92,1023,-6,SE,8.93,0,0
32,2010,1,2,7,4,NA,NA,NA,120,-7,85,1024,-5,SE,10.72,0,0
33,2010,1,2,8,4,NA,NA,NA,132,-8,85,1024,-6,SE,12.51,0,0
34,2010,1,2,9,4,NA,NA,NA,140,-7,85,1025,-5,SE,14.3,0,0
35,2010,1,2,10,4,NA,NA,NA,152,-7,85,1026,-5,SE,17.43,0,0
36,2010,1,2,11,4,NA,NA,NA,148,-8,79,1026,-5,SE,20.56,0,0
37,2010,1,2,12,4,NA,NA,NA,164,-8,79,1026,-5,SE,23.69,0,0
38,2010,1,2,13,4,NA,NA,NA,158,-8,79,1025,-5,SE,27.71,0,0
39,2010,1,2,14,4,NA,NA,NA,154,-9,73,1025,-5,SE,31.73,0,0
40,2010,1,2,15,4,NA,NA,NA,159,-9,73,1025,-5,SE,35.75,0,0
41,2010,1,2,16,4,NA,NA,NA,164,-9,73,1026,-5,SE,37.54,0,0
42,2010,1,2,17,4,NA,NA,NA,170,-8,79,1027,-5,SE,39.33,0,0
43,2010,1,2,18,4,NA,NA,NA,149,-8,79,1027,-5,SE,42.46,0,0
44,2010,1,2,19,4,NA,NA,NA,154,-8,79,1028,-5,SE,44.25,0,0
45,2010,1,2,20,4,NA,NA,NA,164,-7,85,1028,-5,SE,46.04,0,0
46,2010,1,2,21,4,NA,NA,NA,156,-7,85,1027,-5,SE,49.17,0,0
47,2010,1,2,22,4,NA,NA,NA,126,-8,85,1028,-6,SE,52.3,0,0
48,2010,1,2,23,4,NA,NA,NA,90,-8,85,1027,-6,SE,55.43,0,0
49,2010,1,3,0,4,NA,NA,NA,63,-7,92,1027,-6,SE,58.56,0.4,0.4
50,2010,1,3,1,4,NA,NA,NA,65,-8,85,1026,-6,SE,61.69,0.5,0.9
51,2010,1,3,2,4,NA,NA,NA,55,-8,92,1026,-7,SE,65.71,0.5,1.4
52,2010,1,3,3,4,NA,NA,NA,65,-8,92,1025,-7,SE,68.84,0.7,2.1
53,2010,1,3,4,4,NA,NA,NA,83,-8,92,1024,-7,SE,72.86,1.2,3.3
54,2010,1,3,5,4,NA,NA,NA,91,-9,92,1024,-8,SE,76.88,0.7,4
55,2010,1,3,6,4,NA,NA,NA,86,-10,85,1024,-8,SE,80.9,1,5
56,2010,1,3,7,4,NA,NA,NA,82,-10,92,1024,-9,SE,84.92,0.7,5.7
57,2010,1,3,8,4,NA,NA,NA,86,-10,92,1024,-9,SE,89.84,0.5,6.2
58,2010,1,3,9,4,NA,NA,NA,78,-11,85,1023,-9,SE,93.86,0.7,6.9
59,2010,1,3,10,4,NA,NA,NA,98,-11,85,1023,-9,SE,97.88,0.4,7.3
60,2010,1,3,11,4,NA,NA,NA,107,-11,85,1022,-9,SE,102.8,0.5,7.8
61,2010,1,3,12,4,NA,NA,NA,90,-11,85,1021,-9,SE,105.93,1.1,8.9
62,2010,1,3,13,4,NA,NA,NA,96,-11,85,1020,-9,SE,111.74,0.5,9.4
63,2010,1,3,14,4,NA,NA,NA,95,-11,85,1020,-9,SE,116.66,0.3,9.7
64,2010,1,3,15,4,NA,NA,NA,86,-11,85,1020,-9,SE,121.58,0.5,10.2
65,2010,1,3,16,4,NA,NA,NA,70,-11,85,1020,-9,SE,124.71,0.2,10.4
66,2010,1,3,17,4,NA,NA,NA,61,-11,85,1020,-9,SE,127.84,0.1,10.5
67,2010,1,3,18,4,NA,NA,NA,53,-11,85,1021,-9,cv,0.89,0.4,10.9
68,2010,1,3,19,4,NA,NA,NA,71,-11,85,1022,-9,cv,1.78,0.3,11.2
69,2010,1,3,20,4,NA,NA,NA,72,-10,92,1022,-9,NW,4.02,0,0
70,2010,1,3,21,4,NA,NA,NA,76,-11,92,1023,-10,NW,7.15,0,0
71,2010,1,3,22,4,NA,NA,NA,73,-11,85,1023,-9,NW,11.17,0,0
72,2010,1,3,23,4,NA,NA,NA,79,-12,92,1023,-11,NW,14.3,0,0
73,2010,1,4,0,4,NA,NA,NA,58,-14,85,1023,-12,NW,16.09,0,0
74,2010,1,4,1,4,NA,NA,NA,25,-16,56,1023,-9,NW,21.9,0,0
75,2010,1,4,2,4,NA,NA,NA,26,-17,56,1024,-10,NW,29.95,0,0
76,2010,1,4,3,4,NA,NA,NA,28,-18,56,1024,-11,NW,39.78,0,0
77,2010,1,4,4,4,NA,NA,NA,26,-19,51,1025,-11,NW,48.72,0,0
78,2010,1,4,5,4,NA,NA,NA,20,-20,51,1026,-12,NW,55.87,0,0
79,2010,1,4,6,4,NA,NA,NA,29,-21,47,1027,-12,NW,64.81,0,0
80,2010,1,4,7,4,NA,NA,NA,26,-21,51,1027,-13,NW,73.75,0,0
81,2010,1,4,8,4,NA,NA,NA,27,-22,46,1028,-13,NW,80.9,0,0
82,2010,1,4,9,4,NA,NA,NA,27,-22,46,1029,-13,NW,90.73,0,0
83,2010,1,4,10,4,NA,NA,NA,25,-22,43,1030,-12,NW,100.56,0,0
84,2010,1,4,11,4,NA,NA,NA,29,-23,39,1031,-12,NW,108.61,0,0
85,2010,1,4,12,4,NA,NA,NA,32,-21,43,1030,-11,NW,117.55,0,0
86,2010,1,4,13,4,NA,NA,NA,28,-20,43,1030,-10,NW,127.38,0,0
87,2010,1,4,14,4,NA,NA,NA,29,-21,40,1030,-10,NW,136.32,0,0
88,2010,1,4,15,4,NA,NA,NA,30,-21,37,1030,-9,NW,145.26,0,0
89,2010,1,4,16,4,NA,NA,NA,30,-21,37,1031,-9,NW,152.41,0,0
90,2010,1,4,17,4,NA,NA,NA,28,-20,47,1032,-11,NW,159.56,0,0
91,2010,1,4,18,4,NA,NA,NA,26,-23,36,1032,-11,NW,165.37,0,0
92,2010,1,4,19,4,NA,NA,NA,31,-21,47,1033,-12,NW,171.18,0,0
93,2010,1,4,20,4,NA,NA,NA,33,-24,36,1034,-12,NW,180.12,0,0
94,2010,1,4,21,4,NA,NA,NA,29,-24,39,1034,-13,NW,187.27,0,0
95,2010,1,4,22,4,NA,NA,NA,31,-24,39,1035,-13,NW,195.32,0,0
96,2010,1,4,23,4,NA,NA,NA,30,-26,38,1035,-15,NW,198.45,0,0
97,2010,1,5,0,4,NA,NA,NA,34,-26,45,1035,-17,NW,201.58,0,0
98,2010,1,5,1,4,NA,NA,NA,27,-26,49,1035,-18,NW,205.6,0,0
99,2010,1,5,2,4,NA,NA,NA,25,-26,53,1035,-19,NW,208.73,0,0
代码展示:
package com.spark
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, SparkSession}
object DataFrameTest {
//需求: 查看每个月的统计数量
def main(args: Array[String]): Unit = {
Logger.getLogger("org").setLevel(Level.ERROR)
//1,创建SparkSession
val spark = SparkSession.builder()
.master("local[6]")
.appName(this.getClass.getSimpleName)
.getOrCreate()
//隐式转换
import spark.implicits._
//2,读取数据集
val sourceDF: DataFrame = spark.read
.option("header",true) //忽略消息头
.csv("E:\\Project\\Spark\\spark-sql\\input\\BeijingPM20100101_20151231.csv")
//sourceDF.show()
//查看DataFrame 的 schema 信息,要意识到 DataFrame 中是有结构信息的,叫做Schema
sourceDF.printSchema()
//3,处理
/**
* 1,选择列
* 2,过滤掉NA的PM记录
* 3,分组 select year,month, count (pm_Dongsi) from ... where pm_Dongsi pm_Dongsi != NA group by year , month
* 4,聚合
*/
println("--------------表结构和表结果的分界线---------------")
// 得出结论
sourceDF.select('year,'month,'PM_Dongsi)
.where('PM_Dongsi =!= "NA")
.groupBy('year,'month)
.count()
.show()
println("---------代码查询和SQL语句查询的分界线--------------")
//创建临时表
sourceDF.createOrReplaceTempView("pm")
//year,month,day,hour,season,PM_Dongsi
val resultDF = spark.sql("select year,month,count(PM_Dongsi) from pm where PM_Dongsi != 'NA' group by year,month")
resultDF.show()
spark.stop()
}
}
总结
1,DataFrame 是一个类似于关系型数据库表的函数式组件
2,DataFrame 一般处理结构化数据和半结构化数据
3,DataFrame 具有数据对象的 Schema 信息
4,可以使用命令式的 API 操作 DataFrame, 同时也可以使用 SQL 操作 DataFrame
5,DataFrame 可以由一个已经存在的集合直接创建, 也可以读取外部的数据源来创建
Dataset 和 DataFrame 的异同
目标
理解 Dataset 和 DataFrame 之间的关系
DataFrame 就是 Dataset
根据前面的内容, 可以得到如下信息
- Dataset 中可以使用列来访问数据, DataFrame 也可以
- Dataset 的执行是优化的, DataFrame 也是
- Dataset 具有命令式 API, 同时也可以使用 SQL 来访问, DataFrame 也可以使用这两种不同的方式访问
所以这件事就比较蹊跷了, 两个这么相近的东西为什么会同时出现在 SparkSQL 中呢?
确实, 这两个组件是同一个东西, DataFrame 是 Dataset 的一种特殊情况, 也就是说 DataFrame 是 Dataset[Row] 的别名
DataFrame 和 Dataset 所表达的语义不同
第一点: DataFrame 表达的含义是一个支持函数式操作的 表, 而 Dataset 表达是是一个类似 RDD 的东西, Dataset 可以处理任何对象
第二点: DataFrame 中所存放的是 Row 对象, 而 Dataset 中可以存放任何类型的对象
val spark: SparkSession = new sql.SparkSession.Builder()
.appName("hello")
.master("local[6]")
.getOrCreate()
import spark.implicits._
//1
val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()
//2
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
1,DataFrame 就是 Dataset[Row]
2,Dataset 的范型可以是任意类型
第三点: DataFrame 的操作方式和 Dataset 是一样的, 但是对于强类型操作而言, 它们处理的类型不同
@Test
def dataFrame3(): Unit ={
Logger.getLogger("org").setLevel(Level.ERROR)
//创建SparkSession
val spark = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[6]")
.getOrCreate()
//隐式转换
import spark.implicits._
val PersonList: Seq[Person] = Seq(Person("zhangsan", 15), Person("lisi", 20))
//TODO DataFrame 是弱类型的
val df: DataFrame = PersonList.toDF()
//根据下标取 因为年龄是int 类型,所以转换为getAS[Int]
df.map( (row:Row) => Row(row.get(0),row.getAs[Int](1) * 2) ) (RowEncoder.apply(df.schema))
.show()
//弱类型编译不安全
df.groupBy("name.school")
//TODO 是强类型的
val ds: Dataset[Person] = PersonList.toDS()
//根据类的类型取
ds.map((person:Person) => Person(person.name,person.age * 2)).show()
//DataSet 代表的操作是,类型安全的,编译时安全
//ds.filter( person => person.school)
}
case class Person(name:String,age:Int)
第四点: DataFrame 只能做到运行时类型检查, Dataset 能做到编译和运行时都有类型检查
- DataFrame 中存放的数据以 Row 表示, 一个 Row 代表一行数据, 这和关系型数据库类似
- DataFrame 在进行 map 等操作的时候, DataFrame 不能直接使用 Person 这样的 Scala 对象, 所以无法做到编译时检查
- Dataset 表示的具体的某一类对象, 例如 Person, 所以再进行 map 等操作的时候, 传入的是具体的某个 Scala 对象, 如果调用错了方法, 编译时就会被检查出来
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
ds.map(person => person.hello)
这行代码明显报错, 无法通过编译
Row 是什么?
Row 对象表示的是一个 行
Row 的操作类似于 Scala 中的 Map 数据类型
@Test
def row(): Unit ={
//1,Row 如何创建 它是什么
//row 配合 schema对象才会有列名
val p = Person("zhangsan", 15)
val row = Row("zhangsan",16)
println(row.schema) //没有类型
//2,如何从Row 中获取数据
row.getString(0)
row.getInt(1)
//3,Row也是样例类
row match {
case Row(name,age) =>println(name,age)
}
}
case class Person(name:String,age:Int)
DataFrame 和 Dataset 之间可以非常简单的相互转换
case class Person(name:String,age:Int)
val spark: SparkSession = new sql.SparkSession.Builder()
.appName("hello")
.master("local[6]")
.getOrCreate()
import spark.implicits._
val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()
val ds_fdf: Dataset[People] = df.as[People]
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
val df_fds: DataFrame = ds.toDF()
总结
1,DataFrame 就是 Dataset, 他们的方式是一样的, 也都支持 API 和 SQL 两种操作方式
2,DataFrame 只能通过表达式的形式, 或者列的形式来访问数据, 只有 Dataset 支持针对于整个对象的操作
3,DataFrame 中的数据表示为 Row, 是一个行的概念