spark中dataframe的部分操作与作用

本文介绍了Spark DataFrame的生成、Action操作,如show、collect、foreach、describe等,并详细讲解了条件查询与join操作,包括where、filter、select、groupBy、join、agg等方法的使用。

spark中dataframe的部分操作与作用

Spark SQL中的DataFrame类似于一张关系型数据表。在关系型数据库中对单表或进行的查询操作,在DataFrame中都可以通过调用其API接口来实现,可参考官方文档:链接

一.生成dataframe

sparksql可以从其他RDD对象,parquet文件,json文件,hive表,以及通过jdbc连接到其他关系型数据库作为数据源来生成dataframe对象,此处以mysql为例:

	连接代码:
	
	import org.apache.spark.sql.SparkSession
	object sparksqlDataframe {
	  def main(args: Array[String]): Unit = {
	    val spark = SparkSession.builder().appName("sparkSql").master("local[2]").getOrCreate()
	    val jdbcDF = spark.read
	      .format("jdbc")
	      .option("url","jdbc:mysql://localhost:3306/app_food?useUnicode=true&characterEncoding=utf-8")
	      .option("dbtable","tb_food")
	      .option("user","root")
	      .option("password","123456")
	      .load()

二.dataframe的Action操作

  1. show(numRows:Int,truncate:Boolean/Int>): Unit
    show()可直接使用,也可添加int与boolean值,默认展示前20行,一行中最多显示前20个字符,超出以省略号代替,所有表格向右对齐
  • 添加单个int值,表示展示int值行的数据,所有表格向右对齐
    • 添加boolean,是否截断超过20个字符的字符串,默认为true,所有表格向右对齐

    • 添加int与boolean,展示int值行的数据,boolean值为true时,截断超过20个字符的字符串,所有表格向右对齐.

    • 添加两个int时,第一个int为展示多少行数据,第二个int大于0时,截断int值个字符的字符串,所有表格向右对齐。
      使用:

         jdbcDF.show(3, false)         
      
  1. collect
  • collect() : Array[T] 返回包含该数据集中所有行的数组(Array类型)。

    使用:

     	val array = jdbcDF.collect()
    
  • collectAsList() : List[T]

返回包含此数据集中的所有行的一个Java列表(List)。大数据集下执行此操作,会导致OutOfMemoryError,进而使程序崩溃。
使用:

		val list = bcDF.collectAsList()    
  • describe(cols: String): DataFrame*

    计算一个或多个数字和字符串列的统计信息,包括计数,平均值,stddev(标准差),最小值和最大值。 如果没有给出列,则此函数将计算所有数字或字符串列的统计信息。
    使用:

        jdbcDF.describe("FOOD_ID","FOOD_NAME","PREPARE").show()
    
  • foreach
  • foreach(func: ForeachFunction[T]): Unit (特定于Java)在此数据集的每个元素上运行func。

  • foreach(f: (T) ⇒ Unit): Unit 将函数f应用于所有行

  • 其他
  • head() / first(): T 返回第一行

  • head(n: Int): Array[T] / take(n: Int): Array[T] 返回前n行

  • takeAsList(n: Int): List[T] 作为一个List返回前n行
    take操作会将数据移动到应用程序的driver中,n过大会导致OutOfMemoryError,使进程崩溃。

  • count(): Long 返回数据集有多少行

三.dataframe的条件查询与join操作

  • where 条件相关
  • where(conditionExpr: String): Dataset[T] / filter(conditionExpr: String): Dataset[T]
    使用给定的sql表达式过滤行
    用法:

     	jdbcDF.where("FOOD_ID > 20 AND FOOD_NAME LIKE '%菜%'").show()
    
  • where(condition: Column): Dataset[T] / filter(condition: Column): Dataset[T] 过滤器别名,效果同上。
    用法:

    peopleDs.where($"age" > 15)
    peopleDs.filter($"age" > 15)
    
  • 查询指定字段
  • select(col: String, cols: String*): DataFrame 选择一组列,只能在使用列名选择现有的列
    用法:

    	ds.select("colA", "colB")
    	ds.select($"colA", $"colB")
    
  • select(cols: Column*): DataFrame 选择一组基于列的表达式。
    用法:

       ds.select($"colA", $"colB" + 1)
       jdbcDF.select(jdbcDF( "id" ), jdbcDF( "id") + 1 )
    
  • selectExpr(exprs: String*): DataFrame 可对指定字段调用UDF函数,指定别名等,可接受sql表达式
    用法:

    	jdbcDF .selectExpr("id" , "c3 as time" , "round(c4)" )
    	ds.selectExpr("colA", "colB as newName", "abs(colC)")
    	ds.select(expr("colA"), expr("colB as newName"), expr("abs(colC)"))
    
    • apply(colName: String): Column / col(colName: String): Column 根据列名称选择列,并将其作为列返回。
      用法:

      val x = jdbcDF.col("FOOD_ID")
      val y = jdbcDF.apply("FOOD_NAME")   
      
    • drop(col: Column): DataFrame / drop(colNames: String*): DataFrame 返回一个新的DataFrame对象,其中不包含去除的字段,一次只能去除一个字段。 如果不包含列名,则为空。
      用法:

      jdbcDF.drop("id")
      jdbcDF.drop(jdbcDF("id"))
      
  • limit :limit(n: Int): Dataset[T]

    limit方法获取指定DataFrame的前n行记录,得到一个新的DataFrame对象。和take与head不同的是,limit方法不是Action操作,返回一个dataset,而其他返回的array

  • 排序
  • orderBy(sortExprs: Column*): Dataset[T] / sort(sortExprs: Column*): Dataset[T] 返回一个按照某列升序或者降序的dataset
    用法:

       	ds.sort($"col1", $"col2".desc)
       	jdbcDF.orderBy(jdbcDF("c4").desc)
    
*    *sort(sortCol: String, sortCols: String\*): Dataset[T] /  orderBy(sortCol: String, sortCols: String\*): Dataset[T] *    效果相同,此处可直接输入列名
用法:
				
			ds.sort("sortcol")
			ds.sort($"sortcol")
			ds.sort($"sortcol".asc)
  • groupby:
    • groupBy(col1: String, cols: String*): RelationalGroupedDataset 输入string类型字段名

    • defgroupBy(cols: Column*): RelationalGroupedDataset 输入column类型的对象,根据指定的列对其groupby,方便后续处理
      用法:

       jdbcDF.groupBy("c1" )
       jdbcDF.groupBy( jdbcDF( "c1"))
       ds.groupBy($"department").avg()
      
  • distinct
    • distinct:distinct(): Dataset[T]:返回一个仅包含该数据集中唯一行的新数据集。 这是dropDuplicates的别名。
      用法:

       jdbcDF.distinct()
      
    • dropDuplicates(colNames: Array[String]): Dataset[T] / dropDuplicates(col1: String, cols: String): Dataset[T]* 输入string类型字段名or输入column类型的对象,返回一个不重复的新数据集
      用法:

      jdbcDF.dropDuplicates(Seq("c1"))
      
  • 聚合
    • agg(expr: Column, exprs: Column*): DataFrame 聚集在没有组的整个数据集上。一般与groupby方法配合使用
      用法:

        	jdbcDF.agg("id" -> "max", "c4" -> "sum") //求id中的最大值,c4的和
        	ds.agg(max($"age"), avg($"salary"))
        	ds.groupBy().agg(max($"age"), avg($"salary"))
      
  • UNION
    • union(other: Dataset[T]): Dataset[T] 返回一个新的数据集,该数据集包含此数据集和另一个数据集中的行并集。这等效于SQL中的UNION ALL。 要执行SQL样式的集合并集(可对元素进行重复数据删除),请使用此函数,后跟一个distinct。同样作为SQL的标准,此函数按位置(而不是名称)解析列。
      用法:

       jdbcDF.unionALL(jdbcDF.limit(1))
      
  • join
    • join(right: Dataset[_]): DataFrame 加入另一个DataFrame。表现为INNER JOIN,并且需要后续的join谓词。
      用法:

       joinDF1.join(joinDF2)
      
    • join(right: Dataset[_], usingColumn: String): DataFrame 使用给定的列与另一个DataFrame内部相等联接。与其他联接函数不同,联接列将仅在输出中出现一次,即类似于SQL的JOIN USING语法。
      用法:

        df1.join(df2, "user_id")
        joinDF1.join(joinDF2, "id")  
      
    • join(right: Dataset[_], usingColumns: Seq[String]): DataFrame 也可以给定多个列来join,效果同上
      用法:

        df1.join(df2, Seq("user_id", "user_name"))
        joinDF1.join(joinDF2, Seq("id", "name"))
      
  • join(right: Dataset[_], usingColumns: Seq[String], joinType: String): DataFrame 指定join类型并用给定的join表达式与另一个DataFrame连接。joinType包括:inner, cross, outer, full, full_outer, left, left_outer, right, right_outer, left_semi, left_anti.
    用法:

       //scala
       import org.apache.spark.sql.functions._
       df1.join(df2, $"df1Key" === $"df2Key", "outer")
       joinDF1.join(joinDF2, Seq("id", "name"), "inner")
       // Java:
       import static org.apache.spark.sql.functions.*;
       df1.join(df2, col("df1Key").equalTo(col("df2Key")), "outer");
    
  • join(right: Dataset[_], joinExprs: Column): DataFrame 使用传入的column类型进行join
    用法:

      // The following two are equivalent:
      df1.join(df2, $"df1Key" === $"df2Key")
      df1.join(df2).where($"df1Key" === $"df2Key")
    
  • 获取指定字段统计信息
    • stat
      stat: DataFrameStatFunctions,stat方法可以用于计算指定字段或指定字段之间的统计信息,比如方差,协方差等。这个方法返回一个DataFramesStatFunctions类型对象。
      用法:

       jdbcDF.stat.freqItems(Seq ("c1") , 0.3) //统计该字段值出现频率在30%以上的内容
       ds.stat.freqItems(Seq("a"))//在名称为“a”的列中查找出现最频繁的项。
      
  • 交并集
    • 交集 获取两个DataFrame中共有的记录,等同于SQL中的INTERSECT。
      intersect(other: Dataset[T]): Dataset[T]
      用法:

        jdbcDF.intersect(jdbcDF.limit(1))
      
    • 获取一个DataFrame中有另一个DataFrame中没有的记录
      except(other: Dataset[T]): Dataset[T]
      用法:

        jdbcDF.except(jdbcDF.limit(1))
      
  • 操作字段名
    • 重命名dataframe的对应字段名
      withColumnRenamed(existingName: String, newName: String): DataFrame如果指定的字段名不存在,不进行任何操作。
      用法:

       jdbcDF.withColumnRenamed( "id" , "idx" )
      
    • withColumn(colName: String, col: Column): DataFrame 根据指定colName往DataFrame中新增一列,如果colName已存在,则会覆盖当前列。
      用法:

        jdbcDF.withColumn("id2", jdbcDF("id")) 
      
<think>首先,用户的问题是:“我想理解SparkDataFrame和Dataset的区别 请问Apache Spark DataFrame vs Dataset difference”。我需要基于提供的引用信息来回答这个问题。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式使用$$...$$格式。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户可见层: - 回答结构清晰,逐步解决问题。 - 尽量保证回答真实可靠。 参考引用: - [^1]: 讨论了Spark DataFrame和Dataset的发布,以及比较它们的必要性和内容。 - [^2]: Dataset是SparkSQL中的一种数据结构,强类型,有指定schema,是对DataFrame API的扩展。提供了类型安全和面向对象的编程接口。 - [^3]: Spark SQL引入了SchemaRDD(后来是DataFrame和Dataset),支持结构化数据。DataFrame和Dataset是Spark 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、Python和R中可用。 - Dataset:主要在Scala和Java中可用,因为Python和R是动态类型语言,不支持编译时类型安全。 4. **性能**: - 两者共享相同的优化和执行引擎(Tungsten引擎),因此在性能上相似。但Dataset的强类型特性可能在某些场景下提供更好的优化[^3]。 5. **创建方式**: - DataFrame:可以从RDD、Hive表、数据源等创建。 - Dataset:可以通过将DataFrame转换为Dataset(使用as[T]方法)或直接从类型化数据创建。 从引用[^2]:Dataset是对DataFrame API的扩展,提供类型安全和面向对象的编程接口。引用[^3]提到两者共享相同的执行引擎。 结构回答: 1. **介绍**:简要介绍DataFrame和Dataset。 2. **主要区别**:列出关键区别点。 3. **代码示例**:如果可能,展示简单代码(但用户提供的是Scala代码,我可以用类似格式)。 4. **总结**:何时使用哪个。 5. **相关问题**:生成相关问题。 由于用户要求中文回答,我将用中文。 生成相关问题:基于主题,生成2-3个相关问题。 最后,添加引用标识。</think>### Apache SparkDataFrameDataset的区别 在Apache Spark中,DataFrame和Dataset都是用于处理结构化数据的核心抽象,但它们在设计、类型安全性和编程接口上存在关键差异。以下我将基于Spark官方文档和相关资料,逐步解释它们的区别,帮助您清晰理解。核心区别在于:**DataFrame是Dataset的非类型安全(schema-on-read)版本,而Dataset是DataFrame的类型安全(schema-on-write)扩展**,提供编译时类型检查[^2][^3]。 #### 1. **基本定义** - **DataFrame**: - 表示分布式数据集合,以命名列(schema)组织数据,类似于关系数据库中的表或Pandas DataFrame。 - 非类型安全:schema在运行时检查,操作错误(如类型不匹配)在运行时抛出异常。 - 支持多语言:Scala、Java、Python和R[^3]。 - 示例:从CSV读取数据时,schema在读取时推断。 ```scala val df = spark.read.option("header", true).csv("data.csv") // schema在运行时确定 ``` - **Dataset**: - 是DataFrame的强类型扩展,要求显式定义数据类型(例如使用Scala的case类)。 - 类型安全:schema在编译时检查,操作错误(如字段类型错误)在编译期捕获。 - 主要支持静态类型语言:Scala和Java(Python和R因动态类型限制,支持有限)[^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 | 主要Scala和Java | | **性能优化** | 共享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) // 编译时检查pm和year类型 ``` - **性能考虑**: - 两者底层共享相同的优化引擎(Catalyst优化器和Tungsten执行引擎),因此性能差异不大。但在大型数据集上,Dataset的强类型特性可能减少序列化/反序列化开销,因为Encoder直接处理类型化数据[^3]。 - 实际测试中,Dataset在复杂转换(如用户自定义函数)中可能更快,但DataFrame在简单查询中更高效[^2]。 #### 4. **何时使用哪个?** - **使用DataFrame当**: - 需要快速数据探索或SQL-like查询。 - 跨语言工作(如Python或R)。 - 不需要编译时类型检查。 - **使用Dataset当**: - 开发Scala/Java应用,需要高可靠性和类型安全。 - 处理复杂业务逻辑,涉及自定义对象和函数式操作。 - 优化运行时性能(减少类型错误导致的失败)[^2][^3]。 总结:DataFrame和Dataset在Spark中是互补的。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、DataFrame、Dataset。Dataset是SparkSQL中的一种数据结构,它是强类型的,包含指定的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、付费专栏及课程。

余额充值