-
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 中可以存放任何类型的对象
@Test def dataframe3():Unit = { val spark = SparkSession.builder() .master("local[6]") .appName("dataframe3") .getOrCreate() import spark.implicits._ val personList = Seq(Person("zhangsan", 15), Person("lisi", 20)) // DataFrame 是弱类型的 val df:DataFrame = personList.toDF() // Dataset 是强类型 val ds:Dataset[Person] = personList.toDS() } case class Person(name: String, age: Int)
- DataFrame 就是 Dataset[Row]
- Dataset 的范型可以是任意类型
-
第三点: DataFrame 的操作方式和 Dataset 是一样的, 但是对于强类型操作而言, 它们处理的类型不同
DataFrame 在进行强类型操作时候,例如map 算子,其所处理的据类型永远是Row df.map((row: Row) => Row(row.get(0), row.getAs[Int](1) * 2))(RowEncoder.apply(df.schema))show()
但是对于
Dataset 来讲,其中是什么类型,它就处理什么类型ds.map((person: Person) => Person(person.name, person.age * 2)).show()
全代码
@Test def dataframe4(): Unit = { val spark = SparkSession.builder() .master("local[6]") .appName("dataframe4") .getOrCreate() import spark.implicits._ val personList = Seq(Person("zhangsan", 15), Person("lisi", 20)) // DataFrame 是弱类型的 val df: DataFrame = personList.toDF() // DataFrame 永远操作Row对象 df.map((row: Row) => Row(row.get(0), row.getAs[Int](1) * 2))(RowEncoder.apply(df.schema)) .show() // Dataset 是强类型 val ds: Dataset[Person] = personList.toDS() // Dataset 里面存放的是什么对象就操作什么对象,这里是 Person ds.map((person: Person) => Person(person.name, person.age * 2)) .show() }
-
第四点: DataFrame 只能做到运行时类型检查, Dataset 能做到编译和运行时都有类型检查
- DataFrame 中存放的数据以 Row 表示, 一个 Row 代表一行数据, 这和关系型数据库类似
- DataFrame 在进行 map 等操作的时候, DataFrame 不能直接使用 Person 这样的 Scala 对象, 所以无法做到编译时检查
- Dataset 表示的具体的某一类对象, 例如 Person, 所以再进行 map 等操作的时候, 传入的是具体的某个 Scala 对象, 如果调用错了方法, 编译时就会被检查出来
// DataFrame所代表的弱类型操作是编译时不安全,运行时类型安全 df.groupBy("name, school") // Dataset所代表的操作是类型安全的,编译时安全的 ds.filter(person => person.school) //这行代码明显报错, 无法通过编译
-
Row 对象的操作
Row 是什么?
Row 对象表示的是一个 行
Row 的操作类似于 Scala 中的 Map 数据类型
@Test def row(): Unit = { // 1. Row 如何创建,它是什么 // row 对象必须配合 Schema 对象才会有列名 val p = Person("zhangsan", 15) val row =Row("zhangsan", 15) // 2. 如何从 Row 中获取数据 row.getString(0) row.getInt(1) // 3. Row 也是样例类 row match { caseRow(name, age) =>println(name, age) } } case class Person(name: String, age: Int)
-
DataFrame 和 Dataset 之间可以非常简单的相互转换
case class Person(name:String,age:Int) val spark = SparkSession.builder() .appName("df_ds") .master("local[6]") .getOrCreate() import spark.implicits._ val df: DataFrame = Seq(Person("zhangsan", 15), Person("lisi", 15)).toDF() val ds_fdf: Dataset[Person] = df.as[Person] val ds: Dataset[Person] = Seq(Person("zhangsan", 15), Person("lisi", 15)).toDS() val df_fds: DataFrame = ds.toDF()
总结
- DataFrame 就是 Dataset, 他们的方式是一样的, 也都支持 API 和 SQL 两种操作方式
- DataFrame 只能通过表达式的形式, 或者列的形式来访问数据, 只有 Dataset 支持针对于整个对象的操作
- DataFrame 中的数据表示为 Row, 是一个行的概念
-