SparkSQL02

本文深入探讨了Apache Spark中的DataFrame和Dataset概念,包括它们的起源、特性、与RDD的区别,以及如何在Spark SQL中使用这些数据结构进行高效的数据处理和分析。文章还提供了丰富的代码示例,展示了DataFrame和Dataset的创建、操作和转换方法。

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

一、DataFrame

1、其他语言也有DataFrame

ptyhon pandas R

局限性:单机

2、Datasets(1.6版本之后) and DataFrames

DataSet:

(1)有利于RDD基于SparkSQL,优化执行引擎

(2)构建JVM对象使用函数式算子

(3)DataSet的API面向Scala、Java

DataFrames:

(1)是一个DataSet

(2)被组织成列,可以理解为一张表

(3)Java,Scala,python,R

(4)DataSet的一个类型

3、发展历史

SchemaRDD<1.3

===>

DataFrame<1.6

====>

DataSet

4、DataFrame  vs RDD

相同点:

(1)都是分布式的数据集

(2)都是collection

(3)都有数据处理等API接口

不同点:

(1)DF有schema

(2)DF比RDD更详细,性能更好

二、DataFrame代码实现

1、入口点SparkSession

val spark = SparkSession
  .builder()
  .appName("Spark SQL basic example")
  .config("spark.some.config.option", "some-value")
  .getOrCreate()

2、版本不同入口点的历史

Spark SQL入口点

<2:SQLContext                    HiveContext

>=2:SparkSession

3、DF读

(1)读:官网

val df1 = spark.read.json("examples/src/main/resources/people.json")

=format().load(path)

所以标准写法:

val df = spark.read.format("json").load("")

打印schema信息:

(2)显示DF内容

/**
 * Displays the top 20 rows of Dataset in a tabular form. Strings more than 20 characters
 * will be truncated, and all cells will be aligned right.
 *
 * @group action
 * @since 1.6.0
 */
def show(): Unit = show(20)

(3)DF的select用法,需要哪一列就取哪一列

单个参数:

多个参数:

第二种写法:

df.select('name).show()

(4)其他方法:

// This import is needed to use the $-notation
import spark.implicits._
// Print the schema in a tree format
df.printSchema()
// root
// |-- age: long (nullable = true)
// |-- name: string (nullable = true)

// Select only the "name" column
df.select("name").show()
// +-------+
// |   name|
// +-------+
// |Michael|
// |   Andy|
// | Justin|
// +-------+

// Select everybody, but increment the age by 1
df.select($"name", $"age" + 1).show()
// +-------+---------+
// |   name|(age + 1)|
// +-------+---------+
// |Michael|     null|
// |   Andy|       31|
// | Justin|       20|
// +-------+---------+

// Select people older than 21
df.filter($"age" > 21).show()
// +---+----+
// |age|name|
// +---+----+
// | 30|Andy|
// +---+----+

// Count people by age
df.groupBy("age").count().show()
// +----+-----+
// | age|count|
// +----+-----+
// |  19|    1|
// |null|    1|
// |  30|    1|
// +----+-----+

4、DF创建一个视图,便于直接用SQL操作

df.createOrReplaceTempView("people")
val sqlSF = spark.sql("select * from people")
sqlSF.show

三、SparkSQL与RDD交互(重要)

1、RDD转换成数据集的方法

Spark SQL支持将现有的RDDs转换为数据集的两种不同方法。第一种方法使用反射来推断包含特定类型对象的RDD的架构。

创建数据集的第二个方法是通过一个编程接口,该接口允许您构造一个模式,然后将其应用到现有的RDD。

2、反射Inferring the Schema Using Reflection

info.txt ==> DataFrame

package com.HBinz.spark.Spark.sql.day02

import org.apache.spark.sql.SparkSession

object SparkSQLApp {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName("SparkSQLApp")
      .master("local[2]")
      //.enableHiveSupport()    //支持Hive
      .getOrCreate()
      //通过调用spark的sparkcontext方法来将文本文件转换成RDD
    val info = spark.sparkContext.textFile("file:///C:\\Users\\dell\\Desktop\\infos.txt")
    //println(info.count())
    //info.collect().foreach(println)
    //TODO...
    /*思路
        RDD转成DataFrame
        3行数据===>3条记录
     */
    //读取每一行记录采用逗号分割,分割出来的是数组,然后把每一行的数组给他作用到Info类里面去
    import spark.implicits._
    val infoDF = info.map(_.split(",")).map(x=>Info(x(0).toInt,x(1),x(2).toInt)).toDF()
    infoDF.show()
    //SQL的方式展示infoDF
    infoDF.createOrReplaceTempView("T_info")
    spark.sql("select * from T_info").show()

    spark.stop()
  }
  //这个类有RDD信息,我们要把RDD套进这个Info里面,将schame信息定义在case class里面
  case class Info(id:Int,name:String,age:Int)
}

3、旧版本的转DF,翻开官网1.6.1版本

(1)只支持21字段,超过的限制就要使用自定义class来实现Product

总结:

schame信息通过case class来指定的。

4、

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), a DataFrame can be created programmatically with three steps.

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.

如果不能提前定义case classes(例如,记录的结构编码在一个字符串中,或者一个文本数据集将被解析,字段将因用户的不同而不同而不同),可以分三个步骤以编程方式创建数据框架。

1、从原来的RDD创建行的RDD;

2、创建一个schema,这个schema使用;

StructType:1到n个StructField

3、通过由SparkSession提供的createDataFrame方法将模式应用到行的RDD。

官网的实现代码:

val info = spark.sparkContext.textFile("file:///C:\\Users\\dell\\Desktop\\infos.txt")
//println(info.count())
//info.collect().foreach(println)
val rowRDD = info
  .map(_.split(","))
  .map(attributes => Row(attributes(0), attributes(1), attributes(2)))

val schemaString = "id name age"
//将row字段拆出来通过map方法遍历行的名字,类型,和是否允许空值
val fields = schemaString.split(" ")
  .map(fieldName => StructField(fieldName, StringType, nullable = true))
val schema = StructType(fields)

val infoDF2 = spark.createDataFrame(rowRDD, schema)
infoDF2.show()

4、优化

由于StringType的类型不可知的,像官网这样操作,字段类型不可控。因此需要优化:

//生产上的做法:
//转行RDD
//.trim(),去两端空格
val info = spark.sparkContext.textFile("file:///C:\\Users\\dell\\Desktop\\infos.txt")
  .map(_.split(",")).map(x => Row(x(0).trim.toInt,x(1),x(2).trim.toInt))
//自定义StructType,每个StructField自定义类型,类型要跟行RDD每一列的类型相匹配。
val infoFields = StructType(
  Array(
    StructField("id",IntegerType,true),
    StructField("name",StringType,true),
    StructField("age",IntegerType,true)
  )
)
val infoDF2 = spark.createDataFrame(info, infoFields)
infoDF2.show()

如果报错:

就是行RDD和StructField的类型没对上。

四、SparkSQL实践

需求:

将student.data转成DF

1、分析数据源结构

(1)“|”分割

需要转义"\\|"

(2)email长度大于20,需要自定义长度

查看文件行长度wc -l student.data

默认20行列,和true(超出20行列的不显示)

 studentInfo.show(false)即可

(3)空跟null的处理

(4)case class 要在调用的方法的作用域之外(坑)

2、原代码

//TODO..
    val info = spark.sparkContext.textFile("C:\\Users\\dell\\Desktop\\LearningNote\\BigData\\lesson43\\资料\\student.data")
    import spark.implicits._
    val studentInfo = info.map(_.split("\\|")).map(x=>Student(x(0).toInt,x(1),x(2),x(3))).toDF()
    studentInfo.show(false)

  }
  case class Student(id:Int,name:String,phone:String,email:String)
}

3、拓展

(1)head()方法,打印前N条记录

studentInfo.head(5).foreach(println)

(2)frist()方法,打印第1条记录

3、.select("")字段错误报错易读

studentInfo.select("id","nama")

4、.filter(),条件过滤SparkSQL

studentInfo.filter("id>5").show(false)

5、UDF内置函数SUBSTER

studentInfo.filter("SUBSTR(name,0,1) = 'M'").show(false)

等于

studentInfo.filter("name like 'M%'").show(false)

6、排序

.sort()

 studentInfo.sort($"name".desc).show()

studentInfo.sort($"name".desc,$"id".desc).show(25,false)

7、修改列名

student.select($"name".as("new_name"))

8、join

studentInfo.join(studentInfo2,studentInfo.col("id")===studentInfo2.col("id"),"inner")
  .show(26,false)

9、分组

文件格式输出为json,以年龄分组,保存到桌面output文件夹里

infoDF.write.format("json").partitionBy("age").save("C:\\Users\\dell\\Desktop\\output")

30岁有两个

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

### Spark SQL 实战案例 #### 处理大规模日志数据分析 在一个典型的互联网公司环境中,每天都会产生海量的日志文件。为了更好地理解用户行为模式以及优化产品功能,企业需要定期分析这些日志数据。使用 Spark SQL 可以高效地完成这项工作。 ```sql CREATE TEMPORARY VIEW logs USING parquet OPTIONS (path "/data/logs"); SELECT date, COUNT(*) AS pv FROM logs GROUP BY date ORDER BY date DESC LIMIT 10; ``` 这段代码展示了如何创建一个临时视图 `logs` 来表示存储在 Parquet 文件中的日志表,并执行简单的聚合查询统计每日页面浏览量(PV),最后按日期降序排列取前十条记录[^3]。 #### 用户画像构建 基于用户的在线活动历史建立详细的特征描述即用户画像,在精准营销方面具有重要意义。借助于 Spark SQL 的强大能力,可以从多个维度对用户的行为习惯进行全面刻画: ```sql WITH user_actions AS ( SELECT user_id, SUM(CASE WHEN action_type='click' THEN 1 ELSE 0 END) as clicks, SUM(CASE WHEN action_type='purchase' THEN 1 ELSE 0 END) as purchases FROM actions WHERE timestamp >= '2023-01-01' AND timestamp < '2023-02-01' GROUP BY user_id) INSERT INTO TABLE user_profiles PARTITION(month='2023-01') SELECT ua.user_id, CASE WHEN clicks>5 THEN 'active' ELSE 'inactive' END as activity_level, CASE WHEN purchases>=1 THEN 'buyer' ELSE 'non-buyer' END as buyer_status FROM user_actions ua; ``` 此脚本首先计算每位用户在一月份期间点击次数 (`clicks`) 和购买次数 (`purchases`) ,接着根据这两个指标给定活跃程度(`activity_level`)和买方状态(`buyer_status`)标签,并最终更新到持久化的用户档案表中[^2]。 #### 财务报表自动化生成 对于金融机构而言,每月按时编制财务报告是一项重要任务。传统方式往往耗时费力且容易出错;而采用 Spark SQL 则能够显著简化这一过程并提升准确性。 ```sql -- 假设有一个名为 transactions 的事实表包含了所有的交易流水信息 WITH monthly_revenue AS( SELECT DATE_TRUNC('month', transaction_date) as month, SUM(amount) as total_amount FROM transactions WHERE status = 'completed' GROUP BY DATE_TRUNC('month', transaction_date)) INSERT OVERWRITE DIRECTORY '/output/monthly_report' ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' SELECT mr.month, COALESCE(total_expense, 0)-COALESCE(mr.total_amount, 0) as net_profit FROM monthly_revenue mr LEFT JOIN expenses e ON mr.month=e.expense_month; ``` 上述语句实现了月度收入汇总,并将其与支出相减得出净利润额,结果保存至指定目录下供进一步处理或展示[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值