Spark中DataFrame与DataSet的使用

本文详述了Spark中的DataFrame和DataSet的使用,包括DataFrame的创建、从RDD转换、反射与编程方式的操作,以及DataSet的转换和其静态类型与运行时类型安全的特性。在字段名称和类型已知的情况下,推荐使用反射方式创建DataFrame。文中还通过实例展示了如何根据姓名排序和查询邮箱等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:本文主要介绍了DataFrame、DataSet常见API的使用

1、DataFrame的使用

object DataFrameApp {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().master("local[2]").getOrCreate()
​
    //将json转为DataFrame
    val peopleDF = spark.read.format("json").load("file:///F:/people.json") 
   //hdfs://hadoop:8020/input/wc/people.json
    peopleDF.printSchema()
    //默认输出前20条记录
    peopleDF.show()
​
    //查询某列数据
    peopleDF.select("name", "age").show()
    //查询列数据并进行计算
    peopleDF.select(peopleDF.col("name"), (peopleDF.col("age") + 9).as("newAge"))
​
    //条件查询
    peopleDF.filter(peopleDF.col("age") > 19).show()
    peopleDF.where(peopleDF.col("age") > 19).show()
​
    //分组与聚合操作 select age,count(1) from table group by age
    peopleDF.groupBy("age").count().show()
    spark.stop()
  }
}

2、RDD与DataFrame的互操作

(1)反射方式

object DataFrameRDDApp {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("DataFrameRDDApp").master("local[2]").getOrCreate()
    //RDD转DataFrame
    val rdd = spark.sparkContext.textFile("file:///F:/people.txt")
    //toDF需要导入隐式转换
    import spark.implicits._
    val peopleDF = rdd.map(_.split(",")).map(line => people(line(0).toInt, line(1).toString, line(2).toInt)).toDF()
    peopleDF.printSchema()
    peopleDF.show()
    peopleDF.filter(peopleDF.col("age")<25).show()
    //创建临时视图 使用sql操作数据库
    peopleDF.createOrReplaceTempView("peopleTmpView")
    spark.sql("select p.id ID,p.name USERNAME,p.age AGE from  peopleTmpView p WHERE name LIKE '%an%'").show()
    spark.stop()
  }
  case class people(id: Int, name: String, age: Int)
}
​

(2)编程方式

使用情形:

  • When case classes cannot be defined ahead of time (for example, the structure of records is encoded in a string, or a text dataset will be parsed and fields will be projected differently for different users).

创建DataFrame步骤:

  • Create an RDD of Rows from the original RDD;
  • Create the schema represented by a StructType matching the structure of Rows in the RDD created in Step 1.
  • Apply the schema to the RDD of Rows via createDataFrame method provided by SparkSession.

代码:

object DataFrameRDDApp {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("DataFrameRDDApp").master("local[2]").getOrCreate()
    //RDD转DataFrame
    val rdd = spark.sparkContext.textFile("file:///F:/people.txt")
    val peopleRDD = rdd.map(_.split(",")).map(line => Row(line(0).toInt, line(1).toString, line(2).toInt))
    val structType = StructType(Array(StructField("id", IntegerType, true), StructField("name", StringType, true), StructField("age", IntegerType, true)))
    val peopleDF = spark.createDataFrame(peopleRDD, structType)
    peopleDF.printSchema()
    peopleDF.show()
    peopleDF.filter(peopleDF.col("age") < 25).show()
    //创建临时视图 使用sql操作数据库
    peopleDF.createOrReplaceTempView("peopleTmpView")
    spark.sql("select p.id ID,p.name username,p.age AGE from  peopleTmpView p WHERE name LIKE '%li%'").show()
    spark.stop()
  }
}

模糊查询结果:

选型说明:如果字段名称、类型可知,优先选择反射方式

(3)DataFrame其他API

object DataFrameCase {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("DataFrameCase ").master("local[2]").getOrCreate()
    //RDD转DataFrame
    val rdd = spark.sparkContext.textFile("file:///F:/student.data")
    import spark.implicits._
    val studentDF = rdd.map(_.split("\\|")).map(line => student(line(0).toInt, line(1).toString, line(2).toString, line(3).toString)).toDF()
    studentDF.first()
    studentDF.printSchema()
    studentDF.show(30,false)
    studentDF.filter("name='' OR name='null'")
    //查找以M开头的学生
    studentDF.filter("SUBSTR(name,0,1)='E'").show()
    studentDF.sort(studentDF("name")).show()
    studentDF.sort("name","email").show()
    studentDF.sort(studentDF("name").desc).show()
    studentDF.select(studentDF("name").as("studentName")).show()
    //默认内连接 需要使用三个=
    val newStudentDF = rdd.map(_.split("\\|")).map(line => student(line(0).toInt, line(1).toString, line(2).toString, line(3).toString)).toDF()
    studentDF.join(newStudentDF,studentDF("id")=!=newStudentDF("id"),"right").show(500,false)
    studentDF.take(10).foreach(println)
    studentDF.first()
    studentDF.head(5)
    spark.stop()
  }
  case class student(id:Int,name:String,phone:String,email:String)
}

根据姓名排序结果:

3、DataSet的使用

(1)DataFrame转DataSet

object DataSetApp {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("DataSetApp").master("local[2]").getOrCreate()
    val path = "file:///F:/student.csv"
    //解析csv文件 外部数据源功能
    val df = spark.read.option("header", "true").option("inferSchema", true).csv(path)
    df.show()
    //隐式转换 df转ds
    import spark.implicits._
    val ds = df.as[student]
    ds.map(line=>line.email).show()
    spark.stop()
  }
  case class student(id: Int, studentName: String, phone: String, email: String)
}

DataSet的查询邮箱结果:

(2)DataSet静态类型(Static-typing)和运行时类型安全(runtime type-safety)

说明:

  • spark.sql("seletc * from emp e join dept d on e.deptno=d.deptno").show()编译可以通过,执行sql时报错
  • studentDF.seletc(studentDF("name").as("studentName")).show()中编译将不能通过
  • ds.map(line=>line.emails).show()中编译将不能通过,Dataset不管是哪种状态都是安全的,即静态类型和运行时类型安全,因为Dataset两种状态都能把错误抛出来。
<think>首先,用户的问题是:“我想理解SparkDataFrameDataset的区别 请问Apache Spark DataFrame vs Dataset difference”。我需要基于提供的引用信息来回答这个问题。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户可见层: - 回答结构清晰,逐步解决问题。 - 尽量保证回答真实可靠。 参考引用: - [^1]: 讨论了Spark DataFrameDataset的发布,以及比较它们的必要性内容。 - [^2]: DatasetSparkSQL中的一种数据结构,强类型,有指定schema,是对DataFrame API的扩展。提供了类型安全面向对象的编程接口。 - [^3]: Spark SQL引入了SchemaRDD(后来是DataFrameDataset),支持结构化数据。DataFrameDatasetSpark SQL的API,共享相同的执行引擎。 - [^4]: 展示了使用Dataset的代码示例,包括读取数据、过滤类型转换。 关键点: - DataFrame:是Spark SQL的一个抽象,表示分布式数据集合,具有命名列(schema)。类似于关系数据库中的表或Pandas DataFrame。它是非类型安全的(schema在运行时检查)。 - Dataset:从Spark 1.6开始引入,是DataFrame的扩展。Dataset是强类型的,提供编译时类型安全。它允许使用Scala或Java的case类来定义类型。 区别: 1. **类型安全**: - DataFrame:是非类型安全的。操作在运行时检查类型错误。 - Dataset:是强类型的,编译时检查类型错误。这意味着在编译时就能捕获类型不匹配的错误[^2]。 2. **编程接口**: - DataFrame:提供类似于SQL的声明式API,支持DSL(领域特定语言)。 - Dataset:提供面向对象的编程接口,可以使用lambda函数函数式编程[^2][^3]。 3. **语言支持**: - DataFrame:在Scala、Java、PythonR中可用。 - Dataset:主要在ScalaJava中可用,因为PythonR是动态类型语言,不支持编译时类型安全。 4. **性能**: - 两者共享相同的优化执行引擎(Tungsten引擎),因此在性能上相似。但Dataset的强类型特性可能在某些场景下提供更好的优化[^3]。 5. **创建方式**: - DataFrame:可以从RDD、Hive表、数据源等创建。 - Dataset:可以通过将DataFrame转换为Dataset使用as[T]方法)或直接从类型化数据创建。 从引用[^2]:Dataset是对DataFrame API的扩展,提供类型安全面向对象的编程接口。引用[^3]提到两者共享相同的执行引擎。 结构回答: 1. **介绍**:简要介绍DataFrameDataset。 2. **主要区别**:列出关键区别点。 3. **代码示例**:如果可能,展示简单代码(但用户提供的是Scala代码,我可以用类似格式)。 4. **总结**:何时使用哪个。 5. **相关问题**:生成相关问题。 由于用户要求中文回答,我将用中文。 生成相关问题:基于主题,生成2-3个相关问题。 最后,添加引用标识。</think>### Apache SparkDataFrameDataset的区别 在Apache Spark中,DataFrameDataset都是用于处理结构化数据的核心抽象,但它们在设计、类型安全性编程接口上存在关键差异。以下我将基于Spark官方文档相关资料,逐步解释它们的区别,帮助您清晰理解。核心区别在于:**DataFrameDataset的非类型安全(schema-on-read)版本,而DatasetDataFrame的类型安全(schema-on-write)扩展**,提供编译时类型检查[^2][^3]。 #### 1. **基本定义** - **DataFrame**: - 表示分布式数据集合,以命名列(schema)组织数据,类似于关系数据库中的表或Pandas DataFrame。 - 非类型安全:schema在运行时检查,操作错误(如类型不匹配)在运行时抛出异常。 - 支持多语言:Scala、Java、PythonR[^3]。 - 示例:从CSV读取数据时,schema在读取时推断。 ```scala val df = spark.read.option("header", true).csv("data.csv") // schema在运行时确定 ``` - **Dataset**: - 是DataFrame的强类型扩展,要求显式定义数据类型(例如使用Scala的case类)。 - 类型安全:schema在编译时检查,操作错误(如字段类型错误)在编译期捕获。 - 主要支持静态类型语言:ScalaJava(PythonR因动态类型限制,支持有限)[^2]。 - 示例:定义case类后,DataFrame可转换为Dataset。 ```scala case class PMData(pm: Double, year: Int) val ds = df.as[PMData] // 编译时类型检查 ``` #### 2. **关键区别对比** 下表总结主要差异(基于Spark 2.0+版本): | 特性 | DataFrame | Dataset | |---------------------|----------------------------------------|----------------------------------------| | **类型安全** | 非类型安全(运行时检查schema) | 强类型安全(编译时检查schema)[^2] | | **编程接口** | 声明式API(类似SQL的DSL) | 面向对象API(支持lambda函数式编程)[^2] | | **语言支持** | Scala、Java、Python、R | 主要ScalaJava | | **性能优化** | 共享Tungsten引擎,优化执行计划相同 | 相同,但强类型可能减少运行时开销[^3] | | **错误处理** | 运行时异常(如ClassCastException) | 编译时错误(如类型不匹配) | | **创建方式** | 从RDD、文件或Hive表创建 | 从DataFrame转换(`as[T]`)或直接创建 | #### 3. **详细解释核心区别** - **类型安全(Type Safety)**: - DataFrame是非类型安全的,因为它使用`Row`对象表示数据,字段类型在运行时解析。例如,在过滤操作中,如果列名错误或类型不匹配,错误只在执行时暴露[^2]。 ```scala df.filter("year > 2015") // 运行时检查"year"是否存在 ``` - Dataset是强类型的,每个元素是特定类型(如自定义case类)。编译时就能捕获错误,提高代码可靠性。引用[^4]中的示例展示了类型转换: ```scala val ds = sourceDF.select( when('PM_Dongsi === "NA", Double.NaN) // 编译时类型检查 .otherwise('PM_Dongsi.cast(DoubleType)) .as("pm") ).as[PMData] // 转换为Dataset,编译时验证schema ``` 这减少了生产环境中的bug风险[^2][^4]。 - **编程模型**: - DataFrame使用声明式API,适合SQL-like操作(如`select`、`filter`),便于数据工程师处理ETL任务。 - Dataset提供面向对象风格,支持方法链lambda表达式,更适合开发复杂业务逻辑的应用程序(如使用`map`或`reduce`)[^2][^3]。 ```scala // Dataset示例:类型安全的操作 ds.filter(_.pm > 50).map(_.year) // 编译时检查pmyear类型 ``` - **性能考虑**: - 两者底层共享相同的优化引擎(Catalyst优化器Tungsten执行引擎),因此性能差异不大。但在大型数据集上,Dataset的强类型特性可能减少序列化/反序列化开销,因为Encoder直接处理类型化数据[^3]。 - 实际测试中,Dataset在复杂转换(如用户自定义函数)中可能更快,但DataFrame在简单查询中更高效[^2]。 #### 4. **何时使用哪个?** - **使用DataFrame当**: - 需要快速数据探索或SQL-like查询。 - 跨语言工作(如Python或R)。 - 不需要编译时类型检查。 - **使用Dataset当**: - 开发Scala/Java应用,需要高可靠性类型安全。 - 处理复杂业务逻辑,涉及自定义对象函数式操作。 - 优化运行时性能(减少类型错误导致的失败)[^2][^3]。 总结:DataFrameDatasetSpark中是互补的。Dataset本质上是DataFrame的类型安全封装,两者在大多数场景下可互换,但Dataset的强类型特性使其更适合企业级应用开发。Spark官方推荐:**优先使用DataFrame进行通用数据处理,在需要类型安全时切换到Dataset**[^2][^3]。 #### 相关问题 1. 如何在Spark中将DataFrame转换为Dataset? 2. Dataset的强类型特性如何影响Spark作业的性能? 3. 在Python中是否支持Dataset?如果不支持,有哪些替代方案? [^1]: Recently, there are two new data abstractions released dataframe and datasets in apache spark... [^2]: Spark性能优化 -- > Spark SQL、DataFrameDatasetDatasetSparkSQL中的一种数据结构,它是强类型的,包含指定的schema... [^3]: [Spark进阶]-- Spark Dataframe操作。Spark SQL is a component on top of Spark Core that introduces a new data abstraction called SchemaRDD... [^4]: 部分数据展示... 将PM_Dongsi的的数据类型替换成double类型,将这一列的空值替换成double下的nan...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值